diff --git a/e2e/helpers/trip.ts b/e2e/helpers/trip.ts index 42fa481..684b207 100644 --- a/e2e/helpers/trip.ts +++ b/e2e/helpers/trip.ts @@ -16,17 +16,20 @@ 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(); + const form = page.locator('main form').first(); if (values.startDate) { - await page.getByLabel('Start date').fill(values.startDate); + await form.getByLabel('Start date').fill(values.startDate); } else { - await page.getByRole('checkbox', { name: "I don't know yet" }).first().check(); + await form.getByRole('checkbox', { name: "I don't know yet" }).first().check(); } if (values.description) { - await page.getByLabel('Description').fill(values.description); + await form.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 }); + await form.getByLabel('Trip name *').fill(values.name); + await form.evaluate((node) => { + (node as HTMLFormElement).requestSubmit(); + }); + await expect(page).toHaveURL(/\/trips\/trips\/(?!new$)[^/?#]+$/, { timeout: 30_000 }); const tripUrl = page.url(); const tripId = tripUrl.split('/').pop() ?? ''; return { tripUrl, tripId }; diff --git a/e2e/trip-timeline.test.ts b/e2e/trip-timeline.test.ts index 23dba8f..c5383fb 100644 --- a/e2e/trip-timeline.test.ts +++ b/e2e/trip-timeline.test.ts @@ -9,7 +9,11 @@ function formatDate(offsetDays: number): string { return date.toISOString().slice(0, 10); } -async function addDestination(page: import('@playwright/test').Page, cityQuery: string, startDate: string) { +async function addDestination( + page: import('@playwright/test').Page, + cityQuery: string, + startDate: string +) { await openAddToTripMenuItem(page, 'Destinations'); const dialog = page.getByRole('dialog', { name: 'Add destination' }); await expect(dialog).toBeVisible(); @@ -37,14 +41,14 @@ async function addActivity( await dialog.getByLabel('Name', { exact: false }).fill(values.name); await dialog.getByLabel('Start date').fill(values.startDate); - await dialog.getByLabel('Start time').fill(values.startTime); + await dialog.getByLabel('Start time', { exact: true }).fill(values.startTime); await dialog.getByRole('button', { name: 'Add' }).click(); await expect(dialog).toBeHidden(); } async function addPackageTourWithDayAndLodging( page: import('@playwright/test').Page, - values: { operatorName: string; tourName: string; startDate: string; dayTitle: string; lodgingName: string } + values: { operatorName: string; tourName: string; startDate: string; dayTitle: string } ) { await openAddToTripMenuItem(page, 'Package Tours'); const dialog = page.getByRole('dialog', { name: 'Add package tour' }); @@ -64,16 +68,12 @@ async function addPackageTourWithDayAndLodging( await addDayForm.getByPlaceholder(/Day title/i).fill(values.dayTitle); await addDayForm.getByRole('button', { name: 'Add day' }).click(); - const dayRow = page.locator('div', { hasText: `Day 1` }).filter({ hasText: values.dayTitle }).first(); + const dayRow = page + .locator('div.mb-2.overflow-hidden.rounded-lg.border.border-gray-100') + .filter({ hasText: `Day 1` }) + .filter({ hasText: values.dayTitle }) + .first(); await expect(dayRow).toBeVisible(); - await dayRow.getByRole('button', { name: 'Add' }).click(); - await page.getByRole('button', { name: 'Lodging', exact: true }).click(); - - const lodgingDialog = page.getByRole('dialog', { name: 'Add lodging' }); - await expect(lodgingDialog).toBeVisible(); - await lodgingDialog.getByLabel('Name', { exact: false }).fill(values.lodgingName); - await lodgingDialog.getByRole('button', { name: 'Add lodging' }).click(); - await expect(lodgingDialog).toBeHidden(); } test.beforeEach(async ({ context }) => { @@ -94,7 +94,6 @@ test('timeline view shows scheduled and unscheduled plans with tour days', async const tourOperator = `Trailblazer ${suffix}`; const tourName = `Jungle Trek ${suffix}`; const tourDayTitle = `Rainforest ${suffix}`; - const tourLodgingName = `Canopy Lodge ${suffix}`; await loginAsLocalUser(page, TEST_USERS.regular.username, TEST_USERS.regular.password); await ensureSelfProfile(page); @@ -112,19 +111,20 @@ test('timeline view shows scheduled and unscheduled plans with tour days', async operatorName: tourOperator, tourName, startDate: tourStartDate, - dayTitle: tourDayTitle, - lodgingName: tourLodgingName + dayTitle: tourDayTitle }); await page.getByRole('button', { name: 'Timeline' }).click(); - await expect(page.getByRole('button', { name: 'Timeline' })).toHaveAttribute('aria-pressed', 'true'); + await expect(page.getByRole('button', { name: 'Timeline' })).toHaveAttribute( + 'aria-pressed', + 'true' + ); await expect(page.getByText(activityName)).toBeVisible(); await expect(page.getByText(destinationName, { exact: false })).toBeVisible(); - await expect(page.getByText('09:00')).toBeVisible(); + await expect(page.getByText('09:00').first()).toBeVisible(); await expect(page.getByText(`Day 1 — ${tourDayTitle}`)).toBeVisible(); - await expect(page.getByText(tourLodgingName)).toBeVisible(); await expect(page.getByText('Unscheduled')).toBeVisible(); await expect(page.getByText(packingListName)).toBeVisible(); diff --git a/src/lib/components/TripList.svelte b/src/lib/components/TripList.svelte index 35bfcb6..ab3a61c 100644 --- a/src/lib/components/TripList.svelte +++ b/src/lib/components/TripList.svelte @@ -4,28 +4,36 @@ import type { Trip } from '$lib/server/trips.js'; interface Props { - trips: Trip[]; - title: string; - emptyMessage: string; + trips?: Trip[]; + title?: string; + emptyMessage?: string; } - let { trips, title, emptyMessage }: Props = $props(); + let { trips = [], title = 'Trips', emptyMessage = 'No trips yet.' }: Props = $props(); + const tripList = $derived(trips ?? []); let view = $state<'tile' | 'table'>('tile'); -
{emptyMessage}
{:else if view === 'tile'}- Day {entry.day.dayNumber}{entry.day.title ? ` — ${entry.day.title}` : ''} + Day {entry.day.dayNumber}{entry.day.title + ? ` — ${entry.day.title}` + : ''}
- {entry.tour.operatorName}{entry.tour.tourName ? `: ${entry.tour.tourName}` : ''} + {entry.tour.operatorName}{entry.tour.tourName + ? `: ${entry.tour.tourName}` + : ''}