cabloy 5.1.201 → 5.1.203
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/.cabloy-version +1 -1
- package/CHANGELOG.md +17 -0
- package/CONTRIBUTING.md +18 -17
- package/README.md +2 -2
- package/package.json +2 -3
- package/repo-agent-governance/managed-assets.json +33 -33
- package/repo-agent-governance/policies/repository.md +1 -0
- package/repo-agent-governance/skills/cabloy-spec-execution/SKILL.md +3 -2
- package/repo-agent-governance/skills/cabloy-spec-execution/references/execution-protocol.md +1 -1
- package/repo-agent-governance/skills/cabloy-spec-execution/references/status-and-evidence.md +11 -10
- package/repo-agent-governance/skills/cabloy-spec-generation/SKILL.md +1 -1
- package/repo-agent-governance/skills/cabloy-spec-generation/references/canonical-spec-input.md +4 -2
- package/repo-agent-governance/skills/cabloy-spec-generation/references/repo-specs-document-set.md +16 -13
- package/repo-agent-governance/skills/cabloy-spec-generation/references/traceability-and-status-rules.md +18 -11
- package/repo-agent-governance/skills/cabloy-workflow/SKILL.md +2 -2
- package/repo-agent-governance/skills/cabloy-workflow/evals/evals.json +1 -1
- package/repo-agent-governance/skills/cabloy-worktree-environment/SKILL.md +5 -5
- package/repo-agent-governance/tests/spec-audit.test.mjs +323 -0
- package/repo-agent-governance/tools/spec-audit/audit.mjs +129 -3
- package/repo-agent-governance/tools/spec-charts/generate-implementation-charts.mjs +26 -7
- package/repo-agent-governance/tools/spec-charts/generate-implementation-charts.test.mjs +82 -0
- package/repo-agent-governance/tools/spec-charts/spec-parser.mjs +40 -1
- package/repo-docs/ai/ai-spec-driven-development.md +1 -0
- package/repo-docs/ai/playbook-spec-execution.md +3 -2
- package/repo-docs/ai/playbook-spec-generation.md +1 -1
- package/repo-docs/assets/img/cabloy-admin-ssr-cover-en.png +0 -0
- package/repo-docs/assets/img/cabloy-admin-tabs-cover-en.png +0 -0
- package/repo-docs/assets/img/cabloy-ai-crud-cover-en.png +0 -0
- package/repo-docs/demo.md +14 -0
- package/repo-docs/frontend/scripts.md +10 -18
- package/repo-docs/fullstack/parallel-worktree-environment.md +2 -2
- package/repo-docs/index.md +2 -2
- package/repo-docs/reference/repo-scripts.md +15 -31
- package/repo-e2e/config/playwright.config.ts +14 -19
- package/repo-e2e/scripts/e2e.test.ts +17 -0
- package/repo-e2e/scripts/e2e.ts +5 -1
- package/repo-e2e/scripts/runE2e.ts +3 -15
- package/repo-e2e/scripts/runE2eArgs.test.ts +21 -17
- package/repo-e2e/scripts/runE2eArgs.ts +16 -29
- package/repo-e2e/scripts/startE2eVona.ts +20 -9
- package/repo-e2e/specs/a-commerce.spec.ts +232 -31
- package/repo-e2e/specs/cabloy-basic.spec.ts +39 -12
- package/repo-e2e/specs/home-user-account.spec.ts +9 -2
- package/scripts/upgrade.test.ts +147 -0
- package/scripts/upgrade.ts +49 -27
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.dev.ts +51 -5
- package/vona/packages-cli/cli-set-api/src/lib/command/bin.dev.ts +6 -1
- package/vona/packages-cli/cli-set-api/test/binDevMode.test.ts +52 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +41 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/e2eFixture.ts +52 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/lib/e2eFixture.ts +224 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/e2eFixture.ts +694 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/e2eFixture.test.ts +434 -0
|
@@ -34,16 +34,7 @@ async function assertPortAvailable(): Promise<void> {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const parsed = parseE2eArgs(process.argv.slice(2), E2E_SPECS_DIR);
|
|
37
|
-
|
|
38
|
-
throw new Error(
|
|
39
|
-
'The clean E2E run manages the local target and cannot be used with E2E_BASE_URL. Use test:e2e:fast for an external target.',
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (parsed.mode === 'clean') {
|
|
44
|
-
await assertPortAvailable();
|
|
45
|
-
run('npm', ['run', 'db:reset']);
|
|
46
|
-
}
|
|
37
|
+
await assertPortAvailable();
|
|
47
38
|
|
|
48
39
|
const playwrightCommand = resolve(
|
|
49
40
|
E2E_ROOT_DIR,
|
|
@@ -55,8 +46,5 @@ const specPaths = parsed.specNames.map(name => resolve(E2E_SPECS_DIR, `${name}.s
|
|
|
55
46
|
const playwrightArgs = combineGreps(parsed.playwrightArgs, parsed.tags);
|
|
56
47
|
run(playwrightCommand, ['test', '--config', E2E_CONFIG_FILE, ...specPaths, ...playwrightArgs]);
|
|
57
48
|
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
// eslint-disable-next-line no-console
|
|
61
|
-
console.log(`E2E target: ${E2E_LOCAL_BASE_URL}`);
|
|
62
|
-
}
|
|
49
|
+
// eslint-disable-next-line no-console
|
|
50
|
+
console.log(`E2E target: ${E2E_LOCAL_BASE_URL}`);
|
|
@@ -20,15 +20,11 @@ function withSpecs(callback: (specsDir: string) => void): void {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
test('parses
|
|
23
|
+
test('parses multiple specs and repeatable tags', () => {
|
|
24
24
|
withSpecs(specsDir => {
|
|
25
25
|
assert.deepEqual(
|
|
26
|
-
parseE2eArgs(
|
|
27
|
-
['--clean', 'cabloy-basic', 'account', '--tag', '@web', '--tag=@smoke'],
|
|
28
|
-
specsDir,
|
|
29
|
-
),
|
|
26
|
+
parseE2eArgs(['cabloy-basic', 'account', '--tag', '@web', '--tag=@smoke'], specsDir),
|
|
30
27
|
{
|
|
31
|
-
mode: 'clean',
|
|
32
28
|
specNames: ['cabloy-basic', 'account'],
|
|
33
29
|
playwrightArgs: [],
|
|
34
30
|
tags: ['@web', '@smoke'],
|
|
@@ -39,8 +35,7 @@ test('parses mode, multiple specs, and repeatable tags', () => {
|
|
|
39
35
|
|
|
40
36
|
test('allows no spec names and forwards Playwright filters', () => {
|
|
41
37
|
withSpecs(specsDir => {
|
|
42
|
-
assert.deepEqual(parseE2eArgs(['--
|
|
43
|
-
mode: 'fast',
|
|
38
|
+
assert.deepEqual(parseE2eArgs(['--grep', '@flow'], specsDir), {
|
|
44
39
|
specNames: [],
|
|
45
40
|
playwrightArgs: ['--grep', '@flow'],
|
|
46
41
|
tags: [],
|
|
@@ -50,19 +45,28 @@ test('allows no spec names and forwards Playwright filters', () => {
|
|
|
50
45
|
|
|
51
46
|
test('rejects invalid or unknown specs and managed config overrides', () => {
|
|
52
47
|
withSpecs(specsDir => {
|
|
53
|
-
assert.throws(() => parseE2eArgs(['
|
|
54
|
-
assert.throws(() => parseE2eArgs(['
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
assert.throws(() => parseE2eArgs(['../account'], specsDir), /Invalid E2E spec name/);
|
|
49
|
+
assert.throws(() => parseE2eArgs(['missing'], specsDir), /Unknown E2E spec/);
|
|
50
|
+
for (const args of [
|
|
51
|
+
['--config', 'other.ts'],
|
|
52
|
+
['--config=other.ts'],
|
|
53
|
+
['-c', 'other.ts'],
|
|
54
|
+
['-c=other.ts'],
|
|
55
|
+
['-cother.ts'],
|
|
56
|
+
]) {
|
|
57
|
+
assert.throws(() => parseE2eArgs(args, specsDir), /manages --config/);
|
|
58
|
+
}
|
|
59
|
+
for (const args of [['--workers', '2'], ['--workers=2'], ['-j', '2'], ['-j=2'], ['-j2']]) {
|
|
60
|
+
assert.throws(() => parseE2eArgs(args, specsDir), /manages --workers/);
|
|
61
|
+
}
|
|
59
62
|
});
|
|
60
63
|
});
|
|
61
64
|
|
|
62
|
-
test('
|
|
65
|
+
test('rejects removed clean and fast options', () => {
|
|
63
66
|
withSpecs(specsDir => {
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
for (const option of ['--clean', '--fast', '--clean=true', '--fast=false']) {
|
|
68
|
+
assert.throws(() => parseE2eArgs(['account', option], specsDir), /Unsupported E2E option/);
|
|
69
|
+
}
|
|
66
70
|
});
|
|
67
71
|
});
|
|
68
72
|
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
|
|
4
|
-
export type E2eRunMode = 'clean' | 'fast';
|
|
5
|
-
|
|
6
4
|
export type ParsedE2eArgs = {
|
|
7
|
-
mode: E2eRunMode;
|
|
8
5
|
specNames: string[];
|
|
9
6
|
playwrightArgs: string[];
|
|
10
7
|
tags: string[];
|
|
@@ -28,7 +25,6 @@ const VALUE_OPTIONS = new Set([
|
|
|
28
25
|
'--shard',
|
|
29
26
|
'--timeout',
|
|
30
27
|
'--trace',
|
|
31
|
-
'--workers',
|
|
32
28
|
]);
|
|
33
29
|
|
|
34
30
|
function optionName(arg: string): string {
|
|
@@ -46,35 +42,23 @@ export function buildTagGrep(tags: string[]): string | undefined {
|
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
export function parseE2eArgs(args: string[], specsDir: string): ParsedE2eArgs {
|
|
49
|
-
let mode: E2eRunMode | undefined;
|
|
50
|
-
const remainingArgs: string[] = [];
|
|
51
|
-
|
|
52
|
-
for (const arg of args) {
|
|
53
|
-
if (arg === '--clean' || arg === '--fast') {
|
|
54
|
-
if (mode && mode !== arg.slice(2)) {
|
|
55
|
-
throw new Error('Choose exactly one E2E mode: --clean or --fast.');
|
|
56
|
-
}
|
|
57
|
-
if (mode === arg.slice(2)) {
|
|
58
|
-
throw new Error(`E2E mode ${arg} was provided more than once.`);
|
|
59
|
-
}
|
|
60
|
-
mode = arg.slice(2) as E2eRunMode;
|
|
61
|
-
} else {
|
|
62
|
-
remainingArgs.push(arg);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!mode) {
|
|
67
|
-
throw new Error('Missing E2E mode. Use --clean or --fast.');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
45
|
const specNames: string[] = [];
|
|
71
46
|
const playwrightArgs: string[] = [];
|
|
72
47
|
const tags: string[] = [];
|
|
73
48
|
let optionStarted = false;
|
|
74
49
|
let pendingValueFor: string | undefined;
|
|
75
50
|
|
|
76
|
-
for (let index = 0; index <
|
|
77
|
-
const arg =
|
|
51
|
+
for (let index = 0; index < args.length; index++) {
|
|
52
|
+
const arg = args[index];
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
arg === '--clean' ||
|
|
56
|
+
arg.startsWith('--clean=') ||
|
|
57
|
+
arg === '--fast' ||
|
|
58
|
+
arg.startsWith('--fast=')
|
|
59
|
+
) {
|
|
60
|
+
throw new Error(`Unsupported E2E option: ${arg}. Use the single managed test:e2e command.`);
|
|
61
|
+
}
|
|
78
62
|
|
|
79
63
|
if (pendingValueFor) {
|
|
80
64
|
if (!arg || arg.startsWith('--')) {
|
|
@@ -100,9 +84,12 @@ export function parseE2eArgs(args: string[], specsDir: string): ParsedE2eArgs {
|
|
|
100
84
|
continue;
|
|
101
85
|
}
|
|
102
86
|
|
|
103
|
-
if (arg === '--config' || arg.startsWith('--config=')) {
|
|
87
|
+
if (arg === '--config' || arg.startsWith('--config=') || arg.startsWith('-c')) {
|
|
104
88
|
throw new Error('The E2E runner manages --config; do not provide it.');
|
|
105
89
|
}
|
|
90
|
+
if (arg === '--workers' || arg.startsWith('--workers=') || arg.startsWith('-j')) {
|
|
91
|
+
throw new Error('The E2E runner manages --workers; do not provide it.');
|
|
92
|
+
}
|
|
106
93
|
|
|
107
94
|
if (!optionStarted && !arg.startsWith('-')) {
|
|
108
95
|
if (!SPEC_NAME_PATTERN.test(arg)) {
|
|
@@ -133,7 +120,7 @@ export function parseE2eArgs(args: string[], specsDir: string): ParsedE2eArgs {
|
|
|
133
120
|
throw new Error(`Missing value for ${pendingValueFor}.`);
|
|
134
121
|
}
|
|
135
122
|
|
|
136
|
-
return {
|
|
123
|
+
return { specNames, playwrightArgs, tags };
|
|
137
124
|
}
|
|
138
125
|
|
|
139
126
|
export function combineGreps(args: string[], tags: string[]): string[] {
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
import type { ChildProcess } from 'node:child_process';
|
|
2
2
|
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
4
5
|
|
|
5
6
|
import { E2E_LOCAL_BASE_URL, E2E_ROOT_DIR } from './e2e.ts';
|
|
6
7
|
|
|
7
|
-
const child: ChildProcess = spawn(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
const child: ChildProcess = spawn(
|
|
9
|
+
process.execPath,
|
|
10
|
+
[
|
|
11
|
+
'./packages-cli/cli/src/bin/vona.ts',
|
|
12
|
+
':bin:dev',
|
|
13
|
+
'--workers=1',
|
|
14
|
+
'--flavor=normal',
|
|
15
|
+
'--mode=test',
|
|
16
|
+
],
|
|
17
|
+
{
|
|
18
|
+
cwd: resolve(E2E_ROOT_DIR, 'vona'),
|
|
19
|
+
detached: process.platform !== 'win32',
|
|
20
|
+
stdio: 'inherit',
|
|
21
|
+
env: {
|
|
22
|
+
...process.env,
|
|
23
|
+
SERVER_SERVE_PROTOCOL: 'http',
|
|
24
|
+
SERVER_SERVE_HOST: E2E_LOCAL_BASE_URL.replace(/^https?:\/\//, ''),
|
|
25
|
+
},
|
|
15
26
|
},
|
|
16
|
-
|
|
27
|
+
);
|
|
17
28
|
|
|
18
29
|
const gracefulShutdownTimeout = 7000;
|
|
19
30
|
|