@sun-asterisk/sungen 3.2.7 → 3.2.9

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.
Files changed (58) hide show
  1. package/dist/cli/commands/delivery.d.ts +25 -0
  2. package/dist/cli/commands/delivery.d.ts.map +1 -1
  3. package/dist/cli/commands/delivery.js +23 -1
  4. package/dist/cli/commands/delivery.js.map +1 -1
  5. package/dist/exporters/api-testcase-formatter.d.ts.map +1 -1
  6. package/dist/exporters/api-testcase-formatter.js +80 -4
  7. package/dist/exporters/api-testcase-formatter.js.map +1 -1
  8. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  9. package/dist/exporters/csv-exporter.js +6 -1
  10. package/dist/exporters/csv-exporter.js.map +1 -1
  11. package/dist/exporters/types.d.ts +5 -0
  12. package/dist/exporters/types.d.ts.map +1 -1
  13. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -3
  14. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +1 -1
  15. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +2 -2
  16. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +1 -1
  17. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +2 -2
  18. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +2 -2
  19. package/dist/generators/test-generator/step-mapper.d.ts +6 -2
  20. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  21. package/dist/generators/test-generator/step-mapper.js +10 -6
  22. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  23. package/dist/orchestrator/templates/ai-src/commands/create-test.md +2 -2
  24. package/dist/orchestrator/templates/ai-src/commands/run-test.md +1 -1
  25. package/dist/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md +8 -0
  26. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +12 -1
  27. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +28 -0
  28. package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +3 -0
  29. package/dist/orchestrator/templates/specs-api.d.ts +14 -2
  30. package/dist/orchestrator/templates/specs-api.d.ts.map +1 -1
  31. package/dist/orchestrator/templates/specs-api.js +38 -14
  32. package/dist/orchestrator/templates/specs-api.js.map +1 -1
  33. package/dist/orchestrator/templates/specs-api.ts +43 -14
  34. package/dist/orchestrator/templates/specs-db.d.ts +10 -2
  35. package/dist/orchestrator/templates/specs-db.d.ts.map +1 -1
  36. package/dist/orchestrator/templates/specs-db.js +98 -6
  37. package/dist/orchestrator/templates/specs-db.js.map +1 -1
  38. package/dist/orchestrator/templates/specs-db.ts +77 -8
  39. package/package.json +7 -4
  40. package/src/cli/commands/delivery.ts +24 -4
  41. package/src/exporters/api-testcase-formatter.ts +77 -4
  42. package/src/exporters/csv-exporter.ts +6 -1
  43. package/src/exporters/types.ts +5 -0
  44. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -3
  45. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +1 -1
  46. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +2 -2
  47. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +1 -1
  48. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +2 -2
  49. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +2 -2
  50. package/src/generators/test-generator/step-mapper.ts +10 -6
  51. package/src/orchestrator/templates/ai-src/commands/create-test.md +2 -2
  52. package/src/orchestrator/templates/ai-src/commands/run-test.md +1 -1
  53. package/src/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md +8 -0
  54. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +12 -1
  55. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +28 -0
  56. package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +3 -0
  57. package/src/orchestrator/templates/specs-api.ts +43 -14
  58. package/src/orchestrator/templates/specs-db.ts +77 -8
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.db = void 0;
37
+ exports.rewritePlaceholders = rewritePlaceholders;
37
38
  /* eslint-disable */
