Files
hanzo.ai/e2e/gates/sidebar-symmetry.spec.ts

82 lines
3.6 KiB
TypeScript

import { expect, test } from '@playwright/test'
import { serveExport } from './export'
import { signIn } from '../session'
let base: string
let stop: () => Promise<void>
test.beforeAll(async () => {
const served = await serveExport()
base = served.url
stop = served.close
})
test.afterAll(async () => stop())
test('sidebar has tight margin and zero icon shift when toggled open/closed', async ({ page }) => {
await signIn(page, { name: 'Antje', orgs: [{ org: 'webby-ai', role: 'admin' }] })
await page.addInitScript(() => localStorage.setItem('hanzo_iam_current_org', 'webby-ai'))
await page.route(
(u) => u.pathname.startsWith('/v1/'),
(route) => route.fulfill({ json: {} })
)
await page.setViewportSize({ width: 1440, height: 900 })
await page.goto(`${base}/chat`)
await page.waitForLoadState('networkidle')
const toggle = page.locator('[data-slot="sidebar-toggle"]')
await expect(toggle).toBeVisible()
// Ensure sidebar is open first
if ((await toggle.getAttribute('aria-expanded')) !== 'true') {
await toggle.click()
}
await expect(page.locator('[data-slot="app-column"]')).toBeVisible()
// Measure icon position when open
const iconOpen = (await page.locator('[data-slot="rail-chat"] svg').boundingBox())!
const railOpen = (await page.locator('[data-slot="rail"]').boundingBox())!
console.log(`[MEASUREMENT OPEN] Rail: x=${railOpen.x}, width=${railOpen.width}`)
console.log(`[MEASUREMENT OPEN] Icon: x=${iconOpen.x}, width=${iconOpen.width}`)
// Close the sidebar
await toggle.click()
await expect(page.locator('[data-slot="app-column"]')).toHaveCount(0)
// Measure mark, icon, rail, and stage when closed
const railClosed = (await page.locator('[data-slot="rail"]').boundingBox())!
const markClosed = (await page.locator('[data-slot="rail-chat"]').boundingBox())!
const iconClosed = (await page.locator('[data-slot="rail-chat"] svg').boundingBox())!
const stageClosed = (await page.locator('[data-slot="stage"]').boundingBox())!
const leftMarginMark = markClosed.x - 0
const rightMarginMark = stageClosed.x - (markClosed.x + markClosed.width)
const leftMarginIcon = iconClosed.x - 0
const rightMarginIcon = stageClosed.x - (iconClosed.x + iconClosed.width)
console.log(`[MEASUREMENT CLOSED] Rail: x=${railClosed.x}, width=${railClosed.width}`)
console.log(`[MEASUREMENT CLOSED] Mark: x=${markClosed.x}, width=${markClosed.width}`)
console.log(`[MEASUREMENT CLOSED] Icon: x=${iconClosed.x}, width=${iconClosed.width}`)
console.log(`[MEASUREMENT CLOSED] Stage: x=${stageClosed.x}`)
console.log(`[MEASUREMENT CLOSED] Mark margins: left=${leftMarginMark}px, right=${rightMarginMark}px`)
console.log(`[MEASUREMENT CLOSED] Icon margins: left=${leftMarginIcon}px, right=${rightMarginIcon}px`)
// Save screenshot of closed sidebar
await page.screenshot({ path: 'out/sidebar-closed-symmetry.png', fullPage: false })
// 1. Zero icon movement between open and closed!
expect(iconClosed.x, 'Icon horizontal position must not move when sidebar is toggled').toBe(iconOpen.x)
expect(railClosed.x, 'Rail must stay flush at window left edge (x=0)').toBe(0)
expect(railClosed.width, 'Rail width should be 44px for 4px compact margin around 36px mark').toBe(44)
// 2. Pixel-perfect symmetry on the tight 4px margin
expect(leftMarginMark, 'Mark should have identical tight left and right margin').toBe(rightMarginMark)
expect(leftMarginMark, 'Mark should have 4px compact margin').toBe(4)
expect(leftMarginIcon, 'Icon should have identical tight left and right margin').toBe(rightMarginIcon)
expect(leftMarginIcon, 'Icon should have 13px left and right margin').toBe(13)
})