agentic-qe 2.5.6 → 2.5.8

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 (210) hide show
  1. package/.claude/agents/n8n/n8n-base-agent.md +376 -0
  2. package/.claude/agents/n8n/n8n-bdd-scenario-tester.md +613 -0
  3. package/.claude/agents/n8n/n8n-chaos-tester.md +654 -0
  4. package/.claude/agents/n8n/n8n-ci-orchestrator.md +850 -0
  5. package/.claude/agents/n8n/n8n-compliance-validator.md +685 -0
  6. package/.claude/agents/n8n/n8n-expression-validator.md +560 -0
  7. package/.claude/agents/n8n/n8n-integration-test.md +602 -0
  8. package/.claude/agents/n8n/n8n-monitoring-validator.md +589 -0
  9. package/.claude/agents/n8n/n8n-node-validator.md +455 -0
  10. package/.claude/agents/n8n/n8n-performance-tester.md +630 -0
  11. package/.claude/agents/n8n/n8n-security-auditor.md +786 -0
  12. package/.claude/agents/n8n/n8n-trigger-test.md +500 -0
  13. package/.claude/agents/n8n/n8n-unit-tester.md +633 -0
  14. package/.claude/agents/n8n/n8n-version-comparator.md +567 -0
  15. package/.claude/agents/n8n/n8n-workflow-executor.md +392 -0
  16. package/.claude/skills/n8n-expression-testing/SKILL.md +434 -0
  17. package/.claude/skills/n8n-integration-testing-patterns/SKILL.md +540 -0
  18. package/.claude/skills/n8n-security-testing/SKILL.md +599 -0
  19. package/.claude/skills/n8n-trigger-testing-strategies/SKILL.md +541 -0
  20. package/.claude/skills/n8n-workflow-testing-fundamentals/SKILL.md +447 -0
  21. package/CHANGELOG.md +127 -0
  22. package/README.md +7 -4
  23. package/dist/agents/BaseAgent.d.ts +142 -0
  24. package/dist/agents/BaseAgent.d.ts.map +1 -1
  25. package/dist/agents/BaseAgent.js +372 -2
  26. package/dist/agents/BaseAgent.js.map +1 -1
  27. package/dist/agents/TestGeneratorAgent.d.ts +5 -0
  28. package/dist/agents/TestGeneratorAgent.d.ts.map +1 -1
  29. package/dist/agents/TestGeneratorAgent.js +38 -0
  30. package/dist/agents/TestGeneratorAgent.js.map +1 -1
  31. package/dist/agents/index.d.ts +1 -1
  32. package/dist/agents/index.d.ts.map +1 -1
  33. package/dist/agents/index.js.map +1 -1
  34. package/dist/agents/n8n/N8nAPIClient.d.ts +121 -0
  35. package/dist/agents/n8n/N8nAPIClient.d.ts.map +1 -0
  36. package/dist/agents/n8n/N8nAPIClient.js +367 -0
  37. package/dist/agents/n8n/N8nAPIClient.js.map +1 -0
  38. package/dist/agents/n8n/N8nAuditPersistence.d.ts +120 -0
  39. package/dist/agents/n8n/N8nAuditPersistence.d.ts.map +1 -0
  40. package/dist/agents/n8n/N8nAuditPersistence.js +473 -0
  41. package/dist/agents/n8n/N8nAuditPersistence.js.map +1 -0
  42. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts +159 -0
  43. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts.map +1 -0
  44. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js +697 -0
  45. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js.map +1 -0
  46. package/dist/agents/n8n/N8nBaseAgent.d.ts +126 -0
  47. package/dist/agents/n8n/N8nBaseAgent.d.ts.map +1 -0
  48. package/dist/agents/n8n/N8nBaseAgent.js +446 -0
  49. package/dist/agents/n8n/N8nBaseAgent.js.map +1 -0
  50. package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts +164 -0
  51. package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts.map +1 -0
  52. package/dist/agents/n8n/N8nCIOrchestratorAgent.js +610 -0
  53. package/dist/agents/n8n/N8nCIOrchestratorAgent.js.map +1 -0
  54. package/dist/agents/n8n/N8nChaosTesterAgent.d.ts +205 -0
  55. package/dist/agents/n8n/N8nChaosTesterAgent.d.ts.map +1 -0
  56. package/dist/agents/n8n/N8nChaosTesterAgent.js +729 -0
  57. package/dist/agents/n8n/N8nChaosTesterAgent.js.map +1 -0
  58. package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts +228 -0
  59. package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts.map +1 -0
  60. package/dist/agents/n8n/N8nComplianceValidatorAgent.js +986 -0
  61. package/dist/agents/n8n/N8nComplianceValidatorAgent.js.map +1 -0
  62. package/dist/agents/n8n/N8nContractTesterAgent.d.ts +213 -0
  63. package/dist/agents/n8n/N8nContractTesterAgent.d.ts.map +1 -0
  64. package/dist/agents/n8n/N8nContractTesterAgent.js +989 -0
  65. package/dist/agents/n8n/N8nContractTesterAgent.js.map +1 -0
  66. package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts +99 -0
  67. package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts.map +1 -0
  68. package/dist/agents/n8n/N8nExpressionValidatorAgent.js +632 -0
  69. package/dist/agents/n8n/N8nExpressionValidatorAgent.js.map +1 -0
  70. package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts +238 -0
  71. package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts.map +1 -0
  72. package/dist/agents/n8n/N8nFailureModeTesterAgent.js +956 -0
  73. package/dist/agents/n8n/N8nFailureModeTesterAgent.js.map +1 -0
  74. package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts +242 -0
  75. package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts.map +1 -0
  76. package/dist/agents/n8n/N8nIdempotencyTesterAgent.js +992 -0
  77. package/dist/agents/n8n/N8nIdempotencyTesterAgent.js.map +1 -0
  78. package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts +104 -0
  79. package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts.map +1 -0
  80. package/dist/agents/n8n/N8nIntegrationTestAgent.js +653 -0
  81. package/dist/agents/n8n/N8nIntegrationTestAgent.js.map +1 -0
  82. package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts +210 -0
  83. package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts.map +1 -0
  84. package/dist/agents/n8n/N8nMonitoringValidatorAgent.js +669 -0
  85. package/dist/agents/n8n/N8nMonitoringValidatorAgent.js.map +1 -0
  86. package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts +142 -0
  87. package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts.map +1 -0
  88. package/dist/agents/n8n/N8nNodeValidatorAgent.js +1090 -0
  89. package/dist/agents/n8n/N8nNodeValidatorAgent.js.map +1 -0
  90. package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts +198 -0
  91. package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts.map +1 -0
  92. package/dist/agents/n8n/N8nPerformanceTesterAgent.js +653 -0
  93. package/dist/agents/n8n/N8nPerformanceTesterAgent.js.map +1 -0
  94. package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts +245 -0
  95. package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts.map +1 -0
  96. package/dist/agents/n8n/N8nReplayabilityTesterAgent.js +952 -0
  97. package/dist/agents/n8n/N8nReplayabilityTesterAgent.js.map +1 -0
  98. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts +325 -0
  99. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts.map +1 -0
  100. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js +1187 -0
  101. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js.map +1 -0
  102. package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts +91 -0
  103. package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts.map +1 -0
  104. package/dist/agents/n8n/N8nSecurityAuditorAgent.js +825 -0
  105. package/dist/agents/n8n/N8nSecurityAuditorAgent.js.map +1 -0
  106. package/dist/agents/n8n/N8nTestHarness.d.ts +131 -0
  107. package/dist/agents/n8n/N8nTestHarness.d.ts.map +1 -0
  108. package/dist/agents/n8n/N8nTestHarness.js +456 -0
  109. package/dist/agents/n8n/N8nTestHarness.js.map +1 -0
  110. package/dist/agents/n8n/N8nTriggerTestAgent.d.ts +119 -0
  111. package/dist/agents/n8n/N8nTriggerTestAgent.d.ts.map +1 -0
  112. package/dist/agents/n8n/N8nTriggerTestAgent.js +652 -0
  113. package/dist/agents/n8n/N8nTriggerTestAgent.js.map +1 -0
  114. package/dist/agents/n8n/N8nUnitTesterAgent.d.ts +130 -0
  115. package/dist/agents/n8n/N8nUnitTesterAgent.d.ts.map +1 -0
  116. package/dist/agents/n8n/N8nUnitTesterAgent.js +522 -0
  117. package/dist/agents/n8n/N8nUnitTesterAgent.js.map +1 -0
  118. package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts +201 -0
  119. package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts.map +1 -0
  120. package/dist/agents/n8n/N8nVersionComparatorAgent.js +645 -0
  121. package/dist/agents/n8n/N8nVersionComparatorAgent.js.map +1 -0
  122. package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts +120 -0
  123. package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts.map +1 -0
  124. package/dist/agents/n8n/N8nWorkflowExecutorAgent.js +347 -0
  125. package/dist/agents/n8n/N8nWorkflowExecutorAgent.js.map +1 -0
  126. package/dist/agents/n8n/index.d.ts +119 -0
  127. package/dist/agents/n8n/index.d.ts.map +1 -0
  128. package/dist/agents/n8n/index.js +298 -0
  129. package/dist/agents/n8n/index.js.map +1 -0
  130. package/dist/agents/n8n/types.d.ts +486 -0
  131. package/dist/agents/n8n/types.d.ts.map +1 -0
  132. package/dist/agents/n8n/types.js +8 -0
  133. package/dist/agents/n8n/types.js.map +1 -0
  134. package/dist/cli/init/agents.d.ts.map +1 -1
  135. package/dist/cli/init/agents.js +29 -0
  136. package/dist/cli/init/agents.js.map +1 -1
  137. package/dist/cli/init/skills.d.ts.map +1 -1
  138. package/dist/cli/init/skills.js +7 -1
  139. package/dist/cli/init/skills.js.map +1 -1
  140. package/dist/core/memory/HNSWVectorMemory.js +1 -1
  141. package/dist/core/memory/RuVectorPatternStore.d.ts +90 -0
  142. package/dist/core/memory/RuVectorPatternStore.d.ts.map +1 -1
  143. package/dist/core/memory/RuVectorPatternStore.js +209 -0
  144. package/dist/core/memory/RuVectorPatternStore.js.map +1 -1
  145. package/dist/learning/FederatedManager.d.ts +232 -0
  146. package/dist/learning/FederatedManager.d.ts.map +1 -0
  147. package/dist/learning/FederatedManager.js +489 -0
  148. package/dist/learning/FederatedManager.js.map +1 -0
  149. package/dist/learning/HNSWPatternAdapter.d.ts +117 -0
  150. package/dist/learning/HNSWPatternAdapter.d.ts.map +1 -0
  151. package/dist/learning/HNSWPatternAdapter.js +262 -0
  152. package/dist/learning/HNSWPatternAdapter.js.map +1 -0
  153. package/dist/learning/LearningEngine.d.ts +27 -0
  154. package/dist/learning/LearningEngine.d.ts.map +1 -1
  155. package/dist/learning/LearningEngine.js +75 -1
  156. package/dist/learning/LearningEngine.js.map +1 -1
  157. package/dist/learning/PatternCurator.d.ts +217 -0
  158. package/dist/learning/PatternCurator.d.ts.map +1 -0
  159. package/dist/learning/PatternCurator.js +393 -0
  160. package/dist/learning/PatternCurator.js.map +1 -0
  161. package/dist/learning/index.d.ts +6 -0
  162. package/dist/learning/index.d.ts.map +1 -1
  163. package/dist/learning/index.js +16 -1
  164. package/dist/learning/index.js.map +1 -1
  165. package/dist/learning/types.d.ts +4 -0
  166. package/dist/learning/types.d.ts.map +1 -1
  167. package/dist/mcp/server-instructions.d.ts +1 -1
  168. package/dist/mcp/server-instructions.js +1 -1
  169. package/dist/memory/HNSWPatternStore.d.ts +176 -0
  170. package/dist/memory/HNSWPatternStore.d.ts.map +1 -0
  171. package/dist/memory/HNSWPatternStore.js +392 -0
  172. package/dist/memory/HNSWPatternStore.js.map +1 -0
  173. package/dist/memory/index.d.ts +8 -0
  174. package/dist/memory/index.d.ts.map +1 -0
  175. package/dist/memory/index.js +13 -0
  176. package/dist/memory/index.js.map +1 -0
  177. package/dist/providers/HybridRouter.d.ts +85 -4
  178. package/dist/providers/HybridRouter.d.ts.map +1 -1
  179. package/dist/providers/HybridRouter.js +332 -10
  180. package/dist/providers/HybridRouter.js.map +1 -1
  181. package/dist/providers/LLMBaselineTracker.d.ts +120 -0
  182. package/dist/providers/LLMBaselineTracker.d.ts.map +1 -0
  183. package/dist/providers/LLMBaselineTracker.js +305 -0
  184. package/dist/providers/LLMBaselineTracker.js.map +1 -0
  185. package/dist/providers/OpenRouterProvider.d.ts +26 -0
  186. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  187. package/dist/providers/OpenRouterProvider.js +75 -6
  188. package/dist/providers/OpenRouterProvider.js.map +1 -1
  189. package/dist/providers/RuVectorClient.d.ts +259 -0
  190. package/dist/providers/RuVectorClient.d.ts.map +1 -0
  191. package/dist/providers/RuVectorClient.js +416 -0
  192. package/dist/providers/RuVectorClient.js.map +1 -0
  193. package/dist/providers/RuvllmPatternCurator.d.ts +116 -0
  194. package/dist/providers/RuvllmPatternCurator.d.ts.map +1 -0
  195. package/dist/providers/RuvllmPatternCurator.js +323 -0
  196. package/dist/providers/RuvllmPatternCurator.js.map +1 -0
  197. package/dist/providers/RuvllmProvider.d.ts +233 -1
  198. package/dist/providers/RuvllmProvider.d.ts.map +1 -1
  199. package/dist/providers/RuvllmProvider.js +781 -11
  200. package/dist/providers/RuvllmProvider.js.map +1 -1
  201. package/dist/providers/index.d.ts +5 -1
  202. package/dist/providers/index.d.ts.map +1 -1
  203. package/dist/providers/index.js +12 -2
  204. package/dist/providers/index.js.map +1 -1
  205. package/dist/utils/ruvllm-loader.d.ts +98 -1
  206. package/dist/utils/ruvllm-loader.d.ts.map +1 -1
  207. package/dist/utils/ruvllm-loader.js.map +1 -1
  208. package/docs/reference/agents.md +91 -2
  209. package/docs/reference/skills.md +97 -2
  210. package/package.json +2 -2
