test: add deterministic resilience matrix
This commit is contained in:
112
tests/browser/fault-matrix.spec.ts
Normal file
112
tests/browser/fault-matrix.spec.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { expect, test } from '../helpers/serve-dist';
|
||||
import {
|
||||
assertSemanticFaultOracle,
|
||||
authorizedFaultUrl,
|
||||
collectBrowserErrors,
|
||||
missingTokenFaultUrl,
|
||||
readFaultSnapshot,
|
||||
readUnhandledRejections,
|
||||
wrongTokenFaultUrl,
|
||||
} from '../helpers/test-build-auth';
|
||||
import { installSceneInstrumentation, readSceneInstrumentation } from '../helpers/scene-instrumentation';
|
||||
import type { PortfolioFaultId } from '@/testing/fault-hooks';
|
||||
|
||||
const assertNoEscapedErrors = async (
|
||||
page: Parameters<typeof readUnhandledRejections>[0],
|
||||
pageErrors: readonly string[],
|
||||
): Promise<void> => {
|
||||
expect(pageErrors).toEqual([]);
|
||||
expect(await readUnhandledRejections(page)).toEqual([]);
|
||||
expect((await readSceneInstrumentation(page)).unhandledRejections).toEqual([]);
|
||||
};
|
||||
|
||||
test.describe('authenticated fault-build boundary', () => {
|
||||
test.beforeEach(async ({}, testInfo) => {
|
||||
test.skip(testInfo.project.name !== 'desktop-chromium', 'The complete F1–F13 matrix runs once on current desktop Chromium.');
|
||||
});
|
||||
|
||||
test('missing token exposes no fault API and leaves the portfolio operational', async ({ page, distUrl }) => {
|
||||
await page.goto(missingTokenFaultUrl(distUrl, 'F1-scene-chunk'), { waitUntil: 'domcontentloaded' });
|
||||
expect(await page.evaluate(() => '__PORTFOLIO_TEST_HOOKS__' in window)).toBe(false);
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'running');
|
||||
await assertSemanticFaultOracle(page);
|
||||
});
|
||||
|
||||
test('wrong token exposes no fault API and leaves the portfolio operational', async ({ page, distUrl }) => {
|
||||
await page.goto(wrongTokenFaultUrl(distUrl, 'F1-scene-chunk'), { waitUntil: 'domcontentloaded' });
|
||||
expect(await page.evaluate(() => '__PORTFOLIO_TEST_HOOKS__' in window)).toBe(false);
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'running');
|
||||
await assertSemanticFaultOracle(page);
|
||||
});
|
||||
|
||||
test('correct token exposes only the frozen named fault interface', async ({ page, distUrl }) => {
|
||||
await page.goto(authorizedFaultUrl(distUrl, 'F9-hydration-failure'), { waitUntil: 'domcontentloaded' });
|
||||
await expect.poll(() => page.evaluate(() => '__PORTFOLIO_TEST_HOOKS__' in window)).toBe(true);
|
||||
const shape = await page.evaluate(() => {
|
||||
const hooks = (window as unknown as { __PORTFOLIO_TEST_HOOKS__: object }).__PORTFOLIO_TEST_HOOKS__;
|
||||
return { frozen: Object.isFrozen(hooks), keys: Object.keys(hooks).sort() };
|
||||
});
|
||||
expect(shape).toEqual({ frozen: true, keys: ['fault', 'snapshot', 'trigger', 'version'] });
|
||||
expect((await readFaultSnapshot(page)).fault).toBe('F9-hydration-failure');
|
||||
await expect(page.locator('.drive-controls')).toHaveCount(1);
|
||||
await assertSemanticFaultOracle(page);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('scene failure matrix', () => {
|
||||
test.beforeEach(async ({}, testInfo) => {
|
||||
test.skip(testInfo.project.name !== 'desktop-chromium', 'The complete F1–F13 matrix runs once on current desktop Chromium.');
|
||||
});
|
||||
|
||||
for (const fault of [
|
||||
'F1-scene-chunk',
|
||||
'F1-shader',
|
||||
'F1-car',
|
||||
'F1-texture',
|
||||
] as const satisfies readonly PortfolioFaultId[]) {
|
||||
test(`${fault} enters one static fallback without escaping an error`, async ({ page, distUrl }) => {
|
||||
await installSceneInstrumentation(page);
|
||||
const errors = await collectBrowserErrors(page);
|
||||
await page.goto(authorizedFaultUrl(distUrl, fault), { waitUntil: 'domcontentloaded' });
|
||||
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'static');
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-static-fallback', 'true');
|
||||
await expect(page.locator('.journey-poster')).toHaveCount(1);
|
||||
await expect(page.locator('.journey-canvas')).toHaveCount(0);
|
||||
await expect(page.locator('.drive-controls__status')).toHaveText('Visuals unavailable');
|
||||
await assertSemanticFaultOracle(page);
|
||||
const resources = await readSceneInstrumentation(page);
|
||||
expect(resources.activeAnimationFrames).toBe(0);
|
||||
expect(resources.maximumCanvasCount).toBeLessThanOrEqual(1);
|
||||
expect((await readFaultSnapshot(page)).staticFallbackCount).toBe(1);
|
||||
await assertNoEscapedErrors(page, errors.pageErrors);
|
||||
});
|
||||
}
|
||||
|
||||
test('F2 enforces the literal five-second first-frame deadline', async ({ page, distUrl }) => {
|
||||
test.setTimeout(12_000);
|
||||
await installSceneInstrumentation(page);
|
||||
const errors = await collectBrowserErrors(page);
|
||||
const startedAt = Date.now();
|
||||
await page.goto(authorizedFaultUrl(distUrl, 'F2-first-frame-timeout'), { waitUntil: 'domcontentloaded' });
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'static', { timeout: 7_000 });
|
||||
expect(Date.now() - startedAt).toBeGreaterThanOrEqual(4_800);
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-static-fallback', 'true');
|
||||
await expect(page.locator('.journey-canvas')).toHaveCount(0);
|
||||
await assertSemanticFaultOracle(page);
|
||||
await assertNoEscapedErrors(page, errors.pageErrors);
|
||||
});
|
||||
|
||||
test('F3 contains a post-first-frame exception on the next frame', async ({ page, distUrl }) => {
|
||||
await installSceneInstrumentation(page);
|
||||
const errors = await collectBrowserErrors(page);
|
||||
await page.goto(authorizedFaultUrl(distUrl, 'F3-post-first-frame-exception'), { waitUntil: 'domcontentloaded' });
|
||||
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'static');
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-static-fallback', 'true');
|
||||
await expect(page.locator('.journey-canvas')).toHaveCount(0);
|
||||
await assertSemanticFaultOracle(page);
|
||||
expect((await readSceneInstrumentation(page)).activeAnimationFrames).toBe(0);
|
||||
await assertNoEscapedErrors(page, errors.pageErrors);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user