runspec-node 0.26.1 → 0.28.0
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/dist/cli.d.ts +22 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +149 -0
- package/dist/cli.js.map +1 -1
- package/dist/loader.js +1 -0
- package/dist/loader.js.map +1 -1
- package/dist/models.d.ts +1 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/parser.d.ts +2 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +98 -2
- package/dist/parser.js.map +1 -1
- package/dist/runspec.toml +15 -0
- package/dist/serve.d.ts +1 -0
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +1 -0
- package/dist/serve.js.map +1 -1
- package/dist/testing.d.ts +67 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +324 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -1
- package/package.json +11 -1
- package/src/cli.ts +165 -0
- package/src/loader.ts +1 -0
- package/src/models.ts +1 -0
- package/src/parser.ts +96 -2
- package/src/runspec.toml +15 -0
- package/src/serve.ts +1 -1
- package/src/testing.ts +334 -0
- package/src/types.ts +8 -0
- package/tests/test_cli_test.test.ts +134 -0
- package/tests/test_password.test.ts +106 -0
- package/tests/testing.test.ts +132 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { ParseHarness, harness } from '../src/testing';
|
|
2
|
+
|
|
3
|
+
// A spec with a required subcommand, a leaf with a required arg + a flag, and a
|
|
4
|
+
// leaf with only optional args — enough to exercise the whole smoke() walk.
|
|
5
|
+
const SPEC = `
|
|
6
|
+
[config]
|
|
7
|
+
autonomy-default = "autonomous"
|
|
8
|
+
|
|
9
|
+
[tool]
|
|
10
|
+
description = "A tool with subcommands"
|
|
11
|
+
require-command = true
|
|
12
|
+
|
|
13
|
+
[tool.commands.create]
|
|
14
|
+
description = "Create a thing"
|
|
15
|
+
[tool.commands.create.args]
|
|
16
|
+
name = {type = "str", required = true}
|
|
17
|
+
force = {type = "flag", default = false}
|
|
18
|
+
|
|
19
|
+
[tool.commands.show]
|
|
20
|
+
description = "Show a thing"
|
|
21
|
+
[tool.commands.show.args]
|
|
22
|
+
id = {type = "str", required = false, default = "x"}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
function h(): ParseHarness {
|
|
26
|
+
return new ParseHarness({ scriptName: 'tool', toml: SPEC });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ── core parse entry points ────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
test('expectExit(--help) returns 0 WITHOUT killing the process', () => {
|
|
32
|
+
const harn = h();
|
|
33
|
+
// If process.exit weren't intercepted, this would terminate the jest worker.
|
|
34
|
+
const out = harn.expectExit(['create', '--help'], { code: 0 });
|
|
35
|
+
expect(out).toContain('Create a thing');
|
|
36
|
+
harn.close();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('require-command: empty argv exits non-zero (RunSpecError mapped to code 1)', () => {
|
|
40
|
+
const harn = h();
|
|
41
|
+
harn.expectExit([]); // default code = 1
|
|
42
|
+
harn.close();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('missing required arg exits 1 and names the flag', () => {
|
|
46
|
+
const harn = h();
|
|
47
|
+
const out = harn.expectExit(['create'], { code: 1, contains: 'name' });
|
|
48
|
+
expect(out).toContain('name');
|
|
49
|
+
harn.close();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('expectOk returns the parsed args with coercion applied', () => {
|
|
53
|
+
const harn = h();
|
|
54
|
+
const parsed = harn.expectOk(['create', '--name', 'widget', '--force']);
|
|
55
|
+
expect(String(parsed.name)).toBe('widget');
|
|
56
|
+
expect(parsed.force).toBe(true);
|
|
57
|
+
harn.close();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ── smoke() auto-coverage ───────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
test('smoke walks the whole tree and reports the checks performed', () => {
|
|
63
|
+
const harn = h();
|
|
64
|
+
const done = harn.smoke({ values: { name: 'widget' } });
|
|
65
|
+
expect(done).toContain('--help');
|
|
66
|
+
expect(done).toContain('(no command) -> require-command');
|
|
67
|
+
expect(done).toContain('create --help');
|
|
68
|
+
expect(done).toContain('create -> missing required');
|
|
69
|
+
expect(done).toContain('create (valid)');
|
|
70
|
+
expect(done).toContain('show (valid)');
|
|
71
|
+
harn.close();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('smoke without values still does the structural checks', () => {
|
|
75
|
+
const harn = h();
|
|
76
|
+
const done = harn.smoke();
|
|
77
|
+
expect(done).toContain('--help');
|
|
78
|
+
expect(done).toContain('create -> missing required');
|
|
79
|
+
// show has no required args, so it still gets a (valid) check with empty argv
|
|
80
|
+
expect(done).toContain('show (valid)');
|
|
81
|
+
harn.close();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// ── introspection ───────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
test('command/leaf paths and arg introspection', () => {
|
|
87
|
+
const harn = h();
|
|
88
|
+
expect(harn.commandPaths()).toEqual([['create'], ['show']]);
|
|
89
|
+
expect(harn.leafCommandPaths()).toEqual([['create'], ['show']]);
|
|
90
|
+
expect(harn.requiredArgs(['create'])).toEqual(['name']);
|
|
91
|
+
expect(harn.requiredArgs(['show'])).toEqual([]);
|
|
92
|
+
expect(harn.requiresCommand()).toBe(true);
|
|
93
|
+
expect(Object.keys(harn.argsFor(['create']))).toEqual(['name', 'force']);
|
|
94
|
+
harn.close();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ── lifecycle / API guards ──────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
test('factory helper mirrors the constructor', () => {
|
|
100
|
+
const harn = harness('tool', { toml: SPEC });
|
|
101
|
+
expect(harn.scriptName).toBe('tool');
|
|
102
|
+
harn.close();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('rejects passing both config and toml', () => {
|
|
106
|
+
expect(() => new ParseHarness({ scriptName: 'tool', toml: SPEC, configPath: '/x/runspec.toml' })).toThrow(/exactly one/);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('rejects passing neither config nor toml', () => {
|
|
110
|
+
expect(() => new ParseHarness({ scriptName: 'tool' })).toThrow(/exactly one/);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('unknown runnable throws with the available names', () => {
|
|
114
|
+
expect(() => new ParseHarness({ scriptName: 'ghost', toml: SPEC })).toThrow(/ghost/);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ── interception is fully restored ──────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
test('process.exit / stdout.write / console.log are restored after a run', () => {
|
|
120
|
+
const harn = h();
|
|
121
|
+
const exitBefore = process.exit;
|
|
122
|
+
const stdoutBefore = process.stdout.write;
|
|
123
|
+
const logBefore = console.log;
|
|
124
|
+
|
|
125
|
+
harn.expectExit(['--help'], { code: 0 });
|
|
126
|
+
harn.expectExit(['create']); // also exercise the throw path
|
|
127
|
+
|
|
128
|
+
expect(process.exit).toBe(exitBefore);
|
|
129
|
+
expect(process.stdout.write).toBe(stdoutBefore);
|
|
130
|
+
expect(console.log).toBe(logBefore);
|
|
131
|
+
harn.close();
|
|
132
|
+
});
|