|
|
|
|
@@ -2,6 +2,10 @@ import { expect, type Page } from '@playwright/test';
|
|
|
|
|
|
|
|
|
|
const NEW_TRIP_URL = '/trips/trips/new';
|
|
|
|
|
|
|
|
|
|
function escapeRegex(text: string): string {
|
|
|
|
|
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function uniqueSuffix(): string {
|
|
|
|
|
return `${Date.now()}-${Math.floor(Math.random() * 1000)}`;
|
|
|
|
|
}
|
|
|
|
|
@@ -12,38 +16,48 @@ export async function createTrip(
|
|
|
|
|
): Promise<{ tripUrl: string; tripId: string }> {
|
|
|
|
|
await page.goto(NEW_TRIP_URL);
|
|
|
|
|
await expect(page.getByRole('heading', { name: 'Plan New Trip' })).toBeVisible();
|
|
|
|
|
await page.getByLabel('Trip name', { exact: false }).fill(values.name);
|
|
|
|
|
if (values.startDate) {
|
|
|
|
|
await page.getByLabel('Start date').fill(values.startDate);
|
|
|
|
|
} else {
|
|
|
|
|
await page.getByRole('checkbox', { name: "I don't know yet" }).first().check();
|
|
|
|
|
}
|
|
|
|
|
if (values.description) {
|
|
|
|
|
await page.getByLabel('Description').fill(values.description);
|
|
|
|
|
}
|
|
|
|
|
await page.locator('input[name="name"]').fill(values.name);
|
|
|
|
|
await page.getByRole('button', { name: 'Save' }).click();
|
|
|
|
|
await page.waitForURL('**/trips/trips/*', { timeout: 15_000 });
|
|
|
|
|
await expect(page).toHaveURL(/\/trips\/trips\/(?!new$)[^/?#]+$/, { timeout: 15_000 });
|
|
|
|
|
const tripUrl = page.url();
|
|
|
|
|
const tripId = tripUrl.split('/').pop() ?? '';
|
|
|
|
|
return { tripUrl, tripId };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function openAddToTripMenuItem(page: Page, label: string): Promise<void> {
|
|
|
|
|
const addToTripButton = page.getByRole('button', { name: 'Add to trip' });
|
|
|
|
|
const addToTripButton = page.getByRole('button', { name: /^Add to trip$/i });
|
|
|
|
|
if (await addToTripButton.isVisible().catch(() => false)) {
|
|
|
|
|
await addToTripButton.click();
|
|
|
|
|
await page.getByRole('button', { name: label, exact: true }).click();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
await page.getByRole('button', { name: label, exact: true }).click();
|
|
|
|
|
|
|
|
|
|
// In the welcome state, button names include label + subtitle.
|
|
|
|
|
await page
|
|
|
|
|
.getByRole('button', { name: new RegExp(`^${escapeRegex(label)}\\b`, 'i') })
|
|
|
|
|
.first()
|
|
|
|
|
.click();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function openAddTraveller(page: Page): Promise<void> {
|
|
|
|
|
const addToTripButton = page.getByRole('button', { name: 'Add to trip' });
|
|
|
|
|
const existingDialog = page.getByRole('dialog', { name: 'Add traveller' });
|
|
|
|
|
if (await existingDialog.isVisible().catch(() => false)) return;
|
|
|
|
|
|
|
|
|
|
const addToTripButton = page.getByRole('button', { name: /^Add to trip$/i });
|
|
|
|
|
if (await addToTripButton.isVisible().catch(() => false)) {
|
|
|
|
|
await addToTripButton.click();
|
|
|
|
|
await page.getByRole('button', { name: 'Travellers', exact: true }).click();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
await page.getByRole('button', { name: "Who's travelling?" }).click();
|
|
|
|
|
await page.getByRole('button', { name: /^Who's travelling\?/ }).click();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function addTraveller(
|
|
|
|
|
@@ -82,14 +96,14 @@ export async function addFlight(
|
|
|
|
|
const dialog = page.getByRole('dialog', { name: 'Add transportation' });
|
|
|
|
|
await expect(dialog).toBeVisible();
|
|
|
|
|
|
|
|
|
|
await dialog.getByRole('button', { name: 'Flight', exact: true }).click();
|
|
|
|
|
await dialog.getByRole('button', { name: /Flight/ }).click();
|
|
|
|
|
const form = dialog.locator('form');
|
|
|
|
|
await expect(form.getByLabel('Departure date')).toBeVisible();
|
|
|
|
|
await form.getByLabel('Departure date').fill(values.departureDate);
|
|
|
|
|
await form.getByLabel('Airline').fill(values.airlineCode);
|
|
|
|
|
await form.getByLabel('Flight number').fill(values.flightNumber);
|
|
|
|
|
await form.getByLabel('Departure airport').fill(values.departureAirport);
|
|
|
|
|
await form.getByLabel('Arrival airport').fill(values.arrivalAirport);
|
|
|
|
|
await expect(form.locator('input[name="segments[0][departure_date]"]')).toBeVisible();
|
|
|
|
|
await form.locator('input[name="segments[0][departure_date]"]').fill(values.departureDate);
|
|
|
|
|
await form.getByPlaceholder('Search airline or enter code').fill(values.airlineCode);
|
|
|
|
|
await form.locator('input[name="segments[0][flight_number]"]').fill(values.flightNumber);
|
|
|
|
|
await form.getByPlaceholder('Code or search').nth(0).fill(values.departureAirport);
|
|
|
|
|
await form.getByPlaceholder('Code or search').nth(1).fill(values.arrivalAirport);
|
|
|
|
|
await form.getByRole('button', { name: 'Add transportation' }).click();
|
|
|
|
|
await expect(dialog).toBeHidden();
|
|
|
|
|
}
|
|
|
|
|
@@ -115,7 +129,10 @@ export async function addPackingList(
|
|
|
|
|
values: { name: string; items: string[] }
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await openAddToTripMenuItem(page, 'Packing List');
|
|
|
|
|
const dialog = page.getByRole('dialog', { name: /Packing list/i });
|
|
|
|
|
const dialog = page
|
|
|
|
|
.locator('[role="dialog"]')
|
|
|
|
|
.filter({ has: page.getByRole('heading', { name: /Packing list/i }) })
|
|
|
|
|
.first();
|
|
|
|
|
await expect(dialog).toBeVisible();
|
|
|
|
|
|
|
|
|
|
await dialog.getByLabel('Name', { exact: false }).fill(values.name);
|
|
|
|
|
@@ -125,16 +142,16 @@ export async function addPackingList(
|
|
|
|
|
await dialog.getByRole('button', { name: 'Add item' }).click();
|
|
|
|
|
await itemInputs.last().fill(item);
|
|
|
|
|
}
|
|
|
|
|
await dialog.getByRole('button', { name: 'Add' }).click();
|
|
|
|
|
await dialog.getByRole('button', { name: 'Add', exact: true }).click();
|
|
|
|
|
await expect(dialog).toBeHidden();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function addActivity(
|
|
|
|
|
page: Page,
|
|
|
|
|
values: { name: string }
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
export async function addActivity(page: Page, values: { name: string }): Promise<void> {
|
|
|
|
|
await openAddToTripMenuItem(page, 'Attractions & Activities');
|
|
|
|
|
const dialog = page.getByRole('dialog', { name: /Attraction & activity/i });
|
|
|
|
|
const dialog = page
|
|
|
|
|
.locator('[role="dialog"]')
|
|
|
|
|
.filter({ has: page.getByRole('heading', { name: /Attraction & activity/i }) })
|
|
|
|
|
.first();
|
|
|
|
|
await expect(dialog).toBeVisible();
|
|
|
|
|
await dialog.getByLabel('Name', { exact: false }).fill(values.name);
|
|
|
|
|
await dialog.getByRole('button', { name: 'Add' }).click();
|
|
|
|
|
|