test: add Playwright e2e testing framework with core scenario coverage#229
test: add Playwright e2e testing framework with core scenario coverage#229
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Also exclude e2e/ from ESLint to avoid react-hooks false positives on Playwright's fixture `use` callback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the first Playwright spec covering the home page UI and navigation to generation-preview. Also updates playwright config to use port 3002 to avoid conflicts with other running services. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Seeds IndexedDB with a 3-scene stage by navigating to / first (so Dexie initializes the DB at v8), then writing data without re-triggering onupgradeneeded. Verifies the sidebar renders 3 scenes and that clicking a scene switches the active scene heading. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… type annotations - Add data-testid="scene-list", "scene-item", "scene-title" to scene-sidebar.tsx - Update classroom.page.ts selectors to use data-testid instead of CSS class chains - Extract createSettingsStorage() helper into e2e/fixtures/test-data/settings.ts - Update all 3 spec files to use createSettingsStorage() and import defaultTheme from scene-content - Add SceneOutline[] type annotation to mockOutlines via relative import - Add SlideTheme type annotation to defaultTheme in scene-content.ts - Remove redundant mockServerProviders() call from setupGenerationMocks() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cosarah
left a comment
There was a problem hiding this comment.
Code Review: Playwright E2E Testing Framework
Overall this is a well-structured PR that introduces a clean Playwright e2e testing infrastructure with good separation of concerns (fixtures, Page Object Models, test data). The zero-source-code-invasion approach using page.route() for API mocking is solid. A few issues to address:
Bug: Fragile / Incorrect Selector
e2e/tests/classroom-interaction.spec.ts:162
await expect(page.getByRole('heading', { name: '光反应' })).toBeVisible();Classroom page and scene-sidebar.tsx do not render any <h1>–<h6> or role="heading" element for the current scene name. The scene title in the sidebar is a plain <span class="...truncate...">. This assertion will likely fail at runtime.
Suggestion: Either change the assertion to match the actual sidebar markup (e.g. check which scene card has an "active" class), or add a heading element to the classroom page to display the current scene title.
Bug: Fragile CSS-class Selectors
e2e/pages/classroom.page.ts:12
this.sidebarScenes = page.locator('.overflow-y-auto.space-y-2 > div');Targeting Tailwind utility class names is brittle — they can change during routine styling work. Consider adding data-testid attributes to the sidebar container and scene items, e.g. data-testid="scene-list" and data-testid="scene-item".
Same applies to span.truncate (line 28) for scene titles.
Suggestion: Duplicated Constants
SETTINGS_STORAGE and defaultTheme are defined in multiple test files (classroom-interaction.spec.ts, generation-flow.spec.ts, home-to-generation.spec.ts). Consider extracting them into a shared fixture file like e2e/fixtures/test-data/constants.ts to keep things DRY and make future schema changes easier.
Suggestion: setupGenerationMocks double-mocks serverProviders
e2e/fixtures/mock-api.ts:70-75
setupGenerationMocks() calls mockServerProviders(), but the base.ts fixture already calls mockServerProviders() on every test via the mockApi fixture. Calling it twice is harmless but misleading — either remove it from setupGenerationMocks() or document that it's idempotent.
Minor: CI Job — Missing pnpm test in existing lint job
The diff adds a Unit Tests step (pnpm test) to the existing lint job. This is fine but the job name is "Lint / Type Check" — it may be worth updating the name to reflect that it now also runs unit tests, or splitting it into its own job.
Minor: Hardcoded Port
playwright.config.ts:11 — baseURL: 'http://localhost:3002'
Port 3002 is fine as a convention, but if another service is running on that port, tests will silently connect to the wrong server. The webServer.env.PORT is set to match, which is good — just worth noting in a comment or README.
Summary
| Area | Verdict |
|---|---|
| Architecture | ✅ Good — fixtures, POM, test-data separation |
| API Mocking | ✅ Clean page.route() approach |
| Test Coverage | ✅ Covers core happy-path flow |
| Selectors | |
| DRY | |
| CI Integration | ✅ Good — artifact upload on failure |
Main blocker: fix the getByRole('heading') assertion that doesn't match actual markup.
🤖 Generated with Claude Code
…and dedup Must-fix issues from @cosarah's review: - Add data-testid="scene-title-heading" to <h1> in header.tsx and update classroom-interaction test to use getByTestId instead of getByRole("heading"), making the selector explicit and independent of ARIA role assumptions - Add data-testid="scene-list", "scene-item", "scene-item-title" to scene-sidebar.tsx; update classroom.page.ts to use data-testid locators instead of fragile Tailwind class selectors (.overflow-y-auto.space-y-2 > div) Should-fix issues: - Extract SETTINGS_STORAGE and defaultTheme constants duplicated across 3 test files into e2e/fixtures/test-data/constants.ts - Remove redundant mockServerProviders() call from setupGenerationMocks(); base fixture already mocks it for every test Co-Authored-By: Paperclip <noreply@paperclip.ing>
Review Feedback AddressedThanks for the thorough review @cosarah! All must-fix and should-fix items have been implemented. Here's a summary of what changed: Bug Fixes (Must Fix)1. Wrong heading selector (
2. Fragile CSS-class selectors (
Refactoring (Should Fix)3. Duplicated constants
4. Double-mocked serverProviders
Commit: Note: The minor items (CI job name, port comment) are acknowledged — they can be addressed as a follow-up if desired. 🤖 Generated with Claude Code |
|
Thanks for the review! Already fixed in
Re: |
cosarah
left a comment
There was a problem hiding this comment.
Re-review
Previous round feedback all addressed, confirmed working. LGTM overall — clean architecture, solid mock strategy.
One thing to fix before merge:
e2e/ should be excluded from tsconfig.json
tsconfig.json currently includes all **/*.ts files, which pulls e2e tests into Next.js compilation. As the test suite grows and uses Node/Playwright-specific APIs, this will cause false type errors.
Fix: add "e2e" to the exclude array in tsconfig.json, or create a dedicated e2e/tsconfig.json.
🤖 Generated with Claude Code
- Add "e2e" to tsconfig.json exclude array to prevent Next.js compilation from pulling in Playwright/Node APIs - Rename CI job from "Lint & Typecheck" to "Lint, Typecheck & Unit Tests" to reflect that it now also runs unit tests (added in PR #144) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
THU-MAIC#229) * chore: add Playwright e2e testing infrastructure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add e2e mock fixture data Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add MockApi route interception helper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add base fixture and page object models Also exclude e2e/ from ESLint to avoid react-hooks false positives on Playwright's fixture `use` callback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add home-to-generation e2e spec Adds the first Playwright spec covering the home page UI and navigation to generation-preview. Also updates playwright config to use port 3002 to avoid conflicts with other running services. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add generation-flow e2e spec Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add classroom-interaction e2e spec Seeds IndexedDB with a 3-scene stage by navigating to / first (so Dexie initializes the DB at v8), then writing data without re-triggering onupgradeneeded. Verifies the sidebar renders 3 scenes and that clicking a scene switches the active scene heading. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ci: add Playwright e2e test job Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(e2e): fix review issues — data-testid selectors, shared helpers, type annotations - Add data-testid="scene-list", "scene-item", "scene-title" to scene-sidebar.tsx - Update classroom.page.ts selectors to use data-testid instead of CSS class chains - Extract createSettingsStorage() helper into e2e/fixtures/test-data/settings.ts - Update all 3 spec files to use createSettingsStorage() and import defaultTheme from scene-content - Add SceneOutline[] type annotation to mockOutlines via relative import - Add SlideTheme type annotation to defaultTheme in scene-content.ts - Remove redundant mockServerProviders() call from setupGenerationMocks() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: exclude e2e/ from tsconfig, rename CI job - Add "e2e" to tsconfig.json exclude array to prevent Next.js compilation from pulling in Playwright/Node APIs - Rename CI job from "Lint & Typecheck" to "Lint, Typecheck & Unit Tests" to reflect that it now also runs unit tests (added in PR THU-MAIC#144) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: 杨慎 <117187635+cosarah@users.noreply.github.com>
Closes #230
Summary
playwright.config.ts, fixtures, Page Object Models)MockApiclass usingpage.route()to intercept API calls with fixture data (zero source code invasion)Test plan
pnpm test:e2e— all 3 specs pass locally (11.2s)pnpm check— Prettier cleanpnpm lint— no new errors🤖 Generated with Claude Code