feat: Challenges Page and Challenge Solve Modal 1.03

This commit is contained in:
OpenVelo Agent
2026-07-23 03:11:16 +00:00
parent c434e5d3a2
commit 1c64f057f9
10 changed files with 229 additions and 63 deletions
+49 -3
View File
@@ -83,10 +83,10 @@ describe('ChallengesPage (gate + countdown + auto-reload wiring)', () => {
it('starts in unconfigured state so the gate panel would render', () => {
expect(eventStore.state()).toBe('unconfigured');
expect(formatDdHhMm(0)).toBe('00:00:00');
expect(formatDdHhMm(0)).toBe('00:00:00:00');
});
it('shows live countdown in DD:HH:mm when the admin sets a countdown window', () => {
it('shows live countdown in DD:HH:mm:ss when the admin sets a countdown window', () => {
const future = new Date(Date.now() + 90_061_000).toISOString();
const past = new Date(Date.now() - 60_000).toISOString();
eventStore.applyServerStatus({
@@ -99,7 +99,53 @@ describe('ChallengesPage (gate + countdown + auto-reload wiring)', () => {
});
expect(eventStore.state()).toBe('countdown');
expect(eventStore.countdownText()).toBe('01:01:01');
const text = eventStore.countdownText();
expect(text).toMatch(/^01:01:01:\d{2}$/);
const [d, h, m, s] = text.split(':').map(Number);
expect(d).toBe(1);
expect(h).toBe(1);
expect(m).toBe(1);
expect(s).toBeGreaterThanOrEqual(0);
expect(s).toBeLessThanOrEqual(2);
});
it('formats the final minute second-by-second so the user can see time elapse', () => {
eventStore.applyServerStatus({
state: 'countdown',
serverNowUtc: new Date().toISOString(),
eventStartUtc: new Date(Date.now() + 24_000).toISOString(),
eventEndUtc: new Date(Date.now() + 7200_000).toISOString(),
secondsToStart: 24,
secondsToEnd: null,
});
const first = eventStore.countdownText();
expect(first).toMatch(/^00:00:00:\d{2}$/);
expect(Number(first.split(':')[3])).toBeGreaterThanOrEqual(22);
expect(Number(first.split(':')[3])).toBeLessThanOrEqual(24);
eventStore.applyServerStatus({
state: 'countdown',
serverNowUtc: new Date().toISOString(),
eventStartUtc: new Date(Date.now() + 23_000).toISOString(),
eventEndUtc: new Date(Date.now() + 7200_000).toISOString(),
secondsToStart: 23,
secondsToEnd: null,
});
const second = eventStore.countdownText();
expect(Number(second.split(':')[3])).toBeGreaterThanOrEqual(21);
expect(Number(second.split(':')[3])).toBeLessThanOrEqual(23);
eventStore.applyServerStatus({
state: 'countdown',
serverNowUtc: new Date().toISOString(),
eventStartUtc: new Date(Date.now() + 22_000).toISOString(),
eventEndUtc: new Date(Date.now() + 7200_000).toISOString(),
secondsToStart: 22,
secondsToEnd: null,
});
const third = eventStore.countdownText();
expect(Number(third.split(':')[3])).toBeGreaterThanOrEqual(20);
expect(Number(third.split(':')[3])).toBeLessThanOrEqual(22);
});
it('single auto-reload fires exactly once when secondsToStart hits zero', () => {