Something went wrong. Try again.
Hardware-in-the-loop development platform for the Commodore 64.
Something went wrong. Try again.
7.4 kB · 176 lines
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177'use strict';
// Browser acceptance for the read-only memory-checkpoint comparison view.//// These cases drive the real loopback service. They require two things the// fixture/backend must provide://// 1. The fixture seeds two finished runs named `browser-checkpoint-probe`// whose frozen captures match (see// crates/ultimate-tool-server/tests/browser_support/checkpoint_cases.rs).// 2. The service registers the read-only routes// GET /api/v1/runs/checkpoint-comparison?baseline_id=..&candidate_id=..// GET /api/v1/checkpoint-comparisons// GET /api/v1/checkpoint-comparisons/{comparison_id}//// Against a service without those routes the panel renders the route's error// instead of a comparison, so these are not a substitute for that backend work.// No case here authorizes hardware, probes a device, or starts a stream: the// whole point of the view is that comparing two immutable runs is a plain read.
const { test, expect } = require('@playwright/test');const { startFixtureServer } = require('./server.cjs');
let server;test.beforeAll(async () => { test.setTimeout(100_000); server = await startFixtureServer();});test.afterAll(async () => { if (server) await server.stop(); });
function recordRequests(page, server, predicate) { const hits = []; page.on('request', (request) => { if (request.url().startsWith(server.url) && predicate(request)) { hits.push(request.url()); } }); return hits;}
// The two seeded capture runs, in listing order. They share one test name, so// they are distinguished by the run id on each history card.async function checkpointRunIds(page) { const cards = page.locator('#run-list .run-card').filter({ hasText: 'browser-checkpoint-probe' }); await expect(cards).toHaveCount(2); const first = await cards.nth(0).getAttribute('data-run-id'); const second = await cards.nth(1).getAttribute('data-run-id'); return [first, second];}
async function compare(page, baselineId, candidateId) { await page.selectOption('#comparison-baseline', baselineId); await page.selectOption('#comparison-candidate', candidateId); await page.locator('#comparison-run').click();}
test('comparing two seeded captures is one read-only request and reports matched', async ({ page }) => { const comparisonRequests = recordRequests( page, server, (request) => new URL(request.url()).pathname === '/api/v1/runs/checkpoint-comparison', ); const mutations = recordRequests(page, server, (request) => request.method() !== 'GET');
await page.goto(server.url); const [baseline, candidate] = await checkpointRunIds(page); await compare(page, baseline, candidate);
await expect(page.locator('#comparison-badge')).toHaveText('MATCHED'); const result = page.locator('#comparison-result'); await expect(result).toContainText('Where do the captures first differ?'); await expect(result).toContainText('no differing checkpoint row recorded'); await expect(result).toContainText('health'); await expect(result).toContainText('score'); await expect(result).toContainText('both captures share the same target kind');
expect(comparisonRequests.length).toBe(1); expect(mutations).toEqual([]);});
test('a side without a capture reads unavailable and never diverged', async ({ page }) => { await page.goto(server.url); const [baseline] = await checkpointRunIds(page); const smoke = await page.locator('#run-list .run-card').filter({ hasText: 'smoke' }) .first().getAttribute('data-run-id');
await compare(page, baseline, smoke);
await expect(page.locator('#comparison-badge')).toHaveText('UNAVAILABLE'); const result = page.locator('#comparison-result'); await expect(result).toContainText('capture_missing'); await expect(result).not.toContainText('DIVERGED');});
test('the comparison controls offer no mutation and clear cleanly', async ({ page }) => { const mutations = recordRequests(page, server, (request) => request.method() !== 'GET'); await page.goto(server.url); const [baseline, candidate] = await checkpointRunIds(page); await compare(page, baseline, candidate); await expect(page.locator('#comparison-badge')).toHaveText('MATCHED');
await page.locator('#comparison-clear').click(); await expect(page.locator('#comparison-badge')).toHaveText('NOT COMPARED'); await expect(page.locator('#comparison-baseline')).toHaveValue(''); await expect(page.locator('#comparison-candidate')).toHaveValue(''); expect(mutations).toEqual([]);});
test('a stale comparison response cannot replace a newer selection', async ({ page }) => { await page.goto(server.url); const [baseline, candidate] = await checkpointRunIds(page); const smoke = await page.locator('#run-list .run-card').filter({ hasText: 'smoke' }) .first().getAttribute('data-run-id');
let release; let reached; const held = new Promise((resolve) => { release = resolve; }); const waiting = new Promise((resolve) => { reached = resolve; }); await page.route('**/api/v1/runs/checkpoint-comparison*', async (route) => { if (route.request().url().includes(smoke)) { await route.continue(); return; } reached(); await held; await route.continue(); });
// First selection: the matched pair, whose response is held open. await compare(page, baseline, candidate); await waiting; // Second selection: the unavailable pair, which answers immediately. await compare(page, baseline, smoke); await expect(page.locator('#comparison-badge')).toHaveText('UNAVAILABLE');
// The late matched response must not overwrite the newer unavailable result. release(); await expect(page.locator('#comparison-badge')).toHaveText('UNAVAILABLE');});
test('the optional matrix checkbox is offered only for a VICE-plus-Ultimate single repetition', async ({ page }) => { // The fixture deliberately has no executable VICE and no hardware probe. // Override only capability presentation to exercise the selection controls; // this case never submits a job or authorizes any target operation. await page.route('**/api/v1/targets', async (route) => { const response = await route.fetch(); const body = await response.json(); for (const target of body.targets || []) { if (target.kind === 'vice' || target.kind === 'ultimate') target.available = true; } await route.fulfill({ response, json: body }); }); await page.goto(server.url); const label = page.locator('#compare-checkpoints-label'); await expect(label).toBeHidden();
const choice = (kind) => page.locator('#target-options label.target-choice').filter({ hasText: kind }); await choice('fake').click(); await choice('vice').click(); await choice('ultimate').click(); // The selected test must actually declare the frozen-RAM contract. await expect(label).toBeHidden(); await page.selectOption('#test-select', 'browser-checkpoint-probe'); await expect(label).toBeVisible(); await expect(page.locator('#compare-checkpoints-text')).toContainText('frozen memory checkpoints');
// Two repetitions no longer produce a single comparable pair. await page.fill('#repeat-input', '2'); await expect(label).toBeHidden(); await page.fill('#repeat-input', '1'); await expect(label).toBeVisible();
// Dropping one half of the pair withdraws the option again. await choice('ultimate').click(); await expect(label).toBeHidden();});