Тесты Playwright используют узкую часть JavaScript: переменные, функции, объекты, массивы, деструктуризация и async/await. Большинство начинающих либо пропускают язык совсем и не могут отладить сбой, либо пытаются выучить весь JavaScript сначала и никогда не доходят до написания тестов. Эта статья охватывает только то что реально встречается в тестовом коде Playwright. Особый приоритет отдан async/await: забытый await перед вызовом браузера: самая частая причина непостоянных и трудноотслеживаемых сбоев.

Зачем JavaScript QA-инженеру

Тесты Playwright пишутся в файлах JavaScript (или TypeScript). Когда пишешь:

await page.click('.submit-button');

Ключевое слово await, стрелочная функция, строковый аргумент: всё это JavaScript. Если не понимаешь что делает await, не сможешь отладить зависший тест. Если не понимаешь переменные, не сможешь извлечь значение со страницы и проверить его.

Хорошая новость: автоматизация QA использует маленький, предсказуемый кусок JavaScript. Ты не строишь веб-приложения. Ты пишешь скрипты которые с ними взаимодействуют. Паттерны кода повторяются.

Переменные: let и const

Два способа хранить значения:

// const: значение не меняется после присвоения
const baseUrl = 'https://lab.becomeqa.com';
const timeout = 5000;

// let: значение можно менять
let loginAttempts = 0;
loginAttempts = loginAttempts + 1;

В тестовом коде по умолчанию используй const. Тянись к let только когда нужно переприсвоение.

Ещё встретишь var в старом коде. Игнорируй его. У него поведение области видимости вызывающее баги. const и let заменили его.

Типы данных

Типы которые реально используются в тестовом коде:

// String — текст
const username = 'admin@becomeqa.com';
const password = 'testpass123';

// Number — для таймаутов, счётчиков, проверки цен
const timeout = 30000; // 30 секунд в мс
const itemCount = 5;

// Boolean — для условий
const isLoggedIn = true;
const hasError = false;

// null / undefined — отсутствие значения
// Встречаются в проверках
const itemTitle = null; // намеренно пустое

В тестах Playwright строки и числа встречаются везде. Булевы значения появляются в условиях. null появляется когда что-то не найдено.

Функции

Функции упаковывают код для переиспользования. Два стиля которые встретишь:

// Обычная функция (старый стиль, всё ещё распространён)
function generateEmail() {
  return `user_${Date.now()}@test.com`;
}

// Стрелочная функция (современная, частая в коде Playwright)
const generateEmail = () => `user_${Date.now()}@test.com`;

// Стрелочная функция с телом (несколько строк)
const login = async (page, email, password) => {
  await page.getByRole('button', { name: 'Login' }).click();
  await page.getByLabel('Email').fill(email);
  await page.getByLabel('Password').fill(password);
  await page.getByRole('button', { name: 'Submit' }).click();
};

Стрелочные функции стали дефолтом в современном коде Playwright. async перед функцией означает что внутри используется await, это разбирается в Async/Await простым языком (для тестировщиков, которых сбивают с толку промисы).

Объекты

Объекты группируют связанные данные:

// Объект со свойствами
const user = {
  email: 'admin@becomeqa.com',
  password: 'testpass123',
  role: 'admin',
};

// Доступ к свойству через точку
console.log(user.email);  // 'admin@becomeqa.com'

// Или через скобки
console.log(user['email']);  // тот же результат

В Playwright объекты появляются постоянно как опции передаваемые в функции:

// { name: 'Login' } — это объект
await page.getByRole('button', { name: 'Login' }).click();

// { data: { title: 'Tokyo' } } — вложенный объект
await request.post('/api/items', { data: { title: 'Tokyo' } });

Массивы

Массивы хранят списки значений:

const statuses = ['Planned', 'In Progress', 'Completed'];
const prices = [9.99, 14.99, 29.99];

// Доступ по индексу (начинается с 0)
console.log(statuses[0]);  // 'Planned'
console.log(statuses[2]);  // 'Completed'

// Длина
console.log(statuses.length);  // 3

В тестах массивы появляются когда получаешь несколько элементов со страницы:

// Получить текст всех строк таблицы
const rows = await page.getByRole('row').allTextContents();
// rows теперь массив: ['Tokyo', 'Paris', 'London']
expect(rows).toContain('Tokyo');

if / else: принятие решений

// Базовое условие
if (user.role === 'admin') {
  await page.goto('/admin');
} else {
  await page.goto('/dashboard');
}

// Проверка на отсутствие чего-то
const errorMessage = await page.getByText('Invalid credentials').isVisible();
if (errorMessage) {
  console.log('Login failed as expected');
}

Сравнение === (тройное равно) проверяет и значение, и тип. В JavaScript всегда используй === вместо ==.

Шаблонные строки: создание строк

Старый способ (запутанный при множестве переменных):

const url = 'https://' + environment + '.lab.becomeqa.com/item/' + itemId;

Современный способ с обратными кавычками:

const url = `https://${environment}.lab.becomeqa.com/item/${itemId}`;

Шаблонные строки (строки с обратными кавычками) позволяют встраивать переменные через ${}. Встречаются постоянно в тестовом коде для построения URL, сообщений и тестовых данных.

Деструктуризация: извлечение из объектов и массивов

Паттерн который появляется повсюду в фикстурах Playwright и коде Page Object Model:

// Вместо этого:
const email = user.email;
const password = user.password;

// Можно сделать вот так (деструктуризация):
const { email, password } = user;

С массивами:

const [first, second] = ['Tokyo', 'Paris', 'London'];
// first = 'Tokyo', second = 'Paris'

В Playwright это встречается постоянно в параметрах теста:

