@dev-blinq/cucumber_client 1.0.1224-dev → 1.0.1224-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 (40) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +220 -0
  2. package/bin/assets/preload/recorderv3.js +68 -5
  3. package/bin/assets/preload/unique_locators.js +1 -1
  4. package/bin/assets/scripts/aria_snapshot.js +235 -0
  5. package/bin/assets/scripts/dom_attr.js +372 -0
  6. package/bin/assets/scripts/dom_element.js +0 -0
  7. package/bin/assets/scripts/dom_parent.js +185 -0
  8. package/bin/assets/scripts/event_utils.js +105 -0
  9. package/bin/assets/scripts/pw.js +7886 -0
  10. package/bin/assets/scripts/recorder.js +1147 -0
  11. package/bin/assets/scripts/snapshot_capturer.js +155 -0
  12. package/bin/assets/scripts/unique_locators.js +841 -0
  13. package/bin/assets/scripts/yaml.js +4770 -0
  14. package/bin/assets/templates/_hooks_template.txt +37 -0
  15. package/bin/assets/templates/page_template.txt +2 -16
  16. package/bin/assets/templates/utils_template.txt +48 -63
  17. package/bin/client/apiTest/apiTest.js +6 -0
  18. package/bin/client/cli_helpers.js +11 -13
  19. package/bin/client/code_cleanup/utils.js +42 -14
  20. package/bin/client/code_gen/code_inversion.js +68 -10
  21. package/bin/client/code_gen/index.js +3 -0
  22. package/bin/client/code_gen/page_reflection.js +37 -20
  23. package/bin/client/code_gen/playwright_codeget.js +170 -33
  24. package/bin/client/cucumber/feature.js +85 -27
  25. package/bin/client/cucumber/steps_definitions.js +84 -76
  26. package/bin/client/local_agent.js +7 -3
  27. package/bin/client/project.js +7 -1
  28. package/bin/client/recorderv3/bvt_recorder.js +267 -80
  29. package/bin/client/recorderv3/implemented_steps.js +74 -12
  30. package/bin/client/recorderv3/index.js +58 -8
  31. package/bin/client/recorderv3/network.js +299 -0
  32. package/bin/client/recorderv3/step_runner.js +319 -67
  33. package/bin/client/recorderv3/step_utils.js +157 -6
  34. package/bin/client/recorderv3/update_feature.js +58 -30
  35. package/bin/client/recording.js +7 -0
  36. package/bin/client/run_cucumber.js +15 -2
  37. package/bin/client/scenario_report.js +4 -8
  38. package/bin/client/test_scenario.js +0 -1
  39. package/bin/index.js +1 -0
  40. package/package.json +15 -8
