feat(ui-test): Playwright ui tests foundation stage 0#2180
Conversation
There was a problem hiding this comment.
Pull request overview
Introduces a foundational Playwright E2E test setup for the UI, including a dependency-free stub backend and CI workflow, to enable end-to-end coverage of multi-step user flows against mocked API responses.
Changes:
- Added Playwright configuration, fixtures, and an initial smoke spec to validate the app loads against a stubbed backend.
- Implemented a standalone Node HTTP stub backend plus typed mock builders for specs.
- Wired Playwright into UI scripts, gitignore, and a dedicated GitHub Actions workflow.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/playwright/tsconfig.json | Adds Playwright-local TS config extending the UI tsconfig. |
| ui/playwright/tests/smoke.spec.ts | Adds initial Stage 0 smoke test asserting Agents page loads without error state/page errors. |
| ui/playwright/README.md | Documents Playwright scope, mocking approach, layout, and conventions. |
| ui/playwright/mocks/server.mjs | Adds dependency-free stub backend serving /api/* responses used by server actions. |
| ui/playwright/mocks/data.ts | Adds typed mock builders for assertions and future scenario plumbing. |
| ui/playwright/fixtures/test.ts | Adds shared fixture to reset stub and bypass onboarding via localStorage init script. |
| ui/playwright.config.ts | Adds Playwright runner config including webServer boot of stub + next dev. |
| ui/package.json | Adds Playwright test scripts and @playwright/test dev dependency. |
| ui/package-lock.json | Locks @playwright/test and updates Playwright packages. |
| ui/.gitignore | Ignores Playwright report/output directories. |
| .github/workflows/ui-playwright.yaml | Adds CI workflow to run Playwright tests on UI changes and upload reports. |
Files not reviewed (1)
- ui/package-lock.json: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
4bed054 to
4e52362
Compare
|
|
||
| export default defineConfig({ | ||
| testDir: "./playwright/tests", | ||
| outputDir: "./playwright/test-results", |
There was a problem hiding this comment.
With workers: 1 this is a no-op, but raising workers before Stage 1 isolation lands will cause tests to race against the shared stub. Consider setting this to false until then.
There was a problem hiding this comment.
Good call — Fixed in 08b3cfc — set fullyParallel: false to match workers: 1, so both stay off until Stage 1 isolation lands. Thanks @mesutoezdil !
There was a problem hiding this comment.
Good call — Fixed in 08b3cfc — set fullyParallel: false to match workers: 1, so both stay off until Stage 1 isolation lands. Thanks @mesutoezdil !
pls write your own sentences when interacting with me, not llm outputs. thx!
There was a problem hiding this comment.
my bad! I will keep it human moving forward 🤝
|
dco is red |
Signed-off-by: Carlos Logroño Guerrero <carlos.logrono@solo.io>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: cjlg-soloio <carlos.logrono@solo.io> Signed-off-by: Carlos Logroño Guerrero <carlos.logrono@solo.io>
- Force single Playwright worker unconditionally (shared stub + Next server) - Default req.method/req.url before parsing in the stub server - Fail fast on stub reset failure in CI; stay non-fatal locally - Drop unused pull-requests:write permission from the workflow Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Carlos Logroño Guerrero <carlos.logrono@solo.io>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Carlos Logroño Guerrero <carlos.logrono@solo.io>
4e52362 to
be5ee4e
Compare
Pairs fullyParallel with workers: 1 so both stay off until per-test data isolation lands, preventing tests from silently racing against the shared stub backend if the worker count is raised. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Carlos Logroño Guerrero <carlos.logrono@solo.io>
08b3cfc to
5d1b617
Compare
| "compilerOptions": { | ||
| "types": ["node", "@playwright/test"] | ||
| }, | ||
| "include": ["**/*.ts", "**/*.mjs"] |
There was a problem hiding this comment.
mjs without allowJs: true is a no-op, typescript won't check server.mjs
| projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }], | ||
| webServer: [ | ||
| { | ||
| command: "node playwright/mocks/server.mjs", |
There was a problem hiding this comment.
server.mjs reads STUB_PORT from env but this block doesn't pin it, so a shell-set STUB_PORT makes the stub bind to the wrong port and the health check times out
There was a problem hiding this comment.
I pinned STUB_PORT in the stub's webServer env so it always goes to the health endpoint or whatever BACKEND_INTERNAL_URL is set to
| { | ||
| command: "npm run dev", | ||
| url: APP_URL, | ||
| reuseExistingServer: !CI, |
There was a problem hiding this comment.
if npm run dev is already running without BACKEND_INTERNAL_URL, reuseExistingServer reuses it and the stub is bypassed silently
There was a problem hiding this comment.
changed it to boot always on BACKEND_INTERNAL_URL and made it fail loudly if 8001 is already taken
| } from "@/types"; | ||
|
|
||
| export type Namespace = { name: string; status: string }; | ||
| export type ToolServerListEntry = { ref: string; groupKind: string; discoveredTools: unknown[] }; |
There was a problem hiding this comment.
discoveredTools is typed as unknown[] but the real ToolServerResponse from @/types uses DiscoveredTool[]. the file comment promises compile-time drift detection, but this local type bypasses it
There was a problem hiding this comment.
good catch! using RemoteMCPServerResponse now
There was a problem hiding this comment.
again! pls write your own sentences when interacting with me, not llm outputs. thx!
- Pin STUB_PORT on the stub webServer so a shell-set STUB_PORT can't misbind it - Stop reusing the dev server so BACKEND_INTERNAL_URL always applies (no silent stub bypass) - Type ToolServerListEntry as the real RemoteMCPServerResponse for compile-time drift detection - Drop no-op **/*.mjs from the playwright tsconfig include Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Carlos Logroño Guerrero <carlos.logrono@solo.io>
Description
Adds the Stage 0 foundation for Playwright page-level E2E testing of the kagent UI, running against a mocked backend with no cluster required.
The suite targets the one gap nothing else covers — multi-step user flows across components:
The core design challenge: the UI fetches data server-side via Next.js server actions, so browser-level
page.route("**/api/**")intercepts nothing. Instead, this PR boots a dependency-free stub backend and redirects Next's server-side fetch to it viaBACKEND_INTERNAL_URL— whichgetBackendUrl()inui/src/lib/utils.tsalready honors. Playwright'swebServerblock starts both the stub (:8899) andnext dev(:8001) automatically.This is foundation-only: config, stub backend, shared fixture, CI workflow, and a single smoke test. The helper/driver library and per-test scenarios land in Stage 1; feature flows in Stage 3 (see
ui/playwright/README.md).Files
New
ui/playwright.config.tsui/playwright/mocks/server.mjshttponly); serves happy-path payloads mirroringsrc/types/index.ts; exposes/__mock/health&/__mock/resetui/playwright/mocks/data.tsui/playwright/fixtures/test.tstest/expectfixture; resets stub per test and bypasses first-run onboardingui/playwright/tests/smoke.spec.tsui/playwright/tsconfig.jsonui/playwright/README.md.github/workflows/ui-playwright.yamlui/**); installs chromium, runstest:pw, uploads reportModified
ui/package.json—@playwright/testdev dep +test:pw/test:pw:ui/test:pw:debugscriptsui/package-lock.json— lockfile updatesui/.gitignore— ignoresplaywright-report,playwright/test-results,playwright/.cacheTesting
Playwright starts the stub backend and dev server automatically. The smoke test passes when the home page renders the Agents, no "Error Encountered" state appears, and no uncaught page errors occur. CI runs the same on any
ui/**change.Change type
test / ci — new test infrastructure and CI workflow. No product or runtime code changed.
Changelog
Stage 1 of 3 playwright tests for ui