AI Implementation feature(905): Challenges Page and Challenge Solve Modal 1.01 (#47)
This commit was merged in pull request #47.
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import { Injector, runInInjectionContext } from '@angular/core';
|
||||
import { EventStatusStore } from '../../frontend/src/app/core/services/event-status.store';
|
||||
import { EventStatePayload } from '../../frontend/src/app/core/services/event-status.pure';
|
||||
|
||||
describe('EventStatusStore auto-reload watcher (Job 905)', () => {
|
||||
let store: EventStatusStore;
|
||||
|
||||
function payload(over: Partial<EventStatePayload>): EventStatePayload {
|
||||
const start = new Date(Date.now() + 60_000).toISOString();
|
||||
const end = new Date(Date.now() + 7_200_000).toISOString();
|
||||
return {
|
||||
state: 'countdown',
|
||||
serverNowUtc: new Date().toISOString(),
|
||||
eventStartUtc: start,
|
||||
eventEndUtc: end,
|
||||
secondsToStart: 60,
|
||||
secondsToEnd: null,
|
||||
...over,
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
const injector = Injector.create({ providers: [] });
|
||||
store = runInInjectionContext(injector, () => new EventStatusStore());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (store) store.stop();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('fires the reload handler exactly once when secondsToStart crosses zero via the real setInterval path', () => {
|
||||
const handler = jest.fn();
|
||||
store.reloadAtCountdownZero(handler);
|
||||
|
||||
store.applyServerStatus(
|
||||
payload({ state: 'countdown', secondsToStart: 2, eventStartUtc: new Date(Date.now() + 2000).toISOString() }),
|
||||
);
|
||||
|
||||
jest.advanceTimersByTime(3000);
|
||||
|
||||
expect(handler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('re-arms the latch on a state transition so a later end-of-event zero reloads again', () => {
|
||||
const handler = jest.fn();
|
||||
store.reloadAtCountdownZero(handler);
|
||||
|
||||
store.applyServerStatus(
|
||||
payload({
|
||||
state: 'countdown',
|
||||
secondsToStart: 0,
|
||||
eventStartUtc: new Date(Date.now() - 1000).toISOString(),
|
||||
}),
|
||||
);
|
||||
jest.advanceTimersByTime(2000);
|
||||
expect(handler).toHaveBeenCalledTimes(1);
|
||||
|
||||
store.applyServerStatus(
|
||||
payload({
|
||||
state: 'running',
|
||||
secondsToStart: null,
|
||||
secondsToEnd: 9001,
|
||||
eventStartUtc: new Date(Date.now() - 60_000).toISOString(),
|
||||
eventEndUtc: new Date(Date.now() + 9001_000).toISOString(),
|
||||
}),
|
||||
);
|
||||
jest.advanceTimersByTime(1000);
|
||||
expect(handler).toHaveBeenCalledTimes(1);
|
||||
|
||||
store.applyServerStatus(
|
||||
payload({
|
||||
state: 'running',
|
||||
secondsToStart: null,
|
||||
secondsToEnd: 0,
|
||||
eventStartUtc: new Date(Date.now() - 60_000).toISOString(),
|
||||
eventEndUtc: new Date(Date.now() - 1000).toISOString(),
|
||||
}),
|
||||
);
|
||||
jest.advanceTimersByTime(2000);
|
||||
|
||||
expect(handler).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('registering a new handler replaces the previous one without double-firing', () => {
|
||||
const first = jest.fn();
|
||||
const second = jest.fn();
|
||||
store.reloadAtCountdownZero(first);
|
||||
store.reloadAtCountdownZero(second);
|
||||
|
||||
store.applyServerStatus(
|
||||
payload({
|
||||
state: 'countdown',
|
||||
secondsToStart: 0,
|
||||
eventStartUtc: new Date(Date.now() - 1000).toISOString(),
|
||||
}),
|
||||
);
|
||||
jest.advanceTimersByTime(2000);
|
||||
|
||||
expect(first).not.toHaveBeenCalled();
|
||||
expect(second).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('stop() resets the latch so a fresh handler can fire on the next zero', () => {
|
||||
const first = jest.fn();
|
||||
store.reloadAtCountdownZero(first);
|
||||
store.applyServerStatus(
|
||||
payload({
|
||||
state: 'countdown',
|
||||
secondsToStart: 0,
|
||||
eventStartUtc: new Date(Date.now() - 1000).toISOString(),
|
||||
}),
|
||||
);
|
||||
jest.advanceTimersByTime(2000);
|
||||
expect(first).toHaveBeenCalledTimes(1);
|
||||
|
||||
store.stop();
|
||||
|
||||
const second = jest.fn();
|
||||
store.reloadAtCountdownZero(second);
|
||||
store.applyServerStatus(
|
||||
payload({
|
||||
state: 'countdown',
|
||||
secondsToStart: 0,
|
||||
eventStartUtc: new Date(Date.now() - 1000).toISOString(),
|
||||
}),
|
||||
);
|
||||
jest.advanceTimersByTime(2000);
|
||||
|
||||
expect(second).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not disarm the latch when the same zero-countdown payload is delivered repeatedly', () => {
|
||||
const handler = jest.fn();
|
||||
store.reloadAtCountdownZero(handler);
|
||||
|
||||
const frame = payload({
|
||||
state: 'countdown',
|
||||
secondsToStart: 0,
|
||||
eventStartUtc: new Date(Date.now() - 1000).toISOString(),
|
||||
});
|
||||
|
||||
store.applyServerStatus(frame);
|
||||
store.applyServerStatus(frame);
|
||||
store.applyServerStatus(frame);
|
||||
jest.advanceTimersByTime(2000);
|
||||
|
||||
expect(handler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('subscribeReloadAtCountdownZero unregister prevents the handler from firing', () => {
|
||||
const handler = jest.fn();
|
||||
const unsubscribe = store.subscribeReloadAtCountdownZero(handler);
|
||||
|
||||
unsubscribe();
|
||||
|
||||
store.applyServerStatus(
|
||||
payload({
|
||||
state: 'countdown',
|
||||
secondsToStart: 0,
|
||||
eventStartUtc: new Date(Date.now() - 1000).toISOString(),
|
||||
}),
|
||||
);
|
||||
jest.advanceTimersByTime(2000);
|
||||
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('start() preserves a previously registered reload handler (Job 905 handler ownership)', () => {
|
||||
const handler = jest.fn();
|
||||
store.subscribeReloadAtCountdownZero(handler);
|
||||
|
||||
const fakeSource = {
|
||||
addEventListener: jest.fn(),
|
||||
close: jest.fn(),
|
||||
};
|
||||
store.start(() => fakeSource as any);
|
||||
|
||||
expect((store as any).reloadOnZero).toBe(handler);
|
||||
|
||||
store.applyServerStatus(
|
||||
payload({
|
||||
state: 'countdown',
|
||||
secondsToStart: 0,
|
||||
eventStartUtc: new Date(Date.now() - 1000).toISOString(),
|
||||
}),
|
||||
);
|
||||
jest.advanceTimersByTime(2000);
|
||||
|
||||
expect(handler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user