@dev-blinq/cucumber_client 1.0.1390-dev → 1.0.1390-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 (50) 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/toolbar.js +27 -29
  4. package/bin/assets/preload/unique_locators.js +1 -1
  5. package/bin/assets/preload/yaml.js +288 -275
  6. package/bin/assets/scripts/aria_snapshot.js +223 -220
  7. package/bin/assets/scripts/dom_attr.js +329 -329
  8. package/bin/assets/scripts/dom_parent.js +169 -174
  9. package/bin/assets/scripts/event_utils.js +94 -94
  10. package/bin/assets/scripts/pw.js +2050 -1949
  11. package/bin/assets/scripts/recorder.js +13 -23
  12. package/bin/assets/scripts/snapshot_capturer.js +147 -147
  13. package/bin/assets/scripts/unique_locators.js +163 -44
  14. package/bin/assets/scripts/yaml.js +796 -783
  15. package/bin/assets/templates/_hooks_template.txt +6 -2
  16. package/bin/assets/templates/utils_template.txt +2 -2
  17. package/bin/client/code_cleanup/find_step_definition_references.js +0 -2
  18. package/bin/client/code_cleanup/utils.js +5 -1
  19. package/bin/client/code_gen/api_codegen.js +2 -2
  20. package/bin/client/code_gen/code_inversion.js +63 -2
  21. package/bin/client/code_gen/function_signature.js +4 -0
  22. package/bin/client/code_gen/page_reflection.js +846 -906
  23. package/bin/client/code_gen/playwright_codeget.js +27 -3
  24. package/bin/client/cucumber/feature.js +4 -0
  25. package/bin/client/cucumber/feature_data.js +2 -2
  26. package/bin/client/cucumber/project_to_document.js +8 -2
  27. package/bin/client/cucumber/steps_definitions.js +6 -3
  28. package/bin/client/cucumber_selector.js +17 -1
  29. package/bin/client/local_agent.js +3 -2
  30. package/bin/client/parse_feature_file.js +23 -26
  31. package/bin/client/playground/projects/env.json +2 -2
  32. package/bin/client/project.js +186 -202
  33. package/bin/client/recorderv3/bvt_init.js +349 -0
  34. package/bin/client/recorderv3/bvt_recorder.js +1068 -104
  35. package/bin/client/recorderv3/implemented_steps.js +2 -0
  36. package/bin/client/recorderv3/index.js +4 -303
  37. package/bin/client/recorderv3/scriptTest.js +1 -1
  38. package/bin/client/recorderv3/services.js +814 -154
  39. package/bin/client/recorderv3/step_runner.js +315 -206
  40. package/bin/client/recorderv3/step_utils.js +473 -25
  41. package/bin/client/recorderv3/update_feature.js +9 -5
  42. package/bin/client/recorderv3/wbr_entry.js +61 -0
  43. package/bin/client/recording.js +1 -0
  44. package/bin/client/upload-service.js +3 -2
  45. package/bin/client/utils/socket_logger.js +132 -0
  46. package/bin/index.js +4 -1
  47. package/bin/logger.js +3 -2
  48. package/bin/min/consoleApi.min.cjs +2 -3
  49. package/bin/min/injectedScript.min.cjs +16 -16
  50. package/package.json +19 -9
@@ -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,155 @@
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
31
- }
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
- }
28
+ }
29
+ processStyles(document) {
30
+ if (!this.inlineStyles) return;
31
+
32
+ const stylesheets = Array.from(window.document.styleSheets);
33
+ for (const sheet of stylesheets) {
34
+ try {
35
+ const el = sheet.ownerNode.cloneNode(true);
36
+ if (!el.href) {
37
+ document.head.appendChild(el);
38
+ } else {
39
+ const rules = Array.from(sheet.cssRules)
40
+ .map((rule) => rule.cssText)
41
+ .join("\n");
42
+ const styleEl = document.createElement("style");
43
+ styleEl.textContent = rules;
44
+ document.head.appendChild(styleEl);
61
45
  }
46
+ } catch (error) {
47
+ console.warn(`Error processing stylesheet: ${sheet.href}`, error);
48
+ }
62
49
  }
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);
50
+ }
51
+ processImages(document) {
52
+ if (!this.inlineImages) return;
130
53
 
131
- // Store the original document
132
- const originalDoc = window.document;
54
+ const images = document.querySelectorAll("img");
55
+ images.forEach((img) => {
56
+ // Skip SVGs and already data URLs
57
+ if (img.src.startsWith("data:") || img.src.endsWith(".svg")) return;
133
58
 
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;
59
+ // Skip if the image is excluded
60
+ if (this.excludeSelectors.some((selector) => img.matches(selector))) return;
137
61
 
138
- // Process the clone
139
- this.processStyles(doc);
140
- this.processImages(doc);
141
- this.removeExcludedElements(doc);
142
- this.processComputedStyles(doc);
143
-
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
- }
62
+ // Only inline complete images
63
+ if (img.complete && img.naturalWidth !== 0) {
64
+ try {
65
+ img.setAttribute("src", this.imageToDataURL(img, document));
66
+ } catch (e) {
67
+ console.warn(`Failed to process image: ${img.src}`, e);
68
+ }
69
+ }
70
+ });
71
+ }
72
+ removeExcludedElements(document) {
73
+ this.excludeSelectors.forEach((selector) => {
74
+ const elements = document.querySelectorAll(selector);
75
+ elements.forEach((el) => {
76
+ el.parentNode?.removeChild(el);
77
+ });
78
+ });
79
+ }
80
+ processComputedStyles(document) {
81
+ const elements = document.querySelectorAll("*");
82
+
83
+ elements.forEach((el) => {
84
+ // Skip excluded elements
85
+ if (this.excludeSelectors.some((selector) => el.matches(selector))) return;
86
+
87
+ // Get computed style
88
+ const style = window.getComputedStyle(el);
89
+
90
+ // Copy important styles to inline style
91
+ const importantStyles = [
92
+ "display",
93
+ "position",
94
+ "width",
95
+ "height",
96
+ "margin",
97
+ "padding",
98
+ "color",
99
+ "background-color",
100
+ "font-family",
101
+ "font-size",
102
+ "text-align",
103
+ "line-height",
104
+ "border",
105
+ "box-shadow",
106
+ "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
+ createSnapshot() {
127
+ // Clone the document to avoid modifying the original
128
+ const docClone = window.document.cloneNode(true);
129
+
130
+ // Store the original document
131
+ const originalDoc = window.document;
132
+
133
+ // Temporarily "swap" the document for processing
134
+ // (We're just using this as a convention - it doesn't actually replace the global document)
135
+ const doc = docClone;
136
+
137
+ // Process the clone
138
+ this.processStyles(doc);
139
+ this.processImages(doc);
140
+ this.removeExcludedElements(doc);
141
+ this.processComputedStyles(doc);
142
+
143
+ // Generate HTML with doctype
144
+ const doctype = originalDoc.doctype
145
+ ? new XMLSerializer().serializeToString(originalDoc.doctype)
146
+ : "<!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
+ }
154
154
  }
155
- export default SnapshotCapturer;
155
+ export default SnapshotCapturer;