@applitools/screenshoter 3.3.0 → 3.3.4
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/.bongo/dry-run/package-lock.json +19 -19
- package/.bongo/dry-run.tgz +0 -0
- package/CHANGELOG.md +21 -0
- package/package.json +5 -5
- package/src/find-image-pattern.js +2 -1
- package/src/image.js +15 -12
- package/src/scroll-into-viewport.js +16 -7
- package/src/take-viewport-screenshot.js +16 -38
- package/test/e2e/android.spec.js +43 -1
- package/test/e2e/ios.spec.js +83 -5
- package/test/e2e/web-ios.spec.js +41 -6
- package/test/fixtures/ios/app-fully-collapsing.png +0 -0
- package/test/fixtures/ios/app-fully-collection.png +0 -0
- package/test/fixtures/ios/app-fully-overlapped-statusbar.png +0 -0
- package/test/fixtures/ios/app-fully-overlapped.png +0 -0
- package/test/fixtures/ios/app-fully-scroll-statusbar.png +0 -0
- package/test/fixtures/ios/app-fully-scroll.png +0 -0
- package/test/fixtures/ios/app-fully-superview.png +0 -0
- package/test/fixtures/ios/app-fully-table.png +0 -0
- package/test/fixtures/ios/app-statusbar.png +0 -0
- package/test/fixtures/ios/app.png +0 -0
- package/test/fixtures/ios/element-fully.png +0 -0
- package/test/fixtures/ios/element.png +0 -0
- package/test/fixtures/ios/region.png +0 -0
- package/test/fixtures/ios/webview-fully.png +0 -0
- package/test/fixtures/ios/webview.png +0 -0
- package/test/fixtures/web-ios/page-fully-landscape.png +0 -0
- package/test/fixtures/web-ios/page-fully.png +0 -0
- package/test/fixtures/web-ios/page-landscape.png +0 -0
- package/test/fixtures/web-ios/page.png +0 -0
- package/test/it/find-pattern.spec.js +1 -1
- package/logs/screenshot_2021_12_16_19_06_06_332Z_full_app_failed_1639681566332.png +0 -0
- package/logs/screenshot_2021_12_16_19_15_59_935Z_full_app_failed_1639682159935.png +0 -0
- package/logs/screenshot_2021_12_16_19_33_20_679Z_full_app_failed_1639683200679.png +0 -0
- package/logs/screenshot_2021_12_16_19_37_22_120Z_ios_viewport_failed.png +0 -0
- package/logs/screenshot_2021_12_16_19_38_09_461Z_ios_full_page_failed.png +0 -0
- package/logs/screenshot_2021_12_16_19_59_45_182Z_ios_viewport_failed.png +0 -0
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"node_modules/@applitools/screenshoter": {
|
|
12
|
-
"version": "3.
|
|
12
|
+
"version": "3.3.3",
|
|
13
13
|
"resolved": "file:../dry-run.tgz",
|
|
14
|
-
"integrity": "sha512-
|
|
14
|
+
"integrity": "sha512-Z9adhafLkHH0o9oRHyA8QeKWTBNejGVnwX3MzkRI0K6pCS+tdoxwpxzsoJRIhj8WiZn4AWpOgafRh7Uu+QXB1g==",
|
|
15
15
|
"license": "SEE LICENSE IN LICENSE",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@applitools/snippets": "2.1.
|
|
18
|
-
"@applitools/utils": "1.2.
|
|
17
|
+
"@applitools/snippets": "2.1.12",
|
|
18
|
+
"@applitools/utils": "1.2.5",
|
|
19
19
|
"png-async": "0.9.4"
|
|
20
20
|
},
|
|
21
21
|
"engines": {
|
|
@@ -23,17 +23,17 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"node_modules/@applitools/snippets": {
|
|
26
|
-
"version": "2.1.
|
|
27
|
-
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.
|
|
28
|
-
"integrity": "sha512-
|
|
26
|
+
"version": "2.1.12",
|
|
27
|
+
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.12.tgz",
|
|
28
|
+
"integrity": "sha512-XFj9ZMJ8VNNzAuCdGqgmC+SEZY61jRahAiZ/W1dgyJJrEIWmkEm/8USq/3n1mJbbyDV8VhT6+aSikqoanzPETQ==",
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=8.9.0"
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"node_modules/@applitools/utils": {
|
|
34
|
-
"version": "1.2.
|
|
35
|
-
"resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.
|
|
36
|
-
"integrity": "sha512-
|
|
34
|
+
"version": "1.2.5",
|
|
35
|
+
"resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.5.tgz",
|
|
36
|
+
"integrity": "sha512-nETSgqGeCk5yqjFaQ7x1KURf+t5IxsY2RoeFB5w1+6lHprmHdEithMcN0tiJWeqi14QBJdUkXCzCJrMW5RcDFg==",
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">= 8.9.0"
|
|
39
39
|
}
|
|
@@ -47,22 +47,22 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@applitools/screenshoter": {
|
|
49
49
|
"version": "file:../dry-run.tgz",
|
|
50
|
-
"integrity": "sha512-
|
|
50
|
+
"integrity": "sha512-Z9adhafLkHH0o9oRHyA8QeKWTBNejGVnwX3MzkRI0K6pCS+tdoxwpxzsoJRIhj8WiZn4AWpOgafRh7Uu+QXB1g==",
|
|
51
51
|
"requires": {
|
|
52
|
-
"@applitools/snippets": "2.1.
|
|
53
|
-
"@applitools/utils": "1.2.
|
|
52
|
+
"@applitools/snippets": "2.1.12",
|
|
53
|
+
"@applitools/utils": "1.2.5",
|
|
54
54
|
"png-async": "0.9.4"
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"@applitools/snippets": {
|
|
58
|
-
"version": "2.1.
|
|
59
|
-
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.
|
|
60
|
-
"integrity": "sha512-
|
|
58
|
+
"version": "2.1.12",
|
|
59
|
+
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.12.tgz",
|
|
60
|
+
"integrity": "sha512-XFj9ZMJ8VNNzAuCdGqgmC+SEZY61jRahAiZ/W1dgyJJrEIWmkEm/8USq/3n1mJbbyDV8VhT6+aSikqoanzPETQ=="
|
|
61
61
|
},
|
|
62
62
|
"@applitools/utils": {
|
|
63
|
-
"version": "1.2.
|
|
64
|
-
"resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.
|
|
65
|
-
"integrity": "sha512-
|
|
63
|
+
"version": "1.2.5",
|
|
64
|
+
"resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.5.tgz",
|
|
65
|
+
"integrity": "sha512-nETSgqGeCk5yqjFaQ7x1KURf+t5IxsY2RoeFB5w1+6lHprmHdEithMcN0tiJWeqi14QBJdUkXCzCJrMW5RcDFg=="
|
|
66
66
|
},
|
|
67
67
|
"png-async": {
|
|
68
68
|
"version": "0.9.4",
|
package/.bongo/dry-run.tgz
CHANGED
|
Binary file
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,27 @@
|
|
|
4
4
|
## Unreleased
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
## 3.3.4 - 2021/12/23
|
|
8
|
+
|
|
9
|
+
- updated to @applitools/snippets@2.1.12 (from 2.1.11)
|
|
10
|
+
|
|
11
|
+
## 3.3.3 - 2021/12/22
|
|
12
|
+
|
|
13
|
+
- improve default rotation and scaling logic
|
|
14
|
+
- improve scroll into viewport algorithm
|
|
15
|
+
- fix lazy handling of image rotation
|
|
16
|
+
- updated to @applitools/snippets@2.1.11 (from 2.1.10)
|
|
17
|
+
- updated to @applitools/utils@1.2.5 (from 1.2.4)
|
|
18
|
+
|
|
19
|
+
## 3.3.2 - 2021/12/20
|
|
20
|
+
|
|
21
|
+
- add basic support of webview screenshots on ios
|
|
22
|
+
- updated to @applitools/snippets@2.1.10 (from 2.1.8)
|
|
23
|
+
|
|
24
|
+
## 3.3.1 - 2021/12/17
|
|
25
|
+
|
|
26
|
+
- no changes
|
|
27
|
+
|
|
7
28
|
## 3.3.0 - 2021/12/16
|
|
8
29
|
|
|
9
30
|
- fix ios web screenshots on pages without viewport meta tag
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applitools/screenshoter",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.4",
|
|
4
4
|
"description": "Applitools universal screenshoter for web and native applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"applitools",
|
|
@@ -49,14 +49,14 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@applitools/snippets": "2.1.
|
|
53
|
-
"@applitools/utils": "1.2.
|
|
52
|
+
"@applitools/snippets": "2.1.12",
|
|
53
|
+
"@applitools/utils": "1.2.5",
|
|
54
54
|
"png-async": "0.9.4"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@applitools/driver": "1.4.
|
|
57
|
+
"@applitools/driver": "1.4.7",
|
|
58
58
|
"@applitools/sdk-release-kit": "0.13.4",
|
|
59
|
-
"@applitools/spec-driver-webdriverio": "1.2.
|
|
59
|
+
"@applitools/spec-driver-webdriverio": "1.2.5",
|
|
60
60
|
"@applitools/test-utils": "1.0.10",
|
|
61
61
|
"chromedriver": "^95.0.0",
|
|
62
62
|
"eslint": "^7.9.0",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
function findImagePattern(image, pattern) {
|
|
2
2
|
for (let pixel = 0; pixel < image.width * image.height; ++pixel) {
|
|
3
3
|
if (isPattern(image, pixel, pattern)) {
|
|
4
|
-
|
|
4
|
+
const patterOffset = pattern.offset * pattern.pixelRatio
|
|
5
|
+
return {x: (pixel % image.width) - patterOffset, y: Math.floor(pixel / image.width) - patterOffset}
|
|
5
6
|
}
|
|
6
7
|
}
|
|
7
8
|
return null
|
package/src/image.js
CHANGED
|
@@ -38,7 +38,9 @@ function makeImage(data) {
|
|
|
38
38
|
return true
|
|
39
39
|
},
|
|
40
40
|
get size() {
|
|
41
|
-
return utils.geometry.round(
|
|
41
|
+
return utils.geometry.round(
|
|
42
|
+
utils.geometry.rotate(utils.geometry.scale(size, transforms.scale), transforms.rotate),
|
|
43
|
+
)
|
|
42
44
|
},
|
|
43
45
|
get transforms() {
|
|
44
46
|
return {...transforms}
|
|
@@ -51,6 +53,12 @@ function makeImage(data) {
|
|
|
51
53
|
},
|
|
52
54
|
scale(ratio) {
|
|
53
55
|
transforms.scale *= ratio
|
|
56
|
+
// size = utils.geometry.scale(size, ratio)
|
|
57
|
+
return this
|
|
58
|
+
},
|
|
59
|
+
rotate(degrees) {
|
|
60
|
+
transforms.rotate = (transforms.rotate + degrees) % 360
|
|
61
|
+
// size = utils.geometry.rotate(size, degrees)
|
|
54
62
|
return this
|
|
55
63
|
},
|
|
56
64
|
crop(region) {
|
|
@@ -64,17 +72,12 @@ function makeImage(data) {
|
|
|
64
72
|
} else {
|
|
65
73
|
region = utils.geometry.scale(region, 1 / transforms.scale)
|
|
66
74
|
}
|
|
67
|
-
region = utils.geometry.rotate(region, transforms.rotate)
|
|
68
|
-
|
|
75
|
+
region = utils.geometry.rotate(region, -transforms.rotate, utils.geometry.rotate(size, transforms.rotate))
|
|
76
|
+
region = transforms.crop
|
|
69
77
|
? utils.geometry.intersect(transforms.crop, utils.geometry.offset(region, transforms.crop))
|
|
70
78
|
: utils.geometry.intersect({x: 0, y: 0, ...size}, region)
|
|
71
|
-
|
|
72
|
-
size = utils.geometry.
|
|
73
|
-
|
|
74
|
-
return this
|
|
75
|
-
},
|
|
76
|
-
rotate(degree) {
|
|
77
|
-
transforms.rotate = (transforms.rotate + degree) % 360
|
|
79
|
+
transforms.crop = region
|
|
80
|
+
size = utils.geometry.size(transforms.crop)
|
|
78
81
|
return this
|
|
79
82
|
},
|
|
80
83
|
copy(srcImage, offset) {
|
|
@@ -204,9 +207,9 @@ async function transform(image, transforms) {
|
|
|
204
207
|
}
|
|
205
208
|
}, image)
|
|
206
209
|
|
|
207
|
-
image = transforms.rotate > 0 ? await rotate(image, transforms.rotate) : image
|
|
208
210
|
image = transforms.crop ? await extract(image, transforms.crop) : image
|
|
209
211
|
image = transforms.scale !== 1 ? await scale(image, transforms.scale) : image
|
|
212
|
+
image = transforms.rotate !== 0 ? await rotate(image, transforms.rotate) : image
|
|
210
213
|
return image
|
|
211
214
|
}
|
|
212
215
|
|
|
@@ -296,7 +299,7 @@ async function rotate(image, degrees) {
|
|
|
296
299
|
for (let srcY = 0, dstX = 0; srcY < image.height; ++srcY, ++dstX) {
|
|
297
300
|
for (let srcX = 0, dstY = image.width - 1; srcX < image.width; ++srcX, --dstY) {
|
|
298
301
|
const pixel = image.data.readUInt32BE((srcY * image.width + srcX) * 4)
|
|
299
|
-
dstImage.data.writeUInt32BE(pixel, (
|
|
302
|
+
dstImage.data.writeUInt32BE(pixel, (dstY * dstImage.width + dstX) * 4)
|
|
300
303
|
}
|
|
301
304
|
}
|
|
302
305
|
} else {
|
|
@@ -15,14 +15,23 @@ async function scrollIntoViewport({context, scroller, region, logger}) {
|
|
|
15
15
|
let remainingOffset = {x: elementContextRegion.x, y: elementContextRegion.y}
|
|
16
16
|
while (currentContext) {
|
|
17
17
|
const scrollingElement = await currentContext.getScrollingElement()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (!scrollingElement) {
|
|
19
|
+
currentContext = currentContext.parent
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const scrollableRegion = await scrollingElement.getClientRegion()
|
|
24
|
+
const requiredOffset = {
|
|
25
|
+
x: Math.max(
|
|
26
|
+
remainingOffset.x - (scrollableRegion.x + Math.max(scrollableRegion.width - elementContextRegion.width, 0)),
|
|
27
|
+
0,
|
|
28
|
+
),
|
|
29
|
+
y: Math.max(
|
|
30
|
+
remainingOffset.y - (scrollableRegion.y + Math.max(scrollableRegion.height - elementContextRegion.height, 0)),
|
|
31
|
+
0,
|
|
32
|
+
),
|
|
33
|
+
}
|
|
34
|
+
const actualOffset = await scroller.moveTo(requiredOffset, scrollingElement)
|
|
26
35
|
|
|
27
36
|
remainingOffset = utils.geometry.offset(
|
|
28
37
|
utils.geometry.offsetNegative(remainingOffset, actualOffset),
|
|
@@ -7,6 +7,9 @@ function makeTakeViewportScreenshot(options) {
|
|
|
7
7
|
const {driver} = options
|
|
8
8
|
if (driver.isNative) {
|
|
9
9
|
return makeTakeNativeScreenshot(options)
|
|
10
|
+
} else if (driver.isIOS) {
|
|
11
|
+
// safari on ios takes screenshot with browser and os interfaces
|
|
12
|
+
return makeTakeMarkedScreenshot(options)
|
|
10
13
|
} else if (driver.browserName === 'Firefox') {
|
|
11
14
|
try {
|
|
12
15
|
const browserVersion = Number.parseInt(driver.browserVersion, 10)
|
|
@@ -15,28 +18,22 @@ function makeTakeViewportScreenshot(options) {
|
|
|
15
18
|
return makeTakeMainContextScreenshot(options)
|
|
16
19
|
}
|
|
17
20
|
} catch (ignored) {}
|
|
18
|
-
} else if (driver.browserName === 'Safari') {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return makeTakeMarkedScreenshot(options)
|
|
22
|
-
} else if (driver.browserVersion === '11') {
|
|
23
|
-
// safari 11 on macs takes full page screenshot
|
|
24
|
-
return makeTakeSafari11Screenshot(options)
|
|
25
|
-
}
|
|
21
|
+
} else if (driver.browserName === 'Safari' && driver.browserVersion === '11') {
|
|
22
|
+
// safari 11 on macs takes full page screenshot
|
|
23
|
+
return makeTakeSafari11Screenshot(options)
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
return makeTakeDefaultScreenshot(options)
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
function makeTakeDefaultScreenshot({driver, stabilization = {}, debug, logger}) {
|
|
32
|
-
const calculateScaleRatio = makeCalculateScaleRatio({driver})
|
|
33
30
|
return async function takeScreenshot({name} = {}) {
|
|
34
31
|
logger.verbose('Taking screenshot...')
|
|
35
32
|
const image = makeImage(await driver.takeScreenshot())
|
|
36
33
|
await image.debug({...debug, name, suffix: 'original'})
|
|
37
34
|
|
|
38
35
|
if (stabilization.scale) image.scale(stabilization.scale)
|
|
39
|
-
else image.scale(
|
|
36
|
+
else image.scale(1 / driver.pixelRatio)
|
|
40
37
|
|
|
41
38
|
if (stabilization.rotate) image.crop(stabilization.rotate)
|
|
42
39
|
|
|
@@ -47,7 +44,6 @@ function makeTakeDefaultScreenshot({driver, stabilization = {}, debug, logger})
|
|
|
47
44
|
}
|
|
48
45
|
|
|
49
46
|
function makeTakeMainContextScreenshot({driver, stabilization = {}, debug, logger}) {
|
|
50
|
-
const calculateScaleRatio = makeCalculateScaleRatio({driver})
|
|
51
47
|
return async function takeScreenshot({name} = {}) {
|
|
52
48
|
logger.verbose('Taking screenshot...')
|
|
53
49
|
const originalContext = driver.currentContext
|
|
@@ -57,7 +53,7 @@ function makeTakeMainContextScreenshot({driver, stabilization = {}, debug, logge
|
|
|
57
53
|
await image.debug({...debug, name, suffix: 'original'})
|
|
58
54
|
|
|
59
55
|
if (stabilization.scale) image.scale(stabilization.scale)
|
|
60
|
-
else image.scale(
|
|
56
|
+
else image.scale(1 / driver.pixelRatio)
|
|
61
57
|
|
|
62
58
|
if (stabilization.rotate) image.rotate(stabilization.rotate)
|
|
63
59
|
|
|
@@ -68,7 +64,6 @@ function makeTakeMainContextScreenshot({driver, stabilization = {}, debug, logge
|
|
|
68
64
|
}
|
|
69
65
|
|
|
70
66
|
function makeTakeSafari11Screenshot({driver, stabilization = {}, debug, logger}) {
|
|
71
|
-
const calculateScaleRatio = makeCalculateScaleRatio({driver})
|
|
72
67
|
let viewportSize
|
|
73
68
|
|
|
74
69
|
return async function takeScreenshot({name} = {}) {
|
|
@@ -77,7 +72,7 @@ function makeTakeSafari11Screenshot({driver, stabilization = {}, debug, logger})
|
|
|
77
72
|
await image.debug({...debug, name, suffix: 'original'})
|
|
78
73
|
|
|
79
74
|
if (stabilization.scale) image.scale(stabilization.scale)
|
|
80
|
-
else image.scale(
|
|
75
|
+
else image.scale(1 / driver.pixelRatio)
|
|
81
76
|
|
|
82
77
|
if (stabilization.rotate) image.rotate(stabilization.rotate)
|
|
83
78
|
|
|
@@ -93,7 +88,6 @@ function makeTakeSafari11Screenshot({driver, stabilization = {}, debug, logger})
|
|
|
93
88
|
}
|
|
94
89
|
|
|
95
90
|
function makeTakeMarkedScreenshot({driver, stabilization = {}, debug, logger}) {
|
|
96
|
-
const calculateScaleRatio = makeCalculateScaleRatio({driver})
|
|
97
91
|
let viewportRegion
|
|
98
92
|
|
|
99
93
|
return async function takeScreenshot({name} = {}) {
|
|
@@ -102,9 +96,10 @@ function makeTakeMarkedScreenshot({driver, stabilization = {}, debug, logger}) {
|
|
|
102
96
|
await image.debug({...debug, name, suffix: 'original'})
|
|
103
97
|
|
|
104
98
|
if (stabilization.scale) image.scale(stabilization.scale)
|
|
105
|
-
else image.scale(
|
|
99
|
+
else image.scale(1 / driver.pixelRatio)
|
|
106
100
|
|
|
107
101
|
if (stabilization.rotate) image.rotate(stabilization.rotate)
|
|
102
|
+
else if (driver.orientation === 'landscape' && image.width < image.height) image.rotate(-90)
|
|
108
103
|
|
|
109
104
|
if (stabilization.crop) image.crop(stabilization.crop)
|
|
110
105
|
else {
|
|
@@ -119,10 +114,13 @@ function makeTakeMarkedScreenshot({driver, stabilization = {}, debug, logger}) {
|
|
|
119
114
|
|
|
120
115
|
async function getViewportRegion() {
|
|
121
116
|
const marker = await driver.mainContext.execute(snippets.addPageMarker)
|
|
117
|
+
await utils.general.sleep(100)
|
|
118
|
+
|
|
122
119
|
try {
|
|
123
120
|
const image = makeImage(await driver.takeScreenshot())
|
|
124
121
|
|
|
125
|
-
if (stabilization.rotate)
|
|
122
|
+
if (stabilization.rotate) image.rotate(stabilization.rotate)
|
|
123
|
+
else if (driver.orientation === 'landscape' && image.width < image.height) image.rotate(-90)
|
|
126
124
|
|
|
127
125
|
await image.debug({...debug, name: 'marker'})
|
|
128
126
|
|
|
@@ -148,6 +146,7 @@ function makeTakeNativeScreenshot({driver, stabilization = {}, debug, logger}) {
|
|
|
148
146
|
else image.scale(1 / driver.pixelRatio)
|
|
149
147
|
|
|
150
148
|
if (stabilization.rotate) image.rotate(stabilization.rotate)
|
|
149
|
+
else if (driver.orientation === 'landscape' && image.width < image.height) image.rotate(-90)
|
|
151
150
|
|
|
152
151
|
if (stabilization.crop) image.crop(stabilization.crop)
|
|
153
152
|
else {
|
|
@@ -164,25 +163,4 @@ function makeTakeNativeScreenshot({driver, stabilization = {}, debug, logger}) {
|
|
|
164
163
|
}
|
|
165
164
|
}
|
|
166
165
|
|
|
167
|
-
function makeCalculateScaleRatio({driver}) {
|
|
168
|
-
let viewportWidth, contentWidth
|
|
169
|
-
const VIEWPORT_THRESHOLD = 1
|
|
170
|
-
const CONTENT_THRESHOLD = 10
|
|
171
|
-
return async function calculateScaleRatio(imageWidth) {
|
|
172
|
-
if (!viewportWidth) viewportWidth = await driver.getViewportSize().then(size => size.width)
|
|
173
|
-
if (!contentWidth) contentWidth = await driver.mainContext.getContentSize().then(size => size.width)
|
|
174
|
-
// If the image's width is the same as the viewport's width or the
|
|
175
|
-
// top level context's width, no scaling is necessary.
|
|
176
|
-
if (
|
|
177
|
-
(imageWidth >= viewportWidth - VIEWPORT_THRESHOLD && imageWidth <= viewportWidth + VIEWPORT_THRESHOLD) ||
|
|
178
|
-
(imageWidth >= contentWidth - CONTENT_THRESHOLD && imageWidth <= contentWidth + CONTENT_THRESHOLD)
|
|
179
|
-
) {
|
|
180
|
-
return 1
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const scaledImageWidth = Math.round(imageWidth / driver.pixelRatio)
|
|
184
|
-
return viewportWidth / scaledImageWidth / driver.pixelRatio
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
166
|
module.exports = makeTakeViewportScreenshot
|
package/test/e2e/android.spec.js
CHANGED
|
@@ -20,6 +20,17 @@ const env = {
|
|
|
20
20
|
username: process.env.SAUCE_USERNAME,
|
|
21
21
|
accessKey: process.env.SAUCE_ACCESS_KEY,
|
|
22
22
|
},
|
|
23
|
+
|
|
24
|
+
// url: 'http://0.0.0.0:4723/wd/hub',
|
|
25
|
+
// capabilities: {
|
|
26
|
+
// deviceName: 'Google Pixel 3a XL',
|
|
27
|
+
// platformName: 'Android',
|
|
28
|
+
// platformVersion: '10.0',
|
|
29
|
+
// automationName: 'uiautomator2',
|
|
30
|
+
// nativeWebScreenshot: true,
|
|
31
|
+
// avd: 'Pixel_3a_XL',
|
|
32
|
+
// app: 'https://applitools.jfrog.io/artifactory/Examples/android/1.3/app-debug.apk',
|
|
33
|
+
// },
|
|
23
34
|
},
|
|
24
35
|
androidx: {
|
|
25
36
|
url: 'https://ondemand.saucelabs.com/wd/hub',
|
|
@@ -100,6 +111,10 @@ describe('screenshoter', () => {
|
|
|
100
111
|
return fullApp({type: 'non-scrollable'})
|
|
101
112
|
})
|
|
102
113
|
|
|
114
|
+
it.skip('take webview screenshot', () => {
|
|
115
|
+
return webview()
|
|
116
|
+
})
|
|
117
|
+
|
|
103
118
|
it('take region screenshot', () => {
|
|
104
119
|
return region()
|
|
105
120
|
})
|
|
@@ -136,6 +151,10 @@ describe('screenshoter', () => {
|
|
|
136
151
|
return fullApp({type: 'collapsing', x: true})
|
|
137
152
|
})
|
|
138
153
|
|
|
154
|
+
it.skip('take full app screenshot (pager)', () => {
|
|
155
|
+
return fullApp({type: 'pager', x: true})
|
|
156
|
+
})
|
|
157
|
+
|
|
139
158
|
it.skip('take full app screenshot (overlapped status bar)', () => {
|
|
140
159
|
return fullApp({type: 'overlapped', x: true})
|
|
141
160
|
})
|
|
@@ -161,7 +180,10 @@ describe('screenshoter', () => {
|
|
|
161
180
|
}
|
|
162
181
|
async function fullApp({type, x, ...options} = {}) {
|
|
163
182
|
let buttonSelector, expectedPath, scrollingElementSelector
|
|
164
|
-
if (type === '
|
|
183
|
+
if (type === 'pager') {
|
|
184
|
+
buttonSelector = {type: 'id', selector: 'btn_view_pager_2_activity'}
|
|
185
|
+
expectedPath = `./test/fixtures/android/x-app-fully-pager${options.withStatusBar ? '-statusbar' : ''}.png`
|
|
186
|
+
} else if (type === 'overlapped') {
|
|
165
187
|
buttonSelector = {type: 'id', selector: 'btn_recycler_view_under_status_bar_activity'}
|
|
166
188
|
expectedPath = `./test/fixtures/android/x-app-fully-overlapped${options.withStatusBar ? '-statusbar' : ''}.png`
|
|
167
189
|
} else if (type === 'collapsing') {
|
|
@@ -212,6 +234,26 @@ describe('screenshoter', () => {
|
|
|
212
234
|
throw err
|
|
213
235
|
}
|
|
214
236
|
}
|
|
237
|
+
async function webview(options) {
|
|
238
|
+
const expectedPath = `./test/fixtures/android/webview.png`
|
|
239
|
+
const buttonSelector = {type: 'id', selector: 'btn_web_view'}
|
|
240
|
+
|
|
241
|
+
const button = await driver.element(buttonSelector)
|
|
242
|
+
await button.click()
|
|
243
|
+
await driver.target.switchContext('WEBVIEW')
|
|
244
|
+
|
|
245
|
+
await driver.init()
|
|
246
|
+
|
|
247
|
+
const screenshot = await takeScreenshot({logger, driver, wait: 1500, ...options})
|
|
248
|
+
try {
|
|
249
|
+
const actual = await screenshot.image.toObject()
|
|
250
|
+
const expected = await makeImage(expectedPath).toObject()
|
|
251
|
+
assert.strictEqual(pixelmatch(actual.data, expected.data, null, expected.width, expected.height), 0)
|
|
252
|
+
} catch (err) {
|
|
253
|
+
await screenshot.image.debug({path: './logs', name: 'webview_failed', suffix: Date.now()})
|
|
254
|
+
throw err
|
|
255
|
+
}
|
|
256
|
+
}
|
|
215
257
|
async function region(options) {
|
|
216
258
|
const screenshot = await takeScreenshot({
|
|
217
259
|
logger,
|
package/test/e2e/ios.spec.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const assert = require('assert')
|
|
2
2
|
const pixelmatch = require('pixelmatch')
|
|
3
|
+
const utils = require('@applitools/utils')
|
|
3
4
|
const {Driver} = require('@applitools/driver')
|
|
4
5
|
const spec = require('@applitools/spec-driver-webdriverio')
|
|
5
6
|
const makeImage = require('../../src/image')
|
|
@@ -11,10 +12,10 @@ const env = {
|
|
|
11
12
|
name: 'iOS Screenshoter Test',
|
|
12
13
|
deviceName: 'iPhone 11 Pro Simulator',
|
|
13
14
|
platformName: 'iOS',
|
|
14
|
-
platformVersion: '
|
|
15
|
-
appiumVersion: '1.
|
|
15
|
+
platformVersion: '14.5',
|
|
16
|
+
appiumVersion: '1.21.0',
|
|
16
17
|
automationName: 'XCUITest',
|
|
17
|
-
app: 'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.
|
|
18
|
+
app: 'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.9/app/IOSTestApp.zip',
|
|
18
19
|
username: process.env.SAUCE_USERNAME,
|
|
19
20
|
accessKey: process.env.SAUCE_ACCESS_KEY,
|
|
20
21
|
},
|
|
@@ -24,7 +25,8 @@ const env = {
|
|
|
24
25
|
// deviceName: 'iPhone 11 Pro',
|
|
25
26
|
// platformName: 'iOS',
|
|
26
27
|
// platformVersion: '14.5',
|
|
27
|
-
//
|
|
28
|
+
// automationName: 'XCUITest',
|
|
29
|
+
// app: 'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.9/app/IOSTestApp.zip',
|
|
28
30
|
// },
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -101,6 +103,14 @@ describe('screenshoter ios', () => {
|
|
|
101
103
|
return fullApp({type: 'superview'})
|
|
102
104
|
})
|
|
103
105
|
|
|
106
|
+
it('take webview screenshot', () => {
|
|
107
|
+
return webview()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('take full webview screenshot', () => {
|
|
111
|
+
return fullWebview()
|
|
112
|
+
})
|
|
113
|
+
|
|
104
114
|
it('take region screenshot', () => {
|
|
105
115
|
return region()
|
|
106
116
|
})
|
|
@@ -119,6 +129,10 @@ describe('screenshoter ios', () => {
|
|
|
119
129
|
|
|
120
130
|
async function app(options = {}) {
|
|
121
131
|
const expectedPath = `./test/fixtures/ios/app${options.withStatusBar ? '-statusbar' : ''}.png`
|
|
132
|
+
const buttonSelector = {type: 'accessibility id', selector: 'Empty table view'}
|
|
133
|
+
|
|
134
|
+
const button = await driver.element(buttonSelector)
|
|
135
|
+
await button.click()
|
|
122
136
|
|
|
123
137
|
const screenshot = await takeScreenshot({logger, driver, ...options})
|
|
124
138
|
try {
|
|
@@ -180,7 +194,71 @@ describe('screenshoter ios', () => {
|
|
|
180
194
|
throw err
|
|
181
195
|
}
|
|
182
196
|
}
|
|
197
|
+
async function webview(options) {
|
|
198
|
+
const expectedPath = `./test/fixtures/ios/webview.png`
|
|
199
|
+
const buttonSelector = {type: 'accessibility id', selector: 'Web view'}
|
|
200
|
+
|
|
201
|
+
const button = await driver.element(buttonSelector)
|
|
202
|
+
await button.click()
|
|
203
|
+
await driver.target.getContexts()
|
|
204
|
+
await utils.general.sleep(500)
|
|
205
|
+
const [, webview] = await driver.target.getContexts()
|
|
206
|
+
console.log(webview)
|
|
207
|
+
await driver.target.switchContext(webview)
|
|
208
|
+
|
|
209
|
+
await driver.init()
|
|
210
|
+
|
|
211
|
+
const screenshot = await takeScreenshot({logger, driver, wait: 1500, ...options})
|
|
212
|
+
try {
|
|
213
|
+
const actual = await screenshot.image.toObject()
|
|
214
|
+
const expected = await makeImage(expectedPath).toObject()
|
|
215
|
+
assert.strictEqual(pixelmatch(actual.data, expected.data, null, expected.width, expected.height), 0)
|
|
216
|
+
} catch (err) {
|
|
217
|
+
await screenshot.image.debug({path: './logs', name: 'webview_failed', suffix: Date.now()})
|
|
218
|
+
throw err
|
|
219
|
+
} finally {
|
|
220
|
+
await driver.target.switchContext('NATIVE_APP')
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function fullWebview(options) {
|
|
224
|
+
const expectedPath = `./test/fixtures/ios/webview-fully.png`
|
|
225
|
+
const buttonSelector = {type: 'accessibility id', selector: 'Web view'}
|
|
226
|
+
|
|
227
|
+
const button = await driver.element(buttonSelector)
|
|
228
|
+
await button.click()
|
|
229
|
+
await driver.target.getContexts()
|
|
230
|
+
await utils.general.sleep(500)
|
|
231
|
+
const [, webview] = await driver.target.getContexts()
|
|
232
|
+
await driver.target.switchContext(webview)
|
|
233
|
+
|
|
234
|
+
await driver.init()
|
|
235
|
+
const screenshot = await takeScreenshot({
|
|
236
|
+
logger,
|
|
237
|
+
driver,
|
|
238
|
+
wait: 1500,
|
|
239
|
+
fully: true,
|
|
240
|
+
scrollingMode: 'scroll',
|
|
241
|
+
...options,
|
|
242
|
+
})
|
|
243
|
+
try {
|
|
244
|
+
const actual = await screenshot.image.toObject()
|
|
245
|
+
const expected = await makeImage(expectedPath).toObject()
|
|
246
|
+
assert.strictEqual(pixelmatch(actual.data, expected.data, null, expected.width, expected.height), 0)
|
|
247
|
+
} catch (err) {
|
|
248
|
+
await screenshot.image.debug({path: './logs', name: 'full_webview_failed', suffix: Date.now()})
|
|
249
|
+
throw err
|
|
250
|
+
} finally {
|
|
251
|
+
await driver.target.switchContext('NATIVE_APP')
|
|
252
|
+
}
|
|
253
|
+
}
|
|
183
254
|
async function region(options) {
|
|
255
|
+
const expectedPath = `./test/fixtures/ios/region.png`
|
|
256
|
+
const buttonSelector = {type: 'accessibility id', selector: 'Empty table view'}
|
|
257
|
+
|
|
258
|
+
const button = await driver.element(buttonSelector)
|
|
259
|
+
await button.click()
|
|
260
|
+
|
|
261
|
+
await driver.init()
|
|
184
262
|
const screenshot = await takeScreenshot({
|
|
185
263
|
logger,
|
|
186
264
|
driver,
|
|
@@ -191,7 +269,7 @@ describe('screenshoter ios', () => {
|
|
|
191
269
|
})
|
|
192
270
|
try {
|
|
193
271
|
const actual = await screenshot.image.toObject()
|
|
194
|
-
const expected = await makeImage(
|
|
272
|
+
const expected = await makeImage(expectedPath).toObject()
|
|
195
273
|
assert.strictEqual(pixelmatch(actual.data, expected.data, null, expected.width, expected.height), 0)
|
|
196
274
|
} catch (err) {
|
|
197
275
|
await screenshot.image.debug({path: './logs', name: 'region_failed'})
|
package/test/e2e/web-ios.spec.js
CHANGED
|
@@ -18,6 +18,19 @@ const env = {
|
|
|
18
18
|
username: process.env.SAUCE_USERNAME,
|
|
19
19
|
accessKey: process.env.SAUCE_ACCESS_KEY,
|
|
20
20
|
},
|
|
21
|
+
|
|
22
|
+
// url: 'http://0.0.0.0:4723/wd/hub',
|
|
23
|
+
// capabilities: {
|
|
24
|
+
// name: 'iOS Web Screenshoter Test',
|
|
25
|
+
// deviceName: 'iPhone 11 Pro',
|
|
26
|
+
// browserName: 'safari',
|
|
27
|
+
// platformName: 'iOS',
|
|
28
|
+
// platformVersion: '14.5',
|
|
29
|
+
// appiumVersion: '1.20.1',
|
|
30
|
+
// automationName: 'XCUITest',
|
|
31
|
+
// username: process.env.SAUCE_USERNAME,
|
|
32
|
+
// accessKey: process.env.SAUCE_ACCESS_KEY,
|
|
33
|
+
// },
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
describe('screenshoter web ios', () => {
|
|
@@ -39,18 +52,33 @@ describe('screenshoter web ios', () => {
|
|
|
39
52
|
})
|
|
40
53
|
|
|
41
54
|
it('take viewport screenshot', () => {
|
|
42
|
-
return viewport()
|
|
55
|
+
return viewport({orientation: 'portrait'})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('take viewport screenshot with landscape orientation', () => {
|
|
59
|
+
return viewport({orientation: 'landscape'})
|
|
43
60
|
})
|
|
44
61
|
|
|
45
62
|
it('take full page screenshot', () => {
|
|
46
|
-
return fullPage()
|
|
63
|
+
return fullPage({orientation: 'portrait'})
|
|
47
64
|
})
|
|
48
65
|
|
|
49
|
-
|
|
66
|
+
it('take full page screenshot with landscape orientation', () => {
|
|
67
|
+
return fullPage({orientation: 'landscape'})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
async function viewport({orientation = 'portrait', ...options} = {}) {
|
|
71
|
+
const expectedPath = `./test/fixtures/web-ios/page${orientation === 'landscape' ? '-landscape' : ''}.png`
|
|
72
|
+
|
|
73
|
+
await driver.target.setOrientation(orientation.toUpperCase())
|
|
74
|
+
await driver.visit(await driver.getUrl())
|
|
75
|
+
|
|
76
|
+
await driver.init()
|
|
77
|
+
|
|
50
78
|
const screenshot = await takeScreenshot({logger, driver, ...options})
|
|
51
79
|
try {
|
|
52
80
|
const actual = await screenshot.image.toObject()
|
|
53
|
-
const expected = await makeImage(
|
|
81
|
+
const expected = await makeImage(expectedPath).toObject()
|
|
54
82
|
assert.strictEqual(pixelmatch(actual.data, expected.data, null, expected.width, expected.height), 0)
|
|
55
83
|
} catch (err) {
|
|
56
84
|
await screenshot.image.debug({path: './logs', name: 'ios_viewport_failed'})
|
|
@@ -58,11 +86,18 @@ describe('screenshoter web ios', () => {
|
|
|
58
86
|
}
|
|
59
87
|
}
|
|
60
88
|
|
|
61
|
-
async function fullPage(options) {
|
|
89
|
+
async function fullPage({orientation = 'portrait', ...options} = {}) {
|
|
90
|
+
const expectedPath = `./test/fixtures/web-ios/page-fully${orientation === 'landscape' ? '-landscape' : ''}.png`
|
|
91
|
+
|
|
92
|
+
await driver.target.setOrientation(orientation.toUpperCase())
|
|
93
|
+
await driver.visit(await driver.getUrl())
|
|
94
|
+
|
|
95
|
+
await driver.init()
|
|
96
|
+
|
|
62
97
|
const screenshot = await takeScreenshot({logger, driver, fully: true, ...options})
|
|
63
98
|
try {
|
|
64
99
|
const actual = await screenshot.image.toObject()
|
|
65
|
-
const expected = await makeImage(
|
|
100
|
+
const expected = await makeImage(expectedPath).toObject()
|
|
66
101
|
assert.strictEqual(pixelmatch(actual.data, expected.data, null, expected.width, expected.height), 0)
|
|
67
102
|
} catch (err) {
|
|
68
103
|
await screenshot.image.debug({path: './logs', name: 'ios_full_page_failed'})
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -13,7 +13,7 @@ describe('pattern', () => {
|
|
|
13
13
|
{name: 'iPad_5th_portrait', position: {x: 0, y: 140}, offset: 0, pixelRatio: 2},
|
|
14
14
|
{name: 'iPad_5th_landscape', position: {x: 0, y: 140}, offset: 0, pixelRatio: 2},
|
|
15
15
|
{name: 'iPad_9th_portrait', position: {x: 0, y: 136}, offset: 0, pixelRatio: 2},
|
|
16
|
-
{name: 'iPad_9th_landscape', position: {x:
|
|
16
|
+
{name: 'iPad_9th_landscape', position: {x: 640, y: 136}, offset: 1, pixelRatio: 2},
|
|
17
17
|
{name: 'iPhone_XS_portrait_noviewport', position: {x: 0, y: 282}, offset: 0, pixelRatio: 3},
|
|
18
18
|
{name: 'iPhone_XS_portrait_nomarker', position: null, pixelRatio: 3},
|
|
19
19
|
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|