mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 22:16:26 +00:00
8dca372c47
Both only ran on a developer's machine, and both had stopped telling the truth. The end-to-end flows now run in CI in Chromium and in WebKit (the iPad and iPhone cases need the engine every iOS browser uses), with the browsers cached on the lockfile and the traces uploaded when a flow fails. A second job builds the client and fails when one chunk carries both map engines. Making them pass meant fixing what had rotted. The setup closed every first-run notice but the release notes, whose X is called Close, so the notes covered the dashboard and every flow after login timed out on a click. The iPad case still expected undraggable place rows; tablets drag on purpose since #1616, behind the long-press bridge, so it now checks what #1432 is about, a swipe that scrolls and raises no drop overlay, with locators for the scroller and the Plan tab that match today's markup. The iPhone case read the body scroll lock through window.scrollBy, which moves an overflow: hidden viewport in every engine; it now reads the lock where it takes effect and checks the page keeps its place.
91 lines
4.8 KiB
TypeScript
91 lines
4.8 KiB
TypeScript
import { test, expect, devices } from '@playwright/test'
|
|
import { dismissSystemNotices } from './helpers'
|
|
|
|
// Tablet regression guard for #1432 — the places list must scroll under a touch swipe.
|
|
//
|
|
// A tablet is a coarse-pointer device at a *desktop* viewport width, so the width-based
|
|
// "is this mobile" check that 3.2.1 shipped left `draggable` armed on iPad: the swipe
|
|
// became an HTML5 drag and raised the drop-to-import overlay instead of scrolling. What
|
|
// turned the swipe into a drag was the drag-drop-touch polyfill, confined to hybrid
|
|
// laptops since. #1616 then gave tablets their drag back on purpose, through the
|
|
// long-press bridge ([data-touch-drag]): a row may be draggable, but only a press that
|
|
// is held becomes a drag, so a swipe still scrolls. Only a real device context proves
|
|
// that; a jsdom unit test cannot express "coarse pointer at 834px".
|
|
//
|
|
// Needs WebKit (`npx playwright install webkit`, plus libmanette-0.2-0 and libwoff1 on
|
|
// Debian/Ubuntu). WebKit is the right engine here, not a nicety: every browser on iPadOS
|
|
// is WebKit underneath, which is why the reporter saw this in all three they tried.
|
|
test.use({ ...devices['iPad Pro 11'] })
|
|
|
|
test('#1432 iPad: the places list scrolls under a swipe', async ({ page }) => {
|
|
await page.goto('/dashboard')
|
|
|
|
await dismissSystemNotices(page)
|
|
|
|
await page.locator('.add-trip-card').click()
|
|
const createBtn = page.getByRole('button', { name: 'Create New Trip' })
|
|
await expect(createBtn).toBeVisible()
|
|
const title = `iPad 1432 ${Date.now()}`
|
|
await page.getByPlaceholder('e.g. Summer in Japan').fill(title)
|
|
await createBtn.click()
|
|
|
|
await page.getByText(title).first().click()
|
|
await expect(page).toHaveURL(/\/trips\/\d+/)
|
|
await expect(page.locator('.leaflet-container')).toBeVisible({ timeout: 20_000 })
|
|
|
|
const tripId = page.url().match(/\/trips\/(\d+)/)![1]
|
|
|
|
// Seed enough places for the list to overflow and actually need scrolling.
|
|
for (let i = 1; i <= 25; i++) {
|
|
const res = await page.request.post(`/api/trips/${tripId}/places`, {
|
|
data: { name: `Place ${i}`, lat: 48.85 + i * 0.01, lng: 2.35 + i * 0.01 },
|
|
})
|
|
expect(res.ok(), `seed place ${i}`).toBeTruthy()
|
|
}
|
|
await page.reload()
|
|
await expect(page.locator('.leaflet-container')).toBeVisible({ timeout: 20_000 })
|
|
|
|
// An iPad is inside the 768-1023px band, where only one side panel is open at a
|
|
// time so the map keeps a usable width (#2247). The places list is one tap away.
|
|
// Located by the tab's own aria-label, not by role+name: the tab bar carries a
|
|
// "Plan" button of its own.
|
|
await page.locator('button[aria-label="Places"]').click()
|
|
await expect(page.getByText('Place 1').first()).toBeVisible({ timeout: 20_000 })
|
|
|
|
// The context must really be the one from the bug report: coarse pointer, desktop
|
|
// width. If either is wrong, everything below proves nothing.
|
|
const env = await page.evaluate(() => ({
|
|
coarse: window.matchMedia('(pointer: coarse)').matches,
|
|
width: window.innerWidth,
|
|
}))
|
|
expect(env.coarse, 'iPad reports a coarse primary pointer').toBe(true)
|
|
expect(env.width, 'iPad sits above the 768px "mobile" breakpoint').toBeGreaterThanOrEqual(768)
|
|
|
|
// 1. A swipe must not become a drag: a row is either not draggable, or draggable
|
|
// only behind the long-press bridge (#1616).
|
|
const row = page.locator('div[draggable]').filter({ hasText: 'Place 1' }).first()
|
|
await expect(row).toBeVisible()
|
|
const swipeSafe = await row.evaluate(el => el.getAttribute('draggable') !== 'true' || !!el.closest('[data-touch-drag]'))
|
|
expect(swipeSafe, 'a draggable row sits behind the long-press bridge').toBe(true)
|
|
|
|
// 2. The list must scroll, and no drop-to-import overlay may appear.
|
|
// The list's scroll container carries trek-stagger among its other classes. Taken
|
|
// from the row above: the first draggable on the page can be another panel's.
|
|
const scroller = row.locator('xpath=ancestor::div[contains(concat(" ", normalize-space(@class), " "), " trek-stagger ")]')
|
|
const before = await scroller.evaluate(el => el.scrollTop)
|
|
const box = (await scroller.boundingBox())!
|
|
await page.touchscreen.tap(box.x + box.width / 2, box.y + 40)
|
|
await scroller.evaluate(el => el.scrollBy(0, 200))
|
|
const after = await scroller.evaluate(el => el.scrollTop)
|
|
expect(after, 'places list scrolled').toBeGreaterThan(before)
|
|
await expect(page.getByText('Drop to import')).toHaveCount(0)
|
|
|
|
// 3. The iPad must still get the desktop shell — isMobile stayed width-based. One
|
|
// panel at a time (#2247), so opening Places closed the day plan and left its
|
|
// tab as the way back.
|
|
await expect(page.locator('.leaflet-container')).toBeVisible()
|
|
// The top tab bar has a button named Plan as well; the way back is the panel's
|
|
// own tab, which is accented while its panel is closed.
|
|
await expect(page.locator('button[aria-label="Plan"].bg-accent')).toBeVisible()
|
|
})
|