@dev-blinq/cucumber_client 1.0.1431-dev → 1.0.1431-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 (43) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +73 -73
  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 +70 -45
  12. package/bin/assets/scripts/snapshot_capturer.js +147 -147
  13. package/bin/assets/scripts/unique_locators.js +170 -49
  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 +16 -16
  17. package/bin/client/code_cleanup/find_step_definition_references.js +0 -1
  18. package/bin/client/code_gen/api_codegen.js +2 -2
  19. package/bin/client/code_gen/code_inversion.js +63 -2
  20. package/bin/client/code_gen/function_signature.js +4 -0
  21. package/bin/client/code_gen/page_reflection.js +52 -11
  22. package/bin/client/code_gen/playwright_codeget.js +28 -22
  23. package/bin/client/cucumber/feature_data.js +2 -2
  24. package/bin/client/cucumber/project_to_document.js +8 -2
  25. package/bin/client/cucumber/steps_definitions.js +19 -3
  26. package/bin/client/local_agent.js +3 -2
  27. package/bin/client/parse_feature_file.js +23 -26
  28. package/bin/client/playground/projects/env.json +2 -2
  29. package/bin/client/recorderv3/bvt_init.js +363 -0
  30. package/bin/client/recorderv3/bvt_recorder.js +1009 -47
  31. package/bin/client/recorderv3/implemented_steps.js +2 -0
  32. package/bin/client/recorderv3/index.js +3 -283
  33. package/bin/client/recorderv3/scriptTest.js +1 -1
  34. package/bin/client/recorderv3/services.js +818 -142
  35. package/bin/client/recorderv3/step_runner.js +28 -8
  36. package/bin/client/recorderv3/step_utils.js +514 -39
  37. package/bin/client/recorderv3/update_feature.js +32 -13
  38. package/bin/client/recorderv3/wbr_entry.js +61 -0
  39. package/bin/client/recording.js +1 -0
  40. package/bin/client/upload-service.js +4 -2
  41. package/bin/client/utils/socket_logger.js +1 -1
  42. package/bin/index.js +4 -1
  43. package/package.json +6 -4
