@typecad/expect 0.1.0-alpha.2 → 1.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/host/config.js +1 -0
- package/dist/host/preprocessor.d.ts +26 -3
- package/dist/host/preprocessor.js +26 -8
- package/dist/host/protocol-emitter.js +10 -10
- package/dist/host/runner.js +2 -1
- package/dist/host/types.d.ts +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ Hardware test runner for [TypeCAD](../../README.md). Write vitest-style assertio
|
|
|
62
62
|
npm install --save-dev @typecad/expect @typecad/cuttlefish
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
`@typecad/expect` ships the `cuttlefish-test` CLI. It pairs with [`@typecad/cuttlefish`](https://
|
|
65
|
+
`@typecad/expect` ships the `cuttlefish-test` CLI. It pairs with [`@typecad/cuttlefish`](https://cuttlefish.typecad.net), which transpiles your TypeScript test files to C++ for upload to hardware.
|
|
66
66
|
|
|
67
67
|
**Prerequisites:**
|
|
68
68
|
|
package/dist/host/config.js
CHANGED
|
@@ -55,6 +55,7 @@ export function loadConfig(projectRoot, overrides = {}) {
|
|
|
55
55
|
buildTarget: test.buildTarget ?? raw.frameworkData?.buildTarget ?? 'arduino:avr:uno',
|
|
56
56
|
board: test.board ?? raw.board ?? '@typecad/board-arduino-uno',
|
|
57
57
|
target: raw.target ?? 'avr',
|
|
58
|
+
framework: raw.framework,
|
|
58
59
|
projectRoot,
|
|
59
60
|
};
|
|
60
61
|
}
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes how the test protocol emits output for a given framework.
|
|
3
|
+
* Each framework provides its own shim so the preprocessor doesn't hardcode
|
|
4
|
+
* Serial.* (which would force the Arduino core to be linked).
|
|
5
|
+
*/
|
|
6
|
+
export interface OutputShim {
|
|
7
|
+
/** The init call emitted in the preamble, e.g. "Serial.begin(115200)". */
|
|
8
|
+
begin: string;
|
|
9
|
+
/** Print without newline — receives a fully-formed argument expression. */
|
|
10
|
+
print: (expr: string) => string;
|
|
11
|
+
/** Print with newline — receives a fully-formed argument expression. */
|
|
12
|
+
println: (expr: string) => string;
|
|
13
|
+
/** The idle-loop delay call after SUITE_END, e.g. "delay(1000)". */
|
|
14
|
+
delay: string;
|
|
15
|
+
}
|
|
16
|
+
/** Default shim: Arduino HardwareSerial. */
|
|
17
|
+
export declare const serialShim: OutputShim;
|
|
18
|
+
/** AVR native UART shim: routes through framework-avr's _uart_* helpers. */
|
|
19
|
+
export declare const avrUartShim: OutputShim;
|
|
1
20
|
export interface PreprocessorOptions {
|
|
2
21
|
/** Wrap string literals in Arduino F() macro to save SRAM on AVR. */
|
|
3
22
|
isAvr?: boolean;
|
|
23
|
+
/** Output shim — defaults to serialShim (Arduino HardwareSerial). */
|
|
24
|
+
shim?: OutputShim;
|
|
4
25
|
}
|
|
5
26
|
/**
|
|
6
27
|
* Preprocess a test file's TypeScript source.
|
|
@@ -17,10 +38,12 @@ export declare class PreprocessorContext {
|
|
|
17
38
|
private varCounter;
|
|
18
39
|
private fnCounter;
|
|
19
40
|
private preambleEmitted;
|
|
20
|
-
private readonly baudRate;
|
|
21
41
|
readonly isAvr: boolean;
|
|
22
|
-
|
|
23
|
-
|
|
42
|
+
readonly shim: OutputShim;
|
|
43
|
+
constructor(isAvr: boolean, shim?: OutputShim);
|
|
44
|
+
/** Wrap a string literal in F() on AVR to keep it in flash.
|
|
45
|
+
* Only applies when using the serialShim (Arduino core provides F()).
|
|
46
|
+
* The avrUartShim runs without the core, so F() is undefined — plain strings. */
|
|
24
47
|
flash(s: string): string;
|
|
25
48
|
/** Emit a line of TypeScript output. */
|
|
26
49
|
emit(line: string): void;
|
|
@@ -24,6 +24,20 @@
|
|
|
24
24
|
import ts from 'typescript';
|
|
25
25
|
import { isDescribeChain, collectChainSegments } from './chain-collector.js';
|
|
26
26
|
import { emitSegments } from './protocol-emitter.js';
|
|
27
|
+
/** Default shim: Arduino HardwareSerial. */
|
|
28
|
+
export const serialShim = {
|
|
29
|
+
begin: 'Serial.begin(115200)',
|
|
30
|
+
print: (e) => `Serial.print(${e})`,
|
|
31
|
+
println: (e) => `Serial.println(${e})`,
|
|
32
|
+
delay: 'delay(1000)',
|
|
33
|
+
};
|
|
34
|
+
/** AVR native UART shim: routes through framework-avr's _uart_* helpers. */
|
|
35
|
+
export const avrUartShim = {
|
|
36
|
+
begin: '_uart_init(115200)',
|
|
37
|
+
print: (e) => `_uart_print_expr(${e})`,
|
|
38
|
+
println: (e) => `_uart_println_expr(${e})`,
|
|
39
|
+
delay: '_native_delay_ms(1000)',
|
|
40
|
+
};
|
|
27
41
|
/**
|
|
28
42
|
* Preprocess a test file's TypeScript source.
|
|
29
43
|
*
|
|
@@ -35,7 +49,7 @@ import { emitSegments } from './protocol-emitter.js';
|
|
|
35
49
|
*/
|
|
36
50
|
export function preprocess(source, fileName = 'test.ts', options) {
|
|
37
51
|
const sf = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
38
|
-
const ctx = new PreprocessorContext(options?.isAvr ?? false);
|
|
52
|
+
const ctx = new PreprocessorContext(options?.isAvr ?? false, options?.shim);
|
|
39
53
|
for (const stmt of sf.statements) {
|
|
40
54
|
if (ts.isImportDeclaration(stmt)) {
|
|
41
55
|
const moduleSpecifier = stmt.moduleSpecifier.text;
|
|
@@ -56,16 +70,20 @@ export function preprocess(source, fileName = 'test.ts', options) {
|
|
|
56
70
|
// PreprocessorContext — shared state threaded through sub-modules
|
|
57
71
|
// ---------------------------------------------------------------------------
|
|
58
72
|
export class PreprocessorContext {
|
|
59
|
-
constructor(isAvr) {
|
|
73
|
+
constructor(isAvr, shim = serialShim) {
|
|
60
74
|
this.lines = [];
|
|
61
75
|
this.varCounter = 0;
|
|
62
76
|
this.fnCounter = 0;
|
|
63
77
|
this.preambleEmitted = false;
|
|
64
|
-
this.baudRate = 115200;
|
|
65
78
|
this.isAvr = isAvr;
|
|
79
|
+
this.shim = shim;
|
|
66
80
|
}
|
|
67
|
-
/** Wrap a string literal in F() on AVR to keep it in flash.
|
|
81
|
+
/** Wrap a string literal in F() on AVR to keep it in flash.
|
|
82
|
+
* Only applies when using the serialShim (Arduino core provides F()).
|
|
83
|
+
* The avrUartShim runs without the core, so F() is undefined — plain strings. */
|
|
68
84
|
flash(s) {
|
|
85
|
+
if (this.shim === avrUartShim)
|
|
86
|
+
return `"${s}"`;
|
|
69
87
|
return this.isAvr ? `F("${s}")` : `"${s}"`;
|
|
70
88
|
}
|
|
71
89
|
/** Emit a line of TypeScript output. */
|
|
@@ -84,8 +102,8 @@ export class PreprocessorContext {
|
|
|
84
102
|
}
|
|
85
103
|
emitPreamble() {
|
|
86
104
|
this.preambleEmitted = true;
|
|
87
|
-
this.lines.push(
|
|
88
|
-
this.lines.push(
|
|
105
|
+
this.lines.push(`${this.shim.begin};`);
|
|
106
|
+
this.lines.push(`${this.shim.println(this.flash('[TC:SUITE_START]'))};`);
|
|
89
107
|
}
|
|
90
108
|
build() {
|
|
91
109
|
return this.lines.join('\n') + '\n';
|
|
@@ -97,8 +115,8 @@ export class PreprocessorContext {
|
|
|
97
115
|
function processExpressionStatement(stmt, sf, ctx) {
|
|
98
116
|
const expr = stmt.expression;
|
|
99
117
|
if (isDoneCall(expr)) {
|
|
100
|
-
ctx.emit(
|
|
101
|
-
ctx.emit(`while (true) { delay
|
|
118
|
+
ctx.emit(`${ctx.shim.println(ctx.flash('[TC:SUITE_END]'))};`);
|
|
119
|
+
ctx.emit(`while (true) { ${ctx.shim.delay}; }`);
|
|
102
120
|
return;
|
|
103
121
|
}
|
|
104
122
|
if (isDescribeChain(expr)) {
|
|
@@ -22,10 +22,10 @@ export function emitSegments(segments, ctx) {
|
|
|
22
22
|
for (const seg of segments) {
|
|
23
23
|
switch (seg.kind) {
|
|
24
24
|
case 'describe':
|
|
25
|
-
ctx.emit(
|
|
25
|
+
ctx.emit(`${ctx.shim.println(ctx.flash(`[TC:DESCRIBE:${escapeProtocol(seg.name ?? '')}]`))};`);
|
|
26
26
|
break;
|
|
27
27
|
case 'it':
|
|
28
|
-
ctx.emit(
|
|
28
|
+
ctx.emit(`${ctx.shim.println(ctx.flash(`[TC:IT:${escapeProtocol(seg.name ?? '')}]`))};`);
|
|
29
29
|
break;
|
|
30
30
|
case 'expect': {
|
|
31
31
|
if (!seg.matcher)
|
|
@@ -67,17 +67,17 @@ export function emitSegments(segments, ctx) {
|
|
|
67
67
|
export function emitExpectProtocol(actualVar, matcher, matcherArgs, ctx, isString = false) {
|
|
68
68
|
if (isString && matcher === 'toBe') {
|
|
69
69
|
const rawExpected = (matcherArgs[0] ?? '').replace(/^["']|["']$/g, '');
|
|
70
|
-
ctx.emit(
|
|
71
|
-
ctx.emit(
|
|
72
|
-
ctx.emit(
|
|
73
|
-
ctx.emit(
|
|
74
|
-
ctx.emit(
|
|
70
|
+
ctx.emit(`${ctx.shim.print(ctx.flash(`[TC:EXPECT:${matcher}:`))};`);
|
|
71
|
+
ctx.emit(`${ctx.shim.print(`"${escapeProtocol(rawExpected)}"`)};`);
|
|
72
|
+
ctx.emit(`${ctx.shim.print(ctx.flash(':'))};`);
|
|
73
|
+
ctx.emit(`${ctx.shim.print(actualVar)};`);
|
|
74
|
+
ctx.emit(`${ctx.shim.println(ctx.flash(']'))};`);
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
const expectedPart = matcherArgs.join(',');
|
|
78
|
-
ctx.emit(
|
|
79
|
-
ctx.emit(
|
|
80
|
-
ctx.emit(
|
|
78
|
+
ctx.emit(`${ctx.shim.print(ctx.flash(`[TC:EXPECT:${matcher}:${expectedPart}:`))};`);
|
|
79
|
+
ctx.emit(`${ctx.shim.print(actualVar)};`);
|
|
80
|
+
ctx.emit(`${ctx.shim.println(ctx.flash(']'))};`);
|
|
81
81
|
}
|
|
82
82
|
// ---------------------------------------------------------------------------
|
|
83
83
|
// Expression classification helpers
|
package/dist/host/runner.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { findTestFiles } from './finder.js';
|
|
10
|
-
import { preprocess } from './preprocessor.js';
|
|
10
|
+
import { preprocess, serialShim, avrUartShim } from './preprocessor.js';
|
|
11
11
|
import { transpileTestFile, compileSketch, uploadSketch } from './compiler.js';
|
|
12
12
|
import { readSerialOutput } from './serial.js';
|
|
13
13
|
import { parseProtocolLines } from './parser.js';
|
|
@@ -84,6 +84,7 @@ async function processTestFile(filePath, config) {
|
|
|
84
84
|
try {
|
|
85
85
|
preprocessed = preprocess(source, path.basename(filePath), {
|
|
86
86
|
isAvr: config.target === 'avr' || config.target === 'megaavr',
|
|
87
|
+
shim: config.framework === '@typecad/framework-avr' ? avrUartShim : serialShim,
|
|
87
88
|
});
|
|
88
89
|
}
|
|
89
90
|
catch (e) {
|
package/dist/host/types.d.ts
CHANGED
|
@@ -116,6 +116,8 @@ export interface ResolvedConfig {
|
|
|
116
116
|
buildTarget: string;
|
|
117
117
|
board: string;
|
|
118
118
|
target: string;
|
|
119
|
+
/** Framework package name, e.g. "@typecad/framework-avr". */
|
|
120
|
+
framework?: string;
|
|
119
121
|
/** Absolute path to project root. */
|
|
120
122
|
projectRoot: string;
|
|
121
123
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typecad/expect",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-alpha.3",
|
|
4
4
|
"description": "Hardware test framework for TypeCAD — vitest-style assertions over serial",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"serialport": "^12.0.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@typecad/cuttlefish": "
|
|
45
|
+
"@typecad/cuttlefish": "1.0.0-alpha.3",
|
|
46
46
|
"@types/node": "^22.10.7"
|
|
47
47
|
},
|
|
48
48
|
"license": "MIT",
|