test(coverage): drive browser tests to 80% on all metrics (#496) #505
@@ -9,7 +9,10 @@ describe('admin/system page', () => {
|
|||||||
let fetchSpy: ReturnType<typeof vi.spyOn>;
|
let fetchSpy: ReturnType<typeof vi.spyOn>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
|
// mockImplementation (not mockResolvedValue) returns a fresh Response per call so the
|
||||||
|
// body stream isn't already-consumed after the first read.
|
||||||
|
fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(
|
||||||
|
async () =>
|
||||||
new Response(
|
new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
state: 'IDLE',
|
state: 'IDLE',
|
||||||
@@ -79,37 +82,34 @@ describe('admin/system page', () => {
|
|||||||
|
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
const btn = (await page
|
await page.getByRole('button', { name: /jetzt auffüllen/i }).click();
|
||||||
.getByRole('button', { name: /jetzt auffüllen/i })
|
|
||||||
.element()) as HTMLButtonElement;
|
|
||||||
btn.click();
|
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 50));
|
await vi.waitFor(() => {
|
||||||
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
||||||
expect(calls.some((c) => c.includes('backfill-versions'))).toBe(true);
|
expect(calls.some((c) => c.includes('backfill-versions'))).toBe(true);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('triggers file-hashes backfill when its button is clicked', async () => {
|
it('triggers file-hashes backfill when its button is clicked', async () => {
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
const btn = (await page
|
await page.getByRole('button', { name: /datei-hashes berechnen/i }).click();
|
||||||
.getByRole('button', { name: /datei-hashes berechnen/i })
|
|
||||||
.element()) as HTMLButtonElement;
|
|
||||||
btn.click();
|
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 50));
|
await vi.waitFor(() => {
|
||||||
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
||||||
expect(calls.some((c) => c.includes('backfill-file-hashes'))).toBe(true);
|
expect(calls.some((c) => c.includes('backfill-file-hashes'))).toBe(true);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('initial fetch loads import-status and thumbnail-status', async () => {
|
it('initial fetch loads import-status and thumbnail-status', async () => {
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 50));
|
await vi.waitFor(() => {
|
||||||
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
||||||
expect(calls.some((c) => c.includes('import-status'))).toBe(true);
|
expect(calls.some((c) => c.includes('import-status'))).toBe(true);
|
||||||
expect(calls.some((c) => c.includes('thumbnail-status'))).toBe(true);
|
expect(calls.some((c) => c.includes('thumbnail-status'))).toBe(true);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('shows the success banner after backfill versions completes', async () => {
|
it('shows the success banner after backfill versions completes', async () => {
|
||||||
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
||||||
@@ -128,14 +128,11 @@ describe('admin/system page', () => {
|
|||||||
|
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
const btn = (await page
|
await page.getByRole('button', { name: /jetzt auffüllen/i }).click();
|
||||||
.getByRole('button', { name: /jetzt auffüllen/i })
|
|
||||||
.element()) as HTMLButtonElement;
|
|
||||||
btn.click();
|
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
await vi.waitFor(() => {
|
||||||
const banner = document.querySelector('.bg-green-50');
|
expect(document.querySelector('.bg-green-50')).not.toBeNull();
|
||||||
expect(banner).not.toBeNull();
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the running state for import-status', async () => {
|
it('renders the running state for import-status', async () => {
|
||||||
@@ -155,9 +152,10 @@ describe('admin/system page', () => {
|
|||||||
|
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
await vi.waitFor(() => {
|
||||||
expect(document.body.textContent).toMatch(/läuft|wird ausgeführt/i);
|
expect(document.body.textContent).toMatch(/läuft|wird ausgeführt/i);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the DONE state with processed count for import-status', async () => {
|
it('renders the DONE state with processed count for import-status', async () => {
|
||||||
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
||||||
@@ -176,9 +174,10 @@ describe('admin/system page', () => {
|
|||||||
|
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
await vi.waitFor(() => {
|
||||||
expect(document.body.textContent).toContain('99');
|
expect(document.body.textContent).toContain('99');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the FAILED state with the error message for thumbnail-status', async () => {
|
it('renders the FAILED state with the error message for thumbnail-status', async () => {
|
||||||
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
||||||
@@ -205,9 +204,10 @@ describe('admin/system page', () => {
|
|||||||
|
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
await vi.waitFor(() => {
|
||||||
expect(document.body.textContent).toContain('connection refused');
|
expect(document.body.textContent).toContain('connection refused');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the DONE state for thumbnail-status with retry button', async () => {
|
it('renders the DONE state for thumbnail-status with retry button', async () => {
|
||||||
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
||||||
@@ -234,9 +234,9 @@ describe('admin/system page', () => {
|
|||||||
|
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
await vi.waitFor(() => {
|
||||||
const banner = document.querySelector('[data-testid="thumbnails-status-done"]');
|
expect(document.querySelector('[data-testid="thumbnails-status-done"]')).not.toBeNull();
|
||||||
expect(banner).not.toBeNull();
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the FAILED state for import-status with retry button', async () => {
|
it('renders the FAILED state for import-status with retry button', async () => {
|
||||||
@@ -261,9 +261,10 @@ describe('admin/system page', () => {
|
|||||||
|
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
await vi.waitFor(() => {
|
||||||
expect(document.body.textContent).toContain('database error');
|
expect(document.body.textContent).toContain('database error');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the running thumbnail status with progress count', async () => {
|
it('renders the running thumbnail status with progress count', async () => {
|
||||||
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
||||||
@@ -290,34 +291,41 @@ describe('admin/system page', () => {
|
|||||||
|
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
await vi.waitFor(() => {
|
||||||
// Total 100, processed+skipped+failed = 36
|
// Total 100, processed+skipped+failed = 36 — at least one of these surfaces.
|
||||||
expect(document.body.textContent).toMatch(/36|100/);
|
expect(document.body.textContent).toMatch(/36|100/);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('triggers thumbnail backfill when its button is clicked', async () => {
|
it('triggers thumbnail backfill when its button is clicked', async () => {
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 50));
|
await vi.waitFor(() => {
|
||||||
|
expect(document.querySelector('[data-thumbnails-trigger]')).not.toBeNull();
|
||||||
|
});
|
||||||
const btns = Array.from(document.querySelectorAll('[data-thumbnails-trigger]'));
|
const btns = Array.from(document.querySelectorAll('[data-thumbnails-trigger]'));
|
||||||
(btns[0] as HTMLButtonElement)?.click();
|
(btns[0] as HTMLButtonElement).click();
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 50));
|
await vi.waitFor(() => {
|
||||||
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
||||||
expect(calls.some((c) => c.includes('generate-thumbnails'))).toBe(true);
|
expect(calls.some((c) => c.includes('generate-thumbnails'))).toBe(true);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('triggers import when import button is clicked from idle state', async () => {
|
it('triggers import when import button is clicked from idle state', async () => {
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 50));
|
await vi.waitFor(() => {
|
||||||
|
expect(document.querySelector('[data-import-trigger]')).not.toBeNull();
|
||||||
|
});
|
||||||
const btns = Array.from(document.querySelectorAll('[data-import-trigger]'));
|
const btns = Array.from(document.querySelectorAll('[data-import-trigger]'));
|
||||||
(btns[0] as HTMLButtonElement)?.click();
|
(btns[0] as HTMLButtonElement).click();
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 50));
|
await vi.waitFor(() => {
|
||||||
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
const calls = fetchSpy.mock.calls.map((c) => c[0].toString());
|
||||||
expect(calls.some((c) => c.includes('trigger-import'))).toBe(true);
|
expect(calls.some((c) => c.includes('trigger-import'))).toBe(true);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the running thumbnail status without progress when total is 0', async () => {
|
it('renders the running thumbnail status without progress when total is 0', async () => {
|
||||||
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
fetchSpy.mockImplementation(async (url: RequestInfo | URL) => {
|
||||||
@@ -344,8 +352,8 @@ describe('admin/system page', () => {
|
|||||||
|
|
||||||
render(AdminSystemPage, { props: {} });
|
render(AdminSystemPage, { props: {} });
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
await vi.waitFor(() => {
|
||||||
// running state shown but no progress count when total=0
|
|
||||||
expect(document.body.textContent).toMatch(/läuft|wird|generier/i);
|
expect(document.body.textContent).toMatch(/läuft|wird|generier/i);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user