154 lines
4.2 KiB
TypeScript
154 lines
4.2 KiB
TypeScript
import { expect, test, type Page } from '@playwright/test'
|
||
import { gotoAppRoute } from './helpers/navigation'
|
||
import { expectNoHorizontalOverflow } from './helpers/layout'
|
||
|
||
type PublicRouteExpectation = {
|
||
path: string
|
||
heading: string
|
||
cta?: string
|
||
}
|
||
|
||
const responsivePublicRoutes: readonly PublicRouteExpectation[] = [
|
||
{
|
||
path: '/',
|
||
heading: 'One serious home for classic cubes, 120‑cell, 5D, and deep daily practice.',
|
||
cta: 'Create account',
|
||
},
|
||
{
|
||
path: '/features',
|
||
heading: 'A deep training toolkit, not just a timer.',
|
||
},
|
||
{
|
||
path: '/about',
|
||
heading: 'A training stack built for people who want more than a timer.',
|
||
},
|
||
{
|
||
path: '/resources',
|
||
heading: 'Every guide you need to start, train, and grow with HyperTwist.',
|
||
},
|
||
{
|
||
path: '/docs',
|
||
heading: 'The complete HyperTwist manual.',
|
||
},
|
||
{
|
||
path: '/pricing',
|
||
heading: 'Pricing that matches the actual delivery model.',
|
||
},
|
||
{
|
||
path: '/download',
|
||
heading: 'Download the desktop build and pair it with your browser account.',
|
||
},
|
||
{
|
||
path: '/getting-started',
|
||
heading: 'Start on the website. Train in the desktop app.',
|
||
},
|
||
{
|
||
path: '/launch-status',
|
||
heading: 'See how public pages, account access, and desktop delivery work together.',
|
||
},
|
||
{
|
||
path: '/support',
|
||
heading: 'Help for access, downloads, setup, and launch.',
|
||
},
|
||
{
|
||
path: '/login',
|
||
heading: 'Log in to HyperTwist',
|
||
cta: 'Log in',
|
||
},
|
||
{
|
||
path: '/changelog',
|
||
heading: 'Release notes for players, teams, and studios.',
|
||
},
|
||
{
|
||
path: '/open-source-notices',
|
||
heading: 'Open-source notices for public distribution surfaces.',
|
||
},
|
||
{
|
||
path: '/privacy',
|
||
heading: 'Privacy model',
|
||
},
|
||
{
|
||
path: '/terms',
|
||
heading: 'Terms of access',
|
||
},
|
||
{
|
||
path: '/shipping-payment',
|
||
heading: 'Digital delivery only',
|
||
},
|
||
{
|
||
path: '/register',
|
||
heading: 'Create a HyperTwist account',
|
||
cta: 'Create account',
|
||
},
|
||
] as const
|
||
|
||
async function assertResponsivePublicRoute(page: Page, route: PublicRouteExpectation) {
|
||
await gotoAppRoute(page, route.path, { waitUntil: 'domcontentloaded', timeoutMs: 45_000 })
|
||
|
||
await expect(page.getByRole('heading', { level: 1, name: route.heading })).toBeVisible()
|
||
if (route.cta) {
|
||
const button = page.getByRole('button', { name: route.cta })
|
||
if ((await button.count()) > 0) {
|
||
await expect(button.first()).toBeVisible()
|
||
} else {
|
||
await expect(page.getByRole('link', { name: route.cta }).first()).toBeVisible()
|
||
}
|
||
}
|
||
|
||
if (route.path === '/login' || route.path === '/register') {
|
||
await expectAuthShellAboveBackdrop(page)
|
||
}
|
||
|
||
await expectNoHorizontalOverflow(page)
|
||
}
|
||
|
||
async function expectAuthShellAboveBackdrop(page: Page) {
|
||
await expect(page.locator('#hypertwist-auth-shell-backdrop')).toHaveCount(1)
|
||
|
||
const stacking = await page.evaluate(() => {
|
||
const backdrop = document.getElementById('hypertwist-auth-shell-backdrop')
|
||
const shell = document.querySelector('.auth-shell')
|
||
if (!(backdrop instanceof HTMLElement) || !(shell instanceof HTMLElement)) {
|
||
return null
|
||
}
|
||
|
||
const backdropStyle = window.getComputedStyle(backdrop)
|
||
const shellStyle = window.getComputedStyle(shell)
|
||
|
||
return {
|
||
backdropPointerEvents: backdropStyle.pointerEvents,
|
||
backdropZIndex: backdropStyle.zIndex,
|
||
shellIsolation: shellStyle.isolation,
|
||
shellPosition: shellStyle.position,
|
||
shellZIndex: shellStyle.zIndex,
|
||
}
|
||
})
|
||
|
||
expect(stacking).toEqual({
|
||
backdropPointerEvents: 'none',
|
||
backdropZIndex: '0',
|
||
shellIsolation: 'isolate',
|
||
shellPosition: 'relative',
|
||
shellZIndex: '1',
|
||
})
|
||
}
|
||
|
||
test.describe('responsive public routes (mobile)', () => {
|
||
test.use({ viewport: { width: 390, height: 844 } })
|
||
|
||
for (const route of responsivePublicRoutes) {
|
||
test(`${route.path} renders without horizontal overflow`, async ({ page }) => {
|
||
await assertResponsivePublicRoute(page, route)
|
||
})
|
||
}
|
||
})
|
||
|
||
test.describe('responsive public routes (tablet)', () => {
|
||
test.use({ viewport: { width: 820, height: 1180 } })
|
||
|
||
for (const route of responsivePublicRoutes) {
|
||
test(`${route.path} renders without horizontal overflow`, async ({ page }) => {
|
||
await assertResponsivePublicRoute(page, route)
|
||
})
|
||
}
|
||
})
|