38
39
  /**
39
40
  * Sungen Data Driver — runtime DB-verification helper (auto-generated into specs/db.ts).
@@ -42,7 +43,7 @@ exports.db = void 0;
42
43
  * table/column identifiers are validated against a strict allowlist pattern (identifiers
43
44
  * can't be bound as parameters). Secrets come from .env.qa / process.env, never inline.
44
45
  *
45
- * Engines: PostgreSQL (`pg`) and SQLite (`better-sqlite3`), lazy-loaded on first use.
46
+ * Engines: PostgreSQL (`pg`), MySQL (`mysql2`), and SQLite (`better-sqlite3`), lazy-loaded on first use.
46
47
  * Config: a `datasources.yaml` at the project root (or qa/), with ${VAR} resolved from env.
47
48
  *
48
49
  * DO NOT EDIT — regenerated by `sungen generate`.
@@ -56,6 +57,9 @@ const ident = (s) => {
56
57
  throw new Error(`Unsafe identifier: ${JSON.stringify(s)} (allowed: [A-Za-z_][A-Za-z0-9_]*)`);
57
58
  return s;
58
59
  };
60
+ // Single source of truth for the "not supported" error message below and for the mysql/sqlite
61
+ // placeholder rewrite condition (`rewritePlaceholders`) — keep engine names in sync with `DataSourceConfig.engine`.
62
+ const SUPPORTED_ENGINES = ['postgres', 'sqlite', 'mysql'];
59
63
  /**
60
64
  * Open a local TCP forward (127.0.0.1:<ephemeral> → ssh bastion → dstHost:dstPort) for a DB socket.
61
65
  * Sockets are unref()'d so a dangling tunnel never keeps the test process alive after the run.
@@ -144,11 +148,42 @@ function loadConfig() {
144
148
  }
145
149
  return doc.datasources;
146
150
  }
151
+ /**
152
+ * `build()` always emits PG-style `$1..$n` placeholders; mysql2/better-sqlite3 both bind with `?`.
153
+ * Only real placeholders are rewritten — a `$1` sitting inside a single-quoted string literal (e.g.
154
+ * a catalog query `WHERE label = '$1 off'`) is left untouched. SQL escapes an inner quote by doubling
155
+ * it (`''`), which the simple in-string toggle handles (an even number of quotes returns to the same state).
156
+ */
157
+ function rewritePlaceholders(engine, sql) {
158
+ if (engine !== 'sqlite' && engine !== 'mysql')
159
+ return sql;
160
+ let out = '';
161
+ let inStr = false;
162
+ for (let i = 0; i < sql.length; i++) {
163
+ const c = sql[i];
164
+ if (c === "'") {
165
+ inStr = !inStr;
166
+ out += c;
167
+ continue;
168
+ }
169
+ if (!inStr && c === '$' && sql[i + 1] >= '0' && sql[i + 1] <= '9') {
170
+ let j = i + 1;
171
+ while (j < sql.length && sql[j] >= '0' && sql[j] <= '9')
172
+ j++;
173
+ out += '?';
174
+ i = j - 1;
175
+ continue;
176
+ }
177
+ out += c;
178
+ }
179
+ return out;
180
+ }
147
181
  class DataSource {
148
182
  constructor() {
149
183
  this.configs = null;
150
184
  this.engines = new Map();
151
185
  this.tunnels = [];
186
+ this.mysqlPools = [];
152
187
  }
153
188
  cfg(name) {
154
189
  if (!this.configs)
@@ -168,7 +203,7 @@ class DataSource {
168
203
  let engine;
169
204
  if (conf.engine === 'postgres') {
170
205
  let connectionString = conf.url;
171
- if (conf.ssh) { // Cách B: tunnel the DB socket through a bastion
206
+ if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
172
207
  const u = new URL(conf.url);
173
208
  const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 5432));
174
209
  this.tunnels.push(t);
@@ -180,6 +215,56 @@ class DataSource {
180
215
  const pool = new Pool({ connectionString, max: 2, statement_timeout: conf.statement_timeout_ms ?? 4000 });
181
216
  engine = { query: async (sql, params) => (await pool.query(sql, params)).rows };
182
217
  }
218
+ else if (conf.engine === 'mysql') {
219
+ let connectionString = conf.url;
220
+ if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
221
+ const u = new URL(conf.url);
222
+ const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 3306));
223
+ this.tunnels.push(t);
224
+ u.hostname = t.host;
225
+ u.port = String(t.port); // rewrite host:port → 127.0.0.1:<tunnel> (keep user/pass/db/query)
226
+ connectionString = u.toString();
227
+ }
228
+ const mysql = require('mysql2/promise');
229
+ const pool = mysql.createPool({ uri: connectionString, connectionLimit: 2 });
230
+ this.mysqlPools.push(pool);
231
+ // mysql2 ignores the mysqljs per-query `timeout` field, and destroying the connection does
232
+ // not interrupt an in-flight query — so bound the wait client-side by racing the query
233
+ // against a timer. On overrun we reject and destroy the connection (removes it from the pool
234
+ // rather than releasing a still-busy socket); on success we release it back.
235
+ const timeoutMs = conf.statement_timeout_ms ?? 4000;
236
+ engine = {
237
+ query: async (sql, params) => {
238
+ const conn = await pool.getConnection();
239
+ const qp = conn.query(sql, params);
240
+ qp.catch(() => { });
241
+ let timer;
242
+ let timedOut = false;
243
+ const guard = new Promise((_, reject) => {
244
+ timer = setTimeout(() => { timedOut = true; reject(new Error(`Data Driver: mysql query exceeded ${timeoutMs}ms — aborted.`)); }, timeoutMs);
245
+ });
246
+ try {
247
+ const [rows] = await Promise.race([qp, guard]);
248
+ return rows;
249
+ }
250
+ finally {
251
+ clearTimeout(timer);
252
+ if (timedOut) {
253
+ try {
254
+ conn.destroy();
255
+ }
256
+ catch { /* best-effort */ }
257
+ }
258
+ else {
259
+ try {
260
+ conn.release();
261
+ }
262
+ catch { /* already gone */ }
263
+ }
264
+ }
265
+ },
266
+ };
267
+ }
183
268
  else if (conf.engine === 'sqlite') {
184
269
  if (conf.ssh)
185
270
  console.warn(`Data Driver: datasource "${key}" sets ssh: but engine is sqlite (file-based) — ssh ignored.`);
@@ -188,13 +273,20 @@ class DataSource {
188
273
  engine = { query: async (sql, params) => db.prepare(sql).all(...params) };
189
274
  }
190
275
  else {
191
- throw new Error(`Data Driver: engine "${conf.engine}" not supported yet (postgres | sqlite).`);
276
+ throw new Error(`Data Driver: engine "${conf.engine}" not supported (supported: ${SUPPORTED_ENGINES.join(' | ')}). For non-SQL stores (Cosmos NoSQL, Mongo…) verify via @api or @manual.`);
192
277
  }
193
278
  this.engines.set(key, engine);
194
279
  return { engine, conf };
195
280
  }
