@dev-blinq/cucumber_client 1.0.1389-dev → 1.0.1389-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.
- package/bin/assets/bundled_scripts/recorder.js +107 -107
- package/bin/assets/preload/css_gen.js +10 -10
- package/bin/assets/preload/toolbar.js +27 -29
- package/bin/assets/preload/unique_locators.js +1 -1
- package/bin/assets/preload/yaml.js +288 -275
- package/bin/assets/scripts/aria_snapshot.js +223 -220
- package/bin/assets/scripts/dom_attr.js +329 -329
- package/bin/assets/scripts/dom_parent.js +169 -174
- package/bin/assets/scripts/event_utils.js +94 -94
- package/bin/assets/scripts/pw.js +2050 -1949
- package/bin/assets/scripts/recorder.js +13 -23
- package/bin/assets/scripts/snapshot_capturer.js +147 -147
- package/bin/assets/scripts/unique_locators.js +163 -44
- package/bin/assets/scripts/yaml.js +796 -783
- package/bin/assets/templates/_hooks_template.txt +6 -2
- package/bin/assets/templates/utils_template.txt +2 -2
- package/bin/client/code_cleanup/find_step_definition_references.js +0 -2
- package/bin/client/code_cleanup/utils.js +5 -1
- package/bin/client/code_gen/api_codegen.js +2 -2
- package/bin/client/code_gen/code_inversion.js +63 -2
- package/bin/client/code_gen/function_signature.js +4 -0
- package/bin/client/code_gen/page_reflection.js +846 -906
- package/bin/client/code_gen/playwright_codeget.js +27 -3
- package/bin/client/cucumber/feature.js +4 -0
- package/bin/client/cucumber/feature_data.js +2 -2
- package/bin/client/cucumber/project_to_document.js +8 -2
- package/bin/client/cucumber/steps_definitions.js +6 -3
- package/bin/client/cucumber_selector.js +17 -1
- package/bin/client/local_agent.js +3 -2
- package/bin/client/parse_feature_file.js +23 -26
- package/bin/client/playground/projects/env.json +2 -2
- package/bin/client/project.js +186 -202
- package/bin/client/recorderv3/bvt_init.js +349 -0
- package/bin/client/recorderv3/bvt_recorder.js +1068 -104
- package/bin/client/recorderv3/implemented_steps.js +2 -0
- package/bin/client/recorderv3/index.js +4 -303
- package/bin/client/recorderv3/scriptTest.js +1 -1
- package/bin/client/recorderv3/services.js +814 -154
- package/bin/client/recorderv3/step_runner.js +315 -206
- package/bin/client/recorderv3/step_utils.js +473 -25
- package/bin/client/recorderv3/update_feature.js +9 -5
- package/bin/client/recorderv3/wbr_entry.js +61 -0
- package/bin/client/recording.js +1 -0
- package/bin/client/upload-service.js +3 -2
- package/bin/client/utils/socket_logger.js +132 -0
- package/bin/index.js +4 -1
- package/bin/logger.js +3 -2
- package/bin/min/consoleApi.min.cjs +2 -3
- package/bin/min/injectedScript.min.cjs +16 -16
- package/package.json +21 -10
|
@@ -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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
const doc = docClone;
|
|
59
|
+
// Skip if the image is excluded
|
|
60
|
+
if (this.excludeSelectors.some((selector) => img.matches(selector))) return;
|
|
137
61
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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;
|