@yasserkhanorg/e2e-agents 0.10.0 → 0.11.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.
Files changed (93) hide show
  1. package/README.md +112 -584
  2. package/dist/agent/api_catalog.d.ts +11 -0
  3. package/dist/agent/api_catalog.d.ts.map +1 -0
  4. package/dist/agent/api_catalog.js +210 -0
  5. package/dist/agent/llm_agents_flow.d.ts +15 -0
  6. package/dist/agent/llm_agents_flow.d.ts.map +1 -0
  7. package/dist/agent/llm_agents_flow.js +434 -0
  8. package/dist/agent/native_flow.d.ts +6 -0
  9. package/dist/agent/native_flow.d.ts.map +1 -0
  10. package/dist/agent/native_flow.js +179 -0
  11. package/dist/agent/pipeline.d.ts +2 -25
  12. package/dist/agent/pipeline.d.ts.map +1 -1
  13. package/dist/agent/pipeline.js +30 -1329
  14. package/dist/agent/pipeline_types.d.ts +54 -0
  15. package/dist/agent/pipeline_types.d.ts.map +1 -0
  16. package/dist/agent/pipeline_types.js +4 -0
  17. package/dist/agent/pipeline_utils.d.ts +12 -0
  18. package/dist/agent/pipeline_utils.d.ts.map +1 -0
  19. package/dist/agent/pipeline_utils.js +156 -0
  20. package/dist/agent/process_runner.d.ts +10 -0
  21. package/dist/agent/process_runner.d.ts.map +1 -0
  22. package/dist/agent/process_runner.js +92 -0
  23. package/dist/agent/spec_generator.d.ts +5 -0
  24. package/dist/agent/spec_generator.d.ts.map +1 -0
  25. package/dist/agent/spec_generator.js +253 -0
  26. package/dist/agent/validation_runner.d.ts +5 -0
  27. package/dist/agent/validation_runner.d.ts.map +1 -0
  28. package/dist/agent/validation_runner.js +77 -0
  29. package/dist/agentic/playwright_runner.js +1 -1
  30. package/dist/cli/commands/analyze.d.ts +3 -0
  31. package/dist/cli/commands/analyze.d.ts.map +1 -0
  32. package/dist/cli/commands/analyze.js +77 -0
  33. package/dist/cli/commands/feedback.d.ts +3 -0
  34. package/dist/cli/commands/feedback.d.ts.map +1 -0
  35. package/dist/cli/commands/feedback.js +39 -0
  36. package/dist/cli/commands/finalize.d.ts +3 -0
  37. package/dist/cli/commands/finalize.d.ts.map +1 -0
  38. package/dist/cli/commands/finalize.js +41 -0
  39. package/dist/cli/commands/generate.d.ts +4 -0
  40. package/dist/cli/commands/generate.d.ts.map +1 -0
  41. package/dist/cli/commands/generate.js +108 -0
  42. package/dist/cli/commands/heal.d.ts +3 -0
  43. package/dist/cli/commands/heal.d.ts.map +1 -0
  44. package/dist/cli/commands/heal.js +60 -0
  45. package/dist/cli/commands/impact.d.ts +4 -0
  46. package/dist/cli/commands/impact.d.ts.map +1 -0
  47. package/dist/cli/commands/impact.js +26 -0
  48. package/dist/cli/commands/llm_health.d.ts +2 -0
  49. package/dist/cli/commands/llm_health.d.ts.map +1 -0
  50. package/dist/cli/commands/llm_health.js +38 -0
  51. package/dist/cli/commands/plan.d.ts +4 -0
  52. package/dist/cli/commands/plan.d.ts.map +1 -0
  53. package/dist/cli/commands/plan.js +83 -0
  54. package/dist/cli/commands/traceability.d.ts +4 -0
  55. package/dist/cli/commands/traceability.d.ts.map +1 -0
  56. package/dist/cli/commands/traceability.js +77 -0
  57. package/dist/cli/parse_args.d.ts +6 -0
  58. package/dist/cli/parse_args.d.ts.map +1 -0
  59. package/dist/cli/parse_args.js +216 -0
  60. package/dist/cli/types.d.ts +70 -0
  61. package/dist/cli/types.d.ts.map +1 -0
  62. package/dist/cli/types.js +4 -0
  63. package/dist/cli/usage.d.ts +2 -0
  64. package/dist/cli/usage.d.ts.map +1 -0
  65. package/dist/cli/usage.js +86 -0
  66. package/dist/cli.js +26 -1060
  67. package/dist/esm/agent/api_catalog.js +199 -0
  68. package/dist/esm/agent/llm_agents_flow.js +421 -0
  69. package/dist/esm/agent/native_flow.js +175 -0
  70. package/dist/esm/agent/pipeline.js +8 -1307
  71. package/dist/esm/agent/pipeline_types.js +3 -0
  72. package/dist/esm/agent/pipeline_utils.js +146 -0
  73. package/dist/esm/agent/process_runner.js +83 -0
  74. package/dist/esm/agent/spec_generator.js +249 -0
  75. package/dist/esm/agent/validation_runner.js +73 -0
  76. package/dist/esm/agentic/playwright_runner.js +1 -1
  77. package/dist/esm/cli/commands/analyze.js +74 -0
  78. package/dist/esm/cli/commands/feedback.js +36 -0
  79. package/dist/esm/cli/commands/finalize.js +38 -0
  80. package/dist/esm/cli/commands/generate.js +105 -0
  81. package/dist/esm/cli/commands/heal.js +57 -0
  82. package/dist/esm/cli/commands/impact.js +23 -0
  83. package/dist/esm/cli/commands/llm_health.js +35 -0
  84. package/dist/esm/cli/commands/plan.js +80 -0
  85. package/dist/esm/cli/commands/traceability.js +73 -0
  86. package/dist/esm/cli/parse_args.js +210 -0
  87. package/dist/esm/cli/types.js +3 -0
  88. package/dist/esm/cli/usage.js +83 -0
  89. package/dist/esm/cli.js +20 -1054
  90. package/dist/esm/mcp-server.js +18 -1
  91. package/dist/mcp-server.d.ts.map +1 -1
  92. package/dist/mcp-server.js +17 -0
  93. package/package.json +2 -4
