mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
This export carried a second copy of the builder at /dev — the composer,
a run's transcript and a project pane (app/(app)/dev and
components/workspace/{Build,Session,project}.tsx), plus DevStudio.tsx and
enter.ts, which nothing else used. They are deleted, and dev.hanzo.ai is no
longer published from this export.
Every link to the builder is DEV or dev(ref) from lib/host.ts. dev() carries
a run id or a project slug into the path only when it is exactly one, so no
caller can make it name another address. The shell's Dev tile, Projects and
Runs open it there; /code forwards there. The edge's permanent redirects for
hanzo.ai/dev, /code, dev.hanzo.ai and hanzo.app live in universe.
Gates and specs that drove the old /dev drive /chat where they tested the
shared shell, and are removed where the builder itself was the subject.
Co-authored-by: Hanzo Dev <[email protected]>
82 lines
3.3 KiB
TypeScript
82 lines
3.3 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
|
import { signIn } from './session'
|
|
|
|
/**
|
|
* EVERY ROOM, AT EVERY SIZE A PERSON HOLDS ONE AT.
|
|
*
|
|
* Three things a layout owes a reader, and none of them is a style: the page
|
|
* does not scroll sideways, nothing you can press sits outside the window, and
|
|
* the room itself has room. A shell that fails any of these is unusable at that
|
|
* width whatever it looks like at another.
|
|
*
|
|
* The widths are the four form factors rather than a ramp of guesses — a desktop
|
|
* at 1920, a laptop at 1440, a tablet at 834 and a phone at 390.
|
|
*/
|
|
const SIZES = [
|
|
{ w: 1920, h: 1080, name: 'desktop' },
|
|
{ w: 1440, h: 900, name: 'laptop' },
|
|
{ w: 834, h: 1112, name: 'tablet' },
|
|
{ w: 390, h: 844, name: 'phone' },
|
|
]
|
|
|
|
// The rooms this export OWNS. /home is not one: it forwards to /chat and
|
|
// renders nothing of its own, so measuring it measures whatever answers there.
|
|
// /chat is not one either while the address is served by the app it is
|
|
// replacing — asserting our layout against somebody else's page says nothing
|
|
// about either.
|
|
const ROOMS = ['/chat', '/work', '/inbox', '/drive', '/board']
|
|
|
|
|
|
test.describe('the shell holds at every size', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await signIn(page)
|
|
})
|
|
|
|
for (const room of ROOMS) {
|
|
test(`${room} fits`, async ({ page }) => {
|
|
for (const s of SIZES) {
|
|
await page.setViewportSize({ width: s.w, height: s.h })
|
|
await page.goto(room)
|
|
await page.waitForLoadState('networkidle')
|
|
await page.evaluate(() => document.fonts.ready)
|
|
|
|
const look = () =>
|
|
page.evaluate(() => {
|
|
const main = document.querySelector('main')
|
|
const box = main?.getBoundingClientRect()
|
|
return {
|
|
// A page that scrolls sideways has already lost: the reader has to
|
|
// find the content by dragging it into view.
|
|
sideways: document.documentElement.scrollWidth > innerWidth + 1,
|
|
// A control off the window is a control that does not exist. The
|
|
// rail scrolls on purpose, so anything inside a scroller is its own
|
|
// business and not the layout's.
|
|
unreachable: [...document.querySelectorAll('button,[role="button"],a[href]')]
|
|
.filter((e) => {
|
|
const r = e.getBoundingClientRect()
|
|
if (r.width === 0 || r.height === 0) return false
|
|
if (r.right <= innerWidth + 1 && r.left >= -1) return false
|
|
for (let p = e.parentElement; p; p = p.parentElement) {
|
|
const o = getComputedStyle(p).overflowX
|
|
if (o === 'auto' || o === 'scroll') return false
|
|
}
|
|
return true
|
|
})
|
|
.map((e) => e.getAttribute('aria-label') || (e.textContent || '').trim().slice(0, 20))
|
|
.slice(0, 8),
|
|
// The room is what the reader came for; it cannot be a sliver.
|
|
room: box ? Math.round(box.width) : 0,
|
|
}
|
|
})
|
|
|
|
await expect
|
|
.poll(look, { timeout: 12_000, message: `${room} at ${s.name} ${s.w}x${s.h}` })
|
|
.toMatchObject({ sideways: false, unreachable: [] })
|
|
|
|
const { room: width } = await look()
|
|
expect(width, `${room} pane width at ${s.name}`).toBeGreaterThan(s.w * 0.25)
|
|
}
|
|
})
|
|
}
|
|
})
|