@dev-blinq/cucumber_client 1.0.1263-dev → 1.0.1263-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 (38) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +159 -14068
  2. package/bin/assets/preload/recorderv3.js +3 -1
  3. package/bin/assets/scripts/dom_parent.js +26 -0
  4. package/bin/assets/scripts/recorder.js +8 -4
  5. package/bin/assets/scripts/unique_locators.js +847 -547
  6. package/bin/assets/templates/_hooks_template.txt +37 -0
  7. package/bin/assets/templates/page_template.txt +2 -16
  8. package/bin/assets/templates/utils_template.txt +44 -71
  9. package/bin/client/apiTest/apiTest.js +6 -0
  10. package/bin/client/cli_helpers.js +11 -13
  11. package/bin/client/code_cleanup/utils.js +5 -1
  12. package/bin/client/code_gen/code_inversion.js +61 -4
  13. package/bin/client/code_gen/page_reflection.js +838 -899
  14. package/bin/client/code_gen/playwright_codeget.js +52 -13
  15. package/bin/client/cucumber/feature.js +89 -27
  16. package/bin/client/cucumber/project_to_document.js +1 -1
  17. package/bin/client/cucumber/steps_definitions.js +84 -76
  18. package/bin/client/cucumber_selector.js +17 -1
  19. package/bin/client/local_agent.js +3 -3
  20. package/bin/client/project.js +186 -196
  21. package/bin/client/recorderv3/bvt_recorder.js +290 -80
  22. package/bin/client/recorderv3/implemented_steps.js +74 -16
  23. package/bin/client/recorderv3/index.js +47 -25
  24. package/bin/client/recorderv3/network.js +299 -0
  25. package/bin/client/recorderv3/services.js +4 -16
  26. package/bin/client/recorderv3/step_runner.js +331 -67
  27. package/bin/client/recorderv3/step_utils.js +152 -5
  28. package/bin/client/recorderv3/update_feature.js +32 -30
  29. package/bin/client/recording.js +3 -0
  30. package/bin/client/run_cucumber.js +5 -1
  31. package/bin/client/scenario_report.js +0 -5
  32. package/bin/client/test_scenario.js +0 -1
  33. package/bin/client/utils/socket_logger.js +132 -0
  34. package/bin/index.js +1 -0
  35. package/bin/logger.js +3 -2
  36. package/bin/min/consoleApi.min.cjs +2 -3
  37. package/bin/min/injectedScript.min.cjs +16 -16
  38. package/package.json +24 -14
@@ -0,0 +1,37 @@
1
+ import {
2
+ After,
3
+ setDefaultTimeout,
4
+ Before,
5
+ AfterStep,
6
+ BeforeStep,
7
+ } from "@dev-blinq/cucumber-js";
8
+ import { closeContext, initContext, navigate, executeBrunoRequest, verifyFileExists } from "automation_model";
9
+ setDefaultTimeout(60 * 1000);
10
+
11
+ const url = null;
12
+
13
+ const elements = {};
14
+
15
+ let context = null;
16
+ Before(async function (scenario) {
17
+ context = await initContext(url, false, false, this);
18
+ await navigate(url);
19
+ await context.web.beforeScenario(this, scenario);
20
+ });
21
+ After(async function (scenario) {
22
+ await context.web.afterScenario(this, scenario);
23
+ await closeContext();
24
+ context = null;
25
+ });
26
+
27
+ BeforeStep(async function (step) {
28
+ if (context) {
29
+ await context.web.beforeStep(this, step);
30
+ }
31
+ });
32
+
33
+ AfterStep(async function (step) {
34
+ if (context) {
35
+ await context.web.afterStep(this, step);
36
+ }
37
+ });
@@ -1,20 +1,6 @@
1
- import { Given, When, Then, After, setDefaultTimeout, Before} from "@dev-blinq/cucumber-js";
2
- import { closeContext, initContext, navigate } from "automation_model";
3
- setDefaultTimeout(60 * 1000);
4
-
5
- const path = null;
1
+ import { Given, When, Then, After, setDefaultTimeout, Before } from "@dev-blinq/cucumber-js";
2
+ import { closeContext, initContext, navigate, TestContext as context } from "automation_model";
6
3
 
7
4
  const elements = {
8
5
  };
9
6
 
10
- let context = null;
11
- Before(async function () {
12
- if (!context) {
13
- context = await initContext(path, false, false, this);
14
- }
15
- await navigate(path);
16
- });
17
- After(async function () {
18
- await closeContext();
19
- context = null;
20
- });
@@ -2,55 +2,10 @@ import {
2
2
  Given,
3
3
  When,
4
4
  Then,
5
- After,
6
- setDefaultTimeout,
7
- Before,
8
5
  defineStep,
9
- AfterStep,
10
- BeforeStep,
11
6
  } from "@dev-blinq/cucumber-js";
