c67686463a
Registers schedule URL serializer in parity harness with 18-entry fixture corpus and fast-check fuzz. 10 URL round-trip integration tests, 6 SEO/ JSON-LD integration tests. Updates barrel and MF expose from stub to real.
112 lines
3.2 KiB
TypeScript
112 lines
3.2 KiB
TypeScript
/**
|
|
* Integration tests for Schedule SEO + JSON-LD.
|
|
*
|
|
* Verifies SEO builders produce complete, well-formed output and
|
|
* JSON-LD schemas conform to schema.org types.
|
|
*/
|
|
|
|
import { describe, it, expect } from "vitest";
|
|
import {
|
|
buildScheduleStartSeo,
|
|
buildScheduleSearchSeo,
|
|
buildScheduleDetailsSeo,
|
|
} from "@/features/schedule/seo.js";
|
|
import {
|
|
buildScheduleFlightJsonLd,
|
|
buildScheduleFlightListJsonLd,
|
|
} from "@/features/schedule/json-ld.js";
|
|
import { SCHEDULE_FLIGHT_1, SCHEDULE_FLIGHT_2 } from "./fixtures.js";
|
|
import type { IScheduleFlightId } from "@/features/schedule/types.js";
|
|
|
|
function stubT(key: string, opts?: Record<string, unknown>): string {
|
|
if (opts && Object.keys(opts).length > 0) {
|
|
const vars = Object.entries(opts)
|
|
.map(([k, v]) => `${k}=${v}`)
|
|
.join(",");
|
|
return `${key}|${vars}`;
|
|
}
|
|
return key;
|
|
}
|
|
|
|
const CANONICAL = "https://www.aeroflot.ru";
|
|
|
|
describe("Schedule SEO integration", () => {
|
|
it("start page SEO has all required fields", () => {
|
|
const result = buildScheduleStartSeo(stubT, "ru", CANONICAL);
|
|
|
|
expect(result.title).toBeTruthy();
|
|
expect(result.description).toBeTruthy();
|
|
expect(result.canonical).toBeTruthy();
|
|
expect(result.og).toBeDefined();
|
|
expect(result.hreflang).toBeDefined();
|
|
expect(result.hreflang).toHaveLength(10);
|
|
});
|
|
|
|
it("search page SEO canonical matches route format", () => {
|
|
const params = {
|
|
type: "route" as const,
|
|
outbound: {
|
|
departure: "SVO",
|
|
arrival: "LED",
|
|
dateFrom: "20250115",
|
|
dateTo: "20250122",
|
|
},
|
|
};
|
|
|
|
const result = buildScheduleSearchSeo(stubT, params, "ru", CANONICAL);
|
|
|
|
expect(result.canonical).toBe(
|
|
"https://www.aeroflot.ru/ru/schedule/route/SVO-LED-20250115-20250122",
|
|
);
|
|
});
|
|
|
|
it("details page SEO uses article og type", () => {
|
|
const flightIds: IScheduleFlightId[] = [
|
|
{ carrier: "SU", flightNumber: "0012", date: "20220527" },
|
|
];
|
|
|
|
const result = buildScheduleDetailsSeo(
|
|
stubT,
|
|
[SCHEDULE_FLIGHT_1],
|
|
"ru",
|
|
CANONICAL,
|
|
flightIds,
|
|
);
|
|
|
|
expect(result.og.type).toBe("article");
|
|
});
|
|
});
|
|
|
|
describe("Schedule JSON-LD integration", () => {
|
|
it("single flight produces valid Flight schema", () => {
|
|
const jsonLd = buildScheduleFlightJsonLd(SCHEDULE_FLIGHT_1);
|
|
|
|
expect(jsonLd["@type"]).toBe("Flight");
|
|
expect(jsonLd.flightNumber).toBe("SU0012");
|
|
expect(jsonLd.departureAirport).toBeDefined();
|
|
expect(jsonLd.arrivalAirport).toBeDefined();
|
|
});
|
|
|
|
it("flight list produces valid ItemList schema", () => {
|
|
const jsonLd = buildScheduleFlightListJsonLd(
|
|
[SCHEDULE_FLIGHT_1, SCHEDULE_FLIGHT_2],
|
|
"Schedule SVO to LED",
|
|
);
|
|
|
|
expect(jsonLd["@type"]).toBe("ItemList");
|
|
expect(jsonLd.description).toBe("Schedule SVO to LED");
|
|
|
|
const items = jsonLd.itemListElement as unknown as Array<{ position: number }>;
|
|
expect(items).toHaveLength(2);
|
|
expect(items[0]).toHaveProperty("position", 1);
|
|
expect(items[1]).toHaveProperty("position", 2);
|
|
});
|
|
|
|
it("empty flight list produces valid ItemList", () => {
|
|
const jsonLd = buildScheduleFlightListJsonLd([], "No flights");
|
|
|
|
expect(jsonLd["@type"]).toBe("ItemList");
|
|
expect(jsonLd.itemListElement).toEqual([]);
|
|
});
|
|
});
|