/** * Integration tests for the property runner. * * Mocks the renderer adapter and scene target so these tests exercise * the runner orchestration without needing a real browser. * * Pattern: Arrange-Act-Assert with Node.js built-in test runner. */ import { describe, it } from 'node:test' import assert from 'node:assert' import type { SceneTarget, RenderCase } from 'imhotep-core/scene-target' import type { InputDomain } from 'imhotep-core/property-contracts' import type { GeometryWorld } from 'imhotep-core/world' import type { MountedScene, RendererAdapter, PropertyPredicate, PropertyRunnerDeps } from './property-runner.js' import { runProperty, runEnumeratedProperty, runSampledProperty } from './property-runner.js' import type { FastCheckAdapter } from './fast-check-adapter.js' // --------------------------------------------------------------------------- // Mock Builder // --------------------------------------------------------------------------- function createMockRendererAdapter( opts: { failOnCaseId?: string } = {}, ): RendererAdapter { const mountedScenes: MountedScene[] = [] return { async mount(_sceneTarget: SceneTarget, renderCase: RenderCase): Promise { if (opts.failOnCaseId && renderCase.caseId === opts.failOnCaseId) { throw new Error(`Simulated mount failure for ${renderCase.caseId}`) } const scene: MountedScene = { caseId: renderCase.caseId, world: createMockWorld(renderCase.caseId), } mountedScenes.push(scene) return scene }, async unmount(mounted: MountedScene): Promise { const idx = mountedScenes.findIndex((s) => s.caseId === mounted.caseId) if (idx >= 0) mountedScenes.splice(idx, 1) }, } } function createMockWorld(sceneId: string): GeometryWorld { return { sceneId, snapshotId: 'snap-1', env: { viewportWidth: 1280, viewportHeight: 720, deviceScaleFactor: 1, colorScheme: 'light', pointer: 'fine', hover: false, reducedMotion: 'no-preference', locale: 'en', writingMode: 'horizontal-tb', }, source: { url: 'http://localhost/test', browserName: 'chromium', browserVersion: '120', engine: 'chromium-cdp', }, strings: [], subjects: [], dom: { nodes: [], nodeCount: 0 }, frames: [], matrices: [], rects: [], boxes: [], fragments: [], transforms: [], styles: [], text: [], topology: { ancestors: [], descendants: [], stacks: [] }, scroll: [], clipping: [], paint: [], visibility: [], provenance: [], confidence: [], } as unknown as GeometryWorld } function createMockFastCheckAdapter(): FastCheckAdapter { return { assert: async (prop: unknown, params?: Record) => { const p = prop as { predicate: (value: unknown) => boolean | Promise; arbitrary: unknown } const numRuns = (params?.numRuns as number) ?? 100 const seed = (params?.seed as number) ?? 42 // Deterministic pseudo-random from seed for replayability let rngState = seed const next = () => { rngState = (rngState * 16807 + 0) % 2147483647 return rngState / 2147483647 } for (let i = 0; i < numRuns; i++) { const value = generateValue(p.arbitrary, next) const passed = await p.predicate(value) if (!passed) { const err = new Error(`Property failed at run ${i}`) ;(err as unknown as Record).counterexample = value throw err } } }, record: (recordModel: Record) => recordModel, constantFrom: (...values: unknown[]) => values, sample: (arb: unknown, params?: { seed?: number; numRuns?: number }) => { const numRuns = params?.numRuns ?? 10 const seed = params?.seed ?? 42 let rngState = seed const next = () => { rngState = (rngState * 16807 + 0) % 2147483647 return rngState / 2147483647 } const out: unknown[] = [] for (let i = 0; i < numRuns; i++) { out.push(generateValue(arb, next)) } return out }, property: (arb: unknown, predicate: (value: unknown) => boolean | Promise) => { return { arbitrary: arb, predicate } }, } } function generateValue(arb: unknown, next: () => number): unknown { if (Array.isArray(arb)) { const idx = Math.floor(next() * arb.length) return arb[idx] } if (arb && typeof arb === 'object') { const obj: Record = {} for (const [key, val] of Object.entries(arb)) { obj[key] = generateValue(val, next) } return obj } return arb } function createConcurrencyTrackingAdapter(delayMs = 50) { let currentConcurrent = 0 let maxConcurrent = 0 const adapter: RendererAdapter = { async mount(_sceneTarget: SceneTarget, renderCase: RenderCase): Promise { currentConcurrent++ maxConcurrent = Math.max(maxConcurrent, currentConcurrent) await new Promise((r) => setTimeout(r, delayMs)) return { caseId: renderCase.caseId, world: createMockWorld(renderCase.caseId), } }, async unmount(): Promise { currentConcurrent-- }, } return { adapter, getMaxConcurrent: () => maxConcurrent, } } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- const sceneTarget: SceneTarget = { kind: 'fixture', fixtureId: 'button-fixture' } describe('property runner — enumerated mode', () => { it('evaluates every enumerated case deterministically', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = ['sm', 'md', 'lg'] const domain: InputDomain = { mode: 'enumerated', values } const evaluated: string[] = [] const predicate: PropertyPredicate = async (scene) => { evaluated.push((scene as MountedScene).caseId) return true } const result = await runEnumeratedProperty(sceneTarget, domain, predicate, deps) assert.strictEqual(result.mode, 'enumerated-determinate') assert.strictEqual(result.totalCases, 3) assert.strictEqual(result.passed, true) assert.strictEqual(evaluated.length, 3) assert.ok(evaluated.includes('enum-0')) assert.ok(evaluated.includes('enum-1')) assert.ok(evaluated.includes('enum-2')) }) it('stops at first failing case and reports it', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = ['sm', 'md', 'lg'] const domain: InputDomain = { mode: 'enumerated', values } const predicate: PropertyPredicate = async (scene) => { return (scene as MountedScene).caseId !== 'enum-1' } const result = await runEnumeratedProperty(sceneTarget, domain, predicate, deps) assert.strictEqual(result.mode, 'enumerated-determinate') assert.strictEqual(result.passed, false) assert.strictEqual(result.failingCase, 'md') assert.strictEqual(result.caseIndex, 1) }) it('enumerated runs all cases concurrently', async () => { const { adapter } = createConcurrencyTrackingAdapter(50) const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = [1, 2, 3, 4, 5] const domain: InputDomain = { mode: 'enumerated', values } const start = performance.now() const result = await runEnumeratedProperty(sceneTarget, domain, async () => true, deps, { concurrency: 5, }) const elapsed = performance.now() - start assert.strictEqual(result.passed, true) assert.strictEqual(result.totalCases, 5) assert.ok(elapsed < 200, `Expected concurrent run to be fast, but took ${elapsed}ms`) }) it('enumerated stops on first failure with failFast', async () => { const { adapter } = createConcurrencyTrackingAdapter(10) const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = [1, 2, 3, 4, 5] const domain: InputDomain = { mode: 'enumerated', values } const evaluated: number[] = [] const predicate: PropertyPredicate = async (scene) => { const idx = parseInt((scene as MountedScene).caseId.replace('enum-', '')) evaluated.push(idx) return idx !== 2 } const result = await runEnumeratedProperty(sceneTarget, domain, predicate, deps, { concurrency: 1, failFast: true, }) assert.strictEqual(result.passed, false) assert.strictEqual(result.caseIndex, 2) assert.ok(evaluated.includes(0)) assert.ok(evaluated.includes(1)) assert.ok(evaluated.includes(2)) assert.ok(!evaluated.includes(3)) assert.ok(!evaluated.includes(4)) }) it('enumerated runs all cases despite failure without failFast', async () => { const { adapter } = createConcurrencyTrackingAdapter(10) const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = [1, 2, 3, 4, 5] const domain: InputDomain = { mode: 'enumerated', values } const evaluated: number[] = [] const predicate: PropertyPredicate = async (scene) => { const idx = parseInt((scene as MountedScene).caseId.replace('enum-', '')) evaluated.push(idx) return idx !== 2 } const result = await runEnumeratedProperty(sceneTarget, domain, predicate, deps, { concurrency: 1, failFast: false, }) assert.strictEqual(result.passed, false) assert.strictEqual(result.caseIndex, 2) assert.strictEqual(evaluated.length, 5) assert.ok(evaluated.includes(0)) assert.ok(evaluated.includes(1)) assert.ok(evaluated.includes(2)) assert.ok(evaluated.includes(3)) assert.ok(evaluated.includes(4)) }) it('enumerated respects concurrency limit', async () => { const { adapter, getMaxConcurrent } = createConcurrencyTrackingAdapter(50) const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = [1, 2, 3, 4, 5] const domain: InputDomain = { mode: 'enumerated', values } const result = await runEnumeratedProperty(sceneTarget, domain, async () => true, deps, { concurrency: 2, }) assert.strictEqual(result.passed, true) assert.ok(getMaxConcurrent() <= 2, `Expected max concurrent <= 2, but got ${getMaxConcurrent()}`) assert.ok(getMaxConcurrent() > 1, `Expected some concurrency, but got ${getMaxConcurrent()}`) }) }) describe('property runner — sampled mode', () => { it('runs sampled cases with fast-check and reports seed', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('sm', 'md', 'lg'), seed: 42, numRuns: 20, } let runCount = 0 const predicate: PropertyPredicate = async () => { runCount++ return true } const result = await runSampledProperty(sceneTarget, domain, predicate, deps, { seed: 42, numRuns: 20 }) assert.strictEqual(result.mode, 'sampled') assert.strictEqual(result.seed, 42) assert.strictEqual(result.numRuns, 20) assert.strictEqual(result.passed, true) assert.strictEqual(runCount, 20) }) it('captures counterexample on failure', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('sm', 'md', 'lg'), seed: 42, numRuns: 20, } let callCount = 0 const predicate: PropertyPredicate = async () => { callCount++ return callCount < 5 } const result = await runSampledProperty(sceneTarget, domain, predicate, deps, { seed: 42, numRuns: 20 }) assert.strictEqual(result.mode, 'sampled') assert.strictEqual(result.passed, false) assert.ok(result.counterexample !== undefined) assert.ok(result.diagnostics.length > 0) }) it('reproduces identical run with same seed', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('sm', 'md', 'lg'), seed: 123, numRuns: 10, } const run1Inputs: unknown[] = [] const run2Inputs: unknown[] = [] const predicate1: PropertyPredicate = async (_scene, ctx) => { run1Inputs.push(ctx.renderCase.input) return true } const predicate2: PropertyPredicate = async (_scene, ctx) => { run2Inputs.push(ctx.renderCase.input) return true } await runSampledProperty(sceneTarget, domain, predicate1, deps, { seed: 123, numRuns: 10 }) await runSampledProperty(sceneTarget, domain, predicate2, deps, { seed: 123, numRuns: 10 }) assert.deepStrictEqual(run1Inputs, run2Inputs) }) it('sampled runs iterations concurrently', async () => { const { adapter } = createConcurrencyTrackingAdapter(50) const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('a', 'b', 'c'), seed: 42, numRuns: 10, } const start = performance.now() const result = await runSampledProperty(sceneTarget, domain, async () => true, deps, { seed: 42, numRuns: 10, concurrency: 10, }) const elapsed = performance.now() - start assert.strictEqual(result.passed, true) assert.strictEqual(result.numRuns, 10) assert.ok(elapsed < 300, `Expected concurrent sampled run to be fast, but took ${elapsed}ms`) }) it('sampled respects concurrency limit', async () => { const { adapter, getMaxConcurrent } = createConcurrencyTrackingAdapter(50) const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('a', 'b', 'c'), seed: 42, numRuns: 10, } const result = await runSampledProperty(sceneTarget, domain, async () => true, deps, { seed: 42, numRuns: 10, concurrency: 2, }) assert.strictEqual(result.passed, true) assert.ok(getMaxConcurrent() <= 2, `Expected max concurrent <= 2, but got ${getMaxConcurrent()}`) assert.ok(getMaxConcurrent() > 1, `Expected some concurrency, but got ${getMaxConcurrent()}`) }) it('sampled preserves seed and shrinking', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('sm', 'md', 'lg'), seed: 42, numRuns: 20, } let callCount = 0 const predicate: PropertyPredicate = async () => { callCount++ return callCount < 5 } const result1 = await runSampledProperty(sceneTarget, domain, predicate, deps, { seed: 42, numRuns: 20, }) callCount = 0 const result2 = await runSampledProperty(sceneTarget, domain, predicate, deps, { seed: 42, numRuns: 20, }) assert.strictEqual(result1.passed, false) assert.strictEqual(result2.passed, false) assert.deepStrictEqual(result1.counterexample, result2.counterexample) assert.deepStrictEqual(result1.minimalFailingCase, result2.minimalFailingCase) }) }) describe('property runner — error handling', () => { it('error in one case does not crash runner', async () => { const evaluated: number[] = [] const adapter: RendererAdapter = { async mount(_sceneTarget, renderCase) { if (renderCase.caseId === 'enum-2') { throw new Error('Mount failed') } return { caseId: renderCase.caseId, world: createMockWorld(renderCase.caseId), } }, async unmount() {}, } const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = [1, 2, 3, 4, 5] const domain: InputDomain = { mode: 'enumerated', values } const predicate: PropertyPredicate = async (scene) => { const idx = parseInt((scene as MountedScene).caseId.replace('enum-', '')) evaluated.push(idx) return true } const result = await runEnumeratedProperty(sceneTarget, domain, predicate, deps, { failFast: false, }) assert.strictEqual(result.passed, false) assert.strictEqual(result.caseIndex, 2) assert.ok(evaluated.includes(0)) assert.ok(evaluated.includes(1)) assert.ok(evaluated.includes(3)) assert.ok(evaluated.includes(4)) }) }) describe('property runner — mount/unmount', () => { it('mount/unmount called for every case', async () => { let mountCount = 0 let unmountCount = 0 const adapter: RendererAdapter = { async mount(_sceneTarget, renderCase) { mountCount++ return { caseId: renderCase.caseId, world: createMockWorld(renderCase.caseId), } }, async unmount() { unmountCount++ }, } const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = ['a', 'b', 'c', 'd', 'e'] const domain: InputDomain = { mode: 'enumerated', values } const result = await runEnumeratedProperty(sceneTarget, domain, async () => true, deps) assert.strictEqual(result.passed, true) assert.strictEqual(mountCount, 5) assert.strictEqual(unmountCount, 5) }) }) describe('property runner — dispatch', () => { it('dispatches to enumerated runner for enumerated domain', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'enumerated', values: [1, 2, 3] } const result = await runProperty(sceneTarget, domain, async () => true, deps) assert.strictEqual(result.mode, 'enumerated-determinate') }) it('dispatches to sampled runner for generated domain', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('a', 'b'), seed: 1, numRuns: 5, } const result = await runProperty(sceneTarget, domain, async () => true, deps, { seed: 1, numRuns: 5 }) assert.strictEqual(result.mode, 'sampled') }) it('throws for unsupported domain mode', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain = { mode: 'unknown' } as unknown as InputDomain await assert.rejects( async () => runProperty(sceneTarget, domain, async () => true, deps), /Unsupported InputDomain mode/, ) }) }) describe('property runner — report mode', () => { it('compact enumerated result strips verbose fields', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = ['sm', 'md', 'lg'] const domain: InputDomain = { mode: 'enumerated', values } const predicate: PropertyPredicate = async (scene) => { return (scene as MountedScene).caseId !== 'enum-1' } const result = await runEnumeratedProperty(sceneTarget, domain, predicate, deps, { reportMode: 'compact', }) assert.strictEqual(result.mode, 'enumerated-determinate') assert.strictEqual(result.passed, false) assert.strictEqual(result.caseIndex, 1) assert.strictEqual(result.minimalFailingCase, 'md') assert.ok(result.durationMs !== undefined) assert.ok(result.replayPayload !== undefined) assert.deepStrictEqual(result.replayPayload!, { props: 'md', seed: -1, caseIndex: 1, }) assert.strictEqual(result.diagnostics.length, 0) assert.strictEqual((result as any).failingScene, undefined) }) it('verbose enumerated result includes full payload', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const values = ['sm', 'md', 'lg'] const domain: InputDomain = { mode: 'enumerated', values } const predicate: PropertyPredicate = async (scene) => { return (scene as MountedScene).caseId !== 'enum-1' } const result = await runEnumeratedProperty(sceneTarget, domain, predicate, deps, { reportMode: 'verbose', }) assert.strictEqual(result.mode, 'enumerated-determinate') assert.strictEqual(result.passed, false) assert.strictEqual(result.caseIndex, 1) assert.strictEqual(result.minimalFailingCase, 'md') assert.ok(result.durationMs !== undefined) assert.ok(Array.isArray(result.diagnostics)) assert.ok(result.replayPayload !== undefined) assert.deepStrictEqual(result.replayPayload!, { props: 'md', seed: -1, caseIndex: 1, }) }) it('compact sampled result strips verbose fields', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('sm', 'md', 'lg'), seed: 42, numRuns: 20, } let callCount = 0 const predicate: PropertyPredicate = async () => { callCount++ return callCount < 5 } const result = await runSampledProperty(sceneTarget, domain, predicate, deps, { seed: 42, numRuns: 20, reportMode: 'compact', }) assert.strictEqual(result.mode, 'sampled') assert.strictEqual(result.passed, false) assert.strictEqual(result.caseIndex, 4) assert.ok(result.minimalFailingCase !== undefined) assert.ok(result.durationMs !== undefined) assert.ok(result.replayPayload !== undefined) assert.strictEqual(result.replayPayload!.seed, 42) assert.strictEqual(result.replayPayload!.caseIndex, 4) assert.ok(result.replayPayload!.props !== undefined) assert.strictEqual(result.diagnostics.length, 0) assert.strictEqual((result as any).failingScene, undefined) }) it('verbose sampled result includes full payload', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('sm', 'md', 'lg'), seed: 42, numRuns: 20, } let callCount = 0 const predicate: PropertyPredicate = async () => { callCount++ return callCount < 5 } const result = await runSampledProperty(sceneTarget, domain, predicate, deps, { seed: 42, numRuns: 20, reportMode: 'verbose', }) assert.strictEqual(result.mode, 'sampled') assert.strictEqual(result.passed, false) assert.strictEqual(result.caseIndex, 4) assert.ok(result.minimalFailingCase !== undefined) assert.ok(result.durationMs !== undefined) assert.ok(Array.isArray(result.diagnostics)) assert.ok(result.replayPayload !== undefined) assert.strictEqual(result.replayPayload!.seed, 42) assert.strictEqual(result.replayPayload!.caseIndex, 4) assert.ok(result.replayPayload!.props !== undefined) }) it('reproduces identical run from sampled replay payload', async () => { const adapter = createMockRendererAdapter() const fc = createMockFastCheckAdapter() const deps: PropertyRunnerDeps = { rendererAdapter: adapter, worldExtractor: { extract: async (m) => m.world }, fc, } const domain: InputDomain = { mode: 'generated', arbitrary: fc.constantFrom('sm', 'md', 'lg'), seed: 123, numRuns: 10, } let callCount = 0 const collectedInputs: unknown[] = [] const predicate: PropertyPredicate = async (_scene, ctx) => { collectedInputs.push(ctx.renderCase.input) callCount++ return callCount < 7 } const result = await runSampledProperty(sceneTarget, domain, predicate, deps, { seed: 123, numRuns: 10, reportMode: 'verbose', }) assert.strictEqual(result.passed, false) assert.ok(result.replayPayload !== undefined) const replayInputs: unknown[] = [] let replayCount = 0 const replayPayload = result.replayPayload! const replayPredicate: PropertyPredicate = async (_scene, ctx) => { replayInputs.push(ctx.renderCase.input) replayCount++ return replayCount < replayPayload.caseIndex + 1 } await runSampledProperty(sceneTarget, domain, replayPredicate, deps, { seed: replayPayload.seed, numRuns: replayPayload.caseIndex + 1, reportMode: 'verbose', }) assert.deepStrictEqual( collectedInputs[replayPayload.caseIndex], replayInputs[replayPayload.caseIndex], ) }) })