12
- import { closeContext, initContext, navigate, executeBrunoRequest, verifyFileExists } from "automation_model";
7
+ import { closeContext, initContext, navigate, executeBrunoRequest, verifyFileExists, TestContext as context } from "automation_model";
13
8
  import path from "path";
14
- setDefaultTimeout(60 * 1000);
15
-
16
- const url = null;
17
-
18
- const elements = {};
19
-
20
- let context = null;
21
- Before(async function (scenario) {
22
- if (!context) {
23
- context = await initContext(url, false, false, this);
24
- }
25
- await navigate(url);
26
- await context.web.beforeScenario(this, scenario);
27
- });
28
- After(async function (scenario) {
29
- await context.web.afterScenario(this, scenario);
30
- await closeContext();
31
- context = null;
32
- });
33
-
34
- BeforeStep(async function (step) {
35
- if (context) {
36
- await context.web.beforeStep(this, step);
37
- }
38
- });
39
-
40
- AfterStep(async function (step) {
41
- if (context) {
42
- await context.web.afterStep(this, step);
43
- }
44
- });
45
-
46
- /**
47
- * Load test data for a user
48
- * @param {string} user name of the user to load test data for
49
- * @protect
50
- */
51
- async function loadUserData(user) {
52
- await context.web.loadTestDataAsync("users", user, this);
53
- }
54
9
 
55
10
  /**
56
11
  * Verify text exsits in page
@@ -60,7 +15,6 @@ async function loadUserData(user) {
60
15
  async function verifyTextExistsInPage(text) {
61
16
  await context.web.verifyTextExistInPage(text, null, this);
62
17
  }
63
-
64
18
  Then("Verify the text {string} can be found in the page", verifyTextExistsInPage);
65
19
 
66
20
  /**
@@ -87,6 +41,7 @@ async function fillElement(elementDescription, value) {
87
41
  }
88
42
  When("fill {string} with {string}", fillElement);
89
43
  When("Fill {string} with {string}", fillElement);
44
+
90
45
  /**
91
46
  * Verify text does not exist in page
92
47
  * @param {string} text the text to verify does not exist in page
@@ -107,6 +62,24 @@ async function navigateTo(url) {
107
62
  }
108
63
  When("Navigate to {string}", navigateTo);
109
64
 
65
+ /**
66
+ * Navigate to the current page
67
+ * @protect
68
+ */
69
+ async function browserNavigateBack() {
70
+ await context.web.goBack({}, this);
71
+ }
72
+ Then("Browser navigate back", browserNavigateBack);
73
+
74
+ /**
75
+ * Navigate forward in browser history
76
+ * @protect
77
+ */
78
+ async function browserNavigateForward() {
79
+ await context.web.goForward({}, this);
80
+ }
81
+ Then("Browser navigate forward", browserNavigateForward);
82
+
110
83
  /**
111
84
  * Store browser session "<path>"
112
85
  * @param {string} filePath the file path or empty to store in the test data file
@@ -141,6 +114,7 @@ Then(
141
114
  "Identify the text {string}, climb {string} levels in the page, validate text {string} can be found in the context",
142
115
  verifyTextRelatedToText
143
116
  );
117
+
144
118
  /**
145
119
  * execute bruno single request given the bruno project is placed in a folder called bruno under the root of the cucumber project
146
120
  * @requestName the name of the bruno request file
@@ -149,7 +123,6 @@ Then(
149
123
  async function runBrunoRequest(requestName) {
150
124
  await executeBrunoRequest(requestName, {}, context, this);
151
125
  }
152
-
153
126
  When("Bruno - {string}", runBrunoRequest);
154
127
  When("bruno - {string}", runBrunoRequest);
155
128
 
@@ -163,41 +136,41 @@ async function verify_the_downloaded_file_exists(fileName) {
163
136
  const downloadFile = path.join(downloadFolder, fileName);
164
137
  await verifyFileExists(downloadFile, {}, context, this);
165
138
  }
166
-
167
139
  Then("Verify the file {string} exists", { timeout: 60000 }, verify_the_downloaded_file_exists);
168
140
 
169
- When("Noop", async function(){})
170
-
171
141
  /**
172
- * Verify the page url is "<url>"
173
- * @param {string} url URL to be verified against current URL
174
- * @protect
175
- */
142
+ * Noop step for running only hooks
143
+ */
144
+ When("Noop", async function () {});
176
145
 
