@dev-blinq/cucumber_client 1.0.1392-dev → 1.0.1392-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.
Files changed (50) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +105 -105
  2. package/bin/assets/preload/css_gen.js +10 -10
  3. package/bin/assets/preload/toolbar.js +27 -29
  4. package/bin/assets/preload/unique_locators.js +1 -1
  5. package/bin/assets/preload/yaml.js +288 -275
  6. package/bin/assets/scripts/aria_snapshot.js +223 -220
  7. package/bin/assets/scripts/dom_attr.js +329 -329
  8. package/bin/assets/scripts/dom_parent.js +169 -174
  9. package/bin/assets/scripts/event_utils.js +94 -94
  10. package/bin/assets/scripts/pw.js +2050 -1949
  11. package/bin/assets/scripts/recorder.js +70 -45
  12. package/bin/assets/scripts/snapshot_capturer.js +147 -147
  13. package/bin/assets/scripts/unique_locators.js +163 -44
  14. package/bin/assets/scripts/yaml.js +796 -783
  15. package/bin/assets/templates/_hooks_template.txt +6 -2
  16. package/bin/assets/templates/utils_template.txt +2 -2
  17. package/bin/client/code_cleanup/find_step_definition_references.js +0 -2
  18. package/bin/client/code_cleanup/utils.js +5 -1
  19. package/bin/client/code_gen/api_codegen.js +2 -2
  20. package/bin/client/code_gen/code_inversion.js +63 -2
  21. package/bin/client/code_gen/function_signature.js +4 -0
  22. package/bin/client/code_gen/page_reflection.js +846 -906
  23. package/bin/client/code_gen/playwright_codeget.js +27 -3
  24. package/bin/client/cucumber/feature.js +4 -0
  25. package/bin/client/cucumber/feature_data.js +2 -2
  26. package/bin/client/cucumber/project_to_document.js +8 -2
  27. package/bin/client/cucumber/steps_definitions.js +6 -3
  28. package/bin/client/cucumber_selector.js +17 -1
  29. package/bin/client/local_agent.js +3 -2
  30. package/bin/client/parse_feature_file.js +23 -26
  31. package/bin/client/playground/projects/env.json +2 -2
  32. package/bin/client/project.js +186 -202
  33. package/bin/client/recorderv3/bvt_init.js +349 -0
  34. package/bin/client/recorderv3/bvt_recorder.js +1068 -104
  35. package/bin/client/recorderv3/implemented_steps.js +2 -0
  36. package/bin/client/recorderv3/index.js +4 -303
  37. package/bin/client/recorderv3/scriptTest.js +1 -1
  38. package/bin/client/recorderv3/services.js +814 -154
  39. package/bin/client/recorderv3/step_runner.js +315 -206
  40. package/bin/client/recorderv3/step_utils.js +473 -25
  41. package/bin/client/recorderv3/update_feature.js +9 -5
  42. package/bin/client/recorderv3/wbr_entry.js +61 -0
  43. package/bin/client/recording.js +1 -0
  44. package/bin/client/upload-service.js +3 -2
  45. package/bin/client/utils/socket_logger.js +132 -0
  46. package/bin/index.js +4 -1
  47. package/bin/logger.js +3 -2
  48. package/bin/min/consoleApi.min.cjs +2 -3
  49. package/bin/min/injectedScript.min.cjs +16 -16
  50. package/package.json +19 -9
@@ -14,6 +14,428 @@ import { createHash } from "crypto";
14
14
 
15
15
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
16
16
 
