Playwright vs Playwright Test — чим бібліотека відрізняється від test runner?

Уяви, що Playwright Test ще не існує — є тільки сама бібліотека playwright:

import { chromium } from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com');

Це не тест — це просто Node.js-програма що відкриває браузер. На цьому рівні Playwright — бібліотека автоматизації браузера, як Express для HTTP чи React для UI. Вона нічого не знає про test()expect()beforeEach, retries чи паралельний запуск.

Це і є причина плутанини: у npm справді існують два окремі пакети. playwright — бібліотека автоматизації браузера (Browser, BrowserContext, Page, Locator). @playwright/test — test runner, побудований поверх цієї бібліотеки: додає test()expect(), fixtures, hooks, workers, retries, projects, reporters. Коли встановлюєш npm init playwright@latest — отримуєш саме @playwright/test, який використовує automation API всередині себе.

┌─────────────────────────────┐
│       Playwright Test        │   test(), expect(), fixtures,
│                               │   hooks, workers, retries,
│                               │   projects, reporters
└──────────────┬────────────────┘
               │ використовує
               ▼
┌─────────────────────────────┐
│   Browser Automation API     │   BrowserType, Browser,
│                               │   BrowserContext, Page, Locator
└─────────────────────────────┘

Що насправді відбувається коли пишеш npx playwright test: спочатку запускається не браузер, а Test Runner — звичайна Node.js-програма. Вона читає playwright.config.ts, знаходить файли з тестами, завантажує їх щоб дізнатись які test(...) там оголошені, розподіляє роботу і тільки після цього створює workers, які вже виконують самі тести. На цьому етапі браузера може ще не існувати взагалі — він з’явиться лише коли якийсь тест реально попросить browser чи page.

“Завантажує” і “реєструє” — це два різні кроки, які легко сплутати:

  • Імпортує — Node.js виконує файл checkout.spec.ts від початку до кінця, як звичайний JS-файл. Усе на верхньому рівні файлу виконується одразу: імпорти, змінні, і кожен виклик test(...).
  • Реєструє — відбувається під час імпорту, коли виконання доходить до рядка test('назва', callback). Сам виклик test() НЕ запускає callback (тіло тесту з gotoclick) — він лише додає запис у список Test Runner’а: “є тест з такою назвою, ось його код, запусти коли настане час”.

Якщо в файлі 5 тестів — під час імпорту всі 5 викликів test(...) виконаються одразу (бо це просто виконання файлу), і всі 5 з’являться у списку зареєстрованих тестів. Але жоден з їхніх callback-ів ще не запустився.

Аналогія: імпорт файлу — це ніби читати зміст книги рецептів сторінка за сторінкою. Кожен test(...) — рядок у змісті: “Рецепт: Паста, дивись стор. 12”. Ти вже знаєш що рецепт існує і де його шукати (зареєстрував), але поки що нічого не приготував (не виконав). Тільки після того як усі файли імпортовані й усі тести зареєстровані — Test Runner знає повний список того, що треба виконати, і роздає ці задачі по workers, де вже реально запускаються callback-и.

Ключовий інсайт: test('...', callback) нічого не виконує одразу. Він лише реєструє тест у Test Runner — приблизно як runner.addTest(callback). Сам callback запускається пізніше, коли Test Runner вирішить що час його виконувати.

Де живе Worker — він не частина Browser API

Playwright Test Runner
        │
        ├── Worker 1 (окремий Node.js процес)
        │        └── Browser (запускається лише якщо тест його попросив)
        │              └── BrowserContext (новий для кожного тесту)
        │                    └── Page
        │
        └── Worker 2 (окремий Node.js процес)
                 └── Browser
                       └── BrowserContext
                             └── Page

Worker — не частина браузерного API, його створює Test Runner для розподілу тестів по паралельних Node.js процесах. Вже всередині worker-процесу, коли тест попросить фікстуру browser, вона запускає браузер — і саме тому browser є worker-scoped fixture: один браузерний процес живе всередині одного worker, а не сам по собі.

Підсумок двома рівнями: Рівень 1 (Browser Automation API) відповідає на питання “як керувати браузером” — Browser, BrowserContext, Page, Locator. Рівень 2 (Playwright Test) відповідає на питання “як запускати, ізолювати, масштабувати й координувати тести” — test(), fixtures, workers, retries, projects. Тест не керує браузером напряму — він лише описує що треба зробити, а всю координацію бере на себе Test Runner.

🔧 TypeScript / JS патерни в цьому прикладіimport { chromium } from 'playwright' — імпорт з базової бібліотеки (не з @playwright/test); test('...', async () => {}) — виклик функції що реєструє callback, а не виконує його одразу; сам callback — це той самий патерн callback з попередньої картки, тільки викликає його не браузерна подія, а Test Runner коли настає час.

🗣 Як читати вголосПлутанина між Playwright і Playwright Test — одна з тих речей яка заважає зрозуміти fixtures і workers, поки її не проясниш. Playwright — це бібліотека автоматизації браузера, вона просто вміє відкривати сторінки і клікати по елементах, і нічого не знає про тести. Playwright Test — окремий пакет, test runner, побудований поверх цієї бібліотеки: він додає test, expect, fixtures, workers, retries. Коли запускаєш npx playwright test, спочатку піднімається не браузер, а Test Runner — звичайна Node.js програма, яка читає конфіг, знаходить спек-файли, імпортує їх щоб зареєструвати всі test, і тільки потім створює workers для паралельного виконання. І ось найважливіше: сам виклик test з назвою і колбеком нічого не виконує в момент виклику — він просто каже Test Runner “запам’ятай цей тест”, а реальне виконання відкладається на потім. Worker теж не має прямого стосунку до браузерного API — це окремий Node.js процес, який створює Test Runner для паралелізму, і вже всередині цього процесу, коли конкретний тест попросить фікстуру browser, тільки тоді запускається сам браузер. Тому і виходить що тест не керує браузером напряму — він лише описує що потрібно зробити, а вся координація лежить на Test Runner.