trip) add timeline view to trip page (#48)
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m21s
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m21s
Co-authored-by: Shaun Campbell <shaun@campbellwireless.net> Reviewed-on: #48 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 #48.
This commit is contained in:
@@ -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 };
|
||||
|
||||
131
e2e/trip-timeline.test.ts
Normal file
131
e2e/trip-timeline.test.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { TEST_USERS } from './setup/test-users.js';
|
||||
import { loginAsLocalUser, ensureSelfProfile } from './helpers/auth.js';
|
||||
import { addPackingList, createTrip, openAddToTripMenuItem, 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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
await dialog.getByLabel('City', { exact: false }).fill(cityQuery);
|
||||
const cityOption = dialog.locator('ul button').filter({ hasText: cityQuery }).first();
|
||||
await expect(cityOption).toBeVisible();
|
||||
await cityOption.click();
|
||||
|
||||
await dialog.getByLabel('Arrival').fill(startDate);
|
||||
await dialog.getByRole('button', { name: 'Add to trip' }).click();
|
||||
await expect(dialog).toBeHidden();
|
||||
}
|
||||
|
||||
async function addActivity(
|
||||
page: import('@playwright/test').Page,
|
||||
values: { name: string; startDate: string; startTime: string }
|
||||
) {
|
||||
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.getByLabel('Start date').fill(values.startDate);
|
||||
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 }
|
||||
) {
|
||||
await openAddToTripMenuItem(page, 'Package Tours');
|
||||
const dialog = page.getByRole('dialog', { name: 'Add package tour' });
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
await dialog.getByLabel('Tour operator', { exact: false }).fill(values.operatorName);
|
||||
await dialog.getByLabel('Tour name', { exact: false }).fill(values.tourName);
|
||||
await dialog.locator('input[name="start_date"]').fill(values.startDate);
|
||||
await dialog.getByRole('button', { name: 'Add tour' }).click();
|
||||
await expect(dialog).toBeHidden();
|
||||
|
||||
await expect(page.getByText(values.operatorName)).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Add day' }).click();
|
||||
const addDayForm = page.locator('form[action="?/addTourDay"]');
|
||||
await expect(addDayForm).toBeVisible();
|
||||
await addDayForm.getByPlaceholder(/Day title/i).fill(values.dayTitle);
|
||||
await addDayForm.getByRole('button', { name: 'Add day' }).click();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ context }) => {
|
||||
await context.clearCookies();
|
||||
});
|
||||
|
||||
test('timeline view shows scheduled and unscheduled plans with tour days', async ({ page }) => {
|
||||
const suffix = uniqueSuffix();
|
||||
const startDate = formatDate(10);
|
||||
const activityDate = formatDate(11);
|
||||
const destinationDate = formatDate(12);
|
||||
const tourStartDate = formatDate(13);
|
||||
|
||||
const tripName = `E2E Timeline Trip ${suffix}`;
|
||||
const activityName = `Waterfall hike ${suffix}`;
|
||||
const destinationName = 'Tokyo';
|
||||
const packingListName = `Timeline essentials ${suffix}`;
|
||||
const tourOperator = `Trailblazer ${suffix}`;
|
||||
const tourName = `Jungle Trek ${suffix}`;
|
||||
const tourDayTitle = `Rainforest ${suffix}`;
|
||||
|
||||
await loginAsLocalUser(page, TEST_USERS.regular.username, TEST_USERS.regular.password);
|
||||
await ensureSelfProfile(page);
|
||||
|
||||
await createTrip(page, {
|
||||
name: tripName,
|
||||
startDate,
|
||||
description: 'Timeline view coverage'
|
||||
});
|
||||
|
||||
await addDestination(page, destinationName, destinationDate);
|
||||
await addActivity(page, { name: activityName, startDate: activityDate, startTime: '09:00' });
|
||||
await addPackingList(page, { name: packingListName, items: ['Passport', 'Sunscreen'] });
|
||||
await addPackageTourWithDayAndLodging(page, {
|
||||
operatorName: tourOperator,
|
||||
tourName,
|
||||
startDate: tourStartDate,
|
||||
dayTitle: tourDayTitle
|
||||
});
|
||||
|
||||
await page.getByRole('button', { name: 'Timeline' }).click();
|
||||
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').first()).toBeVisible();
|
||||
|
||||
await expect(page.getByText(`Day 1 — ${tourDayTitle}`)).toBeVisible();
|
||||
|
||||
await expect(page.getByText('Unscheduled')).toBeVisible();
|
||||
await expect(page.getByText(packingListName)).toBeVisible();
|
||||
});
|
||||
Reference in New Issue
Block a user