Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 53 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version-file: ".nvmrc"

Expand Down Expand Up @@ -228,7 +228,7 @@ jobs:
run: npm ci

- name: Restore Chromium browser cache
uses: actions/cache@v6
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/.cache/ms-playwright
key: playwright-chromium-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
Expand All @@ -241,14 +241,17 @@ jobs:

- name: Upload UI diagnostics
if: failure()
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: critical-ui-diagnostics-${{ github.run_id }}
path: |
test-results/
playwright-report/
if-no-files-found: ignore

# Production-journey regression suite. Blocking (PT-05): a red production
# journey must fail pr-required instead of normalising as an advisory red.
# Prototype /mockups specs run separately in ui-mockups below.
ui-regression:
name: UI regression
needs: changes
Expand Down Expand Up @@ -338,6 +341,53 @@ jobs:
playwright-report/
if-no-files-found: ignore

# Prototype /mockups specs live in their own advisory lane so a red mockup
# can never mask (or block on) a production-journey regression (PT-05).
ui-mockups:
name: Advisory mockup UI
needs: changes
if: github.event_name == 'pull_request' && needs.changes.outputs.ui_changed == 'true'
continue-on-error: true
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version-file: ".nvmrc"
cache: npm
cache-dependency-path: package-lock.json

- name: Install dependencies
run: npm ci

- name: Restore Chromium browser cache
uses: actions/cache@v6
with:
path: ~/.cache/ms-playwright
key: playwright-chromium-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

- name: Install Chromium browser
run: npx playwright install --with-deps chromium

- name: Chromium mockup regression
run: npm run test:e2e:mockups

- name: Upload mockup UI diagnostics
if: failure()
uses: actions/upload-artifact@v7
Comment thread
coderabbitai[bot] marked this conversation as resolved.
with:
name: mockup-ui-diagnostics-${{ github.run_id }}
path: |
test-results/
playwright-report/
if-no-files-found: ignore

db-reset-verify:
name: Migration replay
needs: changes
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"test:e2e:critical": "node scripts/run-playwright.mjs --project=chromium --grep @critical",
"test:e2e:regression": "node scripts/run-playwright.mjs --project=chromium --grep-invert \"@critical|@quarantine\"",
"test:e2e:quarantine": "node scripts/run-playwright.mjs --project=chromium --grep @quarantine --pass-with-no-tests",
"test:e2e:chromium": "node scripts/run-playwright.mjs --project=chromium",
"test:e2e:mockups": "node scripts/run-playwright.mjs --project=chromium-mockups",
"test:e2e:chromium": "node scripts/run-playwright.mjs --project=chromium --project=chromium-mockups",
"test:e2e:visual": "node scripts/run-playwright.mjs --config=playwright.visual.config.ts",
"verify:cheap": "npm run check:runtime && npm run check:github-actions && npm run sitemap:check && npm run brand:check && npm run check:type-scale && npm run check:icon-scale && npm run lint && npm run typecheck && npm run test",
"verify:pr-local": "node scripts/verify-pr-local.mjs",
Expand Down
19 changes: 19 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ const baseURL = getPlaywrightBaseUrl();
// browser downloads; point this at that binary instead of the managed one.
const chromiumExecutablePath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH;

// Prototype /mockups journeys live in their own advisory project so a red
// mockup can never mask a production-journey regression (PT-05). The two
// Tag-level filters keep production and prototype journeys disjoint even when
// they share a spec file; firefox/webkit retain the legacy full testMatch.
const productionSpecPattern = /.*ui-(smoke|stress|accessibility|tools|overlap|universal-search)\.spec\.ts/;
Comment thread
BigSimmo marked this conversation as resolved.
const mockupSpecPattern = /.*ui-(tools|tools-collapse|tools-task-directory)\.spec\.ts/;
const mockupTag = /@mockup/;

export default defineConfig({
testDir: "./tests",
testMatch:
Expand All @@ -27,6 +35,17 @@ export default defineConfig({
projects: [
{
name: "chromium",
testMatch: productionSpecPattern,
grepInvert: mockupTag,
use: {
...devices["Desktop Chrome"],
...(chromiumExecutablePath ? { launchOptions: { executablePath: chromiumExecutablePath } } : {}),
},
},
{
name: "chromium-mockups",
testMatch: mockupSpecPattern,
grep: mockupTag,
use: {
...devices["Desktop Chrome"],
...(chromiumExecutablePath ? { launchOptions: { executablePath: chromiumExecutablePath } } : {}),
Expand Down
15 changes: 10 additions & 5 deletions tests/ui-smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1886,8 +1886,16 @@ test.describe("Clinical KB UI smoke coverage", () => {

const medicationLink = strip.getByRole("link", { name: "Clozapine", exact: true });
await expect(medicationLink).toHaveAttribute("href", "/medications/clozapine");
await medicationLink.click();
await expect(page).toHaveURL(/\/medications\/clozapine/, { timeout: 15_000 });
// Re-click on each retry: a single click can be swallowed while the answer
// surface is still hydrating (same guard as the launcher mode switches), and
// app-router URLs only commit after the destination responds — a cold dev
// compile of /medications/[slug] can take ~30s when this spec runs without
// the @critical warm-up journeys.
await expect(async () => {
if (/\/medications\/clozapine/.test(page.url())) return;
await medicationLink.click();
await expect(page).toHaveURL(/\/medications\/clozapine/, { timeout: 5_000 });
}).toPass({ timeout: 45_000 });
await expectNoPageHorizontalOverflow(page);
});

Expand Down Expand Up @@ -2419,9 +2427,6 @@ test.describe("Clinical KB UI smoke coverage", () => {
await expectSingleMedicationPage(page);
await expect(page.getByRole("link", { name: "Back to medication search" })).toBeVisible();

await gotoApp(page, "/mockups/medication-prescribing");
await expect(page).toHaveURL(/\/medications\/acamprosate$/);
await expectSingleMedicationPage(page);
expect(parentNodeErrors).toEqual([]);
});

Expand Down
2 changes: 1 addition & 1 deletion tests/ui-tools-collapse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function mockupSearch(page: Page) {
return page.locator("#main-content").getByRole("searchbox").first();
}

test.describe("Tools mockups collapse the primary region when filtering", () => {
test.describe("Tools mockups collapse the primary region when filtering @mockup", () => {
test.describe.configure({ timeout: 60_000 });

test("command center hides Start here and avoids a populated grid over an empty state", async ({ page }) => {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui-tools-task-directory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async function expectNoHorizontalOverflow(page: Page) {
expect(overflow).toBeLessThanOrEqual(2);
}

test.describe("Tools task directory mockup (Concept 4)", () => {
test.describe("Tools task directory mockup (Concept 4) @mockup", () => {
test.describe.configure({ timeout: 60_000 });

test("renders task-grouped rows and is reachable", async ({ page }) => {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui-tools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1514,7 +1514,7 @@ test.describe("Responsive layout guards", () => {
] as const;

for (const route of mockupRoutes) {
test(`${route.name} never overflows horizontally across sizes`, async ({ page }) => {
test(`${route.name} never overflows horizontally across sizes @mockup`, async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 900 });
await gotoLauncher(page, route.path);
await expect(page.getByRole("heading", { level: 1 }).first()).toBeVisible();
Expand Down