Slide 1

Slide 1 text

Zurück in den Browser Das Comeback der Frontend Tests RainerHahnekamp

Slide 2

Slide 2 text

About Me... https://www.youtube.com/ @RainerHahnekamp https://www.ng-news.com ● Rainer Hahnekamp ● ANGULARarchitects.io ● NgRx Core Team ● Developer / Trainer / Speaker @RainerHahnekamp Open Source Projects NgRx Toolkit Testronaut NgRx Sheriff

Slide 3

Slide 3 text

RainerHahnekamp Spotify - Honeycomb Kent Dodds' Trophy Where we left off last year

Slide 4

Slide 4 text

RainerHahnekamp Unit Tests Integration & Component Tests End-to-End (E2E) Tests Where we left off last year…

Slide 5

Slide 5 text

RainerHahnekamp Unit Tests Integration & Component Tests End-to-End (E2E) Tests Angular 21

Slide 6

Slide 6 text

RainerHahnekamp Unit Tests Integration & Component Tests End-to-End (E2E) Tests Angular 21

Slide 7

Slide 7 text

RainerHahnekamp Unit Tests Integration & Component Tests End-to-End (E2E) Tests Angular 21

Slide 8

Slide 8 text

RainerHahnekamp Why Vitest???

Slide 9

Slide 9 text

RainerHahnekamp A little bit of History Browser Browser Emulated Browser (jsdom)

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

RainerHahnekamp Why Browser? ✅ Real Browser Behavior ● Rendering ● User Events ● Timings (Signals) ● New Technologies (Shadow DOM, Hydration) ✅ Switch to "UI is the API" ✅ Performant Headless Browsers ✅ Visualization (DX)

Slide 12

Slide 12 text

Coding Time

Slide 13

Slide 13 text

RainerHahnekamp The Case for E2E Component Testing

Slide 14

Slide 14 text

RainerHahnekamp Helmets on!

Slide 15

Slide 15 text

RainerHahnekamp testronaut.dev

Slide 16

Slide 16 text

RainerHahnekamp Playwright Features Recorder Element Highlight Trace Viewer UI Mode Debugging Agents & MCP

Slide 17

Slide 17 text

RainerHahnekamp test('🚀', async ({ page }) => { await page.goto('/') await page.getByRole(...).click(); await expect(...).toBeVisible() });

Slide 18

Slide 18 text

RainerHahnekamp test('🚀', async ({page,runInBrowser})=>{ await runInBrowser(() => { TestBed.createComponent(...); }); await page.getByRole(...).click(); await expect(...).toBeVisible() }); Angular Dev Server

Slide 19

Slide 19 text

RainerHahnekamp test('🚀', async ({page,runInBrowser})=>{ await runInBrowser(() => { TestBed.createComponent(...); }); await page.getByRole(...).click(); await expect(...).toBeVisible() }); TestBed.createComponent(...); Angular Dev Server

Slide 20

Slide 20 text

RainerHahnekamp test('🚀', async ({ page }) => { await page.goto('/landing'); await page.getByRole(...).click(); });

Slide 21

Slide 21 text

RainerHahnekamp import { Spaceship } from './spaceship'; test('🚀', async ({ mount, page }) => { await mount(Spaceship); await page.getByRole(...).click(); });

Slide 22

Slide 22 text

RainerHahnekamp import { Spaceship } from './spaceship'; test('🚀', async ({ runInBrowser, page }) => { await runInBrowser(() => { TestBed.createComponent(Spaceship); }); await page.getByRole(...).click(); });

Slide 23

Slide 23 text

RainerHahnekamp import { Spaceship } from './spaceship'; test('🚀', async ({ runInBrowser, page }) => { await runInBrowser(() => { TestBed.createComponent(Spaceship); }); await page.getByRole(...).click(); });

Slide 24

Slide 24 text

RainerHahnekamp test('🚀', async ({ page }) => { await page.evaluate(() => mountSpaceship()); await page.getByRole(...).click(); }); import { Spaceship } from './spaceship'; globalThis.mountSpaceship = () => { TestBed.createComponent(Spaceship); };

Slide 25

Slide 25 text

RainerHahnekamp

Slide 26

Slide 26 text

RainerHahnekamp Vielen Dank!