196
- /** Close any open SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
281
+ /** Close open MySQL pools + SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
197
282
  close() {
283
+ for (const p of this.mysqlPools) {
284
+ try {
285
+ void p.end();
286
+ }
287
+ catch { /* best-effort */ }
288
+ }
289
+ this.mysqlPools = [];
198
290
  for (const t of this.tunnels)
199
291
  t.close();
200
292
  this.tunnels = [];
@@ -236,9 +328,9 @@ class DataSource {
236
328
  const n = Number(rows[0]?.n ?? rows[0]?.['count(*)'] ?? 0);
237
329
  (0, test_1.expect)(n, `Expected ${count} row(s) in "${table}"${cols.length ? ' where ' + desc(filter) : ''} — found ${n}`).toBe(Number(count));
238
330
  }
239
- /** Rewrites $1/$2 placeholders to `?` for SQLite. */
331
+ /** Rewrites $1/$2 placeholders to `?` for engines that don't support PG-style positional params. */
240
332
  sqlFor(conf, sql) {
241
- return conf.engine === 'sqlite' ? sql.replace(/\$\d+/g, '?') : sql;
333
+ return rewritePlaceholders(conf.engine, sql);
242
334
  }
243
335
  // --- Named queries (catalog-backed; SQL is resolved + embedded at compile time) -----------
244
336
  /** Read-only guard (second layer): a named query must be a single SELECT/WITH statement. */
