@dev-blinq/cucumber_client 1.0.1225-dev → 1.0.1225-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 +37 -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 +16 -15
  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 +145 -4
  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 +5 -1
  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
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "fs";
2
2
  import path from "path";
3
-
3
+ import { getDefaultPrettierConfig } from "../code_cleanup/utils.js";
4
+ import prettier from "prettier";
4
5
  export function containsScenario({ featureFileContent, scenarioName }) {
5
6
  const lines = featureFileContent.split("\n");
6
7
  for (const line of lines) {
@@ -63,7 +64,7 @@ const escapeNonPrintables = (text) => {
63
64
  export function getCommandContent(command) {
64
65
  switch (command.type) {
65
66
  case "click_element": {
66
- return `click on ${escapeNonPrintables(command.element.name)}`;
67
+ return `${command.count === 2 ? "Double click" : "Click"} on ${escapeNonPrintables(command.element.name)}`;
67
68
  }
68
69
  case "fill_element": {
69
70
  return `fill ${escapeNonPrintables(command.element.name)} with ${escapeNonPrintables(command.parameters[0])}${command.parameters[1] ? ` and press enter key` : ""}`;
@@ -81,7 +82,7 @@ export function getCommandContent(command) {
81
82
  return `verify the element ${escapeNonPrintables(command.element.name)} contains text ${escapeNonPrintables(command.parameters[0])}`;
82
83
  }
83
84
  case "context_click": {
84
- return `click on ${escapeNonPrintables(command.label)} in the context of ${escapeNonPrintables(command.value)}`;
85
+ return `${command.count === 2 ? "Double click" : "Click"} on ${escapeNonPrintables(command.label)} in the context of ${escapeNonPrintables(command.value)}`;
85
86
  }
86
87
  case "hover_element": {
87
88
  return `hover over ${escapeNonPrintables(command.element.name)}`;
@@ -98,6 +99,9 @@ export function getCommandContent(command) {
98
99
  case "verify_page_snapshot": {
99
100
  return `verify page snapshot stored in ${command.parameters[0]}`;
100
101
  }
102
+ case "parameterized_click": {
103
+ return `${command.count === 2 ? "Parameterized double click" : "Parameterized click"} on ${escapeNonPrintables(command.element.name)}`;
104
+ }
101
105
  default: {
102
106
  return "";
103
107
  }
@@ -209,14 +213,12 @@ const GherkinToObject = (gherkin) => {
209
213
  steps: [],
210
214
  };
211
215
  while (idx < lines.length && lines[idx].startsWith("@")) {
212
- skipEmptyLines();
213
216
  const tags = [...lines[idx].matchAll(/@([^@]+)/g)].map((match) => match[1].trim());
214
217
  scenario.tags.push(...(tags ?? []));
215
218
  idx++;
219
+ skipEmptyLines();
216
220
  }
217
221
 
218
- skipEmptyLines();
219
-
220
222
  if (idx < lines.length && (lines[idx].startsWith("Scenario:") || lines[idx].startsWith("Scenario Outline:"))) {
221
223
  scenario.name = lines[idx].substring(lines[idx].indexOf(":") + 1).trim();
222
224
  idx++;
@@ -239,32 +241,32 @@ const GherkinToObject = (gherkin) => {
239
241
  !lines[idx].startsWith("@")
240
242
  ) {
241
243
  const line = lines[idx++];
242
- if (line.startsWith("#")) {
243
- const comment = line;
244
- if (comment) {
245
- const command = {
246
- type: "comment",
247
- text: comment,
248
- };
249
- scenario.steps.push(command);
250
- }
251
- } else if (line.startsWith("Examples:")) {
252
- obj.hasParams = true;
253
- const command = {
244
+ let command;
245
+ if (line.startsWith("Examples:")) {
246
+ scenario.hasParams = true;
247
+ command = {
254
248
  type: "examples",
255
249
  lines: [],
256
250
  };
257
-
258
251
  while (idx < lines.length && lines[idx].startsWith("|")) {
259
252
  const line = lines[idx++];
260
253
  command.lines.push(line);
261
254
  }
262
255
  } else {
263
- scenario.steps.push({
264
- type: "step",
265
- text: line,
266
- });
256
+ if (line.startsWith("#")) {
257
+ command = {
258
+ type: "comment",
259
+ text: line,
260
+ };
261
+ } else {
262
+ command = {
263
+ type: "step",
264
+ text: line,
265
+ };
266
+ }
267
267
  }
268
+ scenario.steps.push(command);
269
+ skipEmptyLines();
268
270
  }
269
271
 
270
272
  return scenario;
@@ -273,7 +275,6 @@ const GherkinToObject = (gherkin) => {
273
275
  while (idx < lines.length) {
274
276
  const scenario = getScenario();
275
277
  if (scenario === -1) break;
276
-
277
278
  if (scenario.error) {
278
279
  return {
279
280
  error: scenario.error,
@@ -299,8 +300,7 @@ function updateExistingScenario({ featureFileContent, scenarioName, scenarioCont
299
300
  skipScenarioIndex = i;
300
301
  continue;
301
302
  }
302
- let scenarioContent = `${featureFileObject.hasParams ? "Scenario Outline" : "Scenario"}: ${scenario.name}`;
303
-
303
+ let scenarioContent = `${scenario.hasParams ? "Scenario Outline" : "Scenario"}: ${scenario.name}`;
304
304
  let tagsLine;
305
305
  if (scenario.tags?.length > 0) {
306
306
  tagsLine = `${scenario.tags.map((t) => `@${t}`).join(" ")}`;
@@ -323,7 +323,6 @@ function updateExistingScenario({ featureFileContent, scenarioName, scenarioCont
323
323
  if (skipScenarioIndex !== -1) {
324
324
  finalContent = results.join("\n") + "\n" + scenarioContent;
325
325
  }
326
-
327
326
  return finalContent;
328
327
  }
329
328
  export async function updateFeatureFile({ featureName, scenario, override, projectDir }) {
@@ -333,6 +332,8 @@ export async function updateFeatureFile({ featureName, scenario, override, proje
333
332
  { scenario },
334
333
  isFeatureFileExists ? GherkinToObject(readFileSync(featureFilePath, "utf8")) : undefined
335
334
  );
335
+ const prettierConfig = getDefaultPrettierConfig();
336
+ // Format the code using Prettier
336
337
 
337
338
  if (isFeatureFileExists) {
338
339
  const featureFileContent = readFileSync(featureFilePath, "utf8");
@@ -341,7 +342,7 @@ export async function updateFeatureFile({ featureName, scenario, override, proje
341
342
  if (!override) {
342
343
  throw new Error(`Scenario "${scenario.name}" already exists in feature "${featureName}"`);
343
344
  } else {
344
- const updatedFeatureFileContent = updateExistingScenario({
345
+ let updatedFeatureFileContent = updateExistingScenario({
345
346
  featureFileContent,
346
347
  scenarioName: scenario.name,
347
348
  scenarioContent,
@@ -349,14 +350,41 @@ export async function updateFeatureFile({ featureName, scenario, override, proje
349
350
  if (updatedFeatureFileContent === "error") {
350
351
  throw new Error(`Error while parsing feature file: Invalid gherkin`);
351
352
  }
353
+ try {
354
+ updatedFeatureFileContent = await prettier.format(updatedFeatureFileContent, {
355
+ ...prettierConfig,
356
+ parser: "gherkin",
357
+ plugins: ["prettier-plugin-gherkin"],
358
+ });
359
+ } catch (error) {
360
+ console.error("Error formatting feature file content with Prettier:", error);
361
+ }
352
362
  writeFileSync(featureFilePath, updatedFeatureFileContent);
353
363
  return;
354
364
  }
355
365
  }
356
- const updatedFeatureFileContent = featureFileContent + "\n" + scenarioContent;
366
+ let updatedFeatureFileContent = featureFileContent + "\n" + scenarioContent;
367
+ try {
368
+ updatedFeatureFileContent = await prettier.format(updatedFeatureFileContent, {
369
+ ...prettierConfig,
370
+ parser: "gherkin",
371
+ plugins: ["prettier-plugin-gherkin"],
372
+ });
373
+ } catch (error) {
374
+ console.error("Error formatting feature file content with Prettier:", error);
375
+ }
357
376
  writeFileSync(featureFilePath, updatedFeatureFileContent);
358
377
  } else {
359
- const featureFileContent = `Feature: ${featureName}\n${scenarioContent}`;
378
+ let featureFileContent = `Feature: ${featureName}\n${scenarioContent}`;
379
+ try {
380
+ featureFileContent = await prettier.format(featureFileContent, {
381
+ ...prettierConfig,
382
+ parser: "gherkin",
383
+ plugins: ["prettier-plugin-gherkin"],
384
+ });
385
+ } catch (error) {
386
+ console.error("Error formatting feature file content with Prettier:", error);
387
+ }
360
388
  writeFileSync(featureFilePath, featureFileContent);
361
389
  }
362
390
  }
@@ -8,7 +8,11 @@ const Types = {
8
8
  API: "api",
9
9
  CLICK: "click_element",
10
10
  CLICK_SIMPLE: "click_simple",
11
+ PARAMETERIZED_CLICK: "parameterized_click",
12
+ CONTEXT_CLICK: "context_click",
11
13
  NAVIGATE: "navigate",
14
+ GO_BACK: "browser_go_back",
15
+ GO_FORWARD: "browser_go_forward",
12
16
  FILL: "fill_element",
13
17
  FILL_SIMPLE: "fill_simple",
14
18
  EXECUTE: "execute_page_method",
@@ -30,6 +34,7 @@ const Types = {
30
34
  SET_COMBO: "set_combo",
31
35
  HOVER: "hover_element",
32
36
  EXTRACT_ATTRIBUTE: "extract_attribute",
37
+ EXTRACT_PROPERTY: "extract_property",
33
38
  CLOSE_PAGE: "close_page",
34
39
  SET_DATE_TIME: "set_date_time",
35
40
  SET_VIEWPORT: "set_viewport",
@@ -41,11 +46,13 @@ const Types = {
41
46
  SET_INPUT: "set_input",
42
47
  WAIT_FOR_USER_INPUT: "wait_for_user_input",
43
48
  VERIFY_ATTRIBUTE: "verify_element_attribute",
49
+ VERIFY_PROPERTY: "verify_element_property",
44
50
  FILL_UNKNOWN: "fill_unknown",
45
51
  VERIFY_TEXT_RELATED_TO_TEXT: "verify_text_in_relation",
46
52
  VERIFY_FILE_EXISTS: "verify_file_exists",
47
53
  SET_INPUT_FILES: "set_input_files",
48
54
  VERIFY_PAGE_SNAPSHOT: "verify_page_snapshot",
55
+ CONDITIONAL_WAIT: "conditional_wait",
49
56
  };
50
57
  class Recording {
51
58
  steps = [];
@@ -131,6 +131,10 @@ const runCucumber = async (
131
131
  } else if (!process.env.NODE_ENV_BLINQ) {
132
132
  serviceUrl = "https://logic.blinq.io";
133
133
  logger.info("Running in prod mode");
134
+ } else if (process.env.WHITELABEL === "true") {
135
+ // if it's a whitelabel it will use "api" instead of "logic"
136
+ serviceUrl = process.env.NODE_ENV_BLINQ;
137
+ logger.info("Running in custom mode: " + serviceUrl);
134
138
  } else {
135
139
  serviceUrl = process.env.NODE_ENV_BLINQ.replace("api", "logic");
136
140
  logger.info("Running in custom mode: " + serviceUrl);
@@ -355,7 +359,7 @@ const runCucumber = async (
355
359
  await aiAgent.createNewStepLocal(
356
360
  featureName,
357
361
  cucumberStep,
358
- feature,
362
+ feature.comments,
359
363
  userData,
360
364
  first,
361
365
  previousTasks,
@@ -33,17 +33,12 @@ const findNextIdInFolder = (folder) => {
33
33
  // get temp file path from --temp-file arg
34
34
  const getTempFilePath = () => {
35
35
  let tempFilePath = null;
36
-
37
36
  for (const arg of process.argv) {
38
37
  const [key, path] = arg.split("=");
39
38
  if (key === "--temp-file" && !!path) {
40
39
  tempFilePath = path;
41
40
  }
42
41
  }
43
-
44
- if (tempFilePath === null) {
45
- tempFilePath = process.env.TEMP_FILE_PATH;
46
- }
47
42
  return tempFilePath;
48
43
  };
49
44
 
@@ -322,7 +317,8 @@ class ScenarioReport {
322
317
  }
323
318
  const reportLink = baseUrl + "/" + projectId + "/scenario-report/" + scenarioId;
324
319
  try {
325
- publishReportLinkToGuacServer(reportLink);
320
+ const status = getJsonReport(this).result.status;
321
+ publishReportLinkToGuacServer(reportLink, status === "PASSED");
326
322
  } catch (err) {
327
323
  logger.error(`Failed to publish report link to guac server: ${err}`);
328
324
  }
@@ -481,11 +477,11 @@ class ScenarioReport {
481
477
  }
482
478
  }
483
479
  }
484
- function publishReportLinkToGuacServer(reportLink) {
480
+ function publishReportLinkToGuacServer(reportLink, status) {
485
481
  try {
486
482
  if (existsSync("/tmp/report_publish.sh")) {
487
483
  const execAsync = promisify(exec);
488
- execAsync("sh /tmp/report_publish.sh " + reportLink);
484
+ execAsync("sh /tmp/report_publish.sh " + reportLink + " " + status);
489
485
  }
490
486
  } catch (error) {
491
487
  logger.error("Error while publishing report link to Guac server: " + error);
@@ -267,7 +267,6 @@ try {
267
267
  }
268
268
  if (validatePath !== "null") {
269
269
  await prevrunResult.context.stable.verifyPagePath(validatePath);
270
-
271
270
  }
272
271
  //finalCompare = true;
273
272
  } catch (e) {
package/bin/index.js CHANGED
@@ -13,4 +13,5 @@ export * from "./client/cucumber/feature_data.js";
13
13
  export * from "./client/cucumber/steps_definitions.js";
14
14
  export * from "./client/profiler.js";
15
15
  export * from "./client/code_cleanup/utils.js";
16
+
16
17
  export * from "./client/code_cleanup/find_step_definition_references.js";
package/package.json CHANGED
@@ -1,22 +1,26 @@
1
1
  {
2
2
  "name": "@dev-blinq/cucumber_client",
3
- "version": "1.0.1225-dev",
4
- "description": "",
3
+ "version": "1.0.1225-stage",
4
+ "description": " ",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
7
7
  "type": "module",
8
8
  "scripts": {
9
- "pack": "mkdir build && mkdir build/bin && cp -R ./src/* ./build/bin && cp ./package.json ./build",
9
+ "pack": "npm run bundle && mkdir build && mkdir build/bin && cp -R ./src/* ./build/bin && cp ./package.json ./build",
10
10
  "test": "node ./test/test.js",
11
11
  "tests_prod": "rm -rf results.json && cross-env NODE_ENV_BLINQ=prod TOKEN=xxx npx mocha --parallel --jobs=10 ./tests",
12
12
  "tests": "node ./multi_test_runner.js",
13
13
  "lint": "eslint ./src/**/*.js",
14
14
  "clean": "rm -rf ./build",
15
15
  "build": "npm run clean && npm run pack",
16
+ "build:watch": "npx nodemon --watch src/client --exec 'npm run build'",
16
17
  "update_logic": "rm -rf ../logic/node_modules/@dev-blinq && mkdir ../logic/node_modules/@dev-blinq && mkdir ../logic/node_modules/@dev-blinq/cucumber_client && mkdir ../logic/node_modules/@dev-blinq/cucumber_client/bin && mkdir ../logic/node_modules/@dev-blinq/cucumber_client/node_modules && cp -R ./build/* ../logic/node_modules/@dev-blinq/cucumber_client/bin && cp -R ./node_modules/* ../logic/node_modules/@dev-blinq/cucumber_client/node_modules && cp ./package.json ../logic/node_modules/@dev-blinq/cucumber_client/",
17
18
  "version-bump": "npm version prepatch --preid=dev",
18
- "scripts_regression": "cross-env HEADLESS=true npx mocha --bail --parallel --jobs=12 ./scripts_regression/ --timeout 120000",
19
- "runtime_reg": "cross-env HEADLESS=true npx mocha --bail --parallel --jobs=12 ./runtime_regression/ --timeout 4800000"
19
+ "scripts_regression": "npm run bundle && cross-env HEADLESS=true npx mocha --bail --parallel --jobs=12 ./scripts_regression/ --timeout 120000",
20
+ "runtime_reg": "npm run bundle && cross-env HEADLESS=true npx mocha --bail --parallel --jobs=12 ./runtime_regression/ --timeout 4800000",
21
+ "bundle": "npx tsup",
22
+ "bundle:watch": "npx tsup --watch",
23
+ "regenerate_baselines": "rm -rf ./scripts_regression/locators_baseline/* ./scripts_regression/commands_baseline/* && npm run scripts_regression && npm run scripts_regression"
20
24
  },
21
25
  "author": "",
22
26
  "license": "ISC",
@@ -26,9 +30,9 @@
26
30
  "@babel/traverse": "^7.27.1",
27
31
  "@babel/types": "^7.27.1",
28
32
  "@cucumber/tag-expressions": "^6.1.1",
29
- "@dev-blinq/cucumber-js": "1.0.172-dev",
33
+ "@dev-blinq/cucumber-js": "1.0.95-stage",
30
34
  "@faker-js/faker": "^8.1.0",
31
- "automation_model": "1.0.737-dev",
35
+ "automation_model": "1.0.716-stage",
32
36
  "axios": "^1.7.4",
33
37
  "chokidar": "^3.6.0",
34
38
  "create-require": "^1.1.1",
@@ -43,6 +47,7 @@
43
47
  "patch-package": "^8.0.0",
44
48
  "playwright-core": "1.52.0",
45
49
  "prettier": "^3.2.5",
50
+ "prettier-plugin-gherkin": "^3.1.2",
46
51
  "pureimage": "0.4.9",
47
52
  "socket.io": "^4.7.5",
48
53
  "socket.io-client": "^4.7.5",
@@ -59,6 +64,8 @@
59
64
  "eslint": "^8.50.0",
60
65
  "http-server": "^14.1.1",
61
66
  "mocha": "^10.2.0",
62
- "readline-sync": "^1.4.10"
67
+ "readline-sync": "^1.4.10",
68
+ "tsup": "^8.5.0",
69
+ "typescript": "^5.8.3"
63
70
  }
64
71
  }