Работа с unicode и различными кодировками

Работа с Unicode и различными кодировками в тестах на Playwright опирается на понимание, какие символы проходят через UI, API и сетевые ответы, а также каким образом браузер и тестовая среда преобразуют текстовые данные.

Браузеры давно перешли на использование UTF-8 как базовой кодировки для HTML-документов, JavaScript-строк и DOM. Это означает, что большинство операций в Playwright, связанных с вводом текста (page.fill, page.type, работа с input), автоматически поддерживают широкий диапазон символов: кириллицу, акценты, иероглифы, эмодзи, диакритические комбинации.

Ключевой аспект: введённая строка должна быть корректной UTF-8 последовательностью без «сломанных» суррогатов. При генерации тестовых данных важно убедиться, что строки не содержат частичных кодовых точек.

Пример ввода текста с комбинированными символами:

await page.fill('input[name="comment"]', 'Café №1 — тестирование Unicode');

Широкие символы, используемые в азиатских языках, также поддерживаются:

await page.fill('input[name="name"]', '山田太郎');

Нормализация и сравнение строк

Unicode предоставляет несколько форм нормализации: NFC, NFD, NFKC и NFKD. В веб-окружении чаще всего используются NFC и NFD. Иногда символы, визуально идентичные, имеют разную бинарную форму. Сравнение таких строк при проверках может приводить к ложным отрицаниям в expect.

Для надёжных сравнений нередко применяется принудительная нормализация:

const text = await page.textContent('#title');
expect(text.normalize('NFC')).toBe(expected.normalize('NFC'));

Особенно важно при работе с диакритикой и акцентами, где один и тот же символ может быть представлен как готовый кодпоинт или как базовый символ плюс комбинирующий акцент.

Кодировки сетевых ответов

Playwright позволяет перехватывать и анализировать сетевой трафик. Ответы HTTP-запросов могут объявлять кодировку разными способами: заголовком Content-Type, метатегом HTML или вовсе отсутствием указания.

Для анализа тела ответа:

const [response] = await Promise.all([
  page.waitForResponse(/api\/user/),
  page.click('#loadUser')
]);
const buffer = await response.body();

Если сервер отдаёт данные в отличной от UTF-8 кодировке (например, Windows-1251), требуется ручная перекодировка при помощи внешних библиотек вроде iconv-lite. Это характерно для старых корпоративных систем и редких API.

Преобразование:

import iconv from 'iconv-lite';

const decoded = iconv.decode(buffer, 'win1251');

В тестировании критично проверить, что после преобразования строки учитываются корректно, включая специальные символы, кавычки, валютные знаки и тональные метки.

Кодировка файлов и загрузки

Загрузка и скачивание файлов через Playwright (page.waitForEvent("download")) нередко подразумевает работу с текстовыми документами (CSV, TSV, TXT), где кодировка не всегда UTF-8.

При открытии CSV:

const download = await page.waitForEvent('download');
await page.click('#exportCsv');
const path = await download.path();
const raw = fs.readFileSync(path);
const content = iconv.decode(raw, 'cp1251');

Важный момент: для CSV-файлов дополнительную сложность создаёт BOM (Byte Order Mark). UTF-8-файлы с BOM могут начинаться с EF BB BF, что ломает первую колонку при парсинге. Перед проверкой данных BOM удаляется вручную или через параметр в парсере.

Работа с вводом и клавиатурными событиями

Playwright эмулирует ввод символов через keyboard.type и keyboard.insertText. При этом:

  • type симулирует нажатия клавиш.
  • insertText напрямую вставляет строку в DOM.

Для сложных Unicode-последовательностей предпочтительно insertText, так как оно не зависит от раскладки клавиатуры.

await page.keyboard.insertText('Český jazyk — тест с диакритикой');

В тестировании компонентов редакторов (Quill, ProseMirror, TinyMCE) требуется проверять корректность работы с комбинируемыми символами, переносами, RTL-направлением (арабский, иврит) и суррогатными парами.

