e2e: add medium-priority Playwright coverage (#44)
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m29s
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m29s
Co-authored-by: Shaun Campbell <shaun@campbellwireless.net> Reviewed-on: #44 Co-authored-by: AI Agent <ai-agent@campbellwireless.net> Co-committed-by: AI Agent <ai-agent@campbellwireless.net>
This commit was merged in pull request #44.
This commit is contained in:
44
e2e/checklist-experience-persistence.test.ts
Normal file
44
e2e/checklist-experience-persistence.test.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { TEST_USERS } from './setup/test-users.js';
|
||||||
|
import { loginAsLocalUser } from './helpers/auth.js';
|
||||||
|
import { addActivity, addPackingList, createTrip, uniqueSuffix } from './helpers/trip.js';
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
await context.clearCookies();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('checklists and experiences persist after reload', async ({ page }) => {
|
||||||
|
const suffix = uniqueSuffix();
|
||||||
|
const tripName = `E2E Checklist ${suffix}`;
|
||||||
|
const listName = `Packing ${suffix}`;
|
||||||
|
const itemOne = `Passport ${suffix}`;
|
||||||
|
const itemTwo = `Sunscreen ${suffix}`;
|
||||||
|
const activityName = `Museum Visit ${suffix}`;
|
||||||
|
|
||||||
|
await loginAsLocalUser(page, TEST_USERS.regular.username, TEST_USERS.regular.password);
|
||||||
|
await createTrip(page, { name: tripName, description: 'Checklist persistence coverage' });
|
||||||
|
|
||||||
|
await addPackingList(page, { name: listName, items: [itemOne, itemTwo] });
|
||||||
|
await addActivity(page, { name: activityName });
|
||||||
|
|
||||||
|
const toggleRequest = page.waitForResponse(
|
||||||
|
(response) =>
|
||||||
|
response.request().method() === 'POST' && response.url().includes('/toggleChecklistItem')
|
||||||
|
);
|
||||||
|
await page.getByRole('checkbox', { name: itemOne }).check();
|
||||||
|
await toggleRequest;
|
||||||
|
await expect(page.getByRole('checkbox', { name: itemOne })).toBeChecked();
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
|
await expect(page.getByText(activityName, { exact: true })).toBeVisible();
|
||||||
|
await expect(page.getByRole('checkbox', { name: itemOne })).toBeChecked();
|
||||||
|
await expect(page.getByRole('checkbox', { name: itemTwo })).not.toBeChecked();
|
||||||
|
|
||||||
|
const listCard = page
|
||||||
|
.getByText(listName, { exact: true })
|
||||||
|
.locator('xpath=ancestor::div[contains(@class,"rounded-xl")]');
|
||||||
|
const items = listCard.locator('label');
|
||||||
|
await expect(items.nth(0)).toContainText(itemOne);
|
||||||
|
await expect(items.nth(1)).toContainText(itemTwo);
|
||||||
|
});
|
||||||
27
e2e/helpers/auth.ts
Normal file
27
e2e/helpers/auth.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { expect, type Page } from '@playwright/test';
|
||||||
|
import { TEST_USERS } from '../setup/test-users.js';
|
||||||
|
|
||||||
|
const LOGIN_URL = '/trips/login';
|
||||||
|
const DASHBOARD_URL = '/trips/dashboard';
|
||||||
|
const PROFILE_URL = '/trips/profile';
|
||||||
|
|
||||||
|
export async function loginAsLocalUser(
|
||||||
|
page: Page,
|
||||||
|
username: string,
|
||||||
|
password: string
|
||||||
|
): Promise<void> {
|
||||||
|
await page.goto(LOGIN_URL);
|
||||||
|
await page.fill('input[name="identifier"]', username);
|
||||||
|
await page.fill('input[name="password"]', password);
|
||||||
|
await page.click('button[type="submit"]:has-text("Sign in locally")');
|
||||||
|
await page.waitForURL(`**${DASHBOARD_URL}`, { timeout: 15_000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ensureSelfProfile(page: Page, email = TEST_USERS.regular.email): Promise<void> {
|
||||||
|
await page.goto(PROFILE_URL);
|
||||||
|
await page.fill('#first_name', 'E2E');
|
||||||
|
await page.fill('#last_name', 'User');
|
||||||
|
await page.fill('#email', email);
|
||||||
|
await page.getByRole('button', { name: 'Save profile' }).click();
|
||||||
|
await expect(page.getByRole('heading', { name: 'My Profile' })).toBeVisible();
|
||||||
|
}
|
||||||
159
e2e/helpers/trip.ts
Normal file
159
e2e/helpers/trip.ts
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
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)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createTrip(
|
||||||
|
page: Page,
|
||||||
|
values: { name: string; startDate?: string; description?: string }
|
||||||
|
): Promise<{ tripUrl: string; tripId: string }> {
|
||||||
|
await page.goto(NEW_TRIP_URL);
|
||||||
|
await expect(page.getByRole('heading', { name: 'Plan New Trip' })).toBeVisible();
|
||||||
|
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 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$/i });
|
||||||
|
if (await addToTripButton.isVisible().catch(() => false)) {
|
||||||
|
await addToTripButton.click();
|
||||||
|
await page.getByRole('button', { name: label, exact: true }).click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addTraveller(
|
||||||
|
page: Page,
|
||||||
|
values: { firstName: string; lastName: string; email?: string }
|
||||||
|
): Promise<void> {
|
||||||
|
await openAddTraveller(page);
|
||||||
|
const dialog = page.getByRole('dialog', { name: 'Add traveller' });
|
||||||
|
await expect(dialog).toBeVisible();
|
||||||
|
|
||||||
|
const addNewButton = dialog.getByRole('button', { name: 'Add someone new' });
|
||||||
|
if (await addNewButton.isVisible().catch(() => false)) {
|
||||||
|
await addNewButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
await dialog.getByLabel('First name', { exact: false }).fill(values.firstName);
|
||||||
|
await dialog.getByLabel('Last name', { exact: false }).fill(values.lastName);
|
||||||
|
if (values.email) {
|
||||||
|
await dialog.getByLabel(/Email/).fill(values.email);
|
||||||
|
}
|
||||||
|
await dialog.getByRole('button', { name: 'Add traveller' }).click();
|
||||||
|
await expect(dialog).toBeHidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addFlight(
|
||||||
|
page: Page,
|
||||||
|
values: {
|
||||||
|
departureDate: string;
|
||||||
|
airlineCode: string;
|
||||||
|
flightNumber: string;
|
||||||
|
departureAirport: string;
|
||||||
|
arrivalAirport: string;
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
await openAddToTripMenuItem(page, 'Transportation');
|
||||||
|
const dialog = page.getByRole('dialog', { name: 'Add transportation' });
|
||||||
|
await expect(dialog).toBeVisible();
|
||||||
|
|
||||||
|
await dialog.getByRole('button', { name: /Flight/ }).click();
|
||||||
|
const form = dialog.locator('form');
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addLodging(
|
||||||
|
page: Page,
|
||||||
|
values: { name: string; guestNames?: string[] }
|
||||||
|
): Promise<void> {
|
||||||
|
await openAddToTripMenuItem(page, 'Lodgings');
|
||||||
|
const dialog = page.getByRole('dialog', { name: 'Add lodging' });
|
||||||
|
await expect(dialog).toBeVisible();
|
||||||
|
|
||||||
|
await dialog.getByLabel('Name', { exact: false }).fill(values.name);
|
||||||
|
for (const guestName of values.guestNames ?? []) {
|
||||||
|
await dialog.getByRole('checkbox', { name: guestName }).check();
|
||||||
|
}
|
||||||
|
await dialog.getByRole('button', { name: 'Add lodging' }).click();
|
||||||
|
await expect(dialog).toBeHidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addPackingList(
|
||||||
|
page: Page,
|
||||||
|
values: { name: string; items: string[] }
|
||||||
|
): Promise<void> {
|
||||||
|
await openAddToTripMenuItem(page, 'Packing List');
|
||||||
|
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);
|
||||||
|
const itemInputs = dialog.getByPlaceholder('Item');
|
||||||
|
await itemInputs.first().fill(values.items[0]);
|
||||||
|
for (const item of values.items.slice(1)) {
|
||||||
|
await dialog.getByRole('button', { name: 'Add item' }).click();
|
||||||
|
await itemInputs.last().fill(item);
|
||||||
|
}
|
||||||
|
await dialog.getByRole('button', { name: 'Add', exact: true }).click();
|
||||||
|
await expect(dialog).toBeHidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addActivity(page: Page, values: { name: string }): Promise<void> {
|
||||||
|
await openAddToTripMenuItem(page, 'Attractions & Activities');
|
||||||
|
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();
|
||||||
|
await expect(dialog).toBeHidden();
|
||||||
|
}
|
||||||
79
e2e/lodging-permissions.test.ts
Normal file
79
e2e/lodging-permissions.test.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { TEST_USERS } from './setup/test-users.js';
|
||||||
|
import { ensureSelfProfile, loginAsLocalUser } from './helpers/auth.js';
|
||||||
|
import {
|
||||||
|
addTraveller,
|
||||||
|
createTrip,
|
||||||
|
openAddToTripMenuItem,
|
||||||
|
openAddTraveller,
|
||||||
|
uniqueSuffix
|
||||||
|
} from './helpers/trip.js';
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
await context.clearCookies();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('lodging guest selection and access boundaries are enforced', async ({ page }) => {
|
||||||
|
const suffix = uniqueSuffix();
|
||||||
|
const tripName = `E2E Lodging ${suffix}`;
|
||||||
|
const guestFirst = 'Jordan';
|
||||||
|
const guestLast = 'Guest';
|
||||||
|
const guestEmail = `jordan.${suffix}@test.local`;
|
||||||
|
const lodgingName = `E2E Lodge ${suffix}`;
|
||||||
|
|
||||||
|
await loginAsLocalUser(page, TEST_USERS.regular.username, TEST_USERS.regular.password);
|
||||||
|
await ensureSelfProfile(page);
|
||||||
|
const { tripUrl } = await createTrip(page, {
|
||||||
|
name: tripName,
|
||||||
|
description: 'Lodging guest coverage'
|
||||||
|
});
|
||||||
|
|
||||||
|
const selfName = 'E2E User';
|
||||||
|
await openAddTraveller(page);
|
||||||
|
const travellerDialog = page.getByRole('dialog', { name: 'Add traveller' });
|
||||||
|
await expect(travellerDialog).toBeVisible();
|
||||||
|
const selfButton = travellerDialog.getByRole('button', { name: /You/ }).first();
|
||||||
|
if (await selfButton.isVisible().catch(() => false)) {
|
||||||
|
await selfButton.click();
|
||||||
|
await expect(travellerDialog).toBeHidden();
|
||||||
|
} else {
|
||||||
|
const selfNameButton = travellerDialog.getByRole('button', { name: selfName }).first();
|
||||||
|
if (await selfNameButton.isVisible().catch(() => false)) {
|
||||||
|
await selfNameButton.click();
|
||||||
|
await expect(travellerDialog).toBeHidden();
|
||||||
|
} else {
|
||||||
|
await travellerDialog.getByRole('button', { name: 'Close' }).click();
|
||||||
|
await expect(travellerDialog).toBeHidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await addTraveller(page, {
|
||||||
|
firstName: guestFirst,
|
||||||
|
lastName: guestLast,
|
||||||
|
email: guestEmail
|
||||||
|
});
|
||||||
|
|
||||||
|
await openAddToTripMenuItem(page, 'Lodgings');
|
||||||
|
const lodgingDialog = page.getByRole('dialog', { name: 'Add lodging' });
|
||||||
|
await expect(lodgingDialog).toBeVisible();
|
||||||
|
|
||||||
|
const guestName = `${guestFirst} ${guestLast}`;
|
||||||
|
await expect(lodgingDialog.getByRole('checkbox', { name: guestName })).toBeVisible();
|
||||||
|
|
||||||
|
await lodgingDialog.getByLabel('Name', { exact: false }).fill(lodgingName);
|
||||||
|
await lodgingDialog.getByText(guestName, { exact: true }).click();
|
||||||
|
await lodgingDialog.getByRole('button', { name: 'Add lodging' }).click();
|
||||||
|
await expect(lodgingDialog).toBeHidden();
|
||||||
|
|
||||||
|
await expect(page.getByText(lodgingName, { exact: true })).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Edit lodging' }).click();
|
||||||
|
const editDialog = page.getByRole('dialog', { name: 'Edit lodging' });
|
||||||
|
await expect(editDialog).toBeVisible();
|
||||||
|
await expect(editDialog.getByRole('checkbox', { name: guestName })).toBeChecked();
|
||||||
|
await editDialog.getByRole('button', { name: 'Close' }).click();
|
||||||
|
|
||||||
|
await page.context().clearCookies();
|
||||||
|
await loginAsLocalUser(page, TEST_USERS.admin.username, TEST_USERS.admin.password);
|
||||||
|
await page.goto(tripUrl);
|
||||||
|
await expect(page.getByText(/Trip not found|Not found/i)).toBeVisible();
|
||||||
|
});
|
||||||
60
e2e/package-tours-import.test.ts
Normal file
60
e2e/package-tours-import.test.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { TEST_USERS } from './setup/test-users.js';
|
||||||
|
import { loginAsLocalUser } from './helpers/auth.js';
|
||||||
|
import { createTrip, openAddToTripMenuItem, uniqueSuffix } from './helpers/trip.js';
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
await context.clearCookies();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('admin can import a package tour and attach it to a trip', async ({ page }) => {
|
||||||
|
if (!process.env.GADVENTURES_API_KEY) {
|
||||||
|
test.skip(true, 'G Adventures API key not set for deterministic import');
|
||||||
|
}
|
||||||
|
|
||||||
|
const suffix = uniqueSuffix();
|
||||||
|
const operatorName = 'G Adventures';
|
||||||
|
|
||||||
|
await loginAsLocalUser(page, TEST_USERS.admin.username, TEST_USERS.admin.password);
|
||||||
|
await page.goto('/trips/admin/tour-operators');
|
||||||
|
await expect(page.getByRole('heading', { name: 'Tour Operators' })).toBeVisible();
|
||||||
|
await page.getByRole('button', { name: 'Add operator' }).click();
|
||||||
|
|
||||||
|
await page.getByPlaceholder('Search or type operator name').fill(operatorName);
|
||||||
|
await page.getByRole('button', { name: 'Add operator' }).last().click();
|
||||||
|
await expect(page.getByText(operatorName, { exact: true })).toBeVisible();
|
||||||
|
|
||||||
|
const operatorCard = page
|
||||||
|
.getByText(operatorName, { exact: true })
|
||||||
|
.locator('xpath=ancestor::div[contains(@class,"rounded-xl")]');
|
||||||
|
await operatorCard.getByRole('link', { name: 'Tours' }).click();
|
||||||
|
await expect(page.getByRole('heading', { name: operatorName })).toBeVisible();
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Import' }).click();
|
||||||
|
const importDialog = page.getByRole('dialog', { name: 'Import tours' });
|
||||||
|
await expect(importDialog).toBeVisible();
|
||||||
|
await importDialog.getByPlaceholder('Search tours...').fill('a');
|
||||||
|
const results = importDialog.locator('ul button');
|
||||||
|
await expect(results.first()).toBeVisible();
|
||||||
|
|
||||||
|
const tourTitle = (await results.first().innerText()).trim();
|
||||||
|
await results.first().click();
|
||||||
|
await importDialog.getByRole('button', { name: 'Import tour' }).click();
|
||||||
|
await expect(importDialog).toBeHidden();
|
||||||
|
await expect(page.getByRole('link', { name: tourTitle })).toBeVisible();
|
||||||
|
|
||||||
|
await page.context().clearCookies();
|
||||||
|
await loginAsLocalUser(page, TEST_USERS.regular.username, TEST_USERS.regular.password);
|
||||||
|
await createTrip(page, { name: `E2E Tour Trip ${suffix}` });
|
||||||
|
|
||||||
|
await openAddToTripMenuItem(page, 'Package Tours');
|
||||||
|
const addDialog = page.getByRole('dialog', { name: 'Add package tour' });
|
||||||
|
await expect(addDialog).toBeVisible();
|
||||||
|
await addDialog.getByPlaceholder('Search or type operator name').fill(operatorName);
|
||||||
|
await addDialog.getByRole('button', { name: operatorName }).click();
|
||||||
|
await expect(addDialog.locator('select')).toContainText(tourTitle);
|
||||||
|
await addDialog.locator('select').first().selectOption({ label: tourTitle });
|
||||||
|
await addDialog.getByRole('button', { name: 'Add tour' }).click();
|
||||||
|
await expect(addDialog).toBeHidden();
|
||||||
|
await expect(page.getByText(tourTitle, { exact: true })).toBeVisible();
|
||||||
|
});
|
||||||
51
e2e/transportation-lifecycle.test.ts
Normal file
51
e2e/transportation-lifecycle.test.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { TEST_USERS } from './setup/test-users.js';
|
||||||
|
import { loginAsLocalUser } from './helpers/auth.js';
|
||||||
|
import { addFlight, createTrip, uniqueSuffix } from './helpers/trip.js';
|
||||||
|
|
||||||
|
function formatDate(offsetDays: number): string {
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() + offsetDays);
|
||||||
|
return date.toISOString().slice(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
await context.clearCookies();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('transportation lifecycle updates the trip view', async ({ page }) => {
|
||||||
|
const suffix = uniqueSuffix();
|
||||||
|
const tripName = `E2E Transport ${suffix}`;
|
||||||
|
const departureDate = formatDate(30);
|
||||||
|
|
||||||
|
await loginAsLocalUser(page, TEST_USERS.regular.username, TEST_USERS.regular.password);
|
||||||
|
await createTrip(page, {
|
||||||
|
name: tripName,
|
||||||
|
startDate: departureDate,
|
||||||
|
description: 'Transportation lifecycle coverage'
|
||||||
|
});
|
||||||
|
|
||||||
|
await addFlight(page, {
|
||||||
|
departureDate,
|
||||||
|
airlineCode: 'UA',
|
||||||
|
flightNumber: '1001',
|
||||||
|
departureAirport: 'SFO',
|
||||||
|
arrivalAirport: 'LAX'
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(page.getByRole('heading', { name: 'Transportation' })).toBeVisible();
|
||||||
|
await expect(page.getByText(/UA\s*1001/)).toBeVisible();
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Edit transportation' }).click();
|
||||||
|
const editDialog = page.getByRole('dialog', { name: 'Edit transportation' });
|
||||||
|
await expect(editDialog).toBeVisible();
|
||||||
|
await editDialog.locator('input[name="segments[0][flight_number]"]').fill('1002');
|
||||||
|
await editDialog.getByRole('button', { name: 'Save changes' }).click();
|
||||||
|
await expect(editDialog).toBeHidden();
|
||||||
|
|
||||||
|
await expect(page.getByText(/UA\s*1002/)).toBeVisible();
|
||||||
|
await expect(page.getByText(/UA\s*1001/)).toHaveCount(0);
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Remove transportation' }).click();
|
||||||
|
await expect(page.getByRole('heading', { name: 'Transportation' })).toHaveCount(0);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user