177
- async function verify_page_url(url){
178
- await context.web.verifyPagePath(url,{}, this);
146
+ /**
147
+ * Verify the page url is "<url>"
148
+ * @param {string} url URL to be verified against current URL
149
+ * @protect
150
+ */
151
+ async function verify_page_url(url) {
152
+ await context.web.verifyPagePath(url, {}, this);
179
153
  }
180
154
  Then("Verify the page url is {string}", verify_page_url);
181
155
 
182
156
  /**
183
- * Verify the page title is "<title>"
184
- * @param {string} title Title to be verified against current Title
185
- * @protect
186
- */
187
- async function verify_page_title(title){
157
+ * Verify the page title is "<title>"
158
+ * @param {string} title Title to be verified against current Title
159
+ * @protect
160
+ */
161
+ async function verify_page_title(title) {
188
162
  await context.web.verifyPageTitle(title, {}, this);
189
163
  }
190
- Then("Verify the page title is {string}",verify_page_title)
164
+ Then("Verify the page title is {string}", verify_page_title);
191
165
 
192
166
  /**
193
- * Explicit wait/sleep function that pauses execution for a specified duration
194
- * @param {duration} - Duration to sleep in milliseconds (default: 1000ms)
195
- * @param {options} - Optional configuration object
196
- * @param {world} - Optional world context
197
- * @returns Promise that resolves after the specified duration
198
- */
167
+ * Explicit wait/sleep function that pauses execution for a specified duration
168
+ * @param {duration} - Duration to sleep in milliseconds (default: 1000ms)
169
+ * @param {options} - Optional configuration object
170
+ * @param {world} - Optional world context
171
+ * @returns Promise that resolves after the specified duration
172
+ */
199
173
  async function sleep(duration) {
200
174
  await context.web.sleep(duration, {}, null);
201
175
  }
202
-
203
- Then("Sleep for {string} ms", {timeout: -1}, sleep);
176
+ Then("Sleep for {string} ms", { timeout: -1 }, sleep);
@@ -75,6 +75,12 @@ class ApiTest {
75
75
  const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
76
76
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
77
77
  writeFileSync(utilsFilePath, utilsContent, "utf8");
78
+ const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
79
+ if (existsSync(hooksTemplateFilePath)) {
80
+ const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
81
+ const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
82
+ writeFileSync(hooksFilePath, hooksContent, "utf8");
83
+ }
78
84
  } catch (error) {
79
85
  console.log("error", error);
80
86
  }
@@ -1,17 +1,15 @@
1
-
2
1
  const validateCLIArg = (arg, name) => {
3
2
  if (!arg) {
4
- throw new Error(`${name} is required`)
3
+ throw new Error(`${name} is required`);
5
4
  }
6
- }
5
+ };
7
6
  const showUsage = (error, usage) => {
8
- console.error(error.message)
9
- console.info(usage)
10
- process.exit(1)
11
- }
12
- const loadArgs = ()=>{
13
- const args = process.argv.slice(2)
14
- return args
15
- }
16
-
17
- export { validateCLIArg, loadArgs, showUsage };
7
+ console.error(error.message);
8
+ console.info(usage);
9
+ process.exit(1);
10
+ };
11
+ const loadArgs = () => {
12
+ const args = process.argv.slice(2);
13
+ return args;
14
+ };
15
+ export { validateCLIArg, loadArgs, showUsage };
@@ -7,10 +7,11 @@ const { default: traverse } = pkg;
7
7
  import pkg1 from "@babel/generator";
8
8
  const { default: generate } = pkg1;
9
9
  import * as t from "@babel/types";
10
-
11
10
  import { CucumberExpression, ParameterTypeRegistry } from "@cucumber/cucumber-expressions";
12
11
  import { existsSync, readFileSync, writeFileSync } from "fs";
13
12
  import { getAiConfig } from "../code_gen/page_reflection.js";
13
+ import socketLogger from "../utils/socket_logger.js";
14
+
14
15
  const STEP_KEYWORDS = new Set(["Given", "When", "Then"]);
15
16
 
