mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-08 04:06:18 -04:00
Add shared Meaning primitive and pure interpretation helpers for interval, tyre age, and championship gap columns on the live tower, tyre deg panel, and championship hub. Thresholds are exported consts (undercut window tied to PIT_LOSS_SECONDS from #13). Co-authored-by: Cursor <cursoragent@cursor.com>
72 lines
2.5 KiB
TypeScript
72 lines
2.5 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
INTERVAL_DRS_MAX_SECONDS,
|
|
INTERVAL_UNDERCUT_MAX_SECONDS,
|
|
INTERVAL_UNDERCUT_MIN_SECONDS,
|
|
MAX_POINTS_PER_ROUND,
|
|
TYRE_CLIFF_LAPS,
|
|
intervalMeaning,
|
|
pointsGapMeaning,
|
|
tyreAgeMeaning,
|
|
} from '../lib/meaning'
|
|
|
|
describe('intervalMeaning', () => {
|
|
it('returns DRS range below the threshold', () => {
|
|
expect(intervalMeaning(0.4)?.caption).toBe('DRS range')
|
|
expect(intervalMeaning(INTERVAL_DRS_MAX_SECONDS - 0.01)?.caption).toBe('DRS range')
|
|
})
|
|
|
|
it('returns undercut window in the middle band', () => {
|
|
expect(intervalMeaning(INTERVAL_UNDERCUT_MIN_SECONDS)?.caption).toBe('undercut window')
|
|
expect(intervalMeaning(2.0)?.caption).toBe('undercut window')
|
|
expect(intervalMeaning(INTERVAL_UNDERCUT_MAX_SECONDS)?.caption).toBe('undercut window')
|
|
})
|
|
|
|
it('returns null outside known bands', () => {
|
|
expect(intervalMeaning(INTERVAL_UNDERCUT_MAX_SECONDS + 0.5)).toBeNull()
|
|
expect(intervalMeaning(10)).toBeNull()
|
|
expect(intervalMeaning(null)).toBeNull()
|
|
expect(intervalMeaning(undefined)).toBeNull()
|
|
expect(intervalMeaning(-1)).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('tyreAgeMeaning', () => {
|
|
it('labels fresh, mid-life, and laps-to-cliff for SOFT', () => {
|
|
const cliff = TYRE_CLIFF_LAPS.SOFT
|
|
expect(tyreAgeMeaning('SOFT', 2)?.caption).toBe('fresh')
|
|
expect(tyreAgeMeaning('SOFT', Math.ceil(cliff * 0.5))?.caption).toBe('mid-life')
|
|
expect(tyreAgeMeaning('SOFT', cliff - 2)?.caption).toBe('~2 laps to cliff')
|
|
expect(tyreAgeMeaning('SOFT', cliff + 5)?.caption).toBe('past cliff')
|
|
})
|
|
|
|
it('handles unknown compounds with defaults', () => {
|
|
expect(tyreAgeMeaning('UNKNOWN', 3)?.caption).toBe('fresh')
|
|
expect(tyreAgeMeaning(undefined, 3)?.caption).toBe('fresh')
|
|
})
|
|
|
|
it('returns null for invalid age', () => {
|
|
expect(tyreAgeMeaning('MEDIUM', null)).toBeNull()
|
|
expect(tyreAgeMeaning('MEDIUM', -1)).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('pointsGapMeaning', () => {
|
|
it('computes catchable pts/round', () => {
|
|
const result = pointsGapMeaning(40, 4, 'VER')
|
|
expect(result?.caption).toBe('~10 pts/round')
|
|
expect(result?.title).toContain('VER')
|
|
})
|
|
|
|
it('marks uncatchable gaps', () => {
|
|
const max = 3 * MAX_POINTS_PER_ROUND
|
|
expect(pointsGapMeaning(max + 1, 3, 'VER')?.caption).toBe('out of reach')
|
|
})
|
|
|
|
it('returns null for leader or invalid input', () => {
|
|
expect(pointsGapMeaning(0, 4)).toBeNull()
|
|
expect(pointsGapMeaning(10, 0)).toBeNull()
|
|
expect(pointsGapMeaning(null, 4)).toBeNull()
|
|
})
|
|
})
|