@activate-core/test-forge 0.5.0 → 0.5.1
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/core/ai-generator.js +5 -9
- package/dist/core/config.js +13 -0
- package/dist/core/run-generate.js +16 -4
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { execFileSync, spawn } from 'node:child_process';
|
|
14
14
|
import { readFileSync, existsSync } from 'node:fs';
|
|
15
|
-
import { loadConfig, splitCommand, withReadOnlyTools } from './config.js';
|
|
15
|
+
import { loadConfig, splitCommand, withReadOnlyTools, resolveFramework, } from './config.js';
|
|
16
16
|
const MAP_PATH = 'testforge.md';
|
|
17
17
|
/**
|
|
18
18
|
* Spawn a command, feed `input` on stdin, resolve with its stdout.
|
|
@@ -42,14 +42,10 @@ function runWithStdin(program, args, input) {
|
|
|
42
42
|
export function buildPrompt(journey, map, testPath, focus) {
|
|
43
43
|
const config = loadConfig();
|
|
44
44
|
// Opinionated: testforge only generates Jest (api/backend) or Playwright
|
|
45
|
-
// (web/UI) tests
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
const configured = (config.framework?.[journey.area] ?? '').toLowerCase();
|
|
50
|
-
const isApi = configured === 'jest' ||
|
|
51
|
-
configured === 'supertest' ||
|
|
52
|
-
(configured !== 'playwright' && journey.type === 'api');
|
|
45
|
+
// (web/UI) tests — any other framework (e.g. a project's Cypress setup)
|
|
46
|
+
// is clamped to the right one for the journey. Single source of truth in
|
|
47
|
+
// resolveFramework, shared with the output-layout decision.
|
|
48
|
+
const isApi = resolveFramework(config, journey.area, journey.type) === 'jest';
|
|
53
49
|
const framework = isApi
|
|
54
50
|
? 'Jest (with supertest for HTTP) for an API/backend test'
|
|
55
51
|
: 'Playwright for a web/UI test';
|
package/dist/core/config.js
CHANGED
|
@@ -99,6 +99,19 @@ export function splitCommand(command) {
|
|
|
99
99
|
const args = tokens.slice(1);
|
|
100
100
|
return { program, args };
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Resolve the test framework for a journey — clamped to the only two
|
|
104
|
+
* testforge supports. A web/UI journey is always Playwright (even if the
|
|
105
|
+
* project uses Cypress); an endpoint journey is Jest. This single source
|
|
106
|
+
* of truth drives BOTH the generation prompt and the output layout.
|
|
107
|
+
*/
|
|
108
|
+
export function resolveFramework(config, area, type) {
|
|
109
|
+
const configured = (config.framework?.[area] ?? '').toLowerCase();
|
|
110
|
+
const isApi = configured === 'jest' ||
|
|
111
|
+
configured === 'supertest' ||
|
|
112
|
+
(configured !== 'playwright' && type === 'api');
|
|
113
|
+
return isApi ? 'jest' : 'playwright';
|
|
114
|
+
}
|
|
102
115
|
/** Is this command Claude's CLI (so Claude-specific flags apply)? */
|
|
103
116
|
function isClaude(program) {
|
|
104
117
|
const base = program.split(/[\\/]/).pop() ?? program;
|
|
@@ -27,7 +27,7 @@ import { planSuite } from './suite-planner.js';
|
|
|
27
27
|
import { isSyntaxValid } from './validate.js';
|
|
28
28
|
import { hashContent, loadCache, saveCache } from './cache.js';
|
|
29
29
|
import { findMarkdownFiles } from './find-files.js';
|
|
30
|
-
import { loadConfig, testDirForType } from './config.js';
|
|
30
|
+
import { loadConfig, testDirForType, resolveFramework } from './config.js';
|
|
31
31
|
const CACHE_PATH = '.testforge/cache.json';
|
|
32
32
|
/** How many times to re-ask the AI when output won't parse. */
|
|
33
33
|
const MAX_GEN_ATTEMPTS = 2;
|
|
@@ -172,9 +172,17 @@ export async function runGenerate(docsDir, useAI = true, force = false, only) {
|
|
|
172
172
|
// --journey <x>: generate just the one matching journey.
|
|
173
173
|
if (only && !matchesJourney(file, journey.id, only))
|
|
174
174
|
continue;
|
|
175
|
-
//
|
|
175
|
+
// Layout depends on the framework's test runner:
|
|
176
|
+
// - Playwright (web/ops) recurses, so each journey gets its own
|
|
177
|
+
// FOLDER of focused specs (testDir/<journey>/<slug>).
|
|
178
|
+
// - Jest is often pointed at a flat glob (e.g. the-seam's
|
|
179
|
+
// `**/service/*.test.js`, which does NOT recurse), so api/lambda
|
|
180
|
+
// files stay FLAT in the test dir, journey-prefixed to stay unique
|
|
181
|
+
// (testDir/<journey>-<slug>). Otherwise they'd silently not run.
|
|
176
182
|
const areaDir = testDirForType(config, journey.area);
|
|
177
|
-
const
|
|
183
|
+
const framework = resolveFramework(config, journey.area, journey.type);
|
|
184
|
+
const flat = framework === 'jest';
|
|
185
|
+
const testDir = flat ? areaDir : join(areaDir, journey.id);
|
|
178
186
|
const seedPath = join(seedDir, `${journey.id}${seedExt}`);
|
|
179
187
|
// Skip if unchanged.
|
|
180
188
|
if (!force && cache[file] === hash) {
|
|
@@ -201,7 +209,11 @@ export async function runGenerate(docsDir, useAI = true, force = false, only) {
|
|
|
201
209
|
// call is independent, so a 15-file journey finishes in ~4 waves
|
|
202
210
|
// instead of 15 serial calls.
|
|
203
211
|
const generated = await pool(planned, GEN_CONCURRENCY, async (pf) => {
|
|
204
|
-
|
|
212
|
+
// Flat (jest): prefix the file with the journey so many journeys can
|
|
213
|
+
// share one dir without clashing. Folder (playwright): the journey
|
|
214
|
+
// folder already namespaces it, so the slug alone is enough.
|
|
215
|
+
const base = flat ? `${journey.id}-${pf.slug}` : pf.slug;
|
|
216
|
+
const filePath = join(testDir, `${base}${ext}`);
|
|
205
217
|
let content;
|
|
206
218
|
let status;
|
|
207
219
|
if (useAI) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@activate-core/test-forge",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
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": {
|