@activate-core/test-forge 0.3.1 → 0.4.0
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 +12 -7
- package/dist/core/index.js +1 -1
- package/dist/core/init.js +11 -4
- package/dist/core/parser.js +9 -2
- package/dist/core/run-generate.js +9 -5
- package/package.json +1 -1
|
@@ -17,13 +17,18 @@ const MAP_PATH = 'testforge.md';
|
|
|
17
17
|
/** Build the prompt we send to the AI for one journey. */
|
|
18
18
|
export function buildPrompt(journey, map, testPath) {
|
|
19
19
|
const config = loadConfig();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
// Opinionated: testforge only generates Jest (api/backend) or Playwright
|
|
21
|
+
// (web/UI) tests. We look the framework up by AREA, but clamp anything
|
|
22
|
+
// that is not jest/supertest/playwright (e.g. a project's cypress setup)
|
|
23
|
+
// to the sensible default for the journey's Type — so no other framework
|
|
24
|
+
// ever leaks into generated output.
|
|
25
|
+
const configured = (config.framework?.[journey.area] ?? '').toLowerCase();
|
|
26
|
+
const isApi = configured === 'jest' ||
|
|
27
|
+
configured === 'supertest' ||
|
|
28
|
+
(configured !== 'playwright' && journey.type === 'api');
|
|
29
|
+
const framework = isApi
|
|
30
|
+
? 'Jest (with supertest for HTTP) for an API/backend test'
|
|
31
|
+
: 'Playwright for a web/UI test';
|
|
27
32
|
const ext = config.testExtension ?? '.test.ts';
|
|
28
33
|
const isJs = ext.endsWith('.js');
|
|
29
34
|
const language = isJs
|
package/dist/core/index.js
CHANGED
package/dist/core/init.js
CHANGED
|
@@ -29,10 +29,17 @@ export function buildInitPrompt() {
|
|
|
29
29
|
` Point each to where that area's tests belong (e.g. in a monorepo,`,
|
|
30
30
|
` api -> "packages/api/test", web -> "packages/webapp/tests", ops ->`,
|
|
31
31
|
` "packages/ops/tests"; in a simple app, one area mapping to "tests").`,
|
|
32
|
-
|
|
33
|
-
`
|
|
34
|
-
`
|
|
35
|
-
|
|
32
|
+
` For UI areas, use a plain tests folder (e.g. ".../tests" or`,
|
|
33
|
+
` ".../tests/e2e") — do NOT point them at a Cypress folder such as`,
|
|
34
|
+
` "cypress/integration", because testforge does not generate Cypress.`,
|
|
35
|
+
`- framework: an object mapping each area to its test framework.`,
|
|
36
|
+
` testforge ONLY generates two kinds of tests, so each value MUST be`,
|
|
37
|
+
` exactly "jest" or "playwright" — never "cypress", "mocha", "vitest",`,
|
|
38
|
+
` or anything else, EVEN IF the project currently uses one of those.`,
|
|
39
|
+
` Choose the closest fit: backend / API / serverless / lambda / worker`,
|
|
40
|
+
` areas -> "jest"; UI / web / browser / admin-panel areas ->`,
|
|
41
|
+
` "playwright". Example: { "api": "jest", "web": "playwright", "ops":`,
|
|
42
|
+
` "playwright", "lambda": "jest" }.`,
|
|
36
43
|
`- testExtension: ".test.ts" or ".test.js" — match what existing`,
|
|
37
44
|
` tests use, or the project's language (TypeScript vs JavaScript).`,
|
|
38
45
|
`- aiCommand: keep it as "claude -p" unless there is a clear reason`,
|
package/dist/core/parser.js
CHANGED
|
@@ -65,8 +65,14 @@ function parseField(line) {
|
|
|
65
65
|
const required = parts.includes('required');
|
|
66
66
|
return { name, type, required };
|
|
67
67
|
}
|
|
68
|
-
/**
|
|
69
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Parse a full markdown string into a Journey object.
|
|
70
|
+
*
|
|
71
|
+
* `area` is the journey's docs subfolder (e.g. "api", "ops"), which drives
|
|
72
|
+
* output location and framework. When omitted (a journey read outside an
|
|
73
|
+
* area folder), it falls back to the parsed `Type:`.
|
|
74
|
+
*/
|
|
75
|
+
export function parseJourney(markdown, area) {
|
|
70
76
|
const lines = markdown.split('\n').map((l) => l.trim());
|
|
71
77
|
let name = '';
|
|
72
78
|
let type = 'web';
|
|
@@ -112,6 +118,7 @@ export function parseJourney(markdown) {
|
|
|
112
118
|
return {
|
|
113
119
|
id: toId(name),
|
|
114
120
|
name,
|
|
121
|
+
area: area ?? type,
|
|
115
122
|
type,
|
|
116
123
|
route,
|
|
117
124
|
coverage,
|
|
@@ -56,8 +56,7 @@ function generateValidated(generate, ext) {
|
|
|
56
56
|
* syntax gate + retry) and falls back to the deterministic basic
|
|
57
57
|
* generator if AI output can't be made valid.
|
|
58
58
|
*/
|
|
59
|
-
function buildTest(
|
|
60
|
-
const journey = parseJourney(markdown);
|
|
59
|
+
function buildTest(journey, useAI, ext, testPath) {
|
|
61
60
|
if (useAI) {
|
|
62
61
|
const ai = generateValidated(() => generateWithAI(journey, testPath), ext);
|
|
63
62
|
if (ai !== null) {
|
|
@@ -116,9 +115,14 @@ export function runGenerate(docsDir, useAI = true, force = false) {
|
|
|
116
115
|
for (const file of files) {
|
|
117
116
|
const markdown = readFileSync(join(docsDir, file), 'utf-8');
|
|
118
117
|
const hash = hashContent(markdown);
|
|
119
|
-
|
|
118
|
+
// The area is the doc's top-level folder (api/web/ops/lambda/...). It
|
|
119
|
+
// decides where the test goes and which framework is used — NOT the
|
|
120
|
+
// "Type:" line, which only says UI-vs-endpoint. This keeps ops/lambda
|
|
121
|
+
// journeys in their own package instead of leaking into web/api.
|
|
122
|
+
const area = file.split(/[\\/]/)[0] || undefined;
|
|
123
|
+
const journey = parseJourney(markdown, area);
|
|
120
124
|
// Where this journey's test and seed go.
|
|
121
|
-
const testDir = testDirForType(config, journey.
|
|
125
|
+
const testDir = testDirForType(config, journey.area);
|
|
122
126
|
const testPath = join(testDir, `${journey.id}${ext}`);
|
|
123
127
|
const seedPath = join(seedDir, `${journey.id}${seedExt}`);
|
|
124
128
|
// Skip if unchanged.
|
|
@@ -137,7 +141,7 @@ export function runGenerate(docsDir, useAI = true, force = false) {
|
|
|
137
141
|
if (collision)
|
|
138
142
|
warnings.push(collision);
|
|
139
143
|
// 1. Test file (syntax-gated before writing).
|
|
140
|
-
const { content, status } = buildTest(
|
|
144
|
+
const { content, status } = buildTest(journey, useAI, ext, testPath);
|
|
141
145
|
mkdirSync(dirname(testPath), { recursive: true });
|
|
142
146
|
writeFileSync(testPath, content, 'utf-8');
|
|
143
147
|
// 2. Seed file (only when using AI — basic mode has no seed concept).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@activate-core/test-forge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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": {
|