diff --git a/src/frontend/components/Standings/createStandings.spec.ts b/src/frontend/components/Standings/createStandings.spec.ts index fbe3976..d466424 100644 --- a/src/frontend/components/Standings/createStandings.spec.ts +++ b/src/frontend/components/Standings/createStandings.spec.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { createStandings, sliceRelevantDrivers } from './createStandings'; +import { createDriverStandings, groupStandingsByClass, sliceRelevantDrivers } from './createStandings'; import type { Session, Telemetry, SessionInfo } from '@irdashies/types'; describe('createStandings', () => { @@ -150,7 +150,7 @@ describe('createStandings', () => { describe('sliceRelevantDrivers', () => { interface DummyStanding { name: string; isPlayer?: boolean } - it('should return only top 3 drivers for class without player', () => { + it('should return only top 3 drivers for classes outside of player\'s class', () => { const results: [string, DummyStanding[]][] = [ [ 'GT3', @@ -174,7 +174,7 @@ describe('createStandings', () => { ], ]; - const filteredDrivers = sliceRelevantDrivers(results); + const filteredDrivers = sliceRelevantDrivers(results, 'GT4'); expect(filteredDrivers).toEqual([ [ @@ -194,12 +194,58 @@ describe('createStandings', () => { ]); }); + it('should return all player\'s class even when player is not in standings', () => { + const results: [string, DummyStanding[]][] = [ + [ + 'GT3', + [ + { name: '1. Bob' }, + { name: '2. Alice' }, + { name: '3. Charlie' }, + { name: '4. David' }, + { name: '5. Eve' }, + ], + ], + [ + 'GT4', + [ + { name: '1. Clark' }, + { name: '2. Richard' }, + { name: '3. Sam' }, + { name: '4. Bingo' }, + { name: '5. Tod' }, + { name: '6. Wallace' }, + ], + ], + ]; + + const filteredDrivers = sliceRelevantDrivers(results, 'GT4'); + + expect(filteredDrivers).toEqual([ + [ + 'GT3', + [{ name: '1. Bob' }, { name: '2. Alice' }, { name: '3. Charlie' }], + ], + [ + 'GT4', + [ + { name: '1. Clark' }, + { name: '2. Richard' }, + { name: '3. Sam' }, + { name: '4. Bingo' }, + { name: '5. Tod' }, + { name: '6. Wallace' }, + ], + ], + ]); + }); + it('should return all drivers when less than 3 available for class without player', () => { const results: [string, DummyStanding[]][] = [ ['GT3', [{ name: 'Bob' }, { name: 'Alice' }]], ]; - const filteredDrivers = sliceRelevantDrivers(results); + const filteredDrivers = sliceRelevantDrivers(results, 'GT3'); expect(filteredDrivers).toEqual([ ['GT3', [{ name: 'Bob' }, { name: 'Alice' }]], @@ -227,7 +273,7 @@ describe('createStandings', () => { ], ]; - const filteredDrivers = sliceRelevantDrivers(results); + const filteredDrivers = sliceRelevantDrivers(results, 'GT3'); expect(filteredDrivers).toEqual([ [ @@ -267,7 +313,7 @@ describe('createStandings', () => { ], ]; - const filteredDrivers = sliceRelevantDrivers(results); + const filteredDrivers = sliceRelevantDrivers(results, 'GT3'); expect(filteredDrivers).toEqual([ [ @@ -286,3 +332,44 @@ describe('createStandings', () => { }); }); }); + + +/** + * This method will create the standings for the current session + * It will group the standings by class and slice the relevant drivers + * + * Only used to simplify testing + */ +function createStandings( + session?: Session, + telemetry?: Telemetry, + currentSession?: SessionInfo, + options?: { + buffer?: number; + numNonClassDrivers?: number; + } +) { + const standings = createDriverStandings( + { + playerIdx: session?.DriverInfo?.DriverCarIdx, + drivers: session?.DriverInfo?.Drivers, + qualifyingResults: session?.QualifyResultsInfo?.Results, + }, + { + carIdxF2TimeValue: telemetry?.CarIdxF2Time?.value, + carIdxOnPitRoadValue: telemetry?.CarIdxOnPitRoad?.value, + carIdxTrackSurfaceValue: telemetry?.CarIdxTrackSurface?.value, + radioTransmitCarIdx: telemetry?.RadioTransmitCarIdx?.value, + }, + { + resultsPositions: currentSession?.ResultsPositions, + resultsFastestLap: currentSession?.ResultsFastestLap, + sessionType: currentSession?.SessionType, + } + ); + const driverClass = session?.DriverInfo?.Drivers?.find( + (driver) => driver.CarIdx === session?.DriverInfo?.DriverCarIdx + )?.CarClassID; + const grouped = groupStandingsByClass(standings); + return sliceRelevantDrivers(grouped, driverClass, options); +}; diff --git a/src/frontend/components/Standings/createStandings.ts b/src/frontend/components/Standings/createStandings.ts index 0b72177..1b6b6d8 100644 --- a/src/frontend/components/Standings/createStandings.ts +++ b/src/frontend/components/Standings/createStandings.ts @@ -1,15 +1,5 @@ -import type { - SessionInfo, - SessionResults, - Driver, - Session, - Telemetry, -} from '@irdashies/types'; -import { - calculateIRatingGain, - RaceResult, - CalculationResult, -} from '@irdashies/utils/iratingGain'; +import type { SessionResults, Driver } from '@irdashies/types'; +import { calculateIRatingGain, RaceResult, CalculationResult } from '@irdashies/utils/iratingGain'; export interface Standings { carIdx: number; @@ -207,18 +197,25 @@ export const augmentStandingsWithIRating = ( * This method will slice up the standings and return only the relevant drivers * Top 3 drivers are always included for each class * Within the player's class it will include the player and 5 drivers before and after + * + * @param groupedStandings - The grouped standings to slice + * @param driverClass - The class of the player + * @param options.buffer - The number of drivers to include before and after the player + * @param options.numNonClassDrivers - The number of drivers to include in classes outside of the player's class + * @returns The sliced standings */ export const sliceRelevantDrivers = ( groupedStandings: [string, T[]][], - { buffer = 3 } = {} + driverClass: string | number | undefined, + { buffer = 3, numNonClassDrivers = 3 } = {} ): [string, T[]][] => { // this is honestly a bit too complicated to maintain so after some testing will // probably simplify it so its a bit more readable return groupedStandings.map(([classIdx, standings]) => { const playerIndex = standings.findIndex((driver) => driver.isPlayer); - if (playerIndex < 0) { + if (String(driverClass) !== classIdx) { // if player is not in this class, return only top 3 drivers in that class - return [classIdx, standings.slice(0, 3)]; + return [classIdx, standings.slice(0, numNonClassDrivers)]; } // if there are less than 10 drivers, return all @@ -252,40 +249,3 @@ export const sliceRelevantDrivers = ( return [classIdx, sliced]; }); }; - -/** - * This method will create the standings for the current session - * It will group the standings by class and slice the relevant drivers - */ -export const createStandings = ( - session?: Session, - telemetry?: Telemetry, - currentSession?: SessionInfo, - options?: { - sliceRelevantDrivers?: { - buffer?: number; - }; - } -) => { - const standings = createDriverStandings( - { - playerIdx: session?.DriverInfo?.DriverCarIdx, - drivers: session?.DriverInfo?.Drivers, - qualifyingResults: session?.QualifyResultsInfo?.Results, - }, - { - carIdxF2TimeValue: telemetry?.CarIdxF2Time?.value, - carIdxOnPitRoadValue: telemetry?.CarIdxOnPitRoad?.value, - carIdxTrackSurfaceValue: telemetry?.CarIdxTrackSurface?.value, - radioTransmitCarIdx: telemetry?.RadioTransmitCarIdx?.value, - }, - { - resultsPositions: currentSession?.ResultsPositions, - resultsFastestLap: currentSession?.ResultsFastestLap, - sessionType: currentSession?.SessionType, - } - ); - - const grouped = groupStandingsByClass(standings); - return sliceRelevantDrivers(grouped, options?.sliceRelevantDrivers); -}; diff --git a/src/frontend/components/Standings/hooks/useDriverStandings.tsx b/src/frontend/components/Standings/hooks/useDriverStandings.tsx index e7b77c7..9b0c34e 100644 --- a/src/frontend/components/Standings/hooks/useDriverStandings.tsx +++ b/src/frontend/components/Standings/hooks/useDriverStandings.tsx @@ -51,6 +51,9 @@ export const useDriverStandings = ({ buffer }: { buffer: number }) => { } ); const groupedByClass = groupStandingsByClass(initialStandings); + const driverClass = sessionDrivers?.find( + (driver) => driver.CarIdx === driverCarIdx + )?.CarClassID; // Calculate iRating changes for race sessions const augmentedGroupedByClass = @@ -58,7 +61,7 @@ export const useDriverStandings = ({ buffer }: { buffer: number }) => { ? augmentStandingsWithIRating(groupedByClass) : groupedByClass; - return sliceRelevantDrivers(augmentedGroupedByClass, { buffer }); + return sliceRelevantDrivers(augmentedGroupedByClass, driverClass, { buffer }); }, [ driverCarIdx, sessionDrivers,