@aljazmc/testero 0.2.1 → 0.3.1
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 -0
- package/bin/main.js +89 -13
- package/bin/types/arguments.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ testero math unit/ # run test files whose path matches a pattern
|
|
|
12
12
|
testero -t "adds numbers" # run tests whose full name matches
|
|
13
13
|
testero --verbose --bail # list every test, stop at the first failing file
|
|
14
14
|
testero --listTests # print the test files instead of running them
|
|
15
|
+
testero --config # write the defaults to testero.config.ts, --config js to testero.config.js
|
|
15
16
|
testero --help
|
|
16
17
|
```
|
|
17
18
|
|
package/bin/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// shared/config/src/config.ts
|
|
4
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
4
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { pathToFileURL } from "node:url";
|
|
7
7
|
|
|
@@ -87,6 +87,62 @@ var Config = class _Config {
|
|
|
87
87
|
}
|
|
88
88
|
return new _Config(defaults, root);
|
|
89
89
|
}
|
|
90
|
+
/** Handles `--config`, writing the defaults to a TypeScript config file, or with `--config js` a JavaScript one. Returns whether it was given. */
|
|
91
|
+
static init(name, defaults, argv = process.argv.slice(2), root = process.cwd()) {
|
|
92
|
+
const index = argv.indexOf("--config");
|
|
93
|
+
if (index === -1) return false;
|
|
94
|
+
const next = argv[index + 1];
|
|
95
|
+
const extension = next === void 0 || next.startsWith("-") ? "ts" : next;
|
|
96
|
+
try {
|
|
97
|
+
console.log(
|
|
98
|
+
`created: ${_Config.export(name, defaults, extension, root)}`
|
|
99
|
+
);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(error instanceof Error ? error.message : error);
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
/** Writes the defaults to `<name>.config.<extension>` in the root, unless a config file is there already. */
|
|
107
|
+
static export(name, defaults, extension = "ts", root = process.cwd()) {
|
|
108
|
+
if (!_Config.extensions.includes(extension))
|
|
109
|
+
throw new Error(
|
|
110
|
+
`Unknown config file type ${extension}, use ts or js.`
|
|
111
|
+
);
|
|
112
|
+
for (const existing of _Config.extensions) {
|
|
113
|
+
const file2 = `${name}.config.${existing}`;
|
|
114
|
+
if (existsSync(join(root, file2)))
|
|
115
|
+
throw new Error(`${file2} already exists.`);
|
|
116
|
+
}
|
|
117
|
+
const file = `${name}.config.${extension}`;
|
|
118
|
+
writeFileSync(
|
|
119
|
+
join(root, file),
|
|
120
|
+
`export default ${_Config.#literal(defaults)};
|
|
121
|
+
`
|
|
122
|
+
);
|
|
123
|
+
return file;
|
|
124
|
+
}
|
|
125
|
+
/** A value as JavaScript source, objects one property per line, short arrays on one line. */
|
|
126
|
+
static #literal(value, indent = "") {
|
|
127
|
+
const inner = `${indent} `;
|
|
128
|
+
const lines = (items, open, close) => items.length === 0 ? `${open}${close}` : `${open}
|
|
129
|
+
${items.map((item) => `${inner}${item},
|
|
130
|
+
`).join("")}${indent}${close}`;
|
|
131
|
+
if (Array.isArray(value)) {
|
|
132
|
+
const items = value.map((item) => _Config.#literal(item, inner));
|
|
133
|
+
const line = `[${items.join(", ")}]`;
|
|
134
|
+
return line.length <= 60 && !line.includes("\n") ? line : lines(items, "[", "]");
|
|
135
|
+
}
|
|
136
|
+
if (value?.constructor === Object)
|
|
137
|
+
return lines(
|
|
138
|
+
Object.entries(value).map(
|
|
139
|
+
([key, item]) => `${/^[A-Za-z_$][\w$]*$/.test(key) ? key : JSON.stringify(key)}: ${_Config.#literal(item, inner)}`
|
|
140
|
+
),
|
|
141
|
+
"{",
|
|
142
|
+
"}"
|
|
143
|
+
);
|
|
144
|
+
return JSON.stringify(value);
|
|
145
|
+
}
|
|
90
146
|
/** Deep merges plain objects, anything else in override replaces base. */
|
|
91
147
|
static merge(base, override) {
|
|
92
148
|
const plain = (value) => value?.constructor === Object;
|
|
@@ -148,6 +204,7 @@ Options:
|
|
|
148
204
|
--listTests print the test files instead of running them
|
|
149
205
|
--passWithNoTests succeed when there are no test files
|
|
150
206
|
--testTimeout <ms> milliseconds a test or hook may take
|
|
207
|
+
--config [js] write the default testero.config.ts, or .js
|
|
151
208
|
-h, --help show this help
|
|
152
209
|
`;
|
|
153
210
|
patterns = [];
|
|
@@ -1592,7 +1649,7 @@ import { dirname as dirname2, isAbsolute, relative } from "node:path";
|
|
|
1592
1649
|
import { fileURLToPath } from "node:url";
|
|
1593
1650
|
|
|
1594
1651
|
// packages/testero/src/transformer.ts
|
|
1595
|
-
import { mkdtempSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "node:fs";
|
|
1652
|
+
import { mkdtempSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1596
1653
|
import { tmpdir } from "node:os";
|
|
1597
1654
|
import { dirname, extname, join as join2, sep } from "node:path";
|
|
1598
1655
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -1610,6 +1667,8 @@ var Transformer = class _Transformer {
|
|
|
1610
1667
|
".mts": "ts",
|
|
1611
1668
|
".tsx": "tsx"
|
|
1612
1669
|
};
|
|
1670
|
+
/** The import.meta properties that describe the source file. */
|
|
1671
|
+
static #meta = ["url", "filename", "dirname"];
|
|
1613
1672
|
/** Marks the resolutions the plugin asks esbuild for itself. */
|
|
1614
1673
|
static #own = /* @__PURE__ */ Symbol("testero");
|
|
1615
1674
|
/** Where compiled test files go, stack frames there are testero's own. */
|
|
@@ -1618,7 +1677,7 @@ var Transformer = class _Transformer {
|
|
|
1618
1677
|
#globals = join2(this.#directory, "globals.mjs");
|
|
1619
1678
|
#count = 0;
|
|
1620
1679
|
constructor() {
|
|
1621
|
-
|
|
1680
|
+
writeFileSync2(this.#globals, Globals.source());
|
|
1622
1681
|
}
|
|
1623
1682
|
/** Bundles a test file, returns the URL of the module to import. */
|
|
1624
1683
|
async compile(file) {
|
|
@@ -1634,6 +1693,12 @@ var Transformer = class _Transformer {
|
|
|
1634
1693
|
sourcemap: "inline",
|
|
1635
1694
|
sourcesContent: false,
|
|
1636
1695
|
inject: [this.#globals],
|
|
1696
|
+
define: Object.fromEntries(
|
|
1697
|
+
_Transformer.#meta.map((name) => [
|
|
1698
|
+
`import.meta.${name}`,
|
|
1699
|
+
`__testero.${name}`
|
|
1700
|
+
])
|
|
1701
|
+
),
|
|
1637
1702
|
// CommonJS code in the bundle may require installed packages
|
|
1638
1703
|
banner: {
|
|
1639
1704
|
js: 'import { createRequire as __testeroRequire } from "node:module";\nconst require = __testeroRequire(import.meta.url);'
|
|
@@ -1641,7 +1706,7 @@ var Transformer = class _Transformer {
|
|
|
1641
1706
|
logLevel: "silent",
|
|
1642
1707
|
plugins: [this.#plugin()]
|
|
1643
1708
|
});
|
|
1644
|
-
|
|
1709
|
+
writeFileSync2(outfile, outputFiles[0]?.text ?? "");
|
|
1645
1710
|
return pathToFileURL2(outfile).href;
|
|
1646
1711
|
}
|
|
1647
1712
|
/** Removes the compiled files. */
|
|
@@ -1652,6 +1717,21 @@ var Transformer = class _Transformer {
|
|
|
1652
1717
|
return {
|
|
1653
1718
|
name: "testero",
|
|
1654
1719
|
setup: (build2) => {
|
|
1720
|
+
build2.onResolve({ filter: /^testero-meta:/ }, (args) => ({
|
|
1721
|
+
path: args.path.slice("testero-meta:".length),
|
|
1722
|
+
namespace: "testero-meta"
|
|
1723
|
+
}));
|
|
1724
|
+
build2.onLoad(
|
|
1725
|
+
{ filter: /.*/, namespace: "testero-meta" },
|
|
1726
|
+
(args) => ({
|
|
1727
|
+
contents: `export default ${JSON.stringify({
|
|
1728
|
+
url: pathToFileURL2(args.path).href,
|
|
1729
|
+
filename: args.path,
|
|
1730
|
+
dirname: dirname(args.path)
|
|
1731
|
+
})};`,
|
|
1732
|
+
loader: "js"
|
|
1733
|
+
})
|
|
1734
|
+
);
|
|
1655
1735
|
build2.onResolve({ filter: /^[^./]/ }, async (args) => {
|
|
1656
1736
|
if (args.pluginData === _Transformer.#own) return void 0;
|
|
1657
1737
|
if (_Transformer.modules.includes(args.path))
|
|
@@ -1674,15 +1754,10 @@ var Transformer = class _Transformer {
|
|
|
1674
1754
|
});
|
|
1675
1755
|
build2.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, (args) => {
|
|
1676
1756
|
if (_Transformer.#installed(args.path)) return void 0;
|
|
1677
|
-
const
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
};
|
|
1682
|
-
const contents = readFileSync2(args.path, "utf8").replace(
|
|
1683
|
-
/\bimport\.meta\.(url|filename|dirname)\b/g,
|
|
1684
|
-
(_, name) => JSON.stringify(values[name])
|
|
1685
|
-
);
|
|
1757
|
+
const source = readFileSync2(args.path, "utf8");
|
|
1758
|
+
const contents = /\bimport\.meta\b/.test(source) ? `${source}
|
|
1759
|
+
import __testero from ${JSON.stringify(`testero-meta:${args.path}`)};
|
|
1760
|
+
` : source;
|
|
1686
1761
|
return {
|
|
1687
1762
|
contents,
|
|
1688
1763
|
loader: _Transformer.#loaders[extname(args.path)] ?? "js",
|
|
@@ -2125,6 +2200,7 @@ var Testero = class {
|
|
|
2125
2200
|
|
|
2126
2201
|
// packages/testero/src/index.ts
|
|
2127
2202
|
try {
|
|
2203
|
+
if (Config.init("testero", config_default)) process.exit();
|
|
2128
2204
|
const args = new Arguments(process.argv.slice(2));
|
|
2129
2205
|
if (args.help) process.stdout.write(Arguments.usage);
|
|
2130
2206
|
else {
|
package/bin/types/arguments.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Rules } from "./rules.js";
|
|
|
2
2
|
import type { Options } from "./testero.js";
|
|
3
3
|
/** Command line arguments, named like Jest's. */
|
|
4
4
|
export default class Arguments {
|
|
5
|
-
static readonly usage = "Usage: testero [patterns...] [options]\n\nRuns the test files whose paths match a pattern, every test file without patterns.\n\nOptions:\n -t, --testNamePattern <regex> run only tests whose name matches\n --bail stop after the first failing test file\n --verbose list every test\n --listTests print the test files instead of running them\n --passWithNoTests succeed when there are no test files\n --testTimeout <ms> milliseconds a test or hook may take\n -h, --help show this help\n";
|
|
5
|
+
static readonly usage = "Usage: testero [patterns...] [options]\n\nRuns the test files whose paths match a pattern, every test file without patterns.\n\nOptions:\n -t, --testNamePattern <regex> run only tests whose name matches\n --bail stop after the first failing test file\n --verbose list every test\n --listTests print the test files instead of running them\n --passWithNoTests succeed when there are no test files\n --testTimeout <ms> milliseconds a test or hook may take\n --config [js] write the default testero.config.ts, or .js\n -h, --help show this help\n";
|
|
6
6
|
readonly patterns: string[];
|
|
7
7
|
readonly rules: Partial<Rules>;
|
|
8
8
|
namePattern: RegExp | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aljazmc/testero",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A simple, Jest like test runner. Easy to understand, easy to customize and easy to extend.",
|
|
5
5
|
"author": "aljazmc",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"modular"
|
|
21
21
|
],
|
|
22
22
|
"engines": {
|
|
23
|
-
"node": "^
|
|
23
|
+
"node": "^22.18.0 || ^24 || ^25 || ^26"
|
|
24
24
|
},
|
|
25
25
|
"types": "./bin/types/testero.d.ts",
|
|
26
26
|
"bin": "./bin/main.js",
|