Measure the budget end to end, and fail on any console error
The Playwright suite now checks PRD section 7 and the budget on the local build and on each preview and live URL. Frame times count the whole frame: Go reports its share after copying the pixels, and the page adds the canvas blit and keeps p50 and p95 over the last 240 frames. A test slows putImageData and expects p95 to show it. The budget test runs on a moving component page and logs what it measures: first frame, p50, p95 and the wasm's brotli bytes, as Cloudflare sent them or compressed locally. Today: first frame about 60 ms, p95 about 10 ms, wasm under 5 MB, against 300 ms, 16 ms and 8 MB. Every page now ends free of console and page errors, whether a test used open() or not; the two tests that fail on purpose say so. Clicks and typing reach components on their own pages.
This commit is contained in:
3 files changed
+89
-22
No files matched your search
+13
-1
@@ -12,6 +12,8 @@ const cursors = ['default', 'pointer', 'text', 'move', 'ew-resize', 'ns-resize',
|
||||
'row-resize', 'vertical-text', 'copy', 'alias', 'context-menu', 'none']
|
||||
const SHIFT = 1, CTRL = 2, ALT = 4, SUPER = 8
|
||||
const log = []
|
||||
// times are recent frames' ms: Go's share plus the blit.
|
||||
const times = []
|
||||
const asked = params.get('zoom')
|
||||
const fixed = /^[1-9]$/.test(asked) ? Number(asked) : 0
|
||||
let api, zoom, scale, timer, first, active = false, skipText = ''
|
||||
@@ -37,11 +39,19 @@ function size () {
|
||||
return [Math.max(1, Math.floor(innerWidth / scale)), Math.max(1, Math.floor(innerHeight / scale))]
|
||||
}
|
||||
|
||||
// percentiles are the median and 95th of the frame times.
|
||||
function percentiles (ms) {
|
||||
const s = [...ms].sort((a, b) => a - b)
|
||||
const at = p => s.length ? s[Math.floor(p * (s.length - 1))] : 0
|
||||
return { p50: at(0.5), p95: at(0.95) }
|
||||
}
|
||||
|
||||
function hashPath () { return decodeURIComponent(location.hash.slice(1)) }
|
||||
|
||||
// show blits a frame and acts on Go's requests.
|
||||
function show (r) {
|
||||
if (!r) return
|
||||
const t = performance.now()
|
||||
if (screen.width !== r.w || screen.height !== r.h) {
|
||||
screen.width = r.w
|
||||
screen.height = r.h
|
||||
@@ -49,6 +59,8 @@ function show (r) {
|
||||
screen.style.width = r.w * scale + 'px'
|
||||
screen.style.height = r.h * scale + 'px'
|
||||
ctx.putImageData(new ImageData(r.pix, r.w, r.h), 0, 0)
|
||||
times.push(r.took + performance.now() - t)
|
||||
if (times.length > 240) times.shift()
|
||||
if (r.path && '#' + r.path !== location.hash && !(r.path === '/' && location.hash === '')) {
|
||||
history.pushState(null, '', '#' + r.path)
|
||||
}
|
||||
@@ -219,7 +231,7 @@ async function load () {
|
||||
ime.focus({ preventScroll: true })
|
||||
window.__8bit = {
|
||||
ready: true,
|
||||
stats: () => ({ ...api.stats(), zoom, first }),
|
||||
stats: () => ({ ...api.stats(), ...percentiles(times), zoom, first }),
|
||||
texts: () => api.texts(),
|
||||
page: () => api.stats().path,
|
||||
find: (label) => api.find(label)?.map(v => v * scale),
|
||||
|
||||
Reference in new issue
Block a user