// { page, request } здесь — деструктуризация из объекта фикстур
test('should work', async ({ page, request }) => {
  // page и request доступны напрямую
});

Модули: import и export

Файлы тестов Playwright импортируют из фреймворка и из собственного кода:

// Импорт из пакета Playwright
import { test, expect } from '@playwright/test';

// Импорт page object
import { LoginPage } from '../pages/LoginPage';

// Импорт тестовых данных
import { testUsers } from '../data/users';

Когда создаёшь вспомогательный файл, экспортируй из него:

// В utils/helpers.ts
export const baseUrl = 'https://lab.becomeqa.com';

export function generateTestEmail() {
  return `test_${Date.now()}@example.com`;
}

import ввозит. export делает доступным для импорта в других местах. Вся система.

Циклы

Иногда в тестовом коде нужны циклы, но реже чем кажется. Основные случаи:

// Перебор массива тестовых данных
const destinations = ['Tokyo', 'Paris', 'London'];

for (const destination of destinations) {
  await page.getByLabel('Destination').fill(destination);
  await page.getByRole('button', { name: 'Save' }).click();
}

Цикл for...of наиболее понятен для итерации массивов. forEach, map, filter разбираются в отдельной статье про методы массивов.

То что путает всех: async/await

Почти каждая строка кода Playwright имеет перед собой await:

await page.goto('https://lab.becomeqa.com');
await page.getByRole('button', { name: 'Login' }).click();
await expect(page.getByText('Dashboard')).toBeVisible();

Правило простое: любой метод Playwright взаимодействующий с браузером возвращает Promise, и нужен await чтобы дождаться завершения.

Если забыть await, тест обычно переходит к следующей строке до того как завершилось текущее действие, что вызывает запутанные, непостоянные сбои.

Этому посвящена Async/Await простым языком (для тестировщиков, которых сбивают с толку промисы). Краткая версия: всегда ставь await перед вызовами Playwright. TypeScript предупредит если забудешь.

Что пока не нужно

Вещи которые QA-инженеры часто пытаются изучить прежде чем они нужны, создавая лишнюю путаницу:

  • Классы: полезны для Page Object Model, но понять POM можно без глубокого знания классов
  • Promises/then/catch: async/await заменяет их в современном коде; сначала выучи await
  • Замыкания, прототипы, цикл событий: внутренности JavaScript которые редко влияют на тестовый код
  • Манипуляции с DOM: ты используешь Playwright для взаимодействия с DOM; напрямую ты его не трогаешь

Изучай их когда конкретная проблема потребует. Не раньше.

Полный тест из всего выше

Тест Playwright использующий каждую концепцию из этой статьи:

import { test, expect } from '@playwright/test';

// Объект: данные пользователя
const testUser = {
  email: 'admin@becomeqa.com',
  password: 'testpass123',
};

// Шаблонная строка: динамический URL
const baseUrl = 'https://lab.becomeqa.com';

test('пользователь может добавить туристический элемент', async ({ page }) => {
  // Деструктуризация: извлекаем email и password из объекта
  const { email, password } = testUser;

  // Функции (методы Playwright) с await
  await page.goto(baseUrl);
  await page.getByRole('button', { name: 'Login' }).click();
  await page.getByLabel('Username').fill(email);
  await page.getByLabel('Password').fill(password);
  await page.getByRole('button', { name: 'Submit' }).click();

  // Строковая переменная
  const destination = 'Tokyo';
  
  await page.getByRole('button', { name: 'Add Item' }).click();
  await page.getByLabel('Destination').fill(destination);

  // Boolean: проверяем видимость чего-то
  const saveButton = page.getByRole('button', { name: 'Save' });
  
  // Условие
  if (await saveButton.isVisible()) {
    await saveButton.click();
  }

  // Массив: allTextContents возвращает массив
  const rows = await page.getByRole('row').allTextContents();
  
  // Проверка на массиве
  expect(rows.some(row => row.includes(destination))).toBeTruthy();
});

Это реальный, полный тест. JavaScript который он использует: ровно то что разобрано в этой статье. Ничего лишнего.

FAQ

Нужно ли проходить полный курс JavaScript прежде чем начать Playwright?

Нет. Изучай концепции JavaScript по мере необходимости. Сразу начинай писать тесты Playwright используя паттерны выше, и ищи ответы когда что-то непонятно. «Сначала выучи JS, потом Playwright» приводит к тому что люди учат JS месяцами и никогда не доходят до Playwright.

JavaScript или TypeScript для Playwright?

TypeScript. Официальная документация Playwright использует TypeScript. TypeScript добавляет типы к JavaScript, что означает: редактор ловит ошибки до запуска тестов. Для начинающих разница небольшая. Подробности в TypeScript для QA: почему статическая типизация улучшает тесты.

Что делать если не понимаю кусок кода Playwright?

Гуглить. MDN Web Docs (developer.mozilla.org) содержит лучший справочник по возможностям языка JavaScript. Документация Playwright (playwright.dev) покрывает Playwright-специфичные API. Между этими двумя источниками есть ответ на любой вопрос о тестовом коде.

Сколько времени нужно чтобы выучить достаточно JavaScript для написания тестов Playwright?

2–4 недели ежедневной практики обычно достаточно чтобы работать продуктивно. Конкретные вещи будешь изучать по мере их встречи, но 2–4 недели сфокусированной работы над концепциями из этой статьи позволяют писать и понимать реальные тесты.

Чаще всего начинающих путает async/await. Она лежит в основе каждого взаимодействия Playwright. Понял её, разблокировал отладку.

→ See also: Async/Await простым языком (для тестировщиков, которых сбивают с толку промисы) | TypeScript для QA: почему статическая типизация улучшает тесты