@dev-blinq/cucumber_client 1.0.1197-dev → 1.0.1197-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 (39) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +220 -0
  2. package/bin/assets/preload/find_context.js +1 -1
  3. package/bin/assets/preload/recorderv3.js +75 -5
  4. package/bin/assets/preload/unique_locators.js +24 -3
  5. package/bin/assets/scripts/aria_snapshot.js +235 -0
  6. package/bin/assets/scripts/dom_attr.js +372 -0
  7. package/bin/assets/scripts/dom_element.js +0 -0
  8. package/bin/assets/scripts/dom_parent.js +185 -0
  9. package/bin/assets/scripts/event_utils.js +105 -0
  10. package/bin/assets/scripts/pw.js +7886 -0
  11. package/bin/assets/scripts/recorder.js +1147 -0
  12. package/bin/assets/scripts/snapshot_capturer.js +155 -0
  13. package/bin/assets/scripts/unique_locators.js +841 -0
  14. package/bin/assets/scripts/yaml.js +4770 -0
  15. package/bin/assets/templates/page_template.txt +2 -16
  16. package/bin/assets/templates/utils_template.txt +59 -7
  17. package/bin/client/cli_helpers.js +11 -13
  18. package/bin/client/code_cleanup/utils.js +42 -14
  19. package/bin/client/code_gen/code_inversion.js +48 -11
  20. package/bin/client/code_gen/index.js +3 -0
  21. package/bin/client/code_gen/page_reflection.js +37 -20
  22. package/bin/client/code_gen/playwright_codeget.js +153 -25
  23. package/bin/client/cucumber/feature.js +92 -35
  24. package/bin/client/cucumber/steps_definitions.js +109 -83
  25. package/bin/client/local_agent.js +6 -2
  26. package/bin/client/project.js +6 -2
  27. package/bin/client/recorderv3/bvt_recorder.js +272 -76
  28. package/bin/client/recorderv3/implemented_steps.js +69 -14
  29. package/bin/client/recorderv3/index.js +49 -7
  30. package/bin/client/recorderv3/network.js +299 -0
  31. package/bin/client/recorderv3/step_runner.js +183 -13
  32. package/bin/client/recorderv3/step_utils.js +155 -8
  33. package/bin/client/recorderv3/update_feature.js +58 -30
  34. package/bin/client/recording.js +7 -0
  35. package/bin/client/run_cucumber.js +16 -2
  36. package/bin/client/scenario_report.js +35 -8
  37. package/bin/client/test_scenario.js +0 -1
  38. package/bin/index.js +1 -0
  39. package/package.json +15 -8
@@ -47,21 +47,30 @@ const _isCodeGenerationStep = (step) => {
47
47
  step.type === Types.SELECT ||
48
48
  step.type === Types.HOVER ||
49
49
  step.type === Types.EXTRACT_ATTRIBUTE ||
50
+ step.type === Types.EXTRACT_PROPERTY ||
50
51
  step.type === Types.SET_DATE_TIME ||
51
52
  step.type === Types.CHECK ||
52
53
  step.type === Types.PRESS ||
53
54
  step.type === Types.SET_INPUT ||
54
55
  step.type === Types.FILL_UNKNOWN ||
55
- step.type === "context_click" ||
56
- step.type === "parameterized_click" ||
56
+ step.type === Types.CONTEXT_CLICK ||
57
+ step.type === Types.PARAMETERIZED_CLICK ||
57
58
  step.type === Types.VERIFY_ATTRIBUTE ||
59
+ step.type === Types.VERIFY_PROPERTY ||
58
60
  step.type === Types.SET_INPUT_FILES ||
59
- step.type === Types.VERIFY_PAGE_SNAPSHOT
61
+ step.type === Types.VERIFY_PAGE_SNAPSHOT ||
62
+ step.type === Types.CONDITIONAL_WAIT
60
63
  ) {
61
64
  return true;
62
65
  }
63
66
  return false;
64
67
  };