@@ -1 +1 @@
1
- {"version":3,"file":"specs-db.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"specs-db.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  * table/column identifiers are validated against a strict allowlist pattern (identifiers
7
7
  * can't be bound as parameters). Secrets come from .env.qa / process.env, never inline.
8
8
  *
9
- * Engines: PostgreSQL (`pg`) and SQLite (`better-sqlite3`), lazy-loaded on first use.
9
+ * Engines: PostgreSQL (`pg`), MySQL (`mysql2`), and SQLite (`better-sqlite3`), lazy-loaded on first use.
10
10
  * Config: a `datasources.yaml` at the project root (or qa/), with ${VAR} resolved from env.
11
11
  *
12
12
  * DO NOT EDIT — regenerated by `sungen generate`.
@@ -21,6 +21,10 @@ const ident = (s: string): string => {
21
21
  return s;
22
22
  };
23
23
 
24
+ // Single source of truth for the "not supported" error message below and for the mysql/sqlite
25
+ // placeholder rewrite condition (`rewritePlaceholders`) — keep engine names in sync with `DataSourceConfig.engine`.
26
+ const SUPPORTED_ENGINES = ['postgres', 'sqlite', 'mysql'] as const;
27
+
24
28
  interface SshConfig {
25
29
  host: string; // jump host reachable from the runner
26
30
  port?: number; // default 22
@@ -37,8 +41,8 @@ interface DataSourceConfig {
37
41
  readonly?: boolean;
38
42
  statement_timeout_ms?: number;
39
43
  max_rows?: number;
40
- // Cách B (fallback): tunnel the DB SOCKET through an SSH bastion. DB-only — the browser/E2E
41
- // still run on the runner; only PG traffic crosses. See docs/spec/sungen_data_driver_ssh_tunnel_spec.md.
44
+ // Fallback path: tunnel the DB SOCKET through an SSH bastion. DB-only — the browser/E2E
45
+ // still run on the runner; only DB traffic crosses. See docs/spec/sungen_data_driver_ssh_tunnel_spec.md.
42
46
  ssh?: SshConfig;
43
47
  }
44
48
 
@@ -120,12 +124,38 @@ function loadConfig(): Record<string, DataSourceConfig> {
120
124
  return doc.datasources;
121
125
  }
122
126
 
127
+ /**
128
+ * `build()` always emits PG-style `$1..$n` placeholders; mysql2/better-sqlite3 both bind with `?`.
129
+ * Only real placeholders are rewritten — a `$1` sitting inside a single-quoted string literal (e.g.
130
+ * a catalog query `WHERE label = '$1 off'`) is left untouched. SQL escapes an inner quote by doubling
131
+ * it (`''`), which the simple in-string toggle handles (an even number of quotes returns to the same state).
132
+ */
133
+ export function rewritePlaceholders(engine: string, sql: string): string {
134
+ if (engine !== 'sqlite' && engine !== 'mysql') return sql;
135
+ let out = '';
136
+ let inStr = false;
137
+ for (let i = 0; i < sql.length; i++) {
138
+ const c = sql[i];
139
+ if (c === "'") { inStr = !inStr; out += c; continue; }
140
+ if (!inStr && c === '$' && sql[i + 1] >= '0' && sql[i + 1] <= '9') {
141
+ let j = i + 1;
142
+ while (j < sql.length && sql[j] >= '0' && sql[j] <= '9') j++;
143
+ out += '?';
144
+ i = j - 1;
145
+ continue;
146
+ }
147
+ out += c;
148
+ }
149
+ return out;
150
+ }
151
+
123
152
  type Engine = { query(sql: string, params: any[]): Promise<any[]>; };
124
153
 
125
154
  class DataSource {
126
155
  private configs: Record<string, DataSourceConfig> | null = null;
127
156
  private engines = new Map<string, Engine>();
128
157
  private tunnels: Array<{ close: () => void }> = [];
158
+ private mysqlPools: Array<{ end: () => Promise<void> }> = [];
129
159
 
130
160
  private cfg(name?: string): { key: string; conf: DataSourceConfig } {
131
161
  if (!this.configs) this.configs = loadConfig();
@@ -142,7 +172,7 @@ class DataSource {
142
172
  let engine: Engine;
143
173
  if (conf.engine === 'postgres') {
144
174
  let connectionString = conf.url;
145
- if (conf.ssh) { // Cách B: tunnel the DB socket through a bastion
175
+ if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
146
176
  const u = new URL(conf.url);
147
177
  const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 5432));
148
178
  this.tunnels.push(t);
@@ -152,20 +182,59 @@ class DataSource {
152
182
  const { Pool } = require('pg');
153
183
  const pool = new Pool({ connectionString, max: 2, statement_timeout: conf.statement_timeout_ms ?? 4000 });
154
184
  engine = { query: async (sql, params) => (await pool.query(sql, params)).rows };
185
+ } else if (conf.engine === 'mysql') {
186
+ let connectionString = conf.url;
187
+ if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
188
+ const u = new URL(conf.url);
189
+ const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 3306));
190
+ this.tunnels.push(t);
191
+ u.hostname = t.host; u.port = String(t.port); // rewrite host:port → 127.0.0.1:<tunnel> (keep user/pass/db/query)
192
+ connectionString = u.toString();
193
+ }
194
+ const mysql = require('mysql2/promise');
195
+ const pool = mysql.createPool({ uri: connectionString, connectionLimit: 2 });
196
+ this.mysqlPools.push(pool);
197
+ // mysql2 ignores the mysqljs per-query `timeout` field, and destroying the connection does
198
+ // not interrupt an in-flight query — so bound the wait client-side by racing the query
199
+ // against a timer. On overrun we reject and destroy the connection (removes it from the pool
200
+ // rather than releasing a still-busy socket); on success we release it back.
201
+ const timeoutMs = conf.statement_timeout_ms ?? 4000;
202
+ engine = {
203
+ query: async (sql, params) => {
204
+ const conn = await pool.getConnection();
205
+ const qp = conn.query(sql, params);
206
+ qp.catch(() => { /* swallow the post-timeout rejection from the abandoned query */ });
207
+ let timer: NodeJS.Timeout | undefined;
208
+ let timedOut = false;
209
+ const guard = new Promise<never>((_, reject) => {
210
+ timer = setTimeout(() => { timedOut = true; reject(new Error(`Data Driver: mysql query exceeded ${timeoutMs}ms — aborted.`)); }, timeoutMs);
211
+ });
212
+ try {
213
+ const [rows] = await Promise.race([qp, guard]);
214
+ return rows as any[];
215
+ } finally {
216
+ clearTimeout(timer);
217
+ if (timedOut) { try { conn.destroy(); } catch { /* best-effort */ } }
218
+ else { try { conn.release(); } catch { /* already gone */ } }
219
+ }
220
+ },
221
+ };
155
222
  } else if (conf.engine === 'sqlite') {
156
223
  if (conf.ssh) console.warn(`Data Driver: datasource "${key}" sets ssh: but engine is sqlite (file-based) — ssh ignored.`);
157
224
  const Database = require('better-sqlite3');
158
225
  const db = new Database(conf.url.replace(/^sqlite:/, ''), { readonly: conf.readonly !== false });
159
226
  engine = { query: async (sql, params) => db.prepare(sql).all(...params) };
160
227
  } else {
161
- throw new Error(`Data Driver: engine "${conf.engine}" not supported yet (postgres | sqlite).`);
228
+ throw new Error(`Data Driver: engine "${conf.engine}" not supported (supported: ${SUPPORTED_ENGINES.join(' | ')}). For non-SQL stores (Cosmos NoSQL, Mongo…) verify via @api or @manual.`);
162
229
  }
163
230
  this.engines.set(key, engine);
164
231
  return { engine, conf };
165
232
  }
166
233
 
167
- /** Close any open SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
234
+ /** Close open MySQL pools + SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
168
235
  close(): void {
236
+ for (const p of this.mysqlPools) { try { void p.end(); } catch { /* best-effort */ } }
237
+ this.mysqlPools = [];
169
238
  for (const t of this.tunnels) t.close();