Право-налево (RTL) и направления текста

Unicode вводит концепцию bidi-алгоритма для работы с RTL-языками. HTML-атрибут dir="rtl" или CSS direction: rtl меняют отображение. При автоматизации важно:

  • Проверять позиционирование курсора.
  • Учитывать арифметику длины строки: визуальная и логическая длина не совпадают.
  • Тестировать копирование и удаление.

Получение логической строки через DOM:

const value = await page.$eval('#input', el => el.value);

Особенность: визуальные сравнения (скриншоты) часто необходимы, так как текстовые сравнения выдают одинаковые результаты при разной верстке.

Эмодзи и суррогатные пары

Эмодзи занимают несколько кодовых точек: базовый символ плюс модификаторы (цвет кожи, гендер, вариации). Их длина в JavaScript измеряется в UTF-16 кодовых единицах, что приводит к неожиданностям. Например:

'?'.length // 2

При сравнении текста тесты должны учитывать, что DOM-методы возвращают UTF-16 строки. Для более точного анализа используют Array.from, интерпретирующий Unicode-графемы:

Array.from('??').length // 1

Это полезно для проверок редакторов, счётчиков символов и валидации длины ввода.

Отображение Unicode в визуальных проверках

Playwright поддерживает page.screenshot и сравнение с эталонными изображениями. Важные нюансы:

  • Разные системы рендерят шрифты по-разному, особенно для азиатских наборов и эмодзи.
  • Шрифты могут не поддерживать отдельные символы, приводя к квадратикам и пропускам.
  • Для стабильных тестов шрифт фиксируется в CI-окружении.

Самый надёжный подход — единая среда выполнения с установленными шрифтами и одинаковым рендерингом.

Локализация и тестовые данные

Unicode влияет на локализацию. Для проверки форматов дат, валют, чисел используются API Intl:

const formatted = new Intl.NumberFormat('ru-RU').format(1234567.89);

В Playwright-тестах часто проверяется отображение:

  • разделительных знаков (точка/запятая),
  • пробелов группы разрядов,
  • знаков валют,
  • календарных систем.

Здесь Unicode регулирует не только символы, но и порядок записи.

API-тесты и сериализация

Работа с page.request.* подразумевает передачу тела запроса и получение JSON-ответов, которые по спецификации должны быть в UTF-8. Однако при взаимодействии с legacy-системами встречаются JSON-файлы в UTF-16LE или CP-1251. Прежде чем парсить, тело необходимо декодировать, иначе JSON.parse упадёт.

Консоль и логирование

Unicode-символы в логах тестов иногда приводят к искажению вывода в CI-инструментах. Особенно проблемны:

  • суррогатные пары,
  • невидимые символы (ZERO WIDTH SPACE, NO-BREAK SPACE),
  • BOM в начале строк.

Для диагностики полезно выводить кодовые точки:

Array.from(str).map(c => c.codePointAt(0).toString(16));

Это позволяет выявить скрытые символы, влияющие на сравнение.

Проблемы межплатформенной совместимости

Windows по умолчанию использует CP-1251 или UTF-16LE в ряде инструментов, тогда как Linux и macOS ориентированы на UTF-8. При работе Playwright-тестов в CI:

  • входные данные должны быть в UTF-8,
  • файлы и фикстуры сохраняются с BOM-free UTF-8,
  • результаты логирования должны быть унифицированы.

Файловая система также может влиять на отображение имён файлов, содержащих сложные грэпемы, что важно при проверке загрузок.

Принципы надёжного тестирования Unicode

Основные подходы:

  • Использование UTF-8 на всех этапах.
  • Исключение BOM и нестандартизированных кодировок.
  • Проверка нормализации строк.
  • Учёт графемных кластеров вместо string.length.
  • Фиксация шрифтов в визуальных тестах.
  • Использование библиотек конвертации для legacy-кодировок.
  • Тестирование RTL-режимов и сложных символов.

Такая методология минимизирует ложные срабатывания в тестах и делает результаты идентичными across-platform.