From c031139b89dcc8962b38ead078aff7b4653adf54 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 22 Jan 2026 06:50:04 -0600 Subject: [PATCH] test(app): model picker smoke test --- packages/app/e2e/model-picker.spec.ts | 43 +++++++++++++++++++++++++++ specs/08-app-e2e-smoke-suite.md | 13 ++++---- 2 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 packages/app/e2e/model-picker.spec.ts diff --git a/packages/app/e2e/model-picker.spec.ts b/packages/app/e2e/model-picker.spec.ts new file mode 100644 index 000000000..9e64b3dfb --- /dev/null +++ b/packages/app/e2e/model-picker.spec.ts @@ -0,0 +1,43 @@ +import { test, expect } from "./fixtures" +import { promptSelector } from "./utils" + +test("smoke model selection updates prompt footer", async ({ page, gotoSession }) => { + await gotoSession() + + await page.locator(promptSelector).click() + await page.keyboard.type("/model") + + const command = page.locator('[data-slash-id="model.choose"]') + await expect(command).toBeVisible() + await command.hover() + + await page.keyboard.press("Enter") + + const dialog = page.getByRole("dialog") + await expect(dialog).toBeVisible() + + const input = dialog.getByRole("textbox").first() + + const selected = dialog.locator('[data-slot="list-item"][data-selected="true"]').first() + await expect(selected).toBeVisible() + + const other = dialog.locator('[data-slot="list-item"]:not([data-selected="true"])').first() + const target = (await other.count()) > 0 ? other : selected + + const key = await target.getAttribute("data-key") + if (!key) throw new Error("Failed to resolve model key from list item") + + const name = (await target.locator("span").first().innerText()).trim() + const model = key.split(":").slice(1).join(":") + + await input.fill(model) + + const item = dialog.locator(`[data-slot="list-item"][data-key="${key}"]`) + await expect(item).toBeVisible() + await item.click() + + await expect(dialog).toHaveCount(0) + + const form = page.locator(promptSelector).locator("xpath=ancestor::form[1]") + await expect(form.locator('[data-component="button"]').filter({ hasText: name }).first()).toBeVisible() +}) diff --git a/specs/08-app-e2e-smoke-suite.md b/specs/08-app-e2e-smoke-suite.md index 3447c18c0..1a7a7a7dc 100644 --- a/specs/08-app-e2e-smoke-suite.md +++ b/specs/08-app-e2e-smoke-suite.md @@ -24,7 +24,7 @@ Add 6 smoke tests to `packages/app/e2e/`: - [x] 1. Settings dialog open / switch / close (`packages/app/e2e/settings.spec.ts`) - [x] 2. Prompt slash command path: `/open` opens file picker (`packages/app/e2e/prompt-slash-open.spec.ts`) - [x] 3. Prompt @mention inserts a file pill token (`packages/app/e2e/prompt-mention.spec.ts`) -- [ ] 4. Model selection UI works end-to-end +- [x] 4. Model selection UI works end-to-end (`packages/app/e2e/model-picker.spec.ts`) - [ ] 5. File viewer renders real file content - [ ] 8. Terminal init + create new terminal @@ -162,10 +162,11 @@ Steps: 1. `await gotoSession()`. 2. Focus prompt, type `/model`, press `Enter`. -3. In the model dialog, use the search field to filter to a deterministic model, e.g. `qwen3-coder`. -4. Select the first matching model. -5. Assert dialog closed. -6. Assert the prompt footer now shows the chosen model name. +3. In the model dialog, pick a visible model that is not the current selection (if available). +4. Use the search field to filter to that model (use its id from the list item's `data-key` to avoid time-based model visibility drift). +5. Select the filtered model. +6. Assert dialog closed. +7. Assert the prompt footer now shows the chosen model name. Acceptance criteria: @@ -217,7 +218,7 @@ These tests run with `fullyParallel: true` in `packages/app/playwright.config.ts - Avoid ordering-based assertions: never assume a “first” session/project/file is stable unless you filtered by unique text. - Prefer deterministic targets: - use `packages/app/package.json` rather than bare `package.json` (multiple hits possible) - - filter model search to a specific id/name (e.g. `qwen3-coder`) + - for models, avoid hardcoding a single model id; pick from the visible list and filter by its `data-key` instead - Prefer robust selectors: - role selectors: `getByRole('dialog')`, `getByRole('textbox')`, `getByRole('tab')` - stable data attributes already present: `promptSelector`, `[data-component="terminal"]`