Що таке Page, BrowserContext і Browser? Як вони пов’язані і чому page: Page усюди в тестах?

Ми щойно розібрались як Playwright спілкується з браузером через WebSocket і CDP. Тепер подивимось на структуру самого браузера — три рівні ієрархії які пояснюють чому тести ізольовані один від одного навіть при паралельному запуску.

Browser — це сам процес браузера (Chromium, Firefox або WebKit). Дорогий у запуску, тому Playwright тримає один на кожен worker (процес виконання). При паралельному запуску з 4 workers — буде 4 браузерних процеси, по одному на кожен worker.

BrowserContext — ізольований “профіль” всередині браузера, схожий на окреме інкогніто вікно. Має свої cookies, localStorage, cache. Playwright створює новий для кожного тесту.

Cookies належать BrowserContext — не Browser і не Page. Кожен context має власний cookie jar: два різні context автоматично мають повністю ізольовані cookies, навіть якщо працюють в одному Browser-процесі. Саме на цьому і побудована ізоляція тестів у Playwright — тест A залогінений під admin ніяк не бачить сесію тесту B. Всі вкладки (Page) всередині одного context — навпаки, cookies спільні.

Page — одна вкладка всередині context. Саме з нею ти взаємодієш через gotoclickfill.

Коли ти пишеш async ({ page }) в тесті — ти не створюєш вкладку вручну. Playwright сам підготував Browser, створив новий BrowserContext і відкрив у ньому Page — і передав тобі готову. Це і є fixture: готовий ресурс який фреймворк підготував за тебе. Детальніше про fixtures — в наступних двох картках.

TypeScript — ієрархія Browser → Context → Page

// Ієрархія, яку будує Playwright під капотом:
// Browser  → один процес браузера (Chromium/Firefox/WebKit)
//   └─ BrowserContext → ізольована "профіль"-сесія (cookies, storage, кеш)
//        └─ Page → одна вкладка всередині цього контексту

test('приклад', async ({ page }: { page: Page }) => {
  // page тут — вже готова вкладка, Playwright сам:
  // 1. запустив Browser (або перевикористав з пулу)
  // 2. створив новий ізольований BrowserContext саме для цього тесту
  // 3. відкрив у ньому одну Page і передав її як fixture

  await page.goto('/login');
  await page.fill('#email', 'test@test.com');
});

// Чому це важливо для ізоляції тестів:
test('тест А логіниться', async ({ page }) => {
  await page.goto('/login'); // свій BrowserContext, свої cookies
});
test('тест Б — гість', async ({ page }) => {
  await page.goto('/'); // НЕ бачить cookies тесту А — інший BrowserContext
});

💡 Простими словами: Browser — це сам додаток-браузер. BrowserContext — окремий “інкогніто-профіль” всередині нього, зі своїми cookies/localStorage/cache. Page — одна вкладка в цьому профілі. Playwright дає кожному тесту власний BrowserContext (а отже й Page), тому тести ніколи не “бачать” стан один одного, навіть якщо запускаються паралельно в одному Browser-процесі.

🔧 TypeScript / JS патерни в цьому прикладі { page }: { page: Page } — деструктуризація з TypeScript анотацією типу; async ({ page }) => {} — стрілочна async функція з деструктуризацією параметра; // коментарі — документують що Playwright робить “за лаштунками”; const — оголошення константи (значення не можна перепризначити).

🗣 Як читати вголос У Playwright є три рівні: Browser — це сам процес браузера, важкий об’єкт, який дорого створювати, тому Playwright переважно перевикористовує один на весь тестовий запуск. Всередині нього BrowserContext — це ізольована сесія, схожа на окреме інкогніто-вікно: у неї свої cookies, localStorage, кеш, дозволи. І вже всередині контексту — Page, конкретна вкладка, з якою ми безпосередньо взаємодіємо через gotoclickfill і так далі. Коли ми пишемо async ({ page }) у тесті, ми не створюємо цю вкладку самі — Playwright робить це автоматично: для кожного тесту піднімає новий BrowserContext, відкриває в ньому одну Page і підставляє її як fixture-аргумент. Анотація типу : Page — це просто TypeScript, який каже компілятору, що це об’єкт класу Page, а не any, щоб автодоповнення й перевірка типів працювали. Саме тому в кожному тесті, кожному Page Object, кожному helper-методі ти бачиш page — це не глобальна змінна, а свіжий ізольований об’єкт, який Playwright інжектить заново щоразу, і завдяки цьому один тест ніколи не впливає на стан іншого, навіть при паралельному запуску.