Zum Inhalt springen

End-to-End-Tests mit Playwright

Die Tests aus dem Kapitel Testen laufen in happy-dom, einer Nachbildung des Browsers in Node. End-to-End-Tests steuern dagegen einen echten Browser. Sie klicken und tippen wie ein Mensch und prüfen, was auf der Seite erscheint. Playwright von Microsoft steuert dafür Chromium, Firefox und WebKit.

Laut Projektauftrag bringen End-to-End-Tests einen Bonuspunkt. Sie sind langsamer und brechen leichter als Tests mit Vitest, schreib deshalb nur wenige, für die Abläufe, die nie kaputtgehen dürfen, etwa Suchen, Anmelden und Anfragen. Laut der Vue-Dokumentation finden sie Fehler im Router, im Zustand und bei Anfragen, die Tests einzelner Komponenten übersehen. Komponenten testest du weiterhin mit Vitest.

Das Beispiel unter optional/end-to-end/de prüft die Demo der Fallbeispiel-App nach Teil 8, die ohne API läuft. In der Konfiguration startet webServer die Demo im Ordner der Fallbeispiel-App und wartet, bis sie antwortet. Lokal verwendet Playwright einen Server, der schon läuft, in CI startet es immer einen eigenen.

playwright.config.js
import { defineConfig, devices } from '@playwright/test'
// Starts the demo of the case app, which needs no API, and runs the tests in Chromium.
// The German and the English variant use their own port, so both can run at the same time.
const PORT = 5199
export default defineConfig({
testDir: 'tests',
forbidOnly: Boolean(process.env.CI),
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
use: {
baseURL: `http://localhost:${PORT}/`,
trace: 'on-first-retry',
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
webServer: {
command: `npm run demo -- --port ${PORT} --strictPort`,
cwd: '../../../case/08-architecture/de',
url: `http://localhost:${PORT}/`,
reuseExistingServer: !process.env.CI,
},
})

Die Tests finden Elemente so, wie ein Screenreader sie kennt, über ihre Rolle und ihren Namen. Playwright wartet bei jeder Aktion, bis das Element bereit ist, und expect wiederholt seine Prüfung bis zu fünf Sekunden lang. Deshalb wartet der Test nicht selbst auf das Ende der Suche. Jeder Test bekommt einen frischen Kontext des Browsers, ohne Favoriten und Anmeldung aus anderen Tests.

catalog.spec.js
import { expect, test } from '@playwright/test'
// Every test gets a fresh browser context, so favorites and the login of one test never reach another
// The card of a thing, found through its heading. Its link and button carry the name of the thing
// as hidden text, the visible label is enough inside the card.
function cardOf(page, name) {
return page.getByRole('article').filter({ has: page.getByRole('heading', { level: 2, name, exact: true }) })
}
test('sucht im Katalog, öffnet ein Ding und kehrt zur Karte zurück', async ({ page }) => {
await page.goto('/')
await expect(page.getByRole('heading', { level: 1, name: 'Katalog' })).toBeVisible()
await expect(page.getByText('60 Dinge gefunden')).toBeVisible()
await page.getByLabel('Suchen').fill('säge')
await expect(page.getByText('2 Dinge gefunden')).toBeVisible()
const reserve = cardOf(page, 'Stichsäge').getByRole('link', { name: /Reservieren/ })
await reserve.click()
await expect(page.getByRole('heading', { level: 1, name: 'Stichsäge' })).toBeVisible()
await page.goBack()
await expect(reserve).toBeFocused()
})
test('behält einen Favoriten nach dem Neuladen', async ({ page }) => {
await page.goto('/')
const favorite = cardOf(page, 'Bohrmaschine').getByRole('button', { name: /Favorit/ })
await favorite.click()
await expect(favorite).toHaveAttribute('aria-pressed', 'true')
await page.reload()
await expect(favorite).toHaveAttribute('aria-pressed', 'true')
})

Die Links und Knöpfe in den Karten tragen nach einem Doppelpunkt den Namen des Dings als versteckten Text. Chromium setzt vor diesen Doppelpunkt ein Leerzeichen, weil der versteckte Text absolut positioniert ist. Ein Test, der den ganzen Namen genau vergleicht, findet den Knopf deshalb nicht. Das Beispiel sucht zuerst die Karte über ihre Überschrift und darin den Knopf.

Mit npx playwright install chromium installierst du den Browser, npm run test:e2e startet die Tests. Im Workflow des Kursrepos installiert GitHub Actions nur Chromium und führt die Tests nach dem Build in beiden Sprachen aus.

Texte und Abbildungen stehen unter CC BY-SA 4.0, der Code unter der MIT-Lizenz und die Daten des Kurses unter CC0 1.0. Die Schrift der Abbildungen ist Kalam unter der SIL Open Font License 1.1. Die Quellen liegen auf GitHub.