eyes_selenium 3.14.10 → 3.15.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/applitools/selenium/concerns/browser_types.rb +15 -0
  3. data/lib/applitools/selenium/concerns/browsers_info.rb +16 -0
  4. data/lib/applitools/selenium/concerns/external_css_resources.rb +31 -0
  5. data/lib/applitools/selenium/concerns/render_browser_info_fluent.rb +42 -0
  6. data/lib/applitools/selenium/concerns/render_resources.rb +15 -0
  7. data/lib/applitools/selenium/concerns/rgrid_dom.rb +46 -0
  8. data/lib/applitools/selenium/concerns/stitch_modes.rb +15 -0
  9. data/lib/applitools/selenium/concerns/test_list.rb +23 -0
  10. data/lib/applitools/selenium/eyes.rb +13 -854
  11. data/lib/applitools/selenium/scripts/process_page_and_serialize.rb +519 -0
  12. data/lib/applitools/selenium/selenium_configuration.rb +64 -0
  13. data/lib/applitools/selenium/selenium_eyes.rb +860 -0
  14. data/lib/applitools/selenium/target.rb +2 -2
  15. data/lib/applitools/selenium/visual_grid/eyes_connector.rb +170 -0
  16. data/lib/applitools/selenium/visual_grid/render_browser_info.rb +31 -0
  17. data/lib/applitools/selenium/visual_grid/render_info.rb +9 -0
  18. data/lib/applitools/selenium/visual_grid/render_request.rb +22 -0
  19. data/lib/applitools/selenium/visual_grid/render_requests.rb +11 -0
  20. data/lib/applitools/selenium/visual_grid/render_task.rb +171 -0
  21. data/lib/applitools/selenium/visual_grid/resource_cache.rb +51 -0
  22. data/lib/applitools/selenium/visual_grid/running_test.rb +198 -0
  23. data/lib/applitools/selenium/visual_grid/thread_pool.rb +94 -0
  24. data/lib/applitools/selenium/visual_grid/vg_resource.rb +37 -0
  25. data/lib/applitools/selenium/visual_grid/vg_task.rb +46 -0
  26. data/lib/applitools/selenium/visual_grid/visual_grid_eyes.rb +236 -0
  27. data/lib/applitools/selenium/visual_grid/visual_grid_runner.rb +57 -0
  28. data/lib/applitools/version.rb +1 -1
  29. data/lib/eyes_selenium.rb +3 -0
  30. metadata +46 -7
