Files

111 lines
4.4 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { serveExport } from './export'
import { signIn } from '../session'
/**
* EVERY ROOM, AT EVERY WIDTH.
*
* `workspace.spec` measures two rooms on a phone, so five of the seven were
* never read at any size — and a room is exactly where this fails: the sidebar
* covering the pane was found at 1440 and again at 834, each time after the
* previous fix was written against the width it was found at.
*
* TWO THINGS, and they are the two ways a room stops being readable:
*
* unreachable something sits past the right edge with nothing that scrolls
* between it and the window. base.css clips the body, so
* such content is not merely off screen — there is no gesture
* that brings it back.
*
* crowded out `main` has been squeezed under a fifth of the window by
* whatever shares the row with it.
*
* TWO KINDS OF BOX ARE NOT DEFECTS, and both were found by measuring rather
* than by reasoning:
*
* inside a scroller that is what a scroller is for, and the board's column
* strip is the case. Without reading the ancestors, the
* board reported three false failures.
*
* carrying nothing a glow anchored to a card corner sits at `right: -64px`
* by design and the body clips it. Nothing is out of
* reach, because there was nothing in it — /pricing
* reported two of these.
*
* So the subject is CONTENT past the edge: a box with words in it, or one a
* reader could press.
*/
const SIZES = [
{ name: 'phone', w: 390, h: 844 },
{ name: 'tablet', w: 834, h: 1112 },
{ name: 'laptop', w: 1440, h: 900 },
{ name: 'desktop', w: 1920, h: 1080 },
]
/**
* A PHONE IS ALSO A THUMB, and that is a different measurement.
*
* `@hanzo/design`'s base.css grows every control to a 44px floor under `pointer: coarse`, so
* a room measured in a 390px DESKTOP window is a room whose controls are still
* their small size — the widths that ship to an actual phone were never read.
* Twice now the difference has mattered: the organization switcher was 28px
* until a coarse run showed it, and the composer's controls only reach 44 here.
*
* A separate file rather than a fifth entry above, because the pointer is a
* property of the CONTEXT and Playwright fixes that per file.
*/
const ROOMS = ['/chat', '/home', '/board', '/drive', '/inbox', '/work']
for (const room of ROOMS) {
test(`${room} is readable at every width`, async ({ page }) => {
test.setTimeout(90_000)
await signIn(page)
const site = await serveExport()
try {
for (const s of SIZES) {
await page.setViewportSize({ width: s.w, height: s.h })
await page.goto(`${site.url}${room}`)
await expect(page.locator('main')).toBeVisible({ timeout: 20_000 })
const found = await page.evaluate((width) => {
const scrolls = (el: Element | null): boolean => {
for (let n = el; n; n = n.parentElement) {
const o = getComputedStyle(n).overflowX
if (o === 'auto' || o === 'scroll') return true
}
return false
}
const speaks = (el: Element): boolean =>
(el.textContent || '').trim().length > 0 ||
el.matches('button,[role=button],a[href],select,input,textarea') ||
el.querySelector('button,[role=button],a[href],select,input,textarea') !== null
const past: string[] = []
for (const el of Array.from(document.querySelectorAll('body *'))) {
const b = el.getBoundingClientRect()
if (
b.width > 0 &&
b.height > 0 &&
b.right > width + 1 &&
!scrolls(el.parentElement) &&
speaks(el)
)
past.push(`${el.tagName}.${(el.className || '').toString().split(' ')[0]}`)
}
const box = document.querySelector('main')?.getBoundingClientRect()
return { past: past.slice(0, 3), main: box ? Math.round(box.width) : -1 }
}, s.w)
expect(found.past, `${room} puts something out of reach at ${s.name}`).toEqual([])
expect(found.main, `${room} leaves its pane no room at ${s.name}`).toBeGreaterThan(
s.w * 0.2,
)
}
} finally {
await site.close()
}
})
}