@@ -0,0 +1,54 @@
1
+ export interface PipelineResult {
2
+ flowId: string;
3
+ flowName: string;
4
+ generatedDir: string;
5
+ generateStatus: 'success' | 'skipped' | 'failed';
6
+ healStatus?: 'success' | 'skipped' | 'failed';
7
+ error?: string;
8
+ failureCategory?: 'config' | 'environment' | 'generation' | 'validation' | 'runtime' | 'quality' | 'path-safety' | 'unknown';
9
+ failureCode?: string;
10
+ }
11
+ export interface PipelineSummary {
12
+ runner: 'playwright-agents' | 'e2e-test-gen' | 'package-native' | 'unknown';
13
+ results: PipelineResult[];
14
+ warnings: string[];
15
+ mcp?: {
16
+ requested: boolean;
17
+ active: boolean;
18
+ backend: 'playwright-agents' | 'e2e-test-gen' | 'package-native' | 'unknown';
19
+ };
20
+ }
21
+ export interface SpecHealTarget {
22
+ specPath: string;
23
+ status?: 'failed' | 'flaky';
24
+ reason?: string;
25
+ }
26
+ export type NativeSpecStrategy = 'thread-reply' | 'lifecycle-channel' | 'channel-settings' | 'channel-switch' | 'markdown-post' | 'mentions-post' | 'realtime-post' | 'message-post' | 'channel-baseline' | 'search-baseline' | 'generic-baseline';
27
+ export interface NativeSpecQualityIssue {
28
+ code: 'disallowed-describe' | 'disallowed-only' | 'missing-test' | 'missing-tag' | 'tag-array-disallowed' | 'unknown-api-surface' | 'fragile-system-console-visibility' | 'fragile-selector';
29
+ message: string;
30
+ }
31
+ export interface CommandResult {
32
+ status: number;
33
+ stdout: string;
34
+ stderr: string;
35
+ error?: string;
36
+ }
37
+ export interface ValidationResult {
38
+ status: 'passed' | 'failed' | 'skipped';
39
+ detail?: string;
40
+ }
41
+ export interface ApiSurfaceCatalog {
42
+ pwProps: Set<string>;
43
+ pwNestedMethods: Map<string, Set<string>>;
44
+ initSetupKeys: Set<string>;
45
+ initSetupVariableMethods: Map<string, Set<string>>;
46
+ testBrowserMethods: Set<string>;
47
+ channelsPageMembers: Set<string>;
48
+ sidebarRightMembers: Set<string>;
49
+ }
50
+ export interface InitSetupBinding {
51
+ key: string;
52
+ variable: string;
53
+ }
54
+ //# sourceMappingURL=pipeline_types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline_types.d.ts","sourceRoot":"","sources":["../../src/agent/pipeline_types.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACjD,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,QAAQ,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;IAC7H,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,mBAAmB,GAAG,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAAC;IAC5E,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,CAAC,EAAE;QACF,SAAS,EAAE,OAAO,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;QAChB,OAAO,EAAE,mBAAmB,GAAG,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAAC;KAChF,CAAC;CACL;AAED,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,kBAAkB,GACxB,cAAc,GACd,mBAAmB,GACnB,kBAAkB,GAClB,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,eAAe,GACf,cAAc,GACd,kBAAkB,GAClB,iBAAiB,GACjB,kBAAkB,CAAC;AAEzB,MAAM,WAAW,sBAAsB;IACnC,IAAI,EACE,qBAAqB,GACrB,iBAAiB,GACjB,cAAc,GACd,aAAa,GACb,sBAAsB,GACtB,qBAAqB,GACrB,mCAAmC,GACnC,kBAAkB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,wBAAwB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACnD,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,4 @@
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 });
@@ -0,0 +1,12 @@
1
+ import type { FlowImpact } from './types.js';
2
+ import type { NativeSpecStrategy, PipelineResult, PipelineSummary } from './pipeline_types.js';
3
+ import type { SpecHealTarget } from './pipeline_types.js';
4
+ export declare function createMcpStatus(backend: 'playwright-agents' | 'e2e-test-gen' | 'package-native' | 'unknown', requested: boolean): NonNullable<PipelineSummary['mcp']>;
5
+ export declare function classifyPipelineFailure(result: PipelineResult): PipelineResult;
6
+ export declare function finalizePipelineSummary(summary: PipelineSummary): PipelineSummary;
7
+ export declare function toSafeSlug(value: string): string;
8
+ export declare function stripSpecSuffix(value: string): string;
9
+ export declare function buildSyntheticFlowFromSpecTarget(relativeSpecPath: string, target: SpecHealTarget): FlowImpact;
10
+ export declare function firstFlowFiles(flow: FlowImpact): string[];
11
+ export declare function buildNativeStrategyOrder(flow: FlowImpact): NativeSpecStrategy[];
12
+ //# sourceMappingURL=pipeline_utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline_utils.d.ts","sourceRoot":"","sources":["../../src/agent/pipeline_utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAC,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAC,MAAM,qBAAqB,CAAC;AAE7F,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAExD,wBAAgB,eAAe,CAC3B,OAAO,EAAE,mBAAmB,GAAG,cAAc,GAAG,gBAAgB,GAAG,SAAS,EAC5E,SAAS,EAAE,OAAO,GACnB,WAAW,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAMrC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CA8B9E;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,CAKjF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,gCAAgC,CAAC,gBAAgB,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,UAAU,CAqB7G;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,EAAE,CAEzD;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,GAAG,kBAAkB,EAAE,CAmE/E"}
@@ -0,0 +1,156 @@
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.createMcpStatus = createMcpStatus;
6
+ exports.classifyPipelineFailure = classifyPipelineFailure;
7
+ exports.finalizePipelineSummary = finalizePipelineSummary;
8
+ exports.toSafeSlug = toSafeSlug;
9
+ exports.stripSpecSuffix = stripSpecSuffix;
10
+ exports.buildSyntheticFlowFromSpecTarget = buildSyntheticFlowFromSpecTarget;
11
+ exports.firstFlowFiles = firstFlowFiles;
12
+ exports.buildNativeStrategyOrder = buildNativeStrategyOrder;
13
+ const path_1 = require("path");
14
+ const utils_js_1 = require("./utils.js");
15
+ function createMcpStatus(backend, requested) {
16
+ return {
17
+ requested,
18
+ active: requested && (backend === 'e2e-test-gen' || backend === 'playwright-agents'),
19
+ backend,
20
+ };
21
+ }
22
+ function classifyPipelineFailure(result) {
23
+ if (result.failureCategory || result.failureCode) {
24
+ return result;
25
+ }
26
+ if (!result.error) {
27
+ return result;
28
+ }
29
+ const errorText = result.error.toLowerCase();
30
+ if (errorText.includes('etimedout') || errorText.includes('timed out')) {
31
+ return { ...result, failureCategory: 'environment', failureCode: 'mcp_timeout' };
32
+ }
33
+ if (errorText.includes('outside testsroot')) {
34
+ return { ...result, failureCategory: 'path-safety', failureCode: 'path_outside_tests_root' };
35
+ }
36
+ if (errorText.includes('playwright binary') || errorText.includes('not found')) {
37
+ return { ...result, failureCategory: 'environment', failureCode: 'dependency_missing' };
38
+ }
39
+ if (errorText.includes('compile validation')) {
40
+ return { ...result, failureCategory: 'validation', failureCode: 'compile_validation_failed' };
41
+ }
42
+ if (errorText.includes('runtime validation') || errorText.includes('playwright test failed')) {
43
+ return { ...result, failureCategory: 'runtime', failureCode: 'runtime_validation_failed' };
44
+ }
45
+ if (errorText.includes('quality checks failed') || errorText.includes('invalid test content')) {
46
+ return { ...result, failureCategory: 'quality', failureCode: 'quality_guard_failed' };
47
+ }
48
+ if (errorText.includes('generate failed') || errorText.includes('did not produce expected test file')) {
49
+ return { ...result, failureCategory: 'generation', failureCode: 'generation_failed' };
50
+ }
51
+ return { ...result, failureCategory: 'unknown', failureCode: 'unknown' };
52
+ }
53
+ function finalizePipelineSummary(summary) {
54
+ return {
55
+ ...summary,
56
+ results: summary.results.map(classifyPipelineFailure),
57
+ };
58
+ }
59
+ function toSafeSlug(value) {
60
+ return value.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'flow';
61
+ }
62
+ function stripSpecSuffix(value) {
63
+ return value.replace(/\.(spec|test)\.[^.]+$/i, '').replace(/\.[^.]+$/, '');
64
+ }
65
+ function buildSyntheticFlowFromSpecTarget(relativeSpecPath, target) {
66
+ const normalizedSpecPath = (0, utils_js_1.normalizePath)(relativeSpecPath);
67
+ const noSuffix = stripSpecSuffix(normalizedSpecPath);
68
+ const flowId = toSafeSlug(noSuffix.replace(/\//g, '.'));
69
+ const base = (0, utils_js_1.baseNameWithoutExt)(stripSpecSuffix((0, path_1.basename)(normalizedSpecPath)));
70
+ const flowName = (0, utils_js_1.titleCase)(base.replace(/[._-]+/g, ' ')) || 'Recovered Spec';
71
+ const keywords = (0, utils_js_1.uniqueTokens)((0, utils_js_1.tokenize)(noSuffix.replace(/[/.]/g, ' ')));
72
+ const reasons = [
73
+ `Playwright report marked this spec as ${target.status || 'unstable'}.`,
74
+ target.reason || `Auto-heal target: ${normalizedSpecPath}`,
75
+ ];
76
+ return {
77
+ id: flowId,
78
+ name: flowName,
79
+ kind: 'flow',
80
+ score: target.status === 'failed' ? 12 : 9,
81
+ priority: target.status === 'failed' ? 'P0' : 'P1',
82
+ reasons,
83
+ keywords,
84
+ files: [normalizedSpecPath],
85
+ };
86
+ }
87
+ function firstFlowFiles(flow) {
88
+ return (flow.files || []).filter(Boolean).slice(0, 5);
89
+ }
90
+ function buildNativeStrategyOrder(flow) {
91
+ const flowId = (flow.id || '').toLowerCase();
92
+ const haystack = [
93
+ flow.id,
94
+ flow.name,
95
+ ...(flow.files || []),
96
+ ...(flow.reasons || []),
97
+ ...(flow.keywords || []),
98
+ ].join(' ').toLowerCase();
99
+ const strategies = [];
100
+ if (flowId.includes('search')) {
101
+ strategies.push('search-baseline');
102
+ }
103
+ if (flowId.includes('threads') || flowId.includes('thread')) {
104
+ strategies.push('thread-reply');
105
+ }
106
+ if (flowId.includes('channels.lifecycle')) {
107
+ strategies.push('lifecycle-channel');
108
+ }
109
+ if (flowId.includes('channels.settings')) {
110
+ strategies.push('channel-settings');
111
+ }
112
+ if (flowId.includes('channels.switch')) {
113
+ strategies.push('channel-switch');
114
+ }
115
+ if (flowId.includes('messaging.markdown')) {
116
+ strategies.push('markdown-post');
117
+ }
118
+ if (flowId.includes('messaging.mentions')) {
119
+ strategies.push('mentions-post');
120
+ }
121
+ if (flowId.includes('messaging.realtime')) {
122
+ strategies.push('realtime-post');
123
+ }
124
+ if (/(thread|reply|rhs|sidebar[_-]?right)/.test(haystack)) {
125
+ strategies.push('thread-reply');
126
+ }
127
+ if (/(create|join|leave|invite)/.test(haystack)) {
128
+ strategies.push('lifecycle-channel');
129
+ }
130
+ if (/(settings|preferences)/.test(haystack)) {
131
+ strategies.push('channel-settings');
132
+ }
133
+ if (/(switch|quick\\s*switch)/.test(haystack)) {
134
+ strategies.push('channel-switch');
135
+ }
136
+ if (/(markdown|format)/.test(haystack)) {
137
+ strategies.push('markdown-post');
138
+ }
139
+ if (/(mention|@)/.test(haystack)) {
140
+ strategies.push('mentions-post');
141
+ }
142
+ if (/(realtime|websocket|presence)/.test(haystack)) {
143
+ strategies.push('realtime-post');
144
+ }
145
+ if (/(search|find|spotlight)/.test(haystack)) {
146
+ strategies.push('search-baseline');
147
+ }
148
+ if (/(message|post|realtime|websocket|chat)/.test(haystack)) {
149
+ strategies.push('message-post');
150
+ }
151
+ if (/(channel|navigation|sidebar|switch)/.test(haystack)) {
152
+ strategies.push('channel-baseline');
153
+ }
154
+ strategies.push('generic-baseline');
155
+ return Array.from(new Set(strategies));
156
+ }
@@ -0,0 +1,10 @@
1
+ import type { PipelineConfig } from './config.js';
2
+ import type { CommandResult } from './pipeline_types.js';
3
+ export declare function resolvePlaywrightBinary(testsRoot: string): string | null;
4
+ export declare function summarizeCommandOutput(stdout: string, stderr: string): string;
5
+ export declare function runCommand(command: string, args: string[], cwd: string, timeoutMs?: number): CommandResult;
6
+ export declare function resolveMcpCommandTimeoutMs(pipeline: PipelineConfig): number;
7
+ export declare function resolveMcpRetries(pipeline: PipelineConfig): number;
8
+ export declare function isRetryableMcpFailure(result: CommandResult): boolean;
9
+ export declare function runCommandWithRetries(command: string, args: string[], cwd: string, timeoutMs: number, retries: number): CommandResult;
10
+ //# sourceMappingURL=process_runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process_runner.d.ts","sourceRoot":"","sources":["../../src/agent/process_runner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAEvD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUxE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAO7E;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,SAAiB,GAAG,aAAa,CAsBlH;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAM3E;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAMlE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAQpE;AAED,wBAAgB,qBAAqB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GAChB,aAAa,CAYf"}
@@ -0,0 +1,92 @@
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.resolvePlaywrightBinary = resolvePlaywrightBinary;
6
+ exports.summarizeCommandOutput = summarizeCommandOutput;
7
+ exports.runCommand = runCommand;
8
+ exports.resolveMcpCommandTimeoutMs = resolveMcpCommandTimeoutMs;
9
+ exports.resolveMcpRetries = resolveMcpRetries;
10
+ exports.isRetryableMcpFailure = isRetryableMcpFailure;
11
+ exports.runCommandWithRetries = runCommandWithRetries;
12
+ const fs_1 = require("fs");
13
+ const path_1 = require("path");
14
+ const child_process_1 = require("child_process");
15
+ function resolvePlaywrightBinary(testsRoot) {
16
+ const unixPath = (0, path_1.join)(testsRoot, 'node_modules', '.bin', 'playwright');
17
+ const windowsPath = (0, path_1.join)(testsRoot, 'node_modules', '.bin', 'playwright.cmd');
18
+ if ((0, fs_1.existsSync)(unixPath)) {
19
+ return unixPath;
20
+ }
21
+ if ((0, fs_1.existsSync)(windowsPath)) {
22
+ return windowsPath;
23
+ }
24
+ return null;
25
+ }
26
+ function summarizeCommandOutput(stdout, stderr) {
27
+ const combined = [stdout, stderr].filter(Boolean).join('\n').trim();
28
+ if (!combined) {
29
+ return '';
30
+ }
31
+ const lines = combined.split('\n').slice(-20);
32
+ return lines.join('\n').slice(0, 2000);
33
+ }
34
+ function runCommand(command, args, cwd, timeoutMs = 60 * 60 * 1000) {
35
+ // When spawning `claude`, unset CLAUDECODE so nested invocations are allowed.
36
+ // Claude Code sets this variable to block nested sessions; child processes
37
+ // that spawn their own claude instance must run without it.
38
+ let env;
39
+ if (command === 'claude') {
40
+ const { CLAUDECODE: _, ...rest } = process.env;
41
+ env = rest;
42
+ }
43
+ const result = (0, child_process_1.spawnSync)(command, args, {
44
+ cwd,
45
+ encoding: 'utf-8',
46
+ timeout: timeoutMs,
47
+ stdio: 'pipe',
48
+ ...(env ? { env } : {}),
49
+ });
50
+ return {
51
+ status: result.status ?? 1,
52
+ stdout: result.stdout || '',
53
+ stderr: result.stderr || '',
54
+ error: result.error ? result.error.message : undefined,
55
+ };
56
+ }
57
+ function resolveMcpCommandTimeoutMs(pipeline) {
58
+ const value = pipeline.mcpCommandTimeoutMs;
59
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
60
+ return 180000;
61
+ }
62
+ return Math.max(60000, Math.min(15 * 60 * 1000, Math.round(value)));
63
+ }
64
+ function resolveMcpRetries(pipeline) {
65
+ const value = pipeline.mcpRetries;
66
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
67
+ return 1;
68
+ }
69
+ return Math.max(0, Math.min(5, Math.round(value)));
70
+ }
71
+ function isRetryableMcpFailure(result) {
72
+ const haystack = [result.error || '', result.stderr || '', result.stdout || ''].join('\n').toLowerCase();
73
+ return haystack.includes('etimedout') ||
74
+ haystack.includes('timed out') ||
75
+ haystack.includes('econnreset') ||
76
+ haystack.includes('429') ||
77
+ haystack.includes('rate limit') ||
78
+ haystack.includes('temporar');
79
+ }
80
+ function runCommandWithRetries(command, args, cwd, timeoutMs, retries) {
81
+ let result = runCommand(command, args, cwd, timeoutMs);
82
+ for (let attempt = 1; attempt <= retries; attempt += 1) {
83
+ if (result.status === 0) {
84
+ return result;
85
+ }
86
+ if (!isRetryableMcpFailure(result)) {
87
+ return result;
88
+ }
89
+ result = runCommand(command, args, cwd, timeoutMs);
90
+ }
91
+ return result;
92
+ }
@@ -0,0 +1,5 @@
1
+ import type { FlowImpact } from './types.js';
2
+ import type { ApiSurfaceCatalog, NativeSpecQualityIssue, NativeSpecStrategy } from './pipeline_types.js';
3
+ export declare function validateGeneratedSpecContent(content: string, apiSurface?: ApiSurfaceCatalog): NativeSpecQualityIssue[];
4
+ export declare function createNativePlaywrightSpec(flow: FlowImpact, slug: string, strategy: NativeSpecStrategy): string;
5
+ //# sourceMappingURL=spec_generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec_generator.d.ts","sourceRoot":"","sources":["../../src/agent/spec_generator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAC,iBAAiB,EAAE,sBAAsB,EAAE,kBAAkB,EAAC,MAAM,qBAAqB,CAAC;AAIvG,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,iBAAiB,GAAG,sBAAsB,EAAE,CAgHtH;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAyJ/G"}
@@ -0,0 +1,253 @@
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.validateGeneratedSpecContent = validateGeneratedSpecContent;
6
+ exports.createNativePlaywrightSpec = createNativePlaywrightSpec;
7
+ const api_catalog_js_1 = require("./api_catalog.js");
8
+ const pipeline_utils_js_1 = require("./pipeline_utils.js");
9
+ function validateGeneratedSpecContent(content, apiSurface) {
10
+ const issues = [];
11
+ if (/\btest\.describe\s*\(/.test(content)) {
12
+ issues.push({
13
+ code: 'disallowed-describe',
14
+ message: 'Generated tests must not use test.describe.',
15
+ });
16
+ }
17
+ if (/\btest\.only\s*\(/.test(content)) {
18
+ issues.push({
19
+ code: 'disallowed-only',
20
+ message: 'Generated tests must not use test.only.',
21
+ });
22
+ }
23
+ if (!/\btest\s*\(/.test(content)) {
24
+ issues.push({
25
+ code: 'missing-test',
26
+ message: 'Generated file does not include a test() declaration.',
27
+ });
28
+ }
29
+ if (/\btag\s*:\s*\[/.test(content)) {
30
+ issues.push({
31
+ code: 'tag-array-disallowed',
32
+ message: 'Generated tests must use a single tag string, not a tag array.',
33
+ });
34
+ }
35
+ const hasTagOption = /\btag\s*:\s*['"][^'"]+['"]/.test(content);
36
+ const hasTagInTitle = /\btest(?:\.\w+)?\s*\(\s*['"][^'"]*@ai-assisted[^'"]*['"]/.test(content);
37
+ if (!(hasTagOption || hasTagInTitle) || !/@ai-assisted/.test(content)) {
38
+ issues.push({
39
+ code: 'missing-tag',
40
+ message: "Generated tests must include '@ai-assisted' either as tag option or in test title.",
41
+ });
42
+ }
43
+ if (/\bsystemConsolePage\.toBeVisible\s*\(/.test(content)) {
44
+ issues.push({
45
+ code: 'fragile-system-console-visibility',
46
+ message: 'Avoid systemConsolePage.toBeVisible(); it relies on legacy backstage navigation that may be absent.',
47
+ });
48
+ }
49
+ const fragileSelectors = [
50
+ '.backstage-navbar',
51
+ '.admin-console__wrapper',
52
+ '.left-panel',
53
+ '.panel-card',
54
+ ].filter((selector) => content.includes(selector));
55
+ if (fragileSelectors.length > 0) {
56
+ issues.push({
57
+ code: 'fragile-selector',
58
+ message: `Avoid brittle class selectors in generated tests: ${Array.from(new Set(fragileSelectors)).join(', ')}`,
59
+ });
60
+ }
61
+ if (apiSurface) {
62
+ const unknownPwProps = Array.from((0, api_catalog_js_1.collectMatches)(content, /\bpw\.([A-Za-z_][A-Za-z0-9_]*)\b/g)).filter((prop) => !apiSurface.pwProps.has(prop));
63
+ const unknownBrowserMethods = Array.from((0, api_catalog_js_1.collectMatches)(content, /\bpw\.testBrowser\.([A-Za-z_][A-Za-z0-9_]*)\b/g)).filter((method) => !apiSurface.testBrowserMethods.has(method));
64
+ const unknownNestedPwMembers = [];
65
+ for (const match of content.matchAll(/\bpw\.([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)\b/g)) {
66
+ const objectName = match[1];
67
+ const methodName = match[2];
68
+ if (!objectName || !methodName || objectName === 'testBrowser') {
69
+ continue;
70
+ }
71
+ const knownMethods = apiSurface.pwNestedMethods.get(objectName);
72
+ if (!knownMethods || !knownMethods.has(methodName)) {
73
+ unknownNestedPwMembers.push(`pw.${objectName}.${methodName}`);
74
+ }
75
+ }
76
+ const unknownChannelMembers = Array.from((0, api_catalog_js_1.collectMatches)(content, /\bchannelsPage\.([A-Za-z_][A-Za-z0-9_]*)\b/g)).filter((member) => !apiSurface.channelsPageMembers.has(member));
77
+ const unknownSidebarMembers = Array.from((0, api_catalog_js_1.collectMatches)(content, /\bchannelsPage\.sidebarRight\.([A-Za-z_][A-Za-z0-9_]*)\b/g)).filter((member) => !apiSurface.sidebarRightMembers.has(member));
78
+ const initSetupBindings = (0, api_catalog_js_1.parseInitSetupBindings)(content);
79
+ const unknownInitSetupKeys = initSetupBindings
80
+ .map((binding) => binding.key)
81
+ .filter((key) => !apiSurface.initSetupKeys.has(key));
82
+ const unknownInitSetupVariableMethods = [];
83
+ for (const binding of initSetupBindings) {
84
+ const knownMethods = apiSurface.initSetupVariableMethods.get(binding.variable);
85
+ if (!knownMethods || knownMethods.size === 0) {
86
+ continue;
87
+ }
88
+ const methodPattern = new RegExp(`\\b${(0, api_catalog_js_1.escapeRegExp)(binding.variable)}\\.([A-Za-z_][A-Za-z0-9_]*)\\b`, 'g');
89
+ for (const method of (0, api_catalog_js_1.collectMatches)(content, methodPattern)) {
90
+ if (!knownMethods.has(method)) {
91
+ unknownInitSetupVariableMethods.push(`${binding.variable}.${method}`);
92
+ }
93
+ }
94
+ }
95
+ const unknown = [
96
+ ...unknownPwProps.map((value) => `pw.${value}`),
97
+ ...unknownBrowserMethods.map((value) => `pw.testBrowser.${value}`),
98
+ ...unknownNestedPwMembers,
99
+ ...unknownChannelMembers.map((value) => `channelsPage.${value}`),
100
+ ...unknownSidebarMembers.map((value) => `channelsPage.sidebarRight.${value}`),
101
+ ...unknownInitSetupKeys.map((value) => `pw.initSetup.{${value}}`),
102
+ ...unknownInitSetupVariableMethods,
103
+ ];
104
+ if (unknown.length > 0) {
105
+ issues.push({
106
+ code: 'unknown-api-surface',
107
+ message: `Generated test uses unknown API/page-object members: ${Array.from(new Set(unknown)).join(', ')}`,
108
+ });
109
+ }
110
+ }
111
+ return issues;
112
+ }
113
+ function createNativePlaywrightSpec(flow, slug, strategy) {
114
+ const linkedFiles = (0, pipeline_utils_js_1.firstFlowFiles)(flow).join(', ') || 'N/A';
115
+ const header = [
116
+ "import {test, expect} from '@mattermost/playwright-lib';",
117
+ '',
118
+ '/**',
119
+ ` * Auto-generated by @yasserkhanorg/e2e-agents`,
120
+ ` * Flow: ${flow.id} (${flow.name})`,
121
+ ` * Strategy: ${strategy}`,
122
+ ` * Linked files: ${linkedFiles}`,
123
+ ' */',
124
+ ];
125
+ const start = [
126
+ `test('${flow.priority}: ${flow.name} generated coverage', {tag: '@ai-assisted'}, async ({pw}) => {`,
127
+ ' const {user, team} = await pw.initSetup();',
128
+ ' const {channelsPage} = await pw.testBrowser.login(user);',
129
+ ' await channelsPage.goto(team.name);',
130
+ ];
131
+ const end = [
132
+ '});',
133
+ '',
134
+ ];
135
+ if (strategy === 'thread-reply') {
136
+ return [
137
+ ...header,
138
+ ...start,
139
+ ` const parentMessage = \`ai-${slug}-parent-\${Date.now()}\`;`,
140
+ ' await channelsPage.postMessage(parentMessage);',
141
+ ' const rootPost = await channelsPage.getLastPost();',
142
+ ' await rootPost.openAThread();',
143
+ ` const replyMessage = \`ai-${slug}-reply-\${Date.now()}\`;`,
144
+ ' await channelsPage.sidebarRight.postMessage(replyMessage);',
145
+ ' const lastReply = await channelsPage.sidebarRight.getLastPost();',
146
+ ' await expect(lastReply.container).toContainText(replyMessage);',
147
+ ...end,
148
+ ].join('\n');
149
+ }
150
+ if (strategy === 'lifecycle-channel') {
151
+ return [
152
+ ...header,
153
+ ...start,
154
+ ` const channelName = \`ai-${slug}-\${Date.now().toString().slice(-6)}\`;`,
155
+ " await channelsPage.newChannel(channelName, 'O');",
156
+ ' await expect(channelsPage.page).toHaveURL(new RegExp(`/channels/${channelName}$`));',
157
+ ...end,
158
+ ].join('\n');
159
+ }
160
+ if (strategy === 'channel-settings') {
161
+ return [
162
+ ...header,
163
+ ...start,
164
+ ' await channelsPage.openChannelSettings();',
165
+ " await expect(channelsPage.page.getByRole('dialog', {name: 'Channel Settings'})).toBeVisible();",
166
+ " await channelsPage.page.keyboard.press('Escape');",
167
+ ...end,
168
+ ].join('\n');
169
+ }
170
+ if (strategy === 'channel-switch') {
171
+ return [
172
+ ...header,
173
+ ...start,
174
+ " await channelsPage.goto(team.name, 'off-topic');",
175
+ " await expect(channelsPage.page).toHaveURL(/\\/channels\\/off-topic$/);",
176
+ " await expect(channelsPage.page.locator('#channelHeaderTitle')).toContainText(/off-topic/i);",
177
+ ...end,
178
+ ].join('\n');
179
+ }
180
+ if (strategy === 'markdown-post') {
181
+ return [
182
+ ...header,
183
+ ...start,
184
+ ` const message = '**ai-${slug}-bold** _italic_';`,
185
+ ' await channelsPage.postMessage(message);',
186
+ ' const lastPost = await channelsPage.getLastPost();',
187
+ " await expect(lastPost.container.locator('strong')).toBeVisible();",
188
+ ...end,
189
+ ].join('\n');
190
+ }
191
+ if (strategy === 'mentions-post') {
192
+ return [
193
+ ...header,
194
+ ...start,
195
+ ' const mention = `@${user.username}`;',
196
+ ' await channelsPage.postMessage(`Ping ${mention}`);',
197
+ ' const lastPost = await channelsPage.getLastPost();',
198
+ ' await expect(lastPost.container).toContainText(mention);',
199
+ ...end,
200
+ ].join('\n');
201
+ }
202
+ if (strategy === 'realtime-post') {
203
+ return [
204
+ ...header,
205
+ ...start,
206
+ ` const message = \`ai-${slug}-realtime-\${Date.now()}\`;`,
207
+ ' await channelsPage.postMessage(message);',
208
+ ' const lastPost = await channelsPage.getLastPost();',
209
+ ' await expect(lastPost.container).toContainText(message);',
210
+ " await expect(channelsPage.page.locator('#channel_view')).toBeVisible();",
211
+ ...end,
212
+ ].join('\n');
213
+ }
214
+ if (strategy === 'message-post') {
215
+ return [
216
+ ...header,
217
+ ...start,
218
+ ` const message = \`ai-${slug}-message-\${Date.now()}\`;`,
219
+ ' await channelsPage.postMessage(message);',
220
+ ' await expect(channelsPage.getLastPost()).toContainText(message);',
221
+ ...end,
222
+ ].join('\n');
223
+ }
224
+ if (strategy === 'channel-baseline') {
225
+ return [
226
+ ...header,
227
+ ...start,
228
+ " await expect(channelsPage.page.locator('#channelHeaderTitle')).toBeVisible();",
229
+ " await expect(channelsPage.page.locator('#SidebarContainer')).toBeVisible();",
230
+ ...end,
231
+ ].join('\n');
232
+ }
233
+ if (strategy === 'search-baseline') {
234
+ return [
235
+ ...header,
236
+ ...start,
237
+ ` const searchTerm = \`ai-${slug}-\${Date.now().toString().slice(-6)}\`;`,
238
+ ' await channelsPage.postMessage(searchTerm);',
239
+ ' await channelsPage.globalHeader.openSearch();',
240
+ ' await channelsPage.searchBox.searchInput.fill(searchTerm);',
241
+ " await channelsPage.page.keyboard.press('Enter');",
242
+ " await expect(channelsPage.page.locator('#searchContainer')).toBeVisible();",
243
+ ...end,
244
+ ].join('\n');
245
+ }
246
+ return [
247
+ ...header,
248
+ ...start,
249
+ ' await expect(channelsPage.page).toHaveURL(/\\/channels\\//);',
250
+ " await expect(channelsPage.page.locator('#channelHeaderTitle')).toBeVisible();",
251
+ ...end,
252
+ ].join('\n');
253
+ }
@@ -0,0 +1,5 @@
1
+ import type { PipelineConfig } from './config.js';
2
+ import type { ValidationResult } from './pipeline_types.js';
3
+ export declare function runPlaywrightRuntimeValidation(testsRoot: string, testFile: string, pipeline: PipelineConfig, playwrightBinary: string | null): ValidationResult;
4
+ export declare function runPlaywrightListValidation(testsRoot: string, testFile: string, pipeline: PipelineConfig, playwrightBinary: string | null): ValidationResult;
5
+ //# sourceMappingURL=validation_runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation_runner.d.ts","sourceRoot":"","sources":["../../src/agent/validation_runner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AAI1D,wBAAgB,8BAA8B,CAC1C,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,EACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAChC,gBAAgB,CA+BlB;AAED,wBAAgB,2BAA2B,CACvC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,EACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAChC,gBAAgB,CAqClB"}