@@ -0,0 +1,519 @@
1
+ module Applitools
2
+ module Selenium
3
+ module Scripts
4
+ PROCESS_RESOURCES = <<'END'
5
+
6
+ function __processPageAndSerialize() {
7
+ var processPageAndSerialize = (function () {
8
+ 'use strict';
9
+
10
+ // This code was copied and modified from https://github.com/beatgammit/base64-js/blob/bf68aaa277/index.js
11
+ // License: https://github.com/beatgammit/base64-js/blob/bf68aaa277d9de7007cc0c58279c411bb10670ac/LICENSE
12
+
13
+ function arrayBufferToBase64(ab) {
14
+ const lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
15
+
16
+ const uint8 = new Uint8Array(ab);
17
+ const len = uint8.length;
18
+ const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
19
+ const parts = [];
20
+ const maxChunkLength = 16383; // must be multiple of 3
21
+
22
+ let tmp;
23
+
24
+ // go through the array every three bytes, we'll deal with trailing stuff later
25
+ for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
26
+ parts.push(encodeChunk(i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength));
27
+ }
28
+
29
+ // pad the end with zeros, but make sure to not forget the extra bytes
30
+ if (extraBytes === 1) {
31
+ tmp = uint8[len - 1];
32
+ parts.push(lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3f] + '==');
33
+ } else if (extraBytes === 2) {
34
+ tmp = (uint8[len - 2] << 8) + uint8[len - 1];
35
+ parts.push(lookup[tmp >> 10] + lookup[(tmp >> 4) & 0x3f] + lookup[(tmp << 2) & 0x3f] + '=');
36
+ }
37
+
38
+ return parts.join('');
39
+
40
+ function tripletToBase64(num) {
41
+ return (
42
+ lookup[(num >> 18) & 0x3f] +
43
+ lookup[(num >> 12) & 0x3f] +
44
+ lookup[(num >> 6) & 0x3f] +
45
+ lookup[num & 0x3f]
46
+ );
47
+ }
48
+
49
+ function encodeChunk(start, end) {
50
+ let tmp;
51
+ const output = [];
52
+ for (let i = start; i < end; i += 3) {
53
+ tmp = ((uint8[i] << 16) & 0xff0000) + ((uint8[i + 1] << 8) & 0xff00) + (uint8[i + 2] & 0xff);
54
+ output.push(tripletToBase64(tmp));
55
+ }
56
+ return output.join('');
57
+ }
58
+ }
59
+
60
+ var arrayBufferToBase64_1 = arrayBufferToBase64;
61
+
62
+ function extractLinks(doc = document) {
63
+ const srcsetUrls = [...doc.querySelectorAll('img[srcset],source[srcset]')]
64
+ .map(srcsetEl =>
65
+ srcsetEl
66
+ .getAttribute('srcset')
67
+ .split(',')
68
+ .map(str => str.trim().split(/\s+/)[0]),
69
+ )
70
+ .reduce((acc, urls) => acc.concat(urls), []);
71
+
72
+ const srcUrls = [...doc.querySelectorAll('img[src],source[src]')].map(srcEl =>
73
+ srcEl.getAttribute('src'),
74
+ );
75
+
76
+ const cssUrls = [...doc.querySelectorAll('link[rel="stylesheet"]')].map(link =>
77
+ link.getAttribute('href'),
78
+ );
79
+
80
+ const videoPosterUrls = [...doc.querySelectorAll('video[poster]')].map(videoEl =>
81
+ videoEl.getAttribute('poster'),
82
+ );
83
+
84
+ return [...srcsetUrls, ...srcUrls, ...cssUrls, ...videoPosterUrls];
85
+ }
86
+
87
+ var extractLinks_1 = extractLinks;
88
+
89
+ /* eslint-disable no-use-before-define */
90
+
91
+ function domNodesToCdt(docNode) {
92
+ const NODE_TYPES = {
93
+ ELEMENT: 1,
94
+ TEXT: 3,
95
+ DOCUMENT: 9,
96
+ DOCUMENT_TYPE: 10,
97
+ };
98
+
99
+ const domNodes = [
100
+ {
101
+ nodeType: NODE_TYPES.DOCUMENT,
102
+ },
103
+ ];
104
+ domNodes[0].childNodeIndexes = childrenFactory(domNodes, docNode.childNodes);
105
+ return domNodes;
106
+
107
+ function childrenFactory(domNodes, elementNodes) {
108
+ if (!elementNodes || elementNodes.length === 0) return null;
109
+
110
+ const childIndexes = [];
111
+ elementNodes.forEach(elementNode => {
112
+ const index = elementNodeFactory(domNodes, elementNode);
113
+ if (index !== null) {
114
+ childIndexes.push(index);
115
+ }
116
+ });
117
+
118
+ return childIndexes;
119
+ }
120
+
121
+ function elementNodeFactory(domNodes, elementNode) {
122
+ let node;
123
+ const {nodeType} = elementNode;
124
+ if (nodeType === NODE_TYPES.ELEMENT) {
125
+ if (elementNode.nodeName !== 'SCRIPT') {
126
+ if (
127
+ elementNode.nodeName === 'STYLE' &&
128
+ !elementNode.textContent &&
129
+ elementNode.sheet &&
130
+ elementNode.sheet.cssRules.length
131
+ ) {
132
+ elementNode.appendChild(
133
+ docNode.createTextNode(
134
+ [...elementNode.sheet.cssRules].map(rule => rule.cssText).join(''),
135
+ ),
136
+ );
137
+ }
138
+
139
+ node = {
140
+ nodeType: NODE_TYPES.ELEMENT,
141
+ nodeName: elementNode.nodeName,
142
+ attributes: Object.keys(elementNode.attributes).map(key => {
143
+ let value = elementNode.attributes[key].value;
144
+ const name = elementNode.attributes[key].localName;
145
+
146
+ if (/^blob:/.test(value)) {
147
+ value = value.replace(/^blob:/, '');
148
+ }
149
+
150
+ return {
151
+ name,
152
+ value,
153
+ };
154
+ }),
155
+ childNodeIndexes: elementNode.childNodes.length
156
+ ? childrenFactory(domNodes, elementNode.childNodes)
157
+ : [],
158
+ };
159
+ if (elementNode.checked && !elementNode.attributes.checked) {
160
+ node.attributes.push({name: 'checked', value: 'checked'});
161
+ }
162
+ }
163
+ } else if (nodeType === NODE_TYPES.TEXT) {
164
+ node = {
165
+ nodeType: NODE_TYPES.TEXT,
166
+ nodeValue: elementNode.nodeValue,
167
+ };
168
+ } else if (nodeType === NODE_TYPES.DOCUMENT_TYPE) {
169
+ node = {
170
+ nodeType: NODE_TYPES.DOCUMENT_TYPE,
171
+ nodeName: elementNode.nodeName,
172
+ };
173
+ }
174
+
175
+ if (node) {
176
+ domNodes.push(node);
177
+ return domNodes.length - 1;
178
+ } else {
179
+ // console.log(`Unknown nodeType: ${nodeType}`);
180
+ return null;
181
+ }
182
+ }
183
+ }
184
+
185
+ var domNodesToCdt_1 = domNodesToCdt;
186
+ var NODE_TYPES = {
187
+ ELEMENT: 1,
188
+ TEXT: 3,
189
+ DOCUMENT: 9,
190
+ DOCUMENT_TYPE: 10,
191
+ };
192
+ domNodesToCdt_1.NODE_TYPES = NODE_TYPES;
193
+
194
+ function extractFrames(doc = document) {
195
+ return [...doc.querySelectorAll('iframe[src]:not([src=""])')]
196
+ .map(srcEl => {
197
+ try {
198
+ const contentDoc = srcEl.contentDocument;
199
+ return (
200
+ contentDoc &&
201
+ /^https?:$/.test(contentDoc.location.protocol) &&
202
+ contentDoc.defaultView &&
203
+ contentDoc.defaultView.frameElement &&
204
+ contentDoc
205
+ );
206
+ } catch (err) {
207
+ //for CORS frames
208
+ }
209
+ })
210
+ .filter(x => !!x);
211
+ }
212
+
213
+ var extractFrames_1 = extractFrames;
214
+
215
+ function uniq(arr) {
216
+ return Array.from(new Set(arr)).filter(x => !!x);
217
+ }
218
+
219
+ var uniq_1 = uniq;
220
+
221
+ function aggregateResourceUrlsAndBlobs(resourceUrlsAndBlobsArr) {
222
+ return resourceUrlsAndBlobsArr.reduce(
223
+ ({resourceUrls: allResourceUrls, blobsObj: allBlobsObj}, {resourceUrls, blobsObj}) => ({
224
+ resourceUrls: uniq_1(allResourceUrls.concat(resourceUrls)),
225
+ blobsObj: Object.assign(allBlobsObj, blobsObj),
226
+ }),
227
+ {resourceUrls: [], blobsObj: {}},
228
+ );
229
+ }
230
+
231
+ var aggregateResourceUrlsAndBlobs_1 = aggregateResourceUrlsAndBlobs;
232
+
233
+ function makeGetResourceUrlsAndBlobs({processResource, aggregateResourceUrlsAndBlobs}) {
234
+ return function getResourceUrlsAndBlobs(doc, baseUrl, urls) {
235
+ return Promise.all(
236
+ urls.map(url => processResource(url, doc, baseUrl, getResourceUrlsAndBlobs.bind(null, doc))),
237
+ ).then(resourceUrlsAndBlobsArr => aggregateResourceUrlsAndBlobs(resourceUrlsAndBlobsArr));
238
+ };
239
+ }
240
+
241
+ var getResourceUrlsAndBlobs = makeGetResourceUrlsAndBlobs;
242
+
243
+ function filterInlineUrl(absoluteUrl) {
244
+ return /^(blob|https?):/.test(absoluteUrl);
245
+ }
246
+
247
+ var filterInlineUrl_1 = filterInlineUrl;
248
+
249
+ function absolutizeUrl(url, absoluteUrl) {
250
+ return new URL(url, absoluteUrl).href;
251
+ }
252
+
253
+ var absolutizeUrl_1 = absolutizeUrl;
254
+
255
+ function makeProcessResource({
256
+ fetchUrl,
257
+ findStyleSheetByUrl,
258
+ extractResourcesFromStyleSheet,
259
+ isSameOrigin,
260
+ cache = {},
261
+ }) {
262
+ return function processResource(absoluteUrl, doc, baseUrl, getResourceUrlsAndBlobs) {
263
+ return cache[absoluteUrl] || (cache[absoluteUrl] = doProcessResource(absoluteUrl));
264
+
265
+ function doProcessResource(url) {
266
+ return fetchUrl(url)
267
+ .catch(e => {
268
+ if (probablyCORS(e, url)) {
269
+ return {probablyCORS: true, url};
270
+ } else {
271
+ throw e;
272
+ }
273
+ })
274
+ .then(({url, type, value, probablyCORS}) => {
275
+ if (probablyCORS) {
276
+ return {resourceUrls: [url]};
277
+ }
278
+ const result = {blobsObj: {[url]: {type, value}}};
279
+ if (/text\/css/.test(type)) {
280
+ const styleSheet = findStyleSheetByUrl(url, doc);
281
+ if (!styleSheet) {
282
+ return result;
283
+ }
284
+ const resourceUrls = extractResourcesFromStyleSheet(styleSheet, doc.defaultView)
285
+ .map(resourceUrl => absolutizeUrl_1(resourceUrl, url.replace(/^blob:/, '')))
286
+ .filter(filterInlineUrl_1);
287
+ return getResourceUrlsAndBlobs(baseUrl, resourceUrls).then(
288
+ ({resourceUrls, blobsObj}) => ({
289
+ resourceUrls,
290
+ blobsObj: Object.assign(blobsObj, {[url]: {type, value}}),
291
+ }),
292
+ );
293
+ } else {
294
+ return result;
295
+ }
296
+ })
297
+ .catch(err => {
298
+ console.log('[dom-snapshot] error while fetching', url, err);
299
+ return {};
300
+ });
301
+ }
302
+
303
+ function probablyCORS(err, url) {
304
+ const msgCORS = err.message && err.message.includes('Failed to fetch');
305
+ const nameCORS = err.name && err.name.includes('TypeError');
306
+ return msgCORS && nameCORS && !isSameOrigin(url, baseUrl);
307
+ }
308
+ };
309
+ }
310
+
311
+ var processResource = makeProcessResource;
312
+
313
+ /* global window */
314
+
315
+ function fetchUrl(url, fetch = window.fetch) {
316
+ return fetch(url, {cache: 'force-cache', credentials: 'same-origin'}).then(resp =>
317
+ resp.arrayBuffer().then(buff => ({
318
+ url,
319
+ type: resp.headers.get('Content-Type'),
320
+ value: buff,
321
+ })),
322
+ );
323
+ }
324
+
325
+ var fetchUrl_1 = fetchUrl;
326
+
327
+ function makeFindStyleSheetByUrl({styleSheetCache}) {
328
+ return function findStyleSheetByUrl(url, doc) {
329
+ return styleSheetCache[url] || [...doc.styleSheets].find(styleSheet => styleSheet.href === url);
330
+ };
331
+ }
332
+
333
+ var findStyleSheetByUrl = makeFindStyleSheetByUrl;
334
+
335
+ function getUrlFromCssText(cssText) {
336
+ const re = /url\((?!['"]?:)['"]?([^'")]*)['"]?\)/g;
337
+ const ret = [];
338
+ let result;
339
+ while ((result = re.exec(cssText)) !== null) {
340
+ ret.push(result[1]);
341
+ }
342
+ return ret;
343
+ }
344
+
345
+ var getUrlFromCssText_1 = getUrlFromCssText;
346
+
347
+ // NOTE this code is very similar to the node part of visual-grid-client, but there is a different related to the browser's cssom with import rules
348
+ function makeExtractResourcesFromStyleSheet({styleSheetCache}) {
349
+ return function extractResourcesFromStyleSheet(styleSheet, win = window) {
350
+ return uniq_1(
351
+ [...(styleSheet.cssRules || [])].reduce((acc, rule) => {
352
+ if (rule instanceof win.CSSImportRule) {
353
+ styleSheetCache[rule.styleSheet.href] = rule.styleSheet;
354
+ return acc.concat(rule.href);
355
+ } else if (rule instanceof win.CSSFontFaceRule) {
356
+ return acc.concat(getUrlFromCssText_1(rule.style.getPropertyValue('src')));
357
+ } else if (rule instanceof win.CSSSupportsRule || rule instanceof win.CSSMediaRule) {
358
+ return acc.concat(extractResourcesFromStyleSheet(rule));
359
+ } else if (rule instanceof win.CSSStyleRule) {
360
+ for (let i = 0, ii = rule.style.length; i < ii; i++) {
361
+ const urls = getUrlFromCssText_1(rule.style.getPropertyValue(rule.style[i]));
362
+ urls.length && (acc = acc.concat(urls));
363
+ }
364
+ }
365
+ return acc;
366
+ }, []),
367
+ );
368
+ };
369
+ }
370
+
371
+ var extractResourcesFromStyleSheet = makeExtractResourcesFromStyleSheet;
372
+
373
+ function extractResourceUrlsFromStyleAttrs(cdt) {
374
+ return cdt.reduce((acc, node) => {
375
+ if (node.nodeType === 1) {
376
+ const styleAttr =
377
+ node.attributes && node.attributes.find(attr => attr.name.toUpperCase() === 'STYLE');
378
+
379
+ if (styleAttr) acc = acc.concat(getUrlFromCssText_1(styleAttr.value));
380
+ }
381
+ return acc;
382
+ }, []);
383
+ }
384
+
385
+ var extractResourceUrlsFromStyleAttrs_1 = extractResourceUrlsFromStyleAttrs;
386
+
387
+ function makeExtractResourceUrlsFromStyleTags(extractResourcesFromStyleSheet) {
388
+ return function extractResourceUrlsFromStyleTags(doc) {
389
+ return uniq_1(
390
+ [...doc.getElementsByTagName('style')].reduce((resourceUrls, styleEl) => {
391
+ const styleSheet = [...doc.styleSheets].find(
392
+ styleSheet => styleSheet.ownerNode === styleEl,
393
+ );
394
+ return resourceUrls.concat(extractResourcesFromStyleSheet(styleSheet, doc.defaultView));
395
+ }, []),
396
+ );
397
+ };
398
+ }
399
+
400
+ var extractResourceUrlsFromStyleTags = makeExtractResourceUrlsFromStyleTags;
401
+
402
+ function isSameOrigin(url, baseUrl) {
403
+ const blobOrData = /^(blob|data):/;
404
+ if (blobOrData.test(url)) return true;
405
+ if (blobOrData.test(baseUrl)) return false;
406
+
407
+ const {origin} = new URL(url, baseUrl);
408
+ const {origin: baseOrigin} = new URL(baseUrl);
409
+ return origin === baseOrigin;
410
+ }
411
+
412
+ var isSameOrigin_1 = isSameOrigin;
413
+
414
+ function processPage(doc = document) {
415
+ const styleSheetCache = {};
416
+ const extractResourcesFromStyleSheet$$1 = extractResourcesFromStyleSheet({styleSheetCache});
417
+ const findStyleSheetByUrl$$1 = findStyleSheetByUrl({styleSheetCache});
418
+ const processResource$$1 = processResource({
419
+ fetchUrl: fetchUrl_1,
420
+ findStyleSheetByUrl: findStyleSheetByUrl$$1,
421
+ extractResourcesFromStyleSheet: extractResourcesFromStyleSheet$$1,
422
+ absolutizeUrl: absolutizeUrl_1,
423
+ isSameOrigin: isSameOrigin_1,
424
+ });
425
+
426
+ const getResourceUrlsAndBlobs$$1 = getResourceUrlsAndBlobs({
427
+ processResource: processResource$$1,
428
+ aggregateResourceUrlsAndBlobs: aggregateResourceUrlsAndBlobs_1,
429
+ });
430
+
431
+ const extractResourceUrlsFromStyleTags$$1 = extractResourceUrlsFromStyleTags(
432
+ extractResourcesFromStyleSheet$$1,
433
+ );
434
+
435
+ return doProcessPage(doc);
436
+
437
+ function doProcessPage(doc) {
438
+ const frameElement = doc.defaultView && doc.defaultView.frameElement;
439
+ const url = frameElement ? frameElement.src : doc.location.href;
440
+
441
+ const cdt = domNodesToCdt_1(doc);
442
+
443
+ const links = uniq_1(
444
+ extractLinks_1(doc)
445
+ .concat(extractResourceUrlsFromStyleAttrs_1(cdt))
446
+ .concat(extractResourceUrlsFromStyleTags$$1(doc)),
447
+ )
448
+ .map(absolutizeThisUrl)
449
+ .filter(filterInlineUrlsIfExisting);
450
+
451
+ const resourceUrlsAndBlobsPromise = getResourceUrlsAndBlobs$$1(doc, url, links);
452
+
453
+ const frameDocs = extractFrames_1(doc);
454
+ const processFramesPromise = frameDocs.map(doProcessPage);
455
+
456
+ return Promise.all([resourceUrlsAndBlobsPromise, ...processFramesPromise]).then(
457
+ ([{resourceUrls, blobsObj}, ...framesResults]) => ({
458
+ cdt,
459
+ url,
460
+ resourceUrls,
461
+ blobs: blobsObjToArray(blobsObj),
462
+ frames: framesResults,
463
+ srcAttr: frameElement ? frameElement.getAttribute('src') : undefined,
464
+ }),
465
+ );
466
+
467
+ function absolutizeThisUrl(someUrl) {
468
+ try {
469
+ return absolutizeUrl_1(someUrl, url);
470
+ } catch (err) {
471
+ // can't do anything with a non-absolute url
472
+ }
473
+ }
474
+ }
475
+ }
476
+
477
+ function blobsObjToArray(blobsObj) {
478
+ return Object.keys(blobsObj).map(blobUrl =>
479
+ Object.assign(
480
+ {
481
+ url: blobUrl.replace(/^blob:/, ''),
482
+ },
483
+ blobsObj[blobUrl],
484
+ ),
485
+ );
486
+ }
487
+
488
+ function filterInlineUrlsIfExisting(absoluteUrl) {
489
+ return absoluteUrl && filterInlineUrl_1(absoluteUrl);
490
+ }
491
+
492
+ var processPage_1 = processPage;
493
+
494
+ function processPageAndSerialize(doc) {
495
+ return processPage_1(doc).then(serializeFrame);
496
+ }
497
+
498
+ function serializeFrame(frame) {
499
+ frame.blobs = frame.blobs.map(({url, type, value}) => ({
500
+ url,
501
+ type,
502
+ value: arrayBufferToBase64_1(value),
503
+ }));
504
+ frame.frames.forEach(serializeFrame);
505
+ return frame;
506
+ }
507
+
508
+ var processPageAndSerialize_1 = processPageAndSerialize;
509
+
510
+ return processPageAndSerialize_1;
511
+
512
+ }());
513
+
514
+ return processPageAndSerialize.apply(this, arguments);
515
+ }
516
+ END
517
+ end
518
+ end
519
+ end