import assert from "node:assert/strict"; // Regression coverage for a real bug the user reported: moving a driver from one team to // another (by ticking them on the new team's edit form - there is no separate "move" // action) used to only ever ADD the driver to the new team, never remove them from the // old one, so the same driver ended up listed on two teams at once. Also covers the two // related findings from the same investigation: getTeamDriverPool used to ignore the // event's class/participant scoping entirely (showing every driver in the club), and // deleting a driver left them behind in any team's roster forever. // // The event/session fixtures are seeded directly rather than driven through the 4-step // race wizard (already covered by browser_race_setup.js) - only team management itself is // under test here, and the Teams section only renders once a team_race session exists // (see showTeamsSection in event_manager_controller.js). export async function runTeamMembershipSmoke({ evaluate, navigate, savedState, waitFor, pause, base }) { const click = (id) => evaluate(`document.getElementById(${JSON.stringify(id)}).click()`); const submit = (id) => evaluate(`document.getElementById(${JSON.stringify(id)}).requestSubmit()`); const findEvent = async (id) => (await savedState()).events.find((event) => event.id === id); const patchState = async (patch) => { const current = await savedState(); Object.assign(current, patch); const response = await fetch(base + "/api/state", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(current) }); assert.equal(response.ok, true); }; const driverA = { id: "team_membership_driver_a", name: "Team Membership Driver A", classId: "test_class", transponder: "7810001" }; const driverB = { id: "team_membership_driver_b", name: "Team Membership Driver B", classId: "test_class", transponder: "7810002" }; const raceEvent = { id: "team_membership_event", name: "Team Membership Event", classId: "test_class", mode: "race", raceConfig: { participantsConfigured: true, driverIds: [driverA.id, driverB.id], teams: [] }, }; const teamRaceSession = { id: "team_membership_session", eventId: raceEvent.id, name: "Team Race Session", type: "team_race", mode: "race", durationMin: 45, status: "ready" }; const seeded = await savedState(); await patchState({ drivers: [...seeded.drivers, driverA, driverB], events: [...seeded.events, raceEvent], sessions: [...seeded.sessions, teamRaceSession], }); await navigate("race_setup", "raceWizardStepForm"); await click("event-manage-" + raceEvent.id); await waitFor(async () => await evaluate(`Boolean(document.getElementById("teamForm"))`), "teams section visible for a race-mode event with a team_race session"); await evaluate(`(() => { const form = document.getElementById("teamForm"); form.elements.namedItem("teamName").value = "Membership Team A"; })()`); await evaluate(`document.querySelector('[form="teamForm"][name="teamDriverIds"][value="${driverA.id}"]').checked = true`); await submit("teamForm"); await waitFor(async () => (await findEvent(raceEvent.id)).raceConfig.teams.some((team) => team.name === "Membership Team A"), "team A created"); await evaluate(`(() => { const form = document.getElementById("teamForm"); form.elements.namedItem("teamName").value = "Membership Team B"; })()`); await evaluate(`document.querySelector('[form="teamForm"][name="teamDriverIds"][value="${driverB.id}"]').checked = true`); await submit("teamForm"); await waitFor(async () => (await findEvent(raceEvent.id)).raceConfig.teams.some((team) => team.name === "Membership Team B"), "team B created"); const teamB = (await findEvent(raceEvent.id)).raceConfig.teams.find((team) => team.name === "Membership Team B"); await click("team-edit-" + teamB.id); await waitFor(async () => await evaluate(`Boolean(document.getElementById("teamEditForm"))`), "team B edit form open"); const hintText = await evaluate(`document.querySelector('#teamEditForm [name="teamDriverIds"][value="${driverA.id}"]')?.closest("label")?.textContent || ""`); assert.match(hintText, /Membership Team A/, "the edit form names the other team a driver is currently on, before the move happens"); await evaluate(`document.querySelector('#teamEditForm [name="teamDriverIds"][value="${driverA.id}"]').checked = true`); await pause(5500); // outlast the periodic version poll, matching the existing team-edit browser coverage await click("teamEditSave"); await waitFor(async () => (await findEvent(raceEvent.id)).raceConfig.teams.find((team) => team.name === "Membership Team B").driverIds.includes(driverA.id), "driver A saved onto team B"); const afterMove = await findEvent(raceEvent.id); const teamA = afterMove.raceConfig.teams.find((team) => team.name === "Membership Team A"); const teamBAfter = afterMove.raceConfig.teams.find((team) => team.name === "Membership Team B"); assert.deepEqual(teamA.driverIds, [], "driver A was removed from team A when moved to team B - the reported duplicate-membership bug is fixed"); assert.deepEqual([...teamBAfter.driverIds].sort(), [driverA.id, driverB.id].sort(), "team B now has both drivers"); console.log("PASS moving a driver onto a different team's roster removes them from their previous team, with a warning shown before saving"); await navigate("drivers", "driverForm"); await click("driver-delete-" + driverB.id); await waitFor(async () => !(await savedState()).drivers.some((driver) => driver.id === driverB.id), "driver B deleted"); const afterDelete = (await findEvent(raceEvent.id)).raceConfig.teams.find((team) => team.name === "Membership Team B"); assert.deepEqual(afterDelete.driverIds, [driverA.id], "a deleted driver no longer lingers in a team's roster"); console.log("PASS deleting a driver purges them from every team's roster too"); }