216 lines
6.8 KiB
TypeScript
216 lines
6.8 KiB
TypeScript
import { test, expect } from "./fixtures/console-gate";
|
|
import type { Page } from "@playwright/test";
|
|
|
|
// TIRREDESIGN-12 — when both schedule cities are filled, the date-picker
|
|
// must grey out the days the route does NOT operate. The schedule
|
|
// `/days` bitmask is anchored to the requested calendar minimum date:
|
|
// bit 0 maps to that exact date, not "base date minus one".
|
|
|
|
function ymd(date: Date): string {
|
|
return [
|
|
date.getFullYear(),
|
|
String(date.getMonth() + 1).padStart(2, "0"),
|
|
String(date.getDate()).padStart(2, "0"),
|
|
].join("");
|
|
}
|
|
|
|
function isoYmd(date: Date): string {
|
|
return [
|
|
date.getFullYear(),
|
|
String(date.getMonth() + 1).padStart(2, "0"),
|
|
String(date.getDate()).padStart(2, "0"),
|
|
].join("-");
|
|
}
|
|
|
|
function addDays(date: Date, days: number): Date {
|
|
const next = new Date(date);
|
|
next.setDate(next.getDate() + days);
|
|
return next;
|
|
}
|
|
|
|
async function visibleCalendarCellClasses(page: Page) {
|
|
return page
|
|
.locator(".p-datepicker td:not(.p-datepicker-other-month) span")
|
|
.evaluateAll((nodes) =>
|
|
nodes.map((node) => ({
|
|
text: node.textContent?.trim(),
|
|
className: node.className,
|
|
})),
|
|
);
|
|
}
|
|
|
|
test("Schedule calendar greys out non-operating days for the route", async ({ page }) => {
|
|
const minDate = addDays(new Date(), -1);
|
|
minDate.setHours(0, 0, 0, 0);
|
|
const dateTo = addDays(minDate, 6);
|
|
const daysRequests: string[] = [];
|
|
|
|
await page.route("**/api/flights/v1/*/days/**/schedule/", async (route) => {
|
|
daysRequests.push(route.request().url());
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ days: `0${"1".repeat(381)}` }),
|
|
});
|
|
});
|
|
|
|
await page.goto(
|
|
`/ru-ru/schedule/route/LED-KUF-${ymd(minDate)}-${ymd(dateTo)}-C0`,
|
|
);
|
|
|
|
// Open the picker.
|
|
await page.locator("button.p-datepicker-trigger").first().click();
|
|
const panel = page.locator(".p-datepicker-panel, .p-datepicker").first();
|
|
await expect(panel).toBeVisible();
|
|
|
|
const minDateDay = String(minDate.getDate());
|
|
const nextDateDay = String(addDays(minDate, 1).getDate());
|
|
|
|
await expect.poll(async () => {
|
|
const cells = await visibleCalendarCellClasses(page);
|
|
return cells.find((cell) => cell.text === minDateDay)?.className ?? "";
|
|
}, { timeout: 10000 }).toContain("p-disabled");
|
|
|
|
expect(daysRequests[0]).toContain(
|
|
`/days/${isoYmd(minDate)}/382/route/LED-KUF/schedule/`,
|
|
);
|
|
|
|
const cells = await visibleCalendarCellClasses(page);
|
|
expect(
|
|
cells.find((cell) => cell.text === nextDateDay)?.className ?? "",
|
|
).not.toContain("p-disabled");
|
|
});
|
|
|
|
test("Schedule calendar updates an already-open picker when operating days load", async ({ page }) => {
|
|
const minDate = addDays(new Date(), -1);
|
|
minDate.setHours(0, 0, 0, 0);
|
|
const dateTo = addDays(minDate, 6);
|
|
|
|
let releaseDays: (() => void) | undefined;
|
|
await page.route("**/api/flights/v1/*/days/**/schedule/", async (route) => {
|
|
await new Promise<void>((resolve) => {
|
|
releaseDays = resolve;
|
|
});
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ days: `0${"1".repeat(381)}` }),
|
|
});
|
|
});
|
|
|
|
await page.goto(
|
|
`/ru-ru/schedule/route/LED-KUF-${ymd(minDate)}-${ymd(dateTo)}-C0`,
|
|
);
|
|
|
|
await page.locator("button.p-datepicker-trigger").first().click();
|
|
const panel = page.locator(".p-datepicker-panel, .p-datepicker").first();
|
|
await expect(panel).toBeVisible();
|
|
|
|
const visibleDaySelector =
|
|
".p-datepicker td:not(.p-datepicker-other-month) span";
|
|
const minDateDay = String(minDate.getDate());
|
|
|
|
const classBeforeLoad = await page
|
|
.locator(visibleDaySelector)
|
|
.evaluateAll(
|
|
(nodes, day) =>
|
|
nodes
|
|
.map((node) => ({
|
|
text: node.textContent?.trim(),
|
|
className: node.className,
|
|
}))
|
|
.find((cell) => cell.text === day)?.className ?? "",
|
|
minDateDay,
|
|
);
|
|
expect(classBeforeLoad).not.toContain("p-disabled");
|
|
|
|
releaseDays?.();
|
|
|
|
await expect.poll(async () => {
|
|
const cells = await visibleCalendarCellClasses(page);
|
|
return cells.find((cell) => cell.text === minDateDay)?.className ?? "";
|
|
}, { timeout: 10000 }).toContain("p-disabled");
|
|
});
|
|
|
|
test("Schedule calendar uses the connections days endpoint when transfers are allowed", async ({ page }) => {
|
|
const minDate = addDays(new Date(), -1);
|
|
minDate.setHours(0, 0, 0, 0);
|
|
const dateTo = addDays(minDate, 6);
|
|
const daysRequests: string[] = [];
|
|
|
|
await page.route("**/api/flights/v1/*/days/**/schedule/", async (route) => {
|
|
daysRequests.push(route.request().url());
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ days: `0${"1".repeat(381)}` }),
|
|
});
|
|
});
|
|
|
|
await page.goto(
|
|
`/ru-ru/schedule/route/LED-KUF-${ymd(minDate)}-${ymd(dateTo)}`,
|
|
);
|
|
|
|
await page.locator("button.p-datepicker-trigger").first().click();
|
|
await expect(
|
|
page.locator(".p-datepicker-panel, .p-datepicker").first(),
|
|
).toBeVisible();
|
|
|
|
await expect
|
|
.poll(() => daysRequests[0] ?? "", { timeout: 10000 })
|
|
.toContain("/connections/LED-KUF-1/schedule/");
|
|
await expect.poll(async () => {
|
|
const cells = await visibleCalendarCellClasses(page);
|
|
return (
|
|
cells.find((cell) => cell.text === String(minDate.getDate()))?.className ??
|
|
""
|
|
);
|
|
}, { timeout: 10000 }).toContain("p-disabled");
|
|
});
|
|
|
|
test("Schedule return calendar uses swapped route operating days", async ({ page }) => {
|
|
const minDate = addDays(new Date(), -1);
|
|
minDate.setHours(0, 0, 0, 0);
|
|
const dateTo = addDays(minDate, 6);
|
|
const returnFrom = addDays(dateTo, 1);
|
|
const returnTo = addDays(returnFrom, 6);
|
|
const daysRequests: string[] = [];
|
|
const returnMask = `${"1".repeat(7)}0${"1".repeat(374)}`;
|
|
|
|
await page.route("**/api/flights/v1/*/days/**/schedule/", async (route) => {
|
|
const url = route.request().url();
|
|
daysRequests.push(url);
|
|
await route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
days: url.includes("/connections/KUF-LED-1/")
|
|
? returnMask
|
|
: "1".repeat(382),
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.goto(
|
|
`/ru-ru/schedule/route/LED-KUF-${ymd(minDate)}-${ymd(dateTo)}/KUF-LED-${ymd(returnFrom)}-${ymd(returnTo)}`,
|
|
);
|
|
|
|
const triggers = page.locator("button.p-datepicker-trigger");
|
|
await expect(triggers).toHaveCount(2);
|
|
await triggers.nth(1).click();
|
|
await expect(
|
|
page.locator(".p-datepicker-panel, .p-datepicker").first(),
|
|
).toBeVisible();
|
|
|
|
await expect
|
|
.poll(
|
|
() => daysRequests.some((url) => url.includes("/connections/KUF-LED-1/")),
|
|
{ timeout: 10000 },
|
|
)
|
|
.toBe(true);
|
|
|
|
await expect.poll(async () => {
|
|
const cells = await visibleCalendarCellClasses(page);
|
|
return (
|
|
cells.find((cell) => cell.text === String(returnFrom.getDate()))
|
|
?.className ?? ""
|
|
);
|
|
}, { timeout: 10000 }).toContain("p-disabled");
|
|
});
|