mnswpr/test/date-bucket.test.js
Ayo 9e1114be78 feat: reusable, time-windowed leaderboard (@cozy-games/leaderboard) (#1)
Extract the leaderboard into a generic, backend-agnostic package and add
rolling Today/Week/Month/All-Time windows, a web component, and local-first
development against the Firestore emulator.

Package (leaderboard/):
- LeaderBoardService: backend-agnostic core via a storage-adapter seam, with
  Firebase and Supabase adapters (Supabase client injected, no added dep)
- Rolling time windows (last 24h / 7d / 30d) with hover tooltips; top-N by score
- <cozy-leaderboard> web component built on web-component-base: compose the UI
  in HTML, configure the backend once in JS
- Every user-facing string is configurable (labels, tooltips, empty/loading/
  error messages, anonymous name) so i18n lives in the app; README + CONFIGURATION

App (mnswpr.com):
- Compose the board declaratively in index.html via <cozy-leaderboard>
- Nickname + randomized greeting bar; score submission through the element
- Legends: the current all-time leaders frozen into a static /legends page
- Firebase config and leaderboard namespace via Vite env vars; emulator-first
  local dev (VITE_FIRESTORE_EMULATOR)

Firebase schema-as-code:
- firebase.json, .firebaserc, firestore.rules (public reads, create-only scores,
  no client updates/deletes, namespace-generalized), empty indexes (rolling
  windows need none)
- prod (mw-*) vs dev/test (mw-test-*) separation by collection namespace
- emulator config, seed script, and docs (firebase-leaderboards.md,
  leaderboard-env-migration.md, AYO.md)

Utils/tests: UTC date-bucket helper (retained as metadata) with Vitest coverage.

Reviewed-on: git.ayo.run:3000/
Co-authored-by: Ayo <ayo@ayco.io>
Co-committed-by: Ayo <ayo@ayco.io>
2026-07-03 11:41:14 +00:00

41 lines
1.5 KiB
JavaScript

import { describe, it, expect } from 'vitest'
import { dayKey, weekKey, monthKey, buckets } from '../utils/date-bucket/date-bucket.js'
describe('date-bucket', () => {
it('builds a zero-padded UTC day key', () => {
expect(dayKey(new Date('2026-07-03T12:00:00Z'))).toBe('2026-07-03')
expect(dayKey(new Date('2026-01-09T00:00:00Z'))).toBe('2026-01-09')
})
it('builds a UTC month key', () => {
expect(monthKey(new Date('2026-07-03T12:00:00Z'))).toBe('2026-07')
expect(monthKey(new Date('2026-12-31T23:59:59Z'))).toBe('2026-12')
})
it('builds an ISO week key', () => {
// 2026-07-03 falls in ISO week 27
expect(weekKey(new Date('2026-07-03T12:00:00Z'))).toBe('2026-W27')
// 2026-01-01 is a Thursday -> ISO week 1 of 2026
expect(weekKey(new Date('2026-01-01T00:00:00Z'))).toBe('2026-W01')
})
it('rolls the ISO week-year back at the January boundary', () => {
// 2027-01-01 is a Friday -> still ISO week 53 of 2026
expect(weekKey(new Date('2027-01-01T00:00:00Z'))).toBe('2026-W53')
})
it('uses UTC, not local time', () => {
// Just before UTC midnight is still the 3rd in UTC
expect(dayKey(new Date('2026-07-03T23:59:59Z'))).toBe('2026-07-03')
// Just after is the 4th
expect(dayKey(new Date('2026-07-04T00:00:01Z'))).toBe('2026-07-04')
})
it('returns all three keys together', () => {
expect(buckets(new Date('2026-07-03T12:00:00Z'))).toEqual({
day: '2026-07-03',
week: '2026-W27',
month: '2026-07'
})
})
})