@qulib/core 0.8.2 โ 0.10.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/README.md +38 -13
- package/bin/qulib.js +2 -3
- package/dist/__tests__/playwright-available.d.ts +32 -0
- package/dist/__tests__/playwright-available.d.ts.map +1 -0
- package/dist/__tests__/playwright-available.js +35 -0
- package/dist/adapters/ci-results-adapter.d.ts +67 -0
- package/dist/adapters/ci-results-adapter.d.ts.map +1 -0
- package/dist/adapters/ci-results-adapter.js +143 -0
- package/dist/adapters/cypress-e2e-adapter.d.ts.map +1 -1
- package/dist/adapters/cypress-e2e-adapter.js +25 -2
- package/dist/adapters/playwright-adapter.d.ts.map +1 -1
- package/dist/adapters/playwright-adapter.js +25 -2
- package/dist/adapters/pr-metadata-adapter.d.ts +75 -0
- package/dist/adapters/pr-metadata-adapter.d.ts.map +1 -0
- package/dist/adapters/pr-metadata-adapter.js +146 -0
- package/dist/adapters/validate-specs.d.ts +55 -0
- package/dist/adapters/validate-specs.d.ts.map +1 -0
- package/dist/adapters/validate-specs.js +67 -0
- package/dist/baseline/baseline.d.ts +54 -0
- package/dist/baseline/baseline.d.ts.map +1 -0
- package/dist/baseline/baseline.js +252 -0
- package/dist/baseline/baseline.schema.d.ts +233 -0
- package/dist/baseline/baseline.schema.d.ts.map +1 -0
- package/dist/baseline/baseline.schema.js +59 -0
- package/dist/cli/analyze-diff-run.d.ts +77 -0
- package/dist/cli/analyze-diff-run.d.ts.map +1 -0
- package/dist/cli/analyze-diff-run.js +266 -0
- package/dist/cli/baseline-run.d.ts +55 -0
- package/dist/cli/baseline-run.d.ts.map +1 -0
- package/dist/cli/baseline-run.js +259 -0
- package/dist/cli/confidence-run.d.ts +16 -0
- package/dist/cli/confidence-run.d.ts.map +1 -0
- package/dist/cli/confidence-run.js +162 -0
- package/dist/cli/index.d.ts +11 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +84 -4
- package/dist/cli/scaffold-run.d.ts +86 -0
- package/dist/cli/scaffold-run.d.ts.map +1 -0
- package/dist/cli/scaffold-run.js +232 -0
- package/dist/cli/score-automation-run.d.ts +25 -0
- package/dist/cli/score-automation-run.d.ts.map +1 -0
- package/dist/cli/score-automation-run.js +127 -0
- package/dist/examples/notquality-dogfood/fixture.d.ts +166 -0
- package/dist/examples/notquality-dogfood/fixture.d.ts.map +1 -0
- package/dist/examples/notquality-dogfood/fixture.js +174 -0
- package/dist/examples/notquality-dogfood/run.d.ts +34 -0
- package/dist/examples/notquality-dogfood/run.d.ts.map +1 -0
- package/dist/examples/notquality-dogfood/run.js +139 -0
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/recipes/a11y.d.ts +36 -0
- package/dist/recipes/a11y.d.ts.map +1 -0
- package/dist/recipes/a11y.js +118 -0
- package/dist/recipes/auth.d.ts +38 -0
- package/dist/recipes/auth.d.ts.map +1 -0
- package/dist/recipes/auth.js +156 -0
- package/dist/recipes/index.d.ts +26 -0
- package/dist/recipes/index.d.ts.map +1 -0
- package/dist/recipes/index.js +41 -0
- package/dist/recipes/nav.d.ts +34 -0
- package/dist/recipes/nav.d.ts.map +1 -0
- package/dist/recipes/nav.js +128 -0
- package/dist/recipes/seed.d.ts +34 -0
- package/dist/recipes/seed.d.ts.map +1 -0
- package/dist/recipes/seed.js +87 -0
- package/dist/reporters/heatmap.d.ts +55 -0
- package/dist/reporters/heatmap.d.ts.map +1 -0
- package/dist/reporters/heatmap.js +146 -0
- package/dist/reporters/markdown-reporter.d.ts.map +1 -1
- package/dist/reporters/markdown-reporter.js +4 -1
- package/dist/scaffold-tests.d.ts +21 -0
- package/dist/scaffold-tests.d.ts.map +1 -1
- package/dist/scaffold-tests.js +12 -2
- package/dist/schemas/confidence.schema.d.ts +526 -0
- package/dist/schemas/confidence.schema.d.ts.map +1 -0
- package/dist/schemas/confidence.schema.js +161 -0
- package/dist/schemas/config.schema.d.ts.map +1 -1
- package/dist/schemas/config.schema.js +6 -1
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +3 -0
- package/dist/schemas/recipe.schema.d.ts +66 -0
- package/dist/schemas/recipe.schema.d.ts.map +1 -0
- package/dist/schemas/recipe.schema.js +45 -0
- package/dist/schemas/views.schema.d.ts +234 -0
- package/dist/schemas/views.schema.d.ts.map +1 -0
- package/dist/schemas/views.schema.js +82 -0
- package/dist/tools/scoring/confidence-from-qulib.d.ts +34 -0
- package/dist/tools/scoring/confidence-from-qulib.d.ts.map +1 -0
- package/dist/tools/scoring/confidence-from-qulib.js +206 -0
- package/dist/tools/scoring/confidence-views.d.ts +40 -0
- package/dist/tools/scoring/confidence-views.d.ts.map +1 -0
- package/dist/tools/scoring/confidence-views.js +163 -0
- package/dist/tools/scoring/confidence.d.ts +32 -0
- package/dist/tools/scoring/confidence.d.ts.map +1 -0
- package/dist/tools/scoring/confidence.js +180 -0
- package/dist/tools/scoring/levels.d.ts +15 -0
- package/dist/tools/scoring/levels.d.ts.map +1 -0
- package/dist/tools/scoring/levels.js +21 -0
- package/package.json +18 -8
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-page coverage heatmap for Markdown reports.
|
|
3
|
+
*
|
|
4
|
+
* buildPageHeatmap() is a pure function โ no I/O, no side-effects.
|
|
5
|
+
* It derives a rows ร dimensions matrix from GapAnalysis.gaps and sorts
|
|
6
|
+
* rows worst-first (most critical coverage problems bubble to the top).
|
|
7
|
+
*
|
|
8
|
+
* Dimensions map to the GapSchema.category enum values so the heatmap
|
|
9
|
+
* always stays in sync with what the scanner can produce.
|
|
10
|
+
*/
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Types
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/** The ordered set of gap categories that appear as heatmap columns. */
|
|
15
|
+
export const HEATMAP_DIMENSIONS = [
|
|
16
|
+
'untested-route',
|
|
17
|
+
'a11y',
|
|
18
|
+
'console-error',
|
|
19
|
+
'broken-link',
|
|
20
|
+
'coverage',
|
|
21
|
+
'untested-api-endpoint',
|
|
22
|
+
'auth-surface',
|
|
23
|
+
];
|
|
24
|
+
/** Display labels for each dimension column header. */
|
|
25
|
+
export const DIMENSION_LABELS = {
|
|
26
|
+
'untested-route': 'Untested',
|
|
27
|
+
'a11y': 'A11y',
|
|
28
|
+
'console-error': 'Console',
|
|
29
|
+
'broken-link': 'Links',
|
|
30
|
+
'coverage': 'Coverage',
|
|
31
|
+
'untested-api-endpoint': 'API',
|
|
32
|
+
'auth-surface': 'Auth',
|
|
33
|
+
};
|
|
34
|
+
/** Severity order โ higher index = worse. */
|
|
35
|
+
const SEVERITY_ORDER = {
|
|
36
|
+
low: 1,
|
|
37
|
+
medium: 2,
|
|
38
|
+
high: 3,
|
|
39
|
+
critical: 4,
|
|
40
|
+
};
|
|
41
|
+
/** Intensity glyph scale, indexed by SEVERITY_ORDER value (1..4). */
|
|
42
|
+
const SEVERITY_GLYPHS = {
|
|
43
|
+
0: 'ยท', // no gap on this page for this dimension
|
|
44
|
+
1: '๐ก', // low
|
|
45
|
+
2: '๐ ', // medium
|
|
46
|
+
3: '๐ด', // high
|
|
47
|
+
4: '๐จ', // critical
|
|
48
|
+
};
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Pure builder
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
/**
|
|
53
|
+
* Build a per-page coverage heatmap from a flat list of gaps.
|
|
54
|
+
*
|
|
55
|
+
* @param gaps The gaps array from GapAnalysis.
|
|
56
|
+
* @returns A PageHeatmap with rows sorted worst-first.
|
|
57
|
+
*/
|
|
58
|
+
export function buildPageHeatmap(gaps) {
|
|
59
|
+
const pageMap = new Map();
|
|
60
|
+
for (const gap of gaps) {
|
|
61
|
+
// Only include dimensions the heatmap tracks; skip unknowns gracefully.
|
|
62
|
+
const dim = gap.category;
|
|
63
|
+
if (!HEATMAP_DIMENSIONS.includes(dim))
|
|
64
|
+
continue;
|
|
65
|
+
if (!pageMap.has(gap.path)) {
|
|
66
|
+
pageMap.set(gap.path, new Map());
|
|
67
|
+
}
|
|
68
|
+
const dimMap = pageMap.get(gap.path);
|
|
69
|
+
if (!dimMap.has(dim)) {
|
|
70
|
+
dimMap.set(dim, []);
|
|
71
|
+
}
|
|
72
|
+
dimMap.get(dim).push(gap);
|
|
73
|
+
}
|
|
74
|
+
const rows = [];
|
|
75
|
+
for (const [path, dimMap] of pageMap) {
|
|
76
|
+
const cells = {};
|
|
77
|
+
let worstScore = 0;
|
|
78
|
+
for (const dim of HEATMAP_DIMENSIONS) {
|
|
79
|
+
const dimGaps = dimMap.get(dim) ?? [];
|
|
80
|
+
if (dimGaps.length === 0) {
|
|
81
|
+
cells[dim] = { worstSeverity: null, glyph: SEVERITY_GLYPHS[0], count: 0 };
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
let worst = 'low';
|
|
85
|
+
for (const g of dimGaps) {
|
|
86
|
+
if (SEVERITY_ORDER[g.severity] > SEVERITY_ORDER[worst]) {
|
|
87
|
+
worst = g.severity;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const score = SEVERITY_ORDER[worst];
|
|
91
|
+
worstScore += score;
|
|
92
|
+
cells[dim] = { worstSeverity: worst, glyph: SEVERITY_GLYPHS[score], count: dimGaps.length };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
rows.push({ path, cells, worstScore });
|
|
96
|
+
}
|
|
97
|
+
// Sort worst-first (highest worstScore first), then alphabetically by path for stability.
|
|
98
|
+
rows.sort((a, b) => {
|
|
99
|
+
if (b.worstScore !== a.worstScore)
|
|
100
|
+
return b.worstScore - a.worstScore;
|
|
101
|
+
return a.path.localeCompare(b.path);
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
rows,
|
|
105
|
+
dimensions: HEATMAP_DIMENSIONS,
|
|
106
|
+
totalGaps: gaps.length,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Markdown renderer
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
/**
|
|
113
|
+
* Render a PageHeatmap as a Markdown section string.
|
|
114
|
+
* Returns an empty string when there are no rows (nothing scanned).
|
|
115
|
+
*/
|
|
116
|
+
export function renderHeatmapSection(heatmap) {
|
|
117
|
+
if (heatmap.rows.length === 0) {
|
|
118
|
+
return '';
|
|
119
|
+
}
|
|
120
|
+
const dimLabels = heatmap.dimensions.map((d) => DIMENSION_LABELS[d]);
|
|
121
|
+
// Build table header
|
|
122
|
+
const header = `| Page | ${dimLabels.join(' | ')} |`;
|
|
123
|
+
const separator = `|------|${heatmap.dimensions.map(() => ':---:').join('|')}|`;
|
|
124
|
+
const tableRows = heatmap.rows
|
|
125
|
+
.map((row) => {
|
|
126
|
+
const cells = heatmap.dimensions.map((d) => row.cells[d].glyph).join(' | ');
|
|
127
|
+
return `| \`${row.path}\` | ${cells} |`;
|
|
128
|
+
})
|
|
129
|
+
.join('\n');
|
|
130
|
+
const legend = [
|
|
131
|
+
'**Legend:**',
|
|
132
|
+
`๐จ critical`,
|
|
133
|
+
`๐ด high`,
|
|
134
|
+
`๐ medium`,
|
|
135
|
+
`๐ก low`,
|
|
136
|
+
`ยท none`,
|
|
137
|
+
].join(' ยท ');
|
|
138
|
+
return `## Per-page coverage heatmap
|
|
139
|
+
|
|
140
|
+
${header}
|
|
141
|
+
${separator}
|
|
142
|
+
${tableRows}
|
|
143
|
+
|
|
144
|
+
${legend}
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/markdown-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown-reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/markdown-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAGrE,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqEjG"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { writeFile, mkdir } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { buildPageHeatmap, renderHeatmapSection } from './heatmap.js';
|
|
3
4
|
export async function writeMarkdownReport(analysis, outputDir) {
|
|
4
5
|
await mkdir(outputDir, { recursive: true });
|
|
5
6
|
const rc = analysis.releaseConfidence;
|
|
@@ -16,6 +17,8 @@ export async function writeMarkdownReport(analysis, outputDir) {
|
|
|
16
17
|
const scenarioBlocks = analysis.scenarios
|
|
17
18
|
.map((s) => `### ${s.title}\n${s.description}\n\nSteps:\n${s.steps.map((step) => `- ${step.description}`).join('\n')}\n\nRecommended adapters: ${s.recommendations.map((r) => r.adapter).join(', ')}`)
|
|
18
19
|
.join('\n\n---\n\n');
|
|
20
|
+
const heatmap = buildPageHeatmap(analysis.gaps);
|
|
21
|
+
const heatmapSection = renderHeatmapSection(heatmap);
|
|
19
22
|
const ci = analysis.costIntelligence;
|
|
20
23
|
const costSection = ci
|
|
21
24
|
? `## Cost Intelligence
|
|
@@ -41,7 +44,7 @@ export async function writeMarkdownReport(analysis, outputDir) {
|
|
|
41
44
|
- Scan budget exhausted (unfinished queue): ${analysis.coverageBudgetExceeded ? 'yes' : 'no'}
|
|
42
45
|
${analysis.coverageWarning ? `- Warning: **${analysis.coverageWarning}**` : '- Warning: none'}
|
|
43
46
|
|
|
44
|
-
${costSection}
|
|
47
|
+
${heatmapSection}${costSection}
|
|
45
48
|
## Coverage gaps (${analysis.gaps.length})
|
|
46
49
|
|
|
47
50
|
| Path | Category | Severity | Reason |
|
package/dist/scaffold-tests.d.ts
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
+
import { type SpecValidationReport } from './adapters/validate-specs.js';
|
|
1
2
|
import type { NeutralScenario, GeneratedTest } from './schemas/gap-analysis.schema.js';
|
|
2
3
|
import type { AdapterType } from './schemas/config.schema.js';
|
|
3
4
|
import type { AnalyzeProgressSink } from './harness/progress-log.js';
|
|
4
5
|
import type { TelemetrySink } from './telemetry/telemetry.interface.js';
|
|
6
|
+
import type { RecipeId, RecipeConfig } from './schemas/recipe.schema.js';
|
|
5
7
|
export interface ScaffoldOptions {
|
|
6
8
|
framework?: Extract<AdapterType, 'cypress-e2e' | 'playwright'>;
|
|
7
9
|
maxPagesToScan?: number;
|
|
8
10
|
scenarios?: NeutralScenario[];
|
|
9
11
|
progressLog?: AnalyzeProgressSink;
|
|
10
12
|
telemetry?: TelemetrySink;
|
|
13
|
+
/**
|
|
14
|
+
* Optional list of recipe ids to expand into additional scenarios.
|
|
15
|
+
* Recipe scenarios are APPENDED to any scenarios derived from analysis or
|
|
16
|
+
* supplied via `scenarios` โ they never replace existing content.
|
|
17
|
+
* Example: ['auth', 'a11y'] appends login-flow + a11y scenarios.
|
|
18
|
+
*/
|
|
19
|
+
recipes?: RecipeId[];
|
|
20
|
+
/**
|
|
21
|
+
* Per-recipe configuration overrides (selectors, routes, impact thresholds).
|
|
22
|
+
* Only consulted when `recipes` is non-empty.
|
|
23
|
+
*/
|
|
24
|
+
recipeConfig?: RecipeConfig;
|
|
11
25
|
}
|
|
12
26
|
export interface ProjectConfig {
|
|
13
27
|
configFile: {
|
|
@@ -29,6 +43,13 @@ export interface ScaffoldResult {
|
|
|
29
43
|
generatedTests: GeneratedTest[];
|
|
30
44
|
scenarios: NeutralScenario[];
|
|
31
45
|
projectConfig: ProjectConfig;
|
|
46
|
+
/**
|
|
47
|
+
* Dry-run validation of every generated spec: each spec is transpiled through
|
|
48
|
+
* the TypeScript compiler and any parse/compile error is surfaced here. This
|
|
49
|
+
* is the witness that the scaffold did not emit broken code โ `ok: false`
|
|
50
|
+
* means at least one generated spec will not parse. Always populated.
|
|
51
|
+
*/
|
|
52
|
+
specValidation: SpecValidationReport;
|
|
32
53
|
}
|
|
33
54
|
export declare function scaffoldTests(url: string, options?: ScaffoldOptions): Promise<ScaffoldResult>;
|
|
34
55
|
//# sourceMappingURL=scaffold-tests.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold-tests.d.ts","sourceRoot":"","sources":["../src/scaffold-tests.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"scaffold-tests.d.ts","sourceRoot":"","sources":["../src/scaffold-tests.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEjG,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAEzE,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC;IACrB;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,WAAW,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC1F,YAAY,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC,WAAW,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC;IAC9D,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,aAAa,EAAE,aAAa,CAAC;IAC7B;;;;;OAKG;IACH,cAAc,EAAE,oBAAoB,CAAC;CACtC;AA8ED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CAmDzB"}
|
package/dist/scaffold-tests.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { analyzeApp } from './analyze.js';
|
|
2
2
|
import { createAdapter } from './adapters/adapter-factory.js';
|
|
3
|
+
import { validateGeneratedTests } from './adapters/validate-specs.js';
|
|
4
|
+
import { expandRecipes } from './recipes/index.js';
|
|
3
5
|
function buildCypressProjectConfig(url) {
|
|
4
6
|
return {
|
|
5
7
|
configFile: {
|
|
@@ -104,10 +106,18 @@ export async function scaffoldTests(url, options = {}) {
|
|
|
104
106
|
});
|
|
105
107
|
scenarios = result.gapAnalysis.scenarios;
|
|
106
108
|
}
|
|
109
|
+
// Expand any requested recipes and append their scenarios (additive โ never replace).
|
|
110
|
+
const recipeScenarios = expandRecipes(options.recipes, options.recipeConfig ?? {});
|
|
111
|
+
const allScenarios = recipeScenarios.length > 0
|
|
112
|
+
? [...scenarios, ...recipeScenarios]
|
|
113
|
+
: scenarios;
|
|
107
114
|
const adapter = createAdapter(framework);
|
|
108
|
-
const generatedTests = adapter.renderAll(
|
|
115
|
+
const generatedTests = adapter.renderAll(allScenarios);
|
|
109
116
|
const projectConfig = framework === 'cypress-e2e'
|
|
110
117
|
? buildCypressProjectConfig(url)
|
|
111
118
|
: buildPlaywrightProjectConfig(url);
|
|
112
|
-
|
|
119
|
+
// Dry-run every generated spec through the TS compiler so a parse/compile
|
|
120
|
+
// failure is caught here, not when a developer first runs the suite.
|
|
121
|
+
const specValidation = validateGeneratedTests(generatedTests);
|
|
122
|
+
return { url, framework, generatedTests, scenarios: allScenarios, projectConfig, specValidation };
|
|
113
123
|
}
|