170
239
  this.tunnels = [];
171
240
  }
@@ -211,9 +280,9 @@ class DataSource {
211
280
  expect(n, `Expected ${count} row(s) in "${table}"${cols.length ? ' where ' + desc(filter) : ''} — found ${n}`).toBe(Number(count));
212
281
  }
213
282
 
214
- /** Rewrites $1/$2 placeholders to `?` for SQLite. */
283
+ /** Rewrites $1/$2 placeholders to `?` for engines that don't support PG-style positional params. */
215
284
  private sqlFor(conf: DataSourceConfig, sql: string): string {
216
- return conf.engine === 'sqlite' ? sql.replace(/\$\d+/g, '?') : sql;
285
+ return rewritePlaceholders(conf.engine, sql);
217
286
  }
218
287
 
219
288
  // --- Named queries (catalog-backed; SQL is resolved + embedded at compile time) -----------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "3.2.7",
3
+ "version": "3.2.9",
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/dashboard/api-flows-discovery.run.ts && tsx tests/dashboard/status-results-fallback.run.ts && tsx tests/codegen/verb-ref-masking.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",
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/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
  },
@@ -33,13 +33,13 @@
33
33
  "node": ">=18.0.0"
34
34
  },
35
35
  "dependencies": {
36
- "@sungen/driver-ui": "3.2.7",
37
36
  "@anthropic-ai/sdk": "^0.71.0",
38
37
  "@babel/parser": "^7.28.5",
39
38
  "@babel/traverse": "^7.28.5",
40
39
  "@babel/types": "^7.28.5",
41
40
  "@cucumber/gherkin": "^37.0.0",
42
41
  "@cucumber/messages": "^31.0.0",
42
+ "@sungen/driver-ui": "3.2.9",
43
43
  "chalk": "^5.6.2",
44
44
  "commander": "^14.0.2",
45
45
  "dotenv": "^17.2.3",
@@ -63,5 +63,8 @@
63
63
  "src",
64
64
  "README.md",
65
65
  "LICENSE"
66
- ]
66
+ ],
67
+ "devDependencies": {
68
+ "mysql2": "^3"
69
+ }
67
70
  }
@@ -58,7 +58,7 @@ type UnitKind = 'screen' | 'flow' | 'api';
58
58
  /** qa/ subfolder for a unit kind. */
59
59
  const qaParent = (kind: UnitKind): string => (kind === 'flow' ? 'flows' : kind === 'api' ? 'api' : 'screens');
