@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.
- package/dist/cli/commands/delivery.d.ts +25 -0
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +23 -1
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/exporters/api-testcase-formatter.d.ts.map +1 -1
- package/dist/exporters/api-testcase-formatter.js +80 -4
- package/dist/exporters/api-testcase-formatter.js.map +1 -1
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +6 -1
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/types.d.ts +5 -0
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -3
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +2 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +2 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +2 -2
- package/dist/generators/test-generator/step-mapper.d.ts +6 -2
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +10 -6
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +2 -2
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md +8 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +12 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +28 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +3 -0
- package/dist/orchestrator/templates/specs-api.d.ts +14 -2
- package/dist/orchestrator/templates/specs-api.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-api.js +38 -14
- package/dist/orchestrator/templates/specs-api.js.map +1 -1
- package/dist/orchestrator/templates/specs-api.ts +43 -14
- package/dist/orchestrator/templates/specs-db.d.ts +10 -2
- package/dist/orchestrator/templates/specs-db.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-db.js +98 -6
- package/dist/orchestrator/templates/specs-db.js.map +1 -1
- package/dist/orchestrator/templates/specs-db.ts +77 -8
- package/package.json +7 -4
- package/src/cli/commands/delivery.ts +24 -4
- package/src/exporters/api-testcase-formatter.ts +77 -4
- package/src/exporters/csv-exporter.ts +6 -1
- package/src/exporters/types.ts +5 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +3 -3
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +2 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +2 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +2 -2
- package/src/generators/test-generator/step-mapper.ts +10 -6
- package/src/orchestrator/templates/ai-src/commands/create-test.md +2 -2
- package/src/orchestrator/templates/ai-src/commands/run-test.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md +8 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +12 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +28 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +3 -0
- package/src/orchestrator/templates/specs-api.ts +43 -14
- 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) { //
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
41
|
-
// still run on the runner; only
|
|
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) { //
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
86
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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
|
-
|
|
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,
|
package/src/exporters/types.ts
CHANGED
|
@@ -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}}');
|
package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs
CHANGED
|
@@ -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);
|