eyes_selenium 3.15.39 → 3.15.40
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/applitools/selenium/browser_types.rb +1 -0
- data/lib/applitools/selenium/browsers_info.rb +24 -23
- data/lib/applitools/selenium/concerns/selenium_eyes.rb +154 -0
- data/lib/applitools/selenium/configuration.rb +5 -4
- data/lib/applitools/selenium/css_parser/find_embedded_resources.rb +4 -1
- data/lib/applitools/selenium/css_translate_position_provider.rb +1 -1
- data/lib/applitools/selenium/devices.rb +1 -0
- data/lib/applitools/selenium/dom_capture/dom_capture.rb +9 -8
- data/lib/applitools/selenium/dom_capture/dom_capture_script.rb +3 -1
- data/lib/applitools/selenium/external_css_resources.rb +7 -3
- data/lib/applitools/selenium/eyes.rb +2 -3
- data/lib/applitools/selenium/full_page_capture_algorithm.rb +5 -1
- data/lib/applitools/selenium/orientations.rb +1 -0
- data/lib/applitools/selenium/render_browser_info_fluent.rb +3 -1
- data/lib/applitools/selenium/render_resources.rb +12 -5
- data/lib/applitools/selenium/rgrid_dom.rb +4 -8
- data/lib/applitools/selenium/scripts/get_element_xpath.rb +3 -1
- data/lib/applitools/selenium/scripts/process_page_and_poll.rb +154 -72
- data/lib/applitools/selenium/scripts/process_page_and_serialize.rb +3 -1
- data/lib/applitools/selenium/selenium_eyes.rb +66 -221
- data/lib/applitools/selenium/stitch_modes.rb +2 -1
- data/lib/applitools/selenium/target.rb +31 -14
- data/lib/applitools/selenium/test_list.rb +16 -10
- data/lib/applitools/selenium/visual_grid/chrome_emulation_info.rb +2 -1
- data/lib/applitools/selenium/visual_grid/emulation_base_info.rb +2 -1
- data/lib/applitools/selenium/visual_grid/eyes_connector.rb +13 -7
- data/lib/applitools/selenium/visual_grid/render_browser_info.rb +2 -1
- data/lib/applitools/selenium/visual_grid/render_info.rb +2 -1
- data/lib/applitools/selenium/visual_grid/render_request.rb +4 -3
- data/lib/applitools/selenium/visual_grid/render_requests.rb +2 -1
- data/lib/applitools/selenium/visual_grid/render_task.rb +71 -46
- data/lib/applitools/selenium/visual_grid/resource_cache.rb +4 -3
- data/lib/applitools/selenium/visual_grid/running_test.rb +26 -4
- data/lib/applitools/selenium/visual_grid/thread_pool.rb +3 -2
- data/lib/applitools/selenium/visual_grid/vg_match_window_data.rb +9 -3
- data/lib/applitools/selenium/visual_grid/vg_region.rb +2 -1
- data/lib/applitools/selenium/visual_grid/vg_resource.rb +8 -7
- data/lib/applitools/selenium/visual_grid/vg_task.rb +2 -1
- data/lib/applitools/selenium/visual_grid/visual_grid_eyes.rb +114 -168
- data/lib/applitools/selenium/visual_grid/visual_grid_runner.rb +38 -11
- data/lib/applitools/selenium/visual_grid/web_element_region.rb +3 -1
- data/lib/applitools/version.rb +1 -1
- data/lib/eyes_selenium.rb +1 -1
- metadata +5 -4
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
module Applitools
|
2
3
|
module Selenium
|
3
4
|
class RGridDom
|
@@ -13,21 +14,16 @@ module Applitools
|
|
13
14
|
self.resources = options[:resources]
|
14
15
|
self.hash_format = 'sha256'
|
15
16
|
self.data = {
|
16
|
-
|
17
|
-
|
17
|
+
'resources' => resources,
|
18
|
+
'domNodes' => dom_nodes
|
18
19
|
}
|
19
20
|
self.hash = calculate_sha_256
|
20
21
|
end
|
21
22
|
|
22
23
|
def calculate_sha_256
|
23
|
-
# str = Applitools::Utils.stringify_for_hash(data)
|
24
24
|
Digest::SHA256.hexdigest(content)
|
25
25
|
end
|
26
26
|
|
27
|
-
# def hash
|
28
|
-
# calculate_sha_256
|
29
|
-
# end
|
30
|
-
|
31
27
|
def content
|
32
28
|
Oj.dump(json_value(data.sort.to_h))
|
33
29
|
end
|
@@ -41,4 +37,4 @@ module Applitools
|
|
41
37
|
end
|
42
38
|
end
|
43
39
|
end
|
44
|
-
end
|
40
|
+
end
|
@@ -1,13 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Applitools
|
2
4
|
module Selenium
|
3
5
|
module Scripts
|
4
6
|
PROCESS_PAGE_AND_POLL = <<'END'
|
5
|
-
/* @applitools/dom-snapshot@
|
7
|
+
/* @applitools/dom-snapshot@3.1.4 */
|
6
8
|
|
7
|
-
function
|
8
|
-
var
|
9
|
+
function __processPageAndSerializePoll() {
|
10
|
+
var processPageAndSerializePoll = (function () {
|
9
11
|
'use strict';
|
10
12
|
|
13
|
+
const EYES_NAME_SPACE = '__EYES__APPLITOOLS__';
|
14
|
+
|
15
|
+
function pullify(script, win = window) {
|
16
|
+
return function() {
|
17
|
+
const scriptName = script.name;
|
18
|
+
if (!win[EYES_NAME_SPACE]) {
|
19
|
+
win[EYES_NAME_SPACE] = {};
|
20
|
+
}
|
21
|
+
if (!win[EYES_NAME_SPACE][scriptName]) {
|
22
|
+
win[EYES_NAME_SPACE][scriptName] = {
|
23
|
+
status: 'WIP',
|
24
|
+
value: null,
|
25
|
+
error: null,
|
26
|
+
};
|
27
|
+
script
|
28
|
+
.apply(null, arguments)
|
29
|
+
.then(r => ((resultObject.status = 'SUCCESS'), (resultObject.value = r)))
|
30
|
+
.catch(e => ((resultObject.status = 'ERROR'), (resultObject.error = e.message)));
|
31
|
+
}
|
32
|
+
|
33
|
+
const resultObject = win[EYES_NAME_SPACE][scriptName];
|
34
|
+
if (resultObject.status === 'SUCCESS') {
|
35
|
+
win[EYES_NAME_SPACE][scriptName] = null;
|
36
|
+
}
|
37
|
+
|
38
|
+
return JSON.stringify(resultObject);
|
39
|
+
};
|
40
|
+
}
|
41
|
+
|
42
|
+
var pollify = pullify;
|
43
|
+
|
11
44
|
// This code was copied and modified from https://github.com/beatgammit/base64-js/blob/bf68aaa277/index.js
|
12
45
|
// License: https://github.com/beatgammit/base64-js/blob/bf68aaa277d9de7007cc0c58279c411bb10670ac/LICENSE
|
13
46
|
|
@@ -134,6 +167,21 @@ function __processPageAndPoll() {
|
|
134
167
|
|
135
168
|
var absolutizeUrl_1 = absolutizeUrl;
|
136
169
|
|
170
|
+
const NEED_MAP_INPUT_TYPES = new Set([
|
171
|
+
'date',
|
172
|
+
'datetime-local',
|
173
|
+
'email',
|
174
|
+
'month',
|
175
|
+
'number',
|
176
|
+
'password',
|
177
|
+
'search',
|
178
|
+
'tel',
|
179
|
+
'text',
|
180
|
+
'time',
|
181
|
+
'url',
|
182
|
+
'week',
|
183
|
+
]);
|
184
|
+
|
137
185
|
function domNodesToCdt(docNode, baseUrl) {
|
138
186
|
const cdt = [{nodeType: Node.DOCUMENT_NODE}];
|
139
187
|
const docRoots = [docNode];
|
@@ -171,6 +219,11 @@ function __processPageAndPoll() {
|
|
171
219
|
manualChildNodeIndexes = [cdt.length - 1];
|
172
220
|
}
|
173
221
|
|
222
|
+
if (elementNode.tagName === 'TEXTAREA' && elementNode.value !== elementNode.textContent) {
|
223
|
+
cdt.push(getTextContentNode(elementNode));
|
224
|
+
manualChildNodeIndexes = [cdt.length - 1];
|
225
|
+
}
|
226
|
+
|
174
227
|
node = getBasicNode(elementNode);
|
175
228
|
node.childNodeIndexes =
|
176
229
|
manualChildNodeIndexes ||
|
@@ -226,6 +279,13 @@ function __processPageAndPoll() {
|
|
226
279
|
};
|
227
280
|
}
|
228
281
|
|
282
|
+
function getTextContentNode(elementNode) {
|
283
|
+
return {
|
284
|
+
nodeType: Node.TEXT_NODE,
|
285
|
+
nodeValue: elementNode.value,
|
286
|
+
};
|
287
|
+
}
|
288
|
+
|
229
289
|
function getBasicNode(elementNode) {
|
230
290
|
const node = {
|
231
291
|
nodeType: elementNode.nodeType,
|
@@ -255,7 +315,7 @@ function __processPageAndPoll() {
|
|
255
315
|
|
256
316
|
if (
|
257
317
|
elementNode.tagName === 'INPUT' &&
|
258
|
-
elementNode.type
|
318
|
+
NEED_MAP_INPUT_TYPES.has(elementNode.type) &&
|
259
319
|
(elementNode.attributes.value && elementNode.attributes.value.value) !== elementNode.value
|
260
320
|
) {
|
261
321
|
const nodeAttr = node.attributes.find(a => a.name === 'value');
|
@@ -265,6 +325,13 @@ function __processPageAndPoll() {
|
|
265
325
|
node.attributes.push({name: 'value', value: elementNode.value});
|
266
326
|
}
|
267
327
|
}
|
328
|
+
|
329
|
+
if (elementNode.tagName === 'OPTION' && elementNode.parentElement.value === elementNode.value) {
|
330
|
+
const nodeAttr = node.attributes.find(a => a.name === 'selected');
|
331
|
+
if (!nodeAttr) {
|
332
|
+
node.attributes.push({name: 'selected', value: ''});
|
333
|
+
}
|
334
|
+
}
|
268
335
|
return node;
|
269
336
|
}
|
270
337
|
|
@@ -410,7 +477,7 @@ function __processPageAndPoll() {
|
|
410
477
|
value,
|
411
478
|
styleSheet,
|
412
479
|
);
|
413
|
-
dependentUrls = extractResourcesFromStyleSheet(corsFreeStyleSheet
|
480
|
+
dependentUrls = extractResourcesFromStyleSheet(corsFreeStyleSheet);
|
414
481
|
cleanStyleSheet();
|
415
482
|
}
|
416
483
|
} else if (/image\/svg/.test(type)) {
|
@@ -495,6 +562,19 @@ function __processPageAndPoll() {
|
|
495
562
|
const domparser = parser || new DOMParser();
|
496
563
|
const doc = domparser.parseFromString(svgStr, 'image/svg+xml');
|
497
564
|
|
565
|
+
const srcsetUrls = Array.from(doc.querySelectorAll('img[srcset]'))
|
566
|
+
.map(srcsetEl =>
|
567
|
+
srcsetEl
|
568
|
+
.getAttribute('srcset')
|
569
|
+
.split(', ')
|
570
|
+
.map(str => str.trim().split(/\s+/)[0]),
|
571
|
+
)
|
572
|
+
.reduce((acc, urls) => acc.concat(urls), []);
|
573
|
+
|
574
|
+
const srcUrls = Array.from(doc.querySelectorAll('img[src]')).map(srcEl =>
|
575
|
+
srcEl.getAttribute('src'),
|
576
|
+
);
|
577
|
+
|
498
578
|
const fromHref = Array.from(doc.querySelectorAll('image,use,link[rel="stylesheet"]')).map(
|
499
579
|
e => e.getAttribute('href') || e.getAttribute('xlink:href'),
|
500
580
|
);
|
@@ -504,7 +584,9 @@ function __processPageAndPoll() {
|
|
504
584
|
const fromStyleTags = extractResourceUrlsFromStyleTags(doc, false);
|
505
585
|
const fromStyleAttrs = urlsFromStyleAttrOfDoc(doc);
|
506
586
|
|
507
|
-
return
|
587
|
+
return srcsetUrls
|
588
|
+
.concat(srcUrls)
|
589
|
+
.concat(fromHref)
|
508
590
|
.concat(fromObjects)
|
509
591
|
.concat(fromStyleTags)
|
510
592
|
.concat(fromStyleAttrs)
|
@@ -526,53 +608,78 @@ function __processPageAndPoll() {
|
|
526
608
|
/* global window */
|
527
609
|
|
528
610
|
function fetchUrl(url, fetch = window.fetch) {
|
529
|
-
return
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
611
|
+
// Why return a `new Promise` like this? Because people like Atlassian do horrible things.
|
612
|
+
// They monkey patched window.fetch, and made it so it throws a synchronous exception if the route is not well known.
|
613
|
+
// Returning a new Promise guarantees that `fetchUrl` is the async function that it declares to be.
|
614
|
+
return new Promise((resolve, reject) => {
|
615
|
+
return fetch(url, {cache: 'force-cache', credentials: 'same-origin'})
|
616
|
+
.then(resp =>
|
617
|
+
resp.status === 200
|
618
|
+
? resp.arrayBuffer().then(buff => ({
|
619
|
+
url,
|
620
|
+
type: resp.headers.get('Content-Type'),
|
621
|
+
value: buff,
|
622
|
+
}))
|
623
|
+
: Promise.reject(`bad status code ${resp.status}`),
|
624
|
+
)
|
625
|
+
.then(resolve)
|
626
|
+
.catch(err => reject(err));
|
627
|
+
});
|
538
628
|
}
|
539
629
|
|
540
630
|
var fetchUrl_1 = fetchUrl;
|
541
631
|
|
632
|
+
function sanitizeAuthUrl(urlStr) {
|
633
|
+
const url = new URL(urlStr);
|
634
|
+
if (url.username && url.password) {
|
635
|
+
return urlStr.replace(`${url.username}:${url.password}@`, '');
|
636
|
+
}
|
637
|
+
return urlStr;
|
638
|
+
}
|
639
|
+
|
640
|
+
var sanitizeAuthUrl_1 = sanitizeAuthUrl;
|
641
|
+
|
542
642
|
function makeFindStyleSheetByUrl({styleSheetCache}) {
|
543
643
|
return function findStyleSheetByUrl(url, documents) {
|
544
644
|
const allStylesheets = flat_1(documents.map(d => Array.from(d.styleSheets)));
|
545
645
|
return (
|
546
646
|
styleSheetCache[url] ||
|
547
|
-
allStylesheets.find(styleSheet =>
|
647
|
+
allStylesheets.find(styleSheet => {
|
648
|
+
const styleUrl = styleSheet.href && toUnAnchoredUri_1(styleSheet.href);
|
649
|
+
return styleUrl && sanitizeAuthUrl_1(styleUrl) === url;
|
650
|
+
})
|
548
651
|
);
|
549
652
|
};
|
550
653
|
}
|
551
654
|
|
552
655
|
var findStyleSheetByUrl = makeFindStyleSheetByUrl;
|
553
656
|
|
554
|
-
function makeExtractResourcesFromStyleSheet({styleSheetCache}) {
|
555
|
-
return function extractResourcesFromStyleSheet(styleSheet
|
556
|
-
const win = doc.defaultView || (doc.ownerDocument && doc.ownerDocument.defaultView) || window;
|
657
|
+
function makeExtractResourcesFromStyleSheet({styleSheetCache, CSSRule = window.CSSRule}) {
|
658
|
+
return function extractResourcesFromStyleSheet(styleSheet) {
|
557
659
|
const urls = uniq_1(
|
558
660
|
Array.from(styleSheet.cssRules || []).reduce((acc, rule) => {
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
661
|
+
const getRuleUrls = {
|
662
|
+
[CSSRule.IMPORT_RULE]: () => {
|
663
|
+
if (rule.styleSheet) {
|
664
|
+
styleSheetCache[rule.styleSheet.href] = rule.styleSheet;
|
665
|
+
return rule.href;
|
666
|
+
}
|
667
|
+
},
|
668
|
+
[CSSRule.FONT_FACE_RULE]: () => getUrlFromCssText_1(rule.cssText),
|
669
|
+
[CSSRule.SUPPORTS_RULE]: () => extractResourcesFromStyleSheet(rule),
|
670
|
+
[CSSRule.MEDIA_RULE]: () => extractResourcesFromStyleSheet(rule),
|
671
|
+
[CSSRule.STYLE_RULE]: () => {
|
672
|
+
let rv = [];
|
673
|
+
for (let i = 0, ii = rule.style.length; i < ii; i++) {
|
674
|
+
const urls = getUrlFromCssText_1(rule.style.getPropertyValue(rule.style[i]));
|
675
|
+
rv = rv.concat(urls);
|
676
|
+
}
|
677
|
+
return rv;
|
678
|
+
},
|
679
|
+
}[rule.type];
|
680
|
+
|
681
|
+
const urls = (getRuleUrls && getRuleUrls()) || [];
|
682
|
+
return acc.concat(urls);
|
576
683
|
}, []),
|
577
684
|
);
|
578
685
|
return urls.filter(u => u[0] !== '#');
|
@@ -603,7 +710,7 @@ function __processPageAndPoll() {
|
|
603
710
|
? Array.from(doc.styleSheets).find(styleSheet => styleSheet.ownerNode === styleEl)
|
604
711
|
: styleEl.sheet;
|
605
712
|
return styleSheet
|
606
|
-
? resourceUrls.concat(extractResourcesFromStyleSheet(styleSheet
|
713
|
+
? resourceUrls.concat(extractResourcesFromStyleSheet(styleSheet))
|
607
714
|
: resourceUrls;
|
608
715
|
}, []),
|
609
716
|
);
|
@@ -776,7 +883,7 @@ function __processPageAndPoll() {
|
|
776
883
|
|
777
884
|
var sessionCache = makeSessionCache;
|
778
885
|
|
779
|
-
function processPage(doc = document, {showLogs, useSessionCache} = {}) {
|
886
|
+
function processPage(doc = document, {showLogs, useSessionCache, dontFetchResources} = {}) {
|
780
887
|
const log$$1 = showLogs ? log(Date.now()) : noop;
|
781
888
|
log$$1('processPage start');
|
782
889
|
const sessionCache$$1 = useSessionCache && sessionCache({log: log$$1});
|
@@ -826,12 +933,12 @@ function __processPageAndPoll() {
|
|
826
933
|
.map(toUnAnchoredUri_1)
|
827
934
|
.filter(filterInlineUrlsIfExisting);
|
828
935
|
|
829
|
-
const resourceUrlsAndBlobsPromise =
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
936
|
+
const resourceUrlsAndBlobsPromise = dontFetchResources
|
937
|
+
? Promise.resolve({resourceUrls: urls, blobsObj: {}})
|
938
|
+
: getResourceUrlsAndBlobs$$1({documents: docRoots, urls}).then(result => {
|
939
|
+
sessionCache$$1 && sessionCache$$1.persist();
|
940
|
+
return result;
|
941
|
+
});
|
835
942
|
const canvasBlobs = buildCanvasBlobs_1(canvasElements);
|
836
943
|
const frameDocs = extractFrames_1(docRoots);
|
837
944
|
|
@@ -907,38 +1014,13 @@ function __processPageAndPoll() {
|
|
907
1014
|
|
908
1015
|
var processPageAndSerialize_1 = processPageAndSerialize;
|
909
1016
|
|
910
|
-
|
911
|
-
|
912
|
-
function processPageAndPoll(doc) {
|
913
|
-
if (!window[EYES_NAME_SPACE]) {
|
914
|
-
window[EYES_NAME_SPACE] = {};
|
915
|
-
}
|
916
|
-
if (!window[EYES_NAME_SPACE].processPageAndSerializeResult) {
|
917
|
-
window[EYES_NAME_SPACE].processPageAndSerializeResult = {
|
918
|
-
status: 'WIP',
|
919
|
-
value: null,
|
920
|
-
error: null,
|
921
|
-
};
|
922
|
-
processPageAndSerialize_1(doc)
|
923
|
-
.then(r => ((resultObject.status = 'SUCCESS'), (resultObject.value = r)))
|
924
|
-
.catch(e => ((resultObject.status = 'ERROR'), (resultObject.error = e.message)));
|
925
|
-
}
|
926
|
-
|
927
|
-
const resultObject = window[EYES_NAME_SPACE].processPageAndSerializeResult;
|
928
|
-
if (resultObject.status === 'SUCCESS') {
|
929
|
-
window[EYES_NAME_SPACE].processPageAndSerializeResult = null;
|
930
|
-
}
|
931
|
-
|
932
|
-
return JSON.stringify(resultObject);
|
933
|
-
}
|
934
|
-
|
935
|
-
var processPageAndPoll_1 = processPageAndPoll;
|
1017
|
+
var processPageAndSerializePoll = pollify(processPageAndSerialize_1);
|
936
1018
|
|
937
|
-
return
|
1019
|
+
return processPageAndSerializePoll;
|
938
1020
|
|
939
1021
|
}());
|
940
1022
|
|
941
|
-
return
|
1023
|
+
return processPageAndSerializePoll.apply(this, arguments);
|
942
1024
|
}
|
943
1025
|
END
|
944
1026
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module Applitools::Selenium
|
4
4
|
# The main API gateway for the SDK
|
5
5
|
class SeleniumEyes < Applitools::EyesBase
|
6
|
+
include Applitools::Selenium::Concerns::SeleniumEyes
|
6
7
|
# @!visibility private
|
7
8
|
UNKNOWN_DEVICE_PIXEL_RATIO = 0
|
8
9
|
|
@@ -11,8 +12,6 @@ module Applitools::Selenium
|
|
11
12
|
|
12
13
|
DEFAULT_WAIT_BEFORE_SCREENSHOTS = 0.1 # Seconds
|
13
14
|
|
14
|
-
USE_DEFAULT_MATCH_TIMEOUT = -1
|
15
|
-
|
16
15
|
DEFAULT_STITCHING_OVERLAP = 50 # Pixels
|
17
16
|
|
18
17
|
ENTIRE_ELEMENT_SCREENSHOT = 0
|
@@ -96,10 +95,10 @@ module Applitools::Selenium
|
|
96
95
|
# @return [Applitools::RectangleSize] explicit_entire_size
|
97
96
|
|
98
97
|
attr_accessor :base_agent_id, :screenshot, :force_full_page_screenshot, :hide_scrollbars,
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
98
|
+
:wait_before_screenshots, :debug_screenshots, :stitch_mode, :disable_horizontal_scrolling,
|
99
|
+
:disable_vertical_scrolling, :explicit_entire_size, :debug_screenshot_provider, :stitching_overlap,
|
100
|
+
:full_page_capture_algorithm_left_top_offset, :screenshot_type, :send_dom, :use_dom, :enable_patterns,
|
101
|
+
:config
|
103
102
|
attr_reader :driver
|
104
103
|
|
105
104
|
def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
|
@@ -124,8 +123,8 @@ module Applitools::Selenium
|
|
124
123
|
self.region_visibility_strategy = MoveToRegionVisibilityStrategy.new
|
125
124
|
self.debug_screenshots = false
|
126
125
|
self.debug_screenshot_provider = Applitools::DebugScreenshotProvider.new
|
127
|
-
|
128
|
-
|
126
|
+
.tag_access { tag_for_debug }
|
127
|
+
.debug_flag_access { debug_screenshots }
|
129
128
|
self.disable_horizontal_scrolling = false
|
130
129
|
self.disable_vertical_scrolling = false
|
131
130
|
self.explicit_entire_size = nil
|
@@ -138,7 +137,6 @@ module Applitools::Selenium
|
|
138
137
|
self.prevent_dom_processing = false
|
139
138
|
end
|
140
139
|
|
141
|
-
|
142
140
|
def ensure_config
|
143
141
|
self.config = Applitools::Selenium::Configuration.new
|
144
142
|
end
|
@@ -183,7 +181,7 @@ module Applitools::Selenium
|
|
183
181
|
|
184
182
|
self.eyes_screenshot_factory = lambda do |image|
|
185
183
|
Applitools::Selenium::ViewportScreenshot.new(
|
186
|
-
|
184
|
+
image, driver: @driver, force_offset: position_provider.force_offset
|
187
185
|
)
|
188
186
|
end
|
189
187
|
|
@@ -207,44 +205,6 @@ module Applitools::Selenium
|
|
207
205
|
|
208
206
|
private :perform_driver_specific_settings
|
209
207
|
|
210
|
-
# Sets the stitch mode.
|
211
|
-
#
|
212
|
-
# @param [Hash] value The desired type of stitching (:SCROLL is default).
|
213
|
-
# @option value [Symbol] :css use Css to perform stitching.
|
214
|
-
# @option value [Symbol] :scroll Scroll to perform stitching.
|
215
|
-
# @return [Symbol] The type of stitching.
|
216
|
-
# def stitch_mode=(value)
|
217
|
-
# @stitch_mode = if value.to_s.upcase == Applitools::STITCH_MODE[:css].to_s
|
218
|
-
# Applitools::STITCH_MODE[:css]
|
219
|
-
# else
|
220
|
-
# Applitools::STITCH_MODE[:scroll]
|
221
|
-
# end
|
222
|
-
# unless driver.nil?
|
223
|
-
# self.position_provider = self.class.position_provider(
|
224
|
-
# stitch_mode, driver, disable_horizontal_scrolling, disable_vertical_scrolling, explicit_entire_size
|
225
|
-
# )
|
226
|
-
# end
|
227
|
-
# if stitch_mode == Applitools::STITCH_MODE[:css]
|
228
|
-
# @css_transition_original_hide_scrollbars = hide_scrollbars
|
229
|
-
# self.hide_scrollbars = true
|
230
|
-
# else
|
231
|
-
# self.hide_scrollbars = @css_transition_original_hide_scrollbars || false
|
232
|
-
# end
|
233
|
-
# value
|
234
|
-
# end
|
235
|
-
|
236
|
-
# Takes a snapshot of the application under test and matches it with the expected output.
|
237
|
-
#
|
238
|
-
# @param [String] tag An optional tag to be assosiated with the snapshot.
|
239
|
-
# @param [Fixnum] match_timeout The amount of time to retry matching (seconds)
|
240
|
-
def check_window(tag = nil, match_timeout = USE_DEFAULT_MATCH_TIMEOUT)
|
241
|
-
target = Applitools::Selenium::Target.window.tap do |t|
|
242
|
-
t.timeout(match_timeout)
|
243
|
-
t.fully if force_full_page_screenshot
|
244
|
-
end
|
245
|
-
check(tag, target)
|
246
|
-
end
|
247
|
-
|
248
208
|
# @!visibility private
|
249
209
|
def title
|
250
210
|
return driver.title unless dont_get_title
|
@@ -279,8 +239,8 @@ module Applitools::Selenium
|
|
279
239
|
|
280
240
|
self.eyes_screenshot_factory = lambda do |image|
|
281
241
|
Applitools::Selenium::ViewportScreenshot.new(
|
282
|
-
|
283
|
-
|
242
|
+
image,
|
243
|
+
region_provider: region_to_check
|
284
244
|
)
|
285
245
|
end
|
286
246
|
|
@@ -298,8 +258,10 @@ module Applitools::Selenium
|
|
298
258
|
eyes_element = target_to_check.region_to_check.call(driver)
|
299
259
|
|
300
260
|
unless force_full_page_screenshot
|
301
|
-
region_visibility_strategy.move_to_region
|
302
|
-
|
261
|
+
region_visibility_strategy.move_to_region(
|
262
|
+
original_position_provider,
|
263
|
+
Applitools::Location.new(eyes_element.location.x.to_i, eyes_element.location.y.to_i)
|
264
|
+
)
|
303
265
|
driver.find_element(:css, 'html').scroll_data_attribute = true
|
304
266
|
end
|
305
267
|
|
@@ -315,10 +277,10 @@ module Applitools::Selenium
|
|
315
277
|
inside_a_frame = !driver.frame_chain.empty?
|
316
278
|
|
317
279
|
self.screenshot_type = self.class.obtain_screenshot_type(
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
280
|
+
is_element,
|
281
|
+
inside_a_frame,
|
282
|
+
target_to_check.options[:stitch_content],
|
283
|
+
force_full_page_screenshot
|
322
284
|
)
|
323
285
|
|
324
286
|
case screenshot_type
|
@@ -329,14 +291,14 @@ module Applitools::Selenium
|
|
329
291
|
eyes_element.scroll_data_attribute = true
|
330
292
|
eyes_element.overflow_data_attribute = original_overflow
|
331
293
|
self.position_provider = Applitools::Selenium::CssTranslateElementPositionProvider.new(
|
332
|
-
|
333
|
-
|
294
|
+
driver,
|
295
|
+
eyes_element
|
334
296
|
)
|
335
297
|
end
|
336
298
|
end
|
337
299
|
|
338
300
|
check_window_base(
|
339
|
-
|
301
|
+
region_provider, timeout, match_data
|
340
302
|
)
|
341
303
|
ensure
|
342
304
|
eyes_element.overflow = original_overflow unless original_overflow.nil?
|
@@ -352,38 +314,6 @@ module Applitools::Selenium
|
|
352
314
|
self.prevent_dom_processing = false
|
353
315
|
end
|
354
316
|
|
355
|
-
# Validates the contents of an iframe and matches it with the expected output.
|
356
|
-
#
|
357
|
-
# @param [Hash] options The specific parameters of the desired screenshot.
|
358
|
-
# @option options [Array] :target_frames The frames to check.
|
359
|
-
def check_in_frame(options)
|
360
|
-
frames = options.delete :target_frames
|
361
|
-
|
362
|
-
Applitools::ArgumentGuard.is_a? options, 'options', Hash
|
363
|
-
Applitools::ArgumentGuard.is_a? frames, 'target_frames: []', Array
|
364
|
-
|
365
|
-
return yield if block_given? && frames.empty?
|
366
|
-
|
367
|
-
original_frame_chain = driver.frame_chain
|
368
|
-
|
369
|
-
logger.info 'Switching to target frame according to frames path...'
|
370
|
-
driver.switch_to.frames(frames_path: frames)
|
371
|
-
frame_chain_to_reset = driver.frame_chain
|
372
|
-
logger.info 'Done!'
|
373
|
-
|
374
|
-
ensure_frame_visible
|
375
|
-
|
376
|
-
yield if block_given?
|
377
|
-
|
378
|
-
reset_frames_scroll_position(frame_chain_to_reset)
|
379
|
-
|
380
|
-
logger.info 'Switching back into top level frame...'
|
381
|
-
driver.switch_to.default_content
|
382
|
-
return unless original_frame_chain
|
383
|
-
logger.info 'Switching back into original frame...'
|
384
|
-
driver.switch_to.frames frame_chain: original_frame_chain
|
385
|
-
end
|
386
|
-
|
387
317
|
# Creates a region instance.
|
388
318
|
#
|
389
319
|
# @param [Applitools::Element] element The element.
|
@@ -400,16 +330,16 @@ module Applitools::Selenium
|
|
400
330
|
border_bottom_width = element.border_bottom_width
|
401
331
|
|
402
332
|
Applitools::Region.new(
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
333
|
+
p.x.round + border_left_width,
|
334
|
+
p.y.round + border_top_width,
|
335
|
+
d.width - border_left_width - border_right_width,
|
336
|
+
d.height - border_top_width - border_bottom_width
|
407
337
|
).tap do |r|
|
408
338
|
border_padding = Applitools::PaddingBounds.new(
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
339
|
+
border_left_width,
|
340
|
+
border_top_width,
|
341
|
+
border_right_width,
|
342
|
+
border_bottom_width
|
413
343
|
)
|
414
344
|
r.padding(border_padding)
|
415
345
|
end
|
@@ -418,95 +348,6 @@ module Applitools::Selenium
|
|
418
348
|
private :check_in_frame
|
419
349
|
private :region_for_element
|
420
350
|
|
421
|
-
# Takes a snapshot of the application under test and matches a region of
|
422
|
-
# a specific element with the expected region output.
|
423
|
-
#
|
424
|
-
# @param [Applitools::Selenium::Element] element Represents a region to check.
|
425
|
-
# @param [Symbol] how a finder, such :css or :id. Selects a finder will be used to find an element
|
426
|
-
# See Selenium::Webdriver::Element#find_element documentation for full list of possible finders.
|
427
|
-
# @param [String] what The value will be passed to a specified finder. If finder is :css it must be a css selector.
|
428
|
-
# @param [Hash] options
|
429
|
-
# @option options [String] :tag An optional tag to be associated with the snapshot.
|
430
|
-
# @option options [Fixnum] :match_timeout The amount of time to retry matching. (Seconds)
|
431
|
-
# @option options [Boolean] :stitch_content If set to true, will try to get full content of the element
|
432
|
-
# (including hidden content due overflow settings) by scrolling the element,
|
433
|
-
# taking and stitching partial screenshots.
|
434
|
-
# @example Check region by element
|
435
|
-
# check_region(element, tag: 'Check a region by element', match_timeout: 3, stitch_content: false)
|
436
|
-
# @example Check region by css selector
|
437
|
-
# check_region(:css, '.form-row .input#e_mail', tag: 'Check a region by element', match_timeout: 3,
|
438
|
-
# stitch_content: false)
|
439
|
-
# @!parse def check_region(element, how=nil, what=nil, options = {}); end
|
440
|
-
def check_region(*args)
|
441
|
-
options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge! Applitools::Utils.extract_options!(args)
|
442
|
-
target = Applitools::Selenium::Target.new.region(*args).timeout(options[:match_timeout])
|
443
|
-
target.fully if options[:stitch_content]
|
444
|
-
check(options[:tag], target)
|
445
|
-
end
|
446
|
-
|
447
|
-
# Validates the contents of an iframe and matches it with the expected output.
|
448
|
-
#
|
449
|
-
# @param [Hash] options The specific parameters of the desired screenshot.
|
450
|
-
# @option options [Fixnum] :timeout The amount of time to retry matching. (Seconds)
|
451
|
-
# @option options [String] :tag An optional tag to be associated with the snapshot.
|
452
|
-
# @option options [String] :frame Frame element or frame name or frame id.
|
453
|
-
# @option options [String] :name_or_id The name or id of the target frame (deprecated. use :frame instead).
|
454
|
-
# @option options [String] :frame_element The frame element (deprecated. use :frame instead).
|
455
|
-
# @return [Applitools::MatchResult] The match results.
|
456
|
-
|
457
|
-
def check_frame(options = {})
|
458
|
-
options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge!(options)
|
459
|
-
frame = options[:frame] || options[:frame_element] || options[:name_or_id]
|
460
|
-
target = Applitools::Selenium::Target.frame(frame).timeout(options[:timeout]).fully
|
461
|
-
check(options[:tag], target)
|
462
|
-
end
|
463
|
-
|
464
|
-
# Validates the contents of a region in an iframe and matches it with the expected output.
|
465
|
-
#
|
466
|
-
# @param [Hash] options The specific parameters of the desired screenshot.
|
467
|
-
# @option options [String] :name_or_id The name or id of the target frame (deprecated. use :frame instead).
|
468
|
-
# @option options [String] :frame_element The frame element (deprecated. use :frame instead).
|
469
|
-
# @option options [String] :frame Frame element or frame name or frame id.
|
470
|
-
# @option options [String] :tag An optional tag to be associated with the snapshot.
|
471
|
-
# @option options [Symbol] :by By which identifier to find the region (e.g :css, :id).
|
472
|
-
# @option options [Fixnum] :timeout The amount of time to retry matching. (Seconds)
|
473
|
-
# @option options [Boolean] :stitch_content Whether to stitch the content or not.
|
474
|
-
# @return [Applitools::MatchResult] The match results.
|
475
|
-
def check_region_in_frame(options = {})
|
476
|
-
options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil, stitch_content: false }.merge!(options)
|
477
|
-
Applitools::ArgumentGuard.not_nil options[:by], 'options[:by]'
|
478
|
-
Applitools::ArgumentGuard.is_a? options[:by], 'options[:by]', Array
|
479
|
-
|
480
|
-
how_what = options.delete(:by)
|
481
|
-
frame = options[:frame] || options[:frame_element] || options[:name_or_id]
|
482
|
-
|
483
|
-
target = Applitools::Selenium::Target.new.timeout(options[:timeout])
|
484
|
-
target.frame(frame) if frame
|
485
|
-
target.fully if options[:stitch_content]
|
486
|
-
target.region(*how_what)
|
487
|
-
|
488
|
-
check(options[:tag], target)
|
489
|
-
end
|
490
|
-
|
491
|
-
# Use this method to perform seamless testing with selenium through eyes driver.
|
492
|
-
# It yields a block and passes to it an Applitools::Selenium::Driver instance, which wraps standard driver.
|
493
|
-
# Using Selenium methods inside the 'test' block will send the messages to Selenium
|
494
|
-
# after creating the Eyes triggers for them. Options are similar to {open}
|
495
|
-
# @yieldparam driver [Applitools::Selenium::Driver] Gives a driver to a block, which translates calls to a native
|
496
|
-
# Selemium::Driver instance
|
497
|
-
# @example
|
498
|
-
# eyes.test(app_name: 'my app', test_name: 'my test') do |driver|
|
499
|
-
# driver.get "http://www.google.com"
|
500
|
-
# driver.check_window("initial")
|
501
|
-
# end
|
502
|
-
def test(options = {}, &_block)
|
503
|
-
open(options)
|
504
|
-
yield(driver)
|
505
|
-
close
|
506
|
-
ensure
|
507
|
-
abort_if_not_closed
|
508
|
-
end
|
509
|
-
|
510
351
|
# @!visibility private
|
511
352
|
def scroll_to_region
|
512
353
|
region_visibility_strategy.is_a? Applitools::Selenium::MoveToRegionVisibilityStrategy
|
@@ -531,17 +372,21 @@ module Applitools::Selenium
|
|
531
372
|
end
|
532
373
|
end
|
533
374
|
|
375
|
+
def close_async
|
376
|
+
close(false)
|
377
|
+
end
|
378
|
+
|
534
379
|
private
|
535
380
|
|
536
381
|
attr_accessor :check_frame_or_element, :region_to_check, :dont_get_title,
|
537
|
-
|
538
|
-
|
539
|
-
|
382
|
+
:device_pixel_ratio, :position_provider, :scale_provider, :tag_for_debug,
|
383
|
+
:region_visibility_strategy, :eyes_screenshot_factory, :force_driver_resolution_as_viewport_size,
|
384
|
+
:prevent_dom_processing
|
540
385
|
|
541
386
|
def image_provider
|
542
387
|
Applitools::Selenium::TakesScreenshotImageProvider.new(
|
543
|
-
|
544
|
-
|
388
|
+
driver,
|
389
|
+
debug_screenshot_provider: debug_screenshot_provider
|
545
390
|
)
|
546
391
|
end
|
547
392
|
|
@@ -561,7 +406,7 @@ module Applitools::Selenium
|
|
561
406
|
|
562
407
|
begin
|
563
408
|
algo = Applitools::Selenium::FullPageCaptureAlgorithm.new(
|
564
|
-
|
409
|
+
debug_screenshot_provider: debug_screenshot_provider
|
565
410
|
)
|
566
411
|
case screenshot_type
|
567
412
|
when ENTIRE_ELEMENT_SCREENSHOT
|
@@ -587,15 +432,15 @@ module Applitools::Selenium
|
|
587
432
|
region_provider = Applitools::Selenium::RegionProvider.new(driver, Applitools::Region::EMPTY)
|
588
433
|
|
589
434
|
full_page_image = algo.get_stitched_region(
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
435
|
+
image_provider: image_provider,
|
436
|
+
region_to_check: region_provider,
|
437
|
+
origin_provider: Applitools::Selenium::ScrollPositionProvider.new(driver),
|
438
|
+
position_provider: position_provider,
|
439
|
+
scale_provider: scale_provider,
|
440
|
+
cut_provider: cut_provider,
|
441
|
+
wait_before_screenshots: wait_before_screenshots,
|
442
|
+
eyes_screenshot_factory: eyes_screenshot_factory,
|
443
|
+
stitching_overlap: stitching_overlap
|
599
444
|
)
|
600
445
|
|
601
446
|
# binding.pry
|
@@ -606,8 +451,8 @@ module Applitools::Selenium
|
|
606
451
|
end
|
607
452
|
logger.info 'Creating EyesWebDriver screenshot instance..'
|
608
453
|
result = Applitools::Selenium::FullpageScreenshot.new(
|
609
|
-
|
610
|
-
|
454
|
+
full_page_image,
|
455
|
+
region_provider: region_to_check
|
611
456
|
)
|
612
457
|
logger.info 'Done creating EyesWebDriver screenshot instance!'
|
613
458
|
result
|
@@ -616,22 +461,22 @@ module Applitools::Selenium
|
|
616
461
|
def entire_element_screenshot(algo)
|
617
462
|
logger.info 'Entire element screenshot requested'
|
618
463
|
entire_frame_or_element = algo.get_stitched_region(
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
464
|
+
image_provider: image_provider,
|
465
|
+
region_to_check: region_to_check,
|
466
|
+
origin_provider: position_provider,
|
467
|
+
position_provider: position_provider,
|
468
|
+
scale_provider: scale_provider,
|
469
|
+
cut_provider: cut_provider,
|
470
|
+
wait_before_screenshots: wait_before_screenshots,
|
471
|
+
eyes_screenshot_factory: eyes_screenshot_factory,
|
472
|
+
stitching_overlap: stitching_overlap,
|
473
|
+
top_left_position: full_page_capture_algorithm_left_top_offset
|
629
474
|
)
|
630
475
|
|
631
476
|
logger.info 'Building screenshot object (EyesStitchedElementScreenshot)...'
|
632
477
|
result = Applitools::Selenium::EntireElementScreenshot.new(
|
633
|
-
|
634
|
-
|
478
|
+
entire_frame_or_element,
|
479
|
+
region_provider: region_to_check
|
635
480
|
)
|
636
481
|
logger.info 'Done!'
|
637
482
|
result
|
@@ -692,7 +537,7 @@ module Applitools::Selenium
|
|
692
537
|
|
693
538
|
begin
|
694
539
|
self.scale_provider = Applitools::Selenium::ContextBasedScaleProvider.new(position_provider.entire_size,
|
695
|
-
|
540
|
+
viewport_size, device_pixel_ratio)
|
696
541
|
rescue StandardError
|
697
542
|
logger.info 'Failed to set ContextBasedScaleProvider'
|
698
543
|
logger.info 'Using FixedScaleProvider instead'
|
@@ -856,7 +701,7 @@ module Applitools::Selenium
|
|
856
701
|
|
857
702
|
class << self
|
858
703
|
def position_provider(stitch_mode, driver, disable_horizontal = false, disable_vertical = false,
|
859
|
-
|
704
|
+
explicit_entire_size = nil)
|
860
705
|
|
861
706
|
max_width = nil
|
862
707
|
max_height = nil
|
@@ -867,10 +712,10 @@ module Applitools::Selenium
|
|
867
712
|
case stitch_mode
|
868
713
|
when Applitools::Selenium::StitchModes::SCROLL
|
869
714
|
Applitools::Selenium::ScrollPositionProvider.new(driver, disable_horizontal, disable_vertical,
|
870
|
-
|
715
|
+
max_width, max_height)
|
871
716
|
when Applitools::Selenium::StitchModes::CSS
|
872
717
|
Applitools::Selenium::CssTranslatePositionProvider.new(driver, disable_horizontal, disable_vertical,
|
873
|
-
|
718
|
+
max_width, max_height)
|
874
719
|
end
|
875
720
|
end
|
876
721
|
end
|