@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.
Files changed (37) hide show
  1. package/.bongo/dry-run/package-lock.json +19 -19
  2. package/.bongo/dry-run.tgz +0 -0
  3. package/CHANGELOG.md +21 -0
  4. package/package.json +5 -5
  5. package/src/find-image-pattern.js +2 -1
  6. package/src/image.js +15 -12
  7. package/src/scroll-into-viewport.js +16 -7
  8. package/src/take-viewport-screenshot.js +16 -38
  9. package/test/e2e/android.spec.js +43 -1
  10. package/test/e2e/ios.spec.js +83 -5
  11. package/test/e2e/web-ios.spec.js +41 -6
  12. package/test/fixtures/ios/app-fully-collapsing.png +0 -0
  13. package/test/fixtures/ios/app-fully-collection.png +0 -0
  14. package/test/fixtures/ios/app-fully-overlapped-statusbar.png +0 -0
  15. package/test/fixtures/ios/app-fully-overlapped.png +0 -0
  16. package/test/fixtures/ios/app-fully-scroll-statusbar.png +0 -0
  17. package/test/fixtures/ios/app-fully-scroll.png +0 -0
  18. package/test/fixtures/ios/app-fully-superview.png +0 -0
  19. package/test/fixtures/ios/app-fully-table.png +0 -0
  20. package/test/fixtures/ios/app-statusbar.png +0 -0
  21. package/test/fixtures/ios/app.png +0 -0
  22. package/test/fixtures/ios/element-fully.png +0 -0
  23. package/test/fixtures/ios/element.png +0 -0
  24. package/test/fixtures/ios/region.png +0 -0
  25. package/test/fixtures/ios/webview-fully.png +0 -0
  26. package/test/fixtures/ios/webview.png +0 -0
  27. package/test/fixtures/web-ios/page-fully-landscape.png +0 -0
  28. package/test/fixtures/web-ios/page-fully.png +0 -0
  29. package/test/fixtures/web-ios/page-landscape.png +0 -0
  30. package/test/fixtures/web-ios/page.png +0 -0
  31. package/test/it/find-pattern.spec.js +1 -1
  32. package/logs/screenshot_2021_12_16_19_06_06_332Z_full_app_failed_1639681566332.png +0 -0
  33. package/logs/screenshot_2021_12_16_19_15_59_935Z_full_app_failed_1639682159935.png +0 -0
  34. package/logs/screenshot_2021_12_16_19_33_20_679Z_full_app_failed_1639683200679.png +0 -0
  35. package/logs/screenshot_2021_12_16_19_37_22_120Z_ios_viewport_failed.png +0 -0
  36. package/logs/screenshot_2021_12_16_19_38_09_461Z_ios_full_page_failed.png +0 -0
  37. 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.2.9",
12
+ "version": "3.3.3",
13
13
  "resolved": "file:../dry-run.tgz",
14
- "integrity": "sha512-a9s+yYo2bQ61Nyt/Enqrjgi7BjHV97c5LrEREO2iFisgdq+SzNHaVOav63UIQ9a5qvMAAetyZgU2NgVtPNNWow==",
14
+ "integrity": "sha512-Z9adhafLkHH0o9oRHyA8QeKWTBNejGVnwX3MzkRI0K6pCS+tdoxwpxzsoJRIhj8WiZn4AWpOgafRh7Uu+QXB1g==",
15
15
  "license": "SEE LICENSE IN LICENSE",
16
16
  "dependencies": {
17
- "@applitools/snippets": "2.1.8",
18
- "@applitools/utils": "1.2.4",
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.8",
27
- "resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.8.tgz",
28
- "integrity": "sha512-7CGFsbL9vAd6MBiGLVKHpKYywPXN499/fJMzEAVCLuGCY81CIc/PchqPFWrZZKZOY/IV21RJmE3MvqZAeXTIXA==",
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.4",
35
- "resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.4.tgz",
36
- "integrity": "sha512-w7ma6FFGyqhdP6LEcuHFWOcH7EzBjnoAX3UfbFWcTHA3QXnXPX37Y2ENYRodfwkorP1cUKyUHwNXJB/BMIj/hg==",
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-a9s+yYo2bQ61Nyt/Enqrjgi7BjHV97c5LrEREO2iFisgdq+SzNHaVOav63UIQ9a5qvMAAetyZgU2NgVtPNNWow==",
50
+ "integrity": "sha512-Z9adhafLkHH0o9oRHyA8QeKWTBNejGVnwX3MzkRI0K6pCS+tdoxwpxzsoJRIhj8WiZn4AWpOgafRh7Uu+QXB1g==",
51
51
  "requires": {
52
- "@applitools/snippets": "2.1.8",
53
- "@applitools/utils": "1.2.4",
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.8",
59
- "resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.8.tgz",
60
- "integrity": "sha512-7CGFsbL9vAd6MBiGLVKHpKYywPXN499/fJMzEAVCLuGCY81CIc/PchqPFWrZZKZOY/IV21RJmE3MvqZAeXTIXA=="
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.4",
64
- "resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.4.tgz",
65
- "integrity": "sha512-w7ma6FFGyqhdP6LEcuHFWOcH7EzBjnoAX3UfbFWcTHA3QXnXPX37Y2ENYRodfwkorP1cUKyUHwNXJB/BMIj/hg=="
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",
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.0",
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.8",
53
- "@applitools/utils": "1.2.4",
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.1",
57
+ "@applitools/driver": "1.4.7",
58
58
  "@applitools/sdk-release-kit": "0.13.4",
59
- "@applitools/spec-driver-webdriverio": "1.2.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
- return {x: (pixel % image.width) - pattern.offset, y: Math.floor(pixel / image.width) - pattern.offset}
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(utils.geometry.scale(size, transforms.scale))
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
- transforms.crop = transforms.crop
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.round(utils.geometry.size(transforms.crop))
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, (srcX * dstImage.width + dstY) * 4)
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
- const scrollingElementOffset = scrollingElement
19
- ? utils.geometry.location(await scrollingElement.getClientRegion())
20
- : {x: 0, y: 0}
18
+ if (!scrollingElement) {
19
+ currentContext = currentContext.parent
20
+ continue
21
+ }
21
22
 
22
- const actualOffset = await scroller.moveTo(
23
- utils.geometry.offsetNegative(remainingOffset, scrollingElementOffset),
24
- scrollingElement,
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
- if (driver.isIOS) {
20
- // safari on ios takes screenshot with browser and os interfaces
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(await calculateScaleRatio(image.width))
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(await calculateScaleRatio(image.width))
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(await calculateScaleRatio(image.width))
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(await calculateScaleRatio(image.width))
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) await image.rotate(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
@@ -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 === 'overlapped') {
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,
@@ -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: '13.4',
15
- appiumVersion: '1.19.2',
15
+ platformVersion: '14.5',
16
+ appiumVersion: '1.21.0',
16
17
  automationName: 'XCUITest',
17
- app: 'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.8/app/IOSTestApp.zip',
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
- // app: '/Users/kyrylo/Downloads/IOSTestApp.zip',
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('./test/fixtures/ios/region.png').toObject()
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'})
@@ -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
- async function viewport(options) {
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('./test/fixtures/web-ios/page.png').toObject()
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('./test/fixtures/web-ios/page-fully.png').toObject()
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
@@ -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: 641, y: 137}, offset: 1, pixelRatio: 2},
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
  ]