@applitools/screenshoter 3.3.9 → 3.3.12
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 +15 -0
- package/package.json +10 -9
- package/src/calculate-screenshot-region.js +17 -0
- package/src/image.js +1 -0
- package/src/scroller.js +4 -1
- package/src/take-screenshot.js +6 -6
- package/src/take-stitched-screenshot.js +24 -9
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
## Unreleased
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
## 3.3.12 - 2022/3/14
|
|
8
|
+
|
|
9
|
+
- update snippets
|
|
10
|
+
- updated to @applitools/snippets@2.2.2 (from 2.2.1)
|
|
11
|
+
|
|
12
|
+
## 3.3.11 - 2022/3/12
|
|
13
|
+
|
|
14
|
+
- disable scroll to 0,0 before taking stitched screenshot
|
|
15
|
+
- add better error handling when taking a stitched screenshot
|
|
16
|
+
- updated to @applitools/snippets@2.2.1 (from 2.2.0)
|
|
17
|
+
|
|
18
|
+
## 3.3.10 - 2022/2/16
|
|
19
|
+
|
|
20
|
+
- do not perform initial scrolling in `takeStitchedScreenshot`
|
|
21
|
+
|
|
7
22
|
## 3.3.9 - 2022/2/16
|
|
8
23
|
|
|
9
24
|
- remove prevention of translation in `mixed` scrolling mode
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applitools/screenshoter",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.12",
|
|
4
4
|
"description": "Applitools universal screenshoter for web and native applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"applitools",
|
|
@@ -33,15 +33,15 @@
|
|
|
33
33
|
"scripts": {
|
|
34
34
|
"lint": "eslint . --ext .js",
|
|
35
35
|
"test": "yarn test:it && yarn test:e2e",
|
|
36
|
+
"test:unit": "mocha ./test/unit/*.spec.js --no-timeouts",
|
|
36
37
|
"test:it": "mocha ./test/it/*.spec.js --no-timeouts",
|
|
37
38
|
"test:e2e": "mocha ./test/e2e/**/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-1}",
|
|
38
39
|
"test:e2e:web": "mocha ./test/e2e/web/*.spec.js --no-timeouts -r @applitools/test-utils/mocha-hooks/docker --parallel --jobs ${MOCHA_JOBS:-15}",
|
|
39
|
-
"test:e2e:android": "mocha ./test/e2e/android*/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-
|
|
40
|
-
"test:e2e:ios": "mocha ./test/e2e/ios*/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-
|
|
41
|
-
"setup": "yarn docker:setup && yarn android:setup && yarn ios:setup && yarn appium:setup",
|
|
40
|
+
"test:e2e:android": "APPLITOOLS_TEST_REMOTE=sauce mocha ./test/e2e/android*/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-2}",
|
|
41
|
+
"test:e2e:ios": "APPLITOOLS_TEST_REMOTE=sauce mocha ./test/e2e/ios*/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-2}",
|
|
42
42
|
"setup:web": "yarn docker:setup",
|
|
43
|
-
"setup:android": "
|
|
44
|
-
"setup:ios": "
|
|
43
|
+
"setup:android": "echo \"setup:android - do nothing\"",
|
|
44
|
+
"setup:ios": "echo \"setup:ios - do nothing\"",
|
|
45
45
|
"android:setup": "node ./scripts/android-emulator.js",
|
|
46
46
|
"android:shutdown": "adb -s emulator-5555 emu kill || true && adb -s emulator-5557 emu kill || true",
|
|
47
47
|
"docker:setup": "node ../scripts/scripts/generate-docker-compose-config.js && docker-compose up -d",
|
|
@@ -60,15 +60,16 @@
|
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@applitools/snippets": "2.
|
|
63
|
+
"@applitools/snippets": "2.2.2",
|
|
64
64
|
"@applitools/utils": "1.2.13",
|
|
65
65
|
"png-async": "0.9.4"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@applitools/driver": "1.4
|
|
68
|
+
"@applitools/driver": "1.5.4",
|
|
69
|
+
"@applitools/scripts": "1.1.0",
|
|
69
70
|
"@applitools/sdk-release-kit": "0.13.11",
|
|
70
71
|
"@applitools/spec-driver-webdriverio": "1.2.7",
|
|
71
|
-
"@applitools/test-utils": "1.
|
|
72
|
+
"@applitools/test-utils": "1.1.5",
|
|
72
73
|
"appium": "^1.22.2",
|
|
73
74
|
"chromedriver": "^95.0.0",
|
|
74
75
|
"eslint": "^7.9.0",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const utils = require('@applitools/utils')
|
|
2
|
+
|
|
3
|
+
function calculateScreenshotRegion({cropRegion, stitchedImage, preMoveOffset, postMoveOffset} = {}) {
|
|
4
|
+
cropRegion = !!cropRegion ? cropRegion : {x: 0, y: 0}
|
|
5
|
+
const screenshotRegion = utils.geometry.region(cropRegion, stitchedImage.size)
|
|
6
|
+
|
|
7
|
+
if (JSON.stringify(preMoveOffset) === JSON.stringify(postMoveOffset)) return screenshotRegion
|
|
8
|
+
const moveOffset = utils.geometry.offsetNegative(postMoveOffset, preMoveOffset)
|
|
9
|
+
const compensatedScreenshotRegion = utils.geometry.offset(screenshotRegion, moveOffset)
|
|
10
|
+
|
|
11
|
+
if (preMoveOffset.y === postMoveOffset.y && preMoveOffset.x !== postMoveOffset.x)
|
|
12
|
+
compensatedScreenshotRegion.x = preMoveOffset.x
|
|
13
|
+
|
|
14
|
+
return compensatedScreenshotRegion
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = calculateScreenshotRegion
|
package/src/image.js
CHANGED
|
@@ -84,6 +84,7 @@ function makeImage(data) {
|
|
|
84
84
|
? utils.geometry.intersect(transforms.crop, utils.geometry.offset(region, transforms.crop))
|
|
85
85
|
: utils.geometry.intersect({x: 0, y: 0, ...size}, region)
|
|
86
86
|
transforms.crop = region
|
|
87
|
+
size = utils.geometry.size(transforms.crop)
|
|
87
88
|
return this
|
|
88
89
|
},
|
|
89
90
|
copy(srcImage, offset) {
|
package/src/scroller.js
CHANGED
|
@@ -25,12 +25,14 @@ function makeScroller({logger, element, scrollingMode = 'mixed'}) {
|
|
|
25
25
|
if (scrollingMode === 'scroll') return scrollTo(offset, element)
|
|
26
26
|
if (scrollingMode === 'css') return translateTo(offset, element)
|
|
27
27
|
if (scrollingMode === 'mixed') return shiftTo(offset, element)
|
|
28
|
+
if (scrollingMode === 'mixed+') return shiftTo(offset, element, {formerlyCssScrollingMode: true})
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
async function getInnerOffset(element = defaultElement) {
|
|
31
32
|
if (scrollingMode === 'scroll') return getScrollOffset(element)
|
|
32
33
|
if (scrollingMode === 'css') return getTranslateOffset(element)
|
|
33
34
|
if (scrollingMode === 'mixed') return getShiftOffset(element)
|
|
35
|
+
if (scrollingMode === 'mixed+') return getShiftOffset(element)
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
async function getContentSize() {
|
|
@@ -101,9 +103,10 @@ function makeScroller({logger, element, scrollingMode = 'mixed'}) {
|
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
async function shiftTo(offset, element = defaultElement) {
|
|
106
|
+
async function shiftTo(offset, element = defaultElement, {formerlyCssScrollingMode} = {}) {
|
|
105
107
|
try {
|
|
106
108
|
const scrollOffset = await element.scrollTo(offset)
|
|
109
|
+
if (utils.geometry.equals(scrollOffset, offset) && !formerlyCssScrollingMode) return scrollOffset
|
|
107
110
|
|
|
108
111
|
// there is a "bug" in iOS that will not move a root element if it already scrolled, so it should be translated all the way
|
|
109
112
|
if (element.driver.isIOS && (await element.isRoot())) {
|
package/src/take-screenshot.js
CHANGED
|
@@ -50,7 +50,10 @@ async function takeScreenshot({
|
|
|
50
50
|
|
|
51
51
|
const target = await getTarget({window, context, region, fully, scrollingMode, logger})
|
|
52
52
|
|
|
53
|
-
if (
|
|
53
|
+
if (target.scroller) {
|
|
54
|
+
await target.scroller.preserveState()
|
|
55
|
+
if (driver.isWeb && hideScrollbars) await target.scroller.hideScrollbars()
|
|
56
|
+
}
|
|
54
57
|
|
|
55
58
|
try {
|
|
56
59
|
if (!window) await scrollIntoViewport({...target, logger})
|
|
@@ -63,10 +66,6 @@ async function takeScreenshot({
|
|
|
63
66
|
screenshot.image.scale(driver.viewportScale)
|
|
64
67
|
|
|
65
68
|
if (hooks && hooks.afterScreenshot) {
|
|
66
|
-
// imitate image-like state for the hook
|
|
67
|
-
if (window && fully && target.scroller) {
|
|
68
|
-
await target.scroller.moveTo({x: 0, y: 0}, await driver.mainContext.getScrollingElement())
|
|
69
|
-
}
|
|
70
69
|
await hooks.afterScreenshot({driver, scroller: target.scroller, screenshot})
|
|
71
70
|
}
|
|
72
71
|
|
|
@@ -74,6 +73,7 @@ async function takeScreenshot({
|
|
|
74
73
|
} finally {
|
|
75
74
|
if (target.scroller) {
|
|
76
75
|
await target.scroller.restoreScrollbars()
|
|
76
|
+
await target.scroller.restoreState()
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// if there was active element and we have blurred it, then restore focus
|
|
@@ -123,7 +123,7 @@ async function getTarget({window, context, region, fully, scrollingMode, logger}
|
|
|
123
123
|
const region = isScrollable ? null : await element.getRegion()
|
|
124
124
|
const scrollingElement = isScrollable ? element : await elementContext.getScrollingElement()
|
|
125
125
|
// css stitching could be applied only to root element of its context
|
|
126
|
-
scrollingMode = scrollingMode === 'css' && !(await scrollingElement.isRoot()) ? 'mixed' : scrollingMode
|
|
126
|
+
scrollingMode = scrollingMode === 'css' && !(await scrollingElement.isRoot()) ? 'mixed+' : scrollingMode
|
|
127
127
|
return {
|
|
128
128
|
context: elementContext,
|
|
129
129
|
region,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const utils = require('@applitools/utils')
|
|
2
2
|
const makeImage = require('./image')
|
|
3
3
|
const makeTakeViewportScreenshot = require('./take-viewport-screenshot')
|
|
4
|
+
const calculateScreenshotRegion = require('./calculate-screenshot-region')
|
|
4
5
|
|
|
5
6
|
async function takeStitchedScreenshot({
|
|
6
7
|
logger,
|
|
@@ -21,32 +22,45 @@ async function takeStitchedScreenshot({
|
|
|
21
22
|
const scrollerState = await scroller.preserveState()
|
|
22
23
|
|
|
23
24
|
const initialOffset = region ? utils.geometry.location(region) : {x: 0, y: 0}
|
|
24
|
-
const
|
|
25
|
-
const
|
|
25
|
+
const preMoveOffset = await scroller.getInnerOffset()
|
|
26
|
+
const postMoveOffset = await scroller.moveTo(initialOffset)
|
|
27
|
+
const expectedRemainingOffset = utils.geometry.offsetNegative(initialOffset, postMoveOffset)
|
|
26
28
|
|
|
27
29
|
await utils.general.sleep(wait)
|
|
28
30
|
|
|
29
31
|
const contentSize = await scroller.getContentSize()
|
|
30
32
|
|
|
33
|
+
logger.verbose(
|
|
34
|
+
'preMoveOffset',
|
|
35
|
+
preMoveOffset,
|
|
36
|
+
'initialOffset',
|
|
37
|
+
initialOffset,
|
|
38
|
+
'postMoveOffset',
|
|
39
|
+
postMoveOffset,
|
|
40
|
+
'context.isMain',
|
|
41
|
+
context.isMain,
|
|
42
|
+
)
|
|
43
|
+
|
|
31
44
|
logger.verbose('Getting initial image...')
|
|
32
45
|
let image = await takeViewportScreenshot({name: 'initial', withStatusBar})
|
|
33
46
|
const firstImage = framed ? makeImage(image) : null
|
|
34
47
|
|
|
35
48
|
const scrollerRegion = await scroller.getClientRegion()
|
|
36
49
|
const targetRegion = region
|
|
37
|
-
? utils.geometry.intersect(utils.geometry.region(
|
|
50
|
+
? utils.geometry.intersect(utils.geometry.region(postMoveOffset, scrollerRegion), region)
|
|
38
51
|
: scrollerRegion
|
|
39
52
|
|
|
40
53
|
// TODO the solution should not check driver specifics,
|
|
41
54
|
// in this case target region coordinate should be already related to the scrolling element of the context
|
|
42
55
|
let cropRegion = driver.isNative ? targetRegion : await driver.getRegionInViewport(context, targetRegion)
|
|
56
|
+
if (utils.geometry.isEmpty(cropRegion)) throw new Error('Screenshot region is out of viewport')
|
|
43
57
|
|
|
44
|
-
logger.verbose('cropping...')
|
|
58
|
+
logger.verbose('cropping... cropRegion is', cropRegion)
|
|
45
59
|
image.crop(withStatusBar ? utils.geometry.offset(cropRegion, {x: 0, y: driver.statusBarHeight}) : cropRegion)
|
|
46
60
|
await image.debug({...debug, name: 'initial', suffix: 'region'})
|
|
47
61
|
|
|
48
62
|
const contentRegion = utils.geometry.region({x: 0, y: 0}, contentSize)
|
|
49
|
-
logger.verbose(
|
|
63
|
+
logger.verbose('Scroller size:', contentRegion)
|
|
50
64
|
|
|
51
65
|
if (region) region = utils.geometry.intersect(region, contentRegion)
|
|
52
66
|
else region = contentRegion
|
|
@@ -73,7 +87,7 @@ async function takeStitchedScreenshot({
|
|
|
73
87
|
const compensateOffset = {x: 0, y: initialRegion.y !== partRegion.y ? overlap.top : 0}
|
|
74
88
|
const requiredOffset = utils.geometry.offsetNegative(utils.geometry.location(partRegion), compensateOffset)
|
|
75
89
|
|
|
76
|
-
logger.verbose(
|
|
90
|
+
logger.verbose('Move to', requiredOffset)
|
|
77
91
|
let actualOffset = await scroller.moveTo(requiredOffset)
|
|
78
92
|
// actual scroll position after scrolling might be not equal to required position due to
|
|
79
93
|
// scrollable region shift during scrolling so actual scroll position should be corrected
|
|
@@ -94,7 +108,8 @@ async function takeStitchedScreenshot({
|
|
|
94
108
|
width: partRegion.width,
|
|
95
109
|
height: partRegion.height,
|
|
96
110
|
}
|
|
97
|
-
logger.verbose(
|
|
111
|
+
logger.verbose('Actual offset is', actualOffset, ', remaining offset is', remainingOffset)
|
|
112
|
+
logger.verbose('cropPartRegion is', cropPartRegion)
|
|
98
113
|
|
|
99
114
|
await utils.general.sleep(wait)
|
|
100
115
|
|
|
@@ -126,12 +141,12 @@ async function takeStitchedScreenshot({
|
|
|
126
141
|
|
|
127
142
|
return {
|
|
128
143
|
image: stitchedImage,
|
|
129
|
-
region:
|
|
144
|
+
region: calculateScreenshotRegion({stitchedImage, preMoveOffset, postMoveOffset}),
|
|
130
145
|
}
|
|
131
146
|
} else {
|
|
132
147
|
return {
|
|
133
148
|
image: stitchedImage,
|
|
134
|
-
region:
|
|
149
|
+
region: calculateScreenshotRegion({cropRegion, stitchedImage, preMoveOffset, postMoveOffset}),
|
|
135
150
|
}
|
|
136
151
|
}
|
|
137
152
|
}
|