test: align navigation and sign label checks with the gantry

This commit is contained in:
2026-10-11 00:07:09 +03:00
parent ea2bf02222
commit 10b867143b
2 changed files with 30 additions and 8 deletions

View File

@@ -18,6 +18,16 @@ const sections = [
test.beforeEach(async ({ page }) => installSceneInstrumentation(page)); test.beforeEach(async ({ page }) => installSceneInstrumentation(page));
/**
* The gantry window pre-draws its hover twin for the exit it rests on, which
* is Start at load. That is a window texture, not a roadside sign, so it is
* left out of the roadside label checks; Start never has a roadside sign.
*/
function roadsideLabels(labels: readonly string[]): string[] {
return labels.filter((label) => label !== 'Start');
}
test('keeps navigation, HUD, progress, chapter, and arrival on the same next-frame section snapshot', async ({ test('keeps navigation, HUD, progress, chapter, and arrival on the same next-frame section snapshot', async ({
page, page,
distUrl, distUrl,
@@ -105,7 +115,7 @@ test('draws only the independently eligible next roadside sign during passive fo
await waitForOneAnimationFrame(page); await waitForOneAnimationFrame(page);
await expect.poll(async () => (await readSceneInstrumentation(page)).routeLabelsDrawn).toContain(oracle.expectedLabel); await expect.poll(async () => (await readSceneInstrumentation(page)).routeLabelsDrawn).toContain(oracle.expectedLabel);
const labels = (await readSceneInstrumentation(page)).routeLabelsDrawn; const labels = roadsideLabels((await readSceneInstrumentation(page)).routeLabelsDrawn);
expect([...new Set(labels)]).toEqual([oracle.expectedLabel]); expect([...new Set(labels)]).toEqual([oracle.expectedLabel]);
}); });
@@ -235,12 +245,12 @@ test('shows exactly one destination sign for a navigator click and suppresses hi
await expect(page.locator('#selected-builds-heading')).toBeFocused(); await expect(page.locator('#selected-builds-heading')).toBeFocused();
await page.waitForTimeout(2_400); await page.waitForTimeout(2_400);
const afterRepeat = await readSceneInstrumentation(page); const afterRepeat = await readSceneInstrumentation(page);
expect(afterRepeat.routeLabelsDrawn).toEqual(['Selected builds']); expect(roadsideLabels(afterRepeat.routeLabelsDrawn)).toEqual(['Selected builds']);
expect(countSignPresentations(afterRepeat.signFrames, 'selected-builds')).toBe(1); expect(countSignPresentations(afterRepeat.signFrames, 'selected-builds')).toBe(1);
await page.goBack({ waitUntil: 'domcontentloaded' }); await page.goBack({ waitUntil: 'domcontentloaded' });
await page.waitForTimeout(400); await page.waitForTimeout(400);
expect((await readSceneInstrumentation(page)).routeLabelsDrawn).toEqual(['Selected builds']); expect(roadsideLabels((await readSceneInstrumentation(page)).routeLabelsDrawn)).toEqual(['Selected builds']);
}); });
test('keeps the non-navigator Explore work section jump sign-suppressed', async ({ test('keeps the non-navigator Explore work section jump sign-suppressed', async ({
@@ -430,7 +440,7 @@ test('moves an already-readable portrait sign into navigator-safe placement with
const instrumentation = await readSceneInstrumentation(page); const instrumentation = await readSceneInstrumentation(page);
expect(countSignPresentations(instrumentation.signFrames, 'profile')).toBe(1); expect(countSignPresentations(instrumentation.signFrames, 'profile')).toBe(1);
expect(instrumentation.routeLabelsDrawn).toEqual(['Profile']); expect(roadsideLabels(instrumentation.routeLabelsDrawn)).toEqual(['Profile']);
}); });
test('re-arms passive signage after a navigator round trip to Start', async ({ test('re-arms passive signage after a navigator round trip to Start', async ({
@@ -495,7 +505,7 @@ test('reconciles an idle rapid jump to the latest active route without flashing
frame.targetId === 'selected-builds' frame.targetId === 'selected-builds'
))?.phase, { intervals: [16], timeout: 5_000 }).toBe('dwell'); ))?.phase, { intervals: [16], timeout: 5_000 }).toBe('dwell');
const instrumentation = await readSceneInstrumentation(page); const instrumentation = await readSceneInstrumentation(page);
expect([...new Set(instrumentation.routeLabelsDrawn)]) expect([...new Set(roadsideLabels(instrumentation.routeLabelsDrawn))])
.toEqual(['Selected builds']); .toEqual(['Selected builds']);
const dwell = instrumentation.signFrames.findLast((frame) => ( const dwell = instrumentation.signFrames.findLast((frame) => (
frame.targetId === 'selected-builds' && frame.phase === 'dwell' frame.targetId === 'selected-builds' && frame.phase === 'dwell'

View File

@@ -8,8 +8,11 @@ test('desktop navigation docks without stretching labels or shifting the documen
await page.evaluate(() => document.fonts.ready); await page.evaluate(() => document.fonts.ready);
const nav = page.locator('.site-navigation'); const nav = page.locator('.site-navigation');
await expect(nav).toHaveAttribute('data-enhanced', 'true'); await expect(nav).toHaveAttribute('data-enhanced', 'true');
// Since round 5 the bar is a full-bleed overhead gantry: it is docked at
// the top and unscaled from the first frame instead of growing into place.
const surfaceScale = () => nav.evaluate(el => new DOMMatrixReadOnly(getComputedStyle(el, '::before').transform).a); const surfaceScale = () => nav.evaluate(el => new DOMMatrixReadOnly(getComputedStyle(el, '::before').transform).a);
expect(await surfaceScale()).toBeLessThan(1); expect(await surfaceScale()).toBe(1);
expect((await nav.boundingBox())!.y).toBeLessThan(1);
const label = nav.locator('a').first(); const label = nav.locator('a').first();
const initialFont = await label.evaluate(el => getComputedStyle(el).fontSize); const initialFont = await label.evaluate(el => getComputedStyle(el).fontSize);
// Measure the content beside this fixed header, not unrelated lazy media // Measure the content beside this fixed header, not unrelated lazy media
@@ -26,7 +29,7 @@ test('desktop navigation docks without stretching labels or shifting the documen
expect(await label.evaluate(el => getComputedStyle(el).fontSize)).toBe(initialFont); expect(await label.evaluate(el => getComputedStyle(el).fontSize)).toBe(initialFont);
expect(await heroGeometry()).toEqual(initialHero); expect(await heroGeometry()).toEqual(initialHero);
await page.evaluate(() => scrollTo(0, 0)); await page.evaluate(() => scrollTo(0, 0));
await expect.poll(async () => (await nav.boundingBox())!.y).toBeGreaterThan(10); await expect.poll(async () => (await nav.boundingBox())!.y).toBeLessThan(1);
}); });
test('selected navigation has rounded inset highlights', async ({ page, distUrl }) => { test('selected navigation has rounded inset highlights', async ({ page, distUrl }) => {
@@ -34,7 +37,16 @@ test('selected navigation has rounded inset highlights', async ({ page, distUrl
const disclosure = page.locator('.site-navigation summary'); const disclosure = page.locator('.site-navigation summary');
if (await disclosure.isVisible()) await disclosure.click(); if (await disclosure.isVisible()) await disclosure.click();
const selected = page.locator('.site-navigation a[aria-current="page"]'); const selected = page.locator('.site-navigation a[aria-current="page"]');
expect(await selected.evaluate(el => parseFloat(getComputedStyle(el).borderTopLeftRadius))).toBeGreaterThanOrEqual(5); // On the desktop gantry the current exit is marked by its amber lane lamp
// (the li::after rule) rather than a rounded inset; the menu keeps the inset.
const marker = await selected.evaluate(el => {
const lamp = getComputedStyle(el.parentElement!, '::after');
return {
radius: parseFloat(getComputedStyle(el).borderTopLeftRadius),
lamp: lamp.content !== 'none' && parseFloat(lamp.blockSize) >= 2,
};
});
expect(marker.radius >= 5 || marker.lamp).toBe(true);
await selected.focus(); await selected.focus();
await page.keyboard.press('Shift'); await page.keyboard.press('Shift');
expect(await selected.evaluate(el => getComputedStyle(el).outlineStyle)).not.toBe('none'); expect(await selected.evaluate(el => getComputedStyle(el).outlineStyle)).not.toBe('none');