Files
hanzo.ai/e2e/gates/columns.spec.ts
fbc926f525 The app is @hanzo/rooms 0.1.55's App, at paths under /chat and /dev, behind the organization gate
_web.tsx draws rooms' App inside rooms' `Orgs`, as every room is: a reader
with no organization makes one, one with several picks one, and a team on Free
is asked for the Team plan before the app is drawn; a personal organization on
Free opens it. The app reads its place from the path: /chat and /chat/<id> are
Chat, a path under /dev is Dev. /?chat=<id> and /chat?thread=<id> move to
/chat/<id>, /?at= to /dev?at=, whose place the door replaces with its path, as
it does the builder's grammar rooms 0.1.35-0.1.38 wrote under /dev.

The gates read the app at its paths and the rooms' shell at /home, with new
Linux baselines for it; the limit banner, the model chip and the side panel
are held to what rooms 0.1.55 draws; the landing's budget is 445 KB, since
rooms' lib/host carries @hanzo/build's route grammar. app/gui.css is what the
build writes.


Co-authored-by: Hanzo Dev <[email protected]>
2026-10-16 15:05:24 -07:00

71 lines
2.5 KiB
TypeScript

import { expect, test } from '@playwright/test'
import { rail } from './rail'
import { serveExport } from './export'
import { signIn } from '../session'
/**
* WITH BOTH COLUMNS OPEN, THE ROOM IS STILL A ROOM.
*
* Every other gate opens a room with its columns away, and a sidebar that
* floats over the room passes all of them: nothing is clipped, nothing leaves
* the window, every page still ships. It is simply covered — measured on
* production at 1440, `main` began at x=0 under a 240px sheet and the composer
* read "Hanzo to build…".
*
* So this opens what a working reader opens — the list on the left, the panel on
* the right — and asks the room for its own box at every width.
*/
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' },
]
test.describe('a room keeps its own space beside its columns', () => {
let base: string
let stop: () => Promise<void>
test.beforeAll(async () => {
const served = await serveExport()
base = served.url
stop = served.close
})
test.afterAll(async () => stop())
for (const s of SIZES) {
test(`/home at ${s.name}`, async ({ page }) => {
await signIn(page)
await page.setViewportSize({ width: s.w, height: s.h })
await page.goto(`${base}/home`)
await rail(page, '[data-slot="sidebar"]')
const panel = page.getByRole('button', { name: 'Open side panel' })
if (await panel.isVisible()) await panel.click()
const seen = async () =>
page.evaluate(() => {
const m = document.querySelector('main')!.getBoundingClientRect()
const sheet = document.querySelector('[data-slot="sidebar"]')?.getBoundingClientRect()
return {
left: Math.round(m.x),
width: Math.round(m.width),
sheetRight: sheet && sheet.width > 0 ? Math.round(sheet.right) : 0,
}
})
await expect.poll(async () => (await seen()).sheetRight, { timeout: 15_000 }).toBeGreaterThan(0)
const { left, width, sheetRight } = await seen()
// A phone gives the sheet the screen on purpose; anything wider owes the
// room the space it is standing in.
if (s.w >= 768) {
expect(left, `the room is covered by its own list at ${s.name}`).toBeGreaterThanOrEqual(
sheetRight - 1,
)
}
expect(width, `the room has no room at ${s.name}`).toBeGreaterThan(s.w * 0.2)
})
}
})