17
+ const convertToIdentifier = (text) => {
18
+ // replace all invalid characters with _
19
+ return text.replace(/[^a-zA-Z0-9_]/g, "_");
20
+ };
21
+
22
+ export const isVariable = (text) => {
23
+ if (typeof text !== "string") return false;
24
+ const isParametric = text.startsWith("<") && text.endsWith(">");
25
+ if (!isParametric) return false;
26
+ const l = text.length;
27
+ if (l < 2) return false;
28
+ const leftindex = text.indexOf("<");
29
+ const rightindex = text.indexOf(">");
30
+ return leftindex === 0 && rightindex === l - 1;
31
+ };
32
+
33
+ export const extractQuotes = (text) => {
34
+ const stringRegex = /"([^"]*)"/g;
35
+ const matches = text.match(stringRegex);
36
+ if (!matches) return [];
37
+ const quotes = [];
38
+ for (const match of matches) {
39
+ const value = match.slice(1, -1);
40
+ quotes.push(value);
41
+ }
42
+ return quotes;
43
+ };
44
+
45
+ const replaceLastOccurence = (str, search, replacement) => {
46
+ const lastIndex = str.lastIndexOf(search);
47
+ if (lastIndex === -1) return str;
48
+ return str.substring(0, lastIndex) + replacement + str.substring(lastIndex + search.length);
49
+ };
50
+
51
+ const _toRecordingStep = (cmd) => {
52
+ switch (cmd.type) {
53
+ case "hover_element": {
54
+ return {
55
+ type: "hover_element",
56
+ element: {
57
+ role: cmd.role,
58
+ name: cmd.label,
59
+ },
60
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
61
+ };
62
+ }
63
+ case "click_element": {
64
+ return {
65
+ type: "click_element",
66
+ element: {
67
+ role: cmd.role,
68
+ name: cmd.label,
69
+ },
70
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
71
+ count: cmd.count ?? 1,
72
+ };
73
+ }
74
+ case "context_click": {
75
+ return {
76
+ type: "context_click",
77
+ element: {
78
+ role: cmd.role,
79
+ name: cmd.label,
80
+ },
81
+ label: cmd.label,
82
+ value: cmd.value,
83
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
84
+ text: cmd.text,
85
+ count: cmd.count ?? 1,
86
+ };
87
+ }
88
+ case "parameterized_click": {
89
+ return {
90
+ type: "parameterized_click",
91
+ element: {
92
+ role: cmd.role,
93
+ name: cmd.label,
94
+ },
95
+ label: cmd.label,
96
+ value: cmd.value,
97
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
98
+ count: cmd.count ?? 1,
99
+ };
100
+ }
101
+ case "fill_element": {
102
+ return {
103
+ type: "fill_element",
104
+ element: {
105
+ role: cmd.role,
106
+ name: cmd.label,
107
+ },
108
+ parameters: [cmd.value, cmd.enter ?? false],
109
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
110
+ };
111
+ }
112
+ case "select_combobox": {
113
+ return {
114
+ type: "select_combobox",
115
+ element: {
116
+ role: "combobox",
117
+ name: cmd.label,
118
+ },
119
+ selectMode: "select",
120
+ parameters: [cmd.value],
121
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
122
+ };
123
+ }
124
+ case "verify_page_contains_text": {
125
+ return {
126
+ type: "verify_page_contains_text",
127
+ parameters: [cmd.value, cmd.isRegex],
128
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
129
+ };
130
+ }
131
+ case "verify_element_contains_text": {
132
+ return {
133
+ type: "verify_element_contains_text",
134
+ element: {
135
+ role: cmd.role,
136
+ name: cmd.label,
137
+ },
138
+ parameters: [cmd.value, cmd.climb],
139
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
140
+ };
141
+ }
142
+ case "close_page": {
143
+ return {
144
+ type: "close_page",
145
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
146
+ };
147
+ }
148
+ case "check_element": {
149
+ return {
150
+ type: "check_element",
151
+ element: {
152
+ role: cmd.role,
153
+ name: cmd.label,
154
+ },
155
+ check: cmd.check,
156
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
157
+ };
158
+ }
159
+ case "press_key": {
160
+ return {
161
+ type: "press_key",
162
+ element: {
163
+ role: cmd.role,
164
+ name: cmd.label,
165
+ },
166
+ key: cmd.value,
167
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
168
+ };
169
+ }
170
+ case "load_user": {
171
+ return {
172
+ type: "load_data",
173
+ parameters: ["users", cmd.value],
174
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
175
+ };
176
+ }
177
+ case "load_csv": {
178
+ return {
179
+ type: "load_data",
180
+ parameters: ["csv", `${cmd.label}:${cmd.value}`],
181
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
182
+ };
183
+ }
184
+ case "set_date_time": {
185
+ return {
186
+ type: "set_date_time",
187
+ element: {
188
+ role: cmd.role,
189
+ name: cmd.label,
190
+ },
191
+ parameters: [cmd.value],
192
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
193
+ };
194
+ }
195
+ case "set_input": {
196
+ return {
197
+ type: "set_input",
198
+ element: {
199
+ role: cmd.role,
200
+ name: cmd.label,
201
+ },
202
+ value: cmd.value,
203
+ parameters: [cmd.value],
204
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
205
+ };
206
+ }
207
+ case "extract_attribute": {
208
+ return {
209
+ type: "extract_attribute",
210
+ element: {
211
+ role: cmd.role,
212
+ name: cmd.label,
213
+ },
214
+ parameters: [cmd.selectedField, cmd.variableName],
215
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
216
+ regex: cmd.regex,
217
+ trimSpaces: cmd.trimSpaces,
218
+ };
219
+ }
220
+ case "extract_property": {
221
+ return {
222
+ type: "extract_property",
223
+ element: {
224
+ role: cmd.role,
225
+ name: cmd.label,
226
+ },
227
+ parameters: [cmd.selectedField, cmd.variableName],
228
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
229
+ regex: cmd.regex,
230
+ trimSpaces: cmd.trimSpaces,
231
+ };
232
+ }
233
+ case "verify_element_attribute": {
234
+ return {
235
+ type: "verify_element_attribute",
236
+ element: {
237
+ role: cmd.role,
238
+ name: cmd.label,
239
+ },
240
+ parameters: [cmd.selectedField, cmd.value],
241
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
242
+ };
243
+ }
244
+ case "verify_element_property": {
245
+ return {
246
+ type: "verify_element_property",
247
+ element: {
248
+ role: cmd.role,
249
+ name: cmd.label,
250
+ },
251
+ parameters: [cmd.selectedField, cmd.value],
252
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
253
+ };
254
+ }
255
+ case "conditional_wait": {
256
+ return {
257
+ type: "conditional_wait",
258
+ element: {
259
+ role: cmd.role,
260
+ name: cmd.label,
261
+ },
262
+ parameters: [cmd.timeout, cmd.selectedField, cmd.value],
263
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
264
+ };
265
+ }
266
+ case "navigate": {
267
+ return {
268
+ type: "navigate",
269
+ parameters: [cmd.value],
270
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
271
+ };
272
+ }
273
+ case "browser_go_back": {
274
+ return {
275
+ type: "browser_go_back",
276
+ };
277
+ }
278
+ case "browser_go_forward": {
279
+ return {
280
+ type: "browser_go_forward",
281
+ };
282
+ }
283
+ case "set_input_files": {
284
+ return {
285
+ type: "set_input_files",
286
+ element: {
287
+ role: cmd.role,
288
+ name: cmd.label,
289
+ },
290
+ parameters: [cmd.files],
291
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
292
+ };
293
+ }
294
+ case "verify_page_snapshot": {
295
+ return {
296
+ type: "verify_page_snapshot",
297
+ parameters: [cmd.value],
298
+ selectors: cmd.selectors,
299
+ data: cmd.data,
300
+ };
301
+ }
302
+ default: {
303
+ return {
304
+ type: cmd.type,
305
+ parameters: [cmd.value],
306
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
307
+ };
308
+ }
309
+ }
310
+ };
311
+
312
+ function getBestStrategy(allStrategyLocators) {
313
+ const orderedPriorities = ["custom", "context", "basic", "text_with_index", "ignore_digit", "no_text"];
314
+ for (const strategy of orderedPriorities) {
315
+ if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
316
+ return strategy;
317
+ }
318
+ }
319
+ return null;
320
+ }
321
+
322
+ const _parameterizeLocators = (locators, replacementFromValue, replacementToValue) => {
323
+ for (const loc of locators) {
324
+ if (loc?.css?.includes(replacementFromValue)) {
325
+ loc.css = loc.css.replaceAll(replacementFromValue, replacementToValue);
326
+ }
327
+ if (loc?.text?.includes(replacementFromValue)) {
328
+ loc.text = loc.text.replaceAll(replacementFromValue, replacementToValue);
329
+ }
330
+ if (loc?.climb && typeof loc.climb === "string" && loc.climb?.includes(replacementFromValue)) {
331
+ loc.climb = loc.climb.replaceAll(replacementFromValue, replacementToValue);
332
+ }
333
+ }
334
+ return locators;
335
+ };
336
+ const parameterizeLocators = ({ cmd, locs, isValueVariable, isTargetValueVariable, parametersMap }) => {
337
+ if (isValueVariable) {
338
+ const variable = cmd.value.slice(1, -1);
339
+ // const val = parametersMap[variable];
340
+ if (typeof cmd.text === "string") {
341
+ const replacementFromValue = cmd.text.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
342
+ if (replacementFromValue.length > 0) {
343
+ const replacementToValue = `{${variable}}`;
344
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
345
+ }
346
+ }
347
+ }
348
+ if (isTargetValueVariable) {
349
+ const variable = cmd.targetValue.slice(1, -1);
350
+ // const val = parametersMap[variable];
351
+ if (typeof cmd.targetText === "string") {
352
+ const replacementFromValue = cmd.targetText.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
353
+ if (replacementFromValue.length > 0) {
354
+ const replacementToValue = `{${variable}}`;
355
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
356
+ }
357
+ }
358
+ }
359
+ return locs;
360
+ };
361
+
362
+ //TODO: IMPORTAN
363
+ export const toRecordingStep = (cmd, parametersMap) => {
364
+ if (cmd.type === "api") {
365
+ return {
366
+ type: "api",
367
+ value: cmd.value,
368
+ };
369
+ }
370
+ const step = _toRecordingStep(cmd);
371
+ const cmdID = {
372
+ cmdId: cmd.id,
373
+ };
374
+ Object.assign(step, cmdID);
375
+
376
+ const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
377
+
378
+ if (!locatorsObject) return step;
379
+
380
+ const element_name = cmd?.locators?.element_name ?? `${cmd.label} ${cmd.role ?? "Text"}`;
381
+ locatorsObject.element_name = element_name;
382
+
383
+ const isValueVariable = isVariable(cmd.value);
384
+ const isTargetValueVariable = isVariable(cmd.targetValue);
385
+ const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
386
+ step.locators = locatorsObject;
387
+ step.allStrategyLocators = allStrategyLocators;
388
+ step.isLocatorsAssigned = true;
389
+
390
+ if (!isValueVariable && !isTargetValueVariable) {
391
+ return step;
392
+ }
393
+
394
+ if (isValueVariable) {
395
+ step.dataSource = "parameters";
396
+ step.dataKey = convertToIdentifier(cmd.value.slice(1, -1));
397
+ }
398
+
399
+ if (!allStrategyLocators) {
400
+ let locs = locatorsObject.locators;
401
+ locs = parameterizeLocators({
402
+ cmd,
403
+ locs,
404
+ isValueVariable,
405
+ isTargetValueVariable,
406
+ parametersMap,
407
+ });
408
+ locatorsObject.locators = locs;
409
+ return {
410
+ ...step,
411
+ locators: locatorsObject,
412
+ };
413
+ }
414
+
415
+ for (const key in allStrategyLocators) {
416
+ if (key === "strategy") continue;
417
+ if (key === "no_text" || key === "custom") continue;
418
+ const locators = allStrategyLocators[key];
419
+ if (locators.length === 0) continue;
420
+ parameterizeLocators({
421
+ cmd,
422
+ locs: locators,
423
+ isValueVariable,
424
+ isTargetValueVariable,
425
+ parametersMap,
426
+ });
427
+ }
428
+
429
+ locatorsObject.locators = allStrategyLocators[allStrategyLocators.strategy] ?? locatorsObject.locators;
430
+
431
+ return {
432
+ ...step,
433
+ locators: locatorsObject,
434
+ allStrategyLocators,
435
+ isLocatorsAssigned: true,
436
+ };
437
+ };
438
+
17
439
  export const toMethodName = (str) => {
18
440
  // Remove any non-word characters (excluding underscore) and trim spaces
19
441
  let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
@@ -70,10 +492,13 @@ function makeStepTextUnique(step, stepsDefinitions) {
70
492
  step.text = stepText;
71
493
  }
72
494
 
73
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
495
+ export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
496
+ if (step.commands && Array.isArray(step.commands)) {
497
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
498
+ }
74
499
  let routesPath = path.join(tmpdir(), "blinq_temp_routes");
75
500
 
76
- if (process.env.TEMP_RUN) {
501
+ if (process.env.TEMP_RUN === "true") {
77
502
  if (existsSync(routesPath)) {
78
503
  rmSync(routesPath, { recursive: true });
79
504
  }
@@ -84,9 +509,9 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
84
509
  // remove the folder
85
510
  try {
86
511
  rmSync(routesPath, { recursive: true });
87
- console.log("Removed temp_routes_folder:", routesPath);
512
+ //
88
513
  } catch (error) {
89
- console.error("Error removing temp_routes folder", error);
514
+ //
90
515
  }
91
516
  routesPath = path.join(projectDir, "data", "routes");
92
517
  if (!existsSync(routesPath)) {
@@ -111,28 +536,23 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
111
536
  }
112
537
  }
113
538
 
539
+ routesPath = path.join(tmpdir(), "blinq_temp_routes");
114
540
  if (process.env.TEMP_RUN === "true") {
115
- console.log("Save routes in temp folder for running:", routesPath);
116
541
  if (existsSync(routesPath)) {
117
- console.log("Removing existing temp_routes_folder:", routesPath);
118
542
  rmSync(routesPath, { recursive: true });
119
543
  }
120
544
  mkdirSync(routesPath, { recursive: true });
121
- console.log("Created temp_routes_folder:", routesPath);
122
545
  saveRoutes({ step, folderPath: routesPath });
123
546
  } else {
124
- console.log("Saving routes in project directory:", projectDir);
125
547
  if (existsSync(routesPath)) {
126
548
  // remove the folder
127
549
  try {
128
550
  rmSync(routesPath, { recursive: true });
129
- console.log("Removed temp_routes_folder:", routesPath);
130
551
  } catch (error) {
131
- console.error("Error removing temp_routes folder", error);
552
+ //
132
553
  }
133
554
  }
134
555
  routesPath = path.join(projectDir, "data", "routes");
135
- console.log("Saving routes to:", routesPath);
136
556
  if (!existsSync(routesPath)) {
137
557
  mkdirSync(routesPath, { recursive: true });
138
558
  }
@@ -141,6 +561,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
141
561
 
142
562
  cucumberStep.text = step.text;
143
563
  const recording = new Recording();
564
+
144
565
  const steps = step.commands;
145
566
 
146
567
  recording.loadFromObject({ steps, step: cucumberStep });
@@ -216,12 +637,18 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
216
637
  if (step.commands && step.commands.length > 0 && step.commands[0]) {
217
638
  path = step.commands[0].lastKnownUrlPath;
218
639
  }
640
+ let protect = false;
641
+ if (step.commands && step.commands.length > 0 && step.commands[0].type) {
642
+ if (step.commands[0].type === "verify_element_property" || step.commands[0].type === "conditional_wait") {
643
+ protect = true;
644
+ }
645
+ }
219
646
  const infraResult = codePage.addInfraCommand(
220
647
  methodName,
221
648
  description,
222
649
  cucumberStep.getVariablesList(),
223
650
  generateCodeResult.codeLines,
224
- false,
651
+ protect,
225
652
  "recorder",
226
653
  path
227
654
  );
@@ -243,6 +670,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
243
670
  }
244
671
 
245
672
  codePage.removeUnusedElements();
673
+ codePage.mergeSimilarElements();
246
674
  cucumberStep.methodName = methodName;
247
675
  if (generateCodeResult.locatorsMetadata) {
248
676
  codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
@@ -294,26 +722,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
294
722
  if (error) {
295
723
  throw new Error(error);
296
724
  }
725
+ isUtilStep = codePage.sourceFileName.endsWith("utils.mjs");
297
726
 
298
727
  if (parametersNames.length !== stepParams.length) {
299
728
  // console.log("Parameters mismatch", parametersNames, stepParams);
300
729
  throw new Error("Parameters mismatch");
301
730
  }
302
- for (let i = 0; i < parametersNames.length; i++) {
303
- stepParams[i].argumentName = parametersNames[i];
304
- }
305
731
 
306
- isUtilStep = codePage.sourceFileName.endsWith("utils.mjs");
307
- for (const { code } of codeCommands) {
308
- const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
309
- if (command === undefined || command.type === null) continue;
310
- if (command.element) {
311
- const key = command.element.key;
312
- if (key && locatorsJson[key]) {
313
- command.allStrategyLocators = locatorsJson[key];
732
+ const pattern = step.name;
733
+ if (isUtilStep && pattern === "Verify the file {string} exists") {
734
+ commands.push({
735
+ type: "verify_file_exists",
736
+ parameters: [stepParams[0].text],
737
+ });
738
+ } else {
739
+ for (let i = 0; i < parametersNames.length; i++) {
740
+ stepParams[i].argumentName = parametersNames[i];
741
+ }
742
+
743
+ for (const { code } of codeCommands) {
744
+ const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
745
+ if (command === undefined || command.type === null) continue;
746
+ if (command.element) {
747
+ const key = command.element.key;
748
+ if (key && locatorsJson[key]) {
749
+ command.allStrategyLocators = locatorsJson[key];
750
+ }
314
751
  }
752
+ commands.push(command);
315
753
  }
316
- commands.push(command);
317
754
  }
318
755
  } catch (error) {
319
756
  console.error(error);
@@ -398,6 +835,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
398
835
  console.log("Save routes in temp folder for running:", routesPath);
399
836
  if (existsSync(routesPath)) {
400
837
  console.log("Removing existing temp_routes_folder:", routesPath);
838
+ routesPath = path.join(tmpdir(), `blinq_temp_routes`);
401
839
  rmSync(routesPath, { recursive: true });
402
840
  }
403
841
  mkdirSync(routesPath, { recursive: true });
@@ -421,6 +859,9 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
421
859
  }
422
860
  saveRoutes({ step, folderPath: routesPath });
423
861
  }
862
+ if (step.commands && Array.isArray(step.commands)) {
863
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
864
+ }
424
865
  continue;
425
866
  }
426
867
  const cucumberStep = getCucumberStep({ step });
@@ -428,7 +869,14 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
428
869
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
429
870
  // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
430
871
  let codePage = getCodePage(stepDefsFilePath);
431
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
872
+ codePage = await saveRecording({
873
+ step,
874
+ cucumberStep,
875
+ codePage,
876
+ projectDir,
877
+ stepsDefinitions,
878
+ parametersMap: scenario.parametersMap,
879
+ });
432
880
  if (!codePage) {
433
881
  continue;
434
882
  }
@@ -129,10 +129,14 @@ export function getExamplesContent(parametersMap) {
129
129
  function getTagsContent(scenario, featureFileObject) {
130
130
  let oldTags = featureFileObject?.scenarios?.find((s) => s.name === scenario.name)?.tags ?? [];
131
131
  for (const tag of scenario.tags) {
132
- if (oldTags.includes(tag)) {
133
- oldTags = oldTags.filter((t) => t !== tag);
134
- } else {
135
- oldTags.push(tag);
132
+ if (tag === "global_test_data") {
133
+ if (!oldTags.includes("global_test_data")) {
134
+ oldTags.push("global_test_data");
135
+ }
136
+ }
137
+
138
+ if (tag === "remove_global_test_data") {
139
+ oldTags = oldTags.filter((t) => t !== "global_test_data");
136
140
  }
137
141
  }
138
142
 
@@ -231,7 +235,7 @@ const GherkinToObject = (gherkin) => {
231
235
  skipEmptyLines();
232
236
 
233
237
  if (idx >= lines.length) {
234
- return -1;
238
+ return scenario;
235
239
  }
236
240
 
237
241
  while (
@@ -0,0 +1,61 @@
1
+ import { io } from "socket.io-client";
2
+ import { BVTRecorderInit } from "./bvt_init.js";
3
+ import { loadArgs, showUsage, validateCLIArg } from "../cli_helpers.js";
4
+ const requiredEnvVars = ["PROJECT_ID", "BLINQ_TOKEN", "SESSION_ID", "WORKER_WS_SERVER_URL", "REMOTE_ORIGINS_URL"];
5
+ function validateEnvVariables() {
6
+ const missingVars = requiredEnvVars.filter((varName) => !process.env[varName]);
7
+ if (missingVars.length > 0) {
8
+ throw new Error(`Missing required environment variables: ${missingVars.join(", ")}`);
9
+ }
10
+ else {
11
+ console.log("All required environment variables are set.");
12
+ requiredEnvVars.forEach((varName) => {
13
+ console.log(`${varName}: ${process.env[varName]}`);
14
+ });
15
+ }
16
+ }
17
+ function getEnvironmentConfig() {
18
+ const args = loadArgs();
19
+ const projectDir = args[0] || process.env.PROJECT_ID;
20
+ const envName = args[1]?.split("=")[1] || process.env.ENV_NAME;
21
+ const roomId = args[2] || process.env.SESSION_ID;
22
+ const shouldTakeScreenshot = args[3] || process.env.SHOULD_TAKE_SCREENSHOT;
23
+ const TOKEN = process.env.BLINQ_TOKEN;
24
+ try {
25
+ validateCLIArg(projectDir, "projectDir");
26
+ validateCLIArg(envName, "envName");
27
+ validateCLIArg(roomId, "roomId");
28
+ validateCLIArg(shouldTakeScreenshot, "shouldTakeScreenshot");
29
+ if (!TOKEN) {
30
+ throw new Error("BLINQ_TOKEN env variable not set");
31
+ }
32
+ }
33
+ catch (error) {
34
+ const usage = `Usage: node bvt_recorder.js <projectDir> <envName> <roomId>`;
35
+ if (error instanceof Error) {
36
+ showUsage(error, usage);
37
+ }
38
+ else {
39
+ const unknownError = new Error("An unknown error occurred");
40
+ showUsage(unknownError, usage);
41
+ }
42
+ }
43
+ return { envName, projectDir, roomId, TOKEN };
44
+ }
45
+ function initWebBVTRecorder() {
46
+ const socket = io(process.env.WORKER_WS_SERVER_URL, {
47
+ path: "/ws",
48
+ transports: ["websocket", "polling"],
49
+ reconnection: true,
50
+ });
51
+ validateEnvVariables();
52
+ const { envName, projectDir, roomId, TOKEN } = getEnvironmentConfig();
53
+ BVTRecorderInit({
54
+ envName: envName,
55
+ projectDir,
56
+ roomId,
57
+ TOKEN,
58
+ socket: socket,
59
+ });
60
+ }
61
+ initWebBVTRecorder();
@@ -53,6 +53,7 @@ const Types = {
53
53
  SET_INPUT_FILES: "set_input_files",
54
54
  VERIFY_PAGE_SNAPSHOT: "verify_page_snapshot",
55
55
  CONDITIONAL_WAIT: "conditional_wait",
56
+ SLEEP: "sleep",
56
57
  };
57
58
  class Recording {
58
59
  steps = [];