|
| 1 | +--- |
| 2 | +agent: 'agent' |
| 3 | +description: 'Add a new automated test type to the test-tokenizer generation framework' |
| 4 | +--- |
| 5 | + |
| 6 | +Your goal is to add a new automated test type to the test-tokenizer generation framework in this codebase. The framework auto-generates Jest spec files from declarative test definitions so developers can quickly observe how behavior changes for complex cases. |
| 7 | + |
| 8 | +## Getting Started |
| 9 | + |
| 10 | +1. Get the name of the new test type (e.g. "ENVI Modeler", "Assembler", "Hover Help") |
| 11 | +2. Understand what function(s) the tests will exercise and what inputs/outputs they need |
| 12 | +3. Determine the output lib directory under `libs/tests/<name>/src/lib` |
| 13 | + |
| 14 | +--- |
| 15 | + |
| 16 | +## Step 1 — Add interfaces to `tests.interface.ts` |
| 17 | + |
| 18 | +File: `apps/test-tokenizer/src/test-maker/tests.interface.ts` |
| 19 | + |
| 20 | +Add two interfaces at the bottom of the file, following the `IAssemblerTest` / `IAutoAssemblerTest` pattern: |
| 21 | + |
| 22 | +- **Individual test interface** (`IXxxTest`) — fields: `name: string` plus whatever inputs the function under test requires |
| 23 | +- **Suite wrapper interface** (`IAutoXxxTest extends IBaseAutoTest`) — field: `tests: IXxxTest[]` |
| 24 | + |
| 25 | +Add any required type imports at the top of the file. |
| 26 | + |
| 27 | +Example (for ENVI Modeler): |
| 28 | +```typescript |
| 29 | +export interface IENVIModelerTest { |
| 30 | + name: string; |
| 31 | + nodes: ENVIModelerNode[]; |
| 32 | + edges: ENVIModelerEdge[]; |
| 33 | +} |
| 34 | + |
| 35 | +export interface IAutoENVIModelerTest extends IBaseAutoTest { |
| 36 | + tests: IENVIModelerTest[]; |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +--- |
| 41 | + |
| 42 | +## Step 2 — Create the auto-tests interface file |
| 43 | + |
| 44 | +Create: `apps/test-tokenizer/src/test-maker/tests/auto-<name>-tests.interface.ts` |
| 45 | + |
| 46 | +Export an empty array constant of the suite wrapper type. The developer will populate this with test suites. |
| 47 | + |
| 48 | +Example: |
| 49 | +```typescript |
| 50 | +import { IAutoENVIModelerTest } from '../tests.interface'; |
| 51 | + |
| 52 | +export const AUTO_ENVI_MODELER_TESTS: IAutoENVIModelerTest[] = []; |
| 53 | +``` |
| 54 | + |
| 55 | +--- |
| 56 | + |
| 57 | +## Step 3 — Create the maker function |
| 58 | + |
| 59 | +Create: `apps/test-tokenizer/src/test-maker/makers/tests-for-<name>.ts` |
| 60 | + |
| 61 | +Export an async function `TestsForXxx(name: string, tests: IXxxTest[], uri = join(process.cwd(), 'tokens.ts'))`. |
| 62 | + |
| 63 | +The maker does two things: |
| 64 | +1. **At generation time**: runs the actual logic against each test's inputs to capture expected outputs as inline snapshots |
| 65 | +2. **Emits a spec file**: writes TypeScript source code to `uri` that reproduces the same setup and asserts against those snapshots |
| 66 | + |
| 67 | +### Key patterns |
| 68 | + |
| 69 | +- Build up a `strings: string[]` array and call `writeFileSync(uri, strings.join('\n'))` at the end |
| 70 | +- The generated spec file must be self-contained (all imports, all setup, all assertions inline) |
| 71 | +- For each test, serialize inputs as JSON literals using `JSON.stringify(value, null, 2)` |
| 72 | +- Close with `strings.push('});')` and `strings.push('')` |
| 73 | + |
| 74 | +### Registry setup (required for ENVI Modeler tests) |
| 75 | + |
| 76 | +When the function under test requires an `MCPTaskRegistry`, bootstrap it fully — both at generation time **and** in the emitted spec (once per file, outside the `describe` block): |
| 77 | + |
| 78 | +```typescript |
| 79 | +// generation-time bootstrap |
| 80 | +const logManager = new LogManager({ alert: () => {} }); |
| 81 | +const index = new IDLIndex(logManager, 1, false); |
| 82 | +index.loadGlobalTokens(DEFAULT_IDL_EXTENSION_CONFIG); |
| 83 | +const registry = new MCPTaskRegistry(logManager); |
| 84 | +registry.registerTasksFromGlobalTokens( |
| 85 | + index.globalIndex.globalTokensByTypeByName[GLOBAL_TOKEN_TYPES.FUNCTION], |
| 86 | + index.globalIndex.globalTokensByTypeByName[GLOBAL_TOKEN_TYPES.STRUCTURE], |
| 87 | +); |
| 88 | +``` |
| 89 | + |
| 90 | +Emit the equivalent code into the generated spec using `strings.push(...)`. |
| 91 | + |
| 92 | +Required imports for registry-based tests (in both maker and generated spec): |
| 93 | +- `LogManager` from `@idl/logger` |
| 94 | +- `MCPTaskRegistry` from `@idl/mcp/tasks` |
| 95 | +- `IDL_INDEX_OPTIONS, IDLIndex` from `@idl/parsing/index` |
| 96 | +- `GLOBAL_TOKEN_TYPES` from `@idl/types/idl-data-types` |
| 97 | +- `DEFAULT_IDL_EXTENSION_CONFIG` from `@idl/vscode/extension-config` |
| 98 | + |
| 99 | +### Structure of one generated test |
| 100 | + |
| 101 | +```typescript |
| 102 | +it(`[auto generated] ${testName}`, () => { |
| 103 | + // inputs serialized as JSON literals |
| 104 | + const nodes: ENVIModelerNode[] = <JSON>; |
| 105 | + const edges: ENVIModelerEdge[] = <JSON>; |
| 106 | + |
| 107 | + // call function under test |
| 108 | + const errors = ValidateENVIModelerNodes(nodes, edges, registry); |
| 109 | + |
| 110 | + // inline snapshot of expected output (captured at generation time) |
| 111 | + const expectedErrors: string[] = <captured>; |
| 112 | + expect(errors).toEqual(expectedErrors); |
| 113 | + |
| 114 | + // conditional: only emit workflow assertion when there are no errors |
| 115 | + if (workflow !== undefined) { |
| 116 | + const expectedWorkflow = <captured>; |
| 117 | + expect(CreateENVIModelerWorkflow(nodes, edges, registry)).toEqual(expectedWorkflow); |
| 118 | + } |
| 119 | +}); |
| 120 | +``` |
| 121 | + |
| 122 | +Look at `apps/test-tokenizer/src/test-maker/makers/tests-for-envi-modeler.ts` for a complete reference implementation. |
| 123 | + |
| 124 | +--- |
| 125 | + |
| 126 | +## Step 4 — Wire into the generator |
| 127 | + |
| 128 | +File: `apps/test-tokenizer/src/test-maker/generate-automated-tests.ts` |
| 129 | + |
| 130 | +Add two imports near the other maker/test imports: |
| 131 | +```typescript |
| 132 | +import { TestsForXxx } from './makers/tests-for-xxx'; |
| 133 | +import { AUTO_XXX_TESTS } from './tests/auto-xxx-tests.interface'; |
| 134 | +``` |
| 135 | + |
| 136 | +Add a generation block at the end of `GenerateAutomatedTests()`, following the exact same pattern as every other block: |
| 137 | +```typescript |
| 138 | +const outDirXxx = join(process.cwd(), 'libs/tests/<name>/src/lib'); |
| 139 | +CleanTestDir(outDirXxx); |
| 140 | +console.log('Generating tests for <name>'); |
| 141 | +for (let i = 0; i < AUTO_XXX_TESTS.length; i++) { |
| 142 | + console.log(` Suite (${i}): ${AUTO_XXX_TESTS[i].suiteName}`); |
| 143 | + await TestsForXxx( |
| 144 | + AUTO_XXX_TESTS[i].suiteName, |
| 145 | + AUTO_XXX_TESTS[i].tests, |
| 146 | + join(outDirXxx, AUTO_XXX_TESTS[i].fileName), |
| 147 | + ); |
| 148 | +} |
| 149 | +console.log(); |
| 150 | +``` |
| 151 | + |
| 152 | +--- |
| 153 | + |
| 154 | +## Step 5 — Set up the test lib |
| 155 | + |
| 156 | +If `libs/tests/<name>/` does not yet exist, scaffold it to match the pattern of an existing test lib such as `libs/tests/assembler/`. Required files: |
| 157 | +- `project.json` — name: `"tests-<name>"`, executor: `@nx/jest:jest` |
| 158 | +- `jest.config.ts` |
| 159 | +- `tsconfig.json`, `tsconfig.lib.json`, `tsconfig.spec.json` |
| 160 | +- `.eslintrc.json` |
| 161 | +- `src/index.ts` — leave empty (no exports) |
| 162 | +- `src/lib/` — directory for generated spec files |
| 163 | + |
| 164 | +If the lib already exists, make sure `src/index.ts` is empty (no broken exports). |
| 165 | + |
| 166 | +Also check `tsconfig.base.json` to confirm the path alias follows the `"@idl/tests/<name>"` pattern used by all other test libs. |
| 167 | + |
| 168 | +--- |
| 169 | + |
| 170 | +## Step 6 — Create a placeholder spec |
| 171 | + |
| 172 | +Create: `libs/tests/<name>/src/lib/auto-<name>-basic.spec.ts` |
| 173 | + |
| 174 | +```typescript |
| 175 | +describe(`[auto generated] <Name> placeholder`, () => { |
| 176 | + /* stub — run the test-tokenizer generator to populate this directory */ |
| 177 | +}); |
| 178 | +``` |
| 179 | + |
| 180 | +This ensures Jest recognizes the suite and the directory is non-empty before the generator runs. |
| 181 | + |
| 182 | +--- |
| 183 | + |
| 184 | +## Verification |
| 185 | + |
| 186 | +1. Check for TypeScript errors in the edited files |
| 187 | +2. Populate `AUTO_XXX_TESTS` in the interface file with at least one test suite entry |
| 188 | +3. Run the test-tokenizer NX generator target — should log `Generating tests for <name>` without errors |
| 189 | +4. Run `nx test tests-<name>` — stub spec (or generated specs) should pass |
0 commit comments