velxio/frontend/src/__tests__/examples-netlist-snapshot.t...

79 lines
2.9 KiB
TypeScript

/**
* Netlist snapshot tests (Phase 1d-tests A).
*
* For every example across ALL `examples-*.ts` modules, build the
* SPICE netlist via the production pipeline and snapshot the FULL
* netlist string. If a refactor changes how `componentToSpice`,
* `NetlistBuilder`, or `storeAdapter` emit cards, the diff lands in
* `__snapshots__/examples-netlist-snapshot.test.ts.snap` and a
* reviewer sees exactly what changed across every example.
*
* To intentionally update the snapshots after a legitimate model
* change:
*
* npx vitest run -u src/__tests__/examples-netlist-snapshot.test.ts
*
* The PR diff of the snapshot file becomes the evidence of which
* circuits are affected.
*
* Test fidelity rule (memory `feedback_tests_import_real_code`):
* imports the example arrays directly from the source modules and
* uses the same `exampleToBuildNetlistInput` that production
* `loadExample.ts` uses. Nothing is duplicated.
*/
import { describe, it, expect } from 'vitest';
import { analogExamples } from '../data/examples-analog';
import { digitalExamples } from '../data/examples-digital';
import { hundredDaysExamples } from '../data/examples-100-days';
import { epaperExamples } from '../data/examples-displays-epaper';
import { circuitExamples } from '../data/examples-circuits';
import { exampleToBuildNetlistInput } from '../utils/exampleToBuildNetlistInput';
import { buildNetlist } from '../simulation/spice/NetlistBuilder';
import type { ExampleProject } from '../data/examples';
/**
* Normalise the netlist before snapshotting:
* - strip the leading `* Velxio circuit @ <ISO timestamp>` comment
* (timestamp would flake every run)
*
* Everything else is verbatim — model lines, source ordering, etc.
*/
function snapshotNetlist(netlist: string): string {
return netlist.replace(/^\* Velxio circuit @[^\n]*\n/, '* Velxio circuit\n');
}
function snapshotExample(example: ExampleProject): string {
// Some examples have no electrical components (firmware-only). The
// builder still produces a minimal netlist with the analysis card +
// `.end`; snapshot that for completeness.
const input = exampleToBuildNetlistInput(example);
const { netlist } = buildNetlist(input);
return snapshotNetlist(netlist);
}
interface Bucket {
name: string;
examples: ExampleProject[];
}
const BUCKETS: Bucket[] = [
{ name: 'analog', examples: analogExamples },
{ name: 'digital', examples: digitalExamples },
{ name: '100-days', examples: hundredDaysExamples },
{ name: 'epaper', examples: epaperExamples },
{ name: 'circuits', examples: circuitExamples },
];
for (const bucket of BUCKETS) {
describe(`netlist snapshot — ${bucket.name} (${bucket.examples.length} examples)`, () => {
it.each(bucket.examples.map((ex) => [ex.id, ex] as const))(
'%s',
{ timeout: 5_000 },
(_id, example) => {
const netlist = snapshotExample(example);
expect(netlist).toMatchSnapshot();
},
);
});
}