@dev-blinq/cucumber_client 1.0.1351-dev → 1.0.1351-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 +107 -107
  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 +13 -23
  13. package/bin/assets/scripts/snapshot_capturer.js +153 -146
  14. package/bin/assets/scripts/unique_locators.js +941 -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 +107 -2
  23. package/bin/client/code_gen/page_reflection.js +839 -906
  24. package/bin/client/code_gen/playwright_codeget.js +25 -11
  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 +202 -89
  35. package/bin/client/recorderv3/implemented_steps.js +17 -12
  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 +475 -16
  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 +2 -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
@@ -144,7 +144,7 @@ class BVTRecorder {
144
144
  getAction: (e) => {
145
145
  this.eventUtils.consumeEvent(e);
146
146
  },
147
- getInterestedElement: () => { },
147
+ getInterestedElement: () => {},
148
148
  hoverOutlineStyle: "",
149
149
  });
150
150
 
@@ -159,7 +159,7 @@ class BVTRecorder {
159
159
  };
160
160
  },
161
161
  getAction: () => null,
162
- getInterestedElement: () => { },
162
+ getInterestedElement: () => {},
163
163
  hoverOutlineStyle: "",
164
164
  });
165
165
 
@@ -174,7 +174,7 @@ class BVTRecorder {
174
174
  };
175
175
  },
176
176
  getAction: () => null,
177
- getInterestedElement: () => { },
177
+ getInterestedElement: () => {},
178
178
  hoverOutlineStyle: "",
179
179
  });
180
180
 
@@ -676,7 +676,7 @@ class BVTRecorder {
676
676
 
677
677
  lastInputId = el.dataset.inputId;
678
678
 
679
- el.__locators = this.getLocatorsObject(el);
679
+ // el.__locators = this.getLocatorsObject(el, { maxLocators: 1 });
680
680
  }
681
681
  const role = this.PW.roleUtils.getAriaRole(el);
682
682
  const label =
