Fix silent rendering crash, no-chrome test mode, error detection

- Fix this.this.selColor() typo in renderer (6 occurrences) — broke
  selection/handles rendering since the constants extraction refactor
- Add preserveDrawingBuffer to WebGL surface for reliable screenshots
- Add ?no-chrome URL param to hide panels/toolbar in tests
- CanvasHelper collects pageerror + console.error, assertNoErrors()
  fails tests on any browser-side exception
This commit is contained in:
Danila Poyarkov 2026-02-28 08:39:19 +03:00
parent bcd8bf6bca
commit e37d2b216a
11 changed files with 26 additions and 11 deletions

View file

@ -18,6 +18,7 @@ useEventListener(document, 'wheel', (e: WheelEvent) => {
}, { passive: false })
const params = useUrlSearchParams('history')
const showChrome = !('no-chrome' in params)
if (!('test' in params)) {
createDemoShapes(store)
}
@ -26,12 +27,12 @@ if (!('test' in params)) {
<template>
<div class="flex h-screen w-screen flex-col">
<div class="flex flex-1 overflow-hidden">
<LayersPanel />
<LayersPanel v-if="showChrome" />
<div class="relative flex min-w-0 flex-1">
<EditorCanvas />
<Toolbar />
<Toolbar v-if="showChrome" />
</div>
<PropertiesPanel />
<PropertiesPanel v-if="showChrome" />
</div>
</div>
</template>

View file

@ -35,7 +35,7 @@ export function useCanvas(canvasRef: Ref<HTMLCanvasElement | null>, store: Edito
canvas.width = w * dpr
canvas.height = h * dpr
const surface = ck.MakeWebGLCanvasSurface(canvas)
const surface = ck.MakeWebGLCanvasSurface(canvas, undefined, { preserveDrawingBuffer: 1 })
if (!surface) {
console.error('Failed to create WebGL surface')
return

View file

@ -230,7 +230,7 @@ export class SkiaRenderer {
const rotLinePaint = new this.ck.Paint()
rotLinePaint.setStyle(this.ck.PaintStyle.Stroke)
rotLinePaint.setStrokeWidth(1)
rotLinePaint.setColor(this.this.selColor())
rotLinePaint.setColor(this.selColor())
rotLinePaint.setAntiAlias(true)
canvas.drawLine(mx, y1, mx, rotHandleY, rotLinePaint)
@ -285,7 +285,7 @@ export class SkiaRenderer {
if (node.type === 'FRAME' && node.parentId === graph.rootId) {
const labelPaint = new this.ck.Paint()
labelPaint.setStyle(this.ck.PaintStyle.Fill)
labelPaint.setColor(this.this.selColor())
labelPaint.setColor(this.selColor())
labelPaint.setAntiAlias(true)
canvas.drawText(node.name, sx1, sy1 - 8, labelPaint, this.labelFont)
labelPaint.delete()
@ -307,7 +307,7 @@ export class SkiaRenderer {
const pillPaint = new this.ck.Paint()
pillPaint.setStyle(this.ck.PaintStyle.Fill)
pillPaint.setColor(this.this.selColor())
pillPaint.setColor(this.selColor())
pillPaint.setAntiAlias(true)
const rrect = this.ck.RRectXY(this.ck.LTRBRect(pillX, pillY, pillX + pillW, pillY + pillH), 4, 4)
@ -512,7 +512,7 @@ export class SkiaRenderer {
const paint = new this.ck.Paint()
paint.setStyle(this.ck.PaintStyle.Stroke)
paint.setStrokeWidth(2)
paint.setColor(this.this.selColor())
paint.setColor(this.selColor())
paint.setAntiAlias(true)
if (indicator.direction === 'HORIZONTAL') {
@ -752,7 +752,7 @@ export class SkiaRenderer {
const pathPaint = new this.ck.Paint()
pathPaint.setStyle(this.ck.PaintStyle.Stroke)
pathPaint.setStrokeWidth(2)
pathPaint.setColor(this.this.selColor())
pathPaint.setColor(this.selColor())
pathPaint.setAntiAlias(true)
const handlePaint = new this.ck.Paint()
@ -769,7 +769,7 @@ export class SkiaRenderer {
const vertexStroke = new this.ck.Paint()
vertexStroke.setStyle(this.ck.PaintStyle.Stroke)
vertexStroke.setStrokeWidth(2)
vertexStroke.setColor(this.this.selColor())
vertexStroke.setColor(this.selColor())
vertexStroke.setAntiAlias(true)
const toScreen = (x: number, y: number) => ({

View file

@ -9,7 +9,7 @@ test.describe.configure({ mode: 'serial' })
test.beforeAll(async ({ browser }) => {
page = await browser.newPage()
await page.goto('/?test')
await page.goto('/?test&no-chrome')
canvas = new CanvasHelper(page)
await canvas.waitForInit()
})
@ -23,6 +23,7 @@ test.beforeEach(async () => {
})
async function expectCanvas(name: string) {
canvas.assertNoErrors()
const buffer = await canvas.canvas.screenshot()
expect(buffer).toMatchSnapshot(`${name}.png`)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -3,10 +3,23 @@ import type { Page, Locator } from '@playwright/test'
export class CanvasHelper {
readonly page: Page
readonly canvas: Locator
readonly errors: string[] = []
constructor(page: Page) {
this.page = page
this.canvas = page.locator('canvas')
page.on('pageerror', (err) => this.errors.push(err.message))
page.on('console', (msg) => {
if (msg.type() === 'error') this.errors.push(msg.text())
})
}
assertNoErrors() {
if (this.errors.length > 0) {
const messages = this.errors.join('\n')
this.errors.length = 0
throw new Error(`Browser errors:\n${messages}`)
}
}
async waitForRender() {