@sun-asterisk/sungen 3.2.10-beta.6 → 3.2.10
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/capabilities/discover.d.ts.map +1 -1
- package/dist/capabilities/discover.js +2 -5
- package/dist/capabilities/discover.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +2 -44
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/harness/capability.d.ts +0 -1
- package/dist/harness/capability.d.ts.map +1 -1
- package/dist/harness/capability.js.map +1 -1
- package/dist/harness/catalog/drivers.yaml +1 -2
- package/dist/harness/query-catalog.d.ts +2 -32
- package/dist/harness/query-catalog.d.ts.map +1 -1
- package/dist/harness/query-catalog.js +0 -0
- package/dist/harness/query-catalog.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +0 -1
- package/dist/orchestrator/templates/ai-src/config/claude.md +1 -3
- package/dist/orchestrator/templates/ai-src/config/copilot.md +1 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +5 -15
- package/dist/orchestrator/templates/specs-db.d.ts +11 -201
- package/dist/orchestrator/templates/specs-db.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-db.js +91 -440
- package/dist/orchestrator/templates/specs-db.js.map +1 -1
- package/dist/orchestrator/templates/specs-db.ts +79 -463
- package/dist/orchestrator/templates/specs-test-data.ts +3 -39
- package/package.json +4 -8
- package/src/capabilities/discover.ts +2 -5
- package/src/cli/commands/capability.ts +2 -40
- package/src/harness/capability.ts +0 -1
- package/src/harness/catalog/drivers.yaml +1 -2
- package/src/harness/query-catalog.ts +0 -0
- package/src/index.ts +2 -2
- package/src/orchestrator/templates/ai-src/commands/run-test.md +0 -1
- package/src/orchestrator/templates/ai-src/config/claude.md +1 -3
- package/src/orchestrator/templates/ai-src/config/copilot.md +1 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +5 -15
- package/src/orchestrator/templates/specs-db.ts +79 -463
- package/src/orchestrator/templates/specs-test-data.ts +3 -39
- package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +0 -85
- package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +0 -101
- package/src/orchestrator/templates/ai-src/commands/create-data-test.md +0 -85
- package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +0 -101
|
@@ -48,27 +48,9 @@ export class TestDataLoader {
|
|
|
48
48
|
if (value === undefined || value === null) {
|
|
49
49
|
throw new Error(`Test data key not found: ${key}`);
|
|
50
50
|
}
|
|
51
|
-
// A projected array (e.g. `{{q.rows[*].name}}`) serializes as a JSON list so the full
|
|
52
|
-
// ordered set is preserved for the assert-compare — String([...]) would flatten it to a
|
|
53
|
-
// lossy comma-join that can't be distinguished from a scalar containing commas.
|
|
54
|
-
if (Array.isArray(value)) return this.interpolate(JSON.stringify(value));
|
|
55
51
|
return this.interpolate(String(value));
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
/**
|
|
59
|
-
* Resolve a key to its RAW, uncoerced value (array/object/number kept as-is) — for binding
|
|
60
|
-
* DB query parameters, where type-strict stores (MySQL numeric columns, MongoDB) require the
|
|
61
|
-
* native type. get() stringifies for Gherkin text; raw() must not. Throws on missing, mirroring
|
|
62
|
-
* get()'s guard.
|
|
63
|
-
*/
|
|
64
|
-
raw(key: string): any {
|
|
65
|
-
const value = this.resolve(key);
|
|
66
|
-
if (value === undefined || value === null) {
|
|
67
|
-
throw new Error(`Test data key not found: ${key}`);
|
|
68
|
-
}
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
54
|
/**
|
|
73
55
|
* Substitute embedded `{{ref}}` cross-references inside a resolved string value.
|
|
74
56
|
* Each ref is looked up via resolve() (flat key, @cases row column, or dotted path),
|
|
@@ -96,7 +78,6 @@ export class TestDataLoader {
|
|
|
96
78
|
* `q.count` / `q.length` → number of rows
|
|
97
79
|
* `q.first.col` / `q.last.col` / `q[2].col` → a specific row's column
|
|
98
80
|
* `q.col` → shorthand for the first row's column
|
|
99
|
-
* `q.rows[*].col` → the `col` of EVERY row, projected to an ordered array
|
|
100
81
|
*/
|
|
101
82
|
private resolve(key: string): any {
|
|
102
83
|
// 1. Exact flat key — captured vars (set()) live under a literal, possibly dotted, key.
|
|
@@ -107,26 +88,9 @@ export class TestDataLoader {
|
|
|
107
88
|
return this.data[key];
|
|
108
89
|
}
|
|
109
90
|
// 2. Structured path: head from the row (cases) or shared data, then walk segments.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return TestDataLoader.walk(cur, tokens, 1);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Walk the remaining path tokens from `i`. A `*` token on an array projects: the rest of the
|
|
118
|
-
* path is mapped over every element and the collected array returned (recurses for nested `[*]`).
|
|
119
|
-
* Non-`*` tokens use the linear single-step navigation, so numeric/first/last/object access is
|
|
120
|
-
* unchanged.
|
|
121
|
-
*/
|
|
122
|
-
private static walk(cur: any, tokens: string[], i: number): any {
|
|
123
|
-
for (; i < tokens.length && cur != null; i++) {
|
|
124
|
-
if (tokens[i] === '*' && Array.isArray(cur)) {
|
|
125
|
-
const rest = tokens.slice(i + 1);
|
|
126
|
-
return cur.map((el) => TestDataLoader.walk(el, rest, 0));
|
|
127
|
-
}
|
|
128
|
-
cur = TestDataLoader.step(cur, tokens[i]);
|
|
129
|
-
}
|
|
91
|
+
const tokens = String(key).replace(/\[(\d+)\]/g, '.$1').split('.');
|
|
92
|
+
let cur: any = (this.row && tokens[0] in this.row) ? this.row[tokens[0]] : this.data[tokens[0]];
|
|
93
|
+
for (let i = 1; i < tokens.length && cur != null; i++) cur = TestDataLoader.step(cur, tokens[i]);
|
|
130
94
|
return cur;
|
|
131
95
|
}
|
|
132
96
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sungen",
|
|
3
|
-
"version": "3.2.10
|
|
3
|
+
"version": "3.2.10",
|
|
4
4
|
"description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && mkdir -p dist/orchestrator/templates && mkdir -p dist/dashboard/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && mkdir -p dist/generators/test-generator/adapters/appium/templates/steps && cp -r src/generators/test-generator/adapters/appium/templates/*.hbs dist/generators/test-generator/adapters/appium/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/appium/templates/steps dist/generators/test-generator/adapters/appium/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true && cp -r src/orchestrator/templates/* dist/orchestrator/templates/ && cp src/dashboard/templates/index.html dist/dashboard/templates/index.html && mkdir -p dist/harness/catalog && cp src/harness/catalog/*.yaml dist/harness/catalog/",
|
|
13
13
|
"build:dashboard": "cd ../../dashboard && npm install --silent && npm run build && cd - && cp ../../dashboard/dist/index.html src/dashboard/templates/index.html",
|
|
14
14
|
"dev": "tsx src/cli/index.ts",
|
|
15
|
-
"test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/exporter/overview-sheet.run.ts && tsx tests/exporter/delivery-divider-tcid.run.ts && tsx tests/exporter/note-expected-actual.run.ts && tsx tests/exporter/test-data-vars.run.ts && tsx tests/exporter/mobile-app-id.run.ts && tsx tests/exporter/api-testcase-cells.run.ts && tsx tests/exporter/delivery-cases-result.run.ts && tsx tests/exporter/precondition-auth-role.run.ts && tsx tests/exporter/report-builder-parity.run.ts && tsx tests/exporter/delivery-preflight-testdata.run.ts && tsx tests/exporter/delivery-preflight-selectors.run.ts && tsx tests/dashboard/api-flows-discovery.run.ts && tsx tests/dashboard/status-results-fallback.run.ts && tsx tests/codegen/verb-ref-masking.run.ts && tsx tests/codegen/table-aria-hidden-locators.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/api-runtime/file-upload-multipart.run.ts && tsx tests/api-runtime/schema-assertion.run.ts && tsx tests/runtime/test-data-cross-ref.run.ts && tsx tests/
|
|
15
|
+
"test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/exporter/overview-sheet.run.ts && tsx tests/exporter/delivery-divider-tcid.run.ts && tsx tests/exporter/note-expected-actual.run.ts && tsx tests/exporter/test-data-vars.run.ts && tsx tests/exporter/mobile-app-id.run.ts && tsx tests/exporter/api-testcase-cells.run.ts && tsx tests/exporter/delivery-cases-result.run.ts && tsx tests/exporter/precondition-auth-role.run.ts && tsx tests/exporter/report-builder-parity.run.ts && tsx tests/exporter/delivery-preflight-testdata.run.ts && tsx tests/exporter/delivery-preflight-selectors.run.ts && tsx tests/dashboard/api-flows-discovery.run.ts && tsx tests/dashboard/status-results-fallback.run.ts && tsx tests/codegen/verb-ref-masking.run.ts && tsx tests/codegen/table-aria-hidden-locators.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/api-runtime/file-upload-multipart.run.ts && tsx tests/api-runtime/schema-assertion.run.ts && tsx tests/runtime/test-data-cross-ref.run.ts && tsx tests/capabilities/run.ts && tsx tests/openapi/run.ts && tsx tests/api-field-coverage/run.ts && tsx tests/packaging/run.ts && tsx tests/generate-hint/run.ts && tsx tests/template-assertion/run.ts && tsx tests/journey/run.ts && tsx tests/harness/serial-cascade.run.ts && tsx tests/ai-skills/cross-assistant-orphan.run.ts && tsx tests/init/codex-mcp-config.run.ts && tsx src/orchestrator/ai-skills/golden-skills.test.ts && tsx tests/db-runtime/sql-placeholder-rewrite.run.ts && tsx tests/db-runtime/fallback-flow-codified.run.ts && tsx tests/db-runtime/mysql-integration.opt-in.run.ts",
|
|
16
16
|
"test:update": "tsx tests/golden/run.ts --update && tsx tests/audit/run.ts --update && tsx tests/ingest/run.ts --update",
|
|
17
17
|
"prepublishOnly": "npm run build:dashboard && npm run build"
|
|
18
18
|
},
|
|
@@ -39,8 +39,7 @@
|
|
|
39
39
|
"@babel/types": "^7.28.5",
|
|
40
40
|
"@cucumber/gherkin": "^37.0.0",
|
|
41
41
|
"@cucumber/messages": "^31.0.0",
|
|
42
|
-
"@sungen/driver-
|
|
43
|
-
"@sungen/driver-ui": "3.2.10-beta.6",
|
|
42
|
+
"@sungen/driver-ui": "3.2.10",
|
|
44
43
|
"chalk": "^5.6.2",
|
|
45
44
|
"commander": "^14.0.2",
|
|
46
45
|
"dotenv": "^17.2.3",
|
|
@@ -66,9 +65,6 @@
|
|
|
66
65
|
"LICENSE"
|
|
67
66
|
],
|
|
68
67
|
"devDependencies": {
|
|
69
|
-
"mysql2": "^3"
|
|
70
|
-
"mongodb": "^7",
|
|
71
|
-
"@aws-sdk/client-dynamodb": "^3",
|
|
72
|
-
"@aws-sdk/lib-dynamodb": "^3"
|
|
68
|
+
"mysql2": "^3"
|
|
73
69
|
}
|
|
74
70
|
}
|
|
@@ -27,7 +27,7 @@ import { LOCAL_DRIVERS, registerCoreCapability } from './builtins';
|
|
|
27
27
|
* move to their packages (R5.5/R5.6) they join this list and leave `LOCAL_DRIVERS`; eventually this
|
|
28
28
|
* becomes a scan of installed `@sungen/driver-*` packages (R5.7).
|
|
29
29
|
*/
|
|
30
|
-
const EXTERNAL_DRIVERS = ['@sungen/driver-ui', '@sungen/driver-db', '@sungen/driver-api', '@sungen/driver-mobile'
|
|
30
|
+
const EXTERNAL_DRIVERS = ['@sungen/driver-ui', '@sungen/driver-db', '@sungen/driver-api', '@sungen/driver-mobile'];
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Bundled (non-opt-in) drivers: shipped as a core dependency (`drivers.yaml` `bundled: true`), so the
|
|
@@ -36,10 +36,7 @@ const EXTERNAL_DRIVERS = ['@sungen/driver-ui', '@sungen/driver-db', '@sungen/dri
|
|
|
36
36
|
* symlink for the package is missing). Surface it LOUDLY: otherwise the entire UI step vocabulary is
|
|
37
37
|
* absent and every step silently compiles to "Unrecognized step pattern" with no clue why.
|
|
38
38
|
*/
|
|
39
|
-
|
|
40
|
-
// codegen), so shipping it by default adds no runtime, unlike ui/db/api/mobile. It makes standardized
|
|
41
|
-
// test-data available out of the box (no `capability add` needed); inert until a field-map/recipe exists.
|
|
42
|
-
const BUNDLED_DRIVERS = new Set(['@sungen/driver-ui', '@sungen/driver-data-factory']);
|
|
39
|
+
const BUNDLED_DRIVERS = new Set(['@sungen/driver-ui']);
|
|
43
40
|
|
|
44
41
|
function loadExternalDriver(name: string): void {
|
|
45
42
|
// Resolve from the user's PROJECT first, then from core's own location. Opt-in drivers
|
|
@@ -15,36 +15,6 @@ function coreVersion(): string {
|
|
|
15
15
|
try { return require('../../../package.json').version || 'latest'; } catch { return 'latest'; }
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* Resolve the driver version to install for the running core `core`.
|
|
20
|
-
*
|
|
21
|
-
* Lockstep wants the EXACT `core` version. But the beta channel publishes `<core>-beta.N` before a
|
|
22
|
-
* stable `<core>` ever exists — so a released core (e.g. 3.2.10) asking for `@3.2.10` hits ETARGET
|
|
23
|
-
* out-of-the-box when npm only has `3.2.10-beta.2` (`sungen capability add` then fails for a fresh
|
|
24
|
-
* user). Fall back on the SAME version line: prefer exact `core`, else the highest `core-beta.N`,
|
|
25
|
-
* else the `beta` dist-tag as a last resort. Keeps the driver's pinned core-dep on the same line.
|
|
26
|
-
*/
|
|
27
|
-
function resolveDriverVersion(pkg: string, core: string): string {
|
|
28
|
-
if (core === 'latest') return 'beta';
|
|
29
|
-
try {
|
|
30
|
-
const out = spawnSync('npm', ['view', pkg, 'versions', '--json'], { encoding: 'utf-8', shell: true });
|
|
31
|
-
if (out.status !== 0 || !out.stdout) return core; // can't query → let the exact install try/fail
|
|
32
|
-
let versions: string[] = [];
|
|
33
|
-
const parsed = JSON.parse(out.stdout);
|
|
34
|
-
versions = Array.isArray(parsed) ? parsed : [parsed];
|
|
35
|
-
if (versions.includes(core)) return core; // exact exists → lockstep
|
|
36
|
-
const prefix = `${core}-beta.`;
|
|
37
|
-
const betas = versions
|
|
38
|
-
.filter((v) => typeof v === 'string' && v.startsWith(prefix))
|
|
39
|
-
.map((v) => parseInt(v.slice(prefix.length), 10))
|
|
40
|
-
.filter((n) => Number.isInteger(n));
|
|
41
|
-
if (betas.length) return `${core}-beta.${Math.max(...betas)}`; // highest same-line prerelease
|
|
42
|
-
return 'beta'; // nothing on this line → newest beta dist-tag
|
|
43
|
-
} catch {
|
|
44
|
-
return core;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
18
|
/**
|
|
49
19
|
* Add `test:mobile` to the project's package.json scripts (if absent).
|
|
50
20
|
* Idempotent — skips if the script is already set.
|
|
@@ -217,22 +187,14 @@ export function registerCapabilityCommand(program: Command): void {
|
|
|
217
187
|
}
|
|
218
188
|
|
|
219
189
|
const adapterName = meta.adapter || meta.id;
|
|
220
|
-
|
|
221
|
-
// as a core dependency (`drivers.yaml` bundled: true, e.g. data-factory) — present already,
|
|
222
|
-
// so no install/verify: `capability add` on it just records it in the profile.
|
|
223
|
-
const bundled = adapterRegistry.hasAdapter(adapterName) || meta.bundled === true;
|
|
190
|
+
const bundled = adapterRegistry.hasAdapter(adapterName); // e.g. `web` is bundled in Phase 2a
|
|
224
191
|
|
|
225
192
|
if (!bundled && !o.skipInstall) {
|
|
226
193
|
// Pin the driver to the RUNNING core's exact version (lockstep). Without this, plain
|
|
227
194
|
// `npm install @sungen/driver-x` resolves the package's `latest` dist-tag — which on a
|
|
228
195
|
// beta channel can lag (e.g. a prerelease first-published-as-latest), pulling a driver
|
|
229
196
|
// whose core dep resolves to the wrong @sun-asterisk/sungen and fails to load.
|
|
230
|
-
const
|
|
231
|
-
const target = resolveDriverVersion(meta.package, core); // exact core, else same-line beta, else 'beta' tag
|
|
232
|
-
const spec = `${meta.package}@${target}`;
|
|
233
|
-
if (target !== core) {
|
|
234
|
-
console.log(`ℹ ${meta.package}@${core} is not published yet — installing ${spec} (closest on the ${core} line).`);
|
|
235
|
-
}
|
|
197
|
+
const spec = `${meta.package}@${coreVersion()}`;
|
|
236
198
|
console.log(`📦 Installing ${spec} (dev dependency)...`);
|
|
237
199
|
const r = spawnSync('npm', ['install', '-D', spec], { stdio: 'inherit', shell: true });
|
|
238
200
|
if (r.status !== 0) throw new Error(`npm install -D ${spec} failed.`);
|
|
@@ -24,7 +24,6 @@ export interface DriverMeta {
|
|
|
24
24
|
package: string;
|
|
25
25
|
runtime?: string;
|
|
26
26
|
adapter?: string; // registry adapter name (defaults to id)
|
|
27
|
-
bundled?: boolean; // shipped as a core dependency — present without `capability add`
|
|
28
27
|
capabilities: string[];
|
|
29
28
|
unblocks?: string[];
|
|
30
29
|
}
|
|
@@ -51,8 +51,7 @@ drivers:
|
|
|
51
51
|
data-factory:
|
|
52
52
|
kind: capability
|
|
53
53
|
package: "@sungen/driver-data-factory"
|
|
54
|
-
status:
|
|
55
|
-
bundled: true # runtime-free knowledge+codegen — shipped by default (no `capability add` needed)
|
|
54
|
+
status: planned
|
|
56
55
|
capabilities: ["@dataFactory"]
|
|
57
56
|
unblocks: [M1]
|
|
58
57
|
mock:
|
|
Binary file
|
package/src/index.ts
CHANGED
|
@@ -25,8 +25,8 @@ export { getPathCode, inferPath, resolvePathVariables } from './generators/test-
|
|
|
25
25
|
export { parseQueryOverrides } from './harness/annotation-overrides';
|
|
26
26
|
|
|
27
27
|
// --- Named-query catalog (shared: the DB driver's codegen + core's data-driven advisory lint) ---
|
|
28
|
-
export { resolveQuery, compileQuery,
|
|
29
|
-
export type { QueryEntry
|
|
28
|
+
export { resolveQuery, compileQuery, lintCatalog } from './harness/query-catalog';
|
|
29
|
+
export type { QueryEntry } from './harness/query-catalog';
|
|
30
30
|
|
|
31
31
|
// --- Shared harness: viewpoint catalog + coverage gate / assertion depth ---
|
|
32
32
|
// (the UI capability's gateProvider composes these; they also back core's ingest + audit fallback)
|
|
@@ -127,7 +127,6 @@ If the unit is **api-first**, skip every selector/capture phase (an API test has
|
|
|
127
127
|
## Pre-run (phased — per `sungen-selector-fix` skill)
|
|
128
128
|
|
|
129
129
|
1. Verify `<base>/<name>/` has `.feature` + `test-data.yaml`. **If the feature has `@query` steps**, check the resolved datasource `engine` in `qa/datasources.yaml` — unsupported engine → follow the `sungen-gherkin-syntax` skill § "Unsupported DB engine — fallback" before compiling (do not attempt a direct connect).
|
|
130
|
-
1.5. **Data Factory pre-check (before running)** — Data Factory is bundled with core, so whenever a field-map exists (`qa/data-factory/<name>.fields.yaml`), confirm the test-data is standardized and covers each field before you run: `[ -x ./bin/sungen.js ] && ./bin/sungen.js data lint --screen <name> || npx sungen data lint --screen <name>`. If it reports coverage gaps (missing boundary/invalid, unresolved values, unmapped error codes) or the field-map is newer than `test-data/<name>.yaml`, refresh via `/sungen:create-data-test <name>` (or `sungen data gen --screen <name>`) — then compile. Blind spots (a `type` not in the catalog) are for the QA to answer, not to invent. No field-map / driver disabled → skip this step.
|
|
131
130
|
2. **Phase 0 — Selector Pre-gen**: if `selectors.yaml` is missing/empty or doesn't cover the feature file's `[Reference]`s, apply the following decision tree before running Phase 0 from `sungen-selector-fix`:
|
|
132
131
|
|
|
133
132
|
```
|
|
@@ -18,16 +18,14 @@ You generate 3 files for sungen — a Gherkin compiler that produces Playwright
|
|
|
18
18
|
| `sungen-delivery` | Export Gherkin + Playwright results → CSV test case deliverable |
|
|
19
19
|
| `sungen-capture` | Acquire visual/design context — one skill, 4 modes: figma-mcp (Dev Mode MCP), figma-pat (URL → spec_figma.md), live (Playwright MCP scan), local (images in `requirements/ui/`) |
|
|
20
20
|
| `sungen-locale` | Bootstrap i18n — audit selectors, detect locale switch mechanism, generate test-data overlay |
|
|
21
|
-
| `sungen-data-factory` | Standardized test-data — field-type/security catalog, 4-source method, `.overwrite`, blind-spot prompts |
|
|
22
21
|
|
|
23
|
-
## Workflow (
|
|
22
|
+
## Workflow (7 AI commands)
|
|
24
23
|
|
|
25
24
|
| Command | What it does |
|
|
26
25
|
|---|---|
|
|
27
26
|
| `/sungen:add-screen <name> <path>` | Scaffold `qa/screens/<name>/` directories |
|
|
28
27
|
| `/sungen:add-flow <name> [--path <url>]` | Scaffold `qa/flows/<name>/` directories for E2E cross-screen testing |
|
|
29
28
|
| `/sungen:create-test <name>` | Generate `.feature` + `test-data.yaml` (auto-detects screen or flow) |
|
|
30
|
-
| `/sungen:create-data-test <name>` | Generate standardized test-data (valid/boundary/invalid + CHK trace) from the Data Factory catalog; no name = all units (asks to confirm) |
|
|
31
29
|
| `/sungen:review <name>` | Score syntax, coverage, viewpoint quality (auto-detects screen or flow) |
|
|
32
30
|
| `/sungen:run-test <name>` | Generate `selectors.yaml`, compile, run, auto-fix (auto-detects screen or flow) |
|
|
33
31
|
| `/sungen:delivery [name...]` | Export test cases → CSV for QA delivery (all screens if no arg) |
|
|
@@ -18,16 +18,14 @@ You generate 3 files for sungen — a Gherkin compiler that produces Playwright
|
|
|
18
18
|
| `sungen-delivery` | Export Gherkin + Playwright results → CSV test case deliverable |
|
|
19
19
|
| `sungen-capture` | Acquire visual/design context — one skill, 4 modes: figma-mcp (Dev Mode MCP), figma-pat (URL → spec_figma.md), live (Playwright MCP scan), local (images in `requirements/ui/`) |
|
|
20
20
|
| `sungen-locale` | Bootstrap i18n — audit selectors, detect locale switch mechanism, generate test-data overlay |
|
|
21
|
-
| `sungen-data-factory` | Standardized test-data — field-type/security catalog, 4-source method, `.overwrite`, blind-spot prompts |
|
|
22
21
|
|
|
23
|
-
## Workflow (
|
|
22
|
+
## Workflow (7 AI commands)
|
|
24
23
|
|
|
25
24
|
| Command | What it does |
|
|
26
25
|
|---|---|
|
|
27
26
|
| `/sungen-add-screen <name> <path>` | Scaffold `qa/screens/<name>/` directories |
|
|
28
27
|
| `/sungen-add-flow <name> [--path <url>]` | Scaffold `qa/flows/<name>/` directories for E2E cross-screen testing |
|
|
29
28
|
| `/sungen-create-test <name>` | Generate `.feature` + `test-data.yaml` (auto-detects screen or flow) |
|
|
30
|
-
| `/sungen-create-data-test <name>` | Generate standardized test-data (valid/boundary/invalid + CHK trace) from the Data Factory catalog; no name = all units (asks to confirm) |
|
|
31
29
|
| `/sungen-review <name>` | Score syntax, coverage, viewpoint quality (auto-detects screen or flow) |
|
|
32
30
|
| `/sungen-run-test <name>` | Generate `selectors.yaml`, compile, run, auto-fix (auto-detects screen or flow) |
|
|
33
31
|
| `/sungen-delivery [name...]` | Export test cases → CSV for QA delivery (all screens if no arg) |
|
|
@@ -143,24 +143,14 @@ Scenario: ...
|
|
|
143
143
|
|
|
144
144
|
Path access on a bound result: `{{q.count}}`/`{{q.length}}`, `{{q.first.col}}`, `{{q.last.col}}`, `{{q[2].col}}`, `{{q.col}}` (= first row's col). `expect A is B` also supports `is at least` / `is at most` / `is not`. Tier-2 declarative (trivial inline, no catalog): `User see [<table>] row where [<col>] is {{v}} [has [<col2>] = "x"]`, `… no row where …`, `… count is {{n}}`. Full grammar + catalog/datasource/secret rules → **Advanced → Database** doc. Only emit DB steps when the project has a `database/` catalog / `datasources.yaml`.
|
|
145
145
|
|
|
146
|
-
### NoSQL engines (Cosmos, MongoDB, DynamoDB) — natively supported
|
|
147
|
-
|
|
148
|
-
Beyond the three SQL engines, the Data Driver has native NoSQL engines. Declarative steps (`User see [table] row where …`, `… no row …`, `… count is …`) work across ALL of them; only the raw `@query` catalog shape differs per engine:
|
|
149
|
-
|
|
150
|
-
- **`engine: cosmos`** (Azure Cosmos DB, NoSQL API) — SQL dialect. Author `sql:` catalog entries against the mandatory `c` alias (`SELECT * FROM c WHERE c.<field> = :p`); named params bind automatically. Config: `endpoint`+`key` (or a connection-string `url`), `database`, `container` (default when no `[table]`). Read-only by construction (the query API can't mutate).
|
|
151
|
-
- **`engine: mongodb`** — no SQL. Author a `find:` (`{collection, filter, projection?, sort?, limit?}`) or `pipeline:` (`{collection, stages: […]}`) catalog entry; `:param` placeholders bind at runtime. `$out`/`$merge`/`$where`/`$function`/`$accumulator`/`mapReduce` are refused. Config: a `mongodb://` `url`. Read-only = a server-side read role.
|
|
152
|
-
- **`engine: dynamodb`** — raw `@query` is PartiQL SELECT (`sql: SELECT … FROM "table" WHERE pk = :p`). Declarative steps route GetItem/Query only for partition-/sort-key filters via a `keySchema:` registry; a **non-key filter is refused** (use a raw PartiQL SELECT — never a full-table Scan). Config: `region` + `keySchema` (credentials from the AWS env chain). Read-only = IAM `PartiQLSelect`-only.
|
|
153
|
-
|
|
154
|
-
Full config + catalog examples → **Advanced → Database** doc.
|
|
155
|
-
|
|
156
146
|
### Unsupported DB engine — fallback (ask, don't improvise)
|
|
157
147
|
|
|
158
|
-
Direct SQL verification (`@query`, the Data Driver)
|
|
148
|
+
Direct SQL verification (`@query`, the Data Driver) only supports these engines: **`{postgres, sqlite, mysql}`**. When a datasource's `engine` (or the DB the user describes) is **outside this set** — e.g. Cosmos NoSQL, MongoDB, Cassandra, DynamoDB:
|
|
159
149
|
|
|
160
150
|
1. **Do not** attempt a direct connect. **Do not** improvise a verification method.
|
|
161
151
|
2. Present a fixed `AskUserQuestion` with exactly these 3 branches (always these, no others invented):
|
|
162
152
|
- **@api** — verify state via the project's internal API (route to the API driver).
|
|
163
|
-
- **@manual** — emit a `@manual` step + manual-check guidance (e.g.
|
|
153
|
+
- **@manual** — emit a `@manual` step + manual-check guidance (e.g. Cosmos Data Explorer, `az cosmosdb`, the vendor CLI).
|
|
164
154
|
- **Proxy / read-replica SQL** — if the user can supply a SQL-compatible endpoint (including Azure Cosmos DB **for PostgreSQL** / Citus), treat it as `engine: postgres` (or `mysql`) and use the normal Data Driver path above.
|
|
165
155
|
3. **Recommend, don't decide.** Check the project for signals and put the best-fit branch **first**, tagged **"(Recommended)"** with a one-line reason:
|
|
166
156
|
- a project API driver / `qa/api/` catalog / `PEERCONNE_URL`-style internal API exists → **@api** first;
|
|
@@ -173,11 +163,11 @@ Direct SQL verification (`@query`, the Data Driver) covers these SQL engines: **
|
|
|
173
163
|
Example:
|
|
174
164
|
```
|
|
175
165
|
AskUserQuestion:
|
|
176
|
-
question: "This datasource's engine (
|
|
166
|
+
question: "This datasource's engine (cosmos-nosql) isn't in {postgres, sqlite, mysql} — direct SQL connect isn't supported. How should DB state be verified?"
|
|
177
167
|
options:
|
|
178
168
|
- "@api — verify via the project's internal API (Recommended: qa/api/ catalog found)"
|
|
179
|
-
- "@manual — emit a @manual step +
|
|
180
|
-
- "Proxy / read-replica SQL — supply a SQL-compatible endpoint → engine: postgres"
|
|
169
|
+
- "@manual — emit a @manual step + Cosmos Data Explorer / az cosmosdb check"
|
|
170
|
+
- "Proxy / read-replica SQL — supply a SQL-compatible endpoint (e.g. Cosmos DB for PostgreSQL) → engine: postgres"
|
|
181
171
|
- "Other — describe your own verification approach"
|
|
182
172
|
```
|
|
183
173
|
|