mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-08 04:06:18 -04:00
Refresh live sprint qualifying timing
This commit is contained in:
@@ -187,6 +187,28 @@ describe('TimingTower', () => {
|
||||
render(<TimingTower rows={[]} />)
|
||||
expect(screen.getByText(/no driver timing rows/i)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders the SQ1 cutoff after P17 and marks rows below as at risk', () => {
|
||||
const sprintRows = Array.from({ length: 22 }, (_, index) =>
|
||||
makeRow(String(index + 1), index + 1, `D${index + 1}`),
|
||||
)
|
||||
render(
|
||||
<TimingTower
|
||||
rows={sprintRows}
|
||||
session={{
|
||||
MeetingName: 'British Grand Prix',
|
||||
CircuitName: 'Silverstone',
|
||||
SessionType: 'Sprint Qualifying',
|
||||
SessionName: 'Sprint Qualifying',
|
||||
}}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByTestId('qualifying-cutoff')).toHaveTextContent('SQ1 cutoff')
|
||||
expect(screen.getByTestId('qualifying-cutoff')).toHaveTextContent('P17 advance')
|
||||
expect(screen.getByText('D18').closest('tr')).toHaveClass('danger-row')
|
||||
expect(screen.getByText('D17').closest('tr')).not.toHaveClass('danger-row')
|
||||
})
|
||||
})
|
||||
|
||||
describe('PinnedDrivers', () => {
|
||||
|
||||
@@ -39,6 +39,7 @@ describe('isRaceSession', () => {
|
||||
it('only treats race and sprint sessions as races', () => {
|
||||
expect(isRaceSession('Race')).toBe(true)
|
||||
expect(isRaceSession('Sprint')).toBe(true)
|
||||
expect(isRaceSession('Sprint Qualifying')).toBe(false)
|
||||
expect(isRaceSession('Qualifying')).toBe(false)
|
||||
expect(isRaceSession('Practice')).toBe(false)
|
||||
expect(isRaceSession('')).toBe(false)
|
||||
|
||||
@@ -4,10 +4,13 @@ import {
|
||||
compoundLetter,
|
||||
extrapolateClock,
|
||||
latestRaceControl,
|
||||
liveSessionDisplay,
|
||||
loadPinnedDrivers,
|
||||
mergeVisibleSectors,
|
||||
positionDeltaClass,
|
||||
parseLiveStateEvent,
|
||||
rcFlagClass,
|
||||
rowsWithVisibleSectors,
|
||||
savePinnedDrivers,
|
||||
sortLiveTimingRows,
|
||||
togglePin,
|
||||
@@ -17,7 +20,8 @@ import {
|
||||
tyreLabel,
|
||||
windDirectionLabel,
|
||||
} from '../lib/live'
|
||||
import type { LiveStreamData } from '../types'
|
||||
import type { LiveDriverData, LiveSectorData, LiveStreamData } from '../types'
|
||||
import type { LiveTimingRow } from '../lib/live'
|
||||
|
||||
const snapshot: LiveStreamData = {
|
||||
Drivers: {
|
||||
@@ -106,6 +110,51 @@ const snapshot: LiveStreamData = {
|
||||
Stints: {},
|
||||
}
|
||||
|
||||
function sector(value: string, over: Partial<LiveSectorData> = {}): LiveSectorData {
|
||||
return { Value: value, PersonalFastest: false, OverallFastest: false, ...over }
|
||||
}
|
||||
|
||||
function timingRow(
|
||||
number: string,
|
||||
position: number,
|
||||
driver: Partial<LiveDriverData> = {},
|
||||
): LiveTimingRow {
|
||||
return {
|
||||
RacingNumber: number,
|
||||
Position: position,
|
||||
Driver: {
|
||||
RacingNumber: number,
|
||||
Position: position,
|
||||
PrevPosition: position,
|
||||
GapToLeader: '',
|
||||
Interval: '',
|
||||
LastLapTime: '',
|
||||
LastLapPB: false,
|
||||
LastLapOB: false,
|
||||
BestLapTime: '',
|
||||
BestLapPB: false,
|
||||
BestLapOB: false,
|
||||
BestLapNum: 0,
|
||||
InPit: false,
|
||||
PitOut: false,
|
||||
Retired: false,
|
||||
KnockedOut: false,
|
||||
Cutoff: false,
|
||||
OnFlyingLap: false,
|
||||
NumberOfLaps: 0,
|
||||
SpeedTrap: '',
|
||||
Sectors: [],
|
||||
...driver,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function rows(count: number, knockedOut = 0): LiveTimingRow[] {
|
||||
return Array.from({ length: count }, (_, index) =>
|
||||
timingRow(String(index + 1), index + 1, { KnockedOut: index >= count - knockedOut }),
|
||||
)
|
||||
}
|
||||
|
||||
describe('live transforms', () => {
|
||||
it('parses live EventSource snapshots without changing PascalCase data', () => {
|
||||
const parsed = parseLiveStateEvent(JSON.stringify({ is_live: true, data: snapshot }))
|
||||
@@ -168,6 +217,91 @@ describe('track status mapping', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('live qualifying display', () => {
|
||||
it('puts the SQ1 cutoff after P17 for a 22-car sprint qualifying session', () => {
|
||||
const display = liveSessionDisplay(
|
||||
{ MeetingName: 'British Grand Prix', CircuitName: 'Silverstone', SessionType: 'Sprint Qualifying', SessionName: 'Sprint Qualifying' },
|
||||
rows(22),
|
||||
)
|
||||
expect(display.phaseLabel).toBe('SQ1')
|
||||
expect(display.cutoffPosition).toBe(17)
|
||||
expect(display.advanceCount).toBe(17)
|
||||
expect(display.atRiskStart).toBe(18)
|
||||
expect(display.atRiskEnd).toBe(22)
|
||||
})
|
||||
|
||||
it('keeps the normal Q1 cutoff after P15 for a 20-car qualifying session', () => {
|
||||
const display = liveSessionDisplay(
|
||||
{ MeetingName: 'Monaco Grand Prix', CircuitName: 'Monaco', SessionType: 'Qualifying', SessionName: 'Qualifying' },
|
||||
rows(20),
|
||||
)
|
||||
expect(display.phaseLabel).toBe('Q1')
|
||||
expect(display.cutoffPosition).toBe(15)
|
||||
})
|
||||
|
||||
it('moves phase 2 cutoff after P10 once five cars are knocked out', () => {
|
||||
const display = liveSessionDisplay(
|
||||
{ MeetingName: 'Monaco Grand Prix', CircuitName: 'Monaco', SessionType: 'Qualifying', SessionName: 'Qualifying' },
|
||||
rows(20, 5),
|
||||
)
|
||||
expect(display.phaseLabel).toBe('Q2')
|
||||
expect(display.cutoffPosition).toBe(10)
|
||||
})
|
||||
|
||||
it('shows no cutoff for race sessions or Q3', () => {
|
||||
expect(
|
||||
liveSessionDisplay(
|
||||
{ MeetingName: 'Monaco Grand Prix', CircuitName: 'Monaco', SessionType: 'Race', SessionName: 'Race' },
|
||||
rows(20),
|
||||
).cutoffPosition,
|
||||
).toBeNull()
|
||||
expect(
|
||||
liveSessionDisplay(
|
||||
{ MeetingName: 'Monaco Grand Prix', CircuitName: 'Monaco', SessionType: 'Qualifying', SessionName: 'Q3' },
|
||||
rows(10),
|
||||
).cutoffPosition,
|
||||
).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('visible sector display', () => {
|
||||
it('holds S1 and S2 through temporary blanks while a flying lap is active', () => {
|
||||
const first = [timingRow('4', 1, {
|
||||
NumberOfLaps: 3,
|
||||
OnFlyingLap: true,
|
||||
Sectors: [sector('29.111'), sector('41.222'), sector('')],
|
||||
})]
|
||||
const held = mergeVisibleSectors({}, first)
|
||||
const blank = [timingRow('4', 1, {
|
||||
NumberOfLaps: 3,
|
||||
OnFlyingLap: false,
|
||||
Sectors: [sector(''), sector(''), sector('')],
|
||||
})]
|
||||
const next = mergeVisibleSectors(held, blank)
|
||||
const visibleRows = rowsWithVisibleSectors(blank, next)
|
||||
|
||||
expect(visibleRows[0].Driver.Sectors[0].Value).toBe('29.111')
|
||||
expect(visibleRows[0].Driver.Sectors[1].Value).toBe('41.222')
|
||||
})
|
||||
|
||||
it('clears held sectors after the lap completes and the feed goes blank', () => {
|
||||
const first = mergeVisibleSectors({}, [timingRow('4', 1, {
|
||||
NumberOfLaps: 3,
|
||||
LastLapTime: '1:30.000',
|
||||
OnFlyingLap: true,
|
||||
Sectors: [sector('29.111'), sector('41.222'), sector('20.333')],
|
||||
})])
|
||||
const next = mergeVisibleSectors(first, [timingRow('4', 1, {
|
||||
NumberOfLaps: 4,
|
||||
LastLapTime: '1:30.000',
|
||||
OnFlyingLap: false,
|
||||
Sectors: [sector(''), sector(''), sector('')],
|
||||
})])
|
||||
|
||||
expect(next['4']).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('weather and stint helpers', () => {
|
||||
it('maps wind direction degrees to compass points', () => {
|
||||
expect(windDirectionLabel(0)).toBe('N')
|
||||
|
||||
Reference in New Issue
Block a user