TAU/ DOCS

Test Kernels

Test runtime plugins through the public client path and unit-test definitions with focused mocks.

Install @taucad/runtime-testing and Vitest as development dependencies. Compose the same kernel and bundler plugins your host uses.

Steps

1. Render through a real client

createTestRuntimeClient accepts a normal defineRuntime result and drives the production createRuntimeClient and inProcessTransport path with an isolated fromMemoryFs filesystem. Use public documents and views; the caller owns shutdown:

import { afterEach, describe, expect, it } from 'vitest';
import { esbuild } from '@taucad/esbuild';
import { replicad } from '@taucad/replicad';
import { assertRenderingSuccess, createTestRuntimeClient } from '@taucad/runtime-testing';
import { defineRuntime } from '@taucad/runtime/worker';

const runtime = defineRuntime({
  plugins: [replicad({ kernels: { default: { wasm: 'single' } } }), esbuild()],
});

describe('Replicad kernel', () => {
  const createClient = (files: Record<string, string>) => createTestRuntimeClient({ runtime, files });
  const clients = new Set<ReturnType<typeof createClient>>();

  afterEach(async () => {
    await Promise.all([...clients].map((client) => client.shutdown()));
    clients.clear();
  });

  it('renders and exports a box', async () => {
    const client = createTestRuntimeClient({
      runtime,
      files: {
        'main.ts': `
          import { makeBaseBox } from 'replicad';
          export default () => makeBaseBox(30, 50, 10);
        `,
      },
    });
    clients.add(client);

    const document = client.open({ source: { path: 'main.ts' } });
    const outcome = await document.view('model').rendering();
    expect(outcome.superseded).toBe(false);
    if (outcome.superseded) return;
    assertRenderingSuccess(outcome.rendering);

    const exported = await document.export('step');
    expect(exported.success).toBe(true);
    if (!exported.success) throw new Error(exported.issues.map((issue) => issue.message).join('\n'));
    expect(new TextDecoder().decode(exported.files[0].bytes)).toContain('ISO-10303-21');
  });
});

An export can evaluate its document before any view renders. Assert the output's structure, topology, or intended behavior; nonempty bytes alone do not prove a correct model. Observe document status and issues separately from client logging and telemetry.

2. Use convenience helpers for one-shot tests

createTestGeometry and getTestParameters create and close their own clients:

import { expect, it } from 'vitest';
import { esbuild } from '@taucad/esbuild';
import { replicad } from '@taucad/replicad';
import { assertRenderingSuccess, createTestGeometry, getTestParameters } from '@taucad/runtime-testing';
import { defineRuntime } from '@taucad/runtime/worker';

const runtime = defineRuntime({
  plugins: [replicad({ kernels: { default: { wasm: 'single' } } }), esbuild()],
});

const files = {
  'main.ts': `
    import { makeBaseBox } from 'replicad';
    export const defaultParams = { width: 10 };
    export default (params = defaultParams) => makeBaseBox(params.width, 20, 30);
  `,
};

it('exposes parameters and geometry', async () => {
  const parameters = await getTestParameters({ runtime, files, mainFile: 'main.ts' });
  expect(parameters.defaults).toEqual({ width: 10 });

  const result = await createTestGeometry({
    runtime,
    files,
    open: { source: { path: 'main.ts' }, parameters: { width: 25 } },
    view: (document) => document.view('model'),
  });
  assertRenderingSuccess(result);
});

3. Unit-test plugin definitions

resolveRuntimePluginDefinition from @taucad/runtime/plugin exposes the definition behind a public factory for focused hook tests. Assert operation inputs and outputs directly: evaluate must offer only declared view/export IDs; render must return content without changing the handle; export must return at least one file; and onDispose releases context resources. Use createMockKernelRuntime from @taucad/runtime-testing for kernel services, and pass a fresh operation signal to each call. Keep caching, watch, disposal ordering, and transport invariants in runtime-owned integration tests.

import { expect, it } from 'vitest';
import { resolveRuntimePluginDefinition } from '@taucad/runtime/plugin';
import { myKernel } from './examples/my-kernel.kernel';

it('declares the routes evaluated models can offer', async () => {
  const definition = await resolveRuntimePluginDefinition('kernel', myKernel());
  expect(Object.keys(definition.views)).toEqual(['drawing']);
  expect(Object.keys(definition.exports)).toEqual(['drawing']);
});

4. Assert geometry

Geometry helpers accept the public render result shape and import no runtime internals:

import { it } from 'vitest';
import { esbuild } from '@taucad/esbuild';
import { replicad } from '@taucad/replicad';
import { assertRenderingSuccess, createGeometryTestHelpers, createTestGeometry } from '@taucad/runtime-testing';
import { defineRuntime } from '@taucad/runtime/worker';

const runtime = defineRuntime({
  plugins: [replicad({ kernels: { default: { wasm: 'single' } } }), esbuild()],
});

it('checks the rendered mesh', async () => {
  const result = await createTestGeometry({
    runtime,
    files: {
      'main.ts': `
        import { makeBaseBox } from 'replicad';
        export default () => makeBaseBox(30, 50, 10);
      `,
    },
    open: { source: { path: 'main.ts' } },
    view: (document) => document.view('model'),
  });
  assertRenderingSuccess(result);

  const helpers = createGeometryTestHelpers();
  await helpers.expectValidGltf(result);
  await helpers.expectMeshCount(result, 1);
  await helpers.expectBoundingBoxSize(result, [0.03, 0.01, 0.05], 0.001);
});

On this page