@@ -0,0 +1,697 @@
1
+ "use strict";
2
+ /**
3
+ * N8nBDDScenarioTesterAgent
4
+ *
5
+ * Behavior-Driven Development testing for n8n workflows:
6
+ * - Gherkin scenario parsing
7
+ * - Feature file execution
8
+ * - Business requirement validation
9
+ * - Natural language test definitions
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.N8nBDDScenarioTesterAgent = void 0;
13
+ const N8nBaseAgent_1 = require("./N8nBaseAgent");
14
+ // Step definition patterns
15
+ const STEP_PATTERNS = [
16
+ {
17
+ pattern: /^the workflow "([^"]+)" is (active|inactive)$/i,
18
+ handler: 'checkWorkflowStatus',
19
+ },
20
+ {
21
+ pattern: /^I trigger the workflow with data:?$/i,
22
+ handler: 'triggerWorkflow',
23
+ },
24
+ {
25
+ pattern: /^I execute the workflow$/i,
26
+ handler: 'executeWorkflow',
27
+ },
28
+ {
29
+ pattern: /^the workflow should complete (successfully|with errors?)$/i,
30
+ handler: 'checkCompletion',
31
+ },
32
+ {
33
+ pattern: /^the output should contain "([^"]+)"$/i,
34
+ handler: 'checkOutputContains',
35
+ },
36
+ {
37
+ pattern: /^the output field "([^"]+)" should equal "([^"]+)"$/i,
38
+ handler: 'checkOutputField',
39
+ },
40
+ {
41
+ pattern: /^the node "([^"]+)" should be executed$/i,
42
+ handler: 'checkNodeExecuted',
43
+ },
44
+ {
45
+ pattern: /^the execution time should be less than (\d+)ms$/i,
46
+ handler: 'checkExecutionTime',
47
+ },
48
+ {
49
+ pattern: /^no errors should occur$/i,
50
+ handler: 'checkNoErrors',
51
+ },
52
+ {
53
+ pattern: /^the item count should be (\d+)$/i,
54
+ handler: 'checkItemCount',
55
+ },
56
+ ];
57
+ class N8nBDDScenarioTesterAgent extends N8nBaseAgent_1.N8nBaseAgent {
58
+ constructor(config) {
59
+ const capabilities = [
60
+ {
61
+ name: 'gherkin-parsing',
62
+ version: '1.0.0',
63
+ description: 'Parse and execute Gherkin scenarios',
64
+ parameters: {},
65
+ },
66
+ {
67
+ name: 'step-matching',
68
+ version: '1.0.0',
69
+ description: 'Match natural language steps to actions',
70
+ parameters: {},
71
+ },
72
+ {
73
+ name: 'scenario-execution',
74
+ version: '1.0.0',
75
+ description: 'Execute BDD scenarios against workflows',
76
+ parameters: {},
77
+ },
78
+ {
79
+ name: 'business-validation',
80
+ version: '1.0.0',
81
+ description: 'Validate business requirements',
82
+ parameters: {},
83
+ },
84
+ ];
85
+ super({
86
+ ...config,
87
+ type: 'n8n-bdd-scenario-tester',
88
+ capabilities: [...capabilities, ...(config.capabilities || [])],
89
+ });
90
+ this.lastExecution = null;
91
+ this.lastOutput = null;
92
+ this.executedNodes = new Set();
93
+ }
94
+ async performTask(task) {
95
+ const bddTask = task;
96
+ if (bddTask.type !== 'bdd-test') {
97
+ throw new Error(`Unsupported task type: ${bddTask.type}`);
98
+ }
99
+ return this.runBDDTests(bddTask.target, bddTask.options);
100
+ }
101
+ /**
102
+ * Run BDD tests for workflow
103
+ */
104
+ async runBDDTests(workflowId, options) {
105
+ const workflow = await this.getWorkflow(workflowId);
106
+ const startTime = Date.now();
107
+ // Get scenarios from options or generate from workflow
108
+ let scenarios = options?.scenarios || [];
109
+ if (scenarios.length === 0 && options?.featureFile) {
110
+ scenarios = this.parseFeatureFile(options.featureFile);
111
+ }
112
+ if (scenarios.length === 0) {
113
+ scenarios = this.generateScenariosFromWorkflow(workflow);
114
+ }
115
+ // Filter by tags if specified
116
+ if (options?.tags && options.tags.length > 0) {
117
+ scenarios = scenarios.filter(s => s.tags?.some(t => options.tags.includes(t)));
118
+ }
119
+ // Execute scenarios
120
+ const results = [];
121
+ for (const scenario of scenarios) {
122
+ if (scenario.examples && scenario.examples.length > 0) {
123
+ // Scenario Outline with examples
124
+ for (const example of scenario.examples) {
125
+ const result = await this.executeScenario(workflow, scenario, options?.dryRun, example.values);
126
+ result.example = example.values;
127
+ results.push(result);
128
+ }
129
+ }
130
+ else {
131
+ const result = await this.executeScenario(workflow, scenario, options?.dryRun);
132
+ results.push(result);
133
+ }
134
+ }
135
+ // Calculate summary
136
+ const summary = this.calculateSummary(results, Date.now() - startTime);
137
+ // Calculate coverage
138
+ const coverage = this.calculateCoverage(workflow, results);
139
+ // Generate report if requested
140
+ let report;
141
+ if (options?.generateReport) {
142
+ report = this.generateReport(workflow.name, results, summary);
143
+ }
144
+ const result = {
145
+ workflowId,
146
+ featureName: workflow.name,
147
+ scenarios: results,
148
+ summary,
149
+ coverage,
150
+ report,
151
+ };
152
+ // Store result
153
+ await this.storeTestResult(`bdd-test:${workflowId}`, result);
154
+ // Emit event
155
+ this.emitEvent('bdd.test.completed', {
156
+ workflowId,
157
+ scenariosPassed: summary.passed,
158
+ scenariosFailed: summary.failed,
159
+ coverage: coverage.coveragePercentage,
160
+ });
161
+ return result;
162
+ }
163
+ /**
164
+ * Parse feature file content
165
+ */
166
+ parseFeatureFile(content) {
167
+ const scenarios = [];
168
+ const lines = content.split('\n').map(l => l.trim());
169
+ let currentScenario = null;
170
+ let currentSection = null;
171
+ let currentTags = [];
172
+ for (const line of lines) {
173
+ // Skip empty lines and comments
174
+ if (!line || line.startsWith('#'))
175
+ continue;
176
+ // Parse tags
177
+ if (line.startsWith('@')) {
178
+ currentTags = line.split(/\s+/).filter(t => t.startsWith('@'));
179
+ continue;
180
+ }
181
+ // Parse scenario
182
+ if (line.startsWith('Scenario:') || line.startsWith('Scenario Outline:')) {
183
+ if (currentScenario) {
184
+ scenarios.push(currentScenario);
185
+ }
186
+ currentScenario = {
187
+ name: line.replace(/Scenario( Outline)?:\s*/, ''),
188
+ tags: [...currentTags],
189
+ given: [],
190
+ when: [],
191
+ then: [],
192
+ };
193
+ currentTags = [];
194
+ currentSection = null;
195
+ continue;
196
+ }
197
+ // Parse steps
198
+ if (currentScenario) {
199
+ if (line.startsWith('Given ')) {
200
+ currentSection = 'given';
201
+ currentScenario.given.push({
202
+ keyword: 'Given',
203
+ text: line.replace('Given ', ''),
204
+ });
205
+ }
206
+ else if (line.startsWith('When ')) {
207
+ currentSection = 'when';
208
+ currentScenario.when.push({
209
+ keyword: 'When',
210
+ text: line.replace('When ', ''),
211
+ });
212
+ }
213
+ else if (line.startsWith('Then ')) {
214
+ currentSection = 'then';
215
+ currentScenario.then.push({
216
+ keyword: 'Then',
217
+ text: line.replace('Then ', ''),
218
+ });
219
+ }
220
+ else if (line.startsWith('And ') || line.startsWith('But ')) {
221
+ const keyword = line.startsWith('And ') ? 'And' : 'But';
222
+ const text = line.replace(/^(And|But) /, '');
223
+ if (currentSection && currentScenario[currentSection]) {
224
+ currentScenario[currentSection].push({ keyword, text });
225
+ }
226
+ }
227
+ else if (line.startsWith('Examples:')) {
228
+ // Parse examples table (simplified)
229
+ currentScenario.examples = currentScenario.examples || [];
230
+ }
231
+ }
232
+ }
233
+ if (currentScenario) {
234
+ scenarios.push(currentScenario);
235
+ }
236
+ return scenarios;
237
+ }
238
+ /**
239
+ * Generate scenarios from workflow
240
+ */
241
+ generateScenariosFromWorkflow(workflow) {
242
+ const scenarios = [];
243
+ // Basic execution scenario
244
+ scenarios.push({
245
+ name: `Execute ${workflow.name}`,
246
+ description: 'Verify workflow executes successfully',
247
+ tags: ['@auto-generated', '@smoke'],
248
+ given: [
249
+ { keyword: 'Given', text: `the workflow "${workflow.name}" is active` },
250
+ ],
251
+ when: [
252
+ { keyword: 'When', text: 'I execute the workflow' },
253
+ ],
254
+ then: [
255
+ { keyword: 'Then', text: 'the workflow should complete successfully' },
256
+ { keyword: 'And', text: 'no errors should occur' },
257
+ ],
258
+ });
259
+ // Node-specific scenarios
260
+ for (const node of workflow.nodes) {
261
+ if (node.type.includes('trigger'))
262
+ continue;
263
+ scenarios.push({
264
+ name: `Verify ${node.name} execution`,
265
+ tags: ['@auto-generated', '@node-test'],
266
+ given: [
267
+ { keyword: 'Given', text: `the workflow "${workflow.name}" is active` },
268
+ ],
269
+ when: [
270
+ { keyword: 'When', text: 'I execute the workflow' },
271
+ ],
272
+ then: [
273
+ { keyword: 'Then', text: `the node "${node.name}" should be executed` },
274
+ ],
275
+ });
276
+ }
277
+ return scenarios;
278
+ }
279
+ /**
280
+ * Execute a single scenario
281
+ */
282
+ async executeScenario(workflow, scenario, dryRun, exampleValues) {
283
+ const startTime = Date.now();
284
+ const stepResults = [];
285
+ let scenarioStatus = 'passed';
286
+ let scenarioError;
287
+ // Reset state
288
+ this.lastExecution = null;
289
+ this.lastOutput = null;
290
+ this.executedNodes.clear();
291
+ // Execute all steps
292
+ const allSteps = [
293
+ ...scenario.given,
294
+ ...scenario.when,
295
+ ...scenario.then,
296
+ ];
297
+ for (const step of allSteps) {
298
+ // Replace example placeholders
299
+ let stepText = step.text;
300
+ if (exampleValues) {
301
+ for (const [key, value] of Object.entries(exampleValues)) {
302
+ stepText = stepText.replace(new RegExp(`<${key}>`, 'g'), value);
303
+ }
304
+ }
305
+ const stepResult = await this.executeStep(workflow, { ...step, text: stepText }, dryRun);
306
+ stepResults.push(stepResult);
307
+ if (stepResult.status === 'failed') {
308
+ scenarioStatus = 'failed';
309
+ scenarioError = stepResult.error;
310
+ // Skip remaining steps
311
+ break;
312
+ }
313
+ }
314
+ // Mark remaining steps as skipped
315
+ if (scenarioStatus === 'failed') {
316
+ const executedCount = stepResults.length;
317
+ for (let i = executedCount; i < allSteps.length; i++) {
318
+ stepResults.push({
319
+ keyword: allSteps[i].keyword,
320
+ text: allSteps[i].text,
321
+ status: 'skipped',
322
+ duration: 0,
323
+ });
324
+ }
325
+ }
326
+ return {
327
+ name: scenario.name,
328
+ description: scenario.description,
329
+ tags: scenario.tags || [],
330
+ status: scenarioStatus,
331
+ duration: Date.now() - startTime,
332
+ steps: stepResults,
333
+ error: scenarioError,
334
+ };
335
+ }
336
+ /**
337
+ * Execute a single step
338
+ */
339
+ async executeStep(workflow, step, dryRun) {
340
+ const startTime = Date.now();
341
+ if (dryRun) {
342
+ return {
343
+ keyword: step.keyword,
344
+ text: step.text,
345
+ status: 'pending',
346
+ duration: Date.now() - startTime,
347
+ };
348
+ }
349
+ try {
350
+ // Match step to handler
351
+ const matched = this.matchStep(step.text);
352
+ if (!matched) {
353
+ return {
354
+ keyword: step.keyword,
355
+ text: step.text,
356
+ status: 'pending',
357
+ duration: Date.now() - startTime,
358
+ error: 'No matching step definition found',
359
+ };
360
+ }
361
+ // Execute handler
362
+ await this.executeHandler(workflow, matched.handler, matched.params, step);
363
+ return {
364
+ keyword: step.keyword,
365
+ text: step.text,
366
+ status: 'passed',
367
+ duration: Date.now() - startTime,
368
+ };
369
+ }
370
+ catch (error) {
371
+ return {
372
+ keyword: step.keyword,
373
+ text: step.text,
374
+ status: 'failed',
375
+ duration: Date.now() - startTime,
376
+ error: error instanceof Error ? error.message : String(error),
377
+ };
378
+ }
379
+ }
380
+ /**
381
+ * Match step text to pattern
382
+ */
383
+ matchStep(text) {
384
+ for (const { pattern, handler } of STEP_PATTERNS) {
385
+ const match = text.match(pattern);
386
+ if (match) {
387
+ return {
388
+ handler,
389
+ params: match.slice(1),
390
+ };
391
+ }
392
+ }
393
+ return null;
394
+ }
395
+ /**
396
+ * Execute step handler
397
+ */
398
+ async executeHandler(workflow, handler, params, step) {
399
+ switch (handler) {
400
+ case 'checkWorkflowStatus':
401
+ this.checkWorkflowStatus(workflow, params[0], params[1]);
402
+ break;
403
+ case 'triggerWorkflow':
404
+ await this.triggerWorkflowStep(workflow, step.dataTable);
405
+ break;
406
+ case 'executeWorkflow':
407
+ await this.executeWorkflowStep(workflow);
408
+ break;
409
+ case 'checkCompletion':
410
+ this.checkCompletion(params[0]);
411
+ break;
412
+ case 'checkOutputContains':
413
+ this.checkOutputContains(params[0]);
414
+ break;
415
+ case 'checkOutputField':
416
+ this.checkOutputField(params[0], params[1]);
417
+ break;
418
+ case 'checkNodeExecuted':
419
+ this.checkNodeExecuted(params[0]);
420
+ break;
421
+ case 'checkExecutionTime':
422
+ this.checkExecutionTime(parseInt(params[0], 10));
423
+ break;
424
+ case 'checkNoErrors':
425
+ this.checkNoErrors();
426
+ break;
427
+ case 'checkItemCount':
428
+ this.checkItemCount(parseInt(params[0], 10));
429
+ break;
430
+ default:
431
+ throw new Error(`Unknown handler: ${handler}`);
432
+ }
433
+ }
434
+ // Step handler implementations
435
+ checkWorkflowStatus(workflow, expectedName, expectedStatus) {
436
+ if (!workflow.name.includes(expectedName)) {
437
+ throw new Error(`Workflow name mismatch: expected "${expectedName}", got "${workflow.name}"`);
438
+ }
439
+ const isActive = workflow.active !== false;
440
+ const expectActive = expectedStatus === 'active';
441
+ if (isActive !== expectActive) {
442
+ throw new Error(`Workflow is ${isActive ? 'active' : 'inactive'}, expected ${expectedStatus}`);
443
+ }
444
+ }
445
+ async triggerWorkflowStep(workflow, dataTable) {
446
+ const inputData = dataTable?.[0] || {};
447
+ // Actually execute and wait for completion
448
+ const execution = await this.executeWorkflow(workflow.id, inputData, {
449
+ waitForCompletion: true,
450
+ timeout: 30000,
451
+ });
452
+ this.lastExecution = await this.waitForExecution(execution.id, 30000);
453
+ this.extractExecutedNodes();
454
+ this.extractOutput();
455
+ }
456
+ async executeWorkflowStep(workflow) {
457
+ // Actually execute and wait for completion
458
+ const execution = await this.executeWorkflow(workflow.id, {}, {
459
+ waitForCompletion: true,
460
+ timeout: 30000,
461
+ });
462
+ this.lastExecution = await this.waitForExecution(execution.id, 30000);
463
+ this.extractExecutedNodes();
464
+ this.extractOutput();
465
+ }
466
+ /**
467
+ * Wait for workflow execution to complete
468
+ */
469
+ async waitForExecution(executionId, timeoutMs) {
470
+ const startTime = Date.now();
471
+ while (Date.now() - startTime < timeoutMs) {
472
+ const execution = await this.getExecution(executionId);
473
+ if (execution.status !== 'running' && execution.status !== 'waiting') {
474
+ return execution;
475
+ }
476
+ await new Promise(resolve => setTimeout(resolve, 500));
477
+ }
478
+ throw new Error(`Execution ${executionId} timed out after ${timeoutMs}ms`);
479
+ }
480
+ /**
481
+ * Extract output from last execution for assertions
482
+ */
483
+ extractOutput() {
484
+ if (this.lastExecution?.data?.resultData?.runData) {
485
+ // Get the last node's output
486
+ const lastNode = this.lastExecution.data.resultData.lastNodeExecuted;
487
+ if (lastNode) {
488
+ const nodeRuns = this.lastExecution.data.resultData.runData[lastNode];
489
+ if (nodeRuns && nodeRuns.length > 0) {
490
+ const lastRun = nodeRuns[nodeRuns.length - 1];
491
+ const outputItems = lastRun.data?.main?.[0];
492
+ if (outputItems && outputItems.length > 0) {
493
+ this.lastOutput = outputItems[0].json;
494
+ }
495
+ }
496
+ }
497
+ }
498
+ }
499
+ extractExecutedNodes() {
500
+ if (this.lastExecution?.data?.resultData?.runData) {
501
+ for (const nodeName of Object.keys(this.lastExecution.data.resultData.runData)) {
502
+ this.executedNodes.add(nodeName);
503
+ }
504
+ }
505
+ }
506
+ checkCompletion(expectedResult) {
507
+ if (!this.lastExecution) {
508
+ throw new Error('No execution found');
509
+ }
510
+ const expectSuccess = expectedResult === 'successfully';
511
+ const isSuccess = this.lastExecution.status === 'success';
512
+ if (isSuccess !== expectSuccess) {
513
+ throw new Error(`Workflow ${isSuccess ? 'succeeded' : 'failed'}, expected to ${expectSuccess ? 'succeed' : 'fail'}`);
514
+ }
515
+ }
516
+ checkOutputContains(expectedContent) {
517
+ const outputStr = JSON.stringify(this.lastExecution?.data || {});
518
+ if (!outputStr.includes(expectedContent)) {
519
+ throw new Error(`Output does not contain "${expectedContent}"`);
520
+ }
521
+ }
522
+ checkOutputField(field, expectedValue) {
523
+ const data = this.lastExecution?.data?.resultData?.runData;
524
+ if (!data) {
525
+ throw new Error('No output data found');
526
+ }
527
+ // Simplified field check
528
+ const outputStr = JSON.stringify(data);
529
+ if (!outputStr.includes(`"${field}":`) || !outputStr.includes(expectedValue)) {
530
+ throw new Error(`Field "${field}" does not equal "${expectedValue}"`);
531
+ }
532
+ }
533
+ checkNodeExecuted(nodeName) {
534
+ if (!this.executedNodes.has(nodeName)) {
535
+ throw new Error(`Node "${nodeName}" was not executed`);
536
+ }
537
+ }
538
+ checkExecutionTime(maxMs) {
539
+ if (!this.lastExecution) {
540
+ throw new Error('No execution found');
541
+ }
542
+ const startTime = new Date(this.lastExecution.startedAt).getTime();
543
+ const endTime = this.lastExecution.stoppedAt
544
+ ? new Date(this.lastExecution.stoppedAt).getTime()
545
+ : Date.now();
546
+ const duration = endTime - startTime;
547
+ if (duration > maxMs) {
548
+ throw new Error(`Execution took ${duration}ms, expected less than ${maxMs}ms`);
549
+ }
550
+ }
551
+ checkNoErrors() {
552
+ if (this.lastExecution?.status === 'failed' || this.lastExecution?.status === 'crashed') {
553
+ throw new Error('Execution had errors');
554
+ }
555
+ }
556
+ checkItemCount(expectedCount) {
557
+ let actualCount = 0;
558
+ if (this.lastExecution?.data?.resultData?.runData) {
559
+ for (const nodeData of Object.values(this.lastExecution.data.resultData.runData)) {
560
+ if (Array.isArray(nodeData)) {
561
+ for (const run of nodeData) {
562
+ if (run.data?.main) {
563
+ for (const output of run.data.main) {
564
+ actualCount += output?.length || 0;
565
+ }
566
+ }
567
+ }
568
+ }
569
+ }
570
+ }
571
+ if (actualCount !== expectedCount) {
572
+ throw new Error(`Item count is ${actualCount}, expected ${expectedCount}`);
573
+ }
574
+ }
575
+ /**
576
+ * Calculate summary
577
+ */
578
+ calculateSummary(results, duration) {
579
+ const totalSteps = results.reduce((sum, r) => sum + r.steps.length, 0);
580
+ const stepsPassed = results.reduce((sum, r) => sum + r.steps.filter(s => s.status === 'passed').length, 0);
581
+ const stepsFailed = results.reduce((sum, r) => sum + r.steps.filter(s => s.status === 'failed').length, 0);
582
+ return {
583
+ totalScenarios: results.length,
584
+ passed: results.filter(r => r.status === 'passed').length,
585
+ failed: results.filter(r => r.status === 'failed').length,
586
+ skipped: results.filter(r => r.status === 'skipped').length,
587
+ pending: results.filter(r => r.status === 'pending').length,
588
+ totalSteps,
589
+ stepsPassed,
590
+ stepsFailed,
591
+ duration,
592
+ };
593
+ }
594
+ /**
595
+ * Calculate coverage
596
+ */
597
+ calculateCoverage(workflow, results) {
598
+ const allNodes = new Set(workflow.nodes.map(n => n.name));
599
+ const coveredNodes = new Set();
600
+ for (const result of results) {
601
+ if (result.status === 'passed') {
602
+ // Extract node names from steps
603
+ for (const step of result.steps) {
604
+ const nodeMatch = step.text.match(/node "([^"]+)"/);
605
+ if (nodeMatch) {
606
+ coveredNodes.add(nodeMatch[1]);
607
+ }
608
+ }
609
+ }
610
+ }
611
+ const nodesCovered = [...coveredNodes];
612
+ const nodesUncovered = [...allNodes].filter(n => !coveredNodes.has(n));
613
+ return {
614
+ nodesCovered,
615
+ nodesUncovered,
616
+ pathsCovered: [], // Would require more complex path analysis
617
+ coveragePercentage: allNodes.size > 0
618
+ ? (coveredNodes.size / allNodes.size) * 100
619
+ : 0,
620
+ };
621
+ }
622
+ /**
623
+ * Generate report
624
+ */
625
+ generateReport(featureName, results, summary) {
626
+ const lines = [
627
+ `# BDD Test Report: ${featureName}`,
628
+ '',
629
+ `**Date:** ${new Date().toISOString()}`,
630
+ '',
631
+ '## Summary',
632
+ '',
633
+ `| Metric | Value |`,
634
+ `|--------|-------|`,
635
+ `| Total Scenarios | ${summary.totalScenarios} |`,
636
+ `| Passed | ${summary.passed} |`,
637
+ `| Failed | ${summary.failed} |`,
638
+ `| Skipped | ${summary.skipped} |`,
639
+ `| Duration | ${summary.duration}ms |`,
640
+ '',
641
+ '## Scenarios',
642
+ '',
643
+ ];
644
+ for (const result of results) {
645
+ const statusIcon = result.status === 'passed' ? '✅' :
646
+ result.status === 'failed' ? '❌' :
647
+ result.status === 'skipped' ? '⏭️' : '⏸️';
648
+ lines.push(`### ${statusIcon} ${result.name}`);
649
+ if (result.example) {
650
+ lines.push(`**Example:** ${JSON.stringify(result.example)}`);
651
+ }
652
+ lines.push('');
653
+ for (const step of result.steps) {
654
+ const stepIcon = step.status === 'passed' ? '✅' :
655
+ step.status === 'failed' ? '❌' :
656
+ step.status === 'skipped' ? '⏭️' : '⏸️';
657
+ lines.push(`${stepIcon} **${step.keyword}** ${step.text}`);
658
+ if (step.error) {
659
+ lines.push(` > Error: ${step.error}`);
660
+ }
661
+ }
662
+ lines.push('');
663
+ }
664
+ return {
665
+ format: 'markdown',
666
+ content: lines.join('\n'),
667
+ };
668
+ }
669
+ /**
670
+ * Create scenario from natural language
671
+ */
672
+ createScenarioFromDescription(description) {
673
+ // Simple parser for natural language descriptions
674
+ const scenario = {
675
+ name: 'Generated Scenario',
676
+ given: [],
677
+ when: [],
678
+ then: [],
679
+ };
680
+ const sentences = description.split(/[.!]\s*/);
681
+ for (const sentence of sentences) {
682
+ const lower = sentence.toLowerCase();
683
+ if (lower.includes('given') || lower.includes('assuming')) {
684
+ scenario.given.push({ keyword: 'Given', text: sentence });
685
+ }
686
+ else if (lower.includes('when') || lower.includes('if')) {
687
+ scenario.when.push({ keyword: 'When', text: sentence });
688
+ }
689
+ else if (lower.includes('then') || lower.includes('should') || lower.includes('expect')) {
690
+ scenario.then.push({ keyword: 'Then', text: sentence });
691
+ }
692
+ }
693
+ return scenario;
694
+ }
695
+ }
696
+ exports.N8nBDDScenarioTesterAgent = N8nBDDScenarioTesterAgent;
697
+ //# sourceMappingURL=N8nBDDScenarioTesterAgent.js.map