Logo Light
Published on

Playwright (2. díl) - První test

1. Úvod

V článku popíšu tyto body:

  • jak Playwright nainstalovat,
  • jak vypadá struktura testů,
  • jak je spustit a vyhodnotit,
  • a jak ladit chyby pomocí UI, tracingu a debugovacích nástrojů.

2. Instalace Playwrightu a příprava projektu

Playwright lze do projektu přidat dvěma způsoby:

  • zjednodušená instalace pomocí npm init playwright@latest
  • ruční instalace – postupné příkazy (npm init -y, npm install @playwright/test, …)

1. Zjednodušená instalace

npm init playwright@latest

Co příkaz vykoná?

  • spustí interaktivního průvodce v terminálu, kde lze zvolit programovací jazyk (TypeScript/Javascript), stáhnutí prohlížečů, vytvoření GitHub Actions workflow.
  • vytvoří složku tests/ s ukázkovými testy
  • vytvoří playwright.config.ts s výchozím nastavením
  • nainstaluje @playwright/test (test runner)
  • stáhne prohlížeče

Okamžitě po provedení příkazu můžu spustit ukázkové testy:

npx playwright test

2. Ruční instalace

Pokud chci plnou kontrolu nad instalací (resp. konfigurací) nebo přidávám Playwright do existujícího projektu, můžu ručně zadat příkazy:

mkdir playwright-demo
cd playwright-demo
npm init -y
npm install -D @playwright/test
npx playwright install
  • npm init -y
    • Tímto se vytvoří soubor package.json (inicializuje se nový Node.js projekt) uvnitř složky playwright-demo. Bez přepínače -y by se spustilo interaktivní dotazování na název projektu, verzi, popis atd. S přepínačem budou všechny odpovědi nastavené na default.
  • npm install -D @playwright/test
    • nainstaluje Playwright test runner
  • npx playwright install
    • stáhne prohlížeče

Pak si můžu sám vytvořit složku tests/ a v něm soubory s testy např. prvni-test.spec.ts.


3. První test v Playwrightu

V adresáři tests vytvořím soubor prvni-test.spec.ts. Pro testy budu používat svojí testovací stránku https://users.projects.icanbreakit.eu V prvním testu jen ověřím title stránky.

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

test('homepage has title "test API"', async ({ page }) => {
  await page.goto('https://users.projects.icanbreakit.eu')
  await expect(page).toHaveTitle('test API')
})

Uvedený kód sám o sobě říká, co se v testu bude dít. Možnosti, jak test spustit, jsou v následující kapitole.

Co je v uvedeném příkladu page?

V Playwrightu každý test běží v izolovaném prohlížečovém kontextu. Playwright automaticky vytvoří pro každý test novou stránku (tab) a předá ji jako argument. page je objekt reprezentující tuto aktuální stránku (tab).

Proč se používá destructuring { page }?

