Урок 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/false→boolean. - Квадратные скобки → массив.
- Фигурные скобки с
ключ: значение→ объект.
Шаблонная строка склеивает текст и переменную:
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 {
// иначе — ждём ошибку
}
| Оператор | Смысл |
|---|---|
=== |
Равно (и тип, и значение). В тестах почти всегда он, не == |
!== |
Не равно |
> < >= <= |
Сравнить числа |
&& |
И: оба условия истинны |
| или | Достаточно одного условия (в коде — два символа вертикальной черты подряд) |
! |
Не: перевернуть true ↔ false |
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);
Читаем так:
function makeUser— имя.(role: string, runId: number)— что принимает.: TestUser— что возвращает.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/);
});
Порядок чтения:
import— какие инструменты одолжили.type/interface— форма данных.function— повторяемые шаги.test("...")— сценарий: данные → действия →expect(проверка).- Цепочка
page.getBy...().fill()/.click()— найти элемент, сделать действие. expect(...)— утверждение: страница, текст или элемент такие, как мы ждём.
Комментарий // редактор и Playwright игнорируют. Это подсказка человеку.
Как разбирать незнакомую строку
- Найди
test(— это сценарий. - Иди сверху вниз: данные → действия → проверки.
- Узнай конструкцию по таблице выше:
const,if,=>,{ },: string. - Непонятно — спроси ИИ: «что делает эта строка и зачем она здесь?»
- Не проси «напиши такой же тест» — иначе следующий файл снова будет белибердой.
Ключевые тезисы для теста
- Программирование учат как язык: чтение + письмо, не таблица синтаксиса.
importодалживает код пакета;package.jsonперечисляет зависимости.const— не переназначаем;let— только если значение меняется.- Базовые типы:
string,number,boolean, массив, объект. - TypeScript ловит неверный тип до прогона;
type/interface— контракт на поля объекта. ?— поле необязательно; вертикальная черта — значение из списка вариантов ("free" | "busy").if/===/&&/!и тернарный? :— развилки, как в тест-кейсе.- Массив +
for...of— список и его перебор. - Функция принимает аргументы и
returnрезультат;() => {}— стрелочная запись. - Объект держит поля вместе;
{ name, email } = userиasync ({ page })— деструктуризация. - ИИ на этом этапе — репетитор по строчке, не автор домашки.
Домашнее задание
Индивидуальная проверка ДЗ — на Boosty
Конспект, видео и тест открыты всем. Текст задания и проверка работы — по подписке.
Индивидуальная проверка ДЗ на Boosty