@dev-blinq/cucumber_client 1.0.1403-dev → 1.0.1405-dev
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.
|
@@ -184,34 +184,6 @@ export class BVTRecorder {
|
|
|
184
184
|
projectDir: this.projectDir,
|
|
185
185
|
logger: this.logger,
|
|
186
186
|
});
|
|
187
|
-
this.stepRunner = new BVTStepRunner({
|
|
188
|
-
projectDir: this.projectDir,
|
|
189
|
-
sendExecutionStatus: (data) => {
|
|
190
|
-
if (data && data.type) {
|
|
191
|
-
switch (data.type) {
|
|
192
|
-
case "cmdExecutionStart":
|
|
193
|
-
console.log("Sending cmdExecutionStart event for cmdId:", data);
|
|
194
|
-
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
195
|
-
break;
|
|
196
|
-
case "cmdExecutionSuccess":
|
|
197
|
-
console.log("Sending cmdExecutionSuccess event for cmdId:", data);
|
|
198
|
-
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
199
|
-
break;
|
|
200
|
-
case "cmdExecutionError":
|
|
201
|
-
console.log("Sending cmdExecutionError event for cmdId:", data);
|
|
202
|
-
this.sendEvent(this.events.cmdExecutionError, data);
|
|
203
|
-
break;
|
|
204
|
-
case "interceptResults":
|
|
205
|
-
console.log("Sending interceptResults event");
|
|
206
|
-
this.sendEvent(this.events.interceptResults, data);
|
|
207
|
-
break;
|
|
208
|
-
default:
|
|
209
|
-
console.warn("Unknown command execution status type:", data.type);
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
});
|
|
215
187
|
this.pageSet = new Set();
|
|
216
188
|
this.lastKnownUrlPath = "";
|
|
217
189
|
// TODO: what is world?
|
|
@@ -322,7 +294,7 @@ export class BVTRecorder {
|
|
|
322
294
|
this.#remoteDebuggerPort = await findAvailablePort();
|
|
323
295
|
process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
|
|
324
296
|
|
|
325
|
-
this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
297
|
+
// this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
|
|
326
298
|
this.world = { attach: () => {} };
|
|
327
299
|
|
|
328
300
|
const ai_config_file = path.join(this.projectDir, "ai_config.json");
|
|
@@ -348,12 +320,40 @@ export class BVTRecorder {
|
|
|
348
320
|
let stopTime = Date.now();
|
|
349
321
|
this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
|
|
350
322
|
this.bvtContext = bvtContext;
|
|
323
|
+
this.stepRunner = new BVTStepRunner({
|
|
324
|
+
projectDir: this.projectDir,
|
|
325
|
+
sendExecutionStatus: (data) => {
|
|
326
|
+
if (data && data.type) {
|
|
327
|
+
switch (data.type) {
|
|
328
|
+
case "cmdExecutionStart":
|
|
329
|
+
console.log("Sending cmdExecutionStart event for cmdId:", data);
|
|
330
|
+
this.sendEvent(this.events.cmdExecutionStart, data);
|
|
331
|
+
break;
|
|
332
|
+
case "cmdExecutionSuccess":
|
|
333
|
+
console.log("Sending cmdExecutionSuccess event for cmdId:", data);
|
|
334
|
+
this.sendEvent(this.events.cmdExecutionSuccess, data);
|
|
335
|
+
break;
|
|
336
|
+
case "cmdExecutionError":
|
|
337
|
+
console.log("Sending cmdExecutionError event for cmdId:", data);
|
|
338
|
+
this.sendEvent(this.events.cmdExecutionError, data);
|
|
339
|
+
break;
|
|
340
|
+
case "interceptResults":
|
|
341
|
+
console.log("Sending interceptResults event");
|
|
342
|
+
this.sendEvent(this.events.interceptResults, data);
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
console.warn("Unknown command execution status type:", data.type);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
bvtContext: this.bvtContext,
|
|
351
|
+
});
|
|
351
352
|
const context = bvtContext.playContext;
|
|
352
353
|
this.context = context;
|
|
353
354
|
this.web = bvtContext.stable || bvtContext.web;
|
|
354
355
|
this.web.tryAllStrategies = true;
|
|
355
356
|
this.page = bvtContext.page;
|
|
356
|
-
|
|
357
357
|
this.pageSet.add(this.page);
|
|
358
358
|
this.lastKnownUrlPath = this._updateUrlPath();
|
|
359
359
|
const browser = await this.context.browser();
|
|
@@ -789,7 +789,6 @@ export class BVTRecorder {
|
|
|
789
789
|
}
|
|
790
790
|
|
|
791
791
|
async startRecordingInput() {
|
|
792
|
-
console.log("startRecordingInput");
|
|
793
792
|
await this.setMode("recordingInput");
|
|
794
793
|
}
|
|
795
794
|
async stopRecordingInput() {
|
|
@@ -813,9 +812,17 @@ export class BVTRecorder {
|
|
|
813
812
|
}
|
|
814
813
|
|
|
815
814
|
async abortExecution() {
|
|
816
|
-
this.bvtContext.web.abortedExecution = true;
|
|
817
815
|
await this.stepRunner.abortExecution();
|
|
818
816
|
}
|
|
817
|
+
|
|
818
|
+
async pauseExecution({ cmdId }) {
|
|
819
|
+
await this.stepRunner.pauseExecution(cmdId);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
async resumeExecution({ cmdId }) {
|
|
823
|
+
await this.stepRunner.resumeExecution(cmdId);
|
|
824
|
+
}
|
|
825
|
+
|
|
819
826
|
async dealyedRevertMode() {
|
|
820
827
|
const timerId = setTimeout(async () => {
|
|
821
828
|
await this.revertMode();
|
|
@@ -835,7 +842,6 @@ export class BVTRecorder {
|
|
|
835
842
|
|
|
836
843
|
this.bvtContext.navigate = true;
|
|
837
844
|
this.bvtContext.loadedRoutes = null;
|
|
838
|
-
this.bvtContext.web.abortedExecution = false;
|
|
839
845
|
for (const [key, value] of Object.entries(_env)) {
|
|
840
846
|
process.env[key] = value;
|
|
841
847
|
}
|
|
@@ -871,7 +877,6 @@ export class BVTRecorder {
|
|
|
871
877
|
delete process.env[key];
|
|
872
878
|
}
|
|
873
879
|
this.bvtContext.navigate = false;
|
|
874
|
-
this.bvtContext.web.abortedExecution = false;
|
|
875
880
|
}
|
|
876
881
|
}
|
|
877
882
|
async saveScenario({ scenario, featureName, override, isSingleStep }) {
|
|
@@ -110,6 +110,7 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
|
|
|
110
110
|
socket.emit("BVTRecorder.browserOpened", null, roomId);
|
|
111
111
|
})
|
|
112
112
|
.catch((e) => {
|
|
113
|
+
console.error("BVTRecorder.browserLaunchFailed", e);
|
|
113
114
|
socket.emit("BVTRecorder.browserLaunchFailed", e, roomId);
|
|
114
115
|
});
|
|
115
116
|
const timeOutForFunction = async (promise, timeout = 5000) => {
|
|
@@ -146,7 +147,8 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
|
|
|
146
147
|
// console.log("BVTRecorder.browserOpened");
|
|
147
148
|
socket.emit("BVTRecorder.browserOpened", null, roomId);
|
|
148
149
|
})
|
|
149
|
-
.catch(() => {
|
|
150
|
+
.catch((e) => {
|
|
151
|
+
console.error("BVTRecorder.browserLaunchFailed", e);
|
|
150
152
|
socket.emit("BVTRecorder.browserLaunchFailed", null, roomId);
|
|
151
153
|
});
|
|
152
154
|
},
|
|
@@ -227,6 +229,12 @@ const init = ({ envName, projectDir, roomId, TOKEN }) => {
|
|
|
227
229
|
"recorderWindow.abortExecution": async (input) => {
|
|
228
230
|
return recorder.abortExecution(input);
|
|
229
231
|
},
|
|
232
|
+
"recorderWindow.pauseExecution": async (input) => {
|
|
233
|
+
return recorder.pauseExecution(input);
|
|
234
|
+
},
|
|
235
|
+
"recorderWindow.resumeExecution": async (input) => {
|
|
236
|
+
return recorder.resumeExecution(input);
|
|
237
|
+
},
|
|
230
238
|
"recorderWindow.loadExistingScenario": async (input) => {
|
|
231
239
|
return recorder.loadExistingScenario(input);
|
|
232
240
|
},
|
|
@@ -15,45 +15,62 @@ import fs from "fs";
|
|
|
15
15
|
import { locateDefinitionPath } from "../cucumber/steps_definitions.js";
|
|
16
16
|
import { tmpdir } from "os";
|
|
17
17
|
|
|
18
|
-
// let copiedCodeToTemp = false;
|
|
19
|
-
async function withAbort(fn, signal) {
|
|
20
|
-
if (!signal) {
|
|
21
|
-
return await fn();
|
|
22
|
-
}
|
|
23
|
-
return new Promise((resolve, reject) => {
|
|
24
|
-
const abortHandler = () => reject(new Error("Aborted"));
|
|
25
|
-
signal.addEventListener("abort", abortHandler, { once: true });
|
|
26
|
-
|
|
27
|
-
fn()
|
|
28
|
-
.then(resolve)
|
|
29
|
-
.catch(reject)
|
|
30
|
-
.finally(() => {
|
|
31
|
-
signal.removeEventListener("abort", abortHandler);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
18
|
export class BVTStepRunner {
|
|
36
|
-
#currentStepController;
|
|
19
|
+
#currentStepController = null;
|
|
37
20
|
#port;
|
|
38
|
-
|
|
21
|
+
#lastAttemptedCmdId = null;
|
|
22
|
+
|
|
23
|
+
constructor({ projectDir, sendExecutionStatus, bvtContext }) {
|
|
39
24
|
this.projectDir = projectDir;
|
|
40
25
|
this.sendExecutionStatus = sendExecutionStatus;
|
|
26
|
+
this.bvtContext = bvtContext;
|
|
27
|
+
this.liveExecutionMap = new Map();
|
|
41
28
|
}
|
|
29
|
+
|
|
42
30
|
setRemoteDebugPort(port) {
|
|
43
31
|
this.#port = port;
|
|
44
32
|
}
|
|
33
|
+
|
|
34
|
+
// Abort the current cucumber step execution by signaling the wrapper
|
|
45
35
|
async abortExecution() {
|
|
36
|
+
if (this.bvtContext.web.pausedCmd) {
|
|
37
|
+
this.bvtContext.web.pausedCmd = null;
|
|
38
|
+
}
|
|
39
|
+
this.liveExecutionMap.clear();
|
|
46
40
|
if (this.#currentStepController) {
|
|
47
41
|
this.#currentStepController.abort();
|
|
48
42
|
}
|
|
49
43
|
}
|
|
50
44
|
|
|
45
|
+
async pauseExecution(cmdId) {
|
|
46
|
+
if (this.bvtContext.web) {
|
|
47
|
+
this.bvtContext.web.pausedCmd = this.liveExecutionMap.get(cmdId);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async resumeExecution(cmdId) {
|
|
52
|
+
if (this.bvtContext.web.pausedCmd) {
|
|
53
|
+
const { resolve } = this.bvtContext.web.pausedCmd;
|
|
54
|
+
if (resolve) {
|
|
55
|
+
resolve();
|
|
56
|
+
}
|
|
57
|
+
this.bvtContext.web.pausedCmd = null;
|
|
58
|
+
} else {
|
|
59
|
+
if (this.liveExecutionMap.has(cmdId)) {
|
|
60
|
+
const { resolve } = this.liveExecutionMap.get(cmdId);
|
|
61
|
+
if (resolve) {
|
|
62
|
+
resolve();
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
console.warn(`No paused command found for cmdId: ${cmdId}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
51
70
|
async copyCodetoTempFolder({ step, parametersMap, tempFolderPath }) {
|
|
52
|
-
// const tempFolderPath = path.join(this.projectDir, "__temp_features");
|
|
53
71
|
if (!fs.existsSync(tempFolderPath)) {
|
|
54
72
|
fs.mkdirSync(tempFolderPath);
|
|
55
73
|
}
|
|
56
|
-
//copy all files from "./features" "./temp" folder
|
|
57
74
|
if (fs.existsSync(tempFolderPath)) {
|
|
58
75
|
fs.rmSync(tempFolderPath, { recursive: true });
|
|
59
76
|
}
|
|
@@ -61,12 +78,10 @@ export class BVTStepRunner {
|
|
|
61
78
|
overwrite: true,
|
|
62
79
|
recursive: true,
|
|
63
80
|
});
|
|
64
|
-
// copiedCodeToTemp = true;
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
async writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags }) {
|
|
68
84
|
const tFilePath = path.join(tempFolderPath, "__temp.feature");
|
|
69
|
-
// console.log(tFilePath);
|
|
70
85
|
let tFileContent = `# temp feature file
|
|
71
86
|
Feature: Temp feature
|
|
72
87
|
${tags ? tags.join(" ") : ""}
|
|
@@ -78,211 +93,266 @@ export class BVTStepRunner {
|
|
|
78
93
|
return tFilePath;
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
|
|
96
|
+
// Generate wrapper code that integrates AbortSignal into cucumber step definitions
|
|
97
|
+
generateWrapperCode() {
|
|
98
|
+
return `
|
|
99
|
+
import {setDefinitionFunctionWrapper} from "@dev-blinq/cucumber-js";
|
|
100
|
+
|
|
101
|
+
setDefinitionFunctionWrapper((fn) => {
|
|
102
|
+
return async function (...args) {
|
|
103
|
+
const signal = global.__BVT_STEP_ABORT_SIGNAL;
|
|
104
|
+
if (signal) {
|
|
105
|
+
signal.throwIfAborted?.();
|
|
106
|
+
const abortHandler = () => {
|
|
107
|
+
throw new Error("Aborted");
|
|
108
|
+
};
|
|
109
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
110
|
+
try {
|
|
111
|
+
return await fn.apply(this, args);
|
|
112
|
+
} finally {
|
|
113
|
+
signal.removeEventListener("abort", abortHandler);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return await fn.apply(this, args);
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Write the wrapper code to temp folder
|
|
124
|
+
async writeWrapperCode(tempFolderPath, abortSignal) {
|
|
125
|
+
// tempFolderPath/step_definitions/utils.mjs -> Make a file name that follows this file but always before the next file
|
|
126
|
+
let fileName = "utils" + Math.random().toString(36).substring(2, 7) + ".mjs";
|
|
127
|
+
while (existsSync(path.join(tempFolderPath, "step_definitions", fileName))) {
|
|
128
|
+
fileName = "utils" + Math.random().toString(36).substring(2, 7) + ".mjs";
|
|
129
|
+
}
|
|
130
|
+
const wrapperCode = this.generateWrapperCode();
|
|
131
|
+
|
|
132
|
+
// Ensure directory exists
|
|
133
|
+
const stepDefinitionFolderPath = path.join(tempFolderPath, "step_definitions");
|
|
134
|
+
if (!existsSync(stepDefinitionFolderPath)) {
|
|
135
|
+
mkdirSync(stepDefinitionFolderPath, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
writeFileSync(path.join(stepDefinitionFolderPath, fileName), wrapperCode);
|
|
139
|
+
|
|
140
|
+
// Set the abort signal globally so the wrapper can access it
|
|
141
|
+
global.__BVT_STEP_ABORT_SIGNAL = abortSignal;
|
|
142
|
+
|
|
143
|
+
return path.join(stepDefinitionFolderPath, fileName);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Execute cucumber step - simplified without abort signal handling at this level
|
|
147
|
+
async executeStepWithAbort({ feature_file_path, scenario, tempFolderPath, stepText, config }, options) {
|
|
82
148
|
const { skipAfter = true, skipBefore = true } = options || {};
|
|
83
|
-
const environment = {
|
|
84
|
-
...process.env,
|
|
85
|
-
};
|
|
86
149
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
},
|
|
150
|
+
const environment = { ...process.env };
|
|
151
|
+
const { loadConfiguration, loadSupport, runCucumber } = await import("@dev-blinq/cucumber-js/api");
|
|
152
|
+
|
|
153
|
+
const { runConfiguration } = await loadConfiguration(
|
|
154
|
+
{
|
|
155
|
+
provided: {
|
|
156
|
+
name: [scenario],
|
|
157
|
+
paths: [feature_file_path],
|
|
158
|
+
import: [path.join(tempFolderPath, "step_definitions", "**", "*.mjs")],
|
|
97
159
|
},
|
|
98
|
-
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (testStepResult.status === "FAILED" || testStepResult.status === "AMBIGUOUS") {
|
|
129
|
-
if (!errorMesssage) {
|
|
130
|
-
errorMesssage = testStepResult.message;
|
|
131
|
-
if (info) {
|
|
132
|
-
errInfo = info;
|
|
133
|
-
}
|
|
160
|
+
},
|
|
161
|
+
{ cwd: process.cwd(), env: environment }
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const support = await loadSupport(runConfiguration, { cwd: process.cwd(), env: environment });
|
|
165
|
+
|
|
166
|
+
support.afterTestRunHookDefinitions = [];
|
|
167
|
+
if (skipAfter) {
|
|
168
|
+
support.afterTestCaseHookDefinitions = [];
|
|
169
|
+
}
|
|
170
|
+
if (skipBefore && !config.legacySyntax) {
|
|
171
|
+
support.beforeTestCaseHookDefinitions = support.beforeTestCaseHookDefinitions.filter((hook) => {
|
|
172
|
+
return hook.uri.endsWith("utils.mjs");
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
support.beforeTestRunHookDefinitions = [];
|
|
176
|
+
|
|
177
|
+
let errorMessage = null;
|
|
178
|
+
let info = null;
|
|
179
|
+
let errInfo = null;
|
|
180
|
+
|
|
181
|
+
const result = await runCucumber({ ...runConfiguration, support }, environment, (message) => {
|
|
182
|
+
if (message.testStepFinished) {
|
|
183
|
+
const { testStepFinished } = message;
|
|
184
|
+
const { testStepResult } = testStepFinished;
|
|
185
|
+
if (testStepResult.status === "FAILED" || testStepResult.status === "AMBIGUOUS") {
|
|
186
|
+
if (!errorMessage) {
|
|
187
|
+
errorMessage = testStepResult.message;
|
|
188
|
+
if (info) {
|
|
189
|
+
errInfo = info;
|
|
134
190
|
}
|
|
135
191
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
192
|
+
}
|
|
193
|
+
if (testStepResult.status === "UNDEFINED") {
|
|
194
|
+
if (!errorMessage) {
|
|
195
|
+
errorMessage = `step ${JSON.stringify(stepText)} is ${testStepResult.status}`;
|
|
196
|
+
if (info) {
|
|
197
|
+
errInfo = info;
|
|
142
198
|
}
|
|
143
199
|
}
|
|
144
200
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
201
|
+
}
|
|
202
|
+
if (message.attachment) {
|
|
203
|
+
const attachment = message.attachment;
|
|
204
|
+
if (attachment.mediaType === "application/json" && attachment.body) {
|
|
205
|
+
const body = JSON.parse(attachment.body);
|
|
206
|
+
info = body.info;
|
|
207
|
+
const result = body.result;
|
|
208
|
+
|
|
209
|
+
if (result.status === "PASSED") {
|
|
210
|
+
this.sendExecutionStatus({
|
|
211
|
+
type: "cmdExecutionSuccess",
|
|
212
|
+
cmdId: body.cmdId,
|
|
213
|
+
selectedStrategy: info?.selectedStrategy,
|
|
214
|
+
});
|
|
215
|
+
} else {
|
|
216
|
+
this.sendExecutionStatus({
|
|
217
|
+
type: "cmdExecutionError",
|
|
218
|
+
cmdId: body.cmdId,
|
|
219
|
+
error: {
|
|
220
|
+
message: result.message,
|
|
221
|
+
info,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
} else if (attachment.mediaType === "application/json+intercept-results" && attachment.body) {
|
|
226
|
+
const body = JSON.parse(attachment.body);
|
|
227
|
+
if (body) {
|
|
228
|
+
this.sendExecutionStatus({
|
|
229
|
+
type: "interceptResults",
|
|
230
|
+
interceptResults: body,
|
|
231
|
+
});
|
|
176
232
|
}
|
|
177
233
|
}
|
|
178
|
-
});
|
|
179
|
-
if (errorMesssage) {
|
|
180
|
-
const bvtError = new Error(errorMesssage);
|
|
181
|
-
Object.assign(bvtError, { info: errInfo });
|
|
182
|
-
throw bvtError;
|
|
183
234
|
}
|
|
235
|
+
});
|
|
184
236
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.error("Error running cucumber-js", error);
|
|
191
|
-
throw error;
|
|
237
|
+
if (errorMessage) {
|
|
238
|
+
const bvtError = new Error(errorMessage);
|
|
239
|
+
Object.assign(bvtError, { info: errInfo });
|
|
240
|
+
throw bvtError;
|
|
192
241
|
}
|
|
193
|
-
|
|
242
|
+
|
|
243
|
+
return { result, info };
|
|
244
|
+
}
|
|
194
245
|
|
|
195
246
|
async runStep({ step, parametersMap, envPath, tags, config }, bvtContext, options) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (cmdIDs.length === 0) {
|
|
200
|
-
cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
|
|
201
|
-
}
|
|
202
|
-
const cId = cmdIDs.shift();
|
|
203
|
-
this.sendExecutionStatus({
|
|
204
|
-
type: "cmdExecutionStart",
|
|
205
|
-
cmdId: cId,
|
|
206
|
-
});
|
|
207
|
-
return cId;
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
let codePage; // = getCodePage();
|
|
211
|
-
// const tempFolderPath = process.env.tempFeaturesFolderPath;
|
|
212
|
-
const __temp_features_FolderName = "__temp_features" + Math.random().toString(36).substring(2, 7);
|
|
213
|
-
const tempFolderPath = path.join(this.projectDir, __temp_features_FolderName);
|
|
214
|
-
process.env.tempFeaturesFolderPath = __temp_features_FolderName;
|
|
215
|
-
process.env.TESTCASE_REPORT_FOLDER_PATH = tempFolderPath;
|
|
216
|
-
// if (!copiedCodeToTemp) {
|
|
217
|
-
// await this.copyCodetoTempFolder({ step, parametersMap, tempFolderPath });
|
|
218
|
-
// }
|
|
219
|
-
await this.copyCodetoTempFolder({ step, parametersMap, tempFolderPath });
|
|
220
|
-
// console.log({ feature_file_path });
|
|
221
|
-
let stepsDefinitions = loadStepDefinitions(this.projectDir, false, true);
|
|
222
|
-
// console.log({ stepsDefinitions });
|
|
223
|
-
const cucumberStep = getCucumberStep({ step });
|
|
224
|
-
if (cucumberStep.parameters && Array.isArray(cucumberStep.parameters)) {
|
|
225
|
-
cucumberStep.parameters.forEach((param) => {
|
|
226
|
-
if (param.variableName) {
|
|
227
|
-
param.callValue = parametersMap[param.variableName];
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
}
|
|
247
|
+
// Create a new AbortController for this specific step execution
|
|
248
|
+
this.#currentStepController = new AbortController();
|
|
249
|
+
const { signal } = this.#currentStepController;
|
|
231
250
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
251
|
+
try {
|
|
252
|
+
this.#lastAttemptedCmdId = null;
|
|
253
|
+
let cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
|
|
254
|
+
bvtContext.web.pausedCmd = null;
|
|
255
|
+
|
|
256
|
+
// Clear the liveExecutionMap and set up new entries for this step
|
|
257
|
+
this.liveExecutionMap.clear();
|
|
258
|
+
|
|
259
|
+
for (const cmdId of cmdIDs) {
|
|
260
|
+
this.liveExecutionMap.set(cmdId, {
|
|
261
|
+
resolve: () => {},
|
|
262
|
+
reject: () => {},
|
|
263
|
+
});
|
|
237
264
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
265
|
+
|
|
266
|
+
if (bvtContext.web) {
|
|
267
|
+
bvtContext.web.getCmdId = () => {
|
|
268
|
+
if (cmdIDs.length === 0) {
|
|
269
|
+
cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
|
|
270
|
+
}
|
|
271
|
+
const cId = cmdIDs.shift();
|
|
272
|
+
this.sendExecutionStatus({
|
|
273
|
+
type: "cmdExecutionStart",
|
|
274
|
+
cmdId: cId,
|
|
275
|
+
});
|
|
276
|
+
this.#lastAttemptedCmdId = cId;
|
|
277
|
+
return cId;
|
|
278
|
+
};
|
|
245
279
|
}
|
|
246
|
-
|
|
247
|
-
|
|
280
|
+
|
|
281
|
+
const __temp_features_FolderName = "__temp_features" + Math.random().toString(36).substring(2, 7);
|
|
282
|
+
const tempFolderPath = path.join(this.projectDir, __temp_features_FolderName);
|
|
283
|
+
process.env.tempFeaturesFolderPath = __temp_features_FolderName;
|
|
284
|
+
process.env.TESTCASE_REPORT_FOLDER_PATH = tempFolderPath;
|
|
285
|
+
|
|
286
|
+
await this.copyCodetoTempFolder({ step, parametersMap, tempFolderPath });
|
|
287
|
+
|
|
288
|
+
// Write abort wrapper code with this step's signal
|
|
289
|
+
await this.writeWrapperCode(tempFolderPath, signal);
|
|
290
|
+
|
|
291
|
+
let stepsDefinitions = loadStepDefinitions(this.projectDir, false, true);
|
|
292
|
+
const cucumberStep = getCucumberStep({ step });
|
|
293
|
+
|
|
294
|
+
if (cucumberStep.parameters && Array.isArray(cucumberStep.parameters)) {
|
|
295
|
+
cucumberStep.parameters.forEach((param) => {
|
|
296
|
+
if (param.variableName) {
|
|
297
|
+
param.callValue = parametersMap[param.variableName];
|
|
298
|
+
}
|
|
299
|
+
});
|
|
248
300
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (existsSync(
|
|
254
|
-
|
|
255
|
-
|
|
301
|
+
|
|
302
|
+
if (!step.isImplemented && step.commands.length > 0) {
|
|
303
|
+
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
304
|
+
const stepDefinitionFolderPath = path.join(tempFolderPath, "step_definitions");
|
|
305
|
+
if (!existsSync(stepDefinitionFolderPath)) {
|
|
306
|
+
mkdirSync(stepDefinitionFolderPath, { recursive: true });
|
|
307
|
+
}
|
|
308
|
+
const stepDefsFilePath = locateDefinitionPath(tempFolderPath, pageName);
|
|
309
|
+
let codePage = getCodePage(stepDefsFilePath);
|
|
310
|
+
codePage = await saveRecording({
|
|
311
|
+
step,
|
|
312
|
+
cucumberStep,
|
|
313
|
+
codePage,
|
|
314
|
+
projectDir: this.projectDir,
|
|
315
|
+
stepsDefinitions,
|
|
316
|
+
});
|
|
317
|
+
if (codePage) {
|
|
318
|
+
await codePage.save(stepDefsFilePath);
|
|
319
|
+
}
|
|
320
|
+
if (!codePage) {
|
|
321
|
+
codePage = getUtilsCodePage(this.projectDir);
|
|
256
322
|
}
|
|
257
|
-
mkdirSync(routesPath, { recursive: true });
|
|
258
|
-
console.log("Created temp_routes_folder:", routesPath);
|
|
259
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
260
323
|
} else {
|
|
261
|
-
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
try {
|
|
324
|
+
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
325
|
+
if (process.env.TEMP_RUN === "true") {
|
|
326
|
+
if (existsSync(routesPath)) {
|
|
265
327
|
rmSync(routesPath, { recursive: true });
|
|
266
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
267
|
-
} catch (error) {
|
|
268
|
-
console.error("Error removing temp_routes folder", error);
|
|
269
328
|
}
|
|
270
|
-
}
|
|
271
|
-
routesPath = path.join(this.projectDir, "data", "routes");
|
|
272
|
-
console.log("Saving routes to:", routesPath);
|
|
273
|
-
if (!existsSync(routesPath)) {
|
|
274
329
|
mkdirSync(routesPath, { recursive: true });
|
|
330
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
331
|
+
} else {
|
|
332
|
+
if (existsSync(routesPath)) {
|
|
333
|
+
try {
|
|
334
|
+
rmSync(routesPath, { recursive: true });
|
|
335
|
+
} catch (error) {
|
|
336
|
+
console.error("Error removing temp_routes folder", error);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
routesPath = path.join(this.projectDir, "data", "routes");
|
|
340
|
+
if (!existsSync(routesPath)) {
|
|
341
|
+
mkdirSync(routesPath, { recursive: true });
|
|
342
|
+
}
|
|
343
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
275
344
|
}
|
|
276
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
277
345
|
}
|
|
278
|
-
}
|
|
279
|
-
const feature_file_path = await this.writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags });
|
|
280
|
-
// console.log({ feature_file_path, step_text: step.text });
|
|
281
346
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
347
|
+
const feature_file_path = await this.writeTempFeatureFile({
|
|
348
|
+
step,
|
|
349
|
+
parametersMap,
|
|
350
|
+
tempFolderPath,
|
|
351
|
+
tags,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Execute the cucumber step - if wrapper throws "Aborted", it will propagate up
|
|
355
|
+
const { result, info } = await this.executeStepWithAbort(
|
|
286
356
|
{
|
|
287
357
|
feature_file_path,
|
|
288
358
|
tempFolderPath,
|
|
@@ -292,12 +362,25 @@ export class BVTStepRunner {
|
|
|
292
362
|
},
|
|
293
363
|
options
|
|
294
364
|
);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
365
|
+
|
|
366
|
+
return { result, info };
|
|
367
|
+
} catch (error) {
|
|
368
|
+
if (error.message && error.message.includes("Aborted")) {
|
|
369
|
+
throw new Error("Aborted");
|
|
370
|
+
} else throw error;
|
|
371
|
+
} finally {
|
|
372
|
+
// Clean up this step's controller and global reference
|
|
373
|
+
this.#currentStepController = null;
|
|
374
|
+
global.__BVT_STEP_ABORT_SIGNAL = null;
|
|
375
|
+
|
|
376
|
+
// Clean up temp folder
|
|
377
|
+
const __temp_features_FolderName = process.env.tempFeaturesFolderPath;
|
|
378
|
+
if (__temp_features_FolderName) {
|
|
379
|
+
const tempFolderPath = path.join(this.projectDir, __temp_features_FolderName);
|
|
380
|
+
if (fs.existsSync(tempFolderPath)) {
|
|
381
|
+
fs.rmSync(tempFolderPath, { recursive: true });
|
|
382
|
+
}
|
|
299
383
|
}
|
|
300
|
-
}
|
|
301
|
-
return { result, info };
|
|
384
|
+
}
|
|
302
385
|
}
|
|
303
386
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev-blinq/cucumber_client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1405-dev",
|
|
4
4
|
"description": " ",
|
|
5
5
|
"main": "bin/index.js",
|
|
6
6
|
"types": "bin/index.d.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@babel/traverse": "^7.27.1",
|
|
33
33
|
"@babel/types": "^7.27.1",
|
|
34
34
|
"@cucumber/tag-expressions": "^6.1.1",
|
|
35
|
-
"@dev-blinq/cucumber-js": "1.0.
|
|
35
|
+
"@dev-blinq/cucumber-js": "1.0.183-dev",
|
|
36
36
|
"@faker-js/faker": "^8.1.0",
|
|
37
37
|
"automation_model": "1.0.817-dev",
|
|
38
38
|
"axios": "^1.7.4",
|