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:
@@ -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 }) => {
|
||||
|
||||
Reference in New Issue
Block a user