16
17
  /**
@@ -307,14 +308,17 @@ export function getDefaultPrettierConfig() {
307
308
  const configContent = readFileSync(prettierConfigPath, "utf-8");
308
309
  prettierConfig = JSON.parse(configContent);
309
310
  } catch (error) {
311
+ socketLogger.error("Error parsing Prettier config file", error);
310
312
  console.error(`Error parsing Prettier config file: ${error}`);
311
313
  }
312
314
  } else {
313
315
  // save the default config to .prettierrc
314
316
  try {
315
317
  writeFileSync(prettierConfigPath, JSON.stringify(prettierConfig, null, 2), "utf-8");
318
+ socketLogger.info(`Created default Prettier config at ${prettierConfigPath}`);
316
319
  console.log(`Created default Prettier config at ${prettierConfigPath}`);
317
320
  } catch (error) {
321
+ socketLogger.error(`Error writing Prettier config file: ${error}`);
318
322
  console.error(`Error writing Prettier config file: ${error}`);
319
323
  }
320
324
  }
@@ -310,6 +310,14 @@ const invertStableCommand = (call, elements, stepParams) => {
310
310
  break;
311
311
  }
312
312
 
313
+ case "goBack":
314
+ step.type = Types.GO_BACK;
315
+ break;
316
+
317
+ case "goForward":
318
+ step.type = Types.GO_FORWARD;
319
+ break;
320
+
313
321
  case "reloadPage":
314
322
  step.type = Types.RELOAD;
315
323
  break;
@@ -366,10 +374,15 @@ const invertStableCommand = (call, elements, stepParams) => {
366
374
  }
367
375
  case "verifyTextRelatedToText": {
368
376
  step.type = Types.VERIFY_TEXT_RELATED_TO_TEXT;
369
- const textAnchor = parseDataSource(call.arguments[0], stepParams);
370
- const climb = parseDataSource(call.arguments[1], stepParams);
371
- const textToVerify = parseDataSource(call.arguments[2], stepParams);
372
- step.parameters = [textAnchor.value, +climb.value, textToVerify.value];
377
+ const textAnchorParse = parseDataSource(call.arguments[0], stepParams);
378
+ const textAnchor =
379
+ textAnchorParse.type === "literal" ? textAnchorParse.value : toVariableName(textAnchorParse.dataKey);
380
+ const climbParse = parseDataSource(call.arguments[1], stepParams);
381
+ const climb = climbParse.type === "literal" ? climbParse.value : toVariableName(climbParse.dataKey);
382
+ const textToVerifyParse = parseDataSource(call.arguments[2], stepParams);
383
+ const textToVerify =
384
+ textToVerifyParse.type === "literal" ? textToVerifyParse.value : toVariableName(textToVerifyParse.dataKey);
385
+ step.parameters = [textAnchor, climb, textToVerify];
373
386
  break;
374
387
  }
375
388
  case "setInputFiles": {
@@ -418,6 +431,50 @@ const invertStableCommand = (call, elements, stepParams) => {
418
431
  }
419
432
  break;
420
433
  }
434
+ case "verifyProperty": {
435
+ step.type = Types.VERIFY_PROPERTY;
436
+ step.element = extractElement(call.arguments[0]);
437
+ const property = call.arguments[1].value;
438
+ const value = parseDataSource(call.arguments[2], stepParams);
439
+ if (value.type === "literal") {
440
+ step.parameters = [property, value.value];
441
+ } else {
442
+ step.dataSource = value.dataSource;
443
+ step.dataKey = value.dataKey;
444
+ step.parameters = [property, toVariableName(value.dataKey)];
445
+ }
446
+ break;
447
+ }
448
+ case "extractProperty": {
449
+ step.type = Types.EXTRACT_PROPERTY;
450
+ step.element = extractElement(call.arguments[0]);
451
+ const attribute = call.arguments[1].value;
452
+ const variable = parseDataSource(call.arguments[2], stepParams);
453
+ if (variable.type === "literal") {
454
+ step.parameters = [attribute, variable.value];
455
+ } else {
456
+ step.dataSource = variable.dataSource;
457
+ step.dataKey = variable.dataKey;
458
+ step.parameters = [attribute, toVariableName(variable.dataKey)];
459
+ }
460
+ if (call.arguments[4].type === "ObjectExpression") {
461
+ const properties = call.arguments[4].properties;
462
+ const regexProp = properties.findIndex((prop) => prop.key.name === "regex");
463
+ if (regexProp !== -1) {
464
+ const regex = properties[regexProp].value.value;
465
+ step.regex = regex;
466
+ }
467
+ const trimSpacesProp = properties.findIndex((prop) => prop.key.name === "trimSpaces");
468
+ if (trimSpacesProp !== -1) {
469
+ const trimSpaces = properties[trimSpacesProp].value.value;
470
+ step.trimSpaces = trimSpaces;
471
+ }
472
+ } else {
473
+ step.regex = "";
474
+ step.trimSpaces = false;
475
+ }
476
+ break;
477
+ }
421
478
  default:
422
479
  return; // Skip if no matching method
423
480
  }