@percy/webdriver-utils 1.27.5-beta.0 → 1.27.5-beta.1
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/dist/driver.js +25 -0
- package/dist/providers/genericProvider.js +131 -22
- package/package.json +5 -5
package/dist/driver.js
CHANGED
|
@@ -71,4 +71,29 @@ export default class Driver {
|
|
|
71
71
|
const response = JSON.parse((await request(baseUrl, options)).body);
|
|
72
72
|
return response.value;
|
|
73
73
|
}
|
|
74
|
+
async findElementBoundingBox(using, value) {
|
|
75
|
+
if (using === 'xpath') {
|
|
76
|
+
return await this.findElementXpath(value);
|
|
77
|
+
} else if (using === 'css selector') {
|
|
78
|
+
return await this.findElementSelector(value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async findElementXpath(xpath) {
|
|
82
|
+
xpath = xpath.replace(/'/g, '"');
|
|
83
|
+
const command = {
|
|
84
|
+
script: `return document.evaluate('${xpath}', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.getBoundingClientRect();`,
|
|
85
|
+
args: []
|
|
86
|
+
};
|
|
87
|
+
const response = await this.executeScript(command);
|
|
88
|
+
return response.value;
|
|
89
|
+
}
|
|
90
|
+
async findElementSelector(selector) {
|
|
91
|
+
selector = selector.replace('\\', '\\\\');
|
|
92
|
+
const command = {
|
|
93
|
+
script: `return document.querySelector('${selector}').getBoundingClientRect();`,
|
|
94
|
+
args: []
|
|
95
|
+
};
|
|
96
|
+
const response = await this.executeScript(command);
|
|
97
|
+
return response.value;
|
|
98
|
+
}
|
|
74
99
|
}
|
|
@@ -21,6 +21,14 @@ export default class GenericProvider {
|
|
|
21
21
|
this.debugUrl = null;
|
|
22
22
|
this.header = 0;
|
|
23
23
|
this.footer = 0;
|
|
24
|
+
this.statusBarHeight = 0;
|
|
25
|
+
this.pageXShiftFactor = 0;
|
|
26
|
+
this.pageYShiftFactor = 0;
|
|
27
|
+
this.currentTag = null;
|
|
28
|
+
this.removeElementShiftFactor = 50000;
|
|
29
|
+
this.initialScrollFactor = {
|
|
30
|
+
value: [0, 0]
|
|
31
|
+
};
|
|
24
32
|
}
|
|
25
33
|
addDefaultOptions() {
|
|
26
34
|
this.options.freezeAnimation = this.options.freezeAnimatedImage || this.options.freezeAnimation || false;
|
|
@@ -45,6 +53,22 @@ export default class GenericProvider {
|
|
|
45
53
|
if (i) this.environmentInfo.add(i);
|
|
46
54
|
}
|
|
47
55
|
}
|
|
56
|
+
async getInitialPosition() {
|
|
57
|
+
if (this.currentTag.osName === 'iOS') {
|
|
58
|
+
this.initialScrollFactor = await this.driver.executeScript({
|
|
59
|
+
script: 'return [parseInt(window.scrollX), parseInt(window.scrollY)];',
|
|
60
|
+
args: []
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async scrollToInitialPosition(x, y) {
|
|
65
|
+
if (this.currentTag.osName === 'iOS') {
|
|
66
|
+
await this.driver.executeScript({
|
|
67
|
+
script: `window.scrollTo(${x}, ${y})`,
|
|
68
|
+
args: []
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
48
72
|
async screenshot(name, {
|
|
49
73
|
ignoreRegionXpaths = [],
|
|
50
74
|
ignoreRegionSelectors = [],
|
|
@@ -63,6 +87,8 @@ export default class GenericProvider {
|
|
|
63
87
|
log.debug(`[${name}] : Tag ${JSON.stringify(tag)}`);
|
|
64
88
|
const tiles = await this.getTiles(this.header, this.footer, fullscreen);
|
|
65
89
|
log.debug(`[${name}] : Tiles ${JSON.stringify(tiles)}`);
|
|
90
|
+
this.currentTag = tag;
|
|
91
|
+
this.statusBarHeight = tiles.tiles[0].statusBarHeight;
|
|
66
92
|
const ignoreRegions = await this.findRegions(ignoreRegionXpaths, ignoreRegionSelectors, ignoreRegionElements, customIgnoreRegions);
|
|
67
93
|
const considerRegions = await this.findRegions(considerRegionXpaths, considerRegionSelectors, considerRegionElements, customConsiderRegions);
|
|
68
94
|
await this.setDebugUrl();
|
|
@@ -144,29 +170,62 @@ export default class GenericProvider {
|
|
|
144
170
|
async setDebugUrl() {
|
|
145
171
|
this.debugUrl = 'https://localhost/v1';
|
|
146
172
|
}
|
|
173
|
+
async doTransformations() {
|
|
174
|
+
const hideScrollbarStyle = `
|
|
175
|
+
/* Hide scrollbar for Chrome, Safari and Opera */
|
|
176
|
+
::-webkit-scrollbar {
|
|
177
|
+
display: none !important;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* Hide scrollbar for IE, Edge and Firefox */
|
|
181
|
+
body, html {
|
|
182
|
+
-ms-overflow-style: none !important; /* IE and Edge */
|
|
183
|
+
scrollbar-width: none !important; /* Firefox */
|
|
184
|
+
}`.replace(/\n/g, '');
|
|
185
|
+
const jsScript = `
|
|
186
|
+
const e = document.createElement('style');
|
|
187
|
+
e.setAttribute('class', 'poa-injected');
|
|
188
|
+
e.innerHTML = '${hideScrollbarStyle}'
|
|
189
|
+
document.head.appendChild(e);`;
|
|
190
|
+
await this.driver.executeScript({
|
|
191
|
+
script: jsScript,
|
|
192
|
+
args: []
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async undoTransformations(data) {
|
|
196
|
+
const jsScript = `
|
|
197
|
+
const n = document.querySelectorAll('${data}');
|
|
198
|
+
n.forEach((e) => {e.remove()});`;
|
|
199
|
+
await this.driver.executeScript({
|
|
200
|
+
script: jsScript,
|
|
201
|
+
args: []
|
|
202
|
+
});
|
|
203
|
+
}
|
|
147
204
|
async findRegions(xpaths, selectors, elements, customLocations) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
205
|
+
let isRegionPassed = [xpaths, selectors, elements, customLocations].some(regions => regions.length > 0);
|
|
206
|
+
if (isRegionPassed) {
|
|
207
|
+
await this.doTransformations();
|
|
208
|
+
const xpathRegions = await this.getSeleniumRegionsBy('xpath', xpaths);
|
|
209
|
+
const selectorRegions = await this.getSeleniumRegionsBy('css selector', selectors);
|
|
210
|
+
const elementRegions = await this.getSeleniumRegionsByElement(elements);
|
|
211
|
+
const customRegions = await this.getSeleniumRegionsByLocation(customLocations);
|
|
212
|
+
await this.undoTransformations('.poa-injected');
|
|
213
|
+
return [...xpathRegions, ...selectorRegions, ...elementRegions, ...customRegions];
|
|
214
|
+
} else {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
153
217
|
}
|
|
154
|
-
async
|
|
155
|
-
const scaleFactor =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
};
|
|
161
|
-
const size = {
|
|
162
|
-
height: rect.height,
|
|
163
|
-
width: rect.width
|
|
164
|
-
};
|
|
218
|
+
async getRegionObjectFromBoundingBox(selector, element) {
|
|
219
|
+
const scaleFactor = await this.metaData.devicePixelRatio();
|
|
220
|
+
let headerAdjustment = 0;
|
|
221
|
+
if (this.currentTag.osName === 'iOS') {
|
|
222
|
+
headerAdjustment = this.statusBarHeight;
|
|
223
|
+
}
|
|
165
224
|
const coOrdinates = {
|
|
166
|
-
top: Math.floor(
|
|
167
|
-
bottom: Math.ceil((
|
|
168
|
-
left: Math.floor(
|
|
169
|
-
right: Math.ceil((
|
|
225
|
+
top: Math.floor(element.y * scaleFactor) + Math.floor(headerAdjustment),
|
|
226
|
+
bottom: Math.ceil((element.y + element.height) * scaleFactor) + Math.ceil(headerAdjustment),
|
|
227
|
+
left: Math.floor(element.x * scaleFactor),
|
|
228
|
+
right: Math.ceil((element.x + element.width) * scaleFactor)
|
|
170
229
|
};
|
|
171
230
|
const jsonObject = {
|
|
172
231
|
selector,
|
|
@@ -178,9 +237,9 @@ export default class GenericProvider {
|
|
|
178
237
|
const regionsArray = [];
|
|
179
238
|
for (const idx in elements) {
|
|
180
239
|
try {
|
|
181
|
-
const
|
|
240
|
+
const boundingBoxRegion = await this.driver.findElementBoundingBox(findBy, elements[idx]);
|
|
182
241
|
const selector = `${findBy}: ${elements[idx]}`;
|
|
183
|
-
const region = await this.
|
|
242
|
+
const region = await this.getRegionObjectFromBoundingBox(selector, boundingBoxRegion);
|
|
184
243
|
regionsArray.push(region);
|
|
185
244
|
} catch (e) {
|
|
186
245
|
log.warn(`Selenium Element with ${findBy}: ${elements[idx]} not found. Ignoring this ${findBy}.`);
|
|
@@ -189,8 +248,57 @@ export default class GenericProvider {
|
|
|
189
248
|
}
|
|
190
249
|
return regionsArray;
|
|
191
250
|
}
|
|
251
|
+
async updatePageShiftFactor(location, scaleFactor) {
|
|
252
|
+
const scrollFactors = await this.driver.executeScript({
|
|
253
|
+
script: 'return [parseInt(window.scrollX), parseInt(window.scrollY)];',
|
|
254
|
+
args: []
|
|
255
|
+
});
|
|
256
|
+
if (this.currentTag.osName === 'iOS' || this.currentTag.osName === 'OS X' && parseInt(this.currentTag.browserVersion) > 13 && this.currentTag.browserName.toLowerCase() === 'safari') {
|
|
257
|
+
this.pageYShiftFactor = this.statusBarHeight;
|
|
258
|
+
} else {
|
|
259
|
+
this.pageYShiftFactor = this.statusBarHeight - scrollFactors.value[1] * scaleFactor;
|
|
260
|
+
}
|
|
261
|
+
this.pageXShiftFactor = this.currentTag.osName === 'iOS' ? 0 : -(scrollFactors.value[0] * scaleFactor);
|
|
262
|
+
if (this.currentTag.osName === 'iOS') {
|
|
263
|
+
if (scrollFactors.value[0] !== this.initialScrollFactor.value[0] || scrollFactors.value[1] !== this.initialScrollFactor.value[1]) {
|
|
264
|
+
this.pageXShiftFactor = -1 * this.removeElementShiftFactor;
|
|
265
|
+
this.pageYShiftFactor = -1 * this.removeElementShiftFactor;
|
|
266
|
+
} else if (location.y === 0) {
|
|
267
|
+
this.pageYShiftFactor += -(scrollFactors.value[1] * scaleFactor);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async getRegionObject(selector, elementId) {
|
|
272
|
+
const scaleFactor = await this.metaData.devicePixelRatio();
|
|
273
|
+
const rect = await this.driver.rect(elementId);
|
|
274
|
+
const location = {
|
|
275
|
+
x: rect.x,
|
|
276
|
+
y: rect.y
|
|
277
|
+
};
|
|
278
|
+
const size = {
|
|
279
|
+
height: rect.height,
|
|
280
|
+
width: rect.width
|
|
281
|
+
};
|
|
282
|
+
// Update pageShiftFactor Element is not visible in viewport
|
|
283
|
+
// In case of iOS if the element is not visible in viewport it gives 0 for x-y coordinate.
|
|
284
|
+
// In case of iOS if the element is partially visible it gives negative x-y coordinate.
|
|
285
|
+
// Subtracting ScrollY/ScrollX ensures if the element is visible in viewport or not.
|
|
286
|
+
await this.updatePageShiftFactor(location, scaleFactor);
|
|
287
|
+
const coOrdinates = {
|
|
288
|
+
top: Math.floor(location.y * scaleFactor) + Math.floor(this.pageYShiftFactor),
|
|
289
|
+
bottom: Math.ceil((location.y + size.height) * scaleFactor) + Math.ceil(this.pageYShiftFactor),
|
|
290
|
+
left: Math.floor(location.x * scaleFactor) + Math.floor(this.pageXShiftFactor),
|
|
291
|
+
right: Math.ceil((location.x + size.width) * scaleFactor) + Math.ceil(this.pageXShiftFactor)
|
|
292
|
+
};
|
|
293
|
+
const jsonObject = {
|
|
294
|
+
selector,
|
|
295
|
+
coOrdinates
|
|
296
|
+
};
|
|
297
|
+
return jsonObject;
|
|
298
|
+
}
|
|
192
299
|
async getSeleniumRegionsByElement(elements) {
|
|
193
300
|
const regionsArray = [];
|
|
301
|
+
await this.getInitialPosition();
|
|
194
302
|
for (let index = 0; index < elements.length; index++) {
|
|
195
303
|
try {
|
|
196
304
|
const selector = `element: ${index}`;
|
|
@@ -201,6 +309,7 @@ export default class GenericProvider {
|
|
|
201
309
|
log.debug(e.toString());
|
|
202
310
|
}
|
|
203
311
|
}
|
|
312
|
+
await this.scrollToInitialPosition(this.initialScrollFactor.value[0], this.initialScrollFactor.value[1]);
|
|
204
313
|
return regionsArray;
|
|
205
314
|
}
|
|
206
315
|
async getSeleniumRegionsByLocation(customLocations) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/webdriver-utils",
|
|
3
|
-
"version": "1.27.5-beta.
|
|
3
|
+
"version": "1.27.5-beta.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public",
|
|
12
|
-
"tag": "
|
|
12
|
+
"tag": "beta"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=14"
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"test:coverage": "yarn test --coverage"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@percy/config": "1.27.5-beta.
|
|
33
|
-
"@percy/sdk-utils": "1.27.5-beta.
|
|
32
|
+
"@percy/config": "1.27.5-beta.1",
|
|
33
|
+
"@percy/sdk-utils": "1.27.5-beta.1"
|
|
34
34
|
},
|
|
35
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "f5924b050d0c04f8ac63d610af291b32f51565f8"
|
|
36
36
|
}
|