@@ -684,13 +684,15 @@ class BVTRecorder {
684
684
  this.PW.roleUtils.getElementAccessibleName(el, true) ||
685
685
  "";
686
686
  const result = this.getElementProperties(el);
687
+ const elText = this.PW.selectorUtils.elementText(new Map(), el);
687
688
  return {
688
689
  role,
689
690
  label,
690
691
  inputID: el.dataset.inputId,
691
692
  tagName: el.tagName,
692
693
  type: el.type,
693
- text: this.PW.selectorUtils.elementText(new Map(), el).full.trim(),
694
+ text: elText.full.trim(),
695
+ textNormalized: elText.normalized.trim(),
694
696
  parent: `tagname: ${el.parentElement?.tagName}\ninnerText: ${el.parentElement?.innerText}`,
695
697
  attrs: {
696
698
  placeholder: el.getAttribute("placeholder"),
@@ -788,26 +790,15 @@ class BVTRecorder {
788
790
  });
789
791
  }
790
792
 
791
- getLocatorsObject(el) {
793
+ getLocatorsObject(el, options = {}) {
792
794
  if (this.contextElement) {
793
- const text = this.contextElement.innerText; // TODO: handle case where contextElement is not in dom/ children removed
794
- const contextEl = this.contextElement;
795
- // const { climb, commonParent } = window.getCommonParent(contextEl, el);
796
- const commonParent = this.locatorGenerator.dom_Parent.findLowestCommonAncestor([contextEl, el]);
797
- const climb = this.locatorGenerator.dom_Parent.getClimbCountToParent(contextEl, commonParent);
798
- const result = this.locatorGenerator.getElementLocators(el, {
799
- excludeText: true,
800
- root: commonParent,
801
- });
802
- result.locators.forEach((locator) => {
803
- locator.text = text;
804
- locator.climb = climb;
805
- });
795
+ const result = this.locatorGenerator.toContextLocators(el, this.contextElement, options);
806
796
  return result;
807
797
  }
808
798
  const isRecordingText = this.#mode === "recordingText";
809
799
  return this.locatorGenerator.getElementLocators(el, {
810
800
  excludeText: isRecordingText,
801
+ ...options,
811
802
  });
812
803
  }
813
804
  addListeners() {
@@ -849,8 +840,7 @@ class BVTRecorder {
849
840
  action: action.details,
850
841
  element: this.getElementDetails(actionElement, eventName),
851
842
  isPopupCloseClick: this.isPopupCloseEvent(e),
852
- // ...this.getLocatorsObject(actionElement),
853
- ...(actionElement.__locators ?? this.getLocatorsObject(actionElement)),
843
+ // ...(actionElement.__locators ?? this.getLocatorsObject(actionElement, { maxLocators: 1 })),
854
844
  frame: this.getFrameDetails(),
855
845
  statistics: {
856
846
  time: `${performance.measure("command-received", "command-send").duration.toFixed(2)} ms`,
@@ -868,7 +858,7 @@ class BVTRecorder {
868
858
  // await bvtRecorderBindings.validateLocators(snapshotDetails);
869
859
  // });
870
860
  // console.log(cmd);
871
- await bvtRecorderBindings.recordCommand(cmd);
861
+ bvtRecorderBindings.recordCommand(cmd);
872
862
  this.handleStateTransition(action.element);
873
863
  },
874
864
  { capture: true }
@@ -894,7 +884,7 @@ class BVTRecorder {
894
884
  this.interestedElements.clear();
895
885
  }
896
886
  processAriaSnapshot(snapshot) {
897
- const matchedElements = this.findMatchingElements(snapshot, this.snapshotElements);
887
+ const matchedElements = this.snapshotUtils.findMatchingElements(snapshot, this.snapshotElements);
898
888
  for (const el of matchedElements.values()) {
899
889
  const element = el;
900
890
  if (element) {
@@ -1,155 +1,162 @@
1
1
  class SnapshotCapturer {
2
- constructor(options = {}) {
3
- const {
4
- inlineImages = true,
5
- inlineStyles = true,
6
- excludeSelectors = []
7
- } = options;
8
- // this.options = {
9
- // inlineImages,
10
- // inlineStyles,
11
- // excludeSelectors
12
- // };
13
- this.inlineImages = inlineImages;
14
- this.inlineStyles = inlineStyles;
15
- this.excludeSelectors = excludeSelectors;
2
+ constructor(options = {}) {
3
+ const { inlineImages = true, inlineStyles = true, excludeSelectors = [] } = options;
4
+ // this.options = {
5
+ // inlineImages,
6
+ // inlineStyles,
7
+ // excludeSelectors
8
+ // };
9
+ this.inlineImages = inlineImages;
10
+ this.inlineStyles = inlineStyles;
11
+ this.excludeSelectors = excludeSelectors;
12
+ }
13
+ imageToDataURL(img, document) {
14
+ try {
15
+ // Create canvas to draw the image
16
+ const canvas = document.createElement("canvas");
17
+ canvas.width = img.naturalWidth || img.width;
18
+ canvas.height = img.naturalHeight || img.height;
19
+ const ctx = canvas.getContext("2d");
20
+
21
+ // Draw image to canvas and convert to data URL
22
+ ctx.drawImage(img, 0, 0);
23
+ return canvas.toDataURL("image/png");
24
+ } catch (error) {
25
+ console.warn(`Failed to inline image: ${img.src}`, error);
26
+ return img.src; // Fall back to original source
16
27
  }
17
- imageToDataURL(img, document) {
18
- try {
19
- // Create canvas to draw the image
20
- const canvas = document.createElement('canvas');
21
- canvas.width = img.naturalWidth || img.width;
22
- canvas.height = img.naturalHeight || img.height;
23
- const ctx = canvas.getContext('2d');
24
-
25
- // Draw image to canvas and convert to data URL
26
- ctx.drawImage(img, 0, 0);
27
- return canvas.toDataURL('image/png');
28
- } catch (error) {
29
- console.warn(`Failed to inline image: ${img.src}`, error);
30
- return img.src; // Fall back to original source
28
+ }
29
+ processStyles(document) {
30
+ if (!this.inlineStyles) return;
31
+
32
+ const stylesheets = Array.from(document.styleSheets);
33
+ for (const sheet of stylesheets) {
34
+ try {
35
+ if (!sheet.href) continue; // Skip inline styles
36
+ const styleEl = document.createElement("style");
37
+ let text = "";
38
+ const cssRules = Array.from(sheet.cssRules || []);
39
+ for (const rule of cssRules) {
40
+ if (rule.cssText) {
41
+ text += rule.cssText + "\n";
42
+ }
31
43
  }
32
- }
33
- processStyles(document) {
34
- if (!this.inlineStyles) return;
35
-
36
- const stylesheets = Array.from(document.styleSheets);
37
- for (const sheet of stylesheets) {
38
- try {
39
- if (!sheet.href) continue; // Skip inline styles
40
- const styleEl = document.createElement('style');
41
- let text = ""
42
- const cssRules = Array.from(sheet.cssRules || []);
43
- for (const rule of cssRules) {
44
- if (rule.cssText) {
45
- text += rule.cssText + '\n';
46
- }
47
- }
48
- styleEl.textContent = text;
49
-
50
- // Replace the link with our new style element
51
- if (sheet.ownerNode && sheet.ownerNode.parentNode) {
52
- sheet.ownerNode.parentNode.replaceChild(styleEl, sheet.ownerNode);
53
- } else if (sheet.ownerNode) {
54
- // If the owner node is not in the document, just append it
55
- document.head.appendChild(styleEl);
56
- }
57
-
58
- } catch (error) {
59
- console.warn(`Error processing stylesheet: ${sheet.href}`, error);
60
- }
44
+ styleEl.textContent = text;
45
+
46
+ // Replace the link with our new style element
47
+ if (sheet.ownerNode && sheet.ownerNode.parentNode) {
48
+ sheet.ownerNode.parentNode.replaceChild(styleEl, sheet.ownerNode);
49
+ } else if (sheet.ownerNode) {
50
+ // If the owner node is not in the document, just append it
51
+ document.head.appendChild(styleEl);
61
52
  }
53
+ } catch (error) {
54
+ console.warn(`Error processing stylesheet: ${sheet.href}`, error);
55
+ }
62
56
  }
63
- processImages(document) {
64
- if (!this.inlineImages) return;
65
-
66
- const images = document.querySelectorAll('img');
67
- images.forEach(img => {
68
- // Skip SVGs and already data URLs
69
- if (img.src.startsWith('data:') || img.src.endsWith('.svg')) return;
70
-
71
- // Skip if the image is excluded
72
- if (this.excludeSelectors.some(selector => img.matches(selector))) return;
73
-
74
- // Only inline complete images
75
- if (img.complete && img.naturalWidth !== 0) {
76
- try {
77
- img.setAttribute('src', this.imageToDataURL(img, document));
78
- } catch (e) {
79
- console.warn(`Failed to process image: ${img.src}`, e);
80
- }
81
- }
82
- });
83
- }
84
- removeExcludedElements(document) {
85
- this.excludeSelectors.forEach(selector => {
86
- const elements = document.querySelectorAll(selector);
87
- elements.forEach(el => {
88
- el.parentNode?.removeChild(el);
89
- });
90
- });
91
- }
92
- processComputedStyles(document) {
93
- const elements = document.querySelectorAll('*');
94
-
95
- elements.forEach(el => {
96
- // Skip excluded elements
97
- if (this.excludeSelectors.some(selector => el.matches(selector))) return;
98
-
99
- // Get computed style
100
- const style = window.getComputedStyle(el);
101
-
102
- // Copy important styles to inline style
103
- const importantStyles = [
104
- 'display', 'position', 'width', 'height', 'margin', 'padding',
105
- 'color', 'background-color', 'font-family', 'font-size',
106
- 'text-align', 'line-height', 'border', 'box-shadow', 'opacity'
107
- // Add more styles as needed
108
- ];
109
-
110
- const inlineStyles = [];
111
-
112
- importantStyles.forEach(prop => {
113
- const value = style.getPropertyValue(prop);
114
- if (value) {
115
- inlineStyles.push(`${prop}: ${value}`);
116
- }
117
- });
118
-
119
- // Set inline style
120
- if (inlineStyles.length > 0) {
121
- const currentStyle = el.getAttribute('style') || '';
122
- el.setAttribute('style', currentStyle + inlineStyles.join('; ') + ';');
123
- }
124
- });
125
-
126
- }
127
- createSnapshot() {
128
- // Clone the document to avoid modifying the original
129
- const docClone = window.document.cloneNode(true);
130
-
131
- // Store the original document
132
- const originalDoc = window.document;
57
+ }
58
+ processImages(document) {
59
+ if (!this.inlineImages) return;
133
60
 
134
- // Temporarily "swap" the document for processing
135
- // (We're just using this as a convention - it doesn't actually replace the global document)
136
- const doc = docClone;
61
+ const images = document.querySelectorAll("img");
62
+ images.forEach((img) => {
63
+ // Skip SVGs and already data URLs
64
+ if (img.src.startsWith("data:") || img.src.endsWith(".svg")) return;
137
65
 
138
- // Process the clone
139
- this.processStyles(doc);
140
- this.processImages(doc);
141
- this.removeExcludedElements(doc);
142
- this.processComputedStyles(doc);
66
+ // Skip if the image is excluded
67
+ if (this.excludeSelectors.some((selector) => img.matches(selector))) return;
143
68
 
144
- // Generate HTML with doctype
145
- const doctype = originalDoc.doctype ?
146
- new XMLSerializer().serializeToString(originalDoc.doctype) : '<!DOCTYPE html>';
147
-
148
- // Get the HTML content
149
- const htmlContent = doc.documentElement.outerHTML;
150
-
151
- // Combine doctype and HTML content
152
- return `${doctype}${htmlContent}`;
153
- }
69
+ // Only inline complete images
70
+ if (img.complete && img.naturalWidth !== 0) {
71
+ try {
72
+ img.setAttribute("src", this.imageToDataURL(img, document));
73
+ } catch (e) {
74
+ console.warn(`Failed to process image: ${img.src}`, e);
75
+ }
76
+ }
77
+ });
78
+ }
79
+ removeExcludedElements(document) {
80
+ this.excludeSelectors.forEach((selector) => {
81
+ const elements = document.querySelectorAll(selector);
82
+ elements.forEach((el) => {
83
+ el.parentNode?.removeChild(el);
84
+ });
85
+ });
86
+ }
87
+ processComputedStyles(document) {
88
+ const elements = document.querySelectorAll("*");
89
+
90
+ elements.forEach((el) => {
91
+ // Skip excluded elements
92
+ if (this.excludeSelectors.some((selector) => el.matches(selector))) return;
93
+
94
+ // Get computed style
95
+ const style = window.getComputedStyle(el);
96
+
97
+ // Copy important styles to inline style
98
+ const importantStyles = [
99
+ "display",
100
+ "position",
101
+ "width",
102
+ "height",
103
+ "margin",
104
+ "padding",
105
+ "color",
106
+ "background-color",
107
+ "font-family",
108
+ "font-size",
109
+ "text-align",
110
+ "line-height",
111
+ "border",
112
+ "box-shadow",
113
+ "opacity",
114
+ // Add more styles as needed
115
+ ];
116
+
117
+ const inlineStyles = [];
118
+
119
+ importantStyles.forEach((prop) => {
120
+ const value = style.getPropertyValue(prop);
121
+ if (value) {
122
+ inlineStyles.push(`${prop}: ${value}`);
123
+ }
124
+ });
125
+
126
+ // Set inline style
127
+ if (inlineStyles.length > 0) {
128
+ const currentStyle = el.getAttribute("style") || "";
129
+ el.setAttribute("style", currentStyle + inlineStyles.join("; ") + ";");
130
+ }
131
+ });
132
+ }
133
+ createSnapshot() {
134
+ // Clone the document to avoid modifying the original
135
+ const docClone = window.document.cloneNode(true);
136
+
137
+ // Store the original document
138
+ const originalDoc = window.document;
139
+
140
+ // Temporarily "swap" the document for processing
141
+ // (We're just using this as a convention - it doesn't actually replace the global document)
142
+ const doc = docClone;
143
+
144
+ // Process the clone
145
+ this.processStyles(doc);
146
+ this.processImages(doc);
147
+ this.removeExcludedElements(doc);
148
+ this.processComputedStyles(doc);
149
+
150
+ // Generate HTML with doctype
151
+ const doctype = originalDoc.doctype
152
+ ? new XMLSerializer().serializeToString(originalDoc.doctype)
153
+ : "<!DOCTYPE html>";
154
+
155
+ // Get the HTML content
156
+ const htmlContent = doc.documentElement.outerHTML;
157
+
158
+ // Combine doctype and HTML content
159
+ return `${doctype}${htmlContent}`;
160
+ }
154
161
  }
155
- export default SnapshotCapturer;
162
+ export default SnapshotCapturer;