test: lock journey lifecycle and static fallback
This commit is contained in:
101
tests/browser/journey-integration.spec.ts
Normal file
101
tests/browser/journey-integration.spec.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { expect, test } from '../helpers/serve-dist';
|
||||
import {
|
||||
installSceneInstrumentation,
|
||||
readSceneInstrumentation,
|
||||
waitForOneAnimationFrame,
|
||||
} from '../helpers/scene-instrumentation';
|
||||
|
||||
const sections = [
|
||||
{ id: 'start', label: 'Start' },
|
||||
{ id: 'profile', label: 'Profile' },
|
||||
{ id: 'experience', label: 'Experience' },
|
||||
{ id: 'infrastructure', label: 'Infrastructure' },
|
||||
{ id: 'selected-builds', label: 'Selected builds' },
|
||||
{ id: 'additional-work', label: 'Additional work' },
|
||||
{ id: 'toolkit', label: 'Toolkit' },
|
||||
{ id: 'contact', label: 'Contact' },
|
||||
] as const;
|
||||
|
||||
test.beforeEach(async ({ page }) => installSceneInstrumentation(page));
|
||||
|
||||
test('keeps navigation, HUD, progress, chapter, and arrival on the same next-frame section snapshot', async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', /running|failed|static/u);
|
||||
const chapterColors: string[] = [];
|
||||
|
||||
for (const section of sections) {
|
||||
await page.evaluate((id) => {
|
||||
const heading = document.getElementById(`${id}-heading`);
|
||||
if (!(heading instanceof HTMLElement)) throw new Error(`Missing heading: ${id}`);
|
||||
const top = heading.getBoundingClientRect().top + window.scrollY;
|
||||
const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
|
||||
const destination = id === 'contact'
|
||||
? document.documentElement.scrollHeight
|
||||
: Math.max(0, top - viewportHeight * 0.35 + 1);
|
||||
window.scrollTo({ top: destination, behavior: 'instant' });
|
||||
}, section.id);
|
||||
await waitForOneAnimationFrame(page);
|
||||
|
||||
await expect(page.locator(`nav a[href="#${section.id}"]`)).toHaveAttribute('aria-current', 'page');
|
||||
await expect(page.locator('.journey-hud__section')).toHaveText(section.label);
|
||||
const routeText = await page.locator('.journey-hud__route').textContent();
|
||||
expect(routeText).toMatch(/^ROUTE (?:100|[0-9]{1,2})%$/u);
|
||||
if (section.id === 'contact') {
|
||||
await expect(page.locator('.journey-hud__arrival')).toHaveText('ARRIVED');
|
||||
await expect(page.locator('.journey-hud__route')).toHaveText('ROUTE 100%');
|
||||
} else {
|
||||
await expect(page.locator('.journey-hud__arrival')).toHaveCount(0);
|
||||
}
|
||||
// The section snapshot is emitted in the scroll RAF above; the scene's
|
||||
// already-running loop must consume it no later than its next RAF.
|
||||
await waitForOneAnimationFrame(page);
|
||||
const clearColors = (await readSceneInstrumentation(page)).clearColors.filter((color) => color !== '#000000');
|
||||
const latestChapterColor = clearColors.at(-1);
|
||||
if (latestChapterColor === undefined) throw new Error(`Missing rendered chapter color for ${section.id}`);
|
||||
chapterColors.push(latestChapterColor);
|
||||
}
|
||||
|
||||
const instrumentation = await readSceneInstrumentation(page);
|
||||
expect(instrumentation.maximumCanvasCount).toBeLessThanOrEqual(1);
|
||||
const expectedChapterColors = (page.viewportSize()?.width ?? 0) < 768
|
||||
? [
|
||||
'#040713', '#040713', '#060918', '#080b1d',
|
||||
'#0d1025', '#0d1025', '#18152c', '#2b2035',
|
||||
]
|
||||
: [
|
||||
'#000102', '#000102', '#000102', '#010103',
|
||||
'#010105', '#010105', '#020206', '#060409',
|
||||
];
|
||||
expect(chapterColors).toEqual(expectedChapterColors);
|
||||
});
|
||||
|
||||
test('draws only the independently eligible next roadside sign during passive forward travel', async ({
|
||||
page,
|
||||
distUrl,
|
||||
}) => {
|
||||
await page.goto(distUrl, { waitUntil: 'domcontentloaded' });
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'running');
|
||||
|
||||
const oracle = await page.evaluate(() => {
|
||||
const profile = document.getElementById('profile-heading');
|
||||
if (!(profile instanceof HTMLElement)) throw new Error('Missing profile heading');
|
||||
const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
|
||||
const headingTop = profile.getBoundingClientRect().top + window.scrollY;
|
||||
const destination = Math.max(1, headingTop - viewportHeight * (0.35 + 0.8));
|
||||
return { destination, viewportHeight, expectedLabel: '01 / PROFILE' };
|
||||
});
|
||||
await page.evaluate(() => window.dispatchEvent(new WheelEvent('wheel', { deltaY: 1 })));
|
||||
for (let y = 1; y < oracle.destination; y += oracle.viewportHeight * 0.5) {
|
||||
await page.evaluate((nextY) => window.scrollTo(0, nextY), y);
|
||||
await waitForOneAnimationFrame(page);
|
||||
}
|
||||
await page.evaluate((destination) => window.scrollTo(0, destination), oracle.destination);
|
||||
await waitForOneAnimationFrame(page);
|
||||
|
||||
await expect.poll(async () => (await readSceneInstrumentation(page)).routeLabelsDrawn).toContain(oracle.expectedLabel);
|
||||
const labels = (await readSceneInstrumentation(page)).routeLabelsDrawn;
|
||||
expect([...new Set(labels)]).toEqual([oracle.expectedLabel]);
|
||||
});
|
||||
Reference in New Issue
Block a user