@dev-blinq/cucumber_client 1.0.1219-stage → 1.0.1220-stage

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,39 +184,11 @@ 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.pageMetaDataSet = new Set();
217
189
  this.lastKnownUrlPath = "";
218
190
  // TODO: what is world?
219
- this.world = { attach: () => { } };
191
+ this.world = { attach: () => {} };
220
192
  this.shouldTakeScreenshot = true;
221
193
  this.watcher = null;
222
194
  }
@@ -323,8 +295,8 @@ export class BVTRecorder {
323
295
  this.#remoteDebuggerPort = await findAvailablePort();
324
296
  process.env.CDP_LISTEN_PORT = this.#remoteDebuggerPort;
325
297
 
326
- this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
327
- this.world = { attach: () => { } };
298
+ // this.stepRunner.setRemoteDebugPort(this.#remoteDebuggerPort);
299
+ this.world = { attach: () => {} };
328
300
 
329
301
  const ai_config_file = path.join(this.projectDir, "ai_config.json");
330
302
  let ai_config = {};
@@ -349,12 +321,40 @@ export class BVTRecorder {
349
321
  let stopTime = Date.now();
350
322
  this.logger.info(`Browser launched in ${(stopTime - startTime) / 1000} s`);
351
323
  this.bvtContext = bvtContext;
324
+ this.stepRunner = new BVTStepRunner({
325
+ projectDir: this.projectDir,
326
+ sendExecutionStatus: (data) => {
327
+ if (data && data.type) {
328
+ switch (data.type) {
329
+ case "cmdExecutionStart":
330
+ console.log("Sending cmdExecutionStart event for cmdId:", data);
331
+ this.sendEvent(this.events.cmdExecutionStart, data);
332
+ break;
333
+ case "cmdExecutionSuccess":
334
+ console.log("Sending cmdExecutionSuccess event for cmdId:", data);
335
+ this.sendEvent(this.events.cmdExecutionSuccess, data);
336
+ break;
337
+ case "cmdExecutionError":
338
+ console.log("Sending cmdExecutionError event for cmdId:", data);
339
+ this.sendEvent(this.events.cmdExecutionError, data);
340
+ break;
341
+ case "interceptResults":
342
+ console.log("Sending interceptResults event");
343
+ this.sendEvent(this.events.interceptResults, data);
344
+ break;
345
+ default:
346
+ console.warn("Unknown command execution status type:", data.type);
347
+ break;
348
+ }
349
+ }
350
+ },
351
+ bvtContext: this.bvtContext,
352
+ });
352
353
  const context = bvtContext.playContext;
353
354
  this.context = context;
354
355
  this.web = bvtContext.stable || bvtContext.web;
355
356
  this.web.tryAllStrategies = true;
356
357
  this.page = bvtContext.page;
357
-
358
358
  this.pageSet.add(this.page);
359
359
  this.lastKnownUrlPath = this._updateUrlPath();
360
360
  const browser = await this.context.browser();
@@ -732,7 +732,7 @@ export class BVTRecorder {
732
732
  }
733
733
  async closeBrowser() {
734
734
  delete process.env.TEMP_RUN;
735
- await this.watcher.close().then(() => { });
735
+ await this.watcher.close().then(() => {});
736
736
  this.watcher = null;
737
737
  this.previousIndex = null;
738
738
  this.previousHistoryLength = null;
@@ -790,7 +790,6 @@ export class BVTRecorder {
790
790
  }
791
791
 
792
792
  async startRecordingInput() {
793
- console.log("startRecordingInput");
794
793
  await this.setMode("recordingInput");
795
794
  }
796
795
  async stopRecordingInput() {
@@ -814,9 +813,17 @@ export class BVTRecorder {
814
813
  }
815
814
 
816
815
  async abortExecution() {
817
- this.bvtContext.web.abortedExecution = true;
818
816
  await this.stepRunner.abortExecution();
819
817
  }
818
+
819
+ async pauseExecution({ cmdId }) {
820
+ await this.stepRunner.pauseExecution(cmdId);
821
+ }
822
+
823
+ async resumeExecution({ cmdId }) {
824
+ await this.stepRunner.resumeExecution(cmdId);
825
+ }
826
+
820
827
  async dealyedRevertMode() {
821
828
  const timerId = setTimeout(async () => {
822
829
  await this.revertMode();
@@ -836,7 +843,6 @@ export class BVTRecorder {
836
843
 
837
844
  this.bvtContext.navigate = true;
838
845
  this.bvtContext.loadedRoutes = null;
839
- this.bvtContext.web.abortedExecution = false;
840
846
  for (const [key, value] of Object.entries(_env)) {
841
847
  process.env[key] = value;
842
848
  }
@@ -872,7 +878,6 @@ export class BVTRecorder {
872
878
  delete process.env[key];
873
879
  }
874
880
  this.bvtContext.navigate = false;
875
- this.bvtContext.web.abortedExecution = false;
876
881
  }
877
882
  }
878
883
  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
- constructor({ projectDir, sendExecutionStatus }) {
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
- executeStepRemote = async ({ feature_file_path, scenario, tempFolderPath, stepText, config }, options) => {
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
- try {
88
- const { loadConfiguration, loadSupport, runCucumber } = await import("@dev-blinq/cucumber-js/api");
89
- const { runConfiguration } = await loadConfiguration(
90
- {
91
- provided: {
92
- name: [scenario],
93
- paths: [feature_file_path],
94
- import: [path.join(tempFolderPath, "step_definitions", "**", "*.mjs")],
95
- // format: ["bvt"],
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
- { cwd: process.cwd(), env: environment }
99
- );
100
- // const files = glob.sync(path.join(tempFolderPath, "step_definitions", "**", "*.mjs"));
101
- // console.log("Files found:", files);
102
- const support = await loadSupport(runConfiguration, { cwd: process.cwd(), env: environment });
103
- // console.log("found ", support.stepDefinitions.length, "step definitions");
104
- // support.stepDefinitions.map((step) => {
105
- // console.log("step", step.pattern);
106
- // });
107
-
108
- support.afterTestRunHookDefinitions = [];
109
- if (skipAfter) {
110
- // ignore afterAll/after hooks
111
- support.afterTestCaseHookDefinitions = [];
112
- }
113
- if (skipBefore && !config.legacySyntax) {
114
- // ignore beforeAll/before hooks
115
- support.beforeTestCaseHookDefinitions = support.beforeTestCaseHookDefinitions.filter((hook) => {
116
- return hook.uri.endsWith("utils.mjs");
117
- });
118
- }
119
- support.beforeTestRunHookDefinitions = [];
120
-
121
- let errorMesssage = null;
122
- let info = null;
123
- let errInfo = null;
124
- const result = await runCucumber({ ...runConfiguration, support }, environment, (message) => {
125
- if (message.testStepFinished) {
126
- const { testStepFinished } = message;
127
- const { testStepResult } = testStepFinished;
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
- if (testStepResult.status === "UNDEFINED") {
137
- if (!errorMesssage) {
138
- errorMesssage = `step ${JSON.stringify(stepText)} is ${testStepResult.status}`;
139
- if (info) {
140
- errInfo = info;
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
- if (message.attachment) {
146
- const attachment = message.attachment;
147
- if (attachment.mediaType === "application/json" && attachment.body) {
148
- const body = JSON.parse(attachment.body);
149
- info = body.info;
150
- const result = body.result;
151
-
152
- if (result.status === "PASSED") {
153
- this.sendExecutionStatus({
154
- type: "cmdExecutionSuccess",
155
- cmdId: body.cmdId,
156
- selectedStrategy: info?.selectedStrategy,
157
- });
158
- } else {
159
- this.sendExecutionStatus({
160
- type: "cmdExecutionError",
161
- cmdId: body.cmdId,
162
- error: {
163
- message: result.message,
164
- info,
165
- },
166
- });
167
- }
168
- } else if (attachment.mediaType === "application/json+intercept-results" && attachment.body) {
169
- const body = JSON.parse(attachment.body);
170
- if (body) {
171
- this.sendExecutionStatus({
172
- type: "interceptResults",
173
- interceptResults: body,
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
- return {
186
- result,
187
- info,
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
- let cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
197
- if (bvtContext.web) {
198
- bvtContext.web.getCmdId = () => {
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
- if (!step.isImplemented && step.commands.length > 0) {
233
- const pageName = generatePageName(step.startFrame?.url ?? "default");
234
- const stepDefinitionFolderPath = path.join(tempFolderPath, "step_definitions");
235
- if (!existsSync(stepDefinitionFolderPath)) {
236
- mkdirSync(stepDefinitionFolderPath, { recursive: true });
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
- const stepDefsFilePath = locateDefinitionPath(tempFolderPath, pageName);
239
- //path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
240
- codePage = getCodePage(stepDefsFilePath);
241
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir: this.projectDir, stepsDefinitions });
242
- if (codePage) {
243
- await codePage.save(stepDefsFilePath);
244
- // console.log("saved code page: ", stepDefsFilePath);
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
- if (!codePage) {
247
- codePage = getUtilsCodePage(this.projectDir);
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
- } else {
250
- let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
251
- if (process.env.TEMP_RUN === "true") {
252
- // console.log("Save routes in temp folder for running:", routesPath);
253
- if (existsSync(routesPath)) {
254
- console.log("Removing existing temp_routes_folder:", routesPath);
255
- rmSync(routesPath, { recursive: true });
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
- console.log("Saving routes in project directory:", this.projectDir);
262
- if (existsSync(routesPath)) {
263
- // remove the folder
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
- const stepExecController = new AbortController();
283
- this.#currentStepController = stepExecController;
284
- const { result, info } = await withAbort(async () => {
285
- return await this.executeStepRemote(
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
- }, stepExecController.signal).finally(() => {
296
- // rm temp folder
297
- if (fs.existsSync(tempFolderPath)) {
298
- fs.rmSync(tempFolderPath, { recursive: true });
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.1219-stage",
3
+ "version": "1.0.1220-stage",
4
4
  "description": " ",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",