Skip to content

test(dashboard): Playwright end-to-end smoke suite in CI#320

Merged
njbrake merged 2 commits into
mainfrom
dashboard-e2e-ci
Jul 20, 2026
Merged

test(dashboard): Playwright end-to-end smoke suite in CI#320
njbrake merged 2 commits into
mainfrom
dashboard-e2e-ci

Conversation

@njbrake

@njbrake njbrake commented Jul 20, 2026

Copy link
Copy Markdown
Member

Description

Adds Playwright end-to-end coverage for the dashboard, the missing layer above the Vitest component tests.

A Playwright project boots a real gateway (web/playwright.config.ts webServerweb/e2e/serve.sh: fresh SQLite, migrated to head, a fixed master key, serving the built bundle in standalone mode) and drives a browser through the core multi-page flows:

  • first-run onboarding (Welcome screen before any provider exists),
  • adding a provider and seeing it land in the table (onboarding then clears),
  • navigating the management pages (Models, Aliases, Settings, Providers),
  • creating an alias.

An e2e job is wired into the dashboard CI workflow (.github/workflows/otari-dashboard.yml): it sets up Python + uv and Node, builds the bundle, installs Chromium, and runs the suite. Vitest is scoped to src/ so it does not try to collect the browser specs. The committed bundle shifted only because the new dev dependency bumped the minifier; the app is unchanged (54 Vitest tests still pass).

Verified locally: npx playwright test4 passed (browser against the gateway-served bundle).

PR Type

  • Infrastructure / CI

Relevant issues

Closes #310. Part of #299.

Checklist

  • I understand the code I am submitting.
  • I have added or updated tests that cover my change (tests/unit, tests/integration).
  • I ran the Definition of Done checks locally (make lint, make typecheck; web typecheck, Vitest, and the new Playwright suite).
  • Documentation was updated where necessary.
  • If the API contract changed, I regenerated the OpenAPI spec (no API change in this PR).

AI Usage

  • This is fully AI-generated.

AI Model/Tool used: Claude Code (Opus 4.8, 1M context)

Any additional AI details you'd like to share: The agent built and ran the suite (4 passing browser tests against a live gateway) before opening this. Reasoning and decisions are @njbrake's; the code and prose are the agent's.

  • I am an AI Agent filling out this form (check box if true)

🤖 Generated with Claude Code

Summary

  • Added Playwright end-to-end smoke tests covering onboarding, provider creation, page navigation, and alias creation.
  • Added CI automation to build the dashboard and run the browser tests against a fresh gateway and database.
  • Added supporting test configuration, scripts, and dependency setup.
  • Updated Vitest scope to keep browser tests separate from component tests.
  • Refreshed generated dashboard assets after dependency/minifier changes.

Benefits

  • Validates key dashboard workflows across multiple pages.
  • Provides repeatable browser coverage in CI.
  • Keeps end-to-end runs isolated and deterministic.

@njbrake
njbrake temporarily deployed to integration-tests July 20, 2026 14:10 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@njbrake, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 886b679a-7adb-4fe2-af95-412ca3a4591b

📥 Commits

Reviewing files that changed from the base of the PR and between 0664f79 and 12bf8cd.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • .github/workflows/otari-dashboard.yml
  • web/.gitignore
  • web/e2e/dashboard.spec.ts
  • web/e2e/otari.yml
  • web/e2e/serve.sh
  • web/package.json
  • web/playwright.config.ts
  • web/vite.config.ts

Walkthrough

Adds Playwright dashboard smoke tests with an isolated gateway configuration, generated asset updates, Vitest test scoping, and a GitHub Actions job that builds the dashboard and runs Chromium-based E2E tests.

Changes

Dashboard E2E coverage

