@dev-blinq/cucumber_client 1.0.1342-dev → 1.0.1342-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 (53) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +110 -110
  2. package/bin/assets/preload/css_gen.js +10 -10
  3. package/bin/assets/preload/recorderv3.js +3 -1
  4. package/bin/assets/preload/toolbar.js +27 -29
  5. package/bin/assets/preload/unique_locators.js +1 -1
  6. package/bin/assets/preload/yaml.js +288 -275
  7. package/bin/assets/scripts/aria_snapshot.js +223 -220
  8. package/bin/assets/scripts/dom_attr.js +329 -329
  9. package/bin/assets/scripts/dom_parent.js +169 -174
  10. package/bin/assets/scripts/event_utils.js +94 -94
  11. package/bin/assets/scripts/pw.js +2050 -1949
  12. package/bin/assets/scripts/recorder.js +8 -18
  13. package/bin/assets/scripts/snapshot_capturer.js +153 -146
  14. package/bin/assets/scripts/unique_locators.js +940 -815
  15. package/bin/assets/scripts/yaml.js +796 -783
  16. package/bin/assets/templates/_hooks_template.txt +41 -0
  17. package/bin/assets/templates/utils_template.txt +2 -45
  18. package/bin/client/apiTest/apiTest.js +6 -0
  19. package/bin/client/cli_helpers.js +11 -13
  20. package/bin/client/code_cleanup/utils.js +5 -1
  21. package/bin/client/code_gen/api_codegen.js +2 -2
  22. package/bin/client/code_gen/code_inversion.js +112 -4
  23. package/bin/client/code_gen/page_reflection.js +839 -906
  24. package/bin/client/code_gen/playwright_codeget.js +26 -18
  25. package/bin/client/cucumber/feature.js +89 -27
  26. package/bin/client/cucumber/feature_data.js +2 -2
  27. package/bin/client/cucumber/project_to_document.js +9 -3
  28. package/bin/client/cucumber/steps_definitions.js +6 -3
  29. package/bin/client/cucumber_selector.js +17 -1
  30. package/bin/client/local_agent.js +6 -5
  31. package/bin/client/parse_feature_file.js +23 -26
  32. package/bin/client/playground/projects/env.json +2 -2
  33. package/bin/client/project.js +186 -196
  34. package/bin/client/recorderv3/bvt_recorder.js +136 -81
  35. package/bin/client/recorderv3/implemented_steps.js +24 -14
  36. package/bin/client/recorderv3/index.js +59 -54
  37. package/bin/client/recorderv3/network.js +22 -5
  38. package/bin/client/recorderv3/scriptTest.js +1 -1
  39. package/bin/client/recorderv3/services.js +4 -16
  40. package/bin/client/recorderv3/step_runner.js +318 -209
  41. package/bin/client/recorderv3/step_utils.js +476 -17
  42. package/bin/client/recorderv3/update_feature.js +32 -30
  43. package/bin/client/recording.js +1 -0
  44. package/bin/client/run_cucumber.js +1 -1
  45. package/bin/client/scenario_report.js +0 -5
  46. package/bin/client/test_scenario.js +0 -1
  47. package/bin/client/upload-service.js +3 -2
  48. package/bin/client/utils/socket_logger.js +132 -0
  49. package/bin/index.js +1 -0
  50. package/bin/logger.js +3 -2
  51. package/bin/min/consoleApi.min.cjs +2 -3
  52. package/bin/min/injectedScript.min.cjs +16 -16
  53. package/package.json +21 -12
@@ -4,6 +4,7 @@ import { StepsDefinitions } from "../cucumber/steps_definitions.js";
4
4
  import path from "path";
5
5
  import { CodePage } from "./page_reflection.js";
6
6
  import { convertToIdentifier, escapeNonPrintables } from "./utils.js";
