Files
josh 68616327ec
CI / checks (push) Successful in 5m59s
Add all eligible files to beta setup
2026-10-14 19:47:31 -04:00

164 lines
5.6 KiB
TypeScript

import { afterEach, beforeEach, expect, it, vi } from 'vitest';
import { flushSync, mount, unmount } from 'svelte';
import { writable } from 'svelte/store';
import BetaSetupPage from '../../src/routes/betas/new/+page.svelte';
const currentPage = writable({
url: new URL('http://localhost/betas/new'),
data: { user: { role: 'operator' } }
});
vi.mock('$app/stores', () => ({
page: {
subscribe: (run: Parameters<typeof currentPage.subscribe>[0]) => currentPage.subscribe(run)
}
}));
vi.mock('$app/navigation', () => ({ goto: vi.fn(), beforeNavigate: vi.fn() }));
vi.mock('../../src/lib/ui/stores/fleet.js', () => ({
fleetServers: writable([]),
fleetError: writable(''),
refreshFleet: vi.fn().mockResolvedValue(undefined)
}));
let component: ReturnType<typeof mount> | undefined;
beforeEach(() => {
const storage = new Map<string, string>();
vi.stubGlobal('localStorage', {
getItem: (key: string) => storage.get(key) ?? null,
setItem: (key: string, value: string) => storage.set(key, value),
removeItem: (key: string) => storage.delete(key)
});
window.history.replaceState({}, '', '/betas/new');
});
afterEach(async () => {
if (component) await unmount(component);
component = undefined;
document.body.replaceChildren();
vi.unstubAllGlobals();
});
function file(
id: number,
options: {
currentVersionId?: number;
latestVersionId?: number | null;
runningBetaId?: number;
} = {}
) {
const latestVersionId =
options.latestVersionId === undefined ? id * 10 + 2 : options.latestVersionId;
return {
id,
name: `File ${id}`,
kind: 'script',
target_rel_path: `file-${id}.gsc`,
scope_type: null,
scope_value: null,
current_version_id: options.currentVersionId ?? id * 10,
latestVersion: latestVersionId === null ? null : { id: latestVersionId },
runningBetaId: options.runningBetaId ?? null
};
}
const files = [
file(1),
file(2),
file(3, { currentVersionId: 32 }),
file(4, { runningBetaId: 7 }),
file(5, { latestVersionId: null })
];
const button = (label: string) =>
[...document.querySelectorAll('button')].find((node) => node.textContent?.trim() === label)!;
const selections = () =>
[...document.querySelectorAll('.pick-row')].map((row) =>
[...row.querySelectorAll('select')].map((select) => select.value)
);
async function show({ query = '', failingFile = 0 } = {}) {
window.history.replaceState({}, '', `/betas/new${query}`);
currentPage.set({
url: new URL(window.location.href),
data: { user: { role: 'operator' } }
});
const fetch = vi.fn(async (url: string) => {
if (url === '/api/scripts') return Response.json({ scripts: files });
const script = files.find((entry) => url === `/api/scripts/${entry.id}`);
if (!script) throw new Error(`Unexpected request: ${url}`);
if (script.id === failingFile) return new Response('', { status: 503 });
return Response.json({
script,
versions: [2, 1, 0].map((version) => ({
id: script.id * 10 + version,
version: version + 1,
release_version: `1.0.${version}`,
sha256: `hash-${script.id}-${version}`,
state: 'untested'
})),
matchedServers: []
});
});
vi.stubGlobal('fetch', fetch);
component = mount(BetaSetupPage, { target: document.body });
flushSync();
await vi.waitFor(() => {
flushSync();
expect(button('Add all eligible files')).toBeDefined();
});
return fetch;
}
it('adds every eligible file with its latest unpromoted version and replaces empty rows', async () => {
const fetch = await show();
button('Add file').click();
flushSync();
expect(document.querySelectorAll('.pick-row')).toHaveLength(2);
button('Add all eligible files').click();
await vi.waitFor(() => {
flushSync();
expect(selections()).toEqual([
['1', '12'],
['2', '22']
]);
});
expect(button('Continue').disabled).toBe(false);
expect(button('Add all eligible files').disabled).toBe(true);
button('Add all eligible files').click();
flushSync();
expect(document.querySelectorAll('.pick-row')).toHaveLength(2);
expect(fetch.mock.calls.map(([url]) => url)).toEqual([
'/api/scripts',
'/api/scripts/1',
'/api/scripts/2'
]);
});
it('preserves exact linked versions, including an older upload of a promoted file', async () => {
await show({ query: '?artifact=1:11&artifact=3:31' });
button('Add file').click();
flushSync();
button('Add all eligible files').click();
await vi.waitFor(() => {
flushSync();
expect(selections()).toEqual([
['1', '11'],
['3', '31'],
['2', '22']
]);
});
expect(button('Continue').disabled).toBe(false);
});
it('retains successful selections and blocks continuing when one file cannot load versions', async () => {
await show({ failingFile: 2 });
button('Add all eligible files').click();
await vi.waitFor(() => {
flushSync();
expect(document.querySelector('[role="alert"]')?.textContent).toContain(
'Could not load versions'
);
});
expect(selections()[0]).toEqual(['1', '12']);
expect(selections()[1][0]).toBe('2');
const failedVersion = document.querySelectorAll('.pick-row')[1].querySelectorAll('select')[1];
expect(failedVersion.selectedOptions[0].textContent).toBe('Choose a version');
expect(button('Continue').disabled).toBe(true);
});