diff --git a/e2e/semantic-search.spec.ts b/e2e/semantic-search.spec.ts index 99298648..fa219550 100644 --- a/e2e/semantic-search.spec.ts +++ b/e2e/semantic-search.spec.ts @@ -1,23 +1,34 @@ import { test, expect } from '@playwright/test'; import { navClick } from './helpers/navigation'; +const SEMANTIC_FALLBACK_ASSERTION_TIMEOUT_MS = 10_000; +const SEMANTIC_FALLBACK_TEST_TIMEOUT_MS = 15_000; + /** * Semantic search (N1, Issue #751) — UI-level coverage only. * - * No model download happens here: the spec aborts requests to the Hugging - * Face Hub and the transformers.js WASM CDN, which forces the graceful + * No model download happens here: the spec returns a deterministic 404 for + * Hugging Face Hub and transformers.js WASM CDN requests to force the graceful * lexical fallback path with its surfaced hint. Ranking behavior itself is * covered by the mocked unit tests (embeddings/vector-store/worker). */ test.describe('Semantic search toggle', () => { + // Prevent the app service worker from forwarding model requests outside the + // Playwright route handler. + test.use({ serviceWorkers: 'block' }); + test.beforeEach(async ({ page }) => { - // Block the embedding model and WASM binaries so the embedder cannot - // load — semantic queries then degrade to the lexical fallback hint. - // Subdomains matter: the model files live on cdn-lfs.huggingface.co and - // the tokenizer/assets may come from jsdelivr subdomains. + // Use an immediate missing-asset response instead of relying on browser + // network abort/retry scheduling. Subdomains matter: model files live on + // cdn-lfs.huggingface.co and tokenizer/assets may come from jsdelivr. await page.route( - /^https:\/\/(?:[a-z0-9-]+\.)*(?:huggingface\.co|cdn\.jsdelivr\.net)\//, - (route) => route.abort(), + /^https:\/\/(?:[a-z0-9-]+\.)*(?:huggingface\.co|cdn\.hf\.co|cdn\.jsdelivr\.net)\//, + (route) => + route.fulfill({ + status: 404, + contentType: 'text/plain', + body: 'Model unavailable in E2E', + }), ); await page.goto('/'); await navClick(page, /library/i); @@ -43,19 +54,19 @@ test.describe('Semantic search toggle', () => { test('surfaces a lexical fallback hint when the semantic model is unavailable', async ({ page, }) => { + test.setTimeout(SEMANTIC_FALLBACK_TEST_TIMEOUT_MS); + await page.getByRole('switch', { name: /semantic search/i }).click(); - await page.getByRole('searchbox', { name: /search library/i }).fill('triz'); + const searchInput = page.getByRole('searchbox', { name: /search library/i }); + await searchInput.fill('triz'); const status = page.getByRole('status').filter({ hasText: /keyword results/i }); - // transformers.js spends several seconds failing its CDN fetches (model - // + WASM) before surfacing the embedder error that drives the fallback, and a - // full parallel sweep stretches that well past 20s — it failed on both the - // attempt and the retry of one four-project run while passing 9/9 in - // isolation (plans/149). The budget is deliberately generous because this - // assertion waits on a failing network stack, not on app logic. - await expect(status).toBeVisible({ timeout: 60_000 }); - - // The search box keeps working — results still render (graceful fallback). - await expect(page.getByRole('searchbox', { name: /search library/i })).toHaveValue('triz'); + await expect(status).toBeVisible({ + timeout: SEMANTIC_FALLBACK_ASSERTION_TIMEOUT_MS, + }); + await expect(searchInput).toHaveValue('triz'); + await expect( + page.getByRole('heading', { name: 'TRIZ Contradiction Matrix' }), + ).toBeVisible(); }); }); \ No newline at end of file diff --git a/plans/149-nightly-full-viewport-e2e-2026-09-24.md b/plans/149-nightly-full-viewport-e2e-2026-09-24.md index 353cafd1..8b4efeb2 100644 --- a/plans/149-nightly-full-viewport-e2e-2026-09-24.md +++ b/plans/149-nightly-full-viewport-e2e-2026-09-24.md @@ -182,16 +182,19 @@ have used. ## 5. Follow-ups -1. **The next real nightly should be confirmed.** The dispatch run proves the - mechanism; the 03:00 UTC schedule run is the last piece. If it reports - `E2E Tests: skipped` again, the cause is a dependency this plan did not see. - → **Explained and hardened** (plans/151 §2). The `2026-09-24T07:59Z` scheduled - run skipped every job because its head (`064702a`) predates this plan's fix; - the schedule runs the workflow as it exists on the default branch at trigger - time. The same investigation found the nightly's scope depended on the path - filter's incidental fallback, which plans/151 replaced with an explicit - `schedule`/`workflow_dispatch` guard. The next nightly is the first scheduled - execution of the fixed workflow. +1. **Nightly execution — confirmed 2026-09-25.** Scheduled run + [`36112610311`](https://github.com/d-oit/do-knowledge-studio/actions/runs/36112610311) + on `main` (`094b7e0`) ran both `Unit Tests` and `E2E Tests`. E2E completed + `604` tests (`600 passed`, `4 skipped`) in `13.6m` across chromium, mobile, + tablet, and desktop-xl. This confirms the scheduled job runs; the run used + `main` before this local semantic-search test change and does not validate + that edit. + → **Explained and hardened** (plans/151 §2). The `2026-09-24T07:59Z` + scheduled run skipped every job because its head (`064702a`) predates this + plan's fix; the schedule runs the workflow as it exists on the default + branch at trigger time. The same investigation found the nightly's scope + depended on the path filter's incidental fallback, which plans/151 replaced + with an explicit `schedule`/`workflow_dispatch` guard. 2. **Pre-hydration interaction audit — closed at the helpers (2026-09-24).** The exposure was measured across all 24 specs rather than patched per spec: - `openNavIfHidden` (and therefore `navClick`) now waits for `data-app-ready` diff --git a/plans/151-quality-gate-lint-cache-and-nightly-guard-2026-09-24.md b/plans/151-quality-gate-lint-cache-and-nightly-guard-2026-09-24.md index 49463322..39c91929 100644 --- a/plans/151-quality-gate-lint-cache-and-nightly-guard-2026-09-24.md +++ b/plans/151-quality-gate-lint-cache-and-nightly-guard-2026-09-24.md @@ -116,18 +116,28 @@ output and deleting the `frontend=true` emission each fail the test. (`ci-and-labels.yml`, "Generate coverage badge"). Pre-existing and style-level (`actionlint` runs with `fail_level: error`), so it does not fail CI; it is the last finding in that file and unrelated to this change. -2. **Confirm the next nightly** — the first scheduled execution of the guarded - workflow. `gh api "repos/d-oit/do-knowledge-studio/actions/runs?event=schedule"` - and check `E2E Tests` is not `skipped`. - → **Mechanism proven on `main`** (dispatch run - [`36048842590`](https://github.com/d-oit/do-knowledge-studio/actions/runs/36048842590), - head `55033bf`): `Treat every path as changed on scheduled and manual runs` - ran, **Unit Tests ran** instead of being skipped, and `E2E Tests` swept - `604 tests` with `600 passed` in 9.9 min across all four projects. The guarded - path and the scheduled path differ only in `github.event_name`, which the - contract test pins — the remaining confirmation is the next 03:00 UTC - (observed ~08:00 UTC) scheduled run. -3. **`semantic-search.spec.ts` load sensitivity** (plans/148 §6.1) — unchanged. +2. **Confirm the next nightly** — closed 2026-09-25. Scheduled run + [`36112610311`](https://github.com/d-oit/do-knowledge-studio/actions/runs/36112610311) + on `main` (`094b7e0`) ran `Unit Tests` and `E2E Tests` successfully; E2E + completed `604` tests (`600 passed`, `4 skipped`) in `13.6m` across all four + projects. `Quality Gate`, `Build`, `Coverage Report`, and `Dependency Verify` + were skipped as configured; the workflow limits scheduled runs to unit and + E2E jobs; this confirmation came from the scheduled event, not a manual dispatch. +3. **`semantic-search.spec.ts` load sensitivity** (plans/148 §6.1) — resolved + in test code only. The spec blocks service workers (the app SW bypassed + Playwright routes) and returns deterministic `404`s for Hugging Face + Hub/CDN and jsDelivr model requests. The fallback uses a `10s` assertion + timeout inside a `15s` test timeout. Chromium repeat-each=3 passed all nine + test instances in `29.8s`; the latest-main four-project, zero-retry sweep + passed `600/604` (`4` skipped) in `8.8m`. + The nightly at `094b7e0` confirms the scheduled job runs but predates this + local edit; it does not validate the updated semantic-search spec. No + production semantic-search code changed. 4. **ESLint 10 workaround** (plans/140 §2) — still blocked upstream. 5. **Graph density** (plans/148 §6.3) — the remaining item from the same follow-up sweep; tracked in plans/152. +6. **HomeView hydration mismatch (E2E warning)** — React logs mismatched motion + styles during the local and scheduled sweeps (`opacity: "0"` vs `1`; progress + widths `0px` vs computed percentages). The same warning appears in the + pre-change manual run `36048842590`; track it separately rather than + suppressing it in the semantic-search test.