fix: close phase 2 journey blockers

This commit is contained in:
2026-09-02 06:04:20 +03:00
parent a86767cdc6
commit 77895c01ff
19 changed files with 378 additions and 26 deletions

View File

@@ -38,7 +38,10 @@ const policy = (overrides: Partial<MotionPolicy> = {}): MotionPolicy => ({
describe('SceneCanvas', () => {
beforeEach(() => sceneMocks.build.mockClear());
afterEach(() => vi.restoreAllMocks());
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it.each([
['reduced motion', policy({ allowSceneRequest: false, maximumTier: 'static', reason: 'system-reduce' })],
@@ -81,6 +84,36 @@ describe('SceneCanvas', () => {
wrapper.unmount();
});
it('forwards each complete scene frame interval to adaptive quality', async () => {
const callbacks = new Map<number, FrameRequestCallback>();
let nextFrameId = 1;
vi.stubGlobal('requestAnimationFrame', vi.fn((callback: FrameRequestCallback) => {
const id = nextFrameId++;
callbacks.set(id, callback);
return id;
}));
vi.stubGlobal('cancelAnimationFrame', vi.fn((id: number) => callbacks.delete(id)));
const sampleFrame = vi.fn();
const wrapper = mount(SceneCanvas, {
props: {
journey: journey(),
policy: policy(),
quality: 'high',
sceneDescription: 'A decorative night road behind the portfolio content.',
sampleFrame,
},
});
await flushPromises();
const frame = [...callbacks.entries()][0]!;
callbacks.delete(frame[0]);
vi.spyOn(performance, 'now').mockReturnValueOnce(2_000).mockReturnValueOnce(2_301);
frame[1](2_000);
expect(sampleFrame).toHaveBeenCalledWith(2_000, 2_301);
wrapper.unmount();
});
it('falls back to the poster without obscuring semantic content after scene failure', async () => {
sceneMocks.build.mockRejectedValueOnce(new Error('WebGL unavailable'));
const wrapper = mount(SceneCanvas, {
@@ -123,6 +156,38 @@ describe('SceneCanvas', () => {
expect(wrapper.get('img').isVisible()).toBe(true);
wrapper.unmount();
});
it('retains the same canvas and scene generation for a visitor pause and resume', async () => {
const wrapper = mount(SceneCanvas, {
props: {
journey: journey(),
policy: policy(),
quality: 'high',
sceneDescription: 'A decorative night road behind the portfolio content.',
},
});
await flushPromises();
const canvas = wrapper.get('canvas').element;
const runtime = await sceneMocks.build.mock.results[0]!.value;
await wrapper.setProps({
policy: policy({ allowSceneRequest: false, maximumTier: 'static', reason: 'visitor-pause' }),
quality: 'high',
});
await flushPromises();
expect(wrapper.get('canvas').element).toBe(canvas);
expect(runtime.dispose).not.toHaveBeenCalled();
expect(sceneMocks.build).toHaveBeenCalledTimes(1);
await wrapper.setProps({ policy: policy() });
await flushPromises();
expect(wrapper.get('canvas').element).toBe(canvas);
expect(runtime.dispose).not.toHaveBeenCalled();
expect(sceneMocks.build).toHaveBeenCalledTimes(1);
wrapper.unmount();
});
});
describe('DriveControls', () => {