@activate-core/test-forge 0.18.6 → 0.18.8

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/index.js CHANGED
@@ -92,6 +92,8 @@ function printUsage() {
92
92
  console.log(' testforge env Write .env.testing.example from docs');
93
93
  console.log(' testforge run [--area a] [--journey j] Run the generated tests (Jest/Playwright)');
94
94
  console.log(' testforge run --env-file <file> Run against another server (e.g. .env.dev / .env.staging)');
95
+ console.log(' testforge run --headed | --ui | --debug Watch the browser / open the Playwright UI');
96
+ console.log(' testforge run -- <flags> Pass anything else straight to the runner');
95
97
  }
96
98
  async function main() {
97
99
  if (command === 'update') {
@@ -125,10 +127,19 @@ async function main() {
125
127
  runEnvTemplate('docs', '.env.testing.example', '.', generatedScanDirs());
126
128
  }
127
129
  else if (command === 'run') {
130
+ // Anything after `--`, plus the runner flags people reach for most, goes
131
+ // straight to the test runner. Watching a UI suite is an ordinary thing to
132
+ // want and should not mean rebuilding the command by hand.
133
+ const dashIdx = process.argv.indexOf('--');
134
+ const passThrough = [
135
+ ...['--headed', '--ui', '--debug'].filter((f) => process.argv.includes(f)),
136
+ ...(dashIdx === -1 ? [] : process.argv.slice(dashIdx + 1)),
137
+ ];
128
138
  const code = runTests({
129
139
  area: flagValue('--area'),
130
140
  journey: flagValue('--journey'),
131
141
  envFile: flagValue('--env-file'),
142
+ passThrough,
132
143
  });
133
144
  process.exitCode = code;
134
145
  }
@@ -225,6 +225,18 @@ export function buildPrompt(journey, map, testPath, focus, harnessPath, transpor
225
225
  ` obvious controller, and it may set the field under a different`,
226
226
  ` name. Only when no code path anywhere produces the state is the`,
227
227
  ` scenario imaginary — then drop it and say what you searched.`,
228
+ ` AND IT MUST SATISFY THE SCHEMA, NOT YOUR IDEA OF IT. A row is`,
229
+ ` written by a database, which refuses it outright if a NOT NULL`,
230
+ ` column with no default is missing, or if an id is of the wrong`,
231
+ ` type. Take both from the schema itself. In particular, DO NOT`,
232
+ ` ASSUME ONE ID TYPE COVERS THE PROJECT: a users table keyed by uuid`,
233
+ ` commonly sits beside tables keyed by integer or bigint, and a uuid`,
234
+ ` supplied for one of those is rejected by the database — every test`,
235
+ ` that touches that fixture then fails for a reason that has nothing`,
236
+ ` to do with the product. The same applies to a query: an array cast`,
237
+ ` (\`= ANY($1::uuid[])\`) must name the column's real type. When you`,
238
+ ` invent a fixture id, make it of the column's type, and prefer`,
239
+ ` letting the database assign it and reading it back.`,
228
240
  `(9) A TIMEOUT IS NOT A BUG REPORT — AND IT IS USUALLY YOU WAITING FOR`,
229
241
  ` THE WRONG THING. Before you accept a wait, ask what event actually`,
230
242
  ` arrives. A page whose job is to REDIRECT never fires a load event on`,
@@ -62,6 +62,17 @@ function buildGroundingPrompt(journey, map, srcDir) {
62
62
  ` returns, and the status codes the handler can emit.`,
63
63
  ` DATA — the models/collections written or read, and the fields that`,
64
64
  ` matter to this journey (including enum values where they exist).`,
65
+ ` For each table a test must SEED, record two things exactly as the`,
66
+ ` schema declares them, never as the ORM or another table suggests:`,
67
+ ` * every column that is NOT NULL and has NO default — a seed that`,
68
+ ` omits one is rejected outright, and the row never exists;`,
69
+ ` * the TYPE of the primary key and of every foreign key a test`,
70
+ ` will supply or query by. Do not assume one id type covers the`,
71
+ ` schema: a users table keyed by uuid sits happily beside tables`,
72
+ ` keyed by integer or bigint, and a uuid handed to one of those`,
73
+ ` is refused by the database, not merely unmatched.`,
74
+ ` Read this from the migrations, the schema dump, or the models'`,
75
+ ` declarations — whichever the project actually keeps.`,
65
76
  ` EXTERNALS — any third-party client the code calls, the module name`,
66
77
  ` a test would have to stand in for, and the env var its base URL or`,
67
78
  ` key comes from.`,
@@ -54,6 +54,17 @@ export const HARNESS_CONTRACT = [
54
54
  ` whose default is ''), write WITHOUT update`,
55
55
  ` validators (runValidators:false) rather`,
56
56
  ` than failing the whole seed.`,
57
+ ` READ THE SCHEMA BEFORE WRITING A SEED.`,
58
+ ` A relational database refuses a row that`,
59
+ ` omits a NOT NULL column with no default,`,
60
+ ` so every such column must be supplied —`,
61
+ ` take the list from the migrations or the`,
62
+ ` schema itself, not from the fields the`,
63
+ ` journey happens to mention. And take each`,
64
+ ` id's TYPE from the schema too: one table`,
65
+ ` keyed by uuid does not make them all uuid,`,
66
+ ` and an id of the wrong type is rejected`,
67
+ ` outright rather than simply not matching.`,
57
68
  ` - TEST_IDS: object the stable well-known test ids/handles.`,
58
69
  ` EVERY id MUST be env-overridable: resolve`,
59
70
  ` each as process.env.TEST_<NAME>_ID ||`,
@@ -304,6 +315,12 @@ export function playwrightConfig(testDirRelToPackage, globalSetupRelToPackage, c
304
315
  ` testMatch: /.*\\.(test|spec)\\.[jt]s$/,`,
305
316
  ` globalSetup: require.resolve('${globalSetupRelToPackage}'),`,
306
317
  ` timeout: 60000,`,
318
+ ` // Serial, like the Jest config: these suites share ONE test database`,
319
+ ` // and seed/reset it, so parallel workers race on the same rows and the`,
320
+ ` // same run gives different answers twice. It also keeps --headed to a`,
321
+ ` // single browser window instead of one per worker.`,
322
+ ` fullyParallel: false,`,
323
+ ` workers: 1,`,
307
324
  ` // Console line output. The last reporter auto-publishes the run to the`,
308
325
  ` // TestForge dashboard (needs TESTFORGE_KEY in env + project/dashboardUrl`,
309
326
  ` // in testforge.config.json).`,
@@ -59,7 +59,25 @@ export function runTests(opts = {}) {
59
59
  if (!cmd) {
60
60
  if (framework === 'playwright') {
61
61
  const target = opts.journey ? `${rel}/${opts.journey}` : rel;
62
- cmd = `npx playwright test ${target}`;
62
+ // Name the config explicitly. It is written beside the AREA's tests
63
+ // (tests/Feature/playwright.config.js for tests/Feature/Web), not at
64
+ // the package root, so Playwright's own discovery finds nothing and
65
+ // silently falls back to defaults — no globalSetup, no reporters, and
66
+ // therefore no run published to the dashboard. Walk up from the test
67
+ // directory to the package root and take the first config found.
68
+ let cfgFlag = '';
69
+ for (let dir = rel;; dir = dirname(dir)) {
70
+ const hit = ['playwright.config.js', 'playwright.config.ts']
71
+ .map((f) => (dir === '.' ? f : join(dir, f)))
72
+ .find((f) => existsSync(join(root, f)));
73
+ if (hit) {
74
+ cfgFlag = ` --config=${hit}`;
75
+ break;
76
+ }
77
+ if (dir === '.' || dir === dirname(dir))
78
+ break;
79
+ }
80
+ cmd = `npx playwright test${cfgFlag} ${target}`;
63
81
  }
64
82
  else {
65
83
  cmd = opts.journey
@@ -67,6 +85,8 @@ export function runTests(opts = {}) {
67
85
  : `npx jest ${rel}`;
68
86
  }
69
87
  }
88
+ if (opts.passThrough?.length)
89
+ cmd += ` ${opts.passThrough.join(' ')}`;
70
90
  ranAnything = true;
71
91
  console.log(`\n=== ${area} (${framework}) ===`);
72
92
  console.log(`$ ${cmd} [cwd: ${root}]`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@activate-core/test-forge",
3
- "version": "0.18.6",
3
+ "version": "0.18.8",
4
4
  "description": "AI-powered QA automation. Scans a project, drafts journey docs, and generates seeded Jest/Playwright tests. Works in any project, with any AI CLI.",
5
5
  "type": "module",
6
6
  "bin": {