@@ -12,6 +12,13 @@ const findElementIdentifier = (node, step, userData, elements) => {
12
12
 
13
13
  let elementIdentifier = null;
14
14
  const keys = Object.keys(elements);
15
+
16
+ for (let i = 0; i < keys.length; i++) {
17
+ const element_key = keys[i];
18
+ const element = elements[element_key];
19
+ element["element_key"] = element_key;
20
+ }
21
+
15
22
  let name = node.name;
16
23
  if (!name && step.dataKey) {
17
24
  name = step.dataKey;
@@ -47,21 +54,30 @@ const _isCodeGenerationStep = (step) => {
47
54
  step.type === Types.SELECT ||
48
55
  step.type === Types.HOVER ||
49
56
  step.type === Types.EXTRACT_ATTRIBUTE ||
57
+ step.type === Types.EXTRACT_PROPERTY ||
50
58
  step.type === Types.SET_DATE_TIME ||
51
59
  step.type === Types.CHECK ||
52
60
  step.type === Types.PRESS ||
53
61
  step.type === Types.SET_INPUT ||
54
62
  step.type === Types.FILL_UNKNOWN ||
55
- step.type === "context_click" ||
56
- step.type === "parameterized_click" ||
63
+ step.type === Types.CONTEXT_CLICK ||
64
+ step.type === Types.PARAMETERIZED_CLICK ||
57
65
  step.type === Types.VERIFY_ATTRIBUTE ||
66
+ step.type === Types.VERIFY_PROPERTY ||
58
67
  step.type === Types.SET_INPUT_FILES ||
59
- step.type === Types.VERIFY_PAGE_SNAPSHOT
68
+ step.type === Types.VERIFY_PAGE_SNAPSHOT ||
69
+ step.type === Types.CONDITIONAL_WAIT
60
70
  ) {
61
71
  return true;
62
72
  }
63
73
  return false;
64
74
  };
75
+ // Note: this function is used to exclude a key from an object
76
+ // Please move it to utils and use it as a reusable function ...
77
+ function excludeKey(obj, keyToRemove) {
78
+ const { [keyToRemove]: _, ...rest } = obj;
79
+ return rest;
80
+ }
65
81
  const splitToLocatorsGroups = (locators) => {
66
82
  const no_text = locators.locators.filter((locator) => locator.mode === "NO_TEXT");
67
83
  const ignore_digit = locators.locators.filter((locator) => locator.mode === "IGNORE_DIGIT");
@@ -83,7 +99,7 @@ const splitToLocatorsGroups = (locators) => {
83
99
  const _generateCodeFromCommand = (step, elements, userData) => {
84
100
  let elementsChanged = false;
85
101
 
86
- let elementIdentifier = null;
102
+ let elementIdentifier = step.locators?.element_key;
87
103
  let locatorObject = step.locators;
88
104
  // handle element
89
105
  let element_name = null;
@@ -121,17 +137,20 @@ const _generateCodeFromCommand = (step, elements, userData) => {
121
137
  let locatorObjectClone = JSON.parse(JSON.stringify(locatorObject));
122
138
  element_name = locatorObjectClone.element_name;
123
139
  delete locatorObjectClone.element_name;
124
- keys.forEach((key) => {
125
- let elementClone = JSON.parse(JSON.stringify(elements[key]));
126
- delete elementClone.element_name;
127
- if (JSON.stringify(elementClone) === JSON.stringify(locatorObjectClone)) {
128
- elementIdentifier = key;
129
- }
130
- });
140
+ if (!elementIdentifier) {
141
+ keys.forEach((key) => {
142
+ let elementClone = JSON.parse(JSON.stringify(elements[key]));
143
+ delete elementClone.element_name;
144
+ if (JSON.stringify(elementClone) === JSON.stringify(locatorObjectClone)) {
145
+ elementIdentifier = key;
146
+ }
147
+ });
148
+ }
131
149
  if (!elementIdentifier) {
132
150
  elementIdentifier = findElementIdentifier(node, step, userData, elements);
133
151
  }
134
- elements[elementIdentifier] = locatorObject;
152
+ const withoutAllStrategyLocators = excludeKey(locatorObject, "allStrategyLocators");
153
+ elements[elementIdentifier] = { ...withoutAllStrategyLocators, element_key: elementIdentifier };
135
154
  elementsChanged = true;
136
155
  }
137
156
  let optionElement = null;
@@ -157,6 +176,7 @@ const _generateCodeFromCommand = (step, elements, userData) => {
157
176
  let variable = null;
158
177
  let format = null;
159
178
  let options = null;
179
+ let property = null;
160
180
  switch (step.type) {
161
181
  case Types.SET_INPUT:
162
182
  line = `await context.web.setInputValue(elements["${elementIdentifier}"], `;
@@ -181,19 +201,51 @@ const _generateCodeFromCommand = (step, elements, userData) => {
181
201
  line = `await context.web.setCheck(elements["${elementIdentifier}"], ${step.check}, _params, null, this);`;
182
202
  codeLines.push(line);
183
203
  } else {
184
- if (element_name) {
185
- comment = `// Click on ${element_name}`;
186
- codeLines.push(escapeNonPrintables(comment));
204
+ switch (step.count) {
205
+ case 1:
206
+ if (element_name) {
207
+ comment = `// Click on ${element_name}`;
208
+ codeLines.push(escapeNonPrintables(comment));
209
+ }
210
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
211
+ codeLines.push(line);
212
+ break;
213
+ case 2:
214
+ if (element_name) {
215
+ comment = `// Double click on ${element_name}`;
216
+ codeLines.push(escapeNonPrintables(comment));
217
+ }
218
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {clickCount:2}, this);`;
219
+ codeLines.push(line);
220
+ break;
221
+ default:
222
+ if (element_name) {
223
+ comment = `// Click on ${element_name}`;
224
+ codeLines.push(escapeNonPrintables(comment));
225
+ }
226
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
227
+ codeLines.push(line);
228
+ break;
187
229
  }
188
-
189
- line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
190
- codeLines.push(line);
191
230
  }
192
231
  break;
193
- case "parameterized_click":
194
- comment = `// Parameterized click on ${step.value}`;
195
- codeLines.push(escapeNonPrintables(comment));
196
- line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
232
+ case Types.PARAMETERIZED_CLICK:
233
+ switch (step.count) {
234
+ case 1:
235
+ comment = `// Parameterized click on ${step.value}`;
236
+ codeLines.push(escapeNonPrintables(comment));
237
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
238
+ break;
239
+ case 2:
240
+ comment = `// Parameterized double click on ${step.value}`;
241
+ codeLines.push(escapeNonPrintables(comment));
242
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {clickCount:2}, this);`;
243
+ break;
244
+ default:
245
+ comment = `// Parameterized click on ${step.value}`;
246
+ codeLines.push(escapeNonPrintables(comment));
247
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
248
+ }
197
249
  codeLines.push(line);
198
250
  break;
199
251
  case Types.SET_DATE_TIME:
@@ -286,6 +338,30 @@ const _generateCodeFromCommand = (step, elements, userData) => {
286
338
  codeLines.push(line);
287
339
  break;
288
340
  }
341
+ case Types.EXTRACT_PROPERTY: {
342
+ property = escapeNonPrintables(step.parameters[0]);
343
+ variable = escapeNonPrintables(step.parameters[1]);
344
+ input = `"${variable}"`;
345
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
346
+ input = "_" + step.dataKey;
347
+ }
348
+ let options = "null";
349
+ if (step.regex !== "") {
350
+ options = `{regex: ${JSON.stringify(step.regex)},trimSpaces: ${step.trimSpaces}}`;
351
+ } else if (step.trimSpaces) {
352
+ options = `{trimSpaces: ${step.trimSpaces}}`;
353
+ } else {
354
+ options = "null";
355
+ }
356
+ line = `await context.web.extractProperty(elements["${elementIdentifier}"], "${property}", ${input}, _params,${options}, this);`;
357
+
358
+ if (element_name) {
359
+ comment = `// Extract property ${property} from ${element_name} to ${variable}`;
360
+ codeLines.push(escapeNonPrintables(comment));
361
+ }
362
+ codeLines.push(line);
363
+ break;
364
+ }
289
365
  case Types.VERIFY_PAGE_SNAPSHOT:
290
366
  comment = step.nestFrmLoc
291
367
  ? `// Verify page snapshot ${step.parameters[0]} in the frame ${step.selectors.iframe_src}`
@@ -507,15 +583,39 @@ const _generateCodeFromCommand = (step, elements, userData) => {
507
583
  line = `await context.web.clickType( elements["${elementIdentifier}"] ,"${step.key}",null, _params, {"press":true}, this);`;
508
584
  codeLines.push(line);
509
585
  break;
510
- case "context_click":
511
- comment = `// Click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
512
- codeLines.push(escapeNonPrintables(comment));
513
- input = `"${escapeNonPrintables(step.value)}"`;
514
- if (step.dataSource === "userData" || step.dataSource === "parameters") {
515
- input = "_" + step.dataKey;
586
+ case Types.CONTEXT_CLICK:
587
+ switch (step.count) {
588
+ case 1:
589
+ comment = `// Click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
590
+ codeLines.push(escapeNonPrintables(comment));
591
+ input = `"${escapeNonPrintables(step.value.replaceAll('"', '\\"'))}"`;
592
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
593
+ input = "_" + step.dataKey;
594
+ }
595
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {"context": ${input}}, this);`;
596
+ codeLines.push(line);
597
+ break;
598
+ case 2:
599
+ comment = `// Double click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
600
+ codeLines.push(escapeNonPrintables(comment));
601
+ input = `"${escapeNonPrintables(step.value.replaceAll('"', '\\"'))}"`;
602
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
603
+ input = "_" + step.dataKey;
604
+ }
605
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {"context": ${input}, clickCount:2}, this);`;
606
+ codeLines.push(line);
607
+ break;
608
+ default:
609
+ comment = `// Click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
610
+ codeLines.push(escapeNonPrintables(comment));
611
+ input = `"${escapeNonPrintables(step.value.replaceAll('"', '\\"'))}"`;
612
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
613
+ input = "_" + step.dataKey;
614
+ }
615
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {"context": ${input}}, this);`;
616
+ codeLines.push(line);
617
+ break;
516
618
  }
517
- line = `await context.web.click(elements["${elementIdentifier}"], _params, {"context": ${input}}, this);`;
518
- codeLines.push(line);
519
619
  break;
520
620
  case "popup_close":
521
621
  break;
@@ -541,6 +641,23 @@ const _generateCodeFromCommand = (step, elements, userData) => {
541
641
  codeLines.push(line);
542
642
  break;
543
643
  }
644
+ case Types.VERIFY_PROPERTY: {
645
+ line = `await context.web.verifyProperty(elements["${elementIdentifier}"], `;
646
+ input = "_param_0";
647
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
648
+ input = "_" + step.dataKey;
649
+ }
650
+ line += `"${step.parameters[0]}", ${input}, _params, null, this);`;
651
+ codeLines.push(line);
652
+ break;
653
+ }
654
+ case Types.CONDITIONAL_WAIT: {
655
+ line = `await context.web.conditionalWait(elements["${elementIdentifier}"], `;
656
+ input = "_param_0";
657
+ line += `"${step.parameters[1]}", ${input}, _params, null, this);`;
658
+ codeLines.push(line);
659
+ break;
660
+ }
544
661
  case Types.SET_INPUT_FILES: {
545
662
  line = `await context.web.setInputFiles(elements["${elementIdentifier}"], `;
546
663
  let files = step.parameters[0];
@@ -566,6 +683,22 @@ const _generateCodeFromCommand = (step, elements, userData) => {
566
683
  return { codeLines, elements: elementsChanged ? elements : null, elementIdentifier, allStrategyLocators };
567
684
  };
568
685
 
686
+ /**
687
+ * Generates a report command based on the given position.
688
+ * @param {"start"|"end"} position
689
+ */
690
+ const generateReportCommand = (position, cmdId) => {
691
+ const codeLines = [];
692
+ if (position === "start") {
693
+ const line = `await context.web.addCommandToReport("${cmdId}", "PASSED", '{"status":"start","cmdId":"${cmdId}"}', {type:"cmdReport"},this);`;
694
+ codeLines.push(line);
695
+ } else if (position === "end") {
696
+ const line = `await context.web.addCommandToReport("${cmdId}", "PASSED", '{"status":"end","cmdId":"${cmdId}"}', {type:"cmdReport"},this);`;
697
+ codeLines.push(line);
698
+ }
699
+ return codeLines;
700
+ };
701
+
569
702
  const generateCode = (recording, codePage, userData, projectDir, methodName) => {
570
703
  const stepsDefinitions = new StepsDefinitions(projectDir);
571
704
  stepsDefinitions.load(false);
@@ -606,12 +739,16 @@ const generateCode = (recording, codePage, userData, projectDir, methodName) =>
606
739
  if (recordingStep.type === Types.COMPLETE) {
607
740
  return;
608
741
  }
609
- // if (!recordingStep.element) {
610
- // logger.info(`Step ${recordingStep.type} doesn't have an element, ignoring`);
611
- // return;
742
+
743
+ // if (process.env.TEMP_RUN === "true") {
744
+ // codeLines.push(...generateReportCommand("start", recordingStep.cmdId));
612
745
  // }
613
746
  const result = _generateCodeFromCommand(recordingStep, elements, userData);
747
+
614
748
  codeLines.push(...result.codeLines);
749
+ // if (process.env.TEMP_RUN === "true") {
750
+ // codeLines.push(...generateReportCommand("end", recordingStep.cmdId));
751
+ // }
615
752
  if (result.elements) {
616
753
  elements = result.elements;
617
754
  }
@@ -1,11 +1,13 @@
1
+ import { IdGenerator } from "@cucumber/messages";
2
+ import { AstBuilder, GherkinClassicTokenMatcher, Parser, compile } from "@cucumber/gherkin";
1
3
  import { loadConfiguration, loadSupport, runCucumber } from "@dev-blinq/cucumber-js/api";
2
4
  import fs from "fs";
3
-
5
+ import os from "os";
6
+ import path from "path";
4
7
  import { parseStepTextParameters, toCucumberExpression, unEscapeNonPrintables } from "./utils.js";
5
- import { AstBuilder, GherkinClassicTokenMatcher, Parser, compile } from "@cucumber/gherkin";
6
- import { IdGenerator } from "@cucumber/messages";
8
+ import stream from "stream";
7
9
  import { testStringForRegex } from "../recorderv3/update_feature.js";
8
- import path from 'path';
10
+ import { generateTestData } from "./feature_data.js";
9
11
  class DataTable {
10
12
  constructor(dataTableDocument) {
11
13
  if (!dataTableDocument) {
@@ -372,6 +374,11 @@ class Examples {
372
374
  return null;
373
375
  }
374
376
  let value = this.tableBody[0].cells[parameterIndex].value;
377
+ if (!value) {
378
+ console.warn(`Parameter ${parameterName} not found in examples table body, or it is empty`);
379
+ return null;
380
+ }
381
+
375
382
  return unEscapeNonPrintables(value); // While editing in recorder, we need to remove backslashes
376
383
  }
377
384
  }
@@ -449,29 +456,7 @@ class Scenario {
449
456
  return this.scenarioText;
450
457
  }
451
458
  }
452
- const scenarioResolution = async (featureFilePath) => {
453
- if (!fs.existsSync(featureFilePath)) {
454
- throw new Error(`Feature file ${featureFilePath} does not exist`);
455
- }
456
- const newId = IdGenerator.uuid();
457
- const builder = new AstBuilder(newId);
458
- const matcher = new GherkinClassicTokenMatcher();
459
-
460
- const parser = new Parser(builder, matcher);
461
-
462
- // normalize the path to start with featuers/ if it's not cut the featureFielPath to only the part after features/
463
- let uri = featureFilePath.startsWith("features/")
464
- ? featureFilePath
465
- : "features/" + featureFilePath.split("features/")[1];
466
-
467
- const featureFileContent = fs.readFileSync(featureFilePath, "utf8");
468
- const gherkinDocument = parser.parse(featureFileContent, newId);
469
459
 
470
- const feature = new Feature(gherkinDocument, featureFileContent);
471
- //const scenario = feature.getScenario(scenarioName);
472
- //return scenario;
473
- return feature;
474
- };
475
460
  const getDocumentAndPickel = (featureName, featureContent, scenarioName) => {
476
461
  const newId = IdGenerator.uuid();
477
462
  const builder = new AstBuilder(newId);
@@ -484,12 +469,85 @@ const getDocumentAndPickel = (featureName, featureContent, scenarioName) => {
484
469
 
485
470
  const gherkinDocument = parser.parse(featureContent, newId);
486
471
  const pickles = compile(gherkinDocument, uri, newId);
487
-
488
472
  // Step 3: Find the specific scenario Pickle
489
473
  const pickle = pickles.find((pickle) => {
490
474
  return pickle.name === scenarioName;
491
475
  });
492
476
  return { gherkinDocument, pickle };
493
477
  };
478
+ const scenarioResolution = async (featureFilePath) => {
479
+ if (!fs.existsSync(featureFilePath)) {
480
+ throw new Error(`Feature file ${featureFilePath} does not exist`);
481
+ }
482
+ const featureFileContent = fs.readFileSync(featureFilePath, "utf8");
483
+ let tmpDir = null;
484
+ try {
485
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "ai-qa"));
486
+ const tmpFeaturePath = path.join(tmpDir, "features");
487
+ fs.mkdirSync(tmpFeaturePath);
488
+ const tmpFeatureFilePath = path.join(tmpFeaturePath, path.basename(featureFilePath));
489
+ let result = generateTestData(featureFilePath);
490
+ if (result.changed) {
491
+ fs.writeFileSync(tmpFeatureFilePath, result.newContent);
492
+ console.log("Fake data was generated for this scenario");
493
+ console.log("Variables:");
494
+ for (let key in result.variables) {
495
+ console.log(`${key}: ${result.variables[key].fake}`);
496
+ }
497
+ console.log("Other fake data:");
498
+ for (let i = 0; i < result.otherFakeData.length; i++) {
499
+ console.log(`${result.otherFakeData[i].var}: ${result.otherFakeData[i].fake}`);
500
+ }
501
+ } else {
502
+ fs.copyFileSync(featureFilePath, tmpFeatureFilePath);
503
+ }
504
+ const writable = new stream.Writable({
505
+ write: function (chunk, encoding, next) {
506
+ //console.log(chunk.toString());
507
+ next();
508
+ },
509
+ });
510
+ const environment = { cwd: tmpDir, stdout: writable, stderr: writable };
511
+ // load configuration from a particular file, and override a specific option
512
+ const provided = {
513
+ default: "--publish-quiet",
514
+ require: [tmpDir],
515
+ failFast: false,
516
+ };
494
517
 
518
+ let gherkinDocument = null;
519
+ const { runConfiguration } = await loadConfiguration({ provided }, environment);
520
+ // load the support code upfront
521
+ const support = await loadSupport(runConfiguration, environment);
522
+ // run cucumber, using the support code we loaded already
523
+ await runCucumber({ ...runConfiguration, support }, environment, function (event) {
524
+ // if (event.source) {
525
+ // scenarioInfo.source = event.source.data;
526
+ // }
527
+ // if (event.pickle && event.pickle.name === scenarioName) {
528
+ // scenarioInfo.pickle = event.pickle;
529
+ // }
530
+ if (event.gherkinDocument) {
531
+ gherkinDocument = event.gherkinDocument;
532
+ }
533
+ //console.log(event);
534
+ //console.log(JSON.stringify(event, null, 2));
535
+ // console.log("");
536
+ });
537
+ const feature = new Feature(gherkinDocument, featureFileContent);
538
+ //const scenario = feature.getScenario(scenarioName);
539
+ //return scenario;
540
+ return feature;
541
+ } finally {
542
+ try {
543
+ if (tmpDir) {
544
+ fs.rmSync(tmpDir, { recursive: true });
545
+ }
546
+ } catch (e) {
547
+ console.error(
548
+ `An error has occurred while removing the temp folder at ${tmpDir}. Please remove it manually. Error: ${e}`
549
+ );
550
+ }
551
+ }
552
+ };
495
553
  export { Feature, Scenario, Step, DataTable, Examples, scenarioResolution, getDocumentAndPickel };
