@mablhq/mabl-cli 1.13.14 → 1.13.21
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/api/basicApiClient.js +53 -16
- package/api/mablApiClient.js +1 -1
- package/browserLauncher/playwrightBrowserLauncher/playwrightFrame.js +10 -0
- package/execution/index.js +1 -1
- package/package.json +3 -3
- package/popupDismissal/index.js +20 -13
- package/resources/popupDismissal.js +1 -1
- package/util/actionabilityUtil.js +29 -8
- package/util/logUtils.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mablhq/mabl-cli",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.21",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"description": "The official mabl command line interface tool",
|
|
6
6
|
"main": "index.js",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"postinstall": "node ./util/postInstallMessage.js"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@playwright/test": "1.17.
|
|
29
|
-
"playwright-core": "1.17.
|
|
28
|
+
"@playwright/test": "1.17.2",
|
|
29
|
+
"playwright-core": "1.17.2",
|
|
30
30
|
"@types/fs-extra": "^8.1.0",
|
|
31
31
|
"@types/serve-handler": "^6.1.0",
|
|
32
32
|
"@types/tmp": "^0.2.0",
|
package/popupDismissal/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.elementsInFrontCount = exports.detectAndDismissPopupCandidates = exports.getAllElementsAbove = exports.elementsByHigherZIndex = void 0;
|
|
3
|
+
exports.elementsInFrontCount = exports.detectAndDismissPopupCandidates = exports.getAllElementsAbove = exports.elementsAtCoordinate = exports.elementsByHigherZIndex = void 0;
|
|
4
4
|
const DISMISSAL_WAIT_TIME_MS = 750;
|
|
5
5
|
const ZINDEX_AUTO = 'auto';
|
|
6
6
|
const MINIMUM_POPUP_DISPLAY_COVERAGE_PX = 0.95;
|
|
@@ -40,9 +40,9 @@ function getEffectiveZIndex(element) {
|
|
|
40
40
|
const isRootElement = (element) => element === undefined || element.tagName.toLowerCase() === 'html';
|
|
41
41
|
let current = element;
|
|
42
42
|
while (!isRootElement(current)) {
|
|
43
|
-
const zIndex = window.getComputedStyle(
|
|
43
|
+
const zIndex = window.getComputedStyle(current).zIndex;
|
|
44
44
|
if (zIndex && zIndex !== ZINDEX_AUTO) {
|
|
45
|
-
return zIndex;
|
|
45
|
+
return parseInt(zIndex, 10);
|
|
46
46
|
}
|
|
47
47
|
current = (current === null || current === void 0 ? void 0 : current.parentElement) ? current.parentElement : undefined;
|
|
48
48
|
}
|
|
@@ -135,13 +135,14 @@ function fireClickEvent(targetElement, leftEdgeClick) {
|
|
|
135
135
|
});
|
|
136
136
|
targetElement.dispatchEvent(mouseEvent);
|
|
137
137
|
}
|
|
138
|
-
function
|
|
138
|
+
function elementsAtCoordinate(element, pointX, pointY) {
|
|
139
139
|
const x = pointX !== undefined ? pointX : getBoundingClientRectWithXY(element).x;
|
|
140
140
|
const y = pointY !== undefined ? pointY : getBoundingClientRectWithXY(element).y;
|
|
141
141
|
const elementsAtPoint = document.elementsFromPoint(x, y);
|
|
142
142
|
const targetIndex = elementsAtPoint.findIndex((iterElement) => iterElement === element);
|
|
143
143
|
return targetIndex ? elementsAtPoint.slice(0, targetIndex) : [];
|
|
144
144
|
}
|
|
145
|
+
exports.elementsAtCoordinate = elementsAtCoordinate;
|
|
145
146
|
function sleep(time) {
|
|
146
147
|
return new Promise((resolve) => setTimeout(resolve, time));
|
|
147
148
|
}
|
|
@@ -158,8 +159,15 @@ function getBoundingClientRectWithXY(element) {
|
|
|
158
159
|
width: Math.floor(rect.right) - Math.ceil(rect.left),
|
|
159
160
|
};
|
|
160
161
|
}
|
|
161
|
-
function getAllElementsAbove(elements) {
|
|
162
|
-
|
|
162
|
+
function getAllElementsAbove(targetElement, elements) {
|
|
163
|
+
if (elements.length === 0) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
const targetZindex = getEffectiveZIndex(targetElement);
|
|
167
|
+
const zIndexAboveTarget = Math.min(...elements === null || elements === void 0 ? void 0 : elements.map((el) => getEffectiveZIndex(el)));
|
|
168
|
+
if (zIndexAboveTarget <= targetZindex) {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
163
171
|
const higherElementsByZIndex = elementsByHigherZIndex(zIndexAboveTarget);
|
|
164
172
|
const allElementsByZIndex = Array.from(higherElementsByZIndex).map(([item, higherElements]) => {
|
|
165
173
|
const allElements = higherElements
|
|
@@ -174,7 +182,7 @@ exports.getAllElementsAbove = getAllElementsAbove;
|
|
|
174
182
|
async function detectAndDismissPopupCandidates(element) {
|
|
175
183
|
const elementBoundingBox = getBoundingClientRectWithXY(element);
|
|
176
184
|
try {
|
|
177
|
-
let higherElements =
|
|
185
|
+
let higherElements = elementsAtCoordinate(element, elementBoundingBox.x, elementBoundingBox.y);
|
|
178
186
|
const elementsInFrontCount = higherElements.length;
|
|
179
187
|
if (elementsInFrontCount === 0) {
|
|
180
188
|
return {
|
|
@@ -186,14 +194,13 @@ async function detectAndDismissPopupCandidates(element) {
|
|
|
186
194
|
for (const candidate of candidates) {
|
|
187
195
|
fireClickEvent(candidate, leftEdgeClick);
|
|
188
196
|
await sleep(DISMISSAL_WAIT_TIME_MS);
|
|
189
|
-
if (
|
|
190
|
-
.length < elementsInFrontCount) {
|
|
197
|
+
if (elementsAtCoordinate(element, elementBoundingBox.x, elementBoundingBox.y).length < elementsInFrontCount) {
|
|
191
198
|
return true;
|
|
192
199
|
}
|
|
193
200
|
}
|
|
194
201
|
return false;
|
|
195
202
|
};
|
|
196
|
-
let allElements = getAllElementsAbove(higherElements);
|
|
203
|
+
let allElements = getAllElementsAbove(element, higherElements);
|
|
197
204
|
const candidatesByZIndex = developCloseCandidates(allElements);
|
|
198
205
|
for (const candidate of candidatesByZIndex) {
|
|
199
206
|
let matched = await clickCandidates(candidate.domCovering, true);
|
|
@@ -207,8 +214,8 @@ async function detectAndDismissPopupCandidates(element) {
|
|
|
207
214
|
};
|
|
208
215
|
}
|
|
209
216
|
}
|
|
210
|
-
higherElements =
|
|
211
|
-
allElements = getAllElementsAbove(higherElements);
|
|
217
|
+
higherElements = elementsAtCoordinate(element, elementBoundingBox.x, elementBoundingBox.y);
|
|
218
|
+
allElements = getAllElementsAbove(element, higherElements);
|
|
212
219
|
const candidates = developCloseCandidates(allElements);
|
|
213
220
|
const result = candidates.length
|
|
214
221
|
? candidates[0]
|
|
@@ -236,6 +243,6 @@ async function detectAndDismissPopupCandidates(element) {
|
|
|
236
243
|
}
|
|
237
244
|
exports.detectAndDismissPopupCandidates = detectAndDismissPopupCandidates;
|
|
238
245
|
function elementsInFrontCount(element) {
|
|
239
|
-
return
|
|
246
|
+
return elementsAtCoordinate(element).length;
|
|
240
247
|
}
|
|
241
248
|
exports.elementsInFrontCount = elementsInFrontCount;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
window.popupDismissal=function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=0)}([function(t,e,n){"use strict";n.r(e),n.d(e,"elementsByHigherZIndex",(function(){return i})),n.d(e,"getAllElementsAbove",(function(){return
|
|
1
|
+
window.popupDismissal=function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=0)}([function(t,e,n){"use strict";n.r(e),n.d(e,"elementsByHigherZIndex",(function(){return i})),n.d(e,"elementsAtCoordinate",(function(){return d})),n.d(e,"getAllElementsAbove",(function(){return h})),n.d(e,"detectAndDismissPopupCandidates",(function(){return g})),n.d(e,"elementsInFrontCount",(function(){return p}));const o=["close","dismiss","exit","not at this time","no thanks","no, thanks","i, agree","i agree"],r=[{attributeName:"className",matchFunction:a},{attributeName:"name",matchFunction:a},{attributeName:"id",matchFunction:a},{attributeName:"href",matchFunction:a},{attributeName:"innerText",matchFunction:function(t,e){const n=new RegExp(`\\b${e}\\b`);return!!t.toLowerCase().match(n)}}];function i(t){return Array.from(document.querySelectorAll("body *")).reduce((e,n)=>{const o=parseFloat(window.getComputedStyle(n).zIndex);return!Number.isNaN(o)&&o>=t&&(e.has(o)?e.get(o).push(n):e.set(o,[n])),e},new Map)}function u(t){const e=t=>void 0===t||"html"===t.tagName.toLowerCase();let n=t;for(;!e(n);){const t=window.getComputedStyle(n).zIndex;if(t&&"auto"!==t)return parseInt(t,10);n=(null==n?void 0:n.parentElement)?n.parentElement:void 0}return 0}function s(t){let e=[t];return Array.from(t.children).forEach(t=>{e=e.concat(s(t))}),e}function a(t,e){return t.toLowerCase().includes(e)}function c(t){const e=[];return t.forEach(t=>{const n=[],i=[];t.elements.forEach(t=>{var e,u;(null===(e=t)||void 0===e?void 0:e.offsetParent)&&(["BUTTON","A","DIV"].includes(null===(u=t.tagName)||void 0===u?void 0:u.toUpperCase())&&function(t){let e=0;return r.forEach(n=>{o.forEach(o=>{t.getAttribute(n.attributeName)&&n.matchFunction(t.getAttribute(n.attributeName),o)&&(e+=1)})}),e}(t)&&!i.includes(t)?i.push(t):function(t){if(t.getBoundingClientRect){const e=t.getBoundingClientRect(),n=e.width/window.innerWidth,o=e.height/window.innerHeight;return n>.95&&o>.95}return!1}(t)&&n.push(t))}),e.push({zIndex:t.zIndex,actionableElements:i,domCovering:n,dismissedStatus:!1,elementsInFront:0})}),e}function l(t,e){const n=t.getBoundingClientRect(),o=n.height/2,r=e?1:n.width/2,i=new MouseEvent("click",{bubbles:!0,cancelable:!0,view:window,detail:0,screenX:n.left+r,screenY:n.top+o,clientX:t.clientLeft+r,clientY:t.clientTop+o,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,button:0,relatedTarget:void 0});t.dispatchEvent(i)}function d(t,e,n){const o=void 0!==e?e:m(t).x,r=void 0!==n?n:m(t).y,i=document.elementsFromPoint(o,r),u=i.findIndex(e=>e===t);return u?i.slice(0,u):[]}function f(t){return new Promise(e=>setTimeout(e,t))}function m(t){const e=t.getBoundingClientRect();return{x:Math.floor(e.x),y:Math.floor(e.y),top:Math.floor(e.top),bottom:Math.ceil(e.bottom),left:Math.floor(e.left),right:Math.ceil(e.right),height:Math.floor(e.bottom)-Math.ceil(e.top),width:Math.floor(e.right)-Math.ceil(e.left)}}function h(t,e){if(0===e.length)return[];const n=u(t),o=Math.min(...null==e?void 0:e.map(t=>u(t)));if(o<=n)return[];const r=i(o),a=Array.from(r).map(([t,e])=>({zIndex:t,elements:e.flatMap(s).reverse()}));return a.sort((t,e)=>e.zIndex-t.zIndex),a}async function g(t){const e=m(t);try{let n=d(t,e.x,e.y);const o=n.length;if(0===o)return{elementsInFront:0,dismissedStatus:!1};const r=async function(n,r){for(const i of n)if(l(i,r),await f(750),d(t,e.x,e.y).length<o)return!0;return!1};let i=h(t,n);const u=c(i);for(const t of u){let e=await r(t.domCovering,!0);if(e||(e=await r(t.actionableElements,!1)),e)return{dismissedStatus:!0,elementsInFront:0}}n=d(t,e.x,e.y),i=h(t,n);const s=c(i),a=s.length?s[0]:{zIndex:0,elementsInFront:0,dismissedStatus:!1,domCovering:[],actionableElements:[]};return a.elementsInFront=n.length,a.dismissedStatus=!1,{dismissedStatus:a.dismissedStatus,elementsInFront:a.elementsInFront}}catch(t){return{elementsInFront:0,dismissedStatus:!1,error:t.toString()}}}function p(t){return d(t).length}}]);
|
|
@@ -29,6 +29,7 @@ const loggingProvider_1 = require("../providers/logging/loggingProvider");
|
|
|
29
29
|
const ACTIONABILITY_CHECK_SCRIPT_LOCATION = resourceUtil_1.findResource('actionabilityCheck.js');
|
|
30
30
|
const EMBEDDED_POPUP_SCRIPT_LOCATION = resourceUtil_1.findResource('popupDismissal.js');
|
|
31
31
|
const NO_CLICKABLE_POINT_ERROR = 'Unable to find a clickable point for the element';
|
|
32
|
+
const POPUP_ERROR_MESSAGE_HINT = 'intercepts pointer events';
|
|
32
33
|
let embeddedPopupScript;
|
|
33
34
|
let embeddedActionabilityLogic;
|
|
34
35
|
async function checkPopupDismissalOnAction(elementHandle, action, executionContext, recheckOnFail = true) {
|
|
@@ -36,15 +37,21 @@ async function checkPopupDismissalOnAction(elementHandle, action, executionConte
|
|
|
36
37
|
await action();
|
|
37
38
|
}
|
|
38
39
|
catch (error) {
|
|
39
|
-
if (error.message.includes(
|
|
40
|
-
await
|
|
40
|
+
if (error.message.includes(POPUP_ERROR_MESSAGE_HINT)) {
|
|
41
|
+
const hasPopup = await isElementWithPopup(elementHandle);
|
|
42
|
+
if (hasPopup) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const dismissalResult = await attemptPopupDismissal(elementHandle, executionContext);
|
|
41
46
|
if (recheckOnFail) {
|
|
42
47
|
return checkPopupDismissalOnAction(elementHandle, action, executionContext, false);
|
|
43
48
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
if (!dismissalResult) {
|
|
50
|
+
logUtils_1.logWebUIAndCliOutput(`Found element may not be actionable`, loggingProvider_1.LogLevel.Warn, executionContext, {
|
|
51
|
+
executionPhase: messaging_1.ExecutionPhase.DURING_ACTION,
|
|
52
|
+
});
|
|
53
|
+
logUtils_1.logInternal(`Found element might not be actionable. ${error.toString()}`);
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
}
|
|
@@ -92,7 +99,14 @@ async function checkActionabilityAt(elementHandle, executionContext, recheckOnFa
|
|
|
92
99
|
if (result !== 'done') {
|
|
93
100
|
if (typeof result === 'object' && 'actionabilityDescription' in result) {
|
|
94
101
|
logUtils_1.logInternal(`${result.actionabilityDescription} intercepts pointer events. Trying to auto-dismiss popups.`);
|
|
95
|
-
await
|
|
102
|
+
const hasPopup = await isElementWithPopup(elementHandle);
|
|
103
|
+
if (hasPopup) {
|
|
104
|
+
return {
|
|
105
|
+
result: 'done',
|
|
106
|
+
clickablePoint: point,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
await attemptPopupDismissal(elementHandle, executionContext);
|
|
96
110
|
if (recheckOnFail) {
|
|
97
111
|
return checkActionabilityAt(elementHandle, executionContext, false);
|
|
98
112
|
}
|
|
@@ -103,7 +117,7 @@ async function checkActionabilityAt(elementHandle, executionContext, recheckOnFa
|
|
|
103
117
|
return { result, clickablePoint: point };
|
|
104
118
|
}
|
|
105
119
|
exports.checkActionabilityAt = checkActionabilityAt;
|
|
106
|
-
async function attemptPopupDismissal(element) {
|
|
120
|
+
async function attemptPopupDismissal(element, executionContext) {
|
|
107
121
|
const frame = await element.frame();
|
|
108
122
|
if (frame === undefined) {
|
|
109
123
|
return false;
|
|
@@ -131,6 +145,9 @@ async function attemptPopupDismissal(element) {
|
|
|
131
145
|
return false;
|
|
132
146
|
}
|
|
133
147
|
}
|
|
148
|
+
logUtils_1.logWebUIAndCliOutput(`A popup was detected and dismissed`, loggingProvider_1.LogLevel.Info, executionContext, {
|
|
149
|
+
executionPhase: messaging_1.ExecutionPhase.DURING_ACTION,
|
|
150
|
+
});
|
|
134
151
|
return true;
|
|
135
152
|
}
|
|
136
153
|
exports.attemptPopupDismissal = attemptPopupDismissal;
|
|
@@ -159,3 +176,7 @@ function isActionabilityLogicInTarget(element) {
|
|
|
159
176
|
function isPopupDismissalInTarget(element) {
|
|
160
177
|
return element.evaluate(() => !!window.popupDismissal);
|
|
161
178
|
}
|
|
179
|
+
function isElementWithPopup(element) {
|
|
180
|
+
return element.evaluate((element) => element.getAttribute('aria-expanded') === 'true' &&
|
|
181
|
+
element.getAttribute('aria-haspopup') === 'true');
|
|
182
|
+
}
|
package/util/logUtils.js
CHANGED
|
@@ -8,8 +8,8 @@ const mablscriptFind_1 = require("../mablscriptFind");
|
|
|
8
8
|
const loggingProvider_1 = require("../providers/logging/loggingProvider");
|
|
9
9
|
const moment_1 = __importDefault(require("moment"));
|
|
10
10
|
const constants_1 = require("../commands/constants");
|
|
11
|
-
function logInternal(logLine) {
|
|
12
|
-
loggingProvider_1.logger.debug(logLine);
|
|
11
|
+
function logInternal(logLine, ...meta) {
|
|
12
|
+
loggingProvider_1.logger.debug(logLine, meta);
|
|
13
13
|
}
|
|
14
14
|
exports.logInternal = logInternal;
|
|
15
15
|
function logCliOutput(logLevel, logLine) {
|