Files
StarPilot/openpilot/starpilot/galaxy/tests/browser/tab-motion.cjs
T
firestar5683 00364fd645 Galaxy Access
Separate Galaxy authentication and local access policy from web presentation.

Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
2026-10-14 19:56:17 -05:00

79 lines
4.6 KiB
JavaScript

const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright')
const assert = require('node:assert/strict')
;(async () => {
const browser = await chromium.launch({ headless: true })
const page = await browser.newPage({ viewport: { width: 390, height: 900 } })
const errors = []
page.on('pageerror', error => errors.push(error.message))
await page.route('**/data/runtime.json', route => route.fulfill({ json: { schemaVersion: 1, monitor: 'local' } }))
await page.route('**/api/**', route => {
const path = new URL(route.request().url()).pathname
const json = path === '/api/auth/session' ? { state: 'configured', authenticated: true, localAccess: true } :
path === '/api/device/state' ? { state: 'parked', maxAgeMs: 3000 } :
path.startsWith('/api/settings/pages/') ? { page: 'hub', view: 'test', parked: true, rows: [] } : null
return route.fulfill({ status: json ? 200 : 503, json: json || { error: 'Unavailable in this test' } })
})
const base = process.env.GALAXY_URL || 'http://127.0.0.1:8765/'
await page.goto(base)
const nav = page.getByRole('navigation', { name: 'Primary navigation' })
await nav.getByRole('button', { name: 'Home', exact: true }).waitFor()
async function checkSlide(name, direction) {
await nav.getByRole('button', { name, exact: true }).click()
await page.waitForFunction(() => !!document.querySelector('.gx-route-enter-active'))
const motion = await page.locator('.gx-route-enter-active').evaluate(element => {
const matrix = new DOMMatrix(getComputedStyle(element).transform)
return { x: matrix.m41, y: matrix.m42 }
})
assert.ok(motion.x * direction > 0, `${name} enters from the correct horizontal side`)
assert.equal(motion.y, 0)
await page.waitForTimeout(500)
const geometry = await nav.evaluate(element => {
const pill = element.querySelector('.gx-nav-indicator').getBoundingClientRect()
const button = element.querySelector('[aria-current="page"]').getBoundingClientRect()
return { delta: Math.abs(pill.x - button.x), widthDelta: Math.abs(pill.width - button.width) }
})
assert.ok(geometry.delta < 2 && geometry.widthDelta < 2, 'glass indicator settles over the selected tab')
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false)
}
await checkSlide('Tools', 1)
await checkSlide('Home', -1)
await checkSlide('Routes', 1)
await checkSlide('Toggles', -1)
// Fast changes must leave only one interactive page and the correct selected tab.
await nav.getByRole('button', { name: 'Tools', exact: true }).click()
await nav.getByRole('button', { name: 'Home', exact: true }).click()
await page.waitForTimeout(600)
assert.equal(await page.locator('.gx-route:not([inert])').count(), 1)
assert.equal(await nav.getByRole('button', { name: 'Home', exact: true }).getAttribute('aria-current'), 'page')
await page.goto(base + '#/model_laboratory')
const heading = page.getByText('Available models', { exact: true })
await heading.waitFor()
for (const width of [320, 360, 390, 600, 1280]) {
await page.setViewportSize({ width, height: 900 })
const layout = await page.evaluate(() => {
const title = [...document.querySelectorAll('.gx-section__title')].find(element => element.textContent === 'Available models')
const summary = title.closest('.gx-card').querySelector('.gx-summary')
const cells = [...summary.children].map(element => element.getBoundingClientRect())
return { headerHeight: title.parentElement.getBoundingClientRect().height,
summaryHeight: summary.getBoundingClientRect().height,
equalRows: cells.every(cell => Math.abs(cell.top - cells[0].top) < 1),
overflow: document.documentElement.scrollWidth > innerWidth }
})
assert.ok(layout.headerHeight < 80 && layout.summaryHeight < 100, 'model summary stays compact')
assert.equal(layout.equalRows, true, 'all three counts share one row')
assert.equal(layout.overflow, false)
}
await page.setViewportSize({ width: 390, height: 900 })
await page.emulateMedia({ reducedMotion: 'reduce' })
await nav.getByRole('button', { name: 'Home', exact: true }).click()
await page.waitForTimeout(50)
assert.equal(await page.locator('.gx-route-enter-active, .gx-route-leave-active').count(), 0)
assert.equal(await page.locator('.gx-nav-indicator').evaluate(element => getComputedStyle(element).transitionDuration), '0s')
assert.deepEqual(errors, [])
console.log('Directional tab slides, moving glass selection, rapid navigation, compact model summary and reduced motion passed.')
await browser.close()
})().catch(error => { console.error(error); process.exit(1) })