mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
The dark block's ground is the design system's --card (#0f0f0f); the tokens stay the theme's. The gate reads the new ground. Claude-Session: https://claude.ai/code/session_01SCPMGSQsLNF2YtHFL4bwyp
59 lines
2.8 KiB
TypeScript
59 lines
2.8 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { readdirSync, readFileSync } from 'node:fs'
|
|
import { join } from 'node:path'
|
|
import { OUT, serveExport } from './export'
|
|
|
|
/**
|
|
* Code reads in the code theme, and the highlighter stays at build.
|
|
*
|
|
* components/code runs shiki in the export and ships coloured spans: @hanzo/ui's
|
|
* `codeTheme`, Dracula on the dark theme and GitHub Light on the light one, each
|
|
* colour written as `light-dark()` so the page's colour scheme picks it. Three
|
|
* kinds of page carry a sample — a connector, a product, a model — and each is
|
|
* read in both themes: the block's ground is neutral (the design system's card
|
|
* on dark, white on light — never Dracula's blue-violet), and a sample of real
|
|
* code is more than three colours.
|
|
*
|
|
* The second test is the other half of the promise: no shiki in the browser.
|
|
* Its grammars and engine are 49 KB gzipped at the least, measured, on pages
|
|
* that are under a JS budget.
|
|
*/
|
|
const ROUTES = ['/integrations/python', '/kms', '/enso']
|
|
const GROUND = { dark: 'rgb(15, 15, 15)', light: 'rgb(255, 255, 255)' } as const
|
|
|
|
for (const theme of ['dark', 'light'] as const)
|
|
for (const route of ROUTES)
|
|
test(`${route} sets its code in the ${theme} code theme`, async ({ page }) => {
|
|
const site = await serveExport()
|
|
try {
|
|
await page.addInitScript((t) => {
|
|
try {
|
|
localStorage.setItem('hanzo.appearance', JSON.stringify({ theme: t }))
|
|
} catch {}
|
|
}, theme)
|
|
await page.goto(site.url + route)
|
|
await expect.poll(() => page.evaluate(() => document.documentElement.classList.contains('light'))).toBe(theme === 'light')
|
|
const blocks = await page.evaluate(() =>
|
|
[...document.querySelectorAll('pre')]
|
|
.filter((pre) => (pre.getAttribute('style') ?? '').includes('light-dark('))
|
|
.map((pre) => ({
|
|
ground: getComputedStyle(pre).backgroundColor,
|
|
colours: new Set([...pre.querySelectorAll('span span')].map((s) => getComputedStyle(s).color)).size,
|
|
})),
|
|
)
|
|
expect(blocks.length, 'a highlighted block on the page').toBeGreaterThan(0)
|
|
for (const b of blocks) expect(b.ground).toBe(GROUND[theme])
|
|
expect(Math.max(...blocks.map((b) => b.colours)), 'token colours in the richest block').toBeGreaterThan(3)
|
|
} finally {
|
|
await site.close()
|
|
}
|
|
})
|
|
|
|
test('no highlighter ships to the browser', () => {
|
|
const chunks = join(OUT, '_next', 'static', 'chunks')
|
|
const walk = (dir: string): string[] =>
|
|
readdirSync(dir, { withFileTypes: true }).flatMap((e) => (e.isDirectory() ? walk(join(dir, e.name)) : [join(dir, e.name)]))
|
|
const shipped = walk(chunks).filter((f) => f.endsWith('.js') && /createHighlighterCore|oniguruma|shikijs/.test(readFileSync(f, 'utf8')))
|
|
expect(shipped, 'client chunks carrying shiki').toEqual([])
|
|
})
|