7
+ import socketLogger from "../utils/socket_logger.js";
7
8
  const findElementIdentifier = (node, step, userData, elements) => {
8
9
  if (node.key) {
9
10
  // incase of rerunning implemented steps
@@ -12,6 +13,13 @@ const findElementIdentifier = (node, step, userData, elements) => {
12
13
 
13
14
  let elementIdentifier = null;
14
15
  const keys = Object.keys(elements);
16
+
17
+ for (let i = 0; i < keys.length; i++) {
18
+ const element_key = keys[i];
19
+ const element = elements[element_key];
20
+ element["element_key"] = element_key;
21
+ }
22
+
15
23
  let name = node.name;
16
24
  if (!name && step.dataKey) {
17
25
  name = step.dataKey;
@@ -92,11 +100,16 @@ const splitToLocatorsGroups = (locators) => {
92
100
  const _generateCodeFromCommand = (step, elements, userData) => {
93
101
  let elementsChanged = false;
94
102
 
95
- let elementIdentifier = null;
103
+ let elementIdentifier = step.locators?.element_key;
96
104
  let locatorObject = step.locators;
97
105
  // handle element
98
106
  let element_name = null;
99
107
  let allStrategyLocators = null;
108
+ const codeLines = [];
109
+
110
+ if (_isCodeGenerationStep(step) === false)
111
+ return { codeLines, elements: elementsChanged ? elements : null, elementIdentifier, allStrategyLocators };
112
+
100
113
  if (_isCodeGenerationStep(step) && step.type !== Types.VERIFY_PAGE_SNAPSHOT) {
101
114
  allStrategyLocators = step.allStrategyLocators ?? splitToLocatorsGroups(step.locators);
102
115
  let node = null;
@@ -130,18 +143,20 @@ const _generateCodeFromCommand = (step, elements, userData) => {
130
143
  let locatorObjectClone = JSON.parse(JSON.stringify(locatorObject));
131
144
  element_name = locatorObjectClone.element_name;
132
145
  delete locatorObjectClone.element_name;
133
- keys.forEach((key) => {
134
- let elementClone = JSON.parse(JSON.stringify(elements[key]));
135
- delete elementClone.element_name;
136
- if (JSON.stringify(elementClone) === JSON.stringify(locatorObjectClone)) {
137
- elementIdentifier = key;
138
- }
139
- });
146
+ if (!elementIdentifier) {
147
+ keys.forEach((key) => {
148
+ let elementClone = JSON.parse(JSON.stringify(elements[key]));
149
+ delete elementClone.element_name;
150
+ if (JSON.stringify(elementClone) === JSON.stringify(locatorObjectClone)) {
151
+ elementIdentifier = key;
152
+ }
153
+ });
154
+ }
140
155
  if (!elementIdentifier) {
141
156
  elementIdentifier = findElementIdentifier(node, step, userData, elements);
142
157
  }
143
158
  const withoutAllStrategyLocators = excludeKey(locatorObject, "allStrategyLocators");
144
- elements[elementIdentifier] = withoutAllStrategyLocators;
159
+ elements[elementIdentifier] = { ...withoutAllStrategyLocators, element_key: elementIdentifier };
145
160
  elementsChanged = true;
146
161
  }
147
162
  let optionElement = null;
@@ -157,8 +172,6 @@ const _generateCodeFromCommand = (step, elements, userData) => {
157
172
  }
158
173
  }
159
174
 
160
- const codeLines = [];
161
-
162
175
  let line = null;
163
176
  let comment = null;
164
177
  let input = null;
@@ -716,6 +729,7 @@ const generateCode = (recording, codePage, userData, projectDir, methodName) =>
716
729
  }
717
730
  let elements = {};
718
731
  if (!codePage) {
732
+ socketLogger.info("CodePage is null");
719
733
  console.log("codePage is null");
720
734
  } else {
721
735
  elements = codePage.getVariableDeclarationAsObject("elements");
@@ -735,13 +749,7 @@ const generateCode = (recording, codePage, userData, projectDir, methodName) =>
735
749
  // codeLines.push(...generateReportCommand("start", recordingStep.cmdId));
736
750
  // }
737
751
  const result = _generateCodeFromCommand(recordingStep, elements, userData);
738
- if (process.env.TEMP_RUN) {
739
- // Add try catch block to the generated code, should attach commandId to world object
740
- result.codeLines.unshift(`try {`);
741
- result.codeLines.push(
742
- `} catch (err) { throw new Error("Error in command ${recordingStep.cmdId}: " + err.message); }`
743
- );
744
- }
752
+
745
753
  codeLines.push(...result.codeLines);
746
754
  // if (process.env.TEMP_RUN === "true") {
747
755
  // codeLines.push(...generateReportCommand("end", recordingStep.cmdId));
@@ -1,11 +1,14 @@
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";
11
+ import socketLogger from "../utils/socket_logger.js";
9
12
  class DataTable {
10
13
  constructor(dataTableDocument) {
11
14
  if (!dataTableDocument) {
@@ -372,6 +375,11 @@ class Examples {
372
375
  return null;
373
376
  }
374
377
  let value = this.tableBody[0].cells[parameterIndex].value;
378
+ if (!value) {
379
+ console.warn(`Parameter ${parameterName} not found in examples table body, or it is empty`);
380
+ return null;
381
+ }
382
+
375
383
  return unEscapeNonPrintables(value); // While editing in recorder, we need to remove backslashes
376
384
  }
377
385
  }
@@ -449,29 +457,7 @@ class Scenario {
449
457
  return this.scenarioText;
450
458
  }
451
459
  }
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
460
 
470
- const feature = new Feature(gherkinDocument, featureFileContent);
471
- //const scenario = feature.getScenario(scenarioName);
472
- //return scenario;
473
- return feature;
474
- };
475
461
  const getDocumentAndPickel = (featureName, featureContent, scenarioName) => {
476
462
  const newId = IdGenerator.uuid();
477
463
  const builder = new AstBuilder(newId);
@@ -484,12 +470,88 @@ const getDocumentAndPickel = (featureName, featureContent, scenarioName) => {
484
470
 
485
471
  const gherkinDocument = parser.parse(featureContent, newId);
486
472
  const pickles = compile(gherkinDocument, uri, newId);
487
-
488
473
  // Step 3: Find the specific scenario Pickle
489
474
  const pickle = pickles.find((pickle) => {
490
475
  return pickle.name === scenarioName;
491
476
  });
492
477
  return { gherkinDocument, pickle };
493
478
  };
479
+ const scenarioResolution = async (featureFilePath) => {
480
+ if (!fs.existsSync(featureFilePath)) {
481
+ throw new Error(`Feature file ${featureFilePath} does not exist`);
482
+ }
483
+ const featureFileContent = fs.readFileSync(featureFilePath, "utf8");
484
+ let tmpDir = null;
485
+ try {
486
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "ai-qa"));
487
+ const tmpFeaturePath = path.join(tmpDir, "features");
488
+ fs.mkdirSync(tmpFeaturePath);
489
+ const tmpFeatureFilePath = path.join(tmpFeaturePath, path.basename(featureFilePath));
490
+ let result = generateTestData(featureFilePath);
491
+ if (result.changed) {
492
+ fs.writeFileSync(tmpFeatureFilePath, result.newContent);
493
+ console.log("Fake data was generated for this scenario");
494
+ console.log("Variables:");
495
+ for (let key in result.variables) {
496
+ console.log(`${key}: ${result.variables[key].fake}`);
497
+ }
498
+ console.log("Other fake data:");
499
+ for (let i = 0; i < result.otherFakeData.length; i++) {
500
+ console.log(`${result.otherFakeData[i].var}: ${result.otherFakeData[i].fake}`);
501
+ }
502
+ } else {
503
+ fs.copyFileSync(featureFilePath, tmpFeatureFilePath);
504
+ }
505
+ const writable = new stream.Writable({
506
+ write: function (chunk, encoding, next) {
507
+ //console.log(chunk.toString());
508
+ next();
509
+ },
510
+ });
511
+ const environment = { cwd: tmpDir, stdout: writable, stderr: writable };
512
+ // load configuration from a particular file, and override a specific option
513
+ const provided = {
514
+ default: "--publish-quiet",
515
+ require: [tmpDir],
516
+ failFast: false,
517
+ };
494
518
 
519
+ let gherkinDocument = null;
520
+ const { runConfiguration } = await loadConfiguration({ provided }, environment);
521
+ // load the support code upfront
522
+ const support = await loadSupport(runConfiguration, environment);
523
+ // run cucumber, using the support code we loaded already
524
+ await runCucumber({ ...runConfiguration, support }, environment, function (event) {
525
+ // if (event.source) {
526
+ // scenarioInfo.source = event.source.data;
527
+ // }
528
+ // if (event.pickle && event.pickle.name === scenarioName) {
529
+ // scenarioInfo.pickle = event.pickle;
530
+ // }
531
+ if (event.gherkinDocument) {
532
+ gherkinDocument = event.gherkinDocument;
533
+ }
534
+ //console.log(event);
535
+ //console.log(JSON.stringify(event, null, 2));
536
+ // console.log("");
537
+ });
538
+ const feature = new Feature(gherkinDocument, featureFileContent);
539
+ //const scenario = feature.getScenario(scenarioName);
540
+ //return scenario;
541
+ return feature;
542
+ } finally {
543
+ try {
544
+ if (tmpDir) {
545
+ fs.rmSync(tmpDir, { recursive: true });
546
+ }
547
+ } catch (e) {
548
+ socketLogger.error(
549
+ `An error has occurred while removing the temp folder at ${tmpDir}. Please remove it manually. Error: ${e}`
550
+ );
551
+ console.error(
552
+ `An error has occurred while removing the temp folder at ${tmpDir}. Please remove it manually. Error: ${e}`
553
+ );
554
+ }
555
+ }
556
+ };
495
557
  export { Feature, Scenario, Step, DataTable, Examples, scenarioResolution, getDocumentAndPickel };
@@ -41,10 +41,10 @@ const generateTestData = (featureFile) => {
41
41
  try {
42
42
  const fake = faker.helpers.fake(match[0]);
43
43
 
44
- if(fake === match[0]){
44
+ if (fake === match[0]) {
45
45
  continue;
46
46
  }
47
-
47
+
48
48
  otherFakeData.push({
49
49
  var: match[0],
50
50
  fake,
@@ -4,7 +4,13 @@ import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherki
4
4
  import { Feature } from "./feature.js";
5
5
  import { StepsDefinitions } from "./steps_definitions.js";
6
6
 
7
- export const projectDocument = (folder, featureName = null, scenarioName = null, supressErrors = false, errors = []) => {
7
+ export const projectDocument = (
8
+ folder,
9
+ featureName = null,
10
+ scenarioName = null,
11
+ supressErrors = false,
12
+ errors = []
13
+ ) => {
8
14
  const uuidFn = () => (23212).toString(16);
9
15
  const builder = new AstBuilder(uuidFn);
10
16
  const matcher = new GherkinClassicTokenMatcher();
@@ -15,7 +21,7 @@ export const projectDocument = (folder, featureName = null, scenarioName = null,
15
21
  // read all the feature files in the project folder under features folder
16
22
  let featureFiles = [];
17
23
  const featuresDir = path.join(folder, "features");
18
- featureFiles = fs.readdirSync(featuresDir).filter((file) => {
24
+ featureFiles = fs.readdirSync(featuresDir, { recursive: true }).filter((file) => {
19
25
  return file.endsWith(".feature");
20
26
  });
21
27
  const documents = [];
@@ -59,4 +65,4 @@ export const projectDocument = (folder, featureName = null, scenarioName = null,
59
65
  // const documents = projectDocument(projectDir, featureName, scenarioName, true, errors);
60
66
  // const args = process.argv.slice(2);
61
67
 
62
- // console.log(JSON.stringify(documents));
68
+ // console.log(JSON.stringify(documents));
@@ -45,7 +45,10 @@ class StepsDefinitions {
45
45
  if (method && expression.keyword && pattern && expression.methodName) {
46
46
  this.steps[pattern] = {
47
47
  // file: path.join(this.isTemp ? "__temp_features" : "features", mjsFile),
48
- file: path.join(this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features", mjsFile),
48
+ file: path.join(
49
+ this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features",
50
+ mjsFile
51
+ ),
49
52
  functionName: expression.methodName,
50
53
  name: pattern,
51
54
  source: this.findKey(method.codePart, "source"),
@@ -56,7 +59,7 @@ class StepsDefinitions {
56
59
  }
57
60
  load(print = true, supressErrors = false, errors = []) {
58
61
  const mjsFiles = findFilesWithExtension(
59
- path.join(this.baseFolder, this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features"),
62
+ path.join(this.baseFolder, this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features"),
60
63
  "mjs"
61
64
  );
62
65
  // console.log({ mjsFiles });
@@ -64,7 +67,7 @@ class StepsDefinitions {
64
67
  const mjsFile = mjsFiles[i];
65
68
  const filePath = path.join(
66
69
  this.baseFolder,
67
- this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features",
70
+ this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features",
68
71
  mjsFile
69
72
  );
70
73
  const codePage = new CodePage(filePath);
@@ -6,6 +6,7 @@ import crypto from "crypto";
6
6
  import { findFilesWithExtension } from "./cucumber/steps_definitions.js";
7
7
  import fs from "fs";
8
8
  //import { spawn } from "child_process";
9
+ const __dirname = path.dirname(new URL(import.meta.url).pathname);
9
10
 
10
11
  if (process.argv.length < 3) {
11
12
  console.log("Usage: node cucumber_selector.js <projectsDir> --all");
@@ -100,8 +101,8 @@ if (process.argv.includes("--scenario-name")) {
100
101
  }
101
102
  const projectName = selectedScenario.featureFile.split(path.sep)[0];
102
103
  let deleteMjsFiles = readlineSync.keyInYN("Delete mjs file?");
104
+ let stepsPath = path.join(selectedScenario.projectsDir, projectName, "features", "step_definitions");
103
105
  if (deleteMjsFiles) {
104
- let stepsPath = path.join(selectedScenario.projectsDir, projectName, "features", "step_definitions");
105
106
  if (fs.existsSync(stepsPath)) {
106
107
  // delete all the *.mjs files
107
108
  let files = fs.readdirSync(stepsPath);
@@ -112,6 +113,21 @@ if (deleteMjsFiles) {
112
113
  }
113
114
  }
114
115
  }
116
+ // copy utils file to the steps directory
117
+ const utilsPath = path.join(__dirname, "../assets/templates/utils_template.txt");
118
+ const utilsContent = fs.readFileSync(utilsPath, "utf8");
119
+ const utilsFileName = stepsPath + "/utils.mjs";
120
+ // if stepsPath directory doesn't exist create it
121
+ if (!fs.existsSync(stepsPath)) {
122
+ fs.mkdirSync(stepsPath, { recursive: true });
123
+ }
124
+ fs.writeFileSync(utilsFileName, utilsContent, "utf8");
125
+ // copy hooks file to the steps directory
126
+ const hooksTemplateFilePath = path.join(__dirname, "../assets/templates/_hooks_template.txt");
127
+ const hooksContent = fs.readFileSync(hooksTemplateFilePath, "utf8");
128
+ const hooksFilePath = path.join(stepsPath, "_hooks.mjs");
129
+ fs.writeFileSync(hooksFilePath, hooksContent, "utf8");
130
+
115
131
  process.env.PROJECT_PATH = path.join(selectedScenario.projectsDir, projectName);
116
132
  //chanks[currentChank][index];
117
133
  console.log("Running scenario:");
@@ -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
  );
@@ -696,6 +696,7 @@ class LocalAgent {
696
696
  stepDiff: stepResult,
697
697
  });
698
698
  page.removeUnusedElements();
699
+ page.mergeSimilarElements();
699
700
  agent.scenarioReport.updateLastStep({
700
701
  code: {
701
702
  cleanFileNames: page.cleanFileNames,
@@ -809,12 +810,12 @@ class LocalAgent {
809
810
  } catch (err) {
810
811
  logger.error("Error reading memory file: " + err.message);
811
812
  }
812
- const screenshot = await agent.getScreenshot(agent.context);
813
+ //const screenshot = await agent.getScreenshot(agent.context);
813
814
  // check if the popup is open
814
815
  const popupResult = await agent.context.web.closeUnexpectedPopups(null, null);
815
816
  if (nodes) {
816
817
  nodes.memory = memory;
817
- nodes.screenshot = screenshot;
818
+ //nodes.screenshot = screenshot;
818
819
  if (popupResult.rerun === true) {
819
820
  nodes.rerun = true;
820
821
  }
@@ -971,7 +972,7 @@ class LocalAgent {
971
972
  async createNewStepLocal(
972
973
  featureName,
973
974
  cucumberStep,
974
- feature,
975
+ comments,
975
976
  userData,
976
977
  firstStep,
977
978
  previousTasks,
@@ -1087,7 +1088,7 @@ class LocalAgent {
1087
1088
  previousTasks,
1088
1089
  scenarioDocument,
1089
1090
  scenarioStepIndex,
1090
- comments: feature.comments,
1091
+ comments,
1091
1092
  featureFileText,
1092
1093
  recover,
1093
1094
  dumpConfig: this.dumpConfig,
@@ -1,37 +1,34 @@
1
- import {
2
- AstBuilder,
3
- GherkinClassicTokenMatcher,
4
- Parser,
5
- } from '@cucumber/gherkin'
6
- import { readFileSync, writeFileSync } from 'fs'
7
- import { loadArgs, showUsage, validateCLIArg } from './cli_helpers.js'
8
- let id =0;
9
- const uuidFn = () => (++id).toString(16)
10
- const builder = new AstBuilder(uuidFn)
11
- const matcher = new GherkinClassicTokenMatcher()
1
+ import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
2
+ import { readFileSync, writeFileSync } from "fs";
3
+ import { loadArgs, showUsage, validateCLIArg } from "./cli_helpers.js";
4
+ let id = 0;
5
+ const uuidFn = () => (++id).toString(16);
6
+ const builder = new AstBuilder(uuidFn);
7
+ const matcher = new GherkinClassicTokenMatcher();
12
8
 
13
- const parser = new Parser(builder, matcher)
9
+ const parser = new Parser(builder, matcher);
14
10
 
15
11
  const parseFeatureFile = (featureFilePath) => {
16
- const source = readFileSync(featureFilePath, 'utf8')
17
- const gherkinDocument = parser.parse(source)
18
- return gherkinDocument
19
- }
12
+ const source = readFileSync(featureFilePath, "utf8");
13
+ const gherkinDocument = parser.parse(source);
14
+ return gherkinDocument;
15
+ };
20
16
 
21
- const args = loadArgs()
22
- const featureFilePath = args[0]
23
- const outputFilePath = args[1]
24
- const usage = 'Usage: node parse_feature_file.js <featureFilePath> [<outputFilePath>] \n\nExample: node parseFeatureFile.js ./features/my.feature ./features/my.feature.json\n\n'
17
+ const args = loadArgs();
18
+ const featureFilePath = args[0];
19
+ const outputFilePath = args[1];
20
+ const usage =
21
+ "Usage: node parse_feature_file.js <featureFilePath> [<outputFilePath>] \n\nExample: node parseFeatureFile.js ./features/my.feature ./features/my.feature.json\n\n";
25
22
  try {
26
- validateCLIArg(featureFilePath, 'featureFilePath')
23
+ validateCLIArg(featureFilePath, "featureFilePath");
27
24
  } catch (error) {
28
- showUsage(error, usage)
25
+ showUsage(error, usage);
29
26
  }
30
- const gherkinDocument = parseFeatureFile(featureFilePath)
27
+ const gherkinDocument = parseFeatureFile(featureFilePath);
31
28
  if (outputFilePath) {
32
- writeFileSync(outputFilePath, JSON.stringify(gherkinDocument, null, 2))
29
+ writeFileSync(outputFilePath, JSON.stringify(gherkinDocument, null, 2));
33
30
  } else {
34
- console.log(JSON.stringify(gherkinDocument, null, 2))
31
+ console.log(JSON.stringify(gherkinDocument, null, 2));
35
32
  }
36
33
 
37
- export { parseFeatureFile }
34
+ export { parseFeatureFile };
@@ -1,3 +1,3 @@
1
1
  {
2
- "baseUrl": "https://google.com"
3
- }
2
+ "baseUrl": "https://google.com"
3
+ }