From 25d9d28a35d63584b6c685ebede1308cc0949f71 Mon Sep 17 00:00:00 2001 From: AI Agent Date: Sun, 22 Feb 2026 23:07:24 +0000 Subject: [PATCH 1/2] e2e) add medium-priority playwright coverage --- e2e/checklist-experience-persistence.test.ts | 39 +++++ e2e/helpers/auth.ts | 27 ++++ e2e/helpers/trip.ts | 142 +++++++++++++++++++ e2e/lodging-permissions.test.ts | 74 ++++++++++ e2e/package-tours-import.test.ts | 60 ++++++++ e2e/transportation-lifecycle.test.ts | 51 +++++++ 6 files changed, 393 insertions(+) create mode 100644 e2e/checklist-experience-persistence.test.ts create mode 100644 e2e/helpers/auth.ts create mode 100644 e2e/helpers/trip.ts create mode 100644 e2e/lodging-permissions.test.ts create mode 100644 e2e/package-tours-import.test.ts create mode 100644 e2e/transportation-lifecycle.test.ts diff --git a/e2e/checklist-experience-persistence.test.ts b/e2e/checklist-experience-persistence.test.ts new file mode 100644 index 0000000..1f35a06 --- /dev/null +++ b/e2e/checklist-experience-persistence.test.ts @@ -0,0 +1,39 @@ +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 }); + + await page.getByRole('checkbox', { name: itemOne }).check(); + 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); +}); diff --git a/e2e/helpers/auth.ts b/e2e/helpers/auth.ts new file mode 100644 index 0000000..dab85ac --- /dev/null +++ b/e2e/helpers/auth.ts @@ -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 { + 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 { + 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(); +} diff --git a/e2e/helpers/trip.ts b/e2e/helpers/trip.ts new file mode 100644 index 0000000..e4d9670 --- /dev/null +++ b/e2e/helpers/trip.ts @@ -0,0 +1,142 @@ +import { expect, type Page } from '@playwright/test'; + +const NEW_TRIP_URL = '/trips/trips/new'; + +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(); + await page.getByLabel('Trip name', { exact: false }).fill(values.name); + if (values.startDate) { + await page.getByLabel('Start date').fill(values.startDate); + } + if (values.description) { + await page.getByLabel('Description').fill(values.description); + } + await page.getByRole('button', { name: 'Save' }).click(); + await page.waitForURL('**/trips/trips/*', { timeout: 15_000 }); + const tripUrl = page.url(); + const tripId = tripUrl.split('/').pop() ?? ''; + return { tripUrl, tripId }; +} + +export async function openAddToTripMenuItem(page: Page, label: string): Promise { + const addToTripButton = page.getByRole('button', { name: 'Add to trip' }); + 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(); +} + +export async function openAddTraveller(page: Page): Promise { + const addToTripButton = page.getByRole('button', { name: 'Add to trip' }); + 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 { + 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 { + await openAddToTripMenuItem(page, 'Transportation'); + const dialog = page.getByRole('dialog', { name: 'Add transportation' }); + await expect(dialog).toBeVisible(); + + await dialog.getByRole('button', { name: 'Flight', exact: true }).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 form.getByRole('button', { name: 'Add transportation' }).click(); + await expect(dialog).toBeHidden(); +} + +export async function addLodging( + page: Page, + values: { name: string; guestNames?: string[] } +): Promise { + 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 { + await openAddToTripMenuItem(page, 'Packing List'); + const dialog = page.getByRole('dialog', { name: /Packing list/i }); + 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' }).click(); + await expect(dialog).toBeHidden(); +} + +export async function addActivity( + page: Page, + values: { name: string } +): Promise { + await openAddToTripMenuItem(page, 'Attractions & Activities'); + const dialog = page.getByRole('dialog', { name: /Attraction & activity/i }); + await expect(dialog).toBeVisible(); + await dialog.getByLabel('Name', { exact: false }).fill(values.name); + await dialog.getByRole('button', { name: 'Add' }).click(); + await expect(dialog).toBeHidden(); +} diff --git a/e2e/lodging-permissions.test.ts b/e2e/lodging-permissions.test.ts new file mode 100644 index 0000000..a0b80f8 --- /dev/null +++ b/e2e/lodging-permissions.test.ts @@ -0,0 +1,74 @@ +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(); + } + } + + 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: selfName })).toBeVisible(); + await expect(lodgingDialog.getByRole('checkbox', { name: guestName })).toBeVisible(); + + await lodgingDialog.getByLabel('Name', { exact: false }).fill(lodgingName); + await lodgingDialog.getByRole('checkbox', { name: guestName }).check(); + 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(); +}); diff --git a/e2e/package-tours-import.test.ts b/e2e/package-tours-import.test.ts new file mode 100644 index 0000000..d0a202e --- /dev/null +++ b/e2e/package-tours-import.test.ts @@ -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(); +}); diff --git a/e2e/transportation-lifecycle.test.ts b/e2e/transportation-lifecycle.test.ts new file mode 100644 index 0000000..56f6333 --- /dev/null +++ b/e2e/transportation-lifecycle.test.ts @@ -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.getByLabel('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); +}); -- 2.49.1 From 44b2dee7e490b907b8e63640b78779a2ab1b6a92 Mon Sep 17 00:00:00 2001 From: Shaun Campbell Date: Sun, 22 Feb 2026 18:58:46 -0500 Subject: [PATCH 2/2] tests) fixing tests --- e2e/checklist-experience-persistence.test.ts | 5 ++ e2e/helpers/trip.ts | 57 +++++++++++++------- e2e/lodging-permissions.test.ts | 11 ++-- e2e/transportation-lifecycle.test.ts | 2 +- 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/e2e/checklist-experience-persistence.test.ts b/e2e/checklist-experience-persistence.test.ts index 1f35a06..876acc8 100644 --- a/e2e/checklist-experience-persistence.test.ts +++ b/e2e/checklist-experience-persistence.test.ts @@ -21,7 +21,12 @@ test('checklists and experiences persist after reload', async ({ page }) => { 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(); diff --git a/e2e/helpers/trip.ts b/e2e/helpers/trip.ts index e4d9670..42fa481 100644 --- a/e2e/helpers/trip.ts +++ b/e2e/helpers/trip.ts @@ -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 { - 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 { - 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 { 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 { +export async function addActivity(page: Page, values: { name: string }): Promise { 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(); diff --git a/e2e/lodging-permissions.test.ts b/e2e/lodging-permissions.test.ts index a0b80f8..e54a8b8 100644 --- a/e2e/lodging-permissions.test.ts +++ b/e2e/lodging-permissions.test.ts @@ -23,7 +23,10 @@ test('lodging guest selection and access boundaries are enforced', async ({ page 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 { tripUrl } = await createTrip(page, { + name: tripName, + description: 'Lodging guest coverage' + }); const selfName = 'E2E User'; await openAddTraveller(page); @@ -38,6 +41,9 @@ test('lodging guest selection and access boundaries are enforced', async ({ page 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(); } } @@ -52,11 +58,10 @@ test('lodging guest selection and access boundaries are enforced', async ({ page await expect(lodgingDialog).toBeVisible(); const guestName = `${guestFirst} ${guestLast}`; - await expect(lodgingDialog.getByRole('checkbox', { name: selfName })).toBeVisible(); await expect(lodgingDialog.getByRole('checkbox', { name: guestName })).toBeVisible(); await lodgingDialog.getByLabel('Name', { exact: false }).fill(lodgingName); - await lodgingDialog.getByRole('checkbox', { name: guestName }).check(); + await lodgingDialog.getByText(guestName, { exact: true }).click(); await lodgingDialog.getByRole('button', { name: 'Add lodging' }).click(); await expect(lodgingDialog).toBeHidden(); diff --git a/e2e/transportation-lifecycle.test.ts b/e2e/transportation-lifecycle.test.ts index 56f6333..9eb8bdc 100644 --- a/e2e/transportation-lifecycle.test.ts +++ b/e2e/transportation-lifecycle.test.ts @@ -39,7 +39,7 @@ test('transportation lifecycle updates the trip view', async ({ page }) => { await page.getByRole('button', { name: 'Edit transportation' }).click(); const editDialog = page.getByRole('dialog', { name: 'Edit transportation' }); await expect(editDialog).toBeVisible(); - await editDialog.getByLabel('Flight number').fill('1002'); + await editDialog.locator('input[name="segments[0][flight_number]"]').fill('1002'); await editDialog.getByRole('button', { name: 'Save changes' }).click(); await expect(editDialog).toBeHidden(); -- 2.49.1