@@ -523,86 +523,94 @@ class StepsDefinitions {
523
523
  // }
524
524
  // );
525
525
  };
526
+ //! Deprecated, not removing until stepRunner.executeStepRemote is stable
527
+ // executeStepRemote = async ({ feature_file_path, scenario, tempFolderPath, stepText, onCommandResult }, options) => {
528
+ // // const { skipAfter = true, skipBefore = true } = options || {};
529
+ // const environment = {
530
+ // ...process.env,
531
+ // };
526
532
 
527
- executeStepRemote = async ({ feature_file_path, scenario, tempFolderPath, stepText }, options) => {
528
- if (!options) {
529
- options = {
530
- skipAfter: true,
531
- };
532
- }
533
- const environment = {
534
- ...process.env,
535
- };
533
+ // try {
534
+ // const { loadConfiguration, loadSupport, runCucumber } = await import("@dev-blinq/cucumber-js/api");
535
+ // const { runConfiguration } = await loadConfiguration(
536
+ // {
537
+ // provided: {
538
+ // name: [scenario],
539
+ // paths: [feature_file_path],
540
+ // import: [path.join(tempFolderPath, "step_definitions", "**", "*.mjs")],
541
+ // // format: ["bvt"],
542
+ // },
543
+ // },
544
+ // { cwd: process.cwd(), env: environment }
545
+ // );
546
+ // // const files = glob.sync(path.join(tempFolderPath, "step_definitions", "**", "*.mjs"));
547
+ // // console.log("Files found:", files);
548
+ // const support = await loadSupport(runConfiguration, { cwd: process.cwd(), env: environment });
549
+ // // console.log("found ", support.stepDefinitions.length, "step definitions");
550
+ // // support.stepDefinitions.map((step) => {
551
+ // // console.log("step", step.pattern);
552
+ // // });
536
553
 
537
- try {
538
- const { loadConfiguration, loadSupport, runCucumber } = await import("@dev-blinq/cucumber-js/api");
539
- const { runConfiguration } = await loadConfiguration(
540
- {
541
- provided: {
542
- name: [scenario],
543
- paths: [feature_file_path],
544
- import: [path.join(tempFolderPath, "step_definitions", "**", "*.mjs")],
545
- // format: ["bvt"],
546
- },
547
- },
548
- { cwd: process.cwd(), env: environment }
549
- );
550
- // const files = glob.sync(path.join(tempFolderPath, "step_definitions", "**", "*.mjs"));
551
- // console.log("Files found:", files);
552
- const support = await loadSupport(runConfiguration, { cwd: process.cwd(), env: environment });
553
- // console.log("found ", support.stepDefinitions.length, "step definitions");
554
- // support.stepDefinitions.map((step) => {
555
- // console.log("step", step.pattern);
556
- // });
554
+ // if (skipAfter) {
555
+ // // ignore afterAll/after hooks
556
+ // support.afterTestCaseHookDefinitions = [];
557
+ // support.afterTestRunHookDefinitions = [];
558
+ // }
559
+ // // if (skipBefore) {
560
+ // // // ignore beforeAll/before hooks
561
+ // // support.beforeTestCaseHookDefinitions = support.beforeTestCaseHookDefinitions.filter((hook) => {
562
+ // // return hook.uri.endsWith("utils.mjs")
563
+ // // });
564
+ // // support.beforeTestRunHookDefinitions = [];
565
+ // // }
557
566
 
558
- if (options.skipAfter) {
559
- // ignore afterAll/after hooks
560
- support.afterTestCaseHookDefinitions = [];
561
- support.afterTestRunHookDefinitions = [];
562
- }
567
+ // let errorMesssage = null;
568
+ // let info = null;
569
+ // let errInfo = null;
570
+ // const result = await runCucumber({ ...runConfiguration, support }, environment, (message) => {
571
+ // if (message.testStepFinished) {
572
+ // const { testStepFinished } = message;
573
+ // const { testStepResult } = testStepFinished;
574
+ // if (testStepResult.status === "FAILED" || testStepResult.status === "AMBIGUOUS") {
575
+ // if (!errorMesssage) {
576
+ // errorMesssage = testStepResult.message;
577
+ // if (info) {
578
+ // errInfo = info;
579
+ // }
580
+ // }
581
+ // }
582
+ // if (testStepResult.status === "UNDEFINED") {
583
+ // if (!errorMesssage) {
584
+ // errorMesssage = `step ${JSON.stringify(stepText)} is ${testStepResult.status}`;
585
+ // if (info) {
586
+ // errInfo = info;
587
+ // }
588
+ // }
589
+ // }
590
+ // }
591
+ // if (message.attachment) {
592
+ // const attachment = message.attachment;
593
+ // if (attachment.mediaType === "application/json" && attachment.body) {
594
+ // const body = JSON.parse(attachment.body);
595
+ // info = body.info;
596
+ // }
597
+ // }
598
+ // });
599
+ // if (errorMesssage) {
600
+ // const bvtError = new Error(errorMesssage);
601
+ // Object.assign(bvtError, { info: errInfo });
602
+ // throw bvtError;
603
+ // }
563
604
 
564
- let errorMesssage = null;
565
- let info = null;
566
- let errInfo = null;
567
- const result = await runCucumber({ ...runConfiguration, support }, environment, (message) => {
568
- if (message.testStepFinished) {
569
- const { testStepFinished } = message;
570
- const { testStepResult } = testStepFinished;
571
- if (testStepResult.status === "FAILED" || testStepResult.status === "AMBIGUOUS") {
572
- if (!errorMesssage) {
573
- errorMesssage = testStepResult.message;
574
- if (info) {
575
- errInfo = info;
576
- }
577
- }
578
- }
579
- if (testStepResult.status === "UNDEFINED") {
580
- if (!errorMesssage) {
581
- errorMesssage = `step ${JSON.stringify(stepText)} is ${testStepResult.status}`;
582
- if (info) {
583
- errInfo = info;
584
- }
585
- }
586
- }
587
- }
588
- if (message.attachment) {
589
- const attachment = message.attachment;
590
- if (attachment.mediaType === "application/json" && attachment.body) {
591
- const body = JSON.parse(attachment.body);
592
- info = body.info;
593
- }
594
- }
595
- });
596
- if (errorMesssage) {
597
- const bvtError = new Error(errorMesssage);
598
- Object.assign(bvtError, { info: errInfo });
599
- throw bvtError;
600
- }
601
- } catch (error) {
602
- console.error("Error running cucumber-js", error);
603
- throw error;
604
- }
605
- };
605
+ // return {
606
+ // result,
607
+ // info,
608
+ // };
609
+ // } catch (error) {
610
+ // console.error("Error running cucumber-js", error);
611
+ // throw error;
612
+ // }
613
+ // };
606
614
  }