68
+ // Note: this function is used to exclude a key from an object
69
+ // Please move it to utils and use it as a reusable function ...
70
+ function excludeKey(obj, keyToRemove) {
71
+ const { [keyToRemove]: _, ...rest } = obj;
72
+ return rest;
73
+ }
65
74
  const splitToLocatorsGroups = (locators) => {
66
75
  const no_text = locators.locators.filter((locator) => locator.mode === "NO_TEXT");
67
76
  const ignore_digit = locators.locators.filter((locator) => locator.mode === "IGNORE_DIGIT");
@@ -131,7 +140,8 @@ const _generateCodeFromCommand = (step, elements, userData) => {
131
140
  if (!elementIdentifier) {
132
141
  elementIdentifier = findElementIdentifier(node, step, userData, elements);
133
142
  }
134
- elements[elementIdentifier] = locatorObject;
143
+ const withoutAllStrategyLocators = excludeKey(locatorObject, "allStrategyLocators");
144
+ elements[elementIdentifier] = withoutAllStrategyLocators;
135
145
  elementsChanged = true;
136
146
  }
137
147
  let optionElement = null;
@@ -157,6 +167,7 @@ const _generateCodeFromCommand = (step, elements, userData) => {
157
167
  let variable = null;
158
168
  let format = null;
159
169
  let options = null;
170
+ let property = null;
160
171
  switch (step.type) {
161
172
  case Types.SET_INPUT:
162
173
  line = `await context.web.setInputValue(elements["${elementIdentifier}"], `;
@@ -181,19 +192,51 @@ const _generateCodeFromCommand = (step, elements, userData) => {
181
192
  line = `await context.web.setCheck(elements["${elementIdentifier}"], ${step.check}, _params, null, this);`;
182
193
  codeLines.push(line);
183
194
  } else {
184
- if (element_name) {
185
- comment = `// Click on ${element_name}`;
186
- codeLines.push(escapeNonPrintables(comment));
195
+ switch (step.count) {
196
+ case 1:
197
+ if (element_name) {
198
+ comment = `// Click on ${element_name}`;
199
+ codeLines.push(escapeNonPrintables(comment));
200
+ }
201
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
202
+ codeLines.push(line);
203
+ break;
204
+ case 2:
205
+ if (element_name) {
206
+ comment = `// Double click on ${element_name}`;
207
+ codeLines.push(escapeNonPrintables(comment));
208
+ }
209
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {clickCount:2}, this);`;
210
+ codeLines.push(line);
211
+ break;
212
+ default:
213
+ if (element_name) {
214
+ comment = `// Click on ${element_name}`;
215
+ codeLines.push(escapeNonPrintables(comment));
216
+ }
217
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
218
+ codeLines.push(line);
219
+ break;
187
220
  }
188
-
189
- line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
190
- codeLines.push(line);
191
221
  }
192
222
  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);`;
223
+ case Types.PARAMETERIZED_CLICK:
224
+ switch (step.count) {
225
+ case 1:
226
+ comment = `// Parameterized click on ${step.value}`;
227
+ codeLines.push(escapeNonPrintables(comment));
228
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
229
+ break;
230
+ case 2:
231
+ comment = `// Parameterized double click on ${step.value}`;
232
+ codeLines.push(escapeNonPrintables(comment));
233
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {clickCount:2}, this);`;
234
+ break;
235
+ default:
236
+ comment = `// Parameterized click on ${step.value}`;
237
+ codeLines.push(escapeNonPrintables(comment));
238
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, null, this);`;
239
+ }
197
240
  codeLines.push(line);
198
241
  break;
199
242
  case Types.SET_DATE_TIME:
@@ -286,6 +329,30 @@ const _generateCodeFromCommand = (step, elements, userData) => {
286
329
  codeLines.push(line);
287
330
  break;
288
331
  }
332
+ case Types.EXTRACT_PROPERTY: {
333
+ property = escapeNonPrintables(step.parameters[0]);
334
+ variable = escapeNonPrintables(step.parameters[1]);
335
+ input = `"${variable}"`;
336
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
337
+ input = "_" + step.dataKey;
338
+ }
339
+ let options = "null";
340
+ if (step.regex !== "") {
341
+ options = `{regex: ${JSON.stringify(step.regex)},trimSpaces: ${step.trimSpaces}}`;
342
+ } else if (step.trimSpaces) {
343
+ options = `{trimSpaces: ${step.trimSpaces}}`;
344
+ } else {
345
+ options = "null";
346
+ }
347
+ line = `await context.web.extractProperty(elements["${elementIdentifier}"], "${property}", ${input}, _params,${options}, this);`;
348
+
349
+ if (element_name) {
350
+ comment = `// Extract property ${property} from ${element_name} to ${variable}`;
351
+ codeLines.push(escapeNonPrintables(comment));
352
+ }
353
+ codeLines.push(line);
354
+ break;
355
+ }
289
356
  case Types.VERIFY_PAGE_SNAPSHOT:
