feat: scroll-driven maintenance window set-piece in the infrastructure chapter
This commit is contained in:
147
tests/unit/maintenance-progress.test.ts
Normal file
147
tests/unit/maintenance-progress.test.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { defineComponent, h, nextTick, shallowRef } from 'vue';
|
||||
import { useMaintenanceWindow } from '@/composables/useMaintenanceWindow';
|
||||
import type { JourneySnapshot } from '@/journey/types';
|
||||
import {
|
||||
createMaintenanceDirection,
|
||||
maintenanceClock,
|
||||
maintenanceProgress,
|
||||
maintenanceStep,
|
||||
maintenanceSubline,
|
||||
} from '@/journey/maintenance-progress';
|
||||
import type { SectionId } from '@/content/types';
|
||||
|
||||
const at = (
|
||||
activeSectionId: SectionId,
|
||||
activeHeadingOffsetVh: number,
|
||||
nextHeadingOffsetVh: number | null,
|
||||
) => ({
|
||||
activeSectionId,
|
||||
sectionActivation: { layoutRevision: 1, activeHeadingOffsetVh, nextHeadingOffsetVh },
|
||||
});
|
||||
|
||||
const live = () => '14:07';
|
||||
|
||||
describe('maintenance progress', () => {
|
||||
it('is 0 at the Infrastructure heading and 1 at the next heading', () => {
|
||||
expect(maintenanceProgress(at('infrastructure', 0, 2.5))).toBe(0);
|
||||
expect(maintenanceProgress(at('infrastructure', -1.25, 1.25))).toBeCloseTo(0.5);
|
||||
expect(maintenanceProgress(at('infrastructure', -2.5, 0))).toBe(1);
|
||||
});
|
||||
|
||||
it('clamps to the window and is null outside the chapter or without geometry', () => {
|
||||
expect(maintenanceProgress(at('infrastructure', 0.2, 2.7))).toBe(0);
|
||||
expect(maintenanceProgress(at('infrastructure', -3, -0.5))).toBe(1);
|
||||
expect(maintenanceProgress(at('experience', -1, 1))).toBeNull();
|
||||
expect(maintenanceProgress(at('selected-builds', -1, 1))).toBeNull();
|
||||
expect(maintenanceProgress(at('infrastructure', 0, null))).toBeNull();
|
||||
expect(maintenanceProgress(at('infrastructure', 1, 1))).toBeNull();
|
||||
expect(maintenanceProgress(at('infrastructure', Number.NaN, 1))).toBeNull();
|
||||
});
|
||||
|
||||
it('maps progress onto open, backup, transfer, verify and done', () => {
|
||||
expect(maintenanceStep(0)).toBe('open');
|
||||
expect(maintenanceStep(0.099)).toBe('open');
|
||||
expect(maintenanceStep(0.1)).toBe('backup');
|
||||
expect(maintenanceStep(0.349)).toBe('backup');
|
||||
expect(maintenanceStep(0.35)).toBe('transfer');
|
||||
expect(maintenanceStep(0.649)).toBe('transfer');
|
||||
expect(maintenanceStep(0.65)).toBe('verify');
|
||||
expect(maintenanceStep(0.899)).toBe('verify');
|
||||
expect(maintenanceStep(0.9)).toBe('done');
|
||||
expect(maintenanceStep(1)).toBe('done');
|
||||
});
|
||||
|
||||
it('holds the clock at 03:12 while the window opens, cuts over at 03:51, then goes live', () => {
|
||||
expect(maintenanceClock(0, live)).toBe('03:12');
|
||||
expect(maintenanceClock(0.1, live)).toBe('03:12');
|
||||
expect(maintenanceClock(0.5, live)).toBe('03:32');
|
||||
expect(maintenanceClock(0.9, live)).toBe('03:51');
|
||||
expect(maintenanceClock(0.999, live)).toBe('03:51');
|
||||
expect(maintenanceClock(1, live)).toBe('14:07');
|
||||
let previous = -1;
|
||||
for (let p = 0; p < 1; p += 0.01) {
|
||||
const minutes = Number(maintenanceClock(p, live).slice(3));
|
||||
expect(minutes).toBeGreaterThanOrEqual(previous);
|
||||
previous = minutes;
|
||||
}
|
||||
});
|
||||
|
||||
it('writes the sub-line from the step, and a rollback while reversing', () => {
|
||||
expect(maintenanceSubline(0.02, false, live)).toBe('MAINTENANCE WINDOW · 03:12');
|
||||
expect(maintenanceSubline(0.92, false, live)).toBe('CUTOVER OK · 03:51');
|
||||
expect(maintenanceSubline(0.5, true, live)).toMatch(/^ROLLBACK CHECKPOINT · 03:\d\d$/u);
|
||||
expect(maintenanceSubline(0.97, false, live)).toBe('CUTOVER OK · 03:51');
|
||||
});
|
||||
|
||||
it('latches a rollback while progress falls and clears it when it rises', () => {
|
||||
const track = createMaintenanceDirection();
|
||||
expect(track(0.2)).toBe(false);
|
||||
expect(track(0.3)).toBe(false);
|
||||
expect(track(0.25)).toBe(true);
|
||||
expect(track(0.25)).toBe(true);
|
||||
expect(track(0.2505)).toBe(true);
|
||||
expect(track(0.3)).toBe(false);
|
||||
expect(track(0.2)).toBe(true);
|
||||
expect(track(null)).toBe(false);
|
||||
expect(track(0.9)).toBe(false);
|
||||
expect(track(0)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useMaintenanceWindow', () => {
|
||||
const snapshot = (activeSectionId: SectionId, a: number, n: number | null): JourneySnapshot => ({
|
||||
scrollY: 0,
|
||||
routeProgress: 0.4,
|
||||
direction: 0,
|
||||
acceleration: 0,
|
||||
driveSpeed: 0,
|
||||
activeSectionId,
|
||||
chapter: 'infrastructure',
|
||||
sign: null,
|
||||
arrived: false,
|
||||
sectionActivation: { layoutRevision: 1, activeHeadingOffsetVh: a, nextHeadingOffsetVh: n },
|
||||
});
|
||||
|
||||
it('steps html[data-maintenance-step] and the sub-line from scroll, then cleans up', async () => {
|
||||
const journey = shallowRef(snapshot('experience', -1, 1));
|
||||
let subline = shallowRef('');
|
||||
const wrapper = mount(defineComponent({
|
||||
setup() {
|
||||
subline = useMaintenanceWindow(journey) as typeof subline;
|
||||
return () => h('span', subline.value);
|
||||
},
|
||||
}));
|
||||
const root = document.documentElement;
|
||||
await nextTick();
|
||||
expect(root.dataset.maintenanceStep).toBeUndefined();
|
||||
expect(subline.value).toBe('');
|
||||
|
||||
journey.value = snapshot('infrastructure', 0, 2);
|
||||
await nextTick();
|
||||
expect(root.dataset.maintenanceStep).toBe('open');
|
||||
expect(subline.value).toBe('MAINTENANCE WINDOW \u00b7 03:12');
|
||||
|
||||
journey.value = snapshot('infrastructure', -1.84, 0.16);
|
||||
await nextTick();
|
||||
expect(root.dataset.maintenanceStep).toBe('done');
|
||||
expect(subline.value).toBe('CUTOVER OK \u00b7 03:51');
|
||||
|
||||
journey.value = snapshot('infrastructure', -1, 1);
|
||||
await nextTick();
|
||||
expect(root.dataset.maintenanceStep).toBe('transfer');
|
||||
expect(subline.value).toMatch(/^ROLLBACK CHECKPOINT/u);
|
||||
|
||||
journey.value = snapshot('selected-builds', 0, 2);
|
||||
await nextTick();
|
||||
expect(root.dataset.maintenanceStep).toBeUndefined();
|
||||
expect(subline.value).toBe('');
|
||||
|
||||
journey.value = snapshot('infrastructure', -0.4, 1.6);
|
||||
await nextTick();
|
||||
expect(root.dataset.maintenanceStep).toBe('backup');
|
||||
wrapper.unmount();
|
||||
expect(root.dataset.maintenanceStep).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user