К списку уроков
Блок 2 · Программирование для автоматизатора

Урок 3. Не понимаете код автотеста?

JS/TS с нуля

Темы урока

Программирование как язык, VS Code/Cursor, import и npm, зачем TypeScript, переменные, типы, условия, циклы, функции, объекты — на реальном booking-flow.spec.ts

Видео урока

Пройти тест по уроку

Конспект урока

Конспект — Урок 3: Не понимаете код автотеста?

Главное за урок

Код — это язык. Его учат чтением чужого текста и письмом своего, а не зубрёжкой справочника. Процессор понимает нули и единицы; JS/TS — способ написать инструкции по-человечески. const, if, function — обычный словарь, как «стол» и «бежать» в русском.

После этого конспекта вы должны узнавать в автотесте: переменные, типы, условия, циклы, функции, объекты, массивы и типы TypeScript. Непонятная строка — вопрос ИИ: что она делает. Не «напиши тест за меня».


Где писать код и откуда берётся Playwright

Пишем в VS Code или Cursor. Файл теста — обычный текст с расширением .ts.

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

import говорит: мы одолжили готовый код из пакета. npm — магазин пакетов. package.json — список зависимостей. node_modules — скачанный код (его нет в Git, ставят командой npm install).

test и expect мы не писали сами. Без import файл их не увидит.


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

Переменная — имя для значения.

Ключевое слово Смысл Когда в тесте
const Задали один раз, больше не переназначаем Почти всегда: пользователь, URL, локатор, runId
let Значение потом меняем Редко: «сначала пусто, потом текст ошибки»
const email = "guest@example.com";
let errorText = "";
errorText = "Неверный пароль"; // let можно переприсвоить
// email = "other@example.com"; // ошибка: const так нельзя

Правило автоматизатора: по умолчанию const. let — только когда без него не обойтись.


Типы данных

Тип отвечает на вопрос: что лежит в переменной? В автотесте почти всё сводится к шести штукам.

Тип Что это Пример в тесте
string Текст в кавычках "guest@example.com", "/login"
number Число, без кавычек 60, Date.now()
boolean Да / нет true, false, результат isVisible()
null «Пусто намеренно» Поля нет, слот не выбран
undefined «Ещё не задали» Забыли передать аргумент
массив [] Список однотипных вещей ["host", "guest"]
объект {} Карточка с полями { name, email, password }
const name: string = "Анна";
const durationMin: number = 60;
const slotFree: boolean = true;
const tags: string[] = ["Playwright", "TypeScript"];

Как отличить на глаз:

  • Кавычки '...' или "..." → строка.
  • Голое число → number.
  • Слова true / falseboolean.
  • Квадратные скобки → массив.
  • Фигурные скобки с ключ: значение → объект.

Шаблонная строка склеивает текст и переменную:

const runId = 17;
const email = `guest-${runId}@example.com`; // "guest-17@example.com"

Обращение к элементу массива — по индексу с нуля: tags[0] это "Playwright". Длина — tags.length.


Зачем TypeScript, а не «просто JavaScript»

TypeScript = JavaScript плюс типы. Браузер по-прежнему выполняет JS; типы проверяют до прогона.

const email: string = "guest@example.com";
// email = 42; // TypeScript не пропустит: число ≠ строка

Без типов такая опечатка всплывёт только красным тестом в браузере. С типами — сразу в редакторе.

Аннотация и вывод типа

const role: string = "host";     // аннотация явная
const role2 = "guest";           // TypeScript сам понял: это string

Явно пишем тип, когда объявляем форму данных или аргумент функции. Если значение очевидно ("host", 60), компилятор часто выводит тип сам.

type и interface — контракт на объект

Оба описывают «какие поля обязаны быть». В тестах чаще type или interface на пользователя, слот, бронь.

type TestUser = {
  name: string;
  email: string;
  password: string;
};

const guest: TestUser = {
  name: "Гость",
  email: "guest@example.com",
  password: "testpass123",
};

Если забыть email или положить туда число — ошибка ещё до запуска браузера.

interface читается так же:

interface Slot {
  date: string;
  time: string;
  free: boolean;
}

Для чтения кода разница type / interface не важна: оба — чертёж объекта.

Необязательное поле и объединение

type User = {
  name: string;
  telegram?: string;          // ? = поля может не быть
};

type Status = "free" | "busy"; // только одно из двух значений

? значит: свойства может не быть, это не ошибка. | — «или»: переменная принимает один вариант из списка. Часто в статусах и ролях.

as и импорт типа

import { test, expect, type Page } from "@playwright/test";

type Page — тип страницы браузера из Playwright. Им пишут аргумент функции: page: Page. Слово type в import говорит: это только для проверки типов, в рантайме его нет.


Условия: if, сравнения, тернарный оператор

Условие в коде — тот же «если… то…» из тест-кейса.

if (slotFree) {
  // слот свободен — бронируем
} else {
  // иначе — ждём ошибку
}
Оператор Смысл
=== Равно (и тип, и значение). В тестах почти всегда он, не ==
!== Не равно
> < >= <= Сравнить числа
&& И: оба условия истинны
или Достаточно одного условия (в коде — два символа вертикальной черты подряд)
! Не: перевернуть truefalse
if (status === "free" && isVisible) {
  // оба условия да
}

if (!dayVisible) {
  // дня не видно — перезагрузить / выбрать другой
}

