@zyplux/spectra 0.3.1 → 0.4.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/README.md +3 -0
- package/dist/fakes/repository-fake.d.ts +27 -0
- package/dist/fakes/repository-fake.js +68 -0
- package/dist/repository-test.d.ts +16 -0
- package/dist/repository-test.js +10 -0
- package/package.json +14 -6
package/README.md
CHANGED
|
@@ -58,6 +58,7 @@ Each tool has a named entry point. Import the Vitest bases from `/library-test-a
|
|
|
58
58
|
| --- | --- |
|
|
59
59
|
| `/library-test-api` | Library and CLI Vitest bases |
|
|
60
60
|
| `/shell-fake`, `/fetch-fake`, `/prompt-fake` | Fakes for subprocesses, HTTP, and prompts |
|
|
61
|
+
| `/repository-fake`, `/repository-test` | Git/GitHub command replies and repository-aware CLI fixtures |
|
|
61
62
|
| `/cli-runner`, `/temp-directory`, `/poll-until` | CLI execution, scratch directories, and test polling |
|
|
62
63
|
| `/test-matchers` | Register shared Vitest matchers |
|
|
63
64
|
| `/console-capture` | Capture console messages |
|
|
@@ -70,10 +71,12 @@ Internal helpers stay beside the tools that use them: pattern matching and modul
|
|
|
70
71
|
|
|
71
72
|
- `libraryTest` — lazy fixtures: `shell` (fake `node:child_process` spawn, installed only when destructured), `tempDir` (auto-removed scratch directory with `path`, `write`, `exists`).
|
|
72
73
|
- `cliTest` — extends `libraryTest`; automatically captures and silences `console` (`logs`), makes `node:timers/promises` sleeps instant, and installs `network` (fake `fetch`). It adds lazy `prompt` (fake `node:readline/promises` interface that records every question and answers with an empty string) and `env` (`set(name, value)` stubs an env var for the test).
|
|
74
|
+
- `repositoryTest` — extends `cliTest` with a lazy `repo` fake and the mocked timer function as `sleep` for polling assertions.
|
|
73
75
|
|
|
74
76
|
## Fakes and helpers
|
|
75
77
|
|
|
76
78
|
- `createShellFake()` — routes commands (`on(pattern, ...replies)`, later routes win, the last reply repeats; `otherwise(reply)` sets a fallback, unrouted commands throw) and records `calls` (`{ argv, cwd?, env?, program, stdin? }`), `commands` (rendered strings), `commandsMatching(pattern)`.
|
|
79
|
+
- `createRepositoryFake(shell, rootPath)` — configures Git/GitHub replies for branches, commits, PRs and repository identity. Common repository writes succeed by default; override their shell replies to test failures.
|
|
77
80
|
- `createConsoleCapture()` — records `logLines`/`warnLines`/`errorLines`.
|
|
78
81
|
- `createFetchFake()` — routes urls (`on(prefixOrRegExp, reply)`, `otherwise(reply)`) and records `requests`; `okResponse()`/`notFoundResponse()` build replies.
|
|
79
82
|
- `createPromptFake()` — records `question()` messages and immediately returns an empty string, simulating Enter without typed input. It lets tests exercise terminal prompts without waiting for keyboard input.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ShellFake } from './shell-fake.ts';
|
|
2
|
+
export type RepositoryFake = {
|
|
3
|
+
queuePrFields: (fields: Record<string, [boolean | number | string, ...(boolean | number | string)[]] | boolean | number | string>) => void;
|
|
4
|
+
setCurrentBranch: (branch: string) => void;
|
|
5
|
+
setHeadSha: (sha: string) => void;
|
|
6
|
+
setPrListState: (state: string) => void;
|
|
7
|
+
setRemoteBranchSha: (branch: string, ...shas: [string, ...string[]]) => void;
|
|
8
|
+
setRemoteMainSha: (sha: string) => void;
|
|
9
|
+
setRepoSlug: (slug: string) => void;
|
|
10
|
+
setRoot: (dir: string) => void;
|
|
11
|
+
setWorkingTreeStatus: (status: string) => void;
|
|
12
|
+
syncFeatureBranch: (branch: string, sha: string) => void;
|
|
13
|
+
syncMain: (sha: string) => void;
|
|
14
|
+
};
|
|
15
|
+
export declare const createRepositoryFake: (shell: ShellFake, rootPath: string) => {
|
|
16
|
+
queuePrFields: (fields: Record<string, string | number | boolean | [string | number | boolean, ...(string | number | boolean)[]]>) => void;
|
|
17
|
+
setCurrentBranch: (branch: string) => void;
|
|
18
|
+
setHeadSha: (sha: string) => void;
|
|
19
|
+
setPrListState: (state: string) => void;
|
|
20
|
+
setRemoteBranchSha: (branch: string, shas_0: string, ...shas: string[]) => void;
|
|
21
|
+
setRemoteMainSha: (sha: string) => void;
|
|
22
|
+
setRepoSlug: (slug: string) => void;
|
|
23
|
+
setRoot: (dir: string) => void;
|
|
24
|
+
setWorkingTreeStatus: (status: string) => void;
|
|
25
|
+
syncFeatureBranch: (branch: string, sha: string) => void;
|
|
26
|
+
syncMain: (sha: string) => void;
|
|
27
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const BENIGN_WRITE_COMMANDS = [
|
|
2
|
+
'gh pr create',
|
|
3
|
+
'gh pr merge',
|
|
4
|
+
'gh pr ready',
|
|
5
|
+
'gh release create',
|
|
6
|
+
'gh release delete',
|
|
7
|
+
'git branch',
|
|
8
|
+
'git checkout',
|
|
9
|
+
'git fetch',
|
|
10
|
+
'git pull',
|
|
11
|
+
'git push',
|
|
12
|
+
];
|
|
13
|
+
export const createRepositoryFake = (shell, rootPath) => {
|
|
14
|
+
const setRoot = (dir) => {
|
|
15
|
+
shell.on('git rev-parse --show-toplevel', dir);
|
|
16
|
+
};
|
|
17
|
+
setRoot(rootPath);
|
|
18
|
+
for (const command of BENIGN_WRITE_COMMANDS)
|
|
19
|
+
shell.on(command, '');
|
|
20
|
+
const setCurrentBranch = (branch) => {
|
|
21
|
+
shell.on('git rev-parse --abbrev-ref HEAD', branch);
|
|
22
|
+
};
|
|
23
|
+
const setHeadSha = (sha) => {
|
|
24
|
+
shell.on('git rev-parse HEAD', sha);
|
|
25
|
+
};
|
|
26
|
+
const setRemoteMainSha = (sha) => {
|
|
27
|
+
shell.on('git rev-parse origin/main', sha);
|
|
28
|
+
};
|
|
29
|
+
const setWorkingTreeStatus = (status) => {
|
|
30
|
+
shell.on('git status --porcelain', status);
|
|
31
|
+
};
|
|
32
|
+
const setRemoteBranchSha = (branch, ...shas) => {
|
|
33
|
+
const [firstSha, ...laterShas] = shas;
|
|
34
|
+
const toRefLine = (sha) => `${sha}\trefs/heads/${branch}`;
|
|
35
|
+
shell.on(`git ls-remote origin refs/heads/${branch}`, toRefLine(firstSha), ...laterShas.map(sha => toRefLine(sha)));
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
queuePrFields: fields => {
|
|
39
|
+
for (const [field, values] of Object.entries(fields)) {
|
|
40
|
+
const [first, ...later] = Array.isArray(values) ? values : [values];
|
|
41
|
+
shell.on(`gh pr view --json ${field}`, JSON.stringify({ [field]: first }), ...later.map(value => JSON.stringify({ [field]: value })));
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
setCurrentBranch,
|
|
45
|
+
setHeadSha,
|
|
46
|
+
setPrListState: state => {
|
|
47
|
+
shell.on('gh pr list', JSON.stringify(state ? [{ state }] : []));
|
|
48
|
+
},
|
|
49
|
+
setRemoteBranchSha,
|
|
50
|
+
setRemoteMainSha,
|
|
51
|
+
setRepoSlug: slug => {
|
|
52
|
+
shell.on('gh repo view --json nameWithOwner', JSON.stringify({ nameWithOwner: slug }));
|
|
53
|
+
},
|
|
54
|
+
setRoot,
|
|
55
|
+
setWorkingTreeStatus,
|
|
56
|
+
syncFeatureBranch: (branch, sha) => {
|
|
57
|
+
setCurrentBranch(branch);
|
|
58
|
+
setHeadSha(sha);
|
|
59
|
+
setRemoteBranchSha(branch, sha);
|
|
60
|
+
},
|
|
61
|
+
syncMain: sha => {
|
|
62
|
+
setCurrentBranch('main');
|
|
63
|
+
setHeadSha(sha);
|
|
64
|
+
setRemoteMainSha(sha);
|
|
65
|
+
setWorkingTreeStatus('');
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
2
|
+
import type { RepositoryFake } from './fakes/repository-fake.ts';
|
|
3
|
+
export declare const repositoryTest: import("vitest").TestAPI<{
|
|
4
|
+
prompt: import("./fakes/prompt-fake.ts").PromptFake;
|
|
5
|
+
shell: import("./fakes/shell-fake.ts").ShellFake;
|
|
6
|
+
tempDir: import("./helpers/temp-directory.ts").TempDir;
|
|
7
|
+
env: import("./library-test-api.ts").EnvStub;
|
|
8
|
+
instantSleep: undefined;
|
|
9
|
+
logs: import("./reporters/console-capture.ts").ConsoleCapture;
|
|
10
|
+
network: import("./fakes/fetch-fake.ts").FetchFake;
|
|
11
|
+
sleep: typeof sleep;
|
|
12
|
+
$__test: (object & Record<"sleep", typeof sleep>) | undefined;
|
|
13
|
+
repo: RepositoryFake;
|
|
14
|
+
$__worker: object | undefined;
|
|
15
|
+
$__file: object | undefined;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
2
|
+
import { createRepositoryFake } from "./fakes/repository-fake.js";
|
|
3
|
+
import { cliTest } from "./library-test-api.js";
|
|
4
|
+
export const repositoryTest = cliTest
|
|
5
|
+
.extend('sleep', () => sleep)
|
|
6
|
+
.extend({
|
|
7
|
+
repo: async ({ shell, tempDir }, use) => {
|
|
8
|
+
await use(createRepositoryFake(shell, tempDir.path));
|
|
9
|
+
},
|
|
10
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zyplux/spectra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Shared Vitest fixtures, matchers, polling, and journal reporting.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"test",
|
|
@@ -59,7 +59,15 @@
|
|
|
59
59
|
"types": "./dist/reporters/journald-reporter.d.ts",
|
|
60
60
|
"default": "./dist/reporters/journald-reporter.js"
|
|
61
61
|
},
|
|
62
|
-
"./package.json": "./package.json"
|
|
62
|
+
"./package.json": "./package.json",
|
|
63
|
+
"./repository-fake": {
|
|
64
|
+
"types": "./dist/fakes/repository-fake.d.ts",
|
|
65
|
+
"default": "./dist/fakes/repository-fake.js"
|
|
66
|
+
},
|
|
67
|
+
"./repository-test": {
|
|
68
|
+
"types": "./dist/repository-test.d.ts",
|
|
69
|
+
"default": "./dist/repository-test.js"
|
|
70
|
+
}
|
|
63
71
|
},
|
|
64
72
|
"files": [
|
|
65
73
|
"README.md",
|
|
@@ -69,13 +77,13 @@
|
|
|
69
77
|
"access": "public"
|
|
70
78
|
},
|
|
71
79
|
"peerDependencies": {
|
|
72
|
-
"vitest": "^5.0.
|
|
80
|
+
"vitest": "^5.0.5"
|
|
73
81
|
},
|
|
74
82
|
"devDependencies": {
|
|
75
|
-
"@types/node": "^26.6.
|
|
76
|
-
"@zyplux/tsconfig": "0.3.
|
|
83
|
+
"@types/node": "^26.6.5",
|
|
84
|
+
"@zyplux/tsconfig": "0.3.3",
|
|
77
85
|
"publint": "^0.3.25",
|
|
78
|
-
"vitest": "^5.0.
|
|
86
|
+
"vitest": "^5.0.5"
|
|
79
87
|
},
|
|
80
88
|
"imports": {
|
|
81
89
|
"#pattern-match": "./dist/helpers/pattern-match.js",
|