/** * Fast Lane name-join tests. * * The Six Flags /wait-times endpoint and Queue-Times use slightly different * ride name conventions, so Fast Lane waits are joined onto Queue-Times rides * by normalized name. These cases lock that join behaviour. * * Run with: npm test */ import { test } from "node:test"; import assert from "node:assert/strict"; import { parseWaitTimes, lookupFastLane } from "../lib/scrapers/sixflags-waittimes"; import type { WTResponse } from "../lib/scrapers/sixflags-waittimes"; function ride( name: string, isFastLane: boolean, fastLaneMinutes: number | null, ): Record { return { id: 1, name, isFastLane, regularWaittime: { createdDateTime: "May 29, 2026 19:00:00", waitTime: 20 }, fastlaneWaittime: fastLaneMinutes === null ? { createdDateTime: "", waitTime: 0 } : { createdDateTime: "May 29, 2026 19:00:00", waitTime: fastLaneMinutes }, fimsId: "RIDE-906-00001", }; } function result(...rides: Record[]) { const json: WTResponse = { parkId: 906, venues: [{ venueId: 1, venueName: "Rides", details: rides as never }], }; const r = parseWaitTimes(json); assert.ok(r, "expected parseWaitTimes to return a result"); return r; } // ── Name joins across QT ↔ SF naming quirks ────────────────────────────────── test("matches across trademark symbols, THE prefix, possessives", () => { const r = result( ride("Batman: The Ride", true, 5), ride("Riddler's Revenge", true, 10), ride("Apocalypse the Ride", true, 15), ); // Queue-Times-style names on the left should resolve to the SF entries. assert.deepEqual(lookupFastLane("BATMAN™ The Ride", r), { hasFastLane: true, fastLaneMinutes: 5, }); assert.deepEqual(lookupFastLane("THE RIDDLER™'s Revenge", r), { hasFastLane: true, fastLaneMinutes: 10, }); // Prefix match: "Apocalypse" is a prefix of "Apocalypse the Ride". assert.deepEqual(lookupFastLane("Apocalypse", r), { hasFastLane: true, fastLaneMinutes: 15, }); }); test("a non-Fast-Lane ride resolves to hasFastLane: false", () => { const r = result(ride("Bucaneer", false, null)); assert.deepEqual(lookupFastLane("Bucaneer", r), { hasFastLane: false, fastLaneMinutes: null, }); }); test("empty fastlane createdDateTime yields fastLaneMinutes: null", () => { const r = result(ride("Batman: The Ride", true, null)); assert.deepEqual(lookupFastLane("Batman: The Ride", r), { hasFastLane: true, fastLaneMinutes: null, }); }); test("a ride absent from SF data returns null", () => { const r = result(ride("Apocalypse the Ride", true, 15)); assert.equal(lookupFastLane("Some Other Coaster", r), null); }); test("conjunction guard: compound name does not match a single ride", () => { const r = result(ride("Joker", true, 25)); // "Joker y Harley Quinn" is a different (compound) ride, not a Joker subtitle. assert.equal(lookupFastLane("Joker y Harley Quinn", r), null); }); test("parseWaitTimes returns null when no ride rows present", () => { assert.equal(parseWaitTimes({ parkId: 1, venues: [] }), null); assert.equal( parseWaitTimes({ parkId: 1, venues: [{ venueId: 9, venueName: "Restaurants", details: [] }] }), null, ); });