feat: relay station masts come online through the toolkit chapter
This commit is contained in:
119
tests/unit/relay-progress.test.ts
Normal file
119
tests/unit/relay-progress.test.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { defineComponent, h, nextTick, shallowRef } from 'vue';
|
||||
import { useRelay } from '@/composables/useRelay';
|
||||
import { BEACON_HZ, publishRelay, readRelay, RELAY_STOPS, relayOnline, rungLit } from '@/journey/relay-progress';
|
||||
import type { JourneySnapshot } from '@/journey/types';
|
||||
import type { PortfolioContent, SectionId } from '@/content/types';
|
||||
|
||||
const content = JSON.parse(
|
||||
readFileSync(resolve(process.cwd(), 'content/public/portfolio-content.json'), 'utf8'),
|
||||
) as PortfolioContent;
|
||||
const counts = content.toolkit.map((group) => group.technologies.length);
|
||||
|
||||
const sweep = (from: number, to: number, steps = 400): number[] =>
|
||||
Array.from({ length: steps + 1 }, (_, i) => from + ((to - from) * i) / steps);
|
||||
|
||||
afterEach(() => publishRelay([]));
|
||||
|
||||
describe('relay progress', () => {
|
||||
it('has one stop per group and blinks well under 3 Hz', () => {
|
||||
expect(RELAY_STOPS).toHaveLength(4);
|
||||
expect(counts).toHaveLength(4);
|
||||
expect(BEACON_HZ).toBeLessThanOrEqual(3);
|
||||
});
|
||||
|
||||
it('brings masts online monotonically and takes them offline in reverse', () => {
|
||||
const forward = sweep(0, 1).map(relayOnline);
|
||||
forward.forEach((n, i) => { if (i > 0) expect(n).toBeGreaterThanOrEqual(forward[i - 1]!); });
|
||||
const backward = [...sweep(0, 1)].reverse().map(relayOnline);
|
||||
expect(backward).toEqual([...forward].reverse());
|
||||
expect(relayOnline(null)).toBe(0);
|
||||
expect(relayOnline(0)).toBe(0);
|
||||
expect(relayOnline(0.6)).toBe(4);
|
||||
});
|
||||
|
||||
it('lights every rung monotonically and reversibly, all lit by the end of the ramp', () => {
|
||||
counts.forEach((count, mast) => {
|
||||
for (let rung = 0; rung < count; rung += 1) {
|
||||
const forward = sweep(0, 1).map((p) => rungLit(p, mast, rung, count));
|
||||
forward.forEach((v, i) => { if (i > 0) expect(v).toBeGreaterThanOrEqual(forward[i - 1]!); });
|
||||
expect([...sweep(0, 1)].reverse().map((p) => rungLit(p, mast, rung, count))).toEqual([...forward].reverse());
|
||||
expect(rungLit(RELAY_STOPS[mast]! - 0.001, mast, rung, count)).toBe(0);
|
||||
expect(rungLit(0.71, mast, rung, count)).toBe(1);
|
||||
}
|
||||
});
|
||||
expect(rungLit(null, 0, 0, 3)).toBe(0);
|
||||
});
|
||||
|
||||
it('lights rungs bottom-up in index order', () => {
|
||||
for (const p of sweep(0, 1)) {
|
||||
counts.forEach((count, mast) => {
|
||||
for (let rung = 1; rung < count; rung += 1) {
|
||||
expect(rungLit(p, mast, rung, count)).toBeLessThanOrEqual(rungLit(p, mast, rung - 1, count));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('carries the rung counts from the DOM to the scene', () => {
|
||||
expect(readRelay().counts).toEqual([]);
|
||||
publishRelay(counts);
|
||||
expect(readRelay().counts).toEqual([11, 8, 4, 3]);
|
||||
publishRelay([1, 2, 3, 4, 5]);
|
||||
expect(readRelay().counts).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useRelay', () => {
|
||||
const snapshot = (activeSectionId: SectionId, a: number, n: number | null): JourneySnapshot => ({
|
||||
scrollY: 0,
|
||||
routeProgress: 0.8,
|
||||
direction: 0,
|
||||
acceleration: 0,
|
||||
driveSpeed: 0,
|
||||
activeSectionId,
|
||||
chapter: 'systems',
|
||||
sign: null,
|
||||
arrived: false,
|
||||
sectionActivation: { layoutRevision: 1, activeHeadingOffsetVh: a, nextHeadingOffsetVh: n },
|
||||
} as unknown as JourneySnapshot);
|
||||
|
||||
it('sets html[data-relay-online] through Toolkit only, publishes counts, and cleans up', async () => {
|
||||
const journey = shallowRef(snapshot('additional-work', -0.5, 1.5));
|
||||
const wrapper = mount(defineComponent({
|
||||
setup() {
|
||||
useRelay(journey, () => content.toolkit);
|
||||
return () => h('span');
|
||||
},
|
||||
}));
|
||||
const root = document.documentElement;
|
||||
await nextTick();
|
||||
expect(readRelay().counts).toEqual(counts);
|
||||
expect(root.dataset.relayOnline).toBeUndefined();
|
||||
|
||||
journey.value = snapshot('toolkit', -0.1, 1.9); // p 0.05
|
||||
await nextTick();
|
||||
expect(root.dataset.relayOnline).toBe('1');
|
||||
journey.value = snapshot('toolkit', -1, 1); // p 0.5
|
||||
await nextTick();
|
||||
expect(root.dataset.relayOnline).toBe('3');
|
||||
journey.value = snapshot('toolkit', -1.3, 0.7); // p 0.65
|
||||
await nextTick();
|
||||
expect(root.dataset.relayOnline).toBe('4');
|
||||
journey.value = snapshot('toolkit', -0.5, 1.5); // p 0.25, scrolling back
|
||||
await nextTick();
|
||||
expect(root.dataset.relayOnline).toBe('2');
|
||||
journey.value = snapshot('contact', -0.2, null);
|
||||
await nextTick();
|
||||
expect(root.dataset.relayOnline).toBeUndefined();
|
||||
journey.value = snapshot('toolkit', -1, 1);
|
||||
await nextTick();
|
||||
expect(root.dataset.relayOnline).toBe('3');
|
||||
wrapper.unmount();
|
||||
expect(root.dataset.relayOnline).toBeUndefined();
|
||||
expect(root.dataset.relayFocus).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user