@applitools/eyes-testcafe 2.0.0-beta.8 → 2.0.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/CHANGELOG.md +467 -0
- package/LICENSE +1 -1
- package/README.md +7 -531
- package/dist/api.js +51 -0
- package/dist/extract-environment.js +15 -0
- package/dist/index-legacy.js +5 -0
- package/dist/index.js +19 -0
- package/dist/legacy.js +225 -0
- package/dist/spec-driver.js +245 -0
- package/package.json +73 -76
- package/types/index-legacy.d.ts +2 -0
- package/types/index.d.ts +1833 -0
- package/dist/captureFrameAndPoll.js +0 -1468
- package/dist/captureFrameAndPollForIE.js +0 -12569
- package/index.js +0 -29
- package/lib/BordersAwareElementContentLocationProvider.js +0 -79
- package/lib/Eyes.js +0 -927
- package/lib/EyesFactory.js +0 -73
- package/lib/EyesTestCafe.js +0 -1270
- package/lib/EyesTestcafeUtils.js +0 -440
- package/lib/EyesVisualGrid.js +0 -345
- package/lib/ImageOrientationHandler.js +0 -31
- package/lib/JavascriptHandler.js +0 -20
- package/lib/TestCafeExecutor.js +0 -57
- package/lib/capture/EyesWebDriverScreenshot.js +0 -650
- package/lib/capture/EyesWebDriverScreenshotFactory.js +0 -32
- package/lib/capture/FirefoxScreenshotImageProvider.js +0 -63
- package/lib/capture/ImageProviderFactory.js +0 -38
- package/lib/capture/SafariScreenshotImageProvider.js +0 -254
- package/lib/capture/TakesScreenshotImageProvider.js +0 -35
- package/lib/errors/EyesDriverOperationError.js +0 -10
- package/lib/errors/NoFramesError.js +0 -7
- package/lib/fluent/AccessibilityRegionByElement.js +0 -46
- package/lib/fluent/AccessibilityRegionBySelector.js +0 -58
- package/lib/fluent/FloatingRegionByElement.js +0 -56
- package/lib/fluent/FloatingRegionBySelector.js +0 -63
- package/lib/fluent/FrameLocator.js +0 -110
- package/lib/fluent/IgnoreRegionByElement.js +0 -51
- package/lib/fluent/IgnoreRegionBySelector.js +0 -57
- package/lib/fluent/SelectorByElement.js +0 -37
- package/lib/fluent/SelectorByLocator.js +0 -47
- package/lib/fluent/Target.js +0 -17
- package/lib/fluent/TestcafeCheckSettings.js +0 -352
- package/lib/frames/Frame.js +0 -149
- package/lib/frames/FrameChain.js +0 -175
- package/lib/getCaptureDomScript.js +0 -14
- package/lib/hash.js +0 -15
- package/lib/isTestcafeSelector.js +0 -7
- package/lib/makeClientFunctionWrapper.js +0 -61
- package/lib/positioning/CssTranslatePositionMemento.js +0 -39
- package/lib/positioning/CssTranslatePositionProvider.js +0 -130
- package/lib/positioning/ElementPositionMemento.js +0 -36
- package/lib/positioning/ElementPositionProvider.js +0 -88
- package/lib/positioning/FirefoxRegionPositionCompensation.js +0 -45
- package/lib/positioning/ImageRotation.js +0 -22
- package/lib/positioning/OverflowAwareCssTranslatePositionProvider.js +0 -17
- package/lib/positioning/OverflowAwareScrollPositionProvider.js +0 -17
- package/lib/positioning/RegionPositionCompensationFactory.js +0 -37
- package/lib/positioning/SafariRegionPositionCompensation.js +0 -26
- package/lib/positioning/ScrollPositionMemento.js +0 -36
- package/lib/positioning/ScrollPositionProvider.js +0 -118
- package/lib/positioning/fixImageMarkPosition.js +0 -36
- package/lib/regionVisibility/MoveToRegionVisibilityStrategy.js +0 -55
- package/lib/regionVisibility/NopRegionVisibilityStrategy.js +0 -35
- package/lib/regionVisibility/RegionVisibilityStrategy.js +0 -30
- package/lib/runner/ClassicRunner.js +0 -31
- package/lib/runner/EyesRunner.js +0 -41
- package/lib/runner/TestResultContainer.js +0 -38
- package/lib/runner/TestResultsSummary.js +0 -112
- package/lib/runner/VisualGridRunner.js +0 -57
- package/lib/safeExecuteFunction.js +0 -28
- package/lib/wrappers/EyesTargetLocator.js +0 -329
- package/lib/wrappers/EyesWebDriver.js +0 -560
- package/lib/wrappers/EyesWebElement.js +0 -383
- package/lib/wrappers/EyesWebElementPromise.js +0 -68
package/lib/EyesTestCafe.js
DELETED
|
@@ -1,1270 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const {DomCapture} = require('@applitools/dom-utils')
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
SimplePropertyHandler,
|
|
7
|
-
CoordinatesType,
|
|
8
|
-
Region,
|
|
9
|
-
Location,
|
|
10
|
-
RectangleSize,
|
|
11
|
-
UserAgent,
|
|
12
|
-
ArgumentGuard,
|
|
13
|
-
TypeUtils,
|
|
14
|
-
StitchMode,
|
|
15
|
-
} = require('@applitools/eyes-common')
|
|
16
|
-
|
|
17
|
-
const {
|
|
18
|
-
FullPageCaptureAlgorithm,
|
|
19
|
-
FixedScaleProviderFactory,
|
|
20
|
-
NullScaleProvider,
|
|
21
|
-
RegionProvider,
|
|
22
|
-
NullRegionProvider,
|
|
23
|
-
ContextBasedScaleProviderFactory,
|
|
24
|
-
ScaleProviderIdentityFactory,
|
|
25
|
-
NullCutProvider,
|
|
26
|
-
MatchResult,
|
|
27
|
-
EyesJsBrowserUtils,
|
|
28
|
-
} = require('@applitools/eyes-sdk-core')
|
|
29
|
-
const getCaptureDomScript = require('./getCaptureDomScript')
|
|
30
|
-
|
|
31
|
-
const {ClassicRunner} = require('./runner/ClassicRunner')
|
|
32
|
-
const {ImageProviderFactory} = require('./capture/ImageProviderFactory')
|
|
33
|
-
const {EyesWebDriverScreenshotFactory} = require('./capture/EyesWebDriverScreenshotFactory')
|
|
34
|
-
const {FrameChain} = require('./frames/FrameChain')
|
|
35
|
-
const {EyesTargetLocator} = require('./wrappers/EyesTargetLocator')
|
|
36
|
-
const {EyesTestcafeUtils} = require('./EyesTestcafeUtils')
|
|
37
|
-
const {EyesWebElement} = require('./wrappers/EyesWebElement')
|
|
38
|
-
const {EyesWebDriverScreenshot} = require('./capture/EyesWebDriverScreenshot')
|
|
39
|
-
const {
|
|
40
|
-
RegionPositionCompensationFactory,
|
|
41
|
-
} = require('./positioning/RegionPositionCompensationFactory')
|
|
42
|
-
const {ScrollPositionProvider} = require('./positioning/ScrollPositionProvider')
|
|
43
|
-
const {ElementPositionProvider} = require('./positioning/ElementPositionProvider')
|
|
44
|
-
const {CssTranslatePositionProvider} = require('./positioning/CssTranslatePositionProvider')
|
|
45
|
-
const {Eyes} = require('./Eyes')
|
|
46
|
-
const GEN_XPATH_SCRIPT = `function genXpath(el) {
|
|
47
|
-
if (!el.ownerDocument) return ''; // this is the document node
|
|
48
|
-
let xpath = '',
|
|
49
|
-
currEl = el,
|
|
50
|
-
doc = el.ownerDocument,
|
|
51
|
-
frameElement = doc.defaultView.frameElement,
|
|
52
|
-
index;
|
|
53
|
-
while (currEl !== doc) {
|
|
54
|
-
index = window.Array.prototype.filter.call(currEl.parentNode.childNodes, node => node.tagName === currEl.tagName).indexOf(currEl) + 1
|
|
55
|
-
xpath = currEl.tagName + '[' + index + ']/' + xpath;
|
|
56
|
-
currEl = currEl.parentNode;
|
|
57
|
-
}
|
|
58
|
-
if (frameElement) {
|
|
59
|
-
xpath = genXpath(frameElement) + ',' + xpath;
|
|
60
|
-
}
|
|
61
|
-
return xpath.replace(/\\/$/, '');
|
|
62
|
-
}`
|
|
63
|
-
|
|
64
|
-
const ELEMENTS_BY_XPATH_SCRIPT = `xpath => {
|
|
65
|
-
const iterator = document.evaluate(xpath, document, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
|
|
66
|
-
const items = [];
|
|
67
|
-
let item = iterator.iterateNext();
|
|
68
|
-
while (item) {
|
|
69
|
-
items.push(item);
|
|
70
|
-
item = iterator.iterateNext();
|
|
71
|
-
}
|
|
72
|
-
return items;
|
|
73
|
-
}`
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* The main API gateway for the SDK.
|
|
77
|
-
*
|
|
78
|
-
* @ignore
|
|
79
|
-
*/
|
|
80
|
-
class EyesTestCafe extends Eyes {
|
|
81
|
-
/** @var {Logger} EyesTestCafe#_logger */
|
|
82
|
-
/** @var {Configuration} EyesTestCafe#_configuration */
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Creates a new (possibly disabled) Eyes instance that interacts with the Eyes Server at the specified url.
|
|
86
|
-
*
|
|
87
|
-
* @param {string} [serverUrl] - The Eyes server URL.
|
|
88
|
-
* @param {boolean} [isDisabled=false] - Set {@code true} to disable Applitools Eyes and use the WebDriver directly.
|
|
89
|
-
* @param {ClassicRunner} [runner] - Set shared ClassicRunner if you want to group results.
|
|
90
|
-
*/
|
|
91
|
-
constructor(serverUrl, isDisabled, runner = new ClassicRunner()) {
|
|
92
|
-
super(serverUrl, isDisabled, runner)
|
|
93
|
-
|
|
94
|
-
/** @type {boolean} */
|
|
95
|
-
this._checkFrameOrElement = false
|
|
96
|
-
|
|
97
|
-
/** @type {Location} */
|
|
98
|
-
this._imageLocation = undefined
|
|
99
|
-
|
|
100
|
-
/** @type {UserAgent} */
|
|
101
|
-
this._userAgent = undefined
|
|
102
|
-
/** @type {ImageProvider} */
|
|
103
|
-
this._imageProvider = undefined
|
|
104
|
-
/** @type {RegionPositionCompensation} */
|
|
105
|
-
this._regionPositionCompensation = undefined
|
|
106
|
-
|
|
107
|
-
/** @type {EyesWebElement|WebElement} */
|
|
108
|
-
this._targetElement = undefined
|
|
109
|
-
/** @type {PositionMemento} */
|
|
110
|
-
this._positionMemento = undefined
|
|
111
|
-
/** @type {Region} */
|
|
112
|
-
this._effectiveViewport = Region.EMPTY
|
|
113
|
-
/** @type {EyesWebDriverScreenshotFactory} */
|
|
114
|
-
this._screenshotFactory = undefined
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* @inheritDoc
|
|
119
|
-
*/
|
|
120
|
-
async open(driver, appName, testName, viewportSize, sessionType) {
|
|
121
|
-
if (this.getIsDisabled()) {
|
|
122
|
-
this._logger.verbose('Ignored')
|
|
123
|
-
return driver
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
ArgumentGuard.notNull(driver, 'driver')
|
|
127
|
-
|
|
128
|
-
// noinspection NonBlockStatementBodyJS
|
|
129
|
-
if (appName) this._configuration.setAppName(appName)
|
|
130
|
-
// noinspection NonBlockStatementBodyJS
|
|
131
|
-
if (testName) this._configuration.setTestName(testName)
|
|
132
|
-
// noinspection NonBlockStatementBodyJS
|
|
133
|
-
if (viewportSize) this._configuration.setViewportSize(viewportSize)
|
|
134
|
-
// noinspection NonBlockStatementBodyJS
|
|
135
|
-
if (sessionType) this._configuration.setSessionType(sessionType)
|
|
136
|
-
|
|
137
|
-
ArgumentGuard.notNull(this._configuration.getAppName(), 'appName')
|
|
138
|
-
ArgumentGuard.notNull(this._configuration.getTestName(), 'testName')
|
|
139
|
-
|
|
140
|
-
this._initDriver(driver)
|
|
141
|
-
|
|
142
|
-
this._screenshotFactory = new EyesWebDriverScreenshotFactory(this._logger, this._driver)
|
|
143
|
-
|
|
144
|
-
const uaString = await this._driver.getUserAgent()
|
|
145
|
-
if (uaString) {
|
|
146
|
-
this._userAgent = UserAgent.parseUserAgentString(uaString, true)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
this._imageProvider = ImageProviderFactory.getImageProvider(
|
|
150
|
-
this._userAgent,
|
|
151
|
-
this,
|
|
152
|
-
this._logger,
|
|
153
|
-
this._driver,
|
|
154
|
-
)
|
|
155
|
-
this._regionPositionCompensation = RegionPositionCompensationFactory.getRegionPositionCompensation(
|
|
156
|
-
this._userAgent,
|
|
157
|
-
this,
|
|
158
|
-
this._logger,
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
await this.openBase(
|
|
162
|
-
this._configuration.getAppName(),
|
|
163
|
-
this._configuration.getTestName(),
|
|
164
|
-
this._configuration.getViewportSize(),
|
|
165
|
-
this._configuration.getSessionType(),
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
this._devicePixelRatio = Eyes.UNKNOWN_DEVICE_PIXEL_RATIO
|
|
169
|
-
|
|
170
|
-
this._driver.setRotation(this._rotation)
|
|
171
|
-
return this._driver
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* @private
|
|
176
|
-
* @return {PositionProvider}
|
|
177
|
-
*/
|
|
178
|
-
_createPositionProvider(scrollRootElement = this._scrollRootElement) {
|
|
179
|
-
// Setting the correct position provider.
|
|
180
|
-
this._logger.verbose(
|
|
181
|
-
'initializing position provider. stitchMode:',
|
|
182
|
-
this._configuration.getStitchMode(),
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
switch (this._configuration.getStitchMode()) {
|
|
186
|
-
case StitchMode.CSS:
|
|
187
|
-
return new CssTranslatePositionProvider(this._logger, this._jsExecutor, scrollRootElement)
|
|
188
|
-
default:
|
|
189
|
-
return new ScrollPositionProvider(this._logger, this._jsExecutor, scrollRootElement)
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Perform visual validation
|
|
195
|
-
*
|
|
196
|
-
* @param {string} name - A name to be associated with the match
|
|
197
|
-
* @param {TestcafeCheckSettings} checkSettings - Target instance which describes whether we want a window/region/frame
|
|
198
|
-
* @return {Promise<TestResults>} - A promise which is resolved when the validation is finished.
|
|
199
|
-
*/
|
|
200
|
-
async testWindow(name, checkSettings) {
|
|
201
|
-
const originalCheckWindowBase = this.checkWindowBase
|
|
202
|
-
try {
|
|
203
|
-
this.checkWindowBase = this.checkSingleWindowBase
|
|
204
|
-
|
|
205
|
-
return await this.check(name, checkSettings)
|
|
206
|
-
} finally {
|
|
207
|
-
this.checkWindowBase = originalCheckWindowBase
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// noinspection FunctionWithMoreThanThreeNegationsJS
|
|
212
|
-
/**
|
|
213
|
-
* @inheritDoc
|
|
214
|
-
*/
|
|
215
|
-
async check(name, checkSettings) {
|
|
216
|
-
if (this._configuration.getIsDisabled()) {
|
|
217
|
-
this._logger.log(`check('${name}', ${checkSettings}): Ignored`)
|
|
218
|
-
return new MatchResult()
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
ArgumentGuard.notNull(checkSettings, 'checkSettings')
|
|
222
|
-
ArgumentGuard.isValidState(this._isOpen, 'Eyes not open')
|
|
223
|
-
|
|
224
|
-
if (TypeUtils.isNotNull(name)) {
|
|
225
|
-
checkSettings.withName(name)
|
|
226
|
-
} else {
|
|
227
|
-
name = checkSettings.getName()
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
this._logger.verbose(`check(${checkSettings}) - begin`)
|
|
231
|
-
this._stitchContent = checkSettings.getStitchContent()
|
|
232
|
-
const targetRegion = checkSettings.getTargetRegion()
|
|
233
|
-
this._scrollRootElement = this._getScrollRootElementFromCheckSettings(checkSettings)
|
|
234
|
-
|
|
235
|
-
this._currentFramePositionProvider = null
|
|
236
|
-
this.setPositionProvider(this._createPositionProvider())
|
|
237
|
-
this._originalFC = this._driver.getFrameChain().clone()
|
|
238
|
-
|
|
239
|
-
// const validationInfo = this.fireValidationWillStartEvent(name);
|
|
240
|
-
|
|
241
|
-
// if (!(await EyesTestcafeUtils.isMobileDevice(this._driver))) {
|
|
242
|
-
this._logger.verbose(`URL: ${await this._driver.getCurrentUrl()}`)
|
|
243
|
-
// }
|
|
244
|
-
|
|
245
|
-
const switchedToFrameCount = await this._switchToFrame(checkSettings)
|
|
246
|
-
|
|
247
|
-
this._regionToCheck = null
|
|
248
|
-
|
|
249
|
-
let result = null
|
|
250
|
-
|
|
251
|
-
const switchTo = this._driver.switchTo()
|
|
252
|
-
let originalFC = null
|
|
253
|
-
|
|
254
|
-
if (targetRegion) {
|
|
255
|
-
this._logger.verbose('have target region')
|
|
256
|
-
originalFC = await this._tryHideScrollbars()
|
|
257
|
-
this._imageLocation = targetRegion.getLocation()
|
|
258
|
-
const source = await this._driver.getCurrentUrl()
|
|
259
|
-
result = await this.checkWindowBase(
|
|
260
|
-
new RegionProvider(targetRegion),
|
|
261
|
-
name,
|
|
262
|
-
false,
|
|
263
|
-
checkSettings,
|
|
264
|
-
source,
|
|
265
|
-
)
|
|
266
|
-
} else if (checkSettings) {
|
|
267
|
-
let targetElement = checkSettings.getTargetElement()
|
|
268
|
-
|
|
269
|
-
const targetSelector = checkSettings.getTargetSelector()
|
|
270
|
-
if (!targetElement && targetSelector) {
|
|
271
|
-
targetElement = this._driver.findElement(targetSelector)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (targetElement) {
|
|
275
|
-
this._logger.verbose('have target element')
|
|
276
|
-
originalFC = await this._tryHideScrollbars()
|
|
277
|
-
this._targetElement = new EyesWebElement(this._logger, this._driver, targetElement)
|
|
278
|
-
if (this._stitchContent) {
|
|
279
|
-
result = await this._checkElement(undefined, name, checkSettings)
|
|
280
|
-
} else {
|
|
281
|
-
result = await this._checkRegionByElement(name, checkSettings)
|
|
282
|
-
}
|
|
283
|
-
this._targetElement = null
|
|
284
|
-
} else if (checkSettings.getFrameChain().length > 0) {
|
|
285
|
-
this._logger.verbose('have frame chain')
|
|
286
|
-
originalFC = await this._tryHideScrollbars()
|
|
287
|
-
if (this._stitchContent) {
|
|
288
|
-
result = await this._checkFullFrameOrElement(name, checkSettings)
|
|
289
|
-
} else {
|
|
290
|
-
result = await this._checkFrameFluent(name, checkSettings)
|
|
291
|
-
}
|
|
292
|
-
} else {
|
|
293
|
-
this._logger.verbose('default case')
|
|
294
|
-
// if (!(await EyesTestcafeUtils.isMobileDevice(this._driver))) {
|
|
295
|
-
// required to prevent cut line on the last stitched part of the page on some browsers (like firefox).
|
|
296
|
-
await switchTo.defaultContent()
|
|
297
|
-
originalFC = await this._tryHideScrollbars()
|
|
298
|
-
const scrollRootElement = this.getScrollRootElement()
|
|
299
|
-
this._currentFramePositionProvider = this._createPositionProvider(scrollRootElement)
|
|
300
|
-
// }
|
|
301
|
-
const source = await this._driver.getCurrentUrl()
|
|
302
|
-
result = await this.checkWindowBase(
|
|
303
|
-
new NullRegionProvider(),
|
|
304
|
-
name,
|
|
305
|
-
false,
|
|
306
|
-
checkSettings,
|
|
307
|
-
source,
|
|
308
|
-
)
|
|
309
|
-
await switchTo.frames(this._originalFC)
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (!result) {
|
|
314
|
-
result = new MatchResult()
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
await this._switchToParentFrame(switchedToFrameCount)
|
|
318
|
-
|
|
319
|
-
if (this._positionMemento) {
|
|
320
|
-
await this._positionProviderHandler.get().restoreState(this._positionMemento)
|
|
321
|
-
this._positionMemento = null
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
await switchTo.resetScroll()
|
|
325
|
-
|
|
326
|
-
if (originalFC) {
|
|
327
|
-
await this._tryRestoreScrollbars(originalFC)
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
await this._trySwitchToFrames(switchTo, this._originalFC)
|
|
331
|
-
|
|
332
|
-
this._stitchContent = false
|
|
333
|
-
this._imageLocation = null
|
|
334
|
-
|
|
335
|
-
this._logger.verbose('check - done!')
|
|
336
|
-
return result
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* @private
|
|
341
|
-
* @param {EyesTargetLocator} switchTo
|
|
342
|
-
* @param {FrameChain} frames
|
|
343
|
-
* @return {Promise}
|
|
344
|
-
*/
|
|
345
|
-
async _trySwitchToFrames(switchTo, frames) {
|
|
346
|
-
// if (await EyesTestcafeUtils.isMobileDevice(this._driver)) {
|
|
347
|
-
// return;
|
|
348
|
-
// }
|
|
349
|
-
|
|
350
|
-
try {
|
|
351
|
-
await switchTo.frames(frames)
|
|
352
|
-
} catch (err) {
|
|
353
|
-
this._logger.log(`WARNING: Failed to switch to original frame chain! ${err}`)
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* @private
|
|
359
|
-
* @return {Promise<MatchResult>}
|
|
360
|
-
*/
|
|
361
|
-
async _checkFrameFluent(name, checkSettings) {
|
|
362
|
-
const frameChain = this._driver.getFrameChain().clone()
|
|
363
|
-
const targetFrame = frameChain.pop()
|
|
364
|
-
this._targetElement = targetFrame.getReference()
|
|
365
|
-
|
|
366
|
-
await this._driver.switchTo().framesDoScroll(frameChain)
|
|
367
|
-
const result = await this._checkRegionByElement(name, checkSettings)
|
|
368
|
-
this._targetElement = null
|
|
369
|
-
return result
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* @private
|
|
374
|
-
* @return {Promise<number>}
|
|
375
|
-
*/
|
|
376
|
-
async _switchToParentFrame(switchedToFrameCount) {
|
|
377
|
-
if (switchedToFrameCount > 0) {
|
|
378
|
-
await this._driver.switchTo().parentFrame()
|
|
379
|
-
return this._switchToParentFrame(switchedToFrameCount - 1)
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
return switchedToFrameCount
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* @private
|
|
387
|
-
* @param {TestcafeCheckSettings} checkSettings
|
|
388
|
-
* @return {Promise<number>}
|
|
389
|
-
*/
|
|
390
|
-
async _switchToFrame(checkSettings) {
|
|
391
|
-
if (!checkSettings) {
|
|
392
|
-
return 0
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const frameChain = checkSettings.getFrameChain()
|
|
396
|
-
let switchedToFrameCount = 0
|
|
397
|
-
for (const frameLocator of frameChain) {
|
|
398
|
-
if (await this._switchToFrameLocator(frameLocator)) {
|
|
399
|
-
switchedToFrameCount += 1
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return switchedToFrameCount
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* @private
|
|
407
|
-
* @param {FrameLocator} frameTarget
|
|
408
|
-
* @return {Promise<boolean>}
|
|
409
|
-
*/
|
|
410
|
-
async _switchToFrameLocator(frameTarget) {
|
|
411
|
-
const switchTo = this._driver.switchTo()
|
|
412
|
-
|
|
413
|
-
if (frameTarget.getFrameIndex()) {
|
|
414
|
-
await switchTo.frame(frameTarget.getFrameIndex())
|
|
415
|
-
await this._updateFrameScrollRoot(frameTarget)
|
|
416
|
-
return true
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
if (frameTarget.getFrameNameOrId()) {
|
|
420
|
-
await switchTo.frame(frameTarget.getFrameNameOrId())
|
|
421
|
-
await this._updateFrameScrollRoot(frameTarget)
|
|
422
|
-
return true
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (frameTarget.getFrameElement()) {
|
|
426
|
-
await switchTo.frame(frameTarget.getFrameElement())
|
|
427
|
-
await this._updateFrameScrollRoot(frameTarget)
|
|
428
|
-
return true
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (frameTarget.getFrameSelector()) {
|
|
432
|
-
const frameElement = this._driver.findElement(frameTarget.getFrameSelector())
|
|
433
|
-
if (frameElement) {
|
|
434
|
-
await switchTo.frame(frameElement)
|
|
435
|
-
await this._updateFrameScrollRoot(frameTarget)
|
|
436
|
-
return true
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return false
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* @private
|
|
445
|
-
* @param {FrameLocator} frameTarget
|
|
446
|
-
*/
|
|
447
|
-
async _updateFrameScrollRoot(frameTarget) {
|
|
448
|
-
const rootElement = await this._getScrollRootElementFromCheckSettings(frameTarget)
|
|
449
|
-
const frame = this._driver.getFrameChain().peek()
|
|
450
|
-
frame.setScrollRootElement(rootElement)
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* @private
|
|
455
|
-
* @return {Promise<MatchResult>}
|
|
456
|
-
*/
|
|
457
|
-
async _checkFullFrameOrElement(name, checkSettings) {
|
|
458
|
-
const self = this
|
|
459
|
-
this._checkFrameOrElement = true
|
|
460
|
-
this._logger.verbose('checkFullFrameOrElement()')
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* @private
|
|
464
|
-
* @type {RegionProvider}
|
|
465
|
-
*/
|
|
466
|
-
const RegionProviderImpl = class RegionProviderImpl extends RegionProvider {
|
|
467
|
-
// noinspection JSUnusedGlobalSymbols
|
|
468
|
-
/** @inheritDoc */
|
|
469
|
-
async getRegion() {
|
|
470
|
-
const region = await self._getFullFrameOrElementRegion()
|
|
471
|
-
self._imageLocation = region.getLocation()
|
|
472
|
-
return region
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const source = await this._driver.getCurrentUrl()
|
|
477
|
-
const result = await this.checkWindowBase(
|
|
478
|
-
new RegionProviderImpl(),
|
|
479
|
-
name,
|
|
480
|
-
false,
|
|
481
|
-
checkSettings,
|
|
482
|
-
source,
|
|
483
|
-
)
|
|
484
|
-
this._checkFrameOrElement = false
|
|
485
|
-
return result
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* @private
|
|
490
|
-
* @return {Promise<Region>}
|
|
491
|
-
*/
|
|
492
|
-
async _getFullFrameOrElementRegion() {
|
|
493
|
-
if (this._checkFrameOrElement) {
|
|
494
|
-
const fc = await this._ensureFrameVisible()
|
|
495
|
-
|
|
496
|
-
// FIXME - Scaling should be handled in a single place instead
|
|
497
|
-
const scaleProviderFactory = await this._updateScalingParams()
|
|
498
|
-
|
|
499
|
-
const screenshotImage = await this._imageProvider.getImage()
|
|
500
|
-
|
|
501
|
-
await this._debugScreenshotsProvider.save(screenshotImage, 'checkFullFrameOrElement')
|
|
502
|
-
|
|
503
|
-
scaleProviderFactory.getScaleProvider(screenshotImage.getWidth())
|
|
504
|
-
|
|
505
|
-
const switchTo = this._driver.switchTo()
|
|
506
|
-
await switchTo.frames(fc)
|
|
507
|
-
|
|
508
|
-
const screenshot = await EyesWebDriverScreenshot.fromScreenshotType(
|
|
509
|
-
this._logger,
|
|
510
|
-
this._driver,
|
|
511
|
-
screenshotImage,
|
|
512
|
-
)
|
|
513
|
-
this._logger.verbose('replacing regionToCheck')
|
|
514
|
-
this.setRegionToCheck(screenshot.getFrameWindow())
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
return Region.EMPTY
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* @private
|
|
522
|
-
* @return {Promise<FrameChain>}
|
|
523
|
-
*/
|
|
524
|
-
async _ensureFrameVisible() {
|
|
525
|
-
this._logger.verbose('scrollRootElement_:', this._scrollRootElement)
|
|
526
|
-
const originalFC = this._driver.getFrameChain().clone()
|
|
527
|
-
const fc = this._driver.getFrameChain().clone()
|
|
528
|
-
await this._driver.executeScript('window.scrollTo(0,0);')
|
|
529
|
-
|
|
530
|
-
while (fc.size() > 0) {
|
|
531
|
-
this._logger.verbose(`fc.Count: ${fc.size()}`)
|
|
532
|
-
// driver.getRemoteWebDriver().switchTo().parentFrame();
|
|
533
|
-
await EyesTargetLocator.tryParentFrame(this._driver.getRemoteWebDriver().switchTo(), fc)
|
|
534
|
-
await this._driver.executeScript('window.scrollTo(0,0);')
|
|
535
|
-
const prevFrame = fc.pop()
|
|
536
|
-
const frame = fc.peek()
|
|
537
|
-
let scrollRootElement = null
|
|
538
|
-
if (fc.size() === this._originalFC.size()) {
|
|
539
|
-
this._logger.verbose('PositionProvider:', this._positionProviderHandler.get())
|
|
540
|
-
this._positionMemento = await this._positionProviderHandler.get().getState()
|
|
541
|
-
scrollRootElement = this._scrollRootElement
|
|
542
|
-
} else {
|
|
543
|
-
if (frame != null) {
|
|
544
|
-
scrollRootElement = await frame.getForceScrollRootElement(this._driver)
|
|
545
|
-
}
|
|
546
|
-
if (scrollRootElement == null) {
|
|
547
|
-
scrollRootElement = this._driver.findElement('html')
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
this._logger.verbose('scrollRootElement:', scrollRootElement)
|
|
551
|
-
|
|
552
|
-
const positionProvider = this._getElementPositionProvider(scrollRootElement)
|
|
553
|
-
await positionProvider.setPosition(prevFrame.getLocation())
|
|
554
|
-
|
|
555
|
-
const reg = new Region(Location.ZERO, prevFrame.getInnerSize())
|
|
556
|
-
this._effectiveViewport.intersect(reg)
|
|
557
|
-
}
|
|
558
|
-
await this._driver.switchTo().frames(originalFC)
|
|
559
|
-
return originalFC
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* @private
|
|
564
|
-
* @param {WebElement} element
|
|
565
|
-
* @return {Promise}
|
|
566
|
-
*/
|
|
567
|
-
async _ensureElementVisible(element) {
|
|
568
|
-
if (this._targetElement == null || !this.getScrollToRegion()) {
|
|
569
|
-
// No element? we must be checking the window.
|
|
570
|
-
return
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// if (await EyesTestcafeUtils.isMobileDevice(this._driver)) {
|
|
574
|
-
// this._logger.log("NATIVE context identified, skipping 'ensure element visible'");
|
|
575
|
-
// return;
|
|
576
|
-
// }
|
|
577
|
-
|
|
578
|
-
const originalFC = this._driver.getFrameChain().clone()
|
|
579
|
-
const switchTo = this._driver.switchTo()
|
|
580
|
-
|
|
581
|
-
const eyesWebElement = new EyesWebElement(this._logger, this._driver, element)
|
|
582
|
-
let elementBounds = await eyesWebElement.getBounds()
|
|
583
|
-
|
|
584
|
-
const currentFrameOffset = originalFC.getCurrentFrameOffset()
|
|
585
|
-
elementBounds = elementBounds.offset(currentFrameOffset.getX(), currentFrameOffset.getY())
|
|
586
|
-
|
|
587
|
-
const viewportBounds = await this._getViewportScrollBounds()
|
|
588
|
-
|
|
589
|
-
this._logger.verbose(`viewportBounds: ${viewportBounds} ; elementBounds: ${elementBounds}`)
|
|
590
|
-
|
|
591
|
-
if (!viewportBounds.contains(elementBounds)) {
|
|
592
|
-
await this._ensureFrameVisible()
|
|
593
|
-
|
|
594
|
-
const rect = await eyesWebElement.getRect()
|
|
595
|
-
const elementLocation = new Location(rect)
|
|
596
|
-
|
|
597
|
-
let scrollRootElement
|
|
598
|
-
if (
|
|
599
|
-
originalFC.size() > 0 &&
|
|
600
|
-
!(await EyesWebElement.equals(element, originalFC.peek().getReference()))
|
|
601
|
-
) {
|
|
602
|
-
await switchTo.frames(originalFC)
|
|
603
|
-
scrollRootElement = await this._driver.findElement('html')
|
|
604
|
-
} else {
|
|
605
|
-
scrollRootElement = this._scrollRootElement
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
const positionProvider = this._getElementPositionProvider(scrollRootElement)
|
|
609
|
-
await positionProvider.setPosition(elementLocation)
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* @private
|
|
615
|
-
* @return {Promise<Region>}
|
|
616
|
-
*/
|
|
617
|
-
async _getViewportScrollBounds() {
|
|
618
|
-
if (!this.getScrollToRegion()) {
|
|
619
|
-
this._logger.log('WARNING: no region visibility strategy! returning an empty region!')
|
|
620
|
-
return Region.EMPTY
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
const originalFrameChain = this._driver.getFrameChain().clone()
|
|
624
|
-
const switchTo = this._driver.switchTo()
|
|
625
|
-
await switchTo.frames(this._originalFC)
|
|
626
|
-
const spp = new ScrollPositionProvider(this._logger, this._jsExecutor, this._scrollRootElement)
|
|
627
|
-
let location = null
|
|
628
|
-
try {
|
|
629
|
-
location = await spp.getCurrentPosition()
|
|
630
|
-
} catch (err) {
|
|
631
|
-
this._logger.log(`WARNING: ${err}`)
|
|
632
|
-
this._logger.log('Assuming position is 0,0')
|
|
633
|
-
location = new Location(Location.ZERO)
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
const size = await this.getViewportSize()
|
|
637
|
-
const viewportBounds = new Region(location, size)
|
|
638
|
-
await switchTo.frames(originalFrameChain)
|
|
639
|
-
return viewportBounds
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* @private
|
|
644
|
-
* @return {Promise<MatchResult>}
|
|
645
|
-
*/
|
|
646
|
-
async _checkRegionByElement(name, checkSettings) {
|
|
647
|
-
const self = this
|
|
648
|
-
|
|
649
|
-
/**
|
|
650
|
-
* @private
|
|
651
|
-
* @type {RegionProvider}
|
|
652
|
-
*/
|
|
653
|
-
const RegionProviderImpl = class RegionProviderImpl extends RegionProvider {
|
|
654
|
-
// noinspection JSUnusedGlobalSymbols
|
|
655
|
-
/** @inheritDoc */
|
|
656
|
-
async getRegion() {
|
|
657
|
-
const rect = await self._targetElement.getRect()
|
|
658
|
-
// noinspection JSSuspiciousNameCombination
|
|
659
|
-
const region = new Region(
|
|
660
|
-
Math.ceil(rect.x),
|
|
661
|
-
Math.ceil(rect.y),
|
|
662
|
-
rect.width,
|
|
663
|
-
rect.height,
|
|
664
|
-
CoordinatesType.CONTEXT_RELATIVE,
|
|
665
|
-
)
|
|
666
|
-
self._imageLocation = region.getLocation()
|
|
667
|
-
return region
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
const source = await this._driver.getCurrentUrl()
|
|
672
|
-
const result = await this.checkWindowBase(
|
|
673
|
-
new RegionProviderImpl(),
|
|
674
|
-
name,
|
|
675
|
-
false,
|
|
676
|
-
checkSettings,
|
|
677
|
-
source,
|
|
678
|
-
)
|
|
679
|
-
this._logger.verbose('Done! trying to scroll back to original position...')
|
|
680
|
-
return result
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* @private
|
|
685
|
-
* @return {Promise<MatchResult>}
|
|
686
|
-
*/
|
|
687
|
-
async _checkElement(eyesElement = this._targetElement, name, checkSettings) {
|
|
688
|
-
this._regionToCheck = null
|
|
689
|
-
const scrollRootElement = await this.getCurrentFrameScrollRootElement()
|
|
690
|
-
const positionProvider = this._createPositionProvider(scrollRootElement)
|
|
691
|
-
const originalPositionMemento = await positionProvider.getState()
|
|
692
|
-
|
|
693
|
-
await this._ensureElementVisible(this._targetElement)
|
|
694
|
-
|
|
695
|
-
let result
|
|
696
|
-
let originalOverflow
|
|
697
|
-
const rect = await eyesElement.getRect()
|
|
698
|
-
|
|
699
|
-
try {
|
|
700
|
-
this._checkFrameOrElement = true
|
|
701
|
-
|
|
702
|
-
const displayStyle = await eyesElement.getComputedStyle('display')
|
|
703
|
-
|
|
704
|
-
if (this._configuration.getHideScrollbars()) {
|
|
705
|
-
originalOverflow = await eyesElement.getOverflow()
|
|
706
|
-
await eyesElement.setOverflow('hidden')
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
const sizeAndBorders = await eyesElement.getSizeAndBorders()
|
|
710
|
-
|
|
711
|
-
if (
|
|
712
|
-
displayStyle !== 'inline' &&
|
|
713
|
-
sizeAndBorders.height <= this._effectiveViewport.getHeight() &&
|
|
714
|
-
sizeAndBorders.width <= this._effectiveViewport.getWidth()
|
|
715
|
-
) {
|
|
716
|
-
this._elementPositionProvider = new ElementPositionProvider(
|
|
717
|
-
this._logger,
|
|
718
|
-
this._driver,
|
|
719
|
-
eyesElement,
|
|
720
|
-
)
|
|
721
|
-
} else {
|
|
722
|
-
this._elementPositionProvider = null
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
const elementRegion = new Region(
|
|
726
|
-
rect.x + sizeAndBorders.left,
|
|
727
|
-
rect.y + sizeAndBorders.top,
|
|
728
|
-
sizeAndBorders.width,
|
|
729
|
-
sizeAndBorders.height,
|
|
730
|
-
CoordinatesType.SCREENSHOT_AS_IS,
|
|
731
|
-
)
|
|
732
|
-
|
|
733
|
-
this._logger.verbose('Element region:', elementRegion)
|
|
734
|
-
|
|
735
|
-
this._regionToCheck = elementRegion
|
|
736
|
-
|
|
737
|
-
if (!this._effectiveViewport.isSizeEmpty()) {
|
|
738
|
-
this._regionToCheck.intersect(this._effectiveViewport)
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
this._imageLocation = this._regionToCheck.getLocation()
|
|
742
|
-
const source = await this._driver.getCurrentUrl()
|
|
743
|
-
result = await this.checkWindowBase(
|
|
744
|
-
new NullRegionProvider(),
|
|
745
|
-
name,
|
|
746
|
-
false,
|
|
747
|
-
checkSettings,
|
|
748
|
-
source,
|
|
749
|
-
)
|
|
750
|
-
} finally {
|
|
751
|
-
if (originalOverflow) {
|
|
752
|
-
await eyesElement.setOverflow(originalOverflow)
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
this._checkFrameOrElement = false
|
|
756
|
-
|
|
757
|
-
await positionProvider.restoreState(originalPositionMemento)
|
|
758
|
-
this._regionToCheck = null
|
|
759
|
-
this._elementPositionProvider = null
|
|
760
|
-
this._imageLocation = null
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
return result
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
/**
|
|
767
|
-
* Updates the state of scaling related parameters.
|
|
768
|
-
*
|
|
769
|
-
* @protected
|
|
770
|
-
* @return {Promise<ScaleProviderFactory>}
|
|
771
|
-
*/
|
|
772
|
-
async _updateScalingParams() {
|
|
773
|
-
// Update the scaling params only if we haven't done so yet, and the user hasn't set anything else manually.
|
|
774
|
-
if (
|
|
775
|
-
this._devicePixelRatio === Eyes.UNKNOWN_DEVICE_PIXEL_RATIO &&
|
|
776
|
-
this._scaleProviderHandler.get() instanceof NullScaleProvider
|
|
777
|
-
) {
|
|
778
|
-
let factory
|
|
779
|
-
this._logger.verbose('Trying to extract device pixel ratio...')
|
|
780
|
-
|
|
781
|
-
try {
|
|
782
|
-
this._devicePixelRatio = await EyesTestcafeUtils.getDevicePixelRatio(this._jsExecutor)
|
|
783
|
-
} catch (err) {
|
|
784
|
-
this._logger.verbose('Failed to extract device pixel ratio! Using default.', err)
|
|
785
|
-
this._devicePixelRatio = Eyes.DEFAULT_DEVICE_PIXEL_RATIO
|
|
786
|
-
}
|
|
787
|
-
this._logger.verbose(`Device pixel ratio: ${this._devicePixelRatio}`)
|
|
788
|
-
|
|
789
|
-
this._logger.verbose('Setting scale provider...')
|
|
790
|
-
try {
|
|
791
|
-
factory = await this._getScaleProviderFactory()
|
|
792
|
-
} catch (err) {
|
|
793
|
-
this._logger.verbose('Failed to set ContextBasedScaleProvider.', err)
|
|
794
|
-
this._logger.verbose('Using FixedScaleProvider instead...')
|
|
795
|
-
factory = new FixedScaleProviderFactory(
|
|
796
|
-
1 / this._devicePixelRatio,
|
|
797
|
-
this._scaleProviderHandler,
|
|
798
|
-
)
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
this._logger.verbose('Done!')
|
|
802
|
-
return factory
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
// If we already have a scale provider set, we'll just use it, and pass a mock as provider handler.
|
|
806
|
-
const nullProvider = new SimplePropertyHandler()
|
|
807
|
-
return new ScaleProviderIdentityFactory(this._scaleProviderHandler.get(), nullProvider)
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
/**
|
|
811
|
-
* @private
|
|
812
|
-
* @return {Promise<ScaleProviderFactory>}
|
|
813
|
-
*/
|
|
814
|
-
async _getScaleProviderFactory() {
|
|
815
|
-
const element = await this._driver.findElement('html')
|
|
816
|
-
const entireSize = await EyesTestcafeUtils.getEntireElementSize(this._jsExecutor, element)
|
|
817
|
-
|
|
818
|
-
return new ContextBasedScaleProviderFactory(
|
|
819
|
-
this._logger,
|
|
820
|
-
entireSize,
|
|
821
|
-
this._viewportSizeHandler.get(),
|
|
822
|
-
this._devicePixelRatio,
|
|
823
|
-
false,
|
|
824
|
-
this._scaleProviderHandler,
|
|
825
|
-
)
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
/**
|
|
829
|
-
* @param {boolean} [throwEx]
|
|
830
|
-
* @return {Promise<TestResults>}
|
|
831
|
-
*/
|
|
832
|
-
async close(throwEx = true) {
|
|
833
|
-
const results = await Eyes.prototype.close.call(this, throwEx)
|
|
834
|
-
|
|
835
|
-
if (this._runner) {
|
|
836
|
-
this._runner._allTestResult.push(results)
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
return results
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
/**
|
|
843
|
-
* @inheritDoc
|
|
844
|
-
*/
|
|
845
|
-
async getViewportSize() {
|
|
846
|
-
let viewportSize = this._viewportSizeHandler.get()
|
|
847
|
-
if (!viewportSize) {
|
|
848
|
-
viewportSize = await this._driver.getDefaultContentViewportSize()
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
return viewportSize
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
/**
|
|
855
|
-
* @inheritDoc
|
|
856
|
-
*/
|
|
857
|
-
async tryCaptureDom() {
|
|
858
|
-
try {
|
|
859
|
-
this._logger.verbose('Getting window DOM...')
|
|
860
|
-
const captureDomScript = await getCaptureDomScript({
|
|
861
|
-
webDriver: this._driver,
|
|
862
|
-
logger: this._logger,
|
|
863
|
-
})
|
|
864
|
-
return await DomCapture.getFullWindowDom(
|
|
865
|
-
this._logger,
|
|
866
|
-
this._driver,
|
|
867
|
-
undefined,
|
|
868
|
-
undefined,
|
|
869
|
-
captureDomScript,
|
|
870
|
-
)
|
|
871
|
-
} catch (err) {
|
|
872
|
-
this._logger.log(`Error capturing DOM of the page: ${JSON.stringify(err)}`)
|
|
873
|
-
return ''
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
/**
|
|
878
|
-
* @private
|
|
879
|
-
* @return {Promise<FrameChain>}
|
|
880
|
-
*/
|
|
881
|
-
async _tryHideScrollbars() {
|
|
882
|
-
// if (await EyesTestcafeUtils.isMobileDevice(this._driver)) {
|
|
883
|
-
// return new FrameChain(this._logger);
|
|
884
|
-
// }
|
|
885
|
-
|
|
886
|
-
if (
|
|
887
|
-
this._configuration.getHideScrollbars() ||
|
|
888
|
-
(this._configuration.getStitchMode() === StitchMode.CSS && this._stitchContent)
|
|
889
|
-
) {
|
|
890
|
-
const originalFC = this._driver.getFrameChain().clone()
|
|
891
|
-
const fc = this._driver.getFrameChain().clone()
|
|
892
|
-
let frame = fc.peek()
|
|
893
|
-
|
|
894
|
-
if (fc.size() > 0) {
|
|
895
|
-
while (fc.size() > 0) {
|
|
896
|
-
this._logger.verbose(`fc.Count = ${fc.size()}`)
|
|
897
|
-
|
|
898
|
-
if (this._stitchContent || fc.size() !== originalFC.size()) {
|
|
899
|
-
if (frame === null) {
|
|
900
|
-
this._logger.verbose('hiding scrollbars of element (1)')
|
|
901
|
-
await EyesTestcafeUtils.setOverflow(this._driver, 'hidden', this._scrollRootElement)
|
|
902
|
-
} else {
|
|
903
|
-
await frame.hideScrollbars(this._driver)
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
await this._driver.switchTo().parentFrame()
|
|
908
|
-
fc.pop()
|
|
909
|
-
frame = fc.peek()
|
|
910
|
-
}
|
|
911
|
-
} else {
|
|
912
|
-
this._logger.verbose('hiding scrollbars of element (2)')
|
|
913
|
-
this._originalOverflow = await EyesTestcafeUtils.setOverflow(
|
|
914
|
-
this._driver,
|
|
915
|
-
'hidden',
|
|
916
|
-
this._scrollRootElement,
|
|
917
|
-
)
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
this._logger.verbose('switching back to original frame')
|
|
921
|
-
await this._driver.switchTo().frames(originalFC)
|
|
922
|
-
this._logger.verbose('done hiding scrollbars.')
|
|
923
|
-
return originalFC
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
return new FrameChain(this._logger)
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
/**
|
|
930
|
-
* @inheritDoc
|
|
931
|
-
*/
|
|
932
|
-
async getImageLocation() {
|
|
933
|
-
if (this._imageLocation) {
|
|
934
|
-
return this._imageLocation
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
return Location.ZERO
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
/**
|
|
941
|
-
* @private
|
|
942
|
-
* @param {FrameChain} frameChain
|
|
943
|
-
* @return {Promise}
|
|
944
|
-
*/
|
|
945
|
-
async _tryRestoreScrollbars(frameChain) {
|
|
946
|
-
// if (await EyesTestcafeUtils.isMobileDevice(this._driver)) {
|
|
947
|
-
// return;
|
|
948
|
-
// }
|
|
949
|
-
|
|
950
|
-
if (
|
|
951
|
-
this._configuration.getHideScrollbars() ||
|
|
952
|
-
(this._configuration.getStitchMode() === StitchMode.CSS && this._stitchContent)
|
|
953
|
-
) {
|
|
954
|
-
await this._driver.switchTo().frames(frameChain)
|
|
955
|
-
const originalFC = frameChain.clone()
|
|
956
|
-
const fc = frameChain.clone()
|
|
957
|
-
if (fc.size() > 0) {
|
|
958
|
-
while (fc.size() > 0) {
|
|
959
|
-
const frame = fc.pop()
|
|
960
|
-
await frame.returnToOriginalOverflow(this._driver)
|
|
961
|
-
await EyesTargetLocator.tryParentFrame(this._driver.getRemoteWebDriver().switchTo(), fc)
|
|
962
|
-
}
|
|
963
|
-
} else {
|
|
964
|
-
this._logger.verbose('returning overflow of element to its original value')
|
|
965
|
-
await EyesTestcafeUtils.setOverflow(
|
|
966
|
-
this._driver,
|
|
967
|
-
this._originalOverflow,
|
|
968
|
-
this._scrollRootElement,
|
|
969
|
-
)
|
|
970
|
-
}
|
|
971
|
-
await this._driver.switchTo().frames(originalFC)
|
|
972
|
-
this._logger.verbose('done restoring scrollbars.')
|
|
973
|
-
} else {
|
|
974
|
-
this._logger.verbose('no need to restore scrollbars.')
|
|
975
|
-
}
|
|
976
|
-
this._driver.getFrameChain().clear()
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
/*
|
|
980
|
-
/**
|
|
981
|
-
* @protected
|
|
982
|
-
* @return {Promise}
|
|
983
|
-
* /
|
|
984
|
-
_afterMatchWindow() {
|
|
985
|
-
if (this.hideScrollbars) {
|
|
986
|
-
try {
|
|
987
|
-
EyesTestcafeUtils.setOverflow(this.driver, this.originalOverflow);
|
|
988
|
-
} catch (EyesDriverOperationException e) {
|
|
989
|
-
// Bummer, but we'll continue with the screenshot anyway :)
|
|
990
|
-
logger.log("WARNING: Failed to revert overflow! Error: " + e.getMessage());
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
*/
|
|
995
|
-
|
|
996
|
-
/**
|
|
997
|
-
* Gets scroll root element.
|
|
998
|
-
*
|
|
999
|
-
* @override
|
|
1000
|
-
* @return {Promise<WebElement>} the scroll root element
|
|
1001
|
-
*/
|
|
1002
|
-
getScrollRootElement() {
|
|
1003
|
-
if (this._scrollRootElement == null) {
|
|
1004
|
-
this._scrollRootElement = this._driver.findElement('html')
|
|
1005
|
-
}
|
|
1006
|
-
return this._scrollRootElement
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
async _scanPage() {
|
|
1010
|
-
this._logger.verbose('scanPage started')
|
|
1011
|
-
await EyesJsBrowserUtils.scanPage(this._driver)
|
|
1012
|
-
this._logger.verbose('scanPage - done!')
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
/**
|
|
1016
|
-
* @private
|
|
1017
|
-
* @param {TestcafeCheckSettings} scrollRootElementContainer
|
|
1018
|
-
* @return {WebElement}
|
|
1019
|
-
*/
|
|
1020
|
-
_getScrollRootElementFromCheckSettings(scrollRootElementContainer) {
|
|
1021
|
-
// if (!EyesTestcafeUtils.isMobileDevice(driver)) {
|
|
1022
|
-
if (scrollRootElementContainer) {
|
|
1023
|
-
let scrollRootElement = scrollRootElementContainer.getScrollRootElement()
|
|
1024
|
-
|
|
1025
|
-
if (!scrollRootElement) {
|
|
1026
|
-
const scrollRootSelector = scrollRootElementContainer.getScrollRootSelector()
|
|
1027
|
-
if (scrollRootSelector) {
|
|
1028
|
-
scrollRootElement = this._driver.findElement(scrollRootSelector)
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
if (scrollRootElement) {
|
|
1033
|
-
return scrollRootElement
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
// }
|
|
1037
|
-
|
|
1038
|
-
return this._driver.findElement('html')
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
// noinspection JSUnusedGlobalSymbols
|
|
1042
|
-
/**
|
|
1043
|
-
* @param {EyesWebElement} scrollRootElement
|
|
1044
|
-
* @return {PositionProvider}
|
|
1045
|
-
*/
|
|
1046
|
-
_getElementPositionProvider(scrollRootElement) {
|
|
1047
|
-
let positionProvider = scrollRootElement.getPositionProvider()
|
|
1048
|
-
if (positionProvider == null) {
|
|
1049
|
-
positionProvider = this._createPositionProvider(scrollRootElement)
|
|
1050
|
-
scrollRootElement.setPositionProvider(positionProvider)
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
this._logger.verbose('position provider:', positionProvider)
|
|
1054
|
-
this._currentFramePositionProvider = positionProvider
|
|
1055
|
-
return positionProvider
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
// noinspection JSUnusedGlobalSymbols
|
|
1059
|
-
/**
|
|
1060
|
-
* @protected
|
|
1061
|
-
* @inheritDoc
|
|
1062
|
-
*/
|
|
1063
|
-
async getScreenshot() {
|
|
1064
|
-
this._logger.verbose('enter()')
|
|
1065
|
-
|
|
1066
|
-
const scaleProviderFactory = await this._updateScalingParams()
|
|
1067
|
-
|
|
1068
|
-
const originalFrameChain = this._driver.getFrameChain().clone()
|
|
1069
|
-
const switchTo = this._driver.switchTo()
|
|
1070
|
-
await switchTo.frames(this._originalFC)
|
|
1071
|
-
|
|
1072
|
-
const positionProvider = this.getPositionProvider()
|
|
1073
|
-
let originalPosition = null
|
|
1074
|
-
if (positionProvider) {
|
|
1075
|
-
// !EyesTestcafeUtils.isMobileDevice(this.driver)
|
|
1076
|
-
originalPosition = await positionProvider.getState()
|
|
1077
|
-
}
|
|
1078
|
-
await switchTo.frames(originalFrameChain)
|
|
1079
|
-
|
|
1080
|
-
const scrollRootElement = await this.getCurrentFrameScrollRootElement()
|
|
1081
|
-
const originProvider = new ScrollPositionProvider(
|
|
1082
|
-
this._logger,
|
|
1083
|
-
this._jsExecutor,
|
|
1084
|
-
scrollRootElement,
|
|
1085
|
-
)
|
|
1086
|
-
|
|
1087
|
-
const algo = new FullPageCaptureAlgorithm(
|
|
1088
|
-
this._logger,
|
|
1089
|
-
this._regionPositionCompensation,
|
|
1090
|
-
this._configuration.getWaitBeforeScreenshots(),
|
|
1091
|
-
this._debugScreenshotsProvider,
|
|
1092
|
-
this._screenshotFactory,
|
|
1093
|
-
originProvider,
|
|
1094
|
-
scaleProviderFactory,
|
|
1095
|
-
this._cutProviderHandler.get(),
|
|
1096
|
-
this._configuration.getStitchOverlap(),
|
|
1097
|
-
this._imageProvider,
|
|
1098
|
-
true,
|
|
1099
|
-
)
|
|
1100
|
-
|
|
1101
|
-
// TODO
|
|
1102
|
-
// maybe we can just do later active elemnt.focus() ? check
|
|
1103
|
-
// I think maybe frame stuff changes the active element
|
|
1104
|
-
// so that is why I saved the xpath etc..
|
|
1105
|
-
let activeElementXpath = null
|
|
1106
|
-
if (this._configuration.getHideCaret()) {
|
|
1107
|
-
try {
|
|
1108
|
-
activeElementXpath = await this._driver.executeScript(
|
|
1109
|
-
`document.activeElement && document.activeElement.blur();
|
|
1110
|
-
return (${GEN_XPATH_SCRIPT})(document.activeElement);`,
|
|
1111
|
-
)
|
|
1112
|
-
} catch (err) {
|
|
1113
|
-
this._logger.verbose(`WARNING: Cannot hide caret! ${err}`)
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
let result
|
|
1118
|
-
if (this._checkFrameOrElement) {
|
|
1119
|
-
this._logger.verbose('Check frame/element requested')
|
|
1120
|
-
|
|
1121
|
-
// if (!EyesTestcafeUtils.isMobileDevice(this.driver)) {
|
|
1122
|
-
await switchTo.frames(originalFrameChain)
|
|
1123
|
-
// }
|
|
1124
|
-
|
|
1125
|
-
let entireFrameOrElement
|
|
1126
|
-
if (!this._elementPositionProvider) {
|
|
1127
|
-
const scrollRootElement2 = await this._driver.findElement('html')
|
|
1128
|
-
const elemPositionProvider = this._getElementPositionProvider(scrollRootElement2)
|
|
1129
|
-
await this._markElementForLayoutRCA(elemPositionProvider)
|
|
1130
|
-
entireFrameOrElement = await algo.getStitchedRegion(
|
|
1131
|
-
this._regionToCheck,
|
|
1132
|
-
null,
|
|
1133
|
-
elemPositionProvider,
|
|
1134
|
-
)
|
|
1135
|
-
} else {
|
|
1136
|
-
await this._markElementForLayoutRCA(this._elementPositionProvider)
|
|
1137
|
-
entireFrameOrElement = await algo.getStitchedRegion(
|
|
1138
|
-
this._regionToCheck,
|
|
1139
|
-
null,
|
|
1140
|
-
this._elementPositionProvider,
|
|
1141
|
-
)
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
this._logger.verbose('Building screenshot object...')
|
|
1145
|
-
const size = new RectangleSize(
|
|
1146
|
-
entireFrameOrElement.getWidth(),
|
|
1147
|
-
entireFrameOrElement.getHeight(),
|
|
1148
|
-
)
|
|
1149
|
-
result = await EyesWebDriverScreenshot.fromFrameSize(
|
|
1150
|
-
this._logger,
|
|
1151
|
-
this._driver,
|
|
1152
|
-
entireFrameOrElement,
|
|
1153
|
-
size,
|
|
1154
|
-
)
|
|
1155
|
-
} else if (this._configuration.getForceFullPageScreenshot() || this._stitchContent) {
|
|
1156
|
-
this._logger.verbose('Full page screenshot requested.')
|
|
1157
|
-
|
|
1158
|
-
// Save the current frame path.
|
|
1159
|
-
const originalFramePosition =
|
|
1160
|
-
originalFrameChain.size() > 0
|
|
1161
|
-
? originalFrameChain.getDefaultContentScrollPosition()
|
|
1162
|
-
: new Location(Location.ZERO)
|
|
1163
|
-
|
|
1164
|
-
await switchTo.frames(this._originalFC)
|
|
1165
|
-
const eyesScrollRootElement = new EyesWebElement(
|
|
1166
|
-
this._logger,
|
|
1167
|
-
this._driver,
|
|
1168
|
-
this._scrollRootElement,
|
|
1169
|
-
)
|
|
1170
|
-
|
|
1171
|
-
const rect = await eyesScrollRootElement.getRect()
|
|
1172
|
-
const sizeAndBorders = await eyesScrollRootElement.getSizeAndBorders()
|
|
1173
|
-
const region = new Region(
|
|
1174
|
-
rect.x + sizeAndBorders.left,
|
|
1175
|
-
rect.y + sizeAndBorders.top,
|
|
1176
|
-
sizeAndBorders.width,
|
|
1177
|
-
sizeAndBorders.height,
|
|
1178
|
-
)
|
|
1179
|
-
|
|
1180
|
-
await this._markElementForLayoutRCA(null)
|
|
1181
|
-
|
|
1182
|
-
const fullPageImage = await algo.getStitchedRegion(
|
|
1183
|
-
region,
|
|
1184
|
-
null,
|
|
1185
|
-
this._positionProviderHandler.get(),
|
|
1186
|
-
)
|
|
1187
|
-
|
|
1188
|
-
await switchTo.frames(originalFrameChain)
|
|
1189
|
-
|
|
1190
|
-
result = await EyesWebDriverScreenshot.fromScreenshotType(
|
|
1191
|
-
this._logger,
|
|
1192
|
-
this._driver,
|
|
1193
|
-
fullPageImage,
|
|
1194
|
-
null,
|
|
1195
|
-
originalFramePosition,
|
|
1196
|
-
)
|
|
1197
|
-
} else {
|
|
1198
|
-
await this._ensureElementVisible(this._targetElement)
|
|
1199
|
-
|
|
1200
|
-
this._logger.verbose('Screenshot requested...')
|
|
1201
|
-
let screenshotImage = await this._imageProvider.getImage()
|
|
1202
|
-
await this._debugScreenshotsProvider.save(screenshotImage, 'original')
|
|
1203
|
-
|
|
1204
|
-
const scaleProvider = scaleProviderFactory.getScaleProvider(screenshotImage.getWidth())
|
|
1205
|
-
if (scaleProvider.getScaleRatio() !== 1) {
|
|
1206
|
-
this._logger.verbose('scaling...')
|
|
1207
|
-
screenshotImage = await screenshotImage.scale(scaleProvider.getScaleRatio())
|
|
1208
|
-
await this._debugScreenshotsProvider.save(screenshotImage, 'scaled')
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
const cutProvider = this._cutProviderHandler.get()
|
|
1212
|
-
if (!(cutProvider instanceof NullCutProvider)) {
|
|
1213
|
-
this._logger.verbose('cutting...')
|
|
1214
|
-
screenshotImage = await cutProvider.cut(screenshotImage)
|
|
1215
|
-
await this._debugScreenshotsProvider.save(screenshotImage, 'cut')
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
this._logger.verbose('Creating screenshot object...')
|
|
1219
|
-
result = await EyesWebDriverScreenshot.fromScreenshotType(
|
|
1220
|
-
this._logger,
|
|
1221
|
-
this._driver,
|
|
1222
|
-
screenshotImage,
|
|
1223
|
-
)
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
if (this._configuration.getHideCaret() && activeElementXpath != null) {
|
|
1227
|
-
try {
|
|
1228
|
-
await this._driver.executeScript(
|
|
1229
|
-
`const activeElement = (${ELEMENTS_BY_XPATH_SCRIPT})(arguments[0]); activeElement[0].focus();`,
|
|
1230
|
-
activeElementXpath,
|
|
1231
|
-
)
|
|
1232
|
-
} catch (err) {
|
|
1233
|
-
this._logger.verbose(
|
|
1234
|
-
`WARNING: Could not return focus to active element! ${JSON.stringify(err)}`,
|
|
1235
|
-
)
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
await switchTo.frames(this._originalFC)
|
|
1240
|
-
if (positionProvider) {
|
|
1241
|
-
await positionProvider.restoreState(originalPosition)
|
|
1242
|
-
}
|
|
1243
|
-
await switchTo.frames(originalFrameChain)
|
|
1244
|
-
|
|
1245
|
-
this._logger.verbose('Done!')
|
|
1246
|
-
return result
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
/**
|
|
1250
|
-
* @private
|
|
1251
|
-
* @param {PositionProvider} elemPositionProvider
|
|
1252
|
-
*/
|
|
1253
|
-
async _markElementForLayoutRCA(elemPositionProvider) {
|
|
1254
|
-
const positionProvider = elemPositionProvider || this.getPositionProvider()
|
|
1255
|
-
const scrolledElement = await positionProvider.getScrolledElement()
|
|
1256
|
-
|
|
1257
|
-
if (scrolledElement) {
|
|
1258
|
-
try {
|
|
1259
|
-
await this._jsExecutor.executeScript(
|
|
1260
|
-
"arguments[0].setAttribute('data-applitools-scroll','true');",
|
|
1261
|
-
scrolledElement,
|
|
1262
|
-
)
|
|
1263
|
-
} catch (err) {
|
|
1264
|
-
this._logger.verbose("Can't set data attribute for element", err)
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
exports.EyesTestCafe = EyesTestCafe
|