Тернарный оператор — if/else в одну строку:

const label = status === "free" ? "можно забронировать" : "уже занято";

Читается: если условие — левое, иначе правое.


Массивы и циклы

Массив хранит список. Цикл его перебирает — чтобы не копировать один и тот же шаг пять раз.

const roles = ["host", "guest", "guest2"];

for (const role of roles) {
  // один проход — один пользователь
}

for (let i = 0; i < roles.length; i++) {
  // если нужен номер: 0, 1, 2
}

for...of — «для каждого элемента». В автотестах его хватает чаще, чем классического for.

while крутит, пока условие истинно. В Playwright явный while редок: ожидание прячет expect(...).toPass() или auto-waiting. Но саму идею «повторяй, пока не получится» вы уже узнаете.

Методы массива, которые часто мелькают при чтении:

Код Смысл
list.length Сколько элементов
list.includes("host") Есть ли такое значение
list.map(...) Превратить каждый элемент в другой
list.filter(...) Оставить тех, кто подходит
list.find(...) Найти первый подходящий

В Playwright .filter({ hasText: "..." }) — та же идея: из нескольких карточек оставить ту, где есть текст.


Функции

Функция — именованный кусок кода, который можно вызвать ещё раз. Две одинаковые регистрации — сигнал вынести функцию.

function makeUser(role: string, runId: number): TestUser {
  return {
    name: `${role} Автотест`,
    email: `${role}-${runId}@example.com`,
    password: "testpass123",
  };
}

const host = makeUser("host", 17);
const guest = makeUser("guest", 17);

Читаем так:

  1. function makeUser — имя.
  2. (role: string, runId: number) — что принимает.
  3. : TestUser — что возвращает.
  4. return { ... } — результат вызова.

Стрелочная функция — тот же смысл, другой синтаксис:

const makeEmail = (role: string, id: number) => `${role}-${id}@example.com`;

() => { ... } значит: «вот что сделать». В Playwright так оформляют тело теста и шаги:

test("логин с верным паролем", async ({ page }) => {
  // шаги
});

async у функции значит: внутри можно писать await (подожди шаг в браузере). Подробно — Урок 4. Для чтения достаточно: где await, там ждут; следующая строка начнётся после этого шага.


Объекты и деструктуризация

Объект группирует связанные поля. Не три переменные, а одна карточка.

const user = {
  name: "Анна",
  email: "anna@example.com",
  password: "testpass123",
};

user.email; // "anna@example.com"

Точка — взять поле. Если поля нет — undefined.

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

const { name, email } = user;
// то же, что:
// const name = user.name;
// const email = user.email;

В тесте вы уже это видели:

test("...", async ({ page, browser }) => {
  // Playwright передал объект с полями page и browser —
  // мы сразу взяли нужные
});

{ page } в аргументе — не «магия фреймворка», а деструктуризация параметра.


Как читать файл автотеста сверху вниз

Типичный .spec.ts складывается из одних и тех же блоков:

import { test, expect, type Page } from "@playwright/test";

type TestUser = { name: string; email: string; password: string };

function makeUser(role: string, runId: number): TestUser { /* ... */ }

test("название сценария", async ({ browser }) => {
  const user = makeUser("guest", Date.now());
  const page = await browser.newPage();

  await page.goto("/login");
  await page.getByLabel("Email").fill(user.email);
  await expect(page).toHaveURL(/\/dashboard/);
});

Порядок чтения:

  1. import — какие инструменты одолжили.
  2. type / interface — форма данных.
  3. function — повторяемые шаги.
  4. test("...") — сценарий: данные → действия → expect (проверка).
  5. Цепочка page.getBy...().fill() / .click() — найти элемент, сделать действие.
  6. expect(...) — утверждение: страница, текст или элемент такие, как мы ждём.

Комментарий // редактор и Playwright игнорируют. Это подсказка человеку.


Как разбирать незнакомую строку

  1. Найди test( — это сценарий.
  2. Иди сверху вниз: данные → действия → проверки.
  3. Узнай конструкцию по таблице выше: const, if, =>, { }, : string.
  4. Непонятно — спроси ИИ: «что делает эта строка и зачем она здесь?»
  5. Не проси «напиши такой же тест» — иначе следующий файл снова будет белибердой.

Ключевые тезисы для теста

  • Программирование учат как язык: чтение + письмо, не таблица синтаксиса.
  • import одалживает код пакета; package.json перечисляет зависимости.
  • const — не переназначаем; let — только если значение меняется.
  • Базовые типы: string, number, boolean, массив, объект.
  • TypeScript ловит неверный тип до прогона; type / interface — контракт на поля объекта.
  • ? — поле необязательно; вертикальная черта — значение из списка вариантов ("free" | "busy").
  • if / === / && / ! и тернарный ? : — развилки, как в тест-кейсе.
  • Массив + for...of — список и его перебор.
  • Функция принимает аргументы и return результат; () => {} — стрелочная запись.
  • Объект держит поля вместе; { name, email } = user и async ({ page }) — деструктуризация.
  • ИИ на этом этапе — репетитор по строчке, не автор домашки.

Домашнее задание

Индивидуальная проверка ДЗ — на Boosty

Конспект, видео и тест открыты всем. Текст задания и проверка работы — по подписке.

Индивидуальная проверка ДЗ на Boosty
База для QA — вопросы на собеседование, тренажёры и материалы