From 9fee0d011136f9a28e00fbbf40cbc83341a946bc Mon Sep 17 00:00:00 2001 From: Les Orchard Date: Tue, 15 Sep 2026 15:59:29 -0700 Subject: [PATCH 1/2] fix(extension): repair rotted e2e sidepanel selectors All 4 e2e tests failed because their selectors described an older UI. The sidepanel itself renders correctly. - `getByText("Pilo Settings")` never matched: the header renders "Tabstack Pilo" (h1) and the view renders "Settings" (h2) as separate nodes, so no single node contains that string. - `getByText("Back to Chat")` never matched: the button is labelled "Back to chat", and getByText is case-sensitive. - `getByTestId("settings-button")` never matched: no such testid exists. The button is an icon button identified by its aria-label, "Open settings". Switched to getByRole throughout, which is both accurate and less brittle than matching raw copy. Also prettier-ignore the playwright artifact directories. They are already gitignored, but prettier still walks them, so running the e2e suite locally left `format:check` failing on playwright-report/index.html and test-results/.last-run.json. Verified: 4 passed on three consecutive runs (14-24s each). Refs #689. --- .prettierignore | 5 +++++ packages/extension/e2e/sidepanel.spec.ts | 16 ++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.prettierignore b/.prettierignore index 59779f7e..c7f566f0 100644 --- a/.prettierignore +++ b/.prettierignore @@ -19,5 +19,10 @@ packages/core/schemas/ # WXT framework auto-generated files packages/extension/.wxt/ +# Playwright e2e artifacts (gitignored, but prettier still walks them, +# so a local e2e run would otherwise fail `format:check`) +packages/extension/playwright-report/ +packages/extension/test-results/ + # Dependencies node_modules/ \ No newline at end of file diff --git a/packages/extension/e2e/sidepanel.spec.ts b/packages/extension/e2e/sidepanel.spec.ts index 62f5f79d..17fe4423 100644 --- a/packages/extension/e2e/sidepanel.spec.ts +++ b/packages/extension/e2e/sidepanel.spec.ts @@ -11,7 +11,7 @@ test.describe("Sidepanel", () => { await page.waitForLoadState("networkidle"); // Verify settings page is shown (extension auto-redirects when no API key) - await expect(page.getByText("Pilo Settings")).toBeVisible(); + await expect(page.getByRole("heading", { name: "Settings" })).toBeVisible(); await expect(page.getByText("API Key")).toBeVisible(); await expect(page.getByText("Save Settings")).toBeVisible(); }); @@ -24,8 +24,8 @@ test.describe("Sidepanel", () => { await page.goto(sidepanelUrl); await page.waitForLoadState("networkidle"); - // Click "Back to Chat" button - await page.getByText("Back to Chat").click(); + // Click the "Back to chat" button + await page.getByRole("button", { name: "Back to chat" }).click(); // Wait for chat view to appear await page.waitForLoadState("networkidle"); @@ -45,7 +45,7 @@ test.describe("Sidepanel", () => { await expect(sendButton).toBeDisabled(); // Verify settings button is visible - const settingsButton = page.getByTestId("settings-button"); + const settingsButton = page.getByRole("button", { name: "Open settings" }); await expect(settingsButton).toBeVisible(); }); @@ -55,7 +55,7 @@ test.describe("Sidepanel", () => { await page.waitForLoadState("networkidle"); // Navigate to chat view - await page.getByText("Back to Chat").click(); + await page.getByRole("button", { name: "Back to chat" }).click(); await page.waitForLoadState("networkidle"); const taskInput = page.getByTestId("task-input"); @@ -77,15 +77,15 @@ test.describe("Sidepanel", () => { await page.waitForLoadState("networkidle"); // Navigate to chat view first - await page.getByText("Back to Chat").click(); + await page.getByRole("button", { name: "Back to chat" }).click(); await page.waitForLoadState("networkidle"); // Click settings button - const settingsButton = page.getByTestId("settings-button"); + const settingsButton = page.getByRole("button", { name: "Open settings" }); await settingsButton.click(); // Should be back on settings page - await expect(page.getByText("Pilo Settings")).toBeVisible(); + await expect(page.getByRole("heading", { name: "Settings" })).toBeVisible(); await expect(page.getByText("API Key")).toBeVisible(); }); }); From 032ceab74c7649531aebbdfb7679c293fd9a8bd9 Mon Sep 17 00:00:00 2001 From: Les Orchard Date: Tue, 15 Sep 2026 15:59:37 -0700 Subject: [PATCH 2/2] ci: call the e2e script that actually exists The extension job ran `test:e2e:headless:chrome`, which is not a script in packages/extension/package.json. `pnpm --filter run ` exits 0 with only a warning, so the step has been reporting green while running nothing: $ pnpm --filter pilo-extension run test:e2e:headless:chrome None of the selected packages has a "test:e2e:headless:chrome" script $ echo $? 0 Sequenced after the spec repair on purpose: correcting this first would have turned the extension job red. Refs #689. --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 4b2a406b..2b224ce2 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -449,7 +449,7 @@ jobs: run: pnpm --filter pilo-extension exec playwright install-deps chromium - name: Run extension e2e tests - run: pnpm --filter pilo-extension run test:e2e:headless:chrome + run: pnpm --filter pilo-extension run test:e2e:headless - name: Upload Playwright report uses: actions/upload-artifact@v4