test: Add browser tests for the media viewing gallery and viewer (#2661)

This commit is contained in:
Dermot Duffy
2026-08-07 16:08:56 -07:00
committed by GitHub
parent 567690831c
commit 673d8fe133
25 changed files with 1563 additions and 144 deletions
+36 -6
View File
@@ -2,8 +2,13 @@ import { delay, http, HttpResponse } from 'msw';
import { setupWorker } from 'msw/browser';
import { beforeAll } from 'vitest';
import { createFixtureURL, SNAPSHOT_FIXTURE_FILENAME } from './fixtures';
const HTTP_OK = 200;
// Where this worker answers.
const TEST_MEDIA_PATH = '/test-media';
// Requests answered per token, so `responses` can be read as a sequence. One
// page runs one test file, so nothing here is shared with another file.
const requestCounts = new Map<string, number>();
@@ -12,7 +17,32 @@ const requestCounts = new Map<string, number>();
// the worker can answer cannot be built without it.
let inUse = false;
export const isTestMediaInUse = (): boolean => inUse;
/**
* See the worker below for what `responses` and `repeat` ask it to do.
*/
export const createTestMediaURL = (
responses: number[],
repeat = false,
filename: string = SNAPSHOT_FIXTURE_FILENAME,
): string => {
if (!inUse) {
throw new Error(
'Media that misbehaves must be served in a file using useTestMedia().',
);
}
return (
// TEST_MEDIA_PATH is intercepted by MSW to allow off-wire testing --
// without the potential for consuming connections which the browser might
// need for loading JS itself.
`${TEST_MEDIA_PATH}/${filename}?` +
new URLSearchParams({
token: crypto.randomUUID(),
responses: responses.join(','),
repeat: String(repeat),
}).toString()
);
};
/**
* Serves a fixture at `/test-media/<file>`, behaving as the query asks:
@@ -21,9 +51,9 @@ export const isTestMediaInUse = (): boolean => inUse;
* behaviour does not depend on what ran before it.
* responses The status to answer each request with, in order: `200` serves
* the file and anything else is sent as an empty error.
* repeat What to do once the `responses` list is exhausted:
* answer every request after it as the last one was, or never
* answer again (i.e. camera going quiet).
* repeat What to do once the `responses` list is exhausted: answer every
* request after it as the last one was, or never answer again
* (i.e. camera going quiet).
*
* Answered from within the page rather than by a server, because a request the
* page is still waiting on holds one of the handful of connections a browser
@@ -37,7 +67,7 @@ export const isTestMediaInUse = (): boolean => inUse;
* it would not.
*/
const worker = setupWorker(
http.get('/test-media/:file', async ({ request, params }) => {
http.get(`${TEST_MEDIA_PATH}/:file`, async ({ request, params }) => {
const url = new URL(request.url);
const token = url.searchParams.get('token');
if (!token) {
@@ -64,7 +94,7 @@ const worker = setupWorker(
// The fixture itself is served by the dev server. A name is sent rather
// than a path, so nothing outside the fixtures themselves is reachable.
const fixture = await fetch(`/tests/browser/fixtures/${String(params.file)}`);
const fixture = await fetch(createFixtureURL(String(params.file)));
return fixture.ok
? new HttpResponse(await fixture.arrayBuffer(), {