Funkce test v našem příkladu obsahuje dva parametry - název/popis testu (string "homepage has title "test API") a callback funkci - v našem případě:

;({ page }) => {
  await page.goto('https://users.projects.icanbreakit.eu')
  await expect(page).toHaveTitle('test API')
}

Každý callback testu dostává jako argument objekt s různými testovacími fixtures (ty si lze představit jako funkce integrované do test runneru - fixtures jsou Playwrightem předem připravené a dostupné v každém testu). Jednou z fixtures je právě page. V destructuring zápisu si test vezme jen ty fixtures, které mu poskytneme. Např. v tomto případě předáme tři fixtures:

test('něco', async ({ page, context, browserName }) => {
  // v testu mám kromě fixture "page" k dispozici i "context" a "browserName"
})

Kdybych destructuring nepoužil, měl bych v testu k dispozici celý objekt a musel bych psát:

test('example', async (fixtures) => {
  const page = fixtures.page
  await page.goto('https://users.projects.icanbreakit.eu')
})

4. Spuštění testů

Spuštění všech testů:

npx playwright test

Po spuštění našeho testu vypíše terminál takovýto výsledek:

Running 3 tests using 3 workers 3 passed (10.2s)

Ve výchozím nastavení se každý test spustí ve všech třech podporovaných prohlížečích – Chromium, Firefox a WebKit (Playwright navíc testy spouští paralelně ve více workerech, což výrazně šetří čas). Toto lze změnit buď v konfiguračním souboru playwright.config.ts, a nebo můžeme při spouštění testu v příkazové řádce použít např. tuto volbu a testy poběží jen ve Firefoxu:

npx playwright test --project=firefox

Spuštění jen konkrétního testu podle jména:

npx playwright test --grep "homepage has title"

Jiná možnost, jak spustit jen jeden test (nebo několik vybraných testů) je použití test.only. Spustí se pak jen testy s tímto označením.

test.only('homepage has title', async ({ page }) => {
  await page.goto('https://users.projects.icanbreakit.eu')
  await expect(page).toHaveTitle('test API')
})

Spuštění v headed režimu (s viditelným prohlížečem):

npx playwright test --headed

Spuštění v UI módu

Běh testů lze sledovat i ve speciálním UI módu, který umožňuje spouštět jednotlivé testytesty, filtrovat je a sledovat výsledky v grafickém rozhraní. Spustíme ho:

npx playwright test --ui

UI mód umožňuje:

  • zobrazí přehled všech testů v projektu (podle struktury složek a souborů projektu)
  • umožňuje spouštět testy jednotlivé i celé skupiny testů
  • filtrování testů podle názvu
  • opakované ruční spouštění
  • sledování testů v reálném čase
  • ukazuje výsledek (passed/failed/skipped), čas běhu
  • v případě chyby zobrazí stack trace, screenshoty, video
  • upravovat kód a test ihned znovu spouštět

Zobrazení reportu:

npx playwright show-report

Report se otevře v novém okně prohlížeče.

Paralelní běh

Ve výchozím nastavení jsou testy spouštěny paralelně (ve více workerech), každý worker běží v odděleném browser contextu, takže testy jsou izolované a neovlivňují se.

Někdy je potřeba paralelní běh vypnout (např. když běží několik testů sdílející jednu databázi apod.):

npx playwright test --workers=1

Počet workerů je omezený na jeden, takže testy běží sekvenčně.

Shrnutí

npx playwright test                   # spustí všechny testy
npx playwright test --grep "title"    # spustí jen testy obsahující "title"
npx playwright test --headed          # běh s viditelným prohlížečem
npx playwright test --ui              # ui mód
npx playwright test --reporter=html   # vygeneruje HTML report
npx playwright test --workers=1       # vypne paralelní běh
npx playwright test --debug           # spustí Inspector

5. Playwright UI, debugging a tracing

Jednou z největších výhod Playwrightu jsou jeho debugovací nástroje.

Playwright Inspector

Kdekoliv v kódu můžeme test zastavit a otevřít Playwright inspector. Test musí být spuštěn v headed režimu, aby se inspektor zobrazil.

await page.pause()

Test se pozastaví, otevře se okno se zdrojovým kódem testu, ve kterém můžeme krokovat jako v debuggeru, klikat na prvky (inspektor po kliknutí na element vygeneruje jeho lokátor), nahrávat další kroky, vidět aktuální stav DOMu apod.

Playwright inspector lze pro účely debuggování spustit i ručně z příkazové řádky:

npx playwright test --debug

Playwright inspector lze také spustit přímo pro nahrávání testů. Inspector pak vygeneruje kód testu podle toho, co na stránce děláte:

npx playwright codegen https://users.projects.icanbreakit.eu

Tracing

Tracing umožňuje záznam celého běhu testu (screenshots, network logy, DOM snapshoty). V tracingu vidíme krok po kroku, co se dělo, proč a kdy test selhal apod.

Zapnutí v konfiguraci:

use: {
  trace: 'on-first-retry'
}

Pro tracing můžeme nastavit tyto hodnoty:

  • off – tracing je vypnutý (defaultní nastavení)
  • on – tracing je zapnutý pro každý běh testu
  • retain-on-failure – tracing se uloží jen pokud test selže. Když test projde, tracing se zahodí. (ideální pro CI/CD)
  • on-first-retry – tracing se zapne až při prvním opakovaném běhu testu (když je v konfiguraci povolené retries)

Po běhu testu můžeme otevřít uložený záznam:

npx playwright show-trace trace.zip

Otevře se přehrávač s časovou osou testu, všemi kroky (kliknutí, fill, navigace), screenshoty a DOM snapshoty v daném okamžiku, síťovými požadavky apod.


6. Příklady často používaných akcí

Interakce se stránkou

Kliknutí na tlačítko

await page.click('text=Submit')

Vyplnění formuláře

await page.fill('#username', 'my-name')
await page.fill('#password', 'secret-password')
await page.click('button[type=submit]')

Ověření textu na stránce

await expect(page.locator('h1')).toHaveText('Vítejte')

Získání textu z elementu

const title = await page.textContent('h1')

Získání atributu z elementu

const href = await page.getAttribute('a.download-link', 'href')

Screenshot stránky nebo elementu

await page.screenshot({ path: 'screenshot.png' })
await page.locator('#logo').screenshot({ path: 'logo.png' })
await page.goto('https://example.com/page1')
await page.goBack()
await page.goForward()

Kontrola URL adresy po přesměrování

await expect(page).toHaveURL(/.*dashboard/)

Pokročilejší scénáře

Práce s cookies

const cookies = await context.cookies()
await context.addCookies([
  { name: 'auth', value: 'my-token', url: 'https://users.projects.icanbreakit.eu/' },
])

Práce s lokálním úložištěm

await page.evaluate(() => localStorage.setItem('theme', 'dark'))

Čekání na síťovou odpověď

// spuštění akce, která pošle request
const response = await Promise.all([
  page.waitForResponse('**/api/save'), // počká na odpověď z API
  page.click('button[type=submit]'),
])

expect(response[0].ok()).toBeTruthy()

Otevření nové karty / okna

Pokud akce na stránce otevře nový tab nebo okno, je třeba na něj počkat:

const [newPage] = await Promise.all([
  context.waitForEvent('page'), // čekáme na otevření nové stránky
  page.click('a[target="_blank"]'), // otevře novou kartu
])

await newPage.waitForLoadState()
await expect(newPage).toHaveTitle('my-title')

Pozn. context je browser context aktuálního testu a každý test má svůj vlastní.

Regulární výrazy

Při používání metod jako locator, toHaveURL, toHaveText apod., lze místo obyčejného stringu použít regulární výraz v syntaxi /pattern/ – Playwright ho pozná automaticky.

Kdy využijeme regulární výrazy?

Obvykle tam, kde se text dynamicky mění:

  • atributy s konzistentním prefixem

Např. chci vyhledat všechny elementy s atributem data-test-id, které začínají item- a zbytek jsou libovolné znaky:

// <button data-test-id="item-123">Item 123</button>
// <button data-test-id="item-abc">Item abc</button>
// ...

const myItems = page.locator('[data-test-id=/^item-/]')
  • URL, kde část je proměnlivá (např. obsahuje parametry)
await expect(page).toHaveURL(/www\.my-page\.com\/item\/.*$/)
  • v textu elementu se mění text (datumy, IDčka apod.)
const message = page.getByTestId('item-added-notif')
await expect(message).toHaveText(/Položka č\. \d* byla vytvořena/)
  • v případě, že potřebuju dynamický regulární výraz, použiju new RegExp():
const dynamicId = 123
await expect(page).toHaveURL(new RegExp(`/item/${dynamicId}$`))

7. Co bude příště?

Příště se podíváme na základní konfiguraci a organizaci testů.