| 504 | * real update sequences without manual commit/layout plumbing. |
| 505 | */ |
| 506 | export function createTestRenderer(opts: TestRendererOptions = {}): TestRenderer { |
| 507 | let prevRoot: RuntimeInstance | null = null; |
| 508 | let allocator: InstanceIdAllocator = createInstanceIdAllocator(1); |
| 509 | const defaultViewport = normalizeViewport(opts.viewport); |
| 510 | const rendererTheme = normalizeTheme(opts.theme); |
| 511 | const defaultFocusedId = opts.focusedId ?? null; |
| 512 | const defaultTick = opts.tick ?? 0; |
| 513 | const defaultMode = opts.mode ?? "test"; |
| 514 | const trace = opts.trace; |
| 515 | const defaultTraceDetail = opts.traceDetail === true; |
| 516 | let warnedTraceDetailWithoutTrace = false; |
| 517 | let renderId = 0; |
| 518 | |
| 519 | const render = (vnode: VNode, renderOpts: TestRenderOptions = {}): TestRenderResult => { |
| 520 | const startedAt = Date.now(); |
| 521 | renderId += 1; |
| 522 | |
| 523 | const viewport = normalizeViewport(renderOpts.viewport ?? defaultViewport); |
| 524 | const focusedId = renderOpts.focusedId === undefined ? defaultFocusedId : renderOpts.focusedId; |
| 525 | const tick = renderOpts.tick ?? defaultTick; |
| 526 | const theme = normalizeTheme(renderOpts.theme ?? rendererTheme); |
| 527 | const traceDetail = renderOpts.traceDetail ?? defaultTraceDetail; |
| 528 | const mode = renderOpts.mode ?? defaultMode; |
| 529 | if (traceDetail && !trace && !warnedTraceDetailWithoutTrace) { |
| 530 | warnedTraceDetailWithoutTrace = true; |
| 531 | console.warn( |
| 532 | "[core/testing] createTestRenderer: traceDetail=true has no effect without a trace callback.", |
| 533 | ); |
| 534 | } |
| 535 | |
| 536 | const commitStartedAt = Date.now(); |
| 537 | const committed = commitVNodeTree(prevRoot, vnode, { allocator }); |
| 538 | const commitMs = Date.now() - commitStartedAt; |
| 539 | if (!committed.ok) { |
| 540 | throw new Error( |
| 541 | `createTestRenderer: commit failed: ${committed.fatal.code}: ${committed.fatal.detail}`, |
| 542 | ); |
| 543 | } |
| 544 | prevRoot = committed.value.root; |
| 545 | |
| 546 | const layoutStartedAt = Date.now(); |
| 547 | const layoutTree = layoutRootOrThrow(prevRoot, viewport); |
| 548 | const layoutMs = Date.now() - layoutStartedAt; |
| 549 | const builder = new RecordingDrawlistBuilder(); |
| 550 | |
| 551 | const drawStartedAt = Date.now(); |
| 552 | renderToDrawlist({ |
| 553 | tree: prevRoot, |
| 554 | layout: layoutTree, |
| 555 | viewport, |
| 556 | focusState: Object.freeze({ focusedId }), |
| 557 | builder, |
| 558 | theme, |
| 559 | tick, |
| 560 | }); |
| 561 | const drawMs = Date.now() - drawStartedAt; |
| 562 | |
| 563 | const ops = builder.snapshotOps(); |