@yasserkhanorg/e2e-agents 1.2.2 → 1.3.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/agent/feedback.d.ts +20 -0
- package/dist/agent/feedback.d.ts.map +1 -1
- package/dist/agent/feedback.js +4 -0
- package/dist/esm/agent/feedback.js +3 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/qa-agent/cli.js +205 -0
- package/dist/esm/qa-agent/orchestrator.js +120 -0
- package/dist/esm/qa-agent/phase1/runner.js +139 -0
- package/dist/esm/qa-agent/phase1/scope.js +126 -0
- package/dist/esm/qa-agent/phase2/agent_browser.js +95 -0
- package/dist/esm/qa-agent/phase2/agent_loop.js +315 -0
- package/dist/esm/qa-agent/phase2/exploration_state.js +76 -0
- package/dist/esm/qa-agent/phase2/tools.js +288 -0
- package/dist/esm/qa-agent/phase2/vision.js +75 -0
- package/dist/esm/qa-agent/phase3/feedback.js +34 -0
- package/dist/esm/qa-agent/phase3/reporter.js +118 -0
- package/dist/esm/qa-agent/phase3/spec_generator.js +62 -0
- package/dist/esm/qa-agent/phase3/verdict.js +66 -0
- package/dist/esm/qa-agent/safe_env.js +23 -0
- package/dist/esm/qa-agent/types.js +3 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/qa-agent/cli.d.ts +3 -0
- package/dist/qa-agent/cli.d.ts.map +1 -0
- package/dist/qa-agent/cli.js +207 -0
- package/dist/qa-agent/orchestrator.d.ts +3 -0
- package/dist/qa-agent/orchestrator.d.ts.map +1 -0
- package/dist/qa-agent/orchestrator.js +123 -0
- package/dist/qa-agent/phase1/runner.d.ts +3 -0
- package/dist/qa-agent/phase1/runner.d.ts.map +1 -0
- package/dist/qa-agent/phase1/runner.js +142 -0
- package/dist/qa-agent/phase1/scope.d.ts +6 -0
- package/dist/qa-agent/phase1/scope.d.ts.map +1 -0
- package/dist/qa-agent/phase1/scope.js +129 -0
- package/dist/qa-agent/phase2/agent_browser.d.ts +35 -0
- package/dist/qa-agent/phase2/agent_browser.d.ts.map +1 -0
- package/dist/qa-agent/phase2/agent_browser.js +99 -0
- package/dist/qa-agent/phase2/agent_loop.d.ts +3 -0
- package/dist/qa-agent/phase2/agent_loop.d.ts.map +1 -0
- package/dist/qa-agent/phase2/agent_loop.js +321 -0
- package/dist/qa-agent/phase2/exploration_state.d.ts +12 -0
- package/dist/qa-agent/phase2/exploration_state.d.ts.map +1 -0
- package/dist/qa-agent/phase2/exploration_state.js +88 -0
- package/dist/qa-agent/phase2/tools.d.ts +28 -0
- package/dist/qa-agent/phase2/tools.d.ts.map +1 -0
- package/dist/qa-agent/phase2/tools.js +292 -0
- package/dist/qa-agent/phase2/vision.d.ts +3 -0
- package/dist/qa-agent/phase2/vision.d.ts.map +1 -0
- package/dist/qa-agent/phase2/vision.js +78 -0
- package/dist/qa-agent/phase3/feedback.d.ts +3 -0
- package/dist/qa-agent/phase3/feedback.d.ts.map +1 -0
- package/dist/qa-agent/phase3/feedback.js +37 -0
- package/dist/qa-agent/phase3/reporter.d.ts +3 -0
- package/dist/qa-agent/phase3/reporter.d.ts.map +1 -0
- package/dist/qa-agent/phase3/reporter.js +121 -0
- package/dist/qa-agent/phase3/spec_generator.d.ts +3 -0
- package/dist/qa-agent/phase3/spec_generator.d.ts.map +1 -0
- package/dist/qa-agent/phase3/spec_generator.js +65 -0
- package/dist/qa-agent/phase3/verdict.d.ts +3 -0
- package/dist/qa-agent/phase3/verdict.d.ts.map +1 -0
- package/dist/qa-agent/phase3/verdict.js +69 -0
- package/dist/qa-agent/safe_env.d.ts +3 -0
- package/dist/qa-agent/safe_env.d.ts.map +1 -0
- package/dist/qa-agent/safe_env.js +26 -0
- package/dist/qa-agent/types.d.ts +122 -0
- package/dist/qa-agent/types.d.ts.map +1 -0
- package/dist/qa-agent/types.js +4 -0
- package/package.json +12 -3
package/dist/index.d.ts
CHANGED
|
@@ -24,8 +24,8 @@ export { analyzeImpact as analyzeImpactV2, getGaps, getPartialGaps } from './eng
|
|
|
24
24
|
export type { ImpactResult, ImpactedFeature, CoverageStatus, ImpactEngineOptions, SpecWithScenarios } from './engine/impact_engine.js';
|
|
25
25
|
export { extractScenarios } from './engine/impact_engine.js';
|
|
26
26
|
export { buildPlanFromImpact } from './engine/plan_builder.js';
|
|
27
|
-
export { appendFeedbackAndRecompute, readCalibration } from './agent/feedback.js';
|
|
28
|
-
export type { RecommendationFeedbackEntry, CalibrationSummary } from './agent/feedback.js';
|
|
27
|
+
export { appendFeedbackAndRecompute, readCalibration, readFlakyTests } from './agent/feedback.js';
|
|
28
|
+
export type { RecommendationFeedbackEntry, CalibrationSummary, FlakySummary } from './agent/feedback.js';
|
|
29
29
|
export { finalizeGeneratedTests } from './agent/handoff.js';
|
|
30
30
|
export type { FinalizeGeneratedTestsOptions, FinalizeGeneratedTestsResult } from './agent/handoff.js';
|
|
31
31
|
export { ingestTraceabilityInput } from './agent/traceability_ingest.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAGH,YAAY,EACR,WAAW,EACX,eAAe,EACf,UAAU,EACV,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,YAAY,GACf,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAC,gBAAgB,EAAE,0BAA0B,EAAC,MAAM,yBAAyB,CAAC;AAGrF,OAAO,EAAC,iBAAiB,EAAE,mBAAmB,EAAC,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EAAC,cAAc,EAAE,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAC,cAAc,EAAE,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AAGpD,OAAO,EAAC,kBAAkB,EAAE,qBAAqB,EAAC,MAAM,uBAAuB,CAAC;AAChF,YAAY,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAGxD,OAAO,EAAC,0BAA0B,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,mBAAmB,EAAC,MAAM,UAAU,CAAC;AACjJ,YAAY,EACR,eAAe,EACf,sBAAsB,EACtB,4BAA4B,EAC5B,6BAA6B,GAChC,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAC,aAAa,IAAI,eAAe,EAAE,OAAO,EAAE,cAAc,EAAC,MAAM,2BAA2B,CAAC;AACpG,YAAY,EAAC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAmB,EAAE,iBAAiB,EAAC,MAAM,2BAA2B,CAAC;AACrI,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAC,mBAAmB,EAAC,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAC,0BAA0B,EAAE,eAAe,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
3
|
// See LICENSE.txt for license information.
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.runAgenticGeneration = exports.getSpecsForFamily = exports.buildSpecIndex = exports.loadOrBuildApiSurface = exports.buildApiSurface = exports.getUserFlowsForBinding = exports.getPriorityForBinding = exports.getCypressSpecDirsForBinding = exports.bindFilesToFamilies = exports.loadRouteFamilyManifest = exports.buildQualityFixPrompt = exports.buildHealPrompt = exports.renderHealMarkdown = exports.resolveHealTargets = exports.healFromReport = exports.runHealStage = exports.detectHallucinatedMethods = exports.parseGenerationResponse = exports.buildGenerationPrompt = exports.runGenerationStage = exports.runPipeline = exports.captureTraceabilityInput = exports.ingestTraceabilityInput = exports.finalizeGeneratedTests = exports.readCalibration = exports.appendFeedbackAndRecompute = exports.buildPlanFromImpact = exports.extractScenarios = exports.getPartialGaps = exports.getGaps = exports.analyzeImpactV2 = exports.captureTraceability = exports.ingestTraceability = exports.handoffGeneratedTests = exports.recommendTestsDeterministic = exports.analyzeImpactDeterministic = exports.validateProviderSetup = exports.LLMProviderFactory = exports.CustomProvider = exports.checkOpenAISetup = exports.OpenAIProvider = exports.checkOllamaSetup = exports.OllamaProvider = exports.checkAnthropicSetup = exports.AnthropicProvider = exports.UnsupportedCapabilityError = exports.LLMProviderError = void 0;
|
|
5
|
+
exports.runAgenticGeneration = exports.getSpecsForFamily = exports.buildSpecIndex = exports.loadOrBuildApiSurface = exports.buildApiSurface = exports.getUserFlowsForBinding = exports.getPriorityForBinding = exports.getCypressSpecDirsForBinding = exports.bindFilesToFamilies = exports.loadRouteFamilyManifest = exports.buildQualityFixPrompt = exports.buildHealPrompt = exports.renderHealMarkdown = exports.resolveHealTargets = exports.healFromReport = exports.runHealStage = exports.detectHallucinatedMethods = exports.parseGenerationResponse = exports.buildGenerationPrompt = exports.runGenerationStage = exports.runPipeline = exports.captureTraceabilityInput = exports.ingestTraceabilityInput = exports.finalizeGeneratedTests = exports.readFlakyTests = exports.readCalibration = exports.appendFeedbackAndRecompute = exports.buildPlanFromImpact = exports.extractScenarios = exports.getPartialGaps = exports.getGaps = exports.analyzeImpactV2 = exports.captureTraceability = exports.ingestTraceability = exports.handoffGeneratedTests = exports.recommendTestsDeterministic = exports.analyzeImpactDeterministic = exports.validateProviderSetup = exports.LLMProviderFactory = exports.CustomProvider = exports.checkOpenAISetup = exports.OpenAIProvider = exports.checkOllamaSetup = exports.OllamaProvider = exports.checkAnthropicSetup = exports.AnthropicProvider = exports.UnsupportedCapabilityError = exports.LLMProviderError = void 0;
|
|
6
6
|
var provider_interface_js_1 = require("./provider_interface.js");
|
|
7
7
|
Object.defineProperty(exports, "LLMProviderError", { enumerable: true, get: function () { return provider_interface_js_1.LLMProviderError; } });
|
|
8
8
|
Object.defineProperty(exports, "UnsupportedCapabilityError", { enumerable: true, get: function () { return provider_interface_js_1.UnsupportedCapabilityError; } });
|
|
@@ -41,6 +41,7 @@ Object.defineProperty(exports, "buildPlanFromImpact", { enumerable: true, get: f
|
|
|
41
41
|
var feedback_js_1 = require("./agent/feedback.js");
|
|
42
42
|
Object.defineProperty(exports, "appendFeedbackAndRecompute", { enumerable: true, get: function () { return feedback_js_1.appendFeedbackAndRecompute; } });
|
|
43
43
|
Object.defineProperty(exports, "readCalibration", { enumerable: true, get: function () { return feedback_js_1.readCalibration; } });
|
|
44
|
+
Object.defineProperty(exports, "readFlakyTests", { enumerable: true, get: function () { return feedback_js_1.readFlakyTests; } });
|
|
44
45
|
var handoff_js_1 = require("./agent/handoff.js");
|
|
45
46
|
Object.defineProperty(exports, "finalizeGeneratedTests", { enumerable: true, get: function () { return handoff_js_1.finalizeGeneratedTests; } });
|
|
46
47
|
var traceability_ingest_js_1 = require("./agent/traceability_ingest.js");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/qa-agent/cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
4
|
+
// See LICENSE.txt for license information.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const orchestrator_js_1 = require("./orchestrator.js");
|
|
8
|
+
const MODES = new Set(['pr', 'hunt', 'fix', 'release']);
|
|
9
|
+
const KNOWN_FLAGS = new Set([
|
|
10
|
+
'--base-url', '--since', '--phase', '--time', '--budget',
|
|
11
|
+
'--headed', '--tests-root', '--project', '--output', '--help', '-h',
|
|
12
|
+
]);
|
|
13
|
+
function printUsage() {
|
|
14
|
+
console.log(`
|
|
15
|
+
Usage: e2e-qa-agent <mode> [options]
|
|
16
|
+
|
|
17
|
+
Modes:
|
|
18
|
+
pr Test changed features from a PR
|
|
19
|
+
hunt Deep-dive into a specific area
|
|
20
|
+
fix Verify healed tests and side effects
|
|
21
|
+
release Full regression + release readiness verdict
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
--base-url <url> Application URL (required)
|
|
25
|
+
--since <ref> Git ref for diff (default: origin/main)
|
|
26
|
+
--phase <1|2|3> Run only up to this phase
|
|
27
|
+
--time <minutes> Time limit (default: 15)
|
|
28
|
+
--budget <usd> LLM budget in USD (default: 2.00)
|
|
29
|
+
--headed Run browser in headed mode
|
|
30
|
+
--tests-root <path> Path to tests directory
|
|
31
|
+
--project <name> Playwright project name
|
|
32
|
+
--output <dir> Output directory (default: .e2e-ai-agents)
|
|
33
|
+
--help Show this help
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
e2e-qa-agent pr --since origin/main --base-url http://localhost:8065
|
|
37
|
+
e2e-qa-agent hunt "channel settings" --base-url http://localhost:8065
|
|
38
|
+
e2e-qa-agent release --base-url http://localhost:8065 --time 30
|
|
39
|
+
e2e-qa-agent fix --base-url http://localhost:8065
|
|
40
|
+
`);
|
|
41
|
+
}
|
|
42
|
+
function parseCliArgs(argv) {
|
|
43
|
+
if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {
|
|
44
|
+
printUsage();
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const modeArg = argv[0];
|
|
48
|
+
if (!MODES.has(modeArg)) {
|
|
49
|
+
console.error(`Unknown mode: ${modeArg}`);
|
|
50
|
+
printUsage();
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const mode = modeArg;
|
|
54
|
+
let baseUrl = '';
|
|
55
|
+
let since;
|
|
56
|
+
let huntTarget;
|
|
57
|
+
let phase;
|
|
58
|
+
let timeLimitMinutes = mode === 'release' ? 30 : 15;
|
|
59
|
+
let budgetUSD = 2.0;
|
|
60
|
+
let headed = false;
|
|
61
|
+
let testsRoot;
|
|
62
|
+
let project;
|
|
63
|
+
let outputDir;
|
|
64
|
+
// For hunt mode, the second positional arg is the target
|
|
65
|
+
let startFlags = 1;
|
|
66
|
+
if (mode === 'hunt' && argv[1] && !argv[1].startsWith('--')) {
|
|
67
|
+
huntTarget = argv[1];
|
|
68
|
+
startFlags = 2;
|
|
69
|
+
}
|
|
70
|
+
for (let i = startFlags; i < argv.length; i++) {
|
|
71
|
+
const arg = argv[i];
|
|
72
|
+
const next = argv[i + 1];
|
|
73
|
+
switch (arg) {
|
|
74
|
+
case '--base-url':
|
|
75
|
+
baseUrl = next || '';
|
|
76
|
+
i++;
|
|
77
|
+
break;
|
|
78
|
+
case '--since':
|
|
79
|
+
since = next;
|
|
80
|
+
i++;
|
|
81
|
+
break;
|
|
82
|
+
case '--phase': {
|
|
83
|
+
const parsed = parseInt(next || '0', 10);
|
|
84
|
+
if (parsed !== 1 && parsed !== 2 && parsed !== 3) {
|
|
85
|
+
console.error(`Error: --phase must be 1, 2, or 3 (got "${next}")`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
phase = parsed;
|
|
89
|
+
i++;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case '--time': {
|
|
93
|
+
const parsed = parseInt(next || '15', 10);
|
|
94
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
95
|
+
console.error(`Error: --time must be a positive number (got "${next}")`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
timeLimitMinutes = parsed;
|
|
99
|
+
i++;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case '--budget': {
|
|
103
|
+
const parsed = parseFloat(next || '2.0');
|
|
104
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
105
|
+
console.error(`Error: --budget must be a positive number (got "${next}")`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
budgetUSD = parsed;
|
|
109
|
+
i++;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case '--headed':
|
|
113
|
+
headed = true;
|
|
114
|
+
break;
|
|
115
|
+
case '--tests-root':
|
|
116
|
+
testsRoot = next;
|
|
117
|
+
i++;
|
|
118
|
+
break;
|
|
119
|
+
case '--project':
|
|
120
|
+
project = next;
|
|
121
|
+
i++;
|
|
122
|
+
break;
|
|
123
|
+
case '--output':
|
|
124
|
+
outputDir = next;
|
|
125
|
+
i++;
|
|
126
|
+
break;
|
|
127
|
+
default:
|
|
128
|
+
if (arg.startsWith('--') && !KNOWN_FLAGS.has(arg)) {
|
|
129
|
+
console.error(`Warning: unknown flag "${arg}" (ignored)`);
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Validate --since and hunt target against flag injection (must not start with -)
|
|
135
|
+
if (since && since.startsWith('-')) {
|
|
136
|
+
console.error(`Error: --since value "${since}" looks like a flag, not a git ref`);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
if (huntTarget && huntTarget.startsWith('-')) {
|
|
140
|
+
console.error(`Error: hunt target "${huntTarget}" looks like a flag`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
if (!baseUrl) {
|
|
144
|
+
console.error('Error: --base-url is required');
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
// Validate baseUrl is a proper HTTP(S) URL
|
|
148
|
+
try {
|
|
149
|
+
const parsed = new URL(baseUrl);
|
|
150
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
151
|
+
console.error(`Error: --base-url must use http or https (got "${parsed.protocol}")`);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
// Normalize: remove trailing slash
|
|
155
|
+
baseUrl = parsed.origin + parsed.pathname.replace(/\/+$/, '');
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
console.error(`Error: --base-url is not a valid URL ("${baseUrl}")`);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
// Validate --output stays within project directory
|
|
162
|
+
if (outputDir) {
|
|
163
|
+
const resolved = (0, path_1.resolve)(outputDir);
|
|
164
|
+
const cwd = process.cwd();
|
|
165
|
+
const normalizedCwd = cwd.endsWith(path_1.sep) ? cwd : cwd + path_1.sep;
|
|
166
|
+
if (resolved !== cwd && !resolved.startsWith(normalizedCwd)) {
|
|
167
|
+
console.error(`Error: --output "${outputDir}" resolves outside the project directory`);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
mode,
|
|
173
|
+
baseUrl,
|
|
174
|
+
since: since || 'origin/main',
|
|
175
|
+
huntTarget,
|
|
176
|
+
phase,
|
|
177
|
+
timeLimitMinutes,
|
|
178
|
+
budgetUSD,
|
|
179
|
+
headed,
|
|
180
|
+
testsRoot,
|
|
181
|
+
project,
|
|
182
|
+
outputDir,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async function main() {
|
|
186
|
+
const config = parseCliArgs(process.argv.slice(2));
|
|
187
|
+
if (!config) {
|
|
188
|
+
process.exit(0);
|
|
189
|
+
}
|
|
190
|
+
const report = await (0, orchestrator_js_1.runQAAgent)(config);
|
|
191
|
+
// Exit code based on verdict
|
|
192
|
+
switch (report.verdict.decision) {
|
|
193
|
+
case 'go':
|
|
194
|
+
process.exit(0);
|
|
195
|
+
break;
|
|
196
|
+
case 'conditional':
|
|
197
|
+
process.exit(1);
|
|
198
|
+
break;
|
|
199
|
+
case 'no-go':
|
|
200
|
+
process.exit(2);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
main().catch((error) => {
|
|
205
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
206
|
+
process.exit(1);
|
|
207
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/qa-agent/orchestrator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA2C,QAAQ,EAAE,QAAQ,EAAC,MAAM,YAAY,CAAC;AAY7F,wBAAsB,UAAU,CAAC,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiGzE"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.runQAAgent = runQAAgent;
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const logger_js_1 = require("../logger.js");
|
|
9
|
+
const runner_js_1 = require("./phase1/runner.js");
|
|
10
|
+
const agent_loop_js_1 = require("./phase2/agent_loop.js");
|
|
11
|
+
const verdict_js_1 = require("./phase3/verdict.js");
|
|
12
|
+
const reporter_js_1 = require("./phase3/reporter.js");
|
|
13
|
+
const spec_generator_js_1 = require("./phase3/spec_generator.js");
|
|
14
|
+
const feedback_js_1 = require("./phase3/feedback.js");
|
|
15
|
+
function emptyPhase2Result() {
|
|
16
|
+
return { findings: [], flowsExplored: [], actionsCount: 0, tokensUsed: 0, costUSD: 0, durationMs: 0 };
|
|
17
|
+
}
|
|
18
|
+
async function runQAAgent(inputConfig) {
|
|
19
|
+
const outputDir = inputConfig.outputDir || '.e2e-ai-agents';
|
|
20
|
+
const screenshotDir = inputConfig.screenshotDir || `${outputDir}/qa-screenshots`;
|
|
21
|
+
(0, fs_1.mkdirSync)(screenshotDir, { recursive: true });
|
|
22
|
+
const config = { ...inputConfig, outputDir, screenshotDir };
|
|
23
|
+
// -----------------------------------------------------------------------
|
|
24
|
+
// Phase 1: Scripted (scope resolution + run matched specs)
|
|
25
|
+
// -----------------------------------------------------------------------
|
|
26
|
+
logger_js_1.logger.info('=== Phase 1: Scope & Scripted Tests ===');
|
|
27
|
+
let phase1;
|
|
28
|
+
if (config.phase && config.phase > 1) {
|
|
29
|
+
// Skip Phase 1 — provide empty results
|
|
30
|
+
phase1 = { flows: [], specResults: [] };
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
phase1 = (0, runner_js_1.runPhase1)(config);
|
|
34
|
+
}
|
|
35
|
+
if (phase1.flows.length === 0 && phase1.specResults.length === 0 && !(config.phase && config.phase > 1)) {
|
|
36
|
+
logger_js_1.logger.warn('Phase 1 produced no flows and no spec results — scoping may have failed. Check that route-families.json and plan.json are available.');
|
|
37
|
+
}
|
|
38
|
+
logger_js_1.logger.info('Phase 1 complete', {
|
|
39
|
+
flows: phase1.flows.length,
|
|
40
|
+
specResults: phase1.specResults.length,
|
|
41
|
+
});
|
|
42
|
+
if (config.phase === 1) {
|
|
43
|
+
return earlyReturn(config, phase1);
|
|
44
|
+
}
|
|
45
|
+
// -----------------------------------------------------------------------
|
|
46
|
+
// Phase 2: Autonomous exploration (LLM + agent-browser)
|
|
47
|
+
// -----------------------------------------------------------------------
|
|
48
|
+
logger_js_1.logger.info('=== Phase 2: Autonomous Exploration ===');
|
|
49
|
+
// Verify agent-browser is available before starting the exploration loop
|
|
50
|
+
if (!(config.phase && config.phase > 2)) {
|
|
51
|
+
try {
|
|
52
|
+
(0, child_process_1.execFileSync)('agent-browser', ['--version'], { encoding: 'utf-8', timeout: 5000 });
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
logger_js_1.logger.error('agent-browser CLI not found. Install it (>= 0.18.0) or skip Phase 2 with --phase 1.');
|
|
56
|
+
return earlyReturn(config, phase1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let phase2;
|
|
60
|
+
if (config.phase && config.phase > 2) {
|
|
61
|
+
phase2 = emptyPhase2Result();
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const flows = phase1.flows.length > 0
|
|
65
|
+
? phase1.flows
|
|
66
|
+
: [{ id: 'main', name: 'Main application', priority: 'P1' }];
|
|
67
|
+
// In fix mode, limit Phase 2 to verification only
|
|
68
|
+
const phase2Config = config.mode === 'fix'
|
|
69
|
+
? { ...config, timeLimitMinutes: Math.min(config.timeLimitMinutes ?? 15, 5) }
|
|
70
|
+
: config;
|
|
71
|
+
phase2 = await (0, agent_loop_js_1.runAgentLoop)(phase2Config, flows);
|
|
72
|
+
}
|
|
73
|
+
logger_js_1.logger.info('Phase 2 complete', {
|
|
74
|
+
findings: phase2.findings.length,
|
|
75
|
+
flowsExplored: phase2.flowsExplored.length,
|
|
76
|
+
cost: `$${phase2.costUSD.toFixed(4)}`,
|
|
77
|
+
});
|
|
78
|
+
if (config.phase === 2) {
|
|
79
|
+
return earlyReturn(config, phase1, phase2);
|
|
80
|
+
}
|
|
81
|
+
// -----------------------------------------------------------------------
|
|
82
|
+
// Phase 3: Report + Spec Generation + Verdict
|
|
83
|
+
// -----------------------------------------------------------------------
|
|
84
|
+
logger_js_1.logger.info('=== Phase 3: Report & Verdict ===');
|
|
85
|
+
// Generate specs for discovered bugs
|
|
86
|
+
const generatedSpecs = (0, spec_generator_js_1.generateSpecsForFindings)(phase2.findings, config);
|
|
87
|
+
// Compute verdict
|
|
88
|
+
const verdict = (0, verdict_js_1.computeVerdict)(phase1, phase2);
|
|
89
|
+
// Generate report
|
|
90
|
+
const phase3 = (0, reporter_js_1.generateReport)(config, phase1, phase2, verdict, generatedSpecs);
|
|
91
|
+
// Submit feedback
|
|
92
|
+
try {
|
|
93
|
+
(0, feedback_js_1.submitFeedback)(config);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
logger_js_1.logger.warn('Feedback submission failed', { error: String(err) });
|
|
97
|
+
}
|
|
98
|
+
logger_js_1.logger.info(`=== QA Agent Complete: ${verdict.decision.toUpperCase()} ===`);
|
|
99
|
+
logger_js_1.logger.info(verdict.reason);
|
|
100
|
+
return buildQAReport(config, phase1, phase2, phase3, verdict);
|
|
101
|
+
}
|
|
102
|
+
function earlyReturn(config, phase1, phase2) {
|
|
103
|
+
const p2 = phase2 || emptyPhase2Result();
|
|
104
|
+
const verdict = (0, verdict_js_1.computeVerdict)(phase1, p2);
|
|
105
|
+
const phase3 = (0, reporter_js_1.generateReport)(config, phase1, p2, verdict, []);
|
|
106
|
+
return buildQAReport(config, phase1, p2, phase3, verdict);
|
|
107
|
+
}
|
|
108
|
+
function buildQAReport(config, phase1, phase2, phase3, verdict) {
|
|
109
|
+
return {
|
|
110
|
+
schemaVersion: '1.0.0',
|
|
111
|
+
generatedAt: new Date().toISOString(),
|
|
112
|
+
mode: config.mode,
|
|
113
|
+
config: {
|
|
114
|
+
baseUrl: config.baseUrl,
|
|
115
|
+
timeLimitMinutes: config.timeLimitMinutes,
|
|
116
|
+
budgetUSD: config.budgetUSD,
|
|
117
|
+
},
|
|
118
|
+
phase1,
|
|
119
|
+
phase2,
|
|
120
|
+
phase3,
|
|
121
|
+
verdict,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase1/runner.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAC,YAAY,EAAE,QAAQ,EAAa,MAAM,aAAa,CAAC;AAGpE,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,GAAG,YAAY,CAwBxD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.runPhase1 = runPhase1;
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const logger_js_1 = require("../../logger.js");
|
|
10
|
+
const safe_env_js_1 = require("../safe_env.js");
|
|
11
|
+
const scope_js_1 = require("./scope.js");
|
|
12
|
+
function runPhase1(config) {
|
|
13
|
+
const { flows, specPaths } = (0, scope_js_1.resolveScope)(config);
|
|
14
|
+
logger_js_1.logger.info('Phase 1: Scope resolved', {
|
|
15
|
+
flows: flows.length,
|
|
16
|
+
specDirs: specPaths.length,
|
|
17
|
+
mode: config.mode,
|
|
18
|
+
});
|
|
19
|
+
// Run e2e-agents CLI for impact/plan if we have a since ref
|
|
20
|
+
if (config.since && config.mode !== 'release') {
|
|
21
|
+
runE2eAgentsCli(config);
|
|
22
|
+
}
|
|
23
|
+
// Run matched Playwright specs
|
|
24
|
+
const specResults = runMatchedSpecs(specPaths, config);
|
|
25
|
+
return {
|
|
26
|
+
flows,
|
|
27
|
+
specResults,
|
|
28
|
+
planPath: config.testsRoot
|
|
29
|
+
? (0, path_1.join)(config.testsRoot, '.e2e-ai-agents', 'plan.json')
|
|
30
|
+
: undefined,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function runE2eAgentsCli(config) {
|
|
34
|
+
const args = ['e2e-ai-agents'];
|
|
35
|
+
switch (config.mode) {
|
|
36
|
+
case 'pr':
|
|
37
|
+
args.push('plan');
|
|
38
|
+
if (config.since)
|
|
39
|
+
args.push('--since', config.since);
|
|
40
|
+
break;
|
|
41
|
+
case 'hunt':
|
|
42
|
+
args.push('impact');
|
|
43
|
+
if (config.huntTarget)
|
|
44
|
+
args.push('--flow-patterns', config.huntTarget);
|
|
45
|
+
if (config.since)
|
|
46
|
+
args.push('--since', config.since);
|
|
47
|
+
break;
|
|
48
|
+
case 'fix':
|
|
49
|
+
args.push('heal');
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (config.testsRoot) {
|
|
55
|
+
args.push('--tests-root', config.testsRoot);
|
|
56
|
+
}
|
|
57
|
+
logger_js_1.logger.info('Running e2e-ai-agents', { args: args.slice(1) });
|
|
58
|
+
const result = (0, child_process_1.spawnSync)('npx', args, {
|
|
59
|
+
cwd: config.testsRoot || process.cwd(),
|
|
60
|
+
encoding: 'utf-8',
|
|
61
|
+
timeout: 120000,
|
|
62
|
+
maxBuffer: 2 * 1024 * 1024,
|
|
63
|
+
env: (0, safe_env_js_1.safeEnv)(),
|
|
64
|
+
});
|
|
65
|
+
// Exit code 2 = "no changes detected" from e2e-agents CLI, not an error
|
|
66
|
+
if (result.status !== 0 && result.status !== 2) {
|
|
67
|
+
logger_js_1.logger.warn('e2e-agents exited with non-zero status', {
|
|
68
|
+
status: result.status,
|
|
69
|
+
stderr: (result.stderr || '').slice(0, 500),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function runMatchedSpecs(specPaths, config) {
|
|
74
|
+
const results = [];
|
|
75
|
+
const specFiles = collectSpecFiles(specPaths);
|
|
76
|
+
if (specFiles.length === 0) {
|
|
77
|
+
logger_js_1.logger.info('No spec files found to run');
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
logger_js_1.logger.info('Running matched specs', { count: specFiles.length });
|
|
81
|
+
for (const specFile of specFiles) {
|
|
82
|
+
const result = runSingleSpec(specFile, config);
|
|
83
|
+
results.push(result);
|
|
84
|
+
}
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
function collectSpecFiles(specPaths) {
|
|
88
|
+
const files = [];
|
|
89
|
+
for (const p of specPaths) {
|
|
90
|
+
if (!(0, fs_1.existsSync)(p))
|
|
91
|
+
continue;
|
|
92
|
+
try {
|
|
93
|
+
const entries = (0, fs_1.readdirSync)(p, { recursive: true, encoding: 'utf-8' });
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
if (typeof entry === 'string' && (entry.endsWith('.spec.ts') || entry.endsWith('.test.ts'))) {
|
|
96
|
+
files.push((0, path_1.join)(p, entry));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Skip unreadable directories
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return files;
|
|
105
|
+
}
|
|
106
|
+
function runSingleSpec(specPath, config) {
|
|
107
|
+
const args = [
|
|
108
|
+
'playwright', 'test',
|
|
109
|
+
specPath,
|
|
110
|
+
'--reporter', 'json',
|
|
111
|
+
];
|
|
112
|
+
if (config.project) {
|
|
113
|
+
args.push('--project', config.project);
|
|
114
|
+
}
|
|
115
|
+
const result = (0, child_process_1.spawnSync)('npx', args, {
|
|
116
|
+
cwd: config.testsRoot || process.cwd(),
|
|
117
|
+
encoding: 'utf-8',
|
|
118
|
+
timeout: 120000,
|
|
119
|
+
maxBuffer: 2 * 1024 * 1024,
|
|
120
|
+
env: (0, safe_env_js_1.safeEnv)(config.baseUrl ? { BASE_URL: config.baseUrl } : {}),
|
|
121
|
+
});
|
|
122
|
+
// Try to parse JSON output
|
|
123
|
+
try {
|
|
124
|
+
const report = JSON.parse(result.stdout || '{}');
|
|
125
|
+
return {
|
|
126
|
+
specPath,
|
|
127
|
+
passed: report.stats?.expected || 0,
|
|
128
|
+
failed: report.stats?.unexpected || 0,
|
|
129
|
+
flaky: report.stats?.flaky || 0,
|
|
130
|
+
skipped: report.stats?.skipped || 0,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return {
|
|
135
|
+
specPath,
|
|
136
|
+
passed: result.status === 0 ? 1 : 0,
|
|
137
|
+
failed: result.status === 0 ? 0 : 1,
|
|
138
|
+
flaky: 0,
|
|
139
|
+
skipped: 0,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase1/scope.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,QAAQ,EAAE,UAAU,EAAC,MAAM,aAAa,CAAC;AAgBtD,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG;IAAC,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAC,CAsDzF"}
|