diff --git a/web/src/routes/collection/collection.test.ts b/web/src/routes/collection/collection.test.ts
index 96623b25..a0e4ff13 100644
--- a/web/src/routes/collection/collection.test.ts
+++ b/web/src/routes/collection/collection.test.ts
@@ -3,22 +3,31 @@ import { render, screen } from '@testing-library/svelte';
import { createRawSnippet } from 'svelte';
import { mockInfiniteQuery } from '../../test-utils/query';
import { pageUrlModule } from '#test-utils/mocks/appState.js';
-import type { WantArtistsPage, WantCounts } from '#lib/api/types.js';
+import { mockQuery } from '../../test-utils/query';
+import type { AdminSummary, WantArtistsPage, WantCounts } from '#lib/api/types.js';
const state = vi.hoisted(() => ({ pageUrl: new URL('http://localhost/collection') }));
vi.mock('$app/state', () => pageUrlModule(state));
vi.mock('#lib/api/wanted.js', () => ({ createWantArtistsQuery: vi.fn() }));
+vi.mock('#lib/api/admin.js', () => ({ createAdminSummaryQuery: vi.fn() }));
import WantedLayout from './+layout.svelte';
import { createWantArtistsQuery } from '#lib/api/wanted.js';
+import { createAdminSummaryQuery } from '#lib/api/admin.js';
const counts: WantCounts = { artists: 7, wanted: 5, missing: 1, held: 9, upgrades: 6, following: 2, activity: 3, attention: 0, blocked: 4, unmatched: 3 };
const children = createRawSnippet(() => ({ render: () => `
body
` }));
afterEach(() => vi.clearAllMocks());
-function setup(c: WantCounts = counts, path = '/collection') {
+const none = { count: 0, since: null };
+const clear: AdminSummary = { requests: none, reports: none, duplicates: none, suspect: none, missing: none };
+
+function setup(c: WantCounts = counts, path = '/collection', summary: AdminSummary = clear) {
state.pageUrl = new URL(`http://localhost${path}`);
+ vi.mocked(createAdminSummaryQuery).mockReturnValue(
+ mockQuery({ data: summary }) as unknown as ReturnType
+ );
const page: WantArtistsPage = {
items: [],
total: 0,
@@ -47,7 +56,10 @@ describe('/collection layout', () => {
['/collection/activity', 'Activity 3'],
['/collection/following', 'Following 2'],
['/collection/blocked', 'Blocked 4'],
- ['/collection/unmatched', 'Unmatched 3']
+ ['/collection/unmatched', 'Unmatched 3'],
+ ['/collection/reports', 'Reports'],
+ ['/collection/duplicates', 'Duplicates'],
+ ['/collection/suspect-sources', 'Suspect sources']
]);
expect(screen.getByRole('link', { name: /activity/i })).toHaveAttribute('aria-current', 'page');
expect(screen.getByRole('link', { name: /^catalogue/i })).not.toHaveAttribute('aria-current');
@@ -76,6 +88,33 @@ describe('/collection layout', () => {
expect(screen.getByRole('link', { name: /see unmatched/i }).getAttribute('href')).toBe('/collection/unmatched');
});
+ // The library's health queues moved here from Admin (M504): their counts
+ // come from the admin summary, accented, and a line names the ones waiting.
+ test('reports, duplicates and suspect sources carry what waits on you, and a line links to each', () => {
+ setup(counts, '/collection', {
+ ...clear,
+ reports: { count: 2, since: null },
+ duplicates: { count: 5, since: null },
+ missing: { count: 9, since: null }
+ });
+ const nav = screen.getByRole('navigation', { name: /collection sections/i });
+ const dup = Array.from(nav.querySelectorAll('a')).find((a) => a.getAttribute('href') === '/collection/duplicates')!;
+ expect(dup.textContent?.replace(/\s+/g, ' ').trim()).toBe('Duplicates 5');
+ const health = screen.getByTestId('collection-health');
+ expect(health.textContent?.replace(/\s+/g, ' ').trim()).toBe(
+ 'To look at: 2 broken tracks reported, 5 sets of duplicates to review.'
+ );
+ expect(Array.from(health.querySelectorAll('a')).map((a) => a.getAttribute('href'))).toEqual([
+ '/collection/reports',
+ '/collection/duplicates'
+ ]);
+ });
+
+ test('with nothing waiting there is no health line', () => {
+ setup();
+ expect(screen.queryByTestId('collection-health')).toBeNull();
+ });
+
test("an artist's page sits under Catalogue", () => {
setup(counts, '/collection/artist/blur');
expect(screen.getByRole('link', { name: /^catalogue/i })).toHaveAttribute('aria-current', 'page');
diff --git a/web/src/routes/admin/duplicates/+page.svelte b/web/src/routes/collection/duplicates/+page.svelte
similarity index 99%
rename from web/src/routes/admin/duplicates/+page.svelte
rename to web/src/routes/collection/duplicates/+page.svelte
index c83c4679..38cb6aa7 100644
--- a/web/src/routes/admin/duplicates/+page.svelte
+++ b/web/src/routes/collection/duplicates/+page.svelte
@@ -187,7 +187,7 @@
}
-{pageTitle('Admin · Duplicates')}
+{pageTitle('Collection · Duplicates')}
diff --git a/web/src/routes/admin/duplicates/duplicates.test.ts b/web/src/routes/collection/duplicates/duplicates.test.ts
similarity index 100%
rename from web/src/routes/admin/duplicates/duplicates.test.ts
rename to web/src/routes/collection/duplicates/duplicates.test.ts
diff --git a/web/src/routes/admin/reports/+page.svelte b/web/src/routes/collection/reports/+page.svelte
similarity index 99%
rename from web/src/routes/admin/reports/+page.svelte
rename to web/src/routes/collection/reports/+page.svelte
index b4648356..688a60dd 100644
--- a/web/src/routes/admin/reports/+page.svelte
+++ b/web/src/routes/collection/reports/+page.svelte
@@ -147,7 +147,7 @@
const plural = (n: number, one: string, many: string) => `${n} ${n === 1 ? one : many}`;
-{pageTitle('Admin · Reports')}
+{pageTitle('Collection · Reports')}
diff --git a/web/src/routes/admin/reports/reports.test.ts b/web/src/routes/collection/reports/reports.test.ts
similarity index 99%
rename from web/src/routes/admin/reports/reports.test.ts
rename to web/src/routes/collection/reports/reports.test.ts
index a10201e9..6cc9c4cb 100644
--- a/web/src/routes/admin/reports/reports.test.ts
+++ b/web/src/routes/collection/reports/reports.test.ts
@@ -63,7 +63,7 @@ function setup(q: AdminQuarantineRow[], e: AdminPlaybackError[], opts: { error?:
return render(Page);
}
-describe('/admin/reports', () => {
+describe('/collection/reports', () => {
test('a flagged track and a track that failed to play are one list, one row per track, newest first', () => {
setup([flagged], [failure('e1', 't1', 'Roygbiv', 1), failure('e2', 't2', 'Intro', 9)]);
const rows = screen.getAllByTestId('report-row');
diff --git a/web/src/routes/admin/suspect-sources/+page.svelte b/web/src/routes/collection/suspect-sources/+page.svelte
similarity index 99%
rename from web/src/routes/admin/suspect-sources/+page.svelte
rename to web/src/routes/collection/suspect-sources/+page.svelte
index d1dfc53f..7e2192fc 100644
--- a/web/src/routes/admin/suspect-sources/+page.svelte
+++ b/web/src/routes/collection/suspect-sources/+page.svelte
@@ -82,7 +82,7 @@
}
-{pageTitle('Admin · Suspect sources')}
+{pageTitle('Collection · Suspect sources')}
diff --git a/web/src/routes/admin/suspect-sources/suspect-sources.test.ts b/web/src/routes/collection/suspect-sources/suspect-sources.test.ts
similarity index 100%
rename from web/src/routes/admin/suspect-sources/suspect-sources.test.ts
rename to web/src/routes/collection/suspect-sources/suspect-sources.test.ts