Layer / File(s) Summary
Dashboard bundle refresh
src/gateway/static/dashboard/assets/*, src/gateway/static/dashboard/index.html
Refreshes bundled JavaScript and Tailwind CSS assets and updates hashed references in the dashboard HTML.
Playwright runner and gateway setup
web/package.json, web/playwright.config.ts, web/e2e/otari.yml, web/e2e/serve.sh, web/.gitignore
Configures Playwright, adds E2E commands and dependency, starts an isolated migrated gateway, and ignores test artifacts.
Dashboard smoke flows
web/e2e/dashboard.spec.ts
Tests authentication, onboarding, provider creation, page navigation, and alias creation in serial order.
CI E2E workflow
.github/workflows/otari-dashboard.yml, web/vite.config.ts
Scopes Vitest to source tests and adds CI setup and execution for the built dashboard’s Playwright suite.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • mozilla-ai/otari#227: Extends the dashboard workflow and adds Playwright tests for related admin dashboard flows.

Suggested reviewers: khaledosman

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is related, but it uses test(dashboard) instead of the required Conventional Commit prefix format. Change it to a supported prefix like test: or ci: and keep the dashboard/Playwright summary concise.
Linked Issues check ⚠️ Warning The PR covers the gateway-served Playwright project, onboarding, provider creation, CI wiring, and aliasing, but not the pricing/test-provider flows in #310. Add end-to-end coverage for testing a provider and setting pricing, or document why those #310 tasks are intentionally out of scope.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description matches the template sections and includes a filled checklist, relevant issues, and AI usage details.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes stand out; the bundle refresh and config updates support the new Playwright E2E work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dashboard-e2e-ci
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch dashboard-e2e-ci

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@khaledosman

khaledosman commented Jul 20, 2026

Copy link
Copy Markdown
Member

I would recommend testing using vitest and the react-testing-library instead of e2e mocked tests that run against a real browser with playwright due to the common downsides of e2e tests being more brittle and slower, etc.. but considering you're testing against the real backend I see there might be some benefit to it

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/otari-dashboard.yml (1)

74-77: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Quote the Python version string in YAML.

A quick tip for the YAML configuration: it's generally a good practice to wrap version numbers in quotes, like "3.14". While 3.14 specifically won't lose precision, if someone later changes this to a version ending in a zero (like 3.10), YAML parses it as the float 3.1 before GitHub Actions sees it, which can lead to confusing workflow errors. Quoting it preempts that little headache!

✨ Proposed tweak
     - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
       with:
-        python-version: 3.14
+        python-version: "3.14"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/otari-dashboard.yml around lines 74 - 77, Quote the Python
version value in the workflow’s setup-uv configuration, changing the unquoted
value under python-version to a YAML string while preserving the existing
version.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/e2e/dashboard.spec.ts`:
- Around line 15-16: Disable retries for the serial test block configured by
test.describe.configure in web/e2e/dashboard.spec.ts, while preserving its
serial execution mode. Set the block-level retry configuration to zero so CI
does not rerun the stateful describe block against mutated shared database
state.

---

Nitpick comments:
In @.github/workflows/otari-dashboard.yml:
- Around line 74-77: Quote the Python version value in the workflow’s setup-uv
configuration, changing the unquoted value under python-version to a YAML string
while preserving the existing version.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dd44b87a-e0e4-42d8-9bfd-0c5a027e8124

📥 Commits

Reviewing files that changed from the base of the PR and between 018a06e and 0664f79.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (11)
  • .github/workflows/otari-dashboard.yml
  • src/gateway/static/dashboard/assets/index-Cn8B0tcC.js
  • src/gateway/static/dashboard/assets/index-LZ1ofGNu.css
  • src/gateway/static/dashboard/index.html
  • web/.gitignore
  • web/e2e/dashboard.spec.ts
  • web/e2e/otari.yml
  • web/e2e/serve.sh
  • web/package.json
  • web/playwright.config.ts
  • web/vite.config.ts

Comment thread web/e2e/dashboard.spec.ts
Comment on lines +15 to +16
// One shared gateway + DB, so the flows build on each other and must run in order.
test.describe.configure({ mode: "serial" });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Disable retries for this serial block to prevent deterministic retry failures.

Hey there! Awesome job getting this E2E suite set up. It’s a great addition.

I noticed a subtle trap with Playwright's serial mode: if a test fails and CI triggers a retry (since retries: 1 in CI), Playwright will retry the entire describe block. Because the shared database is only reset when the webServer boots up, the retried block will start with a mutated database (e.g., the provider added in test 2 will already exist). This will cause test 1 to fail confusingly because it expects the empty-state onboarding screen.

To prevent this, it's best to explicitly disable retries for this stateful block.

🔧 Proposed fix
-// One shared gateway + DB, so the flows build on each other and must run in order.
-test.describe.configure({ mode: "serial" });
+// One shared gateway + DB, so the flows build on each other and must run in order.
+// Retries are disabled because the database is only wiped at server startup;
+// a retried block would inherit the mutated DB state and fail deterministically.
+test.describe.configure({ mode: "serial", retries: 0 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// One shared gateway + DB, so the flows build on each other and must run in order.
test.describe.configure({ mode: "serial" });
// One shared gateway + DB, so the flows build on each other and must run in order.
// Retries are disabled because the database is only wiped at server startup;
// a retried block would inherit the mutated DB state and fail deterministically.
test.describe.configure({ mode: "serial", retries: 0 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/e2e/dashboard.spec.ts` around lines 15 - 16, Disable retries for the
serial test block configured by test.describe.configure in
web/e2e/dashboard.spec.ts, while preserving its serial execution mode. Set the
block-level retry configuration to zero so CI does not rerun the stateful
describe block against mutated shared database state.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Playwright end-to-end smoke test layer for the standalone admin dashboard by booting a real gateway (fresh SQLite + migrated to head) and driving core multi-page operator flows in Chromium, with a new CI job to run it.

Changes:

  • Added Playwright configuration plus an E2E suite that covers login, first-run onboarding, provider creation, page navigation, and alias creation.
  • Added an E2E gateway boot script and dedicated config (fresh SQLite DB, fixed master key, pricing gate disabled) for deterministic runs.
  • Wired a new e2e job into the dashboard GitHub Actions workflow and scoped Vitest collection to web/src so it does not pick up Playwright specs.

Reviewed changes

Copilot reviewed 9 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
web/vite.config.ts Limits Vitest test collection to src/** to avoid collecting Playwright specs.
web/playwright.config.ts Playwright runner + webServer config for booting the gateway and running Chromium E2E.
web/package.json Adds Playwright scripts and @playwright/test dev dependency.
web/package-lock.json Locks Playwright dependency tree (@playwright/test, playwright, playwright-core).
web/e2e/serve.sh Boots a fresh standalone gateway for E2E (wipes DB, runs migrations, serves).
web/e2e/otari.yml E2E-only gateway config (SQLite DB, fixed master key, pricing disabled).
web/e2e/dashboard.spec.ts Serial E2E smoke tests for key dashboard workflows.
web/.gitignore Ignores Playwright artifacts and the E2E SQLite DB files.
src/gateway/static/dashboard/index.html Updates hashed asset references after rebuilding the dashboard bundle.
.github/workflows/otari-dashboard.yml Adds an e2e workflow job to build the bundle, install Chromium, and run Playwright.
Files not reviewed (1)
  • web/package-lock.json: Generated file

Comment thread web/playwright.config.ts Outdated
webServer: {
command: "bash e2e/serve.sh",
url: "http://127.0.0.1:8000/health",
reuseExistingServer: !process.env.CI,
contents: read

steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

@khaledosman khaledosman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E2E/CI smoke suite looks solid; config is internally consistent, ephemeral SQLite files are gitignored, and Vitest is correctly scoped to src/. Two inline notes below. Optional nits: the dashboard and e2e jobs each run npm ci + npm run build from scratch (could share via needs: + an uploaded artifact), and the e2e:install script is unused since CI calls npx playwright install --with-deps chromium directly.

Review created by Claude Code.

Comment thread web/package.json Outdated
"test": "vitest run",
"test:watch": "vitest"
"test:watch": "vitest",
"e2e": "playwright test",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI builds the bundle before npm run e2e, but this script and serve.sh never build. Locally the suite serves the committed (possibly stale) bundle from src/gateway/static/dashboard, so a dev iterating on web/src gets false pass/fail. Suggest "e2e": "npm run build && playwright test" so local matches CI.

exit 1
fi

e2e:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This job boots the real Python gateway, but the workflow only triggers on web/** and src/gateway/static/dashboard/**. A src/gateway backend change that breaks a dashboard-served flow won't run this suite. Either add the relevant backend paths to the trigger or note the scoping is intentional.

@khaledosman khaledosman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more, on package-manager consistency (see inline).

Review created by Claude Code.


- name: Install web deps
working-directory: web
run: npm ci

@khaledosman khaledosman Jul 20, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with the otari.ai platform (which standardizes on pnpm), web/ here should move off npm to pnpm. Beyond consistency, pnpm is faster, more disk-efficient (content-addressed store with hard links), and safer against supply-chain attacks by default: its strict, non-flat node_modules blocks phantom-dependency access and it does not run install lifecycle scripts unless a package is explicitly allow-listed.

This PR follows the existing npm convention correctly, so don't switch just this new job (that would make the repo internally inconsistent). Instead, do it as a dedicated migration: replace package-lock.json with pnpm-lock.yaml, update both the dashboard and e2e jobs (cache: pnpm, pnpm install --frozen-lockfile, pnpm run ...), and update AGENTS.md. Fine as a follow-up PR.

Adds a Playwright project that boots the gateway serving the built bundle
(webServer, SQLite, fixed master key) and walks the core multi-page flows in a
real browser: first-run onboarding, adding a provider, navigating the
management pages, and creating an alias. Wires an `e2e` job into the dashboard
CI workflow, and keeps Vitest scoped to src/ so it does not collect the
browser specs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@njbrake
njbrake force-pushed the dashboard-e2e-ci branch from 0664f79 to ec2200b Compare July 20, 2026 14:32
@njbrake

njbrake commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

I would recommend testing using vitest and the react-testing-library instead of e2e mocked tests that run against a real browser with playwright due to the common downsides of e2e tests being more brittle and slower, etc.. but considering you're testing against the real backend I see there might be some benefit to it

@khaledosman Thanks! I'll look into it: yes the idea was that having at least a small subset of playwright driven tests should give highest confidence that things work, but the flakiness and slowness is a definite downside

…ck db)

- `npm run e2e` builds the bundle first so local runs match CI (khaledosman).
- webServer reuse is opt-in via PLAYWRIGHT_REUSE_SERVER; default always starts a
  fresh gateway so a stray :8000 server can't leave dirty DB state (Copilot).
- retries: 0 for the serial shared-DB suite (CodeRabbit).
- Quote python-version "3.14"; drop the now-redundant CI build step.
- Untrack web/e2e/e2e.db* (committed by mistake; already gitignored).
- Document the e2e job's web-scoped trigger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@njbrake
njbrake merged commit 4fe10c1 into main Jul 20, 2026
3 checks passed
@njbrake
njbrake deleted the dashboard-e2e-ci branch July 20, 2026 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dashboard: end-to-end tests in CI

3 participants