mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 07:40:59 +00:00
111 lines
5.1 KiB
TypeScript
111 lines
5.1 KiB
TypeScript
import { expect, test, type Locator } from '@playwright/test';
|
|
|
|
// A visible DOM node can still be clipped by a panel. Check the whole bar
|
|
// before interacting: Playwright's automatic scroll-into-view could mask it.
|
|
async function expectBarInsidePanel(bar: Locator, panel: Locator) {
|
|
await expect(bar).toBeInViewport({ ratio: 1 });
|
|
await expect
|
|
.poll(async () => {
|
|
const bounds = await bar.boundingBox();
|
|
const parent = await panel.boundingBox();
|
|
return (
|
|
!!bounds &&
|
|
!!parent &&
|
|
bounds.y >= parent.y &&
|
|
bounds.y + bounds.height <= parent.y + parent.height + 1 &&
|
|
bounds.x >= parent.x &&
|
|
bounds.x + bounds.width <= parent.x + parent.width + 1
|
|
);
|
|
})
|
|
.toBe(true);
|
|
}
|
|
|
|
for (const layout of [
|
|
{ name: 'desktop', viewport: { width: 1440, height: 900 }, hasTouch: false },
|
|
{ name: 'tablet', viewport: { width: 834, height: 1112 }, hasTouch: true },
|
|
{ name: 'short desktop', viewport: { width: 1280, height: 600 }, hasTouch: false },
|
|
]) {
|
|
test.describe(layout.name, () => {
|
|
test.use({
|
|
viewport: layout.viewport,
|
|
hasTouch: layout.hasTouch,
|
|
});
|
|
|
|
for (const tours of [false, true]) {
|
|
for (const count of [10, 27, 190]) {
|
|
test(`${count} places, Tours ${tours ? 'on' : 'off'}: selection bar stays reachable`, async ({ page }) => {
|
|
test.setTimeout(90_000);
|
|
const activate = (button: Locator) => (layout.hasTouch ? button.tap() : button.click());
|
|
const addonsResponse = await page.request.get('/api/admin/addons');
|
|
expect(addonsResponse.ok()).toBeTruthy();
|
|
const { addons } = await addonsResponse.json();
|
|
const originalTours = addons.find((addon: { id: string }) => addon.id === 'tours');
|
|
expect(originalTours).toBeDefined();
|
|
let tripId: number | undefined;
|
|
|
|
try {
|
|
expect((await page.request.put('/api/admin/addons/tours', { data: { enabled: tours } })).ok()).toBeTruthy();
|
|
const tripResponse = await page.request.post('/api/trips', {
|
|
data: {
|
|
title: `Selection bar ${layout.name} ${count}`,
|
|
start_date: '2027-01-01',
|
|
end_date: '2027-01-02',
|
|
},
|
|
});
|
|
expect(tripResponse.ok()).toBeTruthy();
|
|
tripId = (await tripResponse.json()).trip.id;
|
|
for (let i = 1; i <= count; i++) {
|
|
const response = await page.request.post(`/api/trips/${tripId}/places`, {
|
|
data: { name: `Selection place ${i}`, lat: 48.85, lng: 2.35 },
|
|
});
|
|
expect(response.ok(), `seed place ${i}`).toBeTruthy();
|
|
}
|
|
|
|
await page.goto(`/trips/${tripId}`);
|
|
if (layout.name === 'tablet') await page.locator('button[aria-label="Places"]').click();
|
|
const sidebar = page
|
|
.locator('[data-touch-drag]')
|
|
.filter({ has: page.getByRole('button', { name: 'Select', exact: true }) });
|
|
const panel = sidebar.locator('..');
|
|
const rows = sidebar.getByRole('option');
|
|
await expect(rows).toHaveCount(count);
|
|
await expect(panel.getByRole('tab', { name: 'Tours', exact: true })).toHaveCount(tours ? 1 : 0);
|
|
await activate(sidebar.getByRole('button', { name: 'Select', exact: true }));
|
|
const bar = sidebar.getByRole('status').locator('../..');
|
|
await expectBarInsidePanel(bar, panel);
|
|
|
|
const list = sidebar.locator('.trek-stagger');
|
|
const before = await bar.boundingBox();
|
|
// Only the list scrolls; the bar must remain at the panel's foot.
|
|
await list.evaluate((el) => {
|
|
el.scrollTop = el.scrollHeight;
|
|
});
|
|
if (count >= 27) await expect.poll(() => list.evaluate((el) => el.scrollTop)).toBeGreaterThan(0);
|
|
await expectBarInsidePanel(bar, panel);
|
|
expect((await bar.boundingBox())?.y).toBe(before?.y);
|
|
await activate(sidebar.getByRole('button', { name: 'Select all', exact: true }));
|
|
await expect(sidebar.getByRole('status')).toHaveText(String(count));
|
|
await activate(sidebar.getByRole('button', { name: 'Done', exact: true }));
|
|
await expect(sidebar.getByRole('status')).toHaveCount(0);
|
|
|
|
await activate(sidebar.getByRole('button', { name: 'Select', exact: true }));
|
|
await list.evaluate((el) => {
|
|
el.scrollTop = 0;
|
|
});
|
|
await expectBarInsidePanel(bar, panel);
|
|
await activate(sidebar.getByRole('button', { name: 'Select all', exact: true }));
|
|
await activate(sidebar.getByRole('button', { name: 'Delete selected', exact: true }));
|
|
await activate(page.getByRole('button', { name: 'Delete', exact: true }));
|
|
await expect(rows).toHaveCount(0);
|
|
} finally {
|
|
if (tripId !== undefined) expect((await page.request.delete(`/api/trips/${tripId}`)).ok()).toBeTruthy();
|
|
expect(
|
|
(await page.request.put('/api/admin/addons/tours', { data: { enabled: !!originalTours.enabled } })).ok()
|
|
).toBeTruthy();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|