test(stammbaum): cover animateView rAF tween + server 401/500 paths (#692)
Add a deterministic stubbed-rAF test for animateView's animated path (was only covering the reduced-motion branch) and assert the server load redirects on 401 and throws on a network 500 (QA review). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,3 +15,28 @@ describe('animateView (reduced motion)', () => {
|
|||||||
cancel();
|
cancel();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('animateView (animated path)', () => {
|
||||||
|
const from = { x: 0, y: 0, z: 1 };
|
||||||
|
const to = { x: 100, y: 0, z: 2 };
|
||||||
|
|
||||||
|
it('tweens across frames and lands exactly on the target', () => {
|
||||||
|
const frames: { x: number }[] = [];
|
||||||
|
const callbacks: FrameRequestCallback[] = [];
|
||||||
|
vi.stubGlobal('performance', { now: () => 0 });
|
||||||
|
vi.stubGlobal('requestAnimationFrame', (cb: FrameRequestCallback) => callbacks.push(cb));
|
||||||
|
vi.stubGlobal('cancelAnimationFrame', () => {});
|
||||||
|
|
||||||
|
animateView(from, to, (v) => frames.push(v), { durationMs: 100 });
|
||||||
|
|
||||||
|
callbacks[0](50); // t = 0.5 → an interpolated frame
|
||||||
|
callbacks[callbacks.length - 1](100); // t = 1 → exact target
|
||||||
|
|
||||||
|
expect(frames.length).toBeGreaterThanOrEqual(2);
|
||||||
|
expect(frames[0].x).toBeGreaterThan(0);
|
||||||
|
expect(frames[0].x).toBeLessThan(100);
|
||||||
|
expect(frames.at(-1)).toEqual(to);
|
||||||
|
|
||||||
|
vi.unstubAllGlobals();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ function mockNetwork() {
|
|||||||
} as unknown as ReturnType<typeof createApiClient>);
|
} as unknown as ReturnType<typeof createApiClient>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mockNetworkResponse(status: number) {
|
||||||
|
vi.mocked(createApiClient).mockReturnValue({
|
||||||
|
GET: vi.fn().mockResolvedValue({ response: { ok: false, status }, error: { code: 'X' } })
|
||||||
|
} as unknown as ReturnType<typeof createApiClient>);
|
||||||
|
}
|
||||||
|
|
||||||
function loadEvent(query = '') {
|
function loadEvent(query = '') {
|
||||||
const url = new URL(`http://localhost/stammbaum${query}`);
|
const url = new URL(`http://localhost/stammbaum${query}`);
|
||||||
return {
|
return {
|
||||||
@@ -53,4 +59,19 @@ describe('/stammbaum +page.server load — initialView', () => {
|
|||||||
const result = await load(loadEvent('?z=99') as never);
|
const result = await load(loadEvent('?z=99') as never);
|
||||||
expect(result.initialView.z).toBe(MAX_ZOOM);
|
expect(result.initialView.z).toBe(MAX_ZOOM);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('redirects to /login when the network API returns 401', async () => {
|
||||||
|
mockNetworkResponse(401);
|
||||||
|
const { load } = await import('./+page.server');
|
||||||
|
await expect(load(loadEvent() as never)).rejects.toMatchObject({
|
||||||
|
status: 302,
|
||||||
|
location: '/login'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an HTTP error when the network API fails', async () => {
|
||||||
|
mockNetworkResponse(500);
|
||||||
|
const { load } = await import('./+page.server');
|
||||||
|
await expect(load(loadEvent() as never)).rejects.toMatchObject({ status: 500 });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user