chore: fix namespaces

This commit is contained in:
2026-07-04 22:40:58 +02:00
parent 832b9b2bad
commit 9d3ef431b0
6 changed files with 66 additions and 26 deletions
+31
View File
@@ -6,6 +6,8 @@ Generate tests from your JSDoc documentation for whichever testing harness you p
> See `gen-doctests --help` for more detail. > See `gen-doctests --help` for more detail.
### Generate tests
Write a doctest alongside your source code: Write a doctest alongside your source code:
````ts ````ts
@@ -47,6 +49,8 @@ test("isEven()", () => {
}); });
``` ```
### Config
You can also define a `doctests.config.ts` file to skip the command arguments: You can also define a `doctests.config.ts` file to skip the command arguments:
```ts ```ts
@@ -58,3 +62,30 @@ export default defineConfig({
outDir: "dist/", outDir: "dist/",
}); });
``` ```
### Skipping tests
You can specify that a test should be ignored or that it fails by specifying
that next to the language tag of the code block:
````ts
/**
* ...
* @example
* ```ts ignore
* // this test is ignored
* ```
* @example
* ```ts fails
* // this test is expected to fail
* expect(1).toBe(2)
* ```
*/
````
If you use the `--must-assert` flag or `onlyGenerateTests` option then tests
which do not use any assertions will not be included in emitted test files.
## License
Published under [MIT License](./LICENSE)
+2 -1
View File
@@ -1,7 +1,8 @@
import { defineConfig } from "./dist/config"; import { defineConfig } from "./dist/config";
export default defineConfig({ export default defineConfig({
include: ["src/**.ts"], include: ["src/**/*.ts"],
outDir: "tests/generated", outDir: "tests/generated",
templateHeader: ["console.log('hello, world!');"], templateHeader: ["console.log('hello, world!');"],
emitRegions: true,
}); });
+10 -3
View File
@@ -1,9 +1,12 @@
{ {
"name": "gen-doctests", "name": "gen-doctests",
"author": {
"name": "BluePlum",
"url": "https://git.louiscreates.com/blueplum"
},
"description": "Tool for generating tests from JSDoc @example:s for various test harnesses", "description": "Tool for generating tests from JSDoc @example:s for various test harnesses",
"keywords": ["tool", "docs", "test", "doctest"], "keywords": ["tool", "docs", "test", "doctest"],
"private": false, "version": "0.2.3",
"version": "0.2.2",
"type": "module", "type": "module",
"bin": "dist/index.js", "bin": "dist/index.js",
"scripts": { "scripts": {
@@ -42,5 +45,9 @@
"jiti": "^2.7.0", "jiti": "^2.7.0",
"picomatch": "^4.0.5", "picomatch": "^4.0.5",
"typescript": "^6.0.3" "typescript": "^6.0.3"
} },
"repository": {
"url": "https://git.louiscreates.com/blueplum/gen-doctests"
},
"license": "MIT"
} }
+8 -5
View File
@@ -182,14 +182,14 @@ export function createGenerator(options: Options) {
fn = harness.skip; fn = harness.skip;
else fn = harness.fail; else fn = harness.fail;
let node: Region | null = null; let node: Region = regions;
for (const region of test.namespace) { for (const region of test.namespace) {
regions[region] ??= {}; node[region] ??= {};
node = regions[region]; node = node[region];
} }
const out = fn(src, name); const out = fn(src, name);
if (node !== null) node[Symbol()] = out; if (test.namespace.length !== 0) node[Symbol()] = out;
else source += out + "\n\n"; else source += out + "\n\n";
} }
@@ -205,8 +205,11 @@ export function createGenerator(options: Options) {
typeof value === "object" && typeof value === "object" &&
typeof key === "string" typeof key === "string"
) { ) {
const src = emitRegions(value);
regions.push( regions.push(
harness.region(emitRegions(value), key), options.emitRegions
? harness.region(src, key)
: src,
); );
} }
} }
-8
View File
@@ -104,17 +104,9 @@ async function main() {
function parseWithOptions(options: Options) { function parseWithOptions(options: Options) {
const doctest = picomatch("**/*" + options.fileExtension + ".*"); const doctest = picomatch("**/*" + options.fileExtension + ".*");
/**
* @example
* Test 1
*/
const files = globSync(options.include) const files = globSync(options.include)
.filter((v) => !doctest(v)) .filter((v) => !doctest(v))
.filter((path) => !lstatSync(path).isDirectory()); .filter((path) => !lstatSync(path).isDirectory());
/**
* @example
* Test 2
*/
const parsed = files const parsed = files
.map((path) => parseFile(path, options)) .map((path) => parseFile(path, options))
.filter((v) => v !== null); .filter((v) => v !== null);
+12 -6
View File
@@ -161,10 +161,18 @@ export function parseFile(
kind = PathFragmentType.Other; kind = PathFragmentType.Other;
if (identifier !== null) { if (identifier !== null) {
name = identifier; name = identifier;
if (node.type && ts.isFunctionLike(node.type)) {
kind = PathFragmentType.Function;
newPath.push({ newPath.push({
kind: PathFragmentType.Other, kind: PathFragmentType.Function,
fragment: identifier, fragment: identifier,
}); });
} else {
newPath.push({
kind: PathFragmentType.Namespace,
fragment: identifier,
});
}
} else if ( } else if (
ts.isComputedPropertyName(node.name) && ts.isComputedPropertyName(node.name) &&
ts.isBinaryExpression(node.name.expression) ts.isBinaryExpression(node.name.expression)
@@ -176,6 +184,7 @@ export function parseFile(
fragment: "unknown", fragment: "unknown",
}); });
} }
visitThis = false; visitThis = false;
if (node.type) if (node.type)
node.type.forEachChild((node) => { node.type.forEachChild((node) => {
@@ -223,15 +232,14 @@ export function parseFile(
.filter((tag) => tag.tagName.text === "example"); .filter((tag) => tag.tagName.text === "example");
if (examples.length === 0) return; if (examples.length === 0) return;
let p = [...path]; const p = [...path];
if (name !== null && kind !== null) if (name !== null && kind !== null)
p.push({ kind, fragment: name }); p.push({ kind, fragment: name });
const pOld = p;
const namespace = []; const namespace = [];
while ( while (
options.emitRegions &&
p[0].kind === PathFragmentType.Namespace && p[0].kind === PathFragmentType.Namespace &&
p.length > 1 p.length > 1
) { ) {
@@ -239,8 +247,6 @@ export function parseFile(
namespace.push(p.shift()!.fragment); namespace.push(p.shift()!.fragment);
} }
if (!options.emitRegions) p = pOld;
name = computeName(p); name = computeName(p);
for (const example of examples) { for (const example of examples) {