607
615
  function findFilesWithExtension(folderPath, extension, rootFolder = null) {
608
616
  let result = [];
@@ -267,7 +267,7 @@ class LocalAgent {
267
267
  null,
268
268
  true,
269
269
  null,
270
- 450,
270
+ -1,
271
271
  null,
272
272
  initScript
273
273
  );
@@ -655,6 +655,7 @@ class LocalAgent {
655
655
 
656
656
  let page = agent.project.getPage(agent.pageName, true);
657
657
  const generateCodeResult = generateCode(recording, page, userData, agent.project.rootFolder, methodName);
658
+
658
659
  if (generateCodeResult.simple === true) {
659
660
  agent.sendDone("generateStepCode", null);
660
661
  break;
@@ -707,6 +708,9 @@ class LocalAgent {
707
708
  cucumberStep: agent.cucumberStep,
708
709
  },
709
710
  });
711
+ if (generateCodeResult.locatorsMetadata) {
712
+ page.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
713
+ }
710
714
  await page.save();
711
715
  agent.scenarioReport.updateLastStep({
712
716
  recording: command.data.recording,
@@ -967,7 +971,7 @@ class LocalAgent {
967
971
  async createNewStepLocal(
968
972
  featureName,
969
973
  cucumberStep,
970
- feature,
974
+ comments,
971
975
  userData,
972
976
  firstStep,
973
977
  previousTasks,
@@ -1083,7 +1087,7 @@ class LocalAgent {
1083
1087
  previousTasks,
1084
1088
  scenarioDocument,
1085
1089
  scenarioStepIndex,
1086
- comments: feature.comments,
1090
+ comments,
1087
1091
  featureFileText,
1088
1092
  recover,
1089
1093
  dumpConfig: this.dumpConfig,