@dev-blinq/cucumber_client 1.0.1195-dev → 1.0.1197-dev

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.
@@ -697,6 +697,24 @@ function querySelector_shadow_aware(element, selector) {
697
697
  }
698
698
  window.querySelector_shadow_aware = querySelector_shadow_aware;
699
699
 
700
+ function findElement(css, scope = document) {
701
+ // return null if no css provided
702
+ if (!css) return null;
703
+ const selector = window.__injectedScript.parseSelector(css);
704
+ try {
705
+ const elements = window.__injectedScript.querySelectorAll(selector, scope, true);
706
+ if (!elements) return null;
707
+ if (elements.length === 1) {
708
+ return elements[0];
709
+ } else {
710
+ return elements;
711
+ }
712
+ } catch (error) {
713
+ return null;
714
+ }
715
+ }
716
+ window.findElement = findElement;
717
+
700
718
  // export {
701
719
  // getUniqueLocators,
702
720
  // getBasicInfo,
@@ -1355,7 +1355,8 @@ function countAccurances(css, scope = document) {
1355
1355
  }
1356
1356
  window.countAccurances = countAccurances;
1357
1357
  function findElement(css, scope = document) {
1358
- // return null if no css provided
1358
+ // Note: If you change anything iin this function
1359
+ // please change it in locators.js
1359
1360
  if (!css) return null;
1360
1361
  const selector = window.__injectedScript.parseSelector(css);
1361
1362
  try {
@@ -289,7 +289,7 @@ class Feature {
289
289
  }
290
290
  return scenarios;
291
291
  }
292
- generateStracture(stepsDefinitions, fileName, scenarioName = null) {
292
+ generateStracture(stepsDefinitions, fileName, scenarioName = null, errorsInParsing = [], parseFailedFiles = false) {
293
293
  const document = {};
294
294
  document.featureName = this.feature.name;
295
295
  document.fileName = fileName;
@@ -311,6 +311,29 @@ class Feature {
311
311
  stepInfo.template = step.getTemplate();
312
312
  scenarioInfo.steps.push(stepInfo);
313
313
  const stepDef = stepsDefinitions.findMatchingStep(stepInfo.template);
314
+ if (!stepDef && parseFailedFiles) {
315
+ //Check if the stepName exists in one of the failedToParseFiles
316
+ try {
317
+ const stepName = stepsDefinitions._stepNameToTemplate(stepInfo.template);
318
+ if (errorsInParsing && errorsInParsing.length > 0) {
319
+ for (let k = 0; k < errorsInParsing.length; k++) {
320
+ const failedFile = errorsInParsing[k].file;
321
+ //Read File content, and check if the stepName exists in the file
322
+ const fileContent = fs.readFileSync(failedFile, "utf8");
323
+ if (fileContent.includes(stepName)) {
324
+ stepInfo.implemented = true;
325
+ stepInfo.mjsFile = failedFile;
326
+ stepInfo.functionName = stepName;
327
+ stepInfo.error = errorsInParsing[k].error;
328
+ console.log(JSON.stringify(stepInfo));
329
+ break;
330
+ }
331
+ }
332
+ }
333
+ } catch (e) {
334
+ console.error(`Error while checking step ${stepInfo.template} in failed files:`, e);
335
+ }
336
+ }
314
337
  stepInfo.implemented = stepDef ? true : false;
315
338
  if (stepDef) {
316
339
  stepInfo.mjsFile = stepDef.file;
@@ -4,14 +4,14 @@ 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) => {
7
+ export const projectDocument = (folder, featureName = null, scenarioName = null, supressErrors = false, errors = []) => {
8
8
  const uuidFn = () => (23212).toString(16);
9
9
  const builder = new AstBuilder(uuidFn);
10
10
  const matcher = new GherkinClassicTokenMatcher();
11
11
  const parser = new Parser(builder, matcher);
12
- const stepDefinition = new StepsDefinitions(folder);
13
- stepDefinition.load(false);
14
-
12
+ // load the step definitions from the project folder under steps folder
13
+ const stepDefinition = new StepsDefinitions(folder, false);
14
+ stepDefinition.load(false, supressErrors, errors);
15
15
  // read all the feature files in the project folder under features folder
16
16
  let featureFiles = [];
17
17
  const featuresDir = path.join(folder, "features");
@@ -27,7 +27,7 @@ export const projectDocument = (folder, featureName = null, scenarioName = null)
27
27
  if (featureName && feature.feature.name !== featureName && featureFile !== featureName) {
28
28
  continue;
29
29
  }
30
- const document = feature.generateStracture(stepDefinition, featureFile, scenarioName);
30
+ const document = feature.generateStracture(stepDefinition, featureFile, scenarioName, errors, supressErrors);
31
31
  if (scenarioName && document.scenarios.length === 0) {
32
32
  continue;
33
33
  }
@@ -55,7 +55,8 @@ export const projectDocument = (folder, featureName = null, scenarioName = null)
55
55
  // scenarioName = arg.substring(9);
56
56
  // }
57
57
  // }
58
- // const documents = projectDocument(projectDir, featureName, scenarioName);
58
+ // let errors = [];
59
+ // const documents = projectDocument(projectDir, featureName, scenarioName, true, errors);
59
60
  // const args = process.argv.slice(2);
60
61
 
61
- // console.log(JSON.stringify(projectDocument(args[0])));
62
+ // console.log(JSON.stringify(documents));
@@ -54,7 +54,7 @@ class StepsDefinitions {
54
54
  }
55
55
  }
56
56
  }
57
- load(print = true) {
57
+ load(print = true, supressErrors = false, errors = []) {
58
58
  const mjsFiles = findFilesWithExtension(
59
59
  path.join(this.baseFolder, this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features"),
60
60
  "mjs"
@@ -62,18 +62,25 @@ class StepsDefinitions {
62
62
  // console.log({ mjsFiles });
63
63
  for (let i = 0; i < mjsFiles.length; i++) {
64
64
  const mjsFile = mjsFiles[i];
65
- const codePage = new CodePage(
66
- path.join(
67
- this.baseFolder,
68
- this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features",
69
- mjsFile
70
- )
71
- );
65
+ console.log("loading step definitions from file", mjsFile);
66
+ const filePath = path.join(
67
+ this.baseFolder,
68
+ this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features",
69
+ mjsFile);
70
+ const codePage = new CodePage(filePath);
72
71
  try {
73
72
  codePage.generateModel();
74
73
  } catch (error) {
75
74
  logger.info("unable to generate model for file", mjsFile);
76
- throw error;
75
+ if(supressErrors) {
76
+ errors.push({
77
+ file: filePath,
78
+ error: error.message,
79
+ });
80
+ }
81
+ else{
82
+ throw error;
83
+ }
77
84
  }
78
85
  this.initPage(codePage, mjsFile);
79
86
  }
@@ -378,6 +378,81 @@ const findLocatorByText = async (context, texts, backendNodeId) => {
378
378
  await _closeCDP();
379
379
  }
380
380
  };
381
+ function classifyLocatorsByStrategy(locators) {
382
+ const locs = [];
383
+
384
+ const allStrategyLocators = {
385
+ basic: [],
386
+ no_text: [],
387
+ ignore_digit: [],
388
+ context: [],
389
+ strategy: "basic",
390
+ };
391
+
392
+ const regexRegex = /\/.*\\[d][+].*\//;
393
+ for (const locator of locators) {
394
+ if (locator.mode) {
395
+ const { mode, ...rest } = locator;
396
+ locs.push(rest);
397
+ allStrategyLocators[locator.mode.toLowerCase()].push(rest);
398
+ } else if (locator.text && locator.text.length > 0) {
399
+ locs.push({
400
+ ...locator,
401
+ mode: "CONTEXT",
402
+ });
403
+ allStrategyLocators["context"].push(locator);
404
+ } else if (regexRegex.test(locator.css)) {
405
+ locs.push({
406
+ ...locator,
407
+ mode: "IGNORE_DIGIT",
408
+ });
409
+ allStrategyLocators["ignore_digit"].push(locator);
410
+ } else if (locator.css.includes("text=") || locator.css.includes("[name=") || locator.css.includes("label=")) {
411
+ locs.push(locator);
412
+ allStrategyLocators["basic"].push(locator);
413
+ } else {
414
+ locs.push({
415
+ ...locator,
416
+ mode: "NO_TEXT",
417
+ });
418
+ allStrategyLocators["no_text"].push(locator);
419
+ }
420
+ }
421
+ if (allStrategyLocators["context"].length > 0) {
422
+ allStrategyLocators["strategy"] = "context";
423
+ } else if (allStrategyLocators["basic"].length === 0 && allStrategyLocators["no_text"].length > 0) {
424
+ allStrategyLocators["strategy"] = "no_text";
425
+ }
426
+
427
+ return {
428
+ locators: locs,
429
+ allStrategyLocators,
430
+ };
431
+ }
432
+
433
+ async function filterLocators(cssLocators, elObjectId) {
434
+ const kept = [];
435
+ for (const selector of cssLocators) {
436
+ // call window.findElement(selector) === el
437
+ const { result } = await cdp.send("Runtime.callFunctionOn", {
438
+ functionDeclaration: `
439
+ function(element,selector) {
440
+ console.log("filterLocators in progress");
441
+ console.log(element,selector);
442
+ return window.findElement(selector) === element;
443
+ }
444
+ `,
445
+ objectId: elObjectId,
446
+ arguments: [{ objectId: elObjectId }, { value: selector }],
447
+ returnByValue: true,
448
+ });
449
+
450
+ if (result.value) {
451
+ kept.push(selector);
452
+ }
453
+ }
454
+ return kept;
455
+ }
381
456
 
382
457
  async function getRecorderLocators(context, backendNodeId) {
383
458
  if (!cdp) {
@@ -388,7 +463,8 @@ async function getRecorderLocators(context, backendNodeId) {
388
463
  backendNodeId: backendNodeId,
389
464
  });
390
465
  const objectId = domNode.object.objectId;
391
- let res = await cdp.send("Runtime.callFunctionOn", {
466
+ let locators = [];
467
+ locators = await cdp.send("Runtime.callFunctionOn", {
392
468
  functionDeclaration:
393
469
  "function(element,options) { console.log(element,options); return window.getPWLocators(element,options); }",
394
470
  objectId: objectId,
@@ -405,8 +481,87 @@ async function getRecorderLocators(context, backendNodeId) {
405
481
  ],
406
482
  returnByValue: true,
407
483
  });
484
+ if (!locators || !locators.result || !locators.result.value) {
485
+ locators = [];
486
+ } else {
487
+ locators = locators.result?.value;
488
+ }
489
+ let cssLocators = [];
490
+ try {
491
+ let origenCss = await cdp.send("Runtime.callFunctionOn", {
492
+ functionDeclaration:
493
+ "function(element) { console.log(element); return window.generateUniqueCSSSelector(element); }",
494
+ objectId: objectId,
495
+ arguments: [
496
+ {
497
+ objectId: objectId,
498
+ },
499
+ ],
500
+ returnByValue: true,
501
+ });
502
+ origenCss = origenCss.result?.value;
503
+ if (origenCss) {
504
+ cssLocators.push(origenCss);
505
+ }
506
+ let noClasses = await cdp.send("Runtime.callFunctionOn", {
507
+ functionDeclaration:
508
+ "function(element,options) { console.log(element,options); return window.CssSelectorGenerator.getCssSelector(element,options); }",
509
+ objectId: objectId,
510
+ arguments: [
511
+ {
512
+ objectId: objectId,
513
+ },
514
+ {
515
+ value: {
516
+ blacklist: [/^(?!.*h\d).*?\d.*/, /\[style/, /\[data-input-id/, /\[blinq-container/],
517
+ combineWithinSelector: true,
518
+ combineBetweenSelectors: true,
519
+ selectors: ["id", "attribute", "tag", "nthchild", "nthoftype"],
520
+ maxCombinations: 100,
521
+ },
522
+ },
523
+ ],
524
+ returnByValue: true,
525
+ });
526
+ noClasses = noClasses.result?.value;
527
+ if (!cssLocators.includes(noClasses)) {
528
+ cssLocators.push(noClasses);
529
+ }
530
+ let withClasses = await cdp.send("Runtime.callFunctionOn", {
531
+ functionDeclaration:
532
+ "function(element,options) { console.log(element,options); return window.CssSelectorGenerator.getCssSelector(element,options); }",
533
+ objectId: objectId,
534
+ arguments: [
535
+ {
536
+ objectId: objectId,
537
+ },
538
+ {
539
+ value: {
540
+ blacklist: [/^(?!.*h\d).*?\d.*/, /\[style/, /\[data-input-id/, /\[blinq-container/],
541
+ combineWithinSelector: true,
542
+ combineBetweenSelectors: true,
543
+ selectors: ["id", "attribute", "tag", "nthchild", "nthoftype", "class"],
544
+ maxCombinations: 100,
545
+ },
546
+ },
547
+ ],
548
+ returnByValue: true,
549
+ });
550
+ withClasses = withClasses.result?.value;
551
+ if (!cssLocators.includes(withClasses)) {
552
+ cssLocators.push(withClasses);
553
+ }
554
+ cssLocators.sort((a, b) => a.length - b.length);
555
+
556
+ cssLocators = await filterLocators(cssLocators, objectId);
557
+ } catch (err) {
558
+ console.error("Error in generateUniqueCSSSelector", error);
559
+ }
560
+ locators?.push(...cssLocators.map((css) => ({ mode: "NO_TEXT", css })));
561
+
562
+ const result = classifyLocatorsByStrategy(locators);
408
563
 
409
- return res.result;
564
+ return { ...result, element_name: "" };
410
565
  } finally {
411
566
  await _closeCDP();
412
567
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev-blinq/cucumber_client",
3
- "version": "1.0.1195-dev",
3
+ "version": "1.0.1197-dev",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",