Files
flights_web/src/features/schedule/url.test.ts
T
gnezim 1c5e85ea8e Implement schedule URL serializer/parser with TDD (Phase 3A)
Covers one-way search, round-trip search, multi-flight details (catch-all),
and airport-code-interleaved details format. Reuses online-board's flight
param parser for individual flight segments.
2026-04-15 09:20:56 +03:00

503 lines
14 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
parseScheduleRouteParams,
buildScheduleRouteParams,
parseScheduleUrl,
buildScheduleUrl,
type ScheduleParams,
} from "./url";
// ---------------------------------------------------------------------------
// parseScheduleRouteParams
// ---------------------------------------------------------------------------
describe("parseScheduleRouteParams", () => {
it("parses route without time range or connections", () => {
expect(parseScheduleRouteParams("MOW-KUF-20220425-20220501")).toEqual({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
});
});
it("parses route with time range", () => {
expect(parseScheduleRouteParams("MOW-KUF-20220425-20220501-06002200")).toEqual({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
timeFrom: "0600",
timeTo: "2200",
});
});
it("parses route with connections only", () => {
expect(parseScheduleRouteParams("MOW-KUF-20220425-20220501-C1")).toEqual({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
connections: 1,
});
});
it("parses route with connections=0", () => {
expect(parseScheduleRouteParams("MOW-KUF-20220425-20220501-C0")).toEqual({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
connections: 0,
});
});
it("parses route with time range and connections", () => {
expect(parseScheduleRouteParams("MOW-KUF-20220425-20220501-06002200-C0")).toEqual({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
timeFrom: "0600",
timeTo: "2200",
connections: 0,
});
});
it("returns null for empty string", () => {
expect(parseScheduleRouteParams("")).toBeNull();
});
it("returns null for insufficient segments", () => {
expect(parseScheduleRouteParams("MOW-KUF-20220425")).toBeNull();
});
it("returns null for invalid dateFrom", () => {
expect(parseScheduleRouteParams("MOW-KUF-2022-20220501")).toBeNull();
});
it("returns null for invalid dateTo", () => {
expect(parseScheduleRouteParams("MOW-KUF-20220425-2022")).toBeNull();
});
});
// ---------------------------------------------------------------------------
// buildScheduleRouteParams
// ---------------------------------------------------------------------------
describe("buildScheduleRouteParams", () => {
it("builds route without time range or connections", () => {
expect(buildScheduleRouteParams({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
})).toBe("MOW-KUF-20220425-20220501");
});
it("builds route with time range", () => {
expect(buildScheduleRouteParams({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
timeFrom: "0600",
timeTo: "2200",
})).toBe("MOW-KUF-20220425-20220501-06002200");
});
it("builds route with connections", () => {
expect(buildScheduleRouteParams({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
connections: 1,
})).toBe("MOW-KUF-20220425-20220501-C1");
});
it("builds route with connections=0", () => {
expect(buildScheduleRouteParams({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
connections: 0,
})).toBe("MOW-KUF-20220425-20220501-C0");
});
it("builds route with time range and connections", () => {
expect(buildScheduleRouteParams({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
timeFrom: "0600",
timeTo: "2200",
connections: 0,
})).toBe("MOW-KUF-20220425-20220501-06002200-C0");
});
it("omits time range when only timeFrom is provided", () => {
expect(buildScheduleRouteParams({
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
timeFrom: "0600",
})).toBe("MOW-KUF-20220425-20220501");
});
});
// ---------------------------------------------------------------------------
// parseScheduleUrl
// ---------------------------------------------------------------------------
describe("parseScheduleUrl", () => {
it("parses start page (no trailing slash)", () => {
expect(parseScheduleUrl("/schedule")).toEqual({ type: "start" });
});
it("parses start page (with trailing slash)", () => {
expect(parseScheduleUrl("/schedule/")).toEqual({ type: "start" });
});
it("parses one-way route search", () => {
expect(parseScheduleUrl("/schedule/route/MOW-KUF-20220425-20220501")).toEqual({
type: "route",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
},
});
});
it("parses one-way route with time range", () => {
expect(parseScheduleUrl("/schedule/route/MOW-KUF-20220425-20220501-06002200")).toEqual({
type: "route",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
timeFrom: "0600",
timeTo: "2200",
},
});
});
it("parses one-way route with connections", () => {
expect(parseScheduleUrl("/schedule/route/MOW-KUF-20220425-20220501-C1")).toEqual({
type: "route",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
connections: 1,
},
});
});
it("parses round-trip route search", () => {
expect(parseScheduleUrl("/schedule/route/MOW-KUF-20220425-20220501/KUF-MOW-20220502-20220508")).toEqual({
type: "roundtrip",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
},
inbound: {
departure: "KUF",
arrival: "MOW",
dateFrom: "20220502",
dateTo: "20220508",
},
});
});
it("parses round-trip with time ranges", () => {
expect(parseScheduleUrl("/schedule/route/MOW-KUF-20220425-20220501-06002200/KUF-MOW-20220502-20220508-06002200")).toEqual({
type: "roundtrip",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
timeFrom: "0600",
timeTo: "2200",
},
inbound: {
departure: "KUF",
arrival: "MOW",
dateFrom: "20220502",
dateTo: "20220508",
timeFrom: "0600",
timeTo: "2200",
},
});
});
it("parses round-trip with connections", () => {
expect(parseScheduleUrl("/schedule/route/MOW-KUF-20220425-20220501-C0/KUF-MOW-20220502-20220508-C0")).toEqual({
type: "roundtrip",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
connections: 0,
},
inbound: {
departure: "KUF",
arrival: "MOW",
dateFrom: "20220502",
dateTo: "20220508",
connections: 0,
},
});
});
it("parses single-flight details", () => {
expect(parseScheduleUrl("/schedule/SU0012-20220527")).toEqual({
type: "details",
flights: [
{ carrier: "SU", flightNumber: "0012", date: "20220527" },
],
});
});
it("parses multi-flight details (connecting)", () => {
expect(parseScheduleUrl("/schedule/SU0012-20220501/SU0013-20220507")).toEqual({
type: "details",
flights: [
{ carrier: "SU", flightNumber: "0012", date: "20220501" },
{ carrier: "SU", flightNumber: "0013", date: "20220507" },
],
});
});
it("parses details with airport codes interleaved", () => {
// Format: /{depCode}/{flight-date}/{arrCode}/{depCode2}/{flight2-date}/{arrCode2}
// Airport codes (3-letter) are skipped, flight segments are parsed
expect(parseScheduleUrl("/schedule/SVO/SU0012-20220501/LED/LED/SU0013-20220507/SVO")).toEqual({
type: "details",
flights: [
{ carrier: "SU", flightNumber: "0012", date: "20220501" },
{ carrier: "SU", flightNumber: "0013", date: "20220507" },
],
});
});
it("returns null for empty string", () => {
expect(parseScheduleUrl("")).toBeNull();
});
it("returns null for non-schedule path", () => {
expect(parseScheduleUrl("/some/other/path")).toBeNull();
});
it("returns null for invalid route params", () => {
expect(parseScheduleUrl("/schedule/route/")).toBeNull();
});
it("handles path without leading slash", () => {
expect(parseScheduleUrl("schedule")).toEqual({ type: "start" });
});
it("handles path without leading slash for route", () => {
expect(parseScheduleUrl("schedule/route/MOW-KUF-20220425-20220501")).toEqual({
type: "route",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
},
});
});
});
// ---------------------------------------------------------------------------
// buildScheduleUrl
// ---------------------------------------------------------------------------
describe("buildScheduleUrl", () => {
it("builds start page URL", () => {
expect(buildScheduleUrl({ type: "start" })).toBe("schedule");
});
it("builds one-way route URL", () => {
expect(buildScheduleUrl({
type: "route",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
},
})).toBe("schedule/route/MOW-KUF-20220425-20220501");
});
it("builds one-way route with time range and connections", () => {
expect(buildScheduleUrl({
type: "route",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
timeFrom: "0600",
timeTo: "2200",
connections: 0,
},
})).toBe("schedule/route/MOW-KUF-20220425-20220501-06002200-C0");
});
it("builds round-trip route URL", () => {
expect(buildScheduleUrl({
type: "roundtrip",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
},
inbound: {
departure: "KUF",
arrival: "MOW",
dateFrom: "20220502",
dateTo: "20220508",
},
})).toBe("schedule/route/MOW-KUF-20220425-20220501/KUF-MOW-20220502-20220508");
});
it("builds round-trip with time ranges and connections", () => {
expect(buildScheduleUrl({
type: "roundtrip",
outbound: {
departure: "MOW",
arrival: "KUF",
dateFrom: "20220425",
dateTo: "20220501",
timeFrom: "0600",
timeTo: "2200",
connections: 0,
},
inbound: {
departure: "KUF",
arrival: "MOW",
dateFrom: "20220502",
dateTo: "20220508",
timeFrom: "0600",
timeTo: "2200",
connections: 0,
},
})).toBe("schedule/route/MOW-KUF-20220425-20220501-06002200-C0/KUF-MOW-20220502-20220508-06002200-C0");
});
it("builds single-flight details URL", () => {
expect(buildScheduleUrl({
type: "details",
flights: [
{ carrier: "SU", flightNumber: "0012", date: "20220527" },
],
})).toBe("schedule/SU0012-20220527");
});
it("builds multi-flight details URL", () => {
expect(buildScheduleUrl({
type: "details",
flights: [
{ carrier: "SU", flightNumber: "0012", date: "20220501" },
{ carrier: "SU", flightNumber: "0013", date: "20220507" },
],
})).toBe("schedule/SU0012-20220501/SU0013-20220507");
});
});
// ---------------------------------------------------------------------------
// Roundtrip tests: build -> parse -> build
// ---------------------------------------------------------------------------
describe("roundtrip: build -> parse -> build", () => {
const cases: Array<{ name: string; params: ScheduleParams }> = [
{ name: "start", params: { type: "start" } },
{
name: "one-way route without extras",
params: {
type: "route",
outbound: { departure: "MOW", arrival: "KUF", dateFrom: "20220425", dateTo: "20220501" },
},
},
{
name: "one-way route with time range",
params: {
type: "route",
outbound: { departure: "MOW", arrival: "KUF", dateFrom: "20220425", dateTo: "20220501", timeFrom: "0600", timeTo: "2200" },
},
},
{
name: "one-way route with connections",
params: {
type: "route",
outbound: { departure: "MOW", arrival: "KUF", dateFrom: "20220425", dateTo: "20220501", connections: 1 },
},
},
{
name: "one-way route with time range and connections",
params: {
type: "route",
outbound: { departure: "MOW", arrival: "KUF", dateFrom: "20220425", dateTo: "20220501", timeFrom: "0600", timeTo: "2200", connections: 0 },
},
},
{
name: "round-trip without extras",
params: {
type: "roundtrip",
outbound: { departure: "MOW", arrival: "KUF", dateFrom: "20220425", dateTo: "20220501" },
inbound: { departure: "KUF", arrival: "MOW", dateFrom: "20220502", dateTo: "20220508" },
},
},
{
name: "round-trip with time range and connections",
params: {
type: "roundtrip",
outbound: { departure: "MOW", arrival: "KUF", dateFrom: "20220425", dateTo: "20220501", timeFrom: "0600", timeTo: "2200", connections: 0 },
inbound: { departure: "KUF", arrival: "MOW", dateFrom: "20220502", dateTo: "20220508", timeFrom: "0600", timeTo: "2200", connections: 0 },
},
},
{
name: "single-flight details",
params: {
type: "details",
flights: [{ carrier: "SU", flightNumber: "0012", date: "20220527" }],
},
},
{
name: "multi-flight details",
params: {
type: "details",
flights: [
{ carrier: "SU", flightNumber: "0012", date: "20220501" },
{ carrier: "SU", flightNumber: "0013", date: "20220507" },
],
},
},
];
for (const { name, params } of cases) {
it(`roundtrips ${name}`, () => {
const url = buildScheduleUrl(params);
const parsed = parseScheduleUrl(url);
expect(parsed).not.toBeNull();
if (parsed === null) return;
const rebuilt = buildScheduleUrl(parsed);
expect(rebuilt).toBe(url);
});
}
});