sefiutils 1.3.1 → 1.4.0
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/lib/fiberUtils.js +3 -14
- package/lib/fiberUtils.js.map +1 -1
- package/lib/globalSetup.js +107 -104
- package/lib/globalSetup.js.map +1 -1
- package/lib/globalTeardown.js +9 -20
- package/lib/globalTeardown.js.map +1 -1
- package/lib/localSetup.js +104 -113
- package/lib/localSetup.js.map +1 -1
- package/lib/localTeardown.js +8 -19
- package/lib/localTeardown.js.map +1 -1
- package/lib/seUtils.js +336 -409
- package/lib/seUtils.js.map +1 -1
- package/package.json +1 -1
- package/src/globalSetup.ts +4 -0
- package/src/seUtils.ts +18 -10
package/lib/seUtils.js
CHANGED
|
@@ -15,22 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
34
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
37
|
};
|
|
@@ -87,9 +88,8 @@ const fsPromises = __importStar(require("fs/promises"));
|
|
|
87
88
|
const path_1 = __importDefault(require("path"));
|
|
88
89
|
const selenium_webdriver_1 = require("selenium-webdriver");
|
|
89
90
|
function getRemoteAddressAndPort() {
|
|
90
|
-
var _a;
|
|
91
91
|
const address = process.env.SEFI_SELENIUM_REMOTE_URL || "http://localhost:4444/wd/hub";
|
|
92
|
-
const port = Number(
|
|
92
|
+
const port = Number(address.match(/:(\d+)\/?/)?.[1] || 80);
|
|
93
93
|
return { address, port };
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
@@ -109,19 +109,17 @@ function cmpStrings(str, stringOrRegEx, options) {
|
|
|
109
109
|
if (stringOrRegEx instanceof RegExp) {
|
|
110
110
|
return stringOrRegEx.test(str);
|
|
111
111
|
}
|
|
112
|
-
return (options
|
|
112
|
+
return (options?.ignoreCase)
|
|
113
113
|
? str.toLocaleLowerCase() === stringOrRegEx.toLocaleLowerCase()
|
|
114
114
|
: str === stringOrRegEx;
|
|
115
115
|
}
|
|
116
|
-
function execScript(driver, scriptName, fct) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const res = yield driver.executeScript(script + `
|
|
116
|
+
async function execScript(driver, scriptName, fct) {
|
|
117
|
+
const scriptPath = path_1.default.join(path_1.default.dirname(__filename), scriptName);
|
|
118
|
+
const script = fs.readFileSync(scriptPath, "utf-8");
|
|
119
|
+
const res = await driver.executeScript(script + `
|
|
121
120
|
return ${fct}();
|
|
122
121
|
`);
|
|
123
|
-
|
|
124
|
-
});
|
|
122
|
+
return res;
|
|
125
123
|
}
|
|
126
124
|
function boundsToString(bounds) {
|
|
127
125
|
return `(${bounds.left};${bounds.top})x(${bounds.right};${bounds.bottom})`;
|
|
@@ -145,134 +143,116 @@ function circa(n1, n2) {
|
|
|
145
143
|
const delta = n1 - n2;
|
|
146
144
|
return Math.abs(delta) < 1;
|
|
147
145
|
}
|
|
148
|
-
function waitFor(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
146
|
+
async function waitFor(callback, timeout = 1000, intervalTime = 100) {
|
|
147
|
+
// try first time
|
|
148
|
+
const start = new Date().getTime();
|
|
149
|
+
try {
|
|
150
|
+
const result = await callback();
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// ignore error
|
|
155
|
+
}
|
|
156
|
+
// if error, try until timeout ms have passed
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
const interval = setInterval(async () => {
|
|
159
|
+
try {
|
|
160
|
+
const result = await callback();
|
|
161
|
+
clearInterval(interval);
|
|
162
|
+
resolve(result);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
const duration = new Date().getTime() - start;
|
|
166
|
+
if (duration > timeout) {
|
|
164
167
|
clearInterval(interval);
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
const duration = new Date().getTime() - start;
|
|
169
|
-
if (duration > timeout) {
|
|
170
|
-
clearInterval(interval);
|
|
171
|
-
reject(error);
|
|
172
|
-
}
|
|
168
|
+
reject(error);
|
|
173
169
|
}
|
|
174
|
-
}
|
|
175
|
-
});
|
|
170
|
+
}
|
|
171
|
+
}, intervalTime);
|
|
176
172
|
});
|
|
177
173
|
}
|
|
178
174
|
/**
|
|
179
175
|
* Takes screenshot of current window and saves it to the given file.
|
|
180
176
|
* The folder is created if it does not yet exists.
|
|
181
177
|
*/
|
|
182
|
-
function saveScreenshot(driver, fileName) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
yield fsPromises.writeFile(fileName, pngBase64, 'base64');
|
|
190
|
-
});
|
|
178
|
+
async function saveScreenshot(driver, fileName) {
|
|
179
|
+
const pngBase64 = await driver.takeScreenshot();
|
|
180
|
+
const dirName = path_1.default.dirname(fileName);
|
|
181
|
+
if (!fs.existsSync(dirName)) {
|
|
182
|
+
await fsPromises.mkdir(dirName, { recursive: true });
|
|
183
|
+
}
|
|
184
|
+
await fsPromises.writeFile(fileName, pngBase64, 'base64');
|
|
191
185
|
}
|
|
192
186
|
/**
|
|
193
187
|
* Helper function for printing out an element (tag, id if present)
|
|
194
188
|
*/
|
|
195
|
-
function describe(we) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
s += "." + clazz;
|
|
206
|
-
}
|
|
207
|
-
let text = yield we.getText();
|
|
208
|
-
s += ` ("${text.substring(0, 7)}${text.length > 7 ? '…' : ''})"`;
|
|
189
|
+
async function describe(we) {
|
|
190
|
+
let s = await we.getTagName();
|
|
191
|
+
const id = await we.getAttribute("id");
|
|
192
|
+
if (id) {
|
|
193
|
+
s += "#" + id;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const clazz = await we.getAttribute("class");
|
|
197
|
+
if (clazz) {
|
|
198
|
+
s += "." + clazz;
|
|
209
199
|
}
|
|
210
|
-
|
|
211
|
-
|
|
200
|
+
let text = await we.getText();
|
|
201
|
+
s += ` ("${text.substring(0, 7)}${text.length > 7 ? '…' : ''})"`;
|
|
202
|
+
}
|
|
203
|
+
return s;
|
|
212
204
|
}
|
|
213
205
|
/**
|
|
214
206
|
* Gets the element by means of `WebElement` by its id attribute,
|
|
215
207
|
* either of the whole document (if driver is given) or of the given parent element.
|
|
216
208
|
*/
|
|
217
|
-
function getElementById(driverOrParent, id) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
return el;
|
|
221
|
-
});
|
|
209
|
+
async function getElementById(driverOrParent, id) {
|
|
210
|
+
const el = await driverOrParent.findElement(selenium_webdriver_1.By.id(id));
|
|
211
|
+
return el;
|
|
222
212
|
}
|
|
223
213
|
/**
|
|
224
214
|
* Gets the first element by means of `WebElement` by its class attribute,
|
|
225
215
|
* either of the whole document (if driver is given) or of the given parent element.
|
|
226
216
|
*/
|
|
227
|
-
function getElementByClass(driverOrParent, clazz) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return el;
|
|
231
|
-
});
|
|
217
|
+
async function getElementByClass(driverOrParent, clazz) {
|
|
218
|
+
const el = await driverOrParent.findElement(selenium_webdriver_1.By.className(clazz));
|
|
219
|
+
return el;
|
|
232
220
|
}
|
|
233
221
|
/**
|
|
234
222
|
* Gets all element by means of `WebElement` by its class attribute,
|
|
235
223
|
* either of the whole document (if driver is given) or of the given parent element.
|
|
236
224
|
*/
|
|
237
|
-
function getElementsByClass(driverOrParent, clazz) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return els;
|
|
241
|
-
});
|
|
225
|
+
async function getElementsByClass(driverOrParent, clazz) {
|
|
226
|
+
const els = await driverOrParent.findElements(selenium_webdriver_1.By.className(clazz));
|
|
227
|
+
return els;
|
|
242
228
|
}
|
|
243
229
|
/**
|
|
244
230
|
* Gets the first element by means of `WebElement` by its tag name,
|
|
245
231
|
* either of the whole document (if driver is given) or of the given parent element.
|
|
246
232
|
*/
|
|
247
|
-
function getElementByTag(driverOrParent, tagName) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return el;
|
|
251
|
-
});
|
|
233
|
+
async function getElementByTag(driverOrParent, tagName) {
|
|
234
|
+
const el = await driverOrParent.findElement(selenium_webdriver_1.By.css(tagName));
|
|
235
|
+
return el;
|
|
252
236
|
}
|
|
253
237
|
/**
|
|
254
238
|
* Gets all elements by means of `WebElement` by its tag name,
|
|
255
239
|
* either of the whole document (if driver is given) or of the given parent element.
|
|
256
240
|
*/
|
|
257
|
-
function getElementsByTag(driverOrParent, tagName) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
return els;
|
|
261
|
-
});
|
|
241
|
+
async function getElementsByTag(driverOrParent, tagName) {
|
|
242
|
+
const els = await driverOrParent.findElements(selenium_webdriver_1.By.css(tagName));
|
|
243
|
+
return els;
|
|
262
244
|
}
|
|
263
245
|
/**
|
|
264
246
|
* Gets all elements by means of `WebElement` by its tag name,
|
|
265
247
|
* either of the whole document (if driver is given) or of the given parent element.
|
|
266
248
|
*/
|
|
267
|
-
function getElementsByTags(driverOrParent, tagNames) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return els;
|
|
275
|
-
});
|
|
249
|
+
async function getElementsByTags(driverOrParent, tagNames) {
|
|
250
|
+
const els = [];
|
|
251
|
+
for (const tagName of tagNames) {
|
|
252
|
+
const elByTag = await driverOrParent.findElements(selenium_webdriver_1.By.css(tagName));
|
|
253
|
+
els.push(...elByTag);
|
|
254
|
+
}
|
|
255
|
+
return els;
|
|
276
256
|
}
|
|
277
257
|
/**
|
|
278
258
|
* Returns first element containing given text or undefined,
|
|
@@ -280,28 +260,24 @@ function getElementsByTags(driverOrParent, tagNames) {
|
|
|
280
260
|
*
|
|
281
261
|
* Test in browser console: `$x("//*[contains(text(), 'Hello')]")`
|
|
282
262
|
*/
|
|
283
|
-
function getElementContainingText(driverOrParent, text) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
});
|
|
263
|
+
async function getElementContainingText(driverOrParent, text) {
|
|
264
|
+
try {
|
|
265
|
+
// cf. https://stackoverflow.com/questions/3655549/xpath-containstext-some-string-doesnt-work-when-used-with-node-with-more
|
|
266
|
+
//const el = await driver.findElement(By.xpath(`//*[contains(text(), "${text}")]`));
|
|
267
|
+
const el = await driverOrParent.findElement(selenium_webdriver_1.By.xpath(`//*[text()[contains(., '${text}')]]`));
|
|
268
|
+
return el;
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
295
273
|
}
|
|
296
274
|
/**
|
|
297
275
|
* Returns the bounding client rect (the bounding box relative to the viewport)
|
|
298
276
|
* of an element (found by its id attribute) by means of a JavaScript call.
|
|
299
277
|
*/
|
|
300
|
-
function getBoundingClientRect(driver, id) {
|
|
301
|
-
return
|
|
302
|
-
|
|
303
|
-
return bcr;
|
|
304
|
-
});
|
|
278
|
+
async function getBoundingClientRect(driver, id) {
|
|
279
|
+
const bcr = await driver.executeScript('return document.getElementById("' + id + '").getBoundingClientRect()');
|
|
280
|
+
return bcr;
|
|
305
281
|
}
|
|
306
282
|
/**
|
|
307
283
|
* Returns all {@link getBoundingClientRect} values of the elements
|
|
@@ -311,37 +287,32 @@ function getBoundingClientRect(driver, id) {
|
|
|
311
287
|
*
|
|
312
288
|
* @param elementList JavaScript code to determine the element list
|
|
313
289
|
*/
|
|
314
|
-
function getBoundingClientRects(driver, jsElementList) {
|
|
315
|
-
|
|
316
|
-
const bcr = yield driver.executeScript(`return (()=>{
|
|
290
|
+
async function getBoundingClientRects(driver, jsElementList) {
|
|
291
|
+
const bcr = await driver.executeScript(`return (()=>{
|
|
317
292
|
const elements = ${jsElementList};
|
|
318
293
|
const bounds=[];
|
|
319
294
|
for (let el of elements) { bounds.push(el.getBoundingClientRect()) }
|
|
320
295
|
return bounds;
|
|
321
296
|
})()`);
|
|
322
|
-
|
|
323
|
-
});
|
|
297
|
+
return bcr;
|
|
324
298
|
}
|
|
325
299
|
/**
|
|
326
300
|
* Returns the node content's rect, in particular of the text, of a element with the given id.
|
|
327
301
|
*/
|
|
328
|
-
function getNodeContentsRect(driver, id) {
|
|
329
|
-
|
|
330
|
-
const bcr = yield driver.executeScript(`return (() => {
|
|
302
|
+
async function getNodeContentsRect(driver, id) {
|
|
303
|
+
const bcr = await driver.executeScript(`return (() => {
|
|
331
304
|
const range = document.createRange();
|
|
332
305
|
range.selectNodeContents(document.getElementById("${id}"));
|
|
333
306
|
return range.getClientRects()[0]
|
|
334
307
|
})()`);
|
|
335
|
-
|
|
336
|
-
});
|
|
308
|
+
return bcr;
|
|
337
309
|
}
|
|
338
310
|
/**
|
|
339
311
|
* Returns the node content's rect, in particular of the text, of multiple elements
|
|
340
312
|
* (selected via evalutation of the jsElementString).
|
|
341
313
|
*/
|
|
342
|
-
function getNodeContentsRects(driver, jsElementList) {
|
|
343
|
-
|
|
344
|
-
const bcr = yield driver.executeScript(`return (() => {
|
|
314
|
+
async function getNodeContentsRects(driver, jsElementList) {
|
|
315
|
+
const bcr = await driver.executeScript(`return (() => {
|
|
345
316
|
const elements = ${jsElementList};
|
|
346
317
|
const bounds=[];
|
|
347
318
|
for (let el of elements) {
|
|
@@ -351,42 +322,37 @@ function getNodeContentsRects(driver, jsElementList) {
|
|
|
351
322
|
}
|
|
352
323
|
return bounds
|
|
353
324
|
})()`);
|
|
354
|
-
|
|
355
|
-
});
|
|
325
|
+
return bcr;
|
|
356
326
|
}
|
|
357
327
|
/**
|
|
358
328
|
* Returns the rects using the driver API.
|
|
359
329
|
*/
|
|
360
|
-
function getRects(driver, locator) {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
return bounds;
|
|
372
|
-
});
|
|
330
|
+
async function getRects(driver, locator) {
|
|
331
|
+
const elements = await driver.findElements(locator);
|
|
332
|
+
const bounds = [];
|
|
333
|
+
for (const element of elements) {
|
|
334
|
+
const rect = await element.getRect();
|
|
335
|
+
bounds.push({
|
|
336
|
+
top: rect.x, left: rect.y,
|
|
337
|
+
bottom: rect.x + rect.width, right: rect.y + rect.height
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return bounds;
|
|
373
341
|
}
|
|
374
342
|
/**
|
|
375
343
|
* Returns the rects of the first child (of each found element) using the driver API.
|
|
376
344
|
*/
|
|
377
|
-
function getRectsOfContent(driver, locator) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
return bounds;
|
|
389
|
-
});
|
|
345
|
+
async function getRectsOfContent(driver, locator) {
|
|
346
|
+
const elements = await driver.findElements(locator);
|
|
347
|
+
const bounds = [];
|
|
348
|
+
for (const element of elements) {
|
|
349
|
+
const rect = await element.getRect();
|
|
350
|
+
bounds.push({
|
|
351
|
+
top: rect.x, left: rect.y,
|
|
352
|
+
bottom: rect.x + rect.width, right: rect.y + rect.height
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return bounds;
|
|
390
356
|
}
|
|
391
357
|
/**
|
|
392
358
|
* Returns all text content of the elements
|
|
@@ -399,23 +365,19 @@ function getRectsOfContent(driver, locator) {
|
|
|
399
365
|
*
|
|
400
366
|
* @param elementList JavaScript code to determine the element list
|
|
401
367
|
*/
|
|
402
|
-
function getTextContents(driver, jsElementList) {
|
|
403
|
-
|
|
404
|
-
const tcs = yield driver.executeScript(`const elements = ${jsElementList};
|
|
368
|
+
async function getTextContents(driver, jsElementList) {
|
|
369
|
+
const tcs = await driver.executeScript(`const elements = ${jsElementList};
|
|
405
370
|
const tcs=[];
|
|
406
371
|
for (let el of elements) { tcs.push(el.textContent) }
|
|
407
372
|
return tcs;
|
|
408
373
|
`);
|
|
409
|
-
|
|
410
|
-
});
|
|
374
|
+
return tcs;
|
|
411
375
|
}
|
|
412
376
|
/**
|
|
413
377
|
* Returns the text content of the whole document.
|
|
414
378
|
*/
|
|
415
|
-
function getDocumentTextContent(driver) {
|
|
416
|
-
return
|
|
417
|
-
return yield driver.executeScript(`return document.documentElement.textContent`);
|
|
418
|
-
});
|
|
379
|
+
async function getDocumentTextContent(driver) {
|
|
380
|
+
return await driver.executeScript(`return document.documentElement.textContent`);
|
|
419
381
|
}
|
|
420
382
|
;
|
|
421
383
|
/**
|
|
@@ -426,99 +388,84 @@ function getDocumentTextContent(driver) {
|
|
|
426
388
|
*
|
|
427
389
|
* @param elementList JavaScript code to determine the element list
|
|
428
390
|
*/
|
|
429
|
-
function getComputedStyles(driver, jsElementList, style) {
|
|
430
|
-
|
|
431
|
-
const tcs = yield driver.executeScript(`const elements = ${jsElementList};
|
|
391
|
+
async function getComputedStyles(driver, jsElementList, style) {
|
|
392
|
+
const tcs = await driver.executeScript(`const elements = ${jsElementList};
|
|
432
393
|
const tcs=[];
|
|
433
394
|
for (let el of elements) { tcs.push(getComputedStyle(el).${style}) }
|
|
434
395
|
return tcs;
|
|
435
396
|
`);
|
|
436
|
-
|
|
437
|
-
});
|
|
397
|
+
return tcs;
|
|
438
398
|
}
|
|
439
399
|
/**
|
|
440
400
|
* Returns the inner size of the windows bounding client rect (the bounding box relative to the viewport)
|
|
441
401
|
* of an element (found by its is attribute) by means of a JavaScript call.
|
|
442
402
|
* The window size contains the scrollbars. I.e. it is a bit larger than {@link getViewPortSize}.
|
|
443
403
|
*/
|
|
444
|
-
function getInnerSizeOfWindow(driver) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
return { width: w, height: h };
|
|
449
|
-
});
|
|
404
|
+
async function getInnerSizeOfWindow(driver) {
|
|
405
|
+
const w = await driver.executeScript('return window.innerWidth');
|
|
406
|
+
const h = await driver.executeScript('return window.innerHeight');
|
|
407
|
+
return { width: w, height: h };
|
|
450
408
|
}
|
|
451
409
|
/**
|
|
452
410
|
* Returns the viewport size, which is the inner window size but not with the scrollbars.
|
|
453
411
|
* I.e. the viewport size is a bit smaller than {@link getInnerSizeOfWindow} (if scrollbars are visible).
|
|
454
412
|
* The value is retrieved by means of JavaScript.
|
|
455
413
|
*/
|
|
456
|
-
function getViewPortSize(driver) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
return { width: clientWidthOfHTML, height: clientHeightOfHTML };
|
|
463
|
-
});
|
|
414
|
+
async function getViewPortSize(driver) {
|
|
415
|
+
const [clientWidthOfHTML, clientHeightOfHTML] = await Promise.all([
|
|
416
|
+
driver.executeScript("return document.documentElement.clientWidth"),
|
|
417
|
+
driver.executeScript("return document.documentElement.clientHeight")
|
|
418
|
+
]);
|
|
419
|
+
return { width: clientWidthOfHTML, height: clientHeightOfHTML };
|
|
464
420
|
}
|
|
465
421
|
/**
|
|
466
422
|
* Returns true if the element identified by its id attribute is with the view port.
|
|
467
423
|
* That does not necessarily means that it is visible, since its display or visible attribute may
|
|
468
424
|
* hide the element.
|
|
469
425
|
*/
|
|
470
|
-
function isInViewPort(driver, id) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
&& bounds.x < x + innerSize.width && bounds.y < y + innerSize.height;
|
|
480
|
-
});
|
|
426
|
+
async function isInViewPort(driver, id) {
|
|
427
|
+
const innerSize = await getInnerSizeOfWindow(driver);
|
|
428
|
+
const [x, y] = await Promise.all([
|
|
429
|
+
driver.executeScript('return window.scrollX'),
|
|
430
|
+
driver.executeScript('return window.scrollY')
|
|
431
|
+
]);
|
|
432
|
+
const bounds = await getBoundingClientRect(driver, id);
|
|
433
|
+
return bounds.x + bounds.width >= x && bounds.y + bounds.height >= y
|
|
434
|
+
&& bounds.x < x + innerSize.width && bounds.y < y + innerSize.height;
|
|
481
435
|
}
|
|
482
436
|
/**
|
|
483
437
|
* Sets the window so that the {@link getViewPortSize} matches the given width and height.
|
|
484
438
|
*/
|
|
485
|
-
function setBrowserWindowSize(driver, width, height) {
|
|
486
|
-
|
|
487
|
-
yield waitForSetRect(driver, width, height);
|
|
488
|
-
});
|
|
439
|
+
async function setBrowserWindowSize(driver, width, height) {
|
|
440
|
+
await waitForSetRect(driver, width, height);
|
|
489
441
|
}
|
|
490
|
-
function waitForSetRect(driver, width, height) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
}));
|
|
442
|
+
async function waitForSetRect(driver, width, height) {
|
|
443
|
+
const rectBefore = await driver.manage().window().getRect();
|
|
444
|
+
await driver.manage().window().setRect({ width: width, height: height });
|
|
445
|
+
return waitFor(async () => {
|
|
446
|
+
const rectAfter = await driver.manage().window().getRect();
|
|
447
|
+
if (rectAfter.width != width || rectAfter.height != height) {
|
|
448
|
+
throw new Error(`Cannot set window to size (${width}, ${height}). Size is probably too small or too large, got ${rectAfter.width}/${rectAfter.height}, was before ${rectBefore.width}/${rectBefore.height}.`);
|
|
449
|
+
}
|
|
500
450
|
});
|
|
501
451
|
}
|
|
502
452
|
/**
|
|
503
453
|
* Sets the window so that the {@link getViewPortSize} matches the given width and height. Warning: This does not work reliably.
|
|
504
454
|
*/
|
|
505
|
-
function setViewPortSize(driver, width, height) {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
function getInheritedBackgroundColorsByJS(driver, nodelist) {
|
|
520
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
521
|
-
const colors = yield driver.executeScript(`
|
|
455
|
+
async function setViewPortSize(driver, width, height) {
|
|
456
|
+
const rect = await driver.manage().window().getRect();
|
|
457
|
+
const innerBefore = await getInnerSizeOfWindow(driver);
|
|
458
|
+
const deltaX = rect.width - innerBefore.width;
|
|
459
|
+
const deltaY = rect.height - innerBefore.height;
|
|
460
|
+
await waitForSetRect(driver, width + deltaX, height + deltaY);
|
|
461
|
+
const [innerAfter, viewport] = await Promise.all([getInnerSizeOfWindow(driver), getViewPortSize(driver)]);
|
|
462
|
+
// adjust scrollbars if necessary
|
|
463
|
+
const sbHeight = innerAfter.height - viewport.height;
|
|
464
|
+
const sbWidth = innerAfter.width - viewport.width;
|
|
465
|
+
await waitForSetRect(driver, width + deltaX + sbWidth, height + deltaY + sbHeight);
|
|
466
|
+
}
|
|
467
|
+
async function getInheritedBackgroundColorsByJS(driver, nodelist) {
|
|
468
|
+
const colors = await driver.executeScript(`
|
|
522
469
|
const list = ${nodelist};
|
|
523
470
|
function getDefaultBackground() {
|
|
524
471
|
var div = document.createElement("div")
|
|
@@ -540,12 +487,10 @@ function getInheritedBackgroundColorsByJS(driver, nodelist) {
|
|
|
540
487
|
}
|
|
541
488
|
return colors;
|
|
542
489
|
`);
|
|
543
|
-
|
|
544
|
-
});
|
|
490
|
+
return colors;
|
|
545
491
|
}
|
|
546
|
-
function getInheritedColorsByJS(driver, nodelist) {
|
|
547
|
-
|
|
548
|
-
const colors = yield driver.executeScript(`
|
|
492
|
+
async function getInheritedColorsByJS(driver, nodelist) {
|
|
493
|
+
const colors = await driver.executeScript(`
|
|
549
494
|
const list = ${nodelist};
|
|
550
495
|
function getDefaultColor() {
|
|
551
496
|
var div = document.createElement("div")
|
|
@@ -567,8 +512,7 @@ function getInheritedColorsByJS(driver, nodelist) {
|
|
|
567
512
|
}
|
|
568
513
|
return colors;
|
|
569
514
|
`);
|
|
570
|
-
|
|
571
|
-
});
|
|
515
|
+
return colors;
|
|
572
516
|
}
|
|
573
517
|
function rgb2hex(rgba) {
|
|
574
518
|
return "#" +
|
|
@@ -579,9 +523,8 @@ function rgb2hex(rgba) {
|
|
|
579
523
|
: parseFloat(n)).toString(16).padStart(2, '0').replace('NaN', ''))
|
|
580
524
|
.join('');
|
|
581
525
|
}
|
|
582
|
-
function getAllTextNodes(driver) {
|
|
583
|
-
|
|
584
|
-
const res = yield driver.executeScript(`
|
|
526
|
+
async function getAllTextNodes(driver) {
|
|
527
|
+
const res = await driver.executeScript(`
|
|
585
528
|
|
|
586
529
|
const TRANSPARENT = "rgba(0, 0, 0, 0)";
|
|
587
530
|
const all = [];
|
|
@@ -609,23 +552,20 @@ collect(document.body);
|
|
|
609
552
|
|
|
610
553
|
return all;
|
|
611
554
|
`);
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
});
|
|
616
|
-
return res;
|
|
555
|
+
res.forEach(e => {
|
|
556
|
+
e.color = rgb2hex(e.color);
|
|
557
|
+
e.background = rgb2hex(e.background);
|
|
617
558
|
});
|
|
559
|
+
return res;
|
|
618
560
|
}
|
|
619
561
|
/**
|
|
620
562
|
* Retrieves all buttons (element button or input with property submit or button) or anchors with href.
|
|
621
563
|
* @param driver
|
|
622
564
|
* @returns
|
|
623
565
|
*/
|
|
624
|
-
function getButtonsAndHRefs(driver) {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
return elements;
|
|
628
|
-
});
|
|
566
|
+
async function getButtonsAndHRefs(driver) {
|
|
567
|
+
const elements = await execScript(driver, "execScripts/getButtonsAndHRefs.js", "getButtonsAndHRefs");
|
|
568
|
+
return elements;
|
|
629
569
|
}
|
|
630
570
|
/**
|
|
631
571
|
* Retrieves a button or link with the given text.
|
|
@@ -636,33 +576,30 @@ function getButtonsAndHRefs(driver) {
|
|
|
636
576
|
* @param posHint Position hint, if there are multiple matches (if -1, the first match is returned)
|
|
637
577
|
* @returns the matching element or undefined
|
|
638
578
|
*/
|
|
639
|
-
function findButtonOrHRefWithText(
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
texts.push(text);
|
|
649
|
-
candidates.push(el);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
if (candidates.length === 1) {
|
|
653
|
-
return candidates[0];
|
|
654
|
-
}
|
|
655
|
-
else {
|
|
656
|
-
const index = findBestMatchingIndex(texts, variations);
|
|
657
|
-
if (index >= 0) {
|
|
658
|
-
return candidates[index];
|
|
659
|
-
}
|
|
579
|
+
async function findButtonOrHRefWithText(driver, variations, posHint = -1) {
|
|
580
|
+
const clickableElements = (await getButtonsAndHRefs(driver));
|
|
581
|
+
const candidates = [];
|
|
582
|
+
const texts = [];
|
|
583
|
+
for (const el of clickableElements) {
|
|
584
|
+
const text = (await el.getText())?.trim();
|
|
585
|
+
if (text) { // ignore empty texts
|
|
586
|
+
texts.push(text);
|
|
587
|
+
candidates.push(el);
|
|
660
588
|
}
|
|
661
|
-
|
|
662
|
-
|
|
589
|
+
}
|
|
590
|
+
if (candidates.length === 1) {
|
|
591
|
+
return candidates[0];
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
const index = findBestMatchingIndex(texts, variations);
|
|
595
|
+
if (index >= 0) {
|
|
596
|
+
return candidates[index];
|
|
663
597
|
}
|
|
664
|
-
|
|
665
|
-
|
|
598
|
+
}
|
|
599
|
+
if (posHint >= 0 && posHint < candidates.length) {
|
|
600
|
+
return candidates[posHint];
|
|
601
|
+
}
|
|
602
|
+
return candidates[0]; // maybe undefined
|
|
666
603
|
}
|
|
667
604
|
/**
|
|
668
605
|
* Returns a submit button or any other button (if onlySubmit is false).
|
|
@@ -675,59 +612,54 @@ function findButtonOrHRefWithText(driver_1, variations_1) {
|
|
|
675
612
|
* @param onlySubmit only submit buttons are considered, otherwise, all buttons are considered
|
|
676
613
|
* @returns a button or undefined
|
|
677
614
|
*/
|
|
678
|
-
function findSubmitButton(
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
if (type === "submit") {
|
|
692
|
-
onlySubmitCandidates.push(el);
|
|
693
|
-
}
|
|
615
|
+
async function findSubmitButton(driver, textCandidates, posHint = -1, onlySubmit = true) {
|
|
616
|
+
const clickableElements = (await getButtonsAndHRefs(driver));
|
|
617
|
+
const candidates = [];
|
|
618
|
+
const onlySubmitCandidates = [];
|
|
619
|
+
for (const el of clickableElements) {
|
|
620
|
+
const type = (await el.getAttribute("type"))?.toLowerCase();
|
|
621
|
+
const tag = (await el.getTagName())?.toLowerCase();
|
|
622
|
+
if (tag === "button" || type == "submit" || type == "button") {
|
|
623
|
+
if (type === "submit" || !onlySubmit) {
|
|
624
|
+
candidates.push(el);
|
|
625
|
+
}
|
|
626
|
+
if (type === "submit") {
|
|
627
|
+
onlySubmitCandidates.push(el);
|
|
694
628
|
}
|
|
695
629
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}
|
|
699
|
-
if (onlySubmitCandidates.length === 1) {
|
|
700
|
-
return onlySubmitCandidates[0];
|
|
701
|
-
}
|
|
702
|
-
const texts = [];
|
|
703
|
-
for (const el of candidates) {
|
|
704
|
-
const text = yield el.getText();
|
|
705
|
-
texts.push(text);
|
|
706
|
-
}
|
|
707
|
-
const index = findBestMatchingIndex(texts, textCandidates);
|
|
708
|
-
if (index >= 0) {
|
|
709
|
-
return candidates[index];
|
|
710
|
-
}
|
|
711
|
-
if (posHint >= 0 && posHint < candidates.length) {
|
|
712
|
-
return candidates[posHint];
|
|
713
|
-
}
|
|
630
|
+
}
|
|
631
|
+
if (candidates.length <= 1) {
|
|
714
632
|
return candidates[0]; // maybe undefined
|
|
715
|
-
}
|
|
633
|
+
}
|
|
634
|
+
if (onlySubmitCandidates.length === 1) {
|
|
635
|
+
return onlySubmitCandidates[0];
|
|
636
|
+
}
|
|
637
|
+
const texts = [];
|
|
638
|
+
for (const el of candidates) {
|
|
639
|
+
const text = await el.getText();
|
|
640
|
+
texts.push(text);
|
|
641
|
+
}
|
|
642
|
+
const index = findBestMatchingIndex(texts, textCandidates);
|
|
643
|
+
if (index >= 0) {
|
|
644
|
+
return candidates[index];
|
|
645
|
+
}
|
|
646
|
+
if (posHint >= 0 && posHint < candidates.length) {
|
|
647
|
+
return candidates[posHint];
|
|
648
|
+
}
|
|
649
|
+
return candidates[0]; // maybe undefined
|
|
716
650
|
}
|
|
717
651
|
/**
|
|
718
652
|
* Scrolls to the element and clicks it.
|
|
719
653
|
* If element is not defined, an error is thrown.
|
|
720
654
|
*/
|
|
721
|
-
function scrollIntoViewAndClick(element) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
yield element.click();
|
|
730
|
-
});
|
|
655
|
+
async function scrollIntoViewAndClick(element) {
|
|
656
|
+
if (!element) {
|
|
657
|
+
throw new Error("Element is undefined");
|
|
658
|
+
}
|
|
659
|
+
const driver = element.getDriver();
|
|
660
|
+
await driver.executeScript("arguments[0].scrollIntoView(true);", element);
|
|
661
|
+
await new Promise(r => setTimeout(r, 700)); // let the page update, this should be super fast
|
|
662
|
+
await element.click();
|
|
731
663
|
}
|
|
732
664
|
/**
|
|
733
665
|
* Only exported for Testing purposes.
|
|
@@ -817,78 +749,75 @@ function findBestMatchingIndex(candidates, variations, maxDistance = 2) {
|
|
|
817
749
|
* @return The input element (or undefined), the associated label (or undefined) and how the input element was found (child, for, near, type or not found)
|
|
818
750
|
* @package findSelectAsWell If true, select elements are also considered; otherwise, only input and textarea elements are considered
|
|
819
751
|
*/
|
|
820
|
-
function getInputElementByLabel(
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const text = (yield weLabel.getText()).toLowerCase().replace(/:\s*/, "").trim();
|
|
835
|
-
txtLabels.push(text);
|
|
836
|
-
}
|
|
837
|
-
catch (error) {
|
|
838
|
-
txtLabels.push("");
|
|
839
|
-
}
|
|
752
|
+
async function getInputElementByLabel(driver, labelCandidates, typeHints, positionHint = -1, findSelectAsWell = false) {
|
|
753
|
+
const tagsToFind = ["input", "textarea"];
|
|
754
|
+
labelCandidates = labelCandidates.map(l => l.toLowerCase());
|
|
755
|
+
if (findSelectAsWell) {
|
|
756
|
+
tagsToFind.push("select");
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
const weLabels = await getElementsByTag(driver, "label");
|
|
760
|
+
if (weLabels.length >= 1) {
|
|
761
|
+
const txtLabels = [];
|
|
762
|
+
for (const weLabel of weLabels) {
|
|
763
|
+
try {
|
|
764
|
+
const text = (await weLabel.getText()).toLowerCase().replace(/:\s*/, "").trim();
|
|
765
|
+
txtLabels.push(text);
|
|
840
766
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
catch (error) {
|
|
851
|
-
// expected
|
|
852
|
-
}
|
|
853
|
-
// for and id is used:
|
|
854
|
-
const forAttribute = (yield weLabels[idxCandidate].getAttribute("for")) || "";
|
|
855
|
-
if (forAttribute !== "") {
|
|
856
|
-
const inputFields = yield getElementsByTags(driver, tagsToFind);
|
|
857
|
-
for (const webElement of inputFields) {
|
|
858
|
-
const id = yield webElement.getAttribute("id");
|
|
859
|
-
if (id === forAttribute) {
|
|
860
|
-
return [webElement, txtLabels[idxCandidate], "for"];
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
// near
|
|
865
|
-
const inputFields = yield driver.findElements((0, selenium_webdriver_1.locateWith)(selenium_webdriver_1.By.tagName("input")).near(weLabels[idxCandidate]));
|
|
767
|
+
catch (error) {
|
|
768
|
+
txtLabels.push("");
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
const idxCandidate = findBestMatchingIndex(txtLabels, labelCandidates);
|
|
772
|
+
if (idxCandidate >= 0) {
|
|
773
|
+
// field is child of label
|
|
774
|
+
try {
|
|
775
|
+
const inputFields = await getElementsByTags(weLabels[idxCandidate], tagsToFind);
|
|
866
776
|
if (inputFields.length > 0) {
|
|
867
|
-
return [inputFields[0], txtLabels[idxCandidate], "
|
|
777
|
+
return [inputFields[0], txtLabels[idxCandidate], "child"];
|
|
868
778
|
}
|
|
869
779
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
const
|
|
877
|
-
const
|
|
878
|
-
|
|
879
|
-
if (
|
|
880
|
-
return [webElement,
|
|
780
|
+
catch (error) {
|
|
781
|
+
// expected
|
|
782
|
+
}
|
|
783
|
+
// for and id is used:
|
|
784
|
+
const forAttribute = (await weLabels[idxCandidate].getAttribute("for")) || "";
|
|
785
|
+
if (forAttribute !== "") {
|
|
786
|
+
const inputFields = await getElementsByTags(driver, tagsToFind);
|
|
787
|
+
for (const webElement of inputFields) {
|
|
788
|
+
const id = await webElement.getAttribute("id");
|
|
789
|
+
if (id === forAttribute) {
|
|
790
|
+
return [webElement, txtLabels[idxCandidate], "for"];
|
|
881
791
|
}
|
|
882
792
|
}
|
|
883
|
-
|
|
793
|
+
}
|
|
794
|
+
// near
|
|
795
|
+
const inputFields = await driver.findElements((0, selenium_webdriver_1.locateWith)(selenium_webdriver_1.By.tagName("input")).near(weLabels[idxCandidate]));
|
|
796
|
+
if (inputFields.length > 0) {
|
|
797
|
+
return [inputFields[0], txtLabels[idxCandidate], "near"];
|
|
884
798
|
}
|
|
885
799
|
}
|
|
886
800
|
}
|
|
887
|
-
|
|
888
|
-
|
|
801
|
+
// try to find input field by type
|
|
802
|
+
const inputFields = await getElementsByTags(driver, tagsToFind);
|
|
803
|
+
if (inputFields.length >= 1) {
|
|
804
|
+
let pos = 0;
|
|
805
|
+
for (const webElement of inputFields) {
|
|
806
|
+
const type = (await webElement.getAttribute("type"))?.toLowerCase();
|
|
807
|
+
const tag = (await webElement.getTagName()).toLowerCase();
|
|
808
|
+
if (typeHints.includes(type) || typeHints.includes(tag)) {
|
|
809
|
+
if (pos >= positionHint) {
|
|
810
|
+
return [webElement, undefined, "type"];
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
pos++;
|
|
814
|
+
}
|
|
889
815
|
}
|
|
890
|
-
|
|
891
|
-
|
|
816
|
+
}
|
|
817
|
+
catch (error) {
|
|
818
|
+
console.log("Error in getInputElementByLabel", error);
|
|
819
|
+
}
|
|
820
|
+
return [undefined, undefined, "not found"];
|
|
892
821
|
}
|
|
893
822
|
// from https://github.com/tj/commander.js/blob/master/lib/suggestSimilar.js
|
|
894
823
|
function editDistance(a, b, maxDistance) {
|
|
@@ -933,23 +862,21 @@ function editDistance(a, b, maxDistance) {
|
|
|
933
862
|
/**
|
|
934
863
|
* Returns true if the two web elements are equal, i.e. have the same id or same tag, location, and size.
|
|
935
864
|
*/
|
|
936
|
-
function equalWebElements(a, b) {
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
return aTag === bTag && aLocation.x === bLocation.x && aLocation.y === bLocation.y && aSize.width === bSize.width && aSize.height === bSize.height;
|
|
953
|
-
});
|
|
865
|
+
async function equalWebElements(a, b) {
|
|
866
|
+
if (a === b)
|
|
867
|
+
return true;
|
|
868
|
+
if (!a || !b)
|
|
869
|
+
return false;
|
|
870
|
+
const aId = await a.getId();
|
|
871
|
+
const bId = await b.getId();
|
|
872
|
+
if (aId)
|
|
873
|
+
return aId === bId;
|
|
874
|
+
const aLocation = await a.getLocation();
|
|
875
|
+
const bLocation = await b.getLocation();
|
|
876
|
+
const aSize = await a.getSize();
|
|
877
|
+
const bSize = await b.getSize();
|
|
878
|
+
const aTag = await a.getTagName();
|
|
879
|
+
const bTag = await b.getTagName();
|
|
880
|
+
return aTag === bTag && aLocation.x === bLocation.x && aLocation.y === bLocation.y && aSize.width === bSize.width && aSize.height === bSize.height;
|
|
954
881
|
}
|
|
955
882
|
//# sourceMappingURL=seUtils.js.map
|