@@ -1,137 +1,132 @@
1
1
  class DOM_Parent {
2
+ getActualParent(element) {
3
+ if (!element || !(element instanceof Element)) {
4
+ throw new Error("Invalid element provided");
5
+ }
6
+
7
+ // TODO: account for slotted elements
8
+
9
+ if (element.assignedSlot) {
10
+ return element.assignedSlot;
11
+ }
12
+ // Get the actual parent element, skipping shadow DOM if necessary
13
+ let parent = element.parentElement;
14
+ if (parent) {
15
+ return parent;
16
+ }
17
+ const parentNode = element.parentNode;
18
+ if (parentNode && parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
19
+ // If the parent is a shadow root, return its host
20
+ return parentNode.host || null;
21
+ }
22
+ return null;
23
+ }
24
+
25
+ getFullAncestorChain(element) {
26
+ if (!element || !(element instanceof Element)) {
27
+ throw new Error("Invalid element provided");
28
+ }
29
+
30
+ const ancestors = [];
31
+ let currentElement = element;
32
+
33
+ while (currentElement) {
34
+ ancestors.push(currentElement);
35
+ currentElement = this.getActualParent(currentElement);
36
+ }
37
+
38
+ return ancestors;
39
+ }
40
+ getFullAncestorChainToRoot(element, root) {
41
+ if (!element || !(element instanceof Element)) {
42
+ throw new Error("Invalid element provided");
43
+ }
44
+ if (!root || !(root instanceof Element)) {
45
+ throw new Error("Invalid root provided");
46
+ }
47
+ if (!this.containsElementCrossShadow(root, element)) {
48
+ throw new Error("Root does not contain the element");
49
+ }
50
+ const ancestors = [];
51
+ let currentElement = element;
52
+ while (currentElement && currentElement !== root && this.containsElementCrossShadow(root, currentElement)) {
53
+ ancestors.push(currentElement);
54
+ currentElement = this.getActualParent(currentElement);
55
+ }
56
+ if (currentElement === root) {
57
+ ancestors.push(currentElement);
58
+ }
59
+ return ancestors;
60
+ }
61
+ getClimbCountToParent(element, targetParent) {
62
+ if (!element || !(element instanceof Element)) {
63
+ throw new Error("Invalid element provided");
64
+ }
65
+ if (!targetParent || !(targetParent instanceof Element)) {
66
+ throw new Error("Invalid target parent provided");
67
+ }
68
+
69
+ let count = 0;
70
+ let currentElement = element;
71
+
72
+ while (currentElement && currentElement !== targetParent) {
73
+ currentElement = this.getActualParent(currentElement);
74
+ count++;
75
+ }
76
+
77
+ return currentElement === targetParent ? count : -1; // Return -1 if target parent is not found
78
+ }
2
79
 
3
- getActualParent(element) {
4
- if (!element || !(element instanceof Element)) {
5
- throw new Error('Invalid element provided');
6
- }
7
-
8
- // TODO: account for slotted elements
9
-
10
- if (element.assignedSlot) {
11
- return element.assignedSlot
12
- }
13
- // Get the actual parent element, skipping shadow DOM if necessary
14
- let parent = element.parentElement;
15
- if (parent) {
16
- return parent;
17
- }
18
- const parentNode = element.parentNode;
19
- if (parentNode && parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
20
- // If the parent is a shadow root, return its host
21
- return parentNode.host || null;
22
- }
23
- return null;
24
-
25
- }
26
-
27
- getFullAncestorChain(element) {
28
- if (!element || !(element instanceof Element)) {
29
- throw new Error('Invalid element provided');
30
- }
31
-
32
- const ancestors = [];
33
- let currentElement = element;
34
-
35
- while (currentElement) {
36
- ancestors.push(currentElement);
37
- currentElement = this.getActualParent(currentElement);
38
- }
39
-
40
- return ancestors;
41
- }
42
- getFullAncestorChainToRoot(element, root) {
43
- if (!element || !(element instanceof Element)) {
44
- throw new Error('Invalid element provided');
45
- }
46
- if (!root || !(root instanceof Element)) {
47
- throw new Error('Invalid root provided');
48
- }
49
- if (!this.containsElementCrossShadow(root, element)) {
50
- throw new Error('Root does not contain the element');
51
- }
52
- const ancestors = [];
53
- let currentElement = element;
54
- while (currentElement && currentElement !== root && this.containsElementCrossShadow(root, currentElement)) {
55
- ancestors.push(currentElement);
56
- currentElement = this.getActualParent(currentElement);
57
-
58
- }
59
- if (currentElement === root) {
60
- ancestors.push(currentElement);
61
- }
62
- return ancestors;
63
- }
64
- getClimbCountToParent(element, targetParent) {
65
- if (!element || !(element instanceof Element)) {
66
- throw new Error('Invalid element provided');
67
- }
68
- if (!targetParent || !(targetParent instanceof Element)) {
69
- throw new Error('Invalid target parent provided');
70
- }
71
-
72
- let count = 0;
73
- let currentElement = element;
74
-
75
- while (currentElement && currentElement !== targetParent) {
76
- currentElement = this.getActualParent(currentElement);
77
- count++;
78
- }
79
-
80
- return currentElement === targetParent ? count : -1; // Return -1 if target parent is not found
81
- }
82
-
83
- containsElementCrossShadow(element, target) {
84
- if (!element || !(element instanceof Element)) {
85
- throw new Error('Invalid element provided');
86
- }
87
- if (!target || !(target instanceof Element)) {
88
- throw new Error('Invalid target provided');
89
- }
90
-
91
- // Check if the element contains the target, accounting for shadow DOM
92
- let currentElement = target;
93
-
94
- while (currentElement) {
95
- if (currentElement === element) {
96
- return true;
97
- }
98
- currentElement = this.getActualParent(currentElement);
99
- }
100
-
101
- return false;
102
- }
103
-
104
- findLowestCommonAncestor(elements) {
105
- if (!Array.isArray(elements) || elements.length === 0) {
106
- throw new Error('Invalid elements array provided');
107
- }
108
-
109
- // Ensure all elements are valid
110
- for (const el of elements) {
111
- if (!(el instanceof Element)) {
112
- throw new Error('All items in the array must be DOM Elements');
113
- }
114
- }
115
-
116
- // Start with the first element's ancestors
117
- let commonAncestors = this.getFullAncestorChain(elements[0]);
118
-
119
- commonAncestors.reverse(); // Reverse to start from the closest ancestor
120
-
121
- // Iterate through the rest of the elements
122
- for (let i = 1; i < elements.length; i++) {
123
- const currentAncestors = this.getFullAncestorChain(elements[i]);
124
- currentAncestors.reverse(); // Reverse to start from the closest ancestor
125
- commonAncestors = commonAncestors.filter(ancestor =>
126
- currentAncestors.includes(ancestor)
127
- );
128
- }
129
-
130
- // Return the lowest common ancestor, or null if none found
131
- return commonAncestors.length > 0 ? commonAncestors[commonAncestors.length - 1] : null;
132
- }
133
-
134
- /**
80
+ containsElementCrossShadow(element, target) {
81
+ if (!element || !(element instanceof Element)) {
82
+ throw new Error("Invalid element provided");
83
+ }
84
+ if (!target || !(target instanceof Element)) {
85
+ throw new Error("Invalid target provided");
86
+ }
87
+
88
+ // Check if the element contains the target, accounting for shadow DOM
89
+ let currentElement = target;
90
+
91
+ while (currentElement) {
92
+ if (currentElement === element) {
93
+ return true;
94
+ }
95
+ currentElement = this.getActualParent(currentElement);
96
+ }
97
+
98
+ return false;
99
+ }
100
+
101
+ findLowestCommonAncestor(elements) {
102
+ if (!Array.isArray(elements) || elements.length === 0) {
103
+ throw new Error("Invalid elements array provided");
104
+ }
105
+
106
+ // Ensure all elements are valid
107
+ for (const el of elements) {
108
+ if (!(el instanceof Element)) {
109
+ throw new Error("All items in the array must be DOM Elements");
110
+ }
111
+ }
112
+
113
+ // Start with the first element's ancestors
114
+ let commonAncestors = this.getFullAncestorChain(elements[0]);
115
+
116
+ commonAncestors.reverse(); // Reverse to start from the closest ancestor
117
+
118
+ // Iterate through the rest of the elements
119
+ for (let i = 1; i < elements.length; i++) {
120
+ const currentAncestors = this.getFullAncestorChain(elements[i]);
121
+ currentAncestors.reverse(); // Reverse to start from the closest ancestor
122
+ commonAncestors = commonAncestors.filter((ancestor) => currentAncestors.includes(ancestor));
123
+ }
124
+
125
+ // Return the lowest common ancestor, or null if none found
126
+ return commonAncestors.length > 0 ? commonAncestors[commonAncestors.length - 1] : null;
127
+ }
128
+
129
+ /**
135
130
  * Finds the branching parent for a target element within an array of elements
136
131
  * The branching parent is the direct child of the common ancestor that contains the target
137
132
  * @param {Element[]} elements - Array of elements (target must be included)
@@ -139,47 +134,47 @@ class DOM_Parent {
139
134
 
140
135
  * @returns {Element|null} - The branching parent element, or null if not found
141
136
  */
142
- findBranchingParent(elements, target) {
143
- if (!Array.isArray(elements) || elements.length === 0) {
144
- throw new Error('Invalid elements array provided');
145
- }
146
- if (!(target instanceof Element)) {
147
- throw new Error('Target must be a DOM Element');
148
- }
149
-
150
- // Ensure all elements are valid
151
- for (const el of elements) {
152
- if (!(el instanceof Element)) {
153
- throw new Error('All items in the array must be DOM Elements');
154
- }
155
- }
156
- // Check if the target is in the elements array
157
- if (!elements.includes(target)) {
158
- // throw new Error("Target element must be included in the elements array");
159
- console.warn("Target element must be included in the elements array, returning null");
160
- return null;
161
- }
162
- const allTargetparents = this.getFullAncestorChain(target);
163
-
164
- // reverse the order of parents to start from the closest parent
165
- allTargetparents.reverse();
166
-
167
- // Find the parent that only contains the target and no other elements
168
- let branchingParent = null;
169
- for (const parent of allTargetparents) {
170
- /// find all elements that are inside this parent
171
- const allElementsThatAreInsideParent = elements.filter(el => this.containsElementCrossShadow(parent, el));
172
- // If the parent contains only the target element, it is the branching parent
173
- if (allElementsThatAreInsideParent.length === 1 && allElementsThatAreInsideParent[0] === target) {
174
- branchingParent = parent;
175
- break;
176
- }
177
- }
178
- if (branchingParent) {
179
- return branchingParent;
180
- }
181
-
182
- return null; // No branching parent found
137
+ findBranchingParent(elements, target) {
138
+ if (!Array.isArray(elements) || elements.length === 0) {
139
+ throw new Error("Invalid elements array provided");
140
+ }
141
+ if (!(target instanceof Element)) {
142
+ throw new Error("Target must be a DOM Element");
183
143
  }
144
+
145
+ // Ensure all elements are valid
146
+ for (const el of elements) {
147
+ if (!(el instanceof Element)) {
148
+ throw new Error("All items in the array must be DOM Elements");
149
+ }
150
+ }
151
+ // Check if the target is in the elements array
152
+ if (!elements.includes(target)) {
153
+ // throw new Error("Target element must be included in the elements array");
154
+ console.warn("Target element must be included in the elements array, returning null");
155
+ return null;
156
+ }
157
+ const allTargetparents = this.getFullAncestorChain(target);
158
+
159
+ // reverse the order of parents to start from the closest parent
160
+ allTargetparents.reverse();
161
+
162
+ // Find the parent that only contains the target and no other elements
163
+ let branchingParent = null;
164
+ for (const parent of allTargetparents) {
165
+ /// find all elements that are inside this parent
166
+ const allElementsThatAreInsideParent = elements.filter((el) => this.containsElementCrossShadow(parent, el));
167
+ // If the parent contains only the target element, it is the branching parent
168
+ if (allElementsThatAreInsideParent.length === 1 && allElementsThatAreInsideParent[0] === target) {
169
+ branchingParent = parent;
170
+ break;
171
+ }
172
+ }
173
+ if (branchingParent) {
174
+ return branchingParent;
175
+ }
176
+
177
+ return null; // No branching parent found
178
+ }
184
179
  }
185
- export default DOM_Parent;
180
+ export default DOM_Parent;
@@ -4,102 +4,102 @@ const closestCrossShadow = __PW.domUtils.closestCrossShadow;
4
4
  const isElementVisible = __PW.domUtils.isElementVisible;
5
5
 
6
6
  export default class EventUtils {
7
- deepEventTarget(event) {
8
- return event.composedPath()[0];
9
- }
10
- deepActiveElement(document) {
11
- let activeElement = document.activeElement;
12
- while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
13
- activeElement = activeElement.shadowRoot.activeElement;
14
- return activeElement;
15
- }
16
- modifiersForEvent(event) {
17
- return (event.altKey ? 1 : 0) | (event.ctrlKey ? 2 : 0) | (event.metaKey ? 4 : 0) | (event.shiftKey ? 8 : 0);
18
- }
19
- buttonForEvent(event) {
20
- switch (event.which) {
21
- case 1:
22
- return "left";
23
- case 2:
24
- return "middle";
25
- case 3:
26
- return "right";
27
- }
7
+ deepEventTarget(event) {
8
+ return event.composedPath()[0];
9
+ }
10
+ deepActiveElement(document) {
11
+ let activeElement = document.activeElement;
12
+ while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
13
+ activeElement = activeElement.shadowRoot.activeElement;
14
+ return activeElement;
15
+ }
16
+ modifiersForEvent(event) {
17
+ return (event.altKey ? 1 : 0) | (event.ctrlKey ? 2 : 0) | (event.metaKey ? 4 : 0) | (event.shiftKey ? 8 : 0);
18
+ }
19
+ buttonForEvent(event) {
20
+ switch (event.which) {
21
+ case 1:
28
22
  return "left";
23
+ case 2:
24
+ return "middle";
25
+ case 3:
26
+ return "right";
29
27
  }
30
- positionForEvent(event) {
31
- const targetElement = event.target;
32
- if (targetElement.nodeName !== "CANVAS") return;
33
- return {
34
- x: event.offsetX,
35
- y: event.offsetY,
36
- };
37
- }
38
- consumeEvent(e) {
39
- e.preventDefault();
40
- e.stopPropagation();
41
- e.stopImmediatePropagation();
42
- }
43
- asCheckbox(node) {
44
- if (!node || node.nodeName !== "INPUT") return null;
45
- const inputElement = node;
46
- return ["checkbox", "radio"].includes(inputElement.type) ? inputElement : null;
47
- }
48
- isRangeInput(node) {
49
- if (!node || node.nodeName !== "INPUT") return false;
50
- const inputElement = node;
51
- return inputElement.type.toLowerCase() === "range";
52
- }
53
- addEventListener(target, eventName, listener, useCapture) {
54
- target.addEventListener(eventName, listener, useCapture);
55
- const remove = () => {
56
- target.removeEventListener(eventName, listener, useCapture);
57
- };
58
- return remove;
59
- }
60
- removeEventListeners(listeners) {
61
- for (const listener of listeners) listener();
62
- listeners.splice(0, listeners.length);
63
- }
28
+ return "left";
29
+ }
30
+ positionForEvent(event) {
31
+ const targetElement = event.target;
32
+ if (targetElement.nodeName !== "CANVAS") return;
33
+ return {
34
+ x: event.offsetX,
35
+ y: event.offsetY,
36
+ };
37
+ }
38
+ consumeEvent(e) {
39
+ e.preventDefault();
40
+ e.stopPropagation();
41
+ e.stopImmediatePropagation();
42
+ }
43
+ asCheckbox(node) {
44
+ if (!node || node.nodeName !== "INPUT") return null;
45
+ const inputElement = node;
46
+ return ["checkbox", "radio"].includes(inputElement.type) ? inputElement : null;
47
+ }
48
+ isRangeInput(node) {
49
+ if (!node || node.nodeName !== "INPUT") return false;
50
+ const inputElement = node;
51
+ return inputElement.type.toLowerCase() === "range";
52
+ }
53
+ addEventListener(target, eventName, listener, useCapture) {
54
+ target.addEventListener(eventName, listener, useCapture);
55
+ const remove = () => {
56
+ target.removeEventListener(eventName, listener, useCapture);
57
+ };
58
+ return remove;
59
+ }
60
+ removeEventListeners(listeners) {
61
+ for (const listener of listeners) listener();
62
+ listeners.splice(0, listeners.length);
63
+ }
64
64
 
65
- getNearestInteractiveElement(targetElement, root = window.document) {
66
- try {
67
- if (!targetElement.matches("input,textarea,select") && !targetElement.isContentEditable) {
68
- const interactiveParent = closestCrossShadow(
69
- targetElement,
70
- "button,select,input,[role=button],[role=checkbox],[role=radio],a,[role=link]",
71
- root
72
- );
73
- if (interactiveParent && isElementVisible(interactiveParent)) return interactiveParent;
74
- }
75
- return targetElement;
76
- } catch (e) {
77
- console.error(e);
78
- // bvtRecorderBindings.log(`Error in getNearestInteractiveElement: ${e.message}`);
79
- return targetElement;
80
- }
65
+ getNearestInteractiveElement(targetElement, root = window.document) {
66
+ try {
67
+ if (!targetElement.matches("input,textarea,select") && !targetElement.isContentEditable) {
68
+ const interactiveParent = closestCrossShadow(
69
+ targetElement,
70
+ "button,select,input,[role=button],[role=checkbox],[role=radio],a,[role=link]",
71
+ root
72
+ );
73
+ if (interactiveParent && isElementVisible(interactiveParent)) return interactiveParent;
74
+ }
75
+ return targetElement;
76
+ } catch (e) {
77
+ console.error(e);
78
+ // bvtRecorderBindings.log(`Error in getNearestInteractiveElement: ${e.message}`);
79
+ return targetElement;
81
80
  }
82
- shouldGenerateKeyPressFor(event) {
83
- // Enter aka. new line is handled in input event.
84
- if (
85
- event.key === "Enter" &&
86
- (this.deepEventTarget(event).nodeName === "TEXTAREA" || this.deepEventTarget(event).isContentEditable)
87
- )
88
- return false;
89
- // Backspace, Delete, AltGraph are changing input, will handle it there.
90
- if (["Backspace", "Delete", "AltGraph"].includes(event.key)) return false;
91
- // Ignore the QWERTZ shortcut for creating a at sign on MacOS
92
- if (event.key === "@" && event.code === "KeyL") return false;
93
- // Allow and ignore common used shortcut for pasting.
94
- if (navigator.platform.includes("Mac")) {
95
- if (event.key === "v" && event.metaKey) return false;
96
- } else {
97
- if (event.key === "v" && event.ctrlKey) return false;
98
- if (event.key === "Insert" && event.shiftKey) return false;
99
- }
100
- if (["Shift", "Control", "Meta", "Alt", "Process"].includes(event.key)) return false;
101
- const hasModifier = event.ctrlKey || event.altKey || event.metaKey;
102
- if (event.key.length === 1 && !hasModifier) return !!this.asCheckbox(this.deepEventTarget(event));
103
- return true;
81
+ }
82
+ shouldGenerateKeyPressFor(event) {
83
+ // Enter aka. new line is handled in input event.
84
+ if (
85
+ event.key === "Enter" &&
86
+ (this.deepEventTarget(event).nodeName === "TEXTAREA" || this.deepEventTarget(event).isContentEditable)
87
+ )
88
+ return false;
89
+ // Backspace, Delete, AltGraph are changing input, will handle it there.
90
+ if (["Backspace", "Delete", "AltGraph"].includes(event.key)) return false;
91
+ // Ignore the QWERTZ shortcut for creating a at sign on MacOS
92
+ if (event.key === "@" && event.code === "KeyL") return false;
93
+ // Allow and ignore common used shortcut for pasting.
94
+ if (navigator.platform.includes("Mac")) {
95
+ if (event.key === "v" && event.metaKey) return false;
96
+ } else {
97
+ if (event.key === "v" && event.ctrlKey) return false;
98
+ if (event.key === "Insert" && event.shiftKey) return false;
104
99
  }
105
- }
100
+ if (["Shift", "Control", "Meta", "Alt", "Process"].includes(event.key)) return false;
101
+ const hasModifier = event.ctrlKey || event.altKey || event.metaKey;
102
+ if (event.key.length === 1 && !hasModifier) return !!this.asCheckbox(this.deepEventTarget(event));
103
+ return true;
104
+ }
105
+ }