290
357
  comment = step.nestFrmLoc
291
358
  ? `// Verify page snapshot ${step.parameters[0]} in the frame ${step.selectors.iframe_src}`
@@ -507,15 +574,39 @@ const _generateCodeFromCommand = (step, elements, userData) => {
507
574
  line = `await context.web.clickType( elements["${elementIdentifier}"] ,"${step.key}",null, _params, {"press":true}, this);`;
508
575
  codeLines.push(line);
509
576
  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;
577
+ case Types.CONTEXT_CLICK:
578
+ switch (step.count) {
579
+ case 1:
580
+ comment = `// Click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
581
+ codeLines.push(escapeNonPrintables(comment));
582
+ input = `"${escapeNonPrintables(step.value.replaceAll('"', '\\"'))}"`;
583
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
584
+ input = "_" + step.dataKey;
585
+ }
586
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {"context": ${input}}, this);`;
587
+ codeLines.push(line);
588
+ break;
589
+ case 2:
590
+ comment = `// Double click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
591
+ codeLines.push(escapeNonPrintables(comment));
592
+ input = `"${escapeNonPrintables(step.value.replaceAll('"', '\\"'))}"`;
593
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
594
+ input = "_" + step.dataKey;
595
+ }
596
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {"context": ${input}, clickCount:2}, this);`;
597
+ codeLines.push(line);
598
+ break;
599
+ default:
600
+ comment = `// Click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
601
+ codeLines.push(escapeNonPrintables(comment));
602
+ input = `"${escapeNonPrintables(step.value.replaceAll('"', '\\"'))}"`;
603
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
604
+ input = "_" + step.dataKey;
605
+ }
606
+ line = `await context.web.click(elements["${elementIdentifier}"], _params, {"context": ${input}}, this);`;
607
+ codeLines.push(line);
608
+ break;
516
609
  }
517
- line = `await context.web.click(elements["${elementIdentifier}"], _params, {"context": ${input}}, this);`;
518
- codeLines.push(line);
519
610
  break;
520
611
  case "popup_close":
521
612
  break;
@@ -541,6 +632,23 @@ const _generateCodeFromCommand = (step, elements, userData) => {
541
632
  codeLines.push(line);
542
633
  break;
543
634
  }
635
+ case Types.VERIFY_PROPERTY: {
636
+ line = `await context.web.verifyProperty(elements["${elementIdentifier}"], `;
637
+ input = "_param_0";
638
+ if (step.dataSource === "userData" || step.dataSource === "parameters") {
639
+ input = "_" + step.dataKey;
640
+ }
641
+ line += `"${step.parameters[0]}", ${input}, _params, null, this);`;
642
+ codeLines.push(line);
643
+ break;
644
+ }
645
+ case Types.CONDITIONAL_WAIT: {
646
+ line = `await context.web.conditionalWait(elements["${elementIdentifier}"], `;
647
+ input = "_param_0";
648
+ line += `"${step.parameters[1]}", ${input}, _params, null, this);`;
649
+ codeLines.push(line);
650
+ break;
651
+ }
544
652
  case Types.SET_INPUT_FILES: {
545
653
  line = `await context.web.setInputFiles(elements["${elementIdentifier}"], `;
546
654
  let files = step.parameters[0];
@@ -566,6 +674,22 @@ const _generateCodeFromCommand = (step, elements, userData) => {
566
674
  return { codeLines, elements: elementsChanged ? elements : null, elementIdentifier, allStrategyLocators };
567
675
  };
568
676
 
677
+ /**
678
+ * Generates a report command based on the given position.
679
+ * @param {"start"|"end"} position
680
+ */
681
+ const generateReportCommand = (position, cmdId) => {
682
+ const codeLines = [];
683
+ if (position === "start") {
684
+ const line = `await context.web.addCommandToReport("${cmdId}", "PASSED", '{"status":"start","cmdId":"${cmdId}"}', {type:"cmdReport"},this);`;
685
+ codeLines.push(line);
686
+ } else if (position === "end") {
687
+ const line = `await context.web.addCommandToReport("${cmdId}", "PASSED", '{"status":"end","cmdId":"${cmdId}"}', {type:"cmdReport"},this);`;
688
+ codeLines.push(line);
689
+ }
690
+ return codeLines;
691
+ };
692
+
569
693
  const generateCode = (recording, codePage, userData, projectDir, methodName) => {
570
694
  const stepsDefinitions = new StepsDefinitions(projectDir);
571
695
  stepsDefinitions.load(false);
@@ -606,12 +730,16 @@ const generateCode = (recording, codePage, userData, projectDir, methodName) =>
606
730
  if (recordingStep.type === Types.COMPLETE) {
607
731
  return;
608
732
  }
609
- // if (!recordingStep.element) {
610
- // logger.info(`Step ${recordingStep.type} doesn't have an element, ignoring`);
611
- // return;
733
+
734
+ // if (process.env.TEMP_RUN === "true") {
735
+ // codeLines.push(...generateReportCommand("start", recordingStep.cmdId));
612
736
  // }
613
737
  const result = _generateCodeFromCommand(recordingStep, elements, userData);
738
+
614
739
  codeLines.push(...result.codeLines);
740
+ // if (process.env.TEMP_RUN === "true") {
741
+ // codeLines.push(...generateReportCommand("end", recordingStep.cmdId));
742
+ // }
615
743
  if (result.elements) {
616
744
  elements = result.elements;
617
745
  }
@@ -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
-
10
+ import { generateTestData } from "./feature_data.js";
9
11
  class DataTable {
10
12
  constructor(dataTableDocument) {
11
13
  if (!dataTableDocument) {
@@ -311,6 +313,12 @@ class Feature {
311
313
  stepInfo.template = step.getTemplate();
312
314
  scenarioInfo.steps.push(stepInfo);
313
315
  const stepDef = stepsDefinitions.findMatchingStep(stepInfo.template);
316
+ stepInfo.implemented = stepDef ? true : false;
317
+ if (stepDef) {
318
+ stepInfo.mjsFile = stepDef.file;
319
+ stepInfo.functionName = stepDef.functionName;
320
+ stepInfo.implemented_at = stepDef.implemented_at;
321
+ }
314
322
  if (!stepDef && parseFailedFiles) {
315
323
  //Check if the stepName exists in one of the failedToParseFiles
316
324
  try {
@@ -322,10 +330,9 @@ class Feature {
322
330
  const fileContent = fs.readFileSync(failedFile, "utf8");
323
331
  if (fileContent.includes(stepName)) {
324
332
  stepInfo.implemented = true;
325
- stepInfo.mjsFile = failedFile;
333
+ stepInfo.mjsFile = failedFile.slice(path.normalize(failedFile).indexOf("features/step_definitions"));
326
334
  stepInfo.functionName = stepName;
327
335
  stepInfo.error = errorsInParsing[k].error;
328
- console.log(JSON.stringify(stepInfo));
329
336
  break;
330
337
  }
331
338
  }
@@ -334,12 +341,6 @@ class Feature {
334
341
  console.error(`Error while checking step ${stepInfo.template} in failed files:`, e);
335
342
  }
336
343
  }
337
- stepInfo.implemented = stepDef ? true : false;
338
- if (stepDef) {
339
- stepInfo.mjsFile = stepDef.file;
340
- stepInfo.functionName = stepDef.functionName;
341
- stepInfo.implemented_at = stepDef.implemented_at;
342
- }
343
344
  }
344
345
  document.scenarios.push(scenarioInfo);
345
346
  }
@@ -373,6 +374,11 @@ class Examples {
373
374
  return null;
374
375
  }
375
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
+
376
382
  return unEscapeNonPrintables(value); // While editing in recorder, we need to remove backslashes
377
383
  }
378
384
  }
@@ -450,29 +456,7 @@ class Scenario {
450
456
  return this.scenarioText;
451
457
  }
452
458
  }
453
- const scenarioResolution = async (featureFilePath) => {
454
- if (!fs.existsSync(featureFilePath)) {
455
- throw new Error(`Feature file ${featureFilePath} does not exist`);
456
- }
457
- const newId = IdGenerator.uuid();
458
- const builder = new AstBuilder(newId);
459
- const matcher = new GherkinClassicTokenMatcher();
460
-
461
- const parser = new Parser(builder, matcher);
462
-
463
- // normalize the path to start with featuers/ if it's not cut the featureFielPath to only the part after features/
464
- let uri = featureFilePath.startsWith("features/")
465
- ? featureFilePath
466
- : "features/" + featureFilePath.split("features/")[1];
467
-
468
- const featureFileContent = fs.readFileSync(featureFilePath, "utf8");
469
- const gherkinDocument = parser.parse(featureFileContent, newId);
470
459
 
471
- const feature = new Feature(gherkinDocument, featureFileContent);
472
- //const scenario = feature.getScenario(scenarioName);
473
- //return scenario;
474
- return feature;
475
- };
476
460
  const getDocumentAndPickel = (featureName, featureContent, scenarioName) => {
477
461
  const newId = IdGenerator.uuid();
478
462
  const builder = new AstBuilder(newId);
@@ -485,12 +469,85 @@ const getDocumentAndPickel = (featureName, featureContent, scenarioName) => {
485
469
 
486
470
  const gherkinDocument = parser.parse(featureContent, newId);
487
471
  const pickles = compile(gherkinDocument, uri, newId);
488
-
489
472
  // Step 3: Find the specific scenario Pickle
490
473
  const pickle = pickles.find((pickle) => {
491
474
  return pickle.name === scenarioName;
492
475
  });
493
476
  return { gherkinDocument, pickle };
494
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
+ };
495
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
+ };
496
553
  export { Feature, Scenario, Step, DataTable, Examples, scenarioResolution, getDocumentAndPickel };
@@ -62,24 +62,23 @@ class StepsDefinitions {
62
62
  // console.log({ mjsFiles });
63
63
  for (let i = 0; i < mjsFiles.length; i++) {
64
64
  const mjsFile = mjsFiles[i];
65
- console.log("loading step definitions from file", mjsFile);
66
65
  const filePath = path.join(
67
66
  this.baseFolder,
68
67
  this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features",
69
- mjsFile);
68
+ mjsFile
69
+ );
70
70
  const codePage = new CodePage(filePath);
71
71
  try {
72
72
  codePage.generateModel();
73
73
  } catch (error) {
74
- logger.info("unable to generate model for file", mjsFile);
75
- if(supressErrors) {
74
+ logger.info("unable to generate model for", mjsFile, error);
75
+ if (supressErrors) {
76
76
  errors.push({
77
77
  file: filePath,
78
78
  error: error.message,
79
79
  });
80
- }
81
- else{
82
- throw error;
80
+ } else {
81
+ continue;
83
82
  }
84
83
  }
85
84
  this.initPage(codePage, mjsFile);
@@ -524,86 +523,94 @@ class StepsDefinitions {
524
523
  // }
525
524
  // );
526
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
+ // };
527
532
 
528
- executeStepRemote = async ({ feature_file_path, scenario, tempFolderPath, stepText }, options) => {
529
- if (!options) {
530
- options = {
531
- skipAfter: true,
532
- };
533
- }
534
- const environment = {
535
- ...process.env,
536
- };
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
+ // // });
537
553
 
538
- try {
539
- const { loadConfiguration, loadSupport, runCucumber } = await import("@dev-blinq/cucumber-js/api");
540
- const { runConfiguration } = await loadConfiguration(
541
- {
542
- provided: {
543
- name: [scenario],
544
- paths: [feature_file_path],
545
- import: [path.join(tempFolderPath, "step_definitions", "**", "*.mjs")],
546
- // format: ["bvt"],
547
- },
548
- },
549
- { cwd: process.cwd(), env: environment }
550
- );
551
- // const files = glob.sync(path.join(tempFolderPath, "step_definitions", "**", "*.mjs"));
552
- // console.log("Files found:", files);
553
- const support = await loadSupport(runConfiguration, { cwd: process.cwd(), env: environment });
554
- // console.log("found ", support.stepDefinitions.length, "step definitions");
555
- // support.stepDefinitions.map((step) => {
556
- // console.log("step", step.pattern);
557
- // });
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
+ // // }
558
566
 
559
- if (options.skipAfter) {
560
- // ignore afterAll/after hooks
561
- support.afterTestCaseHookDefinitions = [];
562
- support.afterTestRunHookDefinitions = [];
563
- }
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
+ // }
564
604
 
565
- let errorMesssage = null;
566
- let info = null;
567
- let errInfo = null;
568
- const result = await runCucumber({ ...runConfiguration, support }, environment, (message) => {
569
- if (message.testStepFinished) {
570
- const { testStepFinished } = message;
571
- const { testStepResult } = testStepFinished;
572
- if (testStepResult.status === "FAILED" || testStepResult.status === "AMBIGUOUS") {
573
- if (!errorMesssage) {
574
- errorMesssage = testStepResult.message;
575
- if (info) {
576
- errInfo = info;
577
- }
578
- }
579
- }
580
- if (testStepResult.status === "UNDEFINED") {
581
- if (!errorMesssage) {
582
- errorMesssage = `step ${JSON.stringify(stepText)} is ${testStepResult.status}`;
583
- if (info) {
584
- errInfo = info;
585
- }
586
- }
587
- }
588
- }
589
- if (message.attachment) {
590
- const attachment = message.attachment;
591
- if (attachment.mediaType === "application/json" && attachment.body) {
592
- const body = JSON.parse(attachment.body);
593
- info = body.info;
594
- }
595
- }
596
- });
597
- if (errorMesssage) {
598
- const bvtError = new Error(errorMesssage);
599
- Object.assign(bvtError, { info: errInfo });
600
- throw bvtError;
601
- }
602
- } catch (error) {
603
- console.error("Error running cucumber-js", error);
604
- throw error;
605
- }
606
- };
605
+ // return {
606
+ // result,
607
+ // info,
608
+ // };
609
+ // } catch (error) {
610
+ // console.error("Error running cucumber-js", error);
611
+ // throw error;
612
+ // }
613
+ // };
607
614
  }
608
615
  function findFilesWithExtension(folderPath, extension, rootFolder = null) {
609
616
  let result = [];
@@ -633,4 +640,23 @@ function findFilesWithExtension(folderPath, extension, rootFolder = null) {
633
640
 
634
641
  return result;
635
642
  }
643
+ export function locateDefinitionPath(featureFolder, pageName) {
644
+ if (!pageName) {
645
+ return null;
646
+ }
647
+ if (!pageName.endsWith("_page.mjs")) {
648
+ pageName = pageName + "_page.mjs";
649
+ }
650
+ // search for files with the pageName
651
+ const stepDefinitionFolderPath = path.join(featureFolder, "step_definitions");
652
+ const files = findFilesWithExtension(stepDefinitionFolderPath, "mjs");
653
+ for (let i = 0; i < files.length; i++) {
654
+ const file = files[i];
655
+ if (file.includes(pageName)) {
656
+ return path.join(stepDefinitionFolderPath, file);
657
+ }
658
+ }
659
+ return path.join(stepDefinitionFolderPath, pageName);
660
+ }
661
+
636
662
  export { StepsDefinitions, findFilesWithExtension };