test(e2e): realign mocks + assertions with the current API and OCR-era UI

The route-mocked E2E specs had drifted from the app they exercise, so
each rendered an empty/500 page and timed out on missing elements:

- Manga detail + reader loads now also fetch read-progress/{id} and
  /similar; several specs never mocked them, so the load's Promise.all
  rejected. Add the mocks and fill the manga fixtures out to a real
  MangaDetail (status/alt_titles/authors/genres/tags/... ) so the
  components stop throwing mid-render.
- back-nav + library: widen `read-progress*`/`page-tags` globs — `*`
  doesn't cross `/`, so `/me/read-progress/{id}` fell through to the
  proxy and 500'd; mock the library page-tags pair too.
- manga-list search: wait for the initial load to settle before
  searching so the search fetch can't race the mount-time load.
- reader-chapter-select: assert the shared `chapterLabel` output.
- admin-analysis: the active OCR backend extracts text only, so the
  detail modal shows OCR (no tags/NSFW) and metrics shows tiles +
  trend charts (no by-model) — assert what the OCR-era UI renders.
- profile: assert the wrong-password 401 stays inline (guards the
  fix in the preceding commit) and mock the guest bookmark/collection
  fetches the /profile load maps to the sign-in prompt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-01 20:11:23 +02:00
parent 0865659ab3
commit 4d02b56b77
8 changed files with 206 additions and 22 deletions

View File

@@ -196,6 +196,21 @@ async function mockAdmin(page: Page, cap: Captured) {
})
})
);
// The metrics tab also pulls a per-bucket time series for the trend
// charts. Registered after the aggregate `metrics**` route so it wins
// for the more specific `/metrics/series` path.
await page.route('**/api/v1/admin/analysis/metrics/series**', (r) =>
r.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
buckets: [
{ t: '2026-06-12T00:00:00Z', n: 1600, ok: 1580, failed: 20, avg_ms: 2300 },
{ t: '2026-06-13T00:00:00Z', n: 1604, ok: 1579, failed: 25, avg_ms: 2500 }
]
})
})
);
// Default: keep the SSE connection pending (no events) so tests that
// don't care about live updates don't trigger reconnect churn. The
@@ -264,11 +279,12 @@ test.describe('/admin/analysis', () => {
await expect(
page.getByTestId('admin-analysis-detail-status')
).toContainText('Analyzed');
await expect(modal).toContainText('model test-model');
// OCR-only backend: the detail surfaces the extracted OCR lines (with
// their kind) — tags / scene / NSFW belong to the dormant vision
// backend and are intentionally not shown here.
await expect(modal).toContainText('OCR text');
await expect(modal).toContainText('Hello there');
await expect(modal).toContainText('action');
await expect(page.getByTestId('admin-analysis-detail-warnings')).toContainText(
'gore'
);
});
test('live SSE events drive the indicator and activity ticker', async ({
@@ -367,8 +383,9 @@ test.describe('/admin/analysis', () => {
await page.getByTestId('admin-analysis-tab-history').click();
const row = page.getByTestId(`analysis-history-row-${pageDone}`);
await expect(row).toContainText('Berserk · Ch 1 · p1');
await expect(row).toContainText('NSFW');
await expect(row).toContainText('test-model'); // model column
await expect(row).toContainText('2.4s'); // duration column
// (No NSFW flag: the active OCR backend extracts text only.)
await row.click();
await expect(page.getByTestId('admin-analysis-detail')).toBeVisible();
@@ -377,16 +394,22 @@ test.describe('/admin/analysis', () => {
);
});
test('metrics tab shows aggregate timing + by-model', async ({ page }) => {
test('metrics tab shows aggregate timing + trend charts', async ({ page }) => {
const cap: Captured = { reenqueue: null, analyzeCalls: 0 };
await mockAdmin(page, cap);
await page.setViewportSize(DESKTOP);
await page.goto('/admin/analysis');
await page.getByTestId('admin-analysis-tab-metrics').click();
// Aggregate tiles for the selected window.
await expect(page.getByTestId('analysis-metrics-n')).toContainText('3204');
await expect(page.getByTestId('analysis-metrics-avg')).toContainText('2.4s');
await expect(page.getByTestId('analysis-metrics')).toContainText('qwen2-vl-7b');
const metrics = page.getByTestId('analysis-metrics');
await expect(metrics).toContainText('Success');
await expect(metrics).toContainText('99%'); // 3159/3204
await expect(metrics).toContainText('45'); // failed
// The per-bucket trend charts render from the series endpoint.
await expect(page.getByTestId('analysis-metrics-charts')).toBeVisible();
});
test('queue an unanalyzed page from its detail modal', async ({ page }) => {