Add motivational training challenges and shared club goals
This commit is contained in:
@@ -6,7 +6,9 @@ import {
|
||||
buildDriverTrainingStats,
|
||||
buildDriverTimingChannelStats,
|
||||
buildTrainingAchievements,
|
||||
buildTrainingHighlights,
|
||||
buildTrainingMarkerComparisons,
|
||||
buildTrainingMissions,
|
||||
buildTrainingPaceBuckets,
|
||||
buildTrainingRecommendation,
|
||||
buildTrainingStintAnalysis,
|
||||
@@ -15,6 +17,7 @@ import {
|
||||
getBestTrainingDurationResult,
|
||||
getDriverTrainingLaps,
|
||||
getNextTrainingMilestone,
|
||||
getClubTrainingGoalProgress,
|
||||
getTrainingGoalProgress,
|
||||
normalizeTrainingState,
|
||||
splitTrainingStints,
|
||||
@@ -48,6 +51,13 @@ test("normalization removes invalid and duplicate training records", () => {
|
||||
{ id: "marker_1", driverId: "driver_1", classId: "class_1", timestamp: 5000, type: "tires", note: " New tires " },
|
||||
{ id: "marker_invalid", driverId: "", classId: "class_1", timestamp: 5000 },
|
||||
],
|
||||
clubGoal: {
|
||||
id: "club_goal_1",
|
||||
title: " Club to 10000 ",
|
||||
reward: " Pizza ",
|
||||
targetLaps: 10000,
|
||||
baselineLaps: 120,
|
||||
},
|
||||
}, { uid: createUid() });
|
||||
|
||||
assert.equal(normalized.goals.length, 1);
|
||||
@@ -57,6 +67,9 @@ test("normalization removes invalid and duplicate training records", () => {
|
||||
assert.deepEqual(normalized.shares.map((share) => share.token), ["a".repeat(24)]);
|
||||
assert.equal(normalized.markers.length, 1);
|
||||
assert.equal(normalized.markers[0].note, "New tires");
|
||||
assert.equal(normalized.clubGoal.title, "Club to 10000");
|
||||
assert.equal(normalized.clubGoal.reward, "Pizza");
|
||||
assert.equal(normalized.clubGoal.baselineLaps, 120);
|
||||
});
|
||||
|
||||
test("ledger sync retains archived laps and rebuilds current session laps", () => {
|
||||
@@ -382,6 +395,47 @@ test("automatic achievements expose unlocked and upcoming personal goals", () =>
|
||||
assert.equal(achievements.find((item) => item.id === "laps_1000").percent, 52);
|
||||
assert.equal(achievements.find((item) => item.id === "days_10").unlocked, false);
|
||||
assert.equal(achievements.find((item) => item.id === "five_minute").unlocked, true);
|
||||
assert.equal(achievements.find((item) => item.id === "laps_250").unlocked, true);
|
||||
assert.equal(achievements.find((item) => item.id === "controlled_10").unlocked, false);
|
||||
});
|
||||
|
||||
test("daily missions recognize volume, control, and completed five-minute stints", () => {
|
||||
const missions = buildTrainingMissions({
|
||||
todayLaps: 12,
|
||||
todayControlledLapStreak: 5,
|
||||
todayCompletedFiveMinuteStints: 1,
|
||||
});
|
||||
|
||||
assert.equal(missions.find((item) => item.id === "laps_today").percent, 60);
|
||||
assert.equal(missions.find((item) => item.id === "controlled_today").completed, true);
|
||||
assert.equal(missions.find((item) => item.id === "five_minute_today").completed, true);
|
||||
});
|
||||
|
||||
test("training highlights prioritize current personal progress", () => {
|
||||
const highlights = buildTrainingHighlights({
|
||||
totalLaps: 600,
|
||||
todayLaps: 24,
|
||||
personalBestGainMs: 320,
|
||||
trendSampleReady: true,
|
||||
averageTrendMs: 180,
|
||||
consistencyTrendPct: 1.4,
|
||||
todayControlledLapStreak: 6,
|
||||
});
|
||||
|
||||
assert.deepEqual(highlights.map((item) => item.id), ["today_laps", "personal_best", "pace_gain"]);
|
||||
});
|
||||
|
||||
test("club goal progress starts at its saved baseline and is bounded", () => {
|
||||
const progress = getClubTrainingGoalProgress({
|
||||
ledger: Array.from({ length: 175 }, (_, index) => ({ id: `lap_${index}` })),
|
||||
clubGoal: { targetLaps: 100, baselineLaps: 125 },
|
||||
});
|
||||
|
||||
assert.equal(progress.currentLaps, 50);
|
||||
assert.equal(progress.remainingLaps, 50);
|
||||
assert.equal(progress.percent, 50);
|
||||
assert.equal(progress.reached, false);
|
||||
assert.equal(getClubTrainingGoalProgress({ ledger: [] }), null);
|
||||
});
|
||||
|
||||
test("pace buckets use a trimmed average and keep classes isolated", () => {
|
||||
|
||||
Reference in New Issue
Block a user