60
60
 
61
- interface DeliveryTarget {
61
+ export interface DeliveryTarget {
62
62
  screen: string;
63
63
  featureBaseName: string;
64
64
  /** Alias of `featureBaseName` — preserves the old `target.name` call sites. */
@@ -256,7 +256,7 @@ function readFlowPagePath(selectorsFile: string): string | undefined {
256
256
  return undefined;
257
257
  }
258
258
 
259
- function runPreflight(cwd: string, target: DeliveryTarget): PreflightCheck {
259
+ export function runPreflight(cwd: string, target: DeliveryTarget): PreflightCheck {
260
260
  const base = qaDir(cwd, target);
261
261
  const genBase = generatedDir(cwd, target);
262
262
  const featureFile = path.join(base, 'features', `${target.featureBaseName}.feature`);
@@ -266,7 +266,13 @@ function runPreflight(cwd: string, target: DeliveryTarget): PreflightCheck {
266
266
  const resultsFile = resolveResultsPath(cwd, target);
267
267
 
268
268
  const featureOk = checkFeatureReal(featureFile);
269
- const testDataOk = checkTestDataHasVars(testDataFile);
269
+ // test-data is only REQUIRED when the feature actually references a
270
+ // {{var}} placeholder. A static screen (e.g. a fixed bottom-navigation bar)
271
+ // legitimately has zero test-data variables — blocking its export was a
272
+ // false positive (issue: preflight blocked static screens).
273
+ const testDataOk = featureReferencesTestData(featureFile)
274
+ ? checkTestDataHasVars(testDataFile)
275
+ : true;
270
276
  // API units have no DOM → no selectors; the catalog (apis.yaml) is the contract. N/A, not missing.
271
277
  const selectorsOk = target.kind === 'api' ? true : checkSelectorsHasEntries(selectorsFile, target.featureBaseName);
272
278
  const specOk = fs.existsSync(specFile);
@@ -333,6 +339,20 @@ function checkTestDataHasVars(testDataFile: string): boolean {
333
339
  return lines.length > 0;
334
340
  }
335
341
 
342
+ /**
343
+ * True when the .feature references at least one test-data variable — a
344
+ * `{{var}}` placeholder that is not a runtime-generated `{{$...}}` var
345
+ * (e.g. `{{$uuid}}`, resolved at execution time, never from test-data.yaml).
346
+ * Comment-only or reference-free features need no test-data entries.
347
+ */
348
+ function featureReferencesTestData(featureFile: string): boolean {
349
+ if (!fs.existsSync(featureFile)) return false;
350
+ const content = fs.readFileSync(featureFile, 'utf-8');
351
+ // Strip Gherkin comment lines so a {{var}} inside a `# ...` note doesn't count.
352
+ const code = content.split('\n').filter((l) => !l.trim().startsWith('#')).join('\n');
353
+ return /\{\{\s*[^$}\s][^}]*\}\}/.test(code);
354
+ }
355
+
336
356
  function checkSelectorsHasEntries(selectorsFile: string, screen: string): boolean {
337
357
  if (!fs.existsSync(selectorsFile)) return false;
338
358
  const content = fs.readFileSync(selectorsFile, 'utf-8');
@@ -342,7 +362,7 @@ function checkSelectorsHasEntries(selectorsFile: string, screen: string): boolea
342
362
  return topLevelKeys > 1;
343
363
  }
344
364
 
345
- function hasBlockingMissing(p: PreflightCheck): boolean {
365
+ export function hasBlockingMissing(p: PreflightCheck): boolean {
346
366
  return !p.featureOk || !p.testDataOk || !p.selectorsOk || !p.specOk;
347
367
  }
348
368
 
@@ -82,8 +82,17 @@ export function formatApiRequest(
82
82
  const method = String(entry.method ?? '').toUpperCase() || '—';
83
83
  const api = entry.path ? resolve(String(entry.path)) : '—';
84
84
  const header = formatHeaders(entry, call.args, localize);
85
- const bodyStr = formatBody(entry.body, call.args, localize);
86
- return [`Method: ${method}`, `API: ${api}`, `Header: ${header}`, `Body: ${bodyStr}`].join('\n');
85
+ const lines = [`Method: ${method}`, `API: ${api}`, `Header: ${header}`];
86
+ // A file upload carries its parts in `files:` / `bodyFile:`, NOT `body:` — render them so the
87
+ // delivery Steps reflect the actual request (without this, fields like `gift_image` vanished from
88
+ // the export even though the compiled .spec.ts sent them).
89
+ const filesStr = formatFiles(entry.files, call.args, localize);
90
+ const bodyFileStr = formatBodyFile(entry.bodyFile, call.args, localize);
91
+ // Body is scalar fields; a pure file upload may have none → still show `Body: —` for a stable shape.
92
+ lines.push(`Body: ${formatBody(entry.body, call.args, localize)}`);
93
+ if (filesStr) lines.push(`Files: ${filesStr}`);
94
+ if (bodyFileStr) lines.push(`Body file: ${bodyFileStr}`);
95
+ return lines.join('\n');
87
96
  });
88
97
 
89
98
  if (blocks.length === 1) return blocks[0];
@@ -180,8 +189,14 @@ function formatHeaders(
180
189
  const parts = Object.entries(explicit).map(([k, v]) =>
181
190
  isSensitiveHeader(k) ? `${k}: ${maskHeaderValue(String(v))}` : `${k}: ${localize(bindParams(String(v), args))}`,
182
191
  );
183
- if (!hasContentType && entry.body !== undefined && entry.body !== null) {
184
- parts.push(`Content-Type: ${contentTypeFor(entry.encoding)}`);
192
+ // Content-Type the request actually sends (Playwright sets it at runtime): a `files:` block is always
193
+ // multipart/form-data (even with `encoding` omitted); a `bodyFile:` sends the file's own media type;
194
+ // otherwise it follows the body encoding. Only shown when the request carries a body/file and the
195
+ // catalog didn't already declare a Content-Type.
196
+ if (!hasContentType) {
197
+ const ct = uploadContentType(entry);
198
+ if (ct) parts.push(`Content-Type: ${ct}`);
199
+ else if (entry.body !== undefined && entry.body !== null) parts.push(`Content-Type: ${contentTypeFor(entry.encoding)}`);
185
200
  }
186
201
  return parts.length > 0 ? parts.join('\n') : '—';
187
202
  }
@@ -192,6 +207,14 @@ function contentTypeFor(encoding?: string): string {
192
207
  return 'application/json';
193
208
  }
194
209
 
210
+ /** Content-Type implied by a file upload, or '' when the entry sends no file. */
211
+ function uploadContentType(entry: ApiCatalogEntry): string {
212
+ if (entry.files && typeof entry.files === 'object' && Object.keys(entry.files).length) return 'multipart/form-data';
213
+ const bf = normalizeFileSpec(entry.bodyFile);
214
+ if (bf) return bf.mimeType || 'application/octet-stream';
215
+ return '';
216
+ }
217
+
195
218
  /**
196
219
  * Compose the Body cell, masking credential fields. When the body is a flat object
197
220
  * with a sensitive key (password/token/secret/…), each value is rendered per-key so
@@ -217,6 +240,56 @@ function isPlainObject(v: unknown): boolean {
217
240
  return typeof v === 'object' && v !== null && !Array.isArray(v);
218
241
  }
219
242
 
243
+ interface NormalizedFileSpec { path: string; mimeType?: string; filename?: string; name?: string }
244
+
245
+ /** Normalize a raw file spec (string shorthand or object) → a typed spec, or undefined if invalid. */
246
+ function normalizeFileSpec(raw: unknown): NormalizedFileSpec | undefined {
247
+ if (typeof raw === 'string') return { path: raw };
248
+ if (isPlainObject(raw)) {
249
+ const o = raw as Record<string, unknown>;
250
+ if (typeof o.path === 'string') {
251
+ return {
252
+ path: o.path,
253
+ mimeType: typeof o.mimeType === 'string' ? o.mimeType : undefined,
254
+ filename: typeof o.filename === 'string' ? o.filename : undefined,
255
+ name: typeof o.name === 'string' ? o.name : undefined,
256
+ };
257
+ }
258
+ }
259
+ return undefined;
260
+ }
261
+
262
+ /** Render one file part: `field: <path> (<mimeType>)`, with `:param`/`{{var}}` resolved. */
263
+ function renderFilePart(field: string, spec: NormalizedFileSpec, args: Record<string, string>, localize: (t: string) => string): string {
264
+ const p = localize(bindParams(spec.path, args));
265
+ return `${spec.name ?? field}: ${p}${spec.mimeType ? ` (${spec.mimeType})` : ''}`;
266
+ }
267
+
268
+ /**
269
+ * Render the `files:` block for the Steps cell. A field value may be a single spec, its string
270
+ * shorthand, or an array (multiple files under one field name) — every part is rendered. Multiple
271
+ * parts are comma-joined. Returns '' when there is no file field.
272
+ */
273
+ function formatFiles(files: unknown, args: Record<string, string>, localize: (t: string) => string): string {
274
+ if (!isPlainObject(files)) return '';
275
+ const parts: string[] = [];
276
+ for (const [field, raw] of Object.entries(files as Record<string, unknown>)) {
277
+ for (const s of Array.isArray(raw) ? raw : [raw]) {
278
+ const spec = normalizeFileSpec(s);
279
+ if (spec) parts.push(renderFilePart(field, spec, args, localize));
280
+ }
281
+ }
282
+ return parts.join(', ');
283
+ }
284
+
285
+ /** Render the `bodyFile:` (raw-binary body) for the Steps cell, or '' when absent. */
286
+ function formatBodyFile(bodyFile: unknown, args: Record<string, string>, localize: (t: string) => string): string {
287
+ const spec = normalizeFileSpec(bodyFile);
288
+ if (!spec) return '';
289
+ const p = localize(bindParams(spec.path, args));
290
+ return `${p} (${spec.mimeType || 'application/octet-stream'})`;
291
+ }
292
+
220
293
  /**
221
294
  * Credential name matcher (substring, not anchored) for both body-field keys and
222
295
  * test-data keys — catches variants like confirmPassword / new_password /
@@ -183,7 +183,12 @@ export function buildTestCaseRows(input: BuildCsvInput): TestCaseRow[] {
183
183
  steps,
184
184
  expectedResults,
185
185
  priority,
186
- testcaseType: m.spec ? testcaseType : testcaseType === 'Manual' ? 'Manual' : 'Not compiled',
186
+ // An execution `result` proves the scenario compiled AND ran it MUST win over the
187
+ // `!m.spec` "not compiled" heuristic, exactly as the testResult branch above does. A @cases
188
+ // scenario compiles to per-row titles with a `— <label>` suffix, so findMatchingSpecTest
189
+ // (base-name match) often leaves m.spec null even though the row ran; without checking result
190
+ // here, those already-passed/failed rows were mislabelled "Not compiled".
191
+ testcaseType: (m.spec || result) ? testcaseType : testcaseType === 'Manual' ? 'Manual' : 'Not compiled',
187
192
  testResult,
188
193
  executedDate,
189
194
  testExecutor: executor,
@@ -177,5 +177,10 @@ export interface ApiCatalogEntry {
177
177
  * Absent/`json` → application/json, `form` → application/x-www-form-urlencoded,
178
178
  * `multipart` → multipart/form-data. */
179
179
  encoding?: 'json' | 'form' | 'multipart';
180
+ /** Multipart file parts (multipart/form-data). A field value may be a single file spec, its string
181
+ * shorthand, or an array of specs (multiple files under one field name). Typed loosely — guard before use. */
182
+ files?: Record<string, unknown>;
183
+ /** Raw-binary body: the request body IS the file's bytes. Typed loosely — guard before use. */
184
+ bodyFile?: unknown;
180
185
  expect?: { status?: number | string };
181
186
  }
@@ -1,3 +1,3 @@
1
- await expect({{> locator}}.getByRole('columnheader', { name: '{{columnName}}' })).toBeVisible();
2
- const {{columnIndexVar}} = (await page.getByRole('columnheader').allTextContents()).findIndex(h => h.includes('{{columnName}}'));
3
- await expect(page.getByRole('row').nth({{rowNth}}).getByRole('cell').nth({{columnIndexVar}})).toHaveText('{{dataValue}}');
1
+ await expect({{> locator}}.getByRole('columnheader', { name: '{{columnName}}', includeHidden: true })).toBeVisible();
2
+ const {{columnIndexVar}} = (await page.getByRole('columnheader', { includeHidden: true }).filter({ visible: true }).allTextContents()).findIndex(h => h.includes('{{columnName}}'));
3
+ await expect(page.getByRole('row', { includeHidden: true }).filter({ visible: true }).nth({{rowNth}}).getByRole('cell', { includeHidden: true }).filter({ visible: true }).nth({{columnIndexVar}})).toHaveText('{{dataValue}}');
@@ -1 +1 @@
1
- await expect({{> locator}}.locator('tbody').getByRole('row')).toHaveCount(0);
1
+ await expect({{> locator}}.locator('tbody').getByRole('row', { includeHidden: true }).filter({ visible: true })).toHaveCount(0);