@applitools/screenshoter 3.2.9 → 3.3.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.
Files changed (98) hide show
  1. package/.bongo/dry-run/package-lock.json +11 -11
  2. package/.bongo/dry-run.tgz +0 -0
  3. package/CHANGELOG.md +6 -0
  4. package/index.js +2 -1
  5. package/logs/screenshot_2021_12_16_19_06_06_332Z_full_app_failed_1639681566332.png +0 -0
  6. package/logs/screenshot_2021_12_16_19_15_59_935Z_full_app_failed_1639682159935.png +0 -0
  7. package/logs/screenshot_2021_12_16_19_33_20_679Z_full_app_failed_1639683200679.png +0 -0
  8. package/logs/screenshot_2021_12_16_19_37_22_120Z_ios_viewport_failed.png +0 -0
  9. package/logs/screenshot_2021_12_16_19_38_09_461Z_ios_full_page_failed.png +0 -0
  10. package/logs/screenshot_2021_12_16_19_59_45_182Z_ios_viewport_failed.png +0 -0
  11. package/package.json +5 -5
  12. package/src/find-image-pattern.js +10 -38
  13. package/src/image.js +104 -64
  14. package/src/take-screenshot.js +136 -160
  15. package/src/take-simple-screenshot.js +25 -0
  16. package/src/take-stitched-screenshot.js +32 -52
  17. package/src/take-viewport-screenshot.js +179 -16
  18. package/test/e2e/android.spec.js +42 -11
  19. package/test/e2e/external.spec.js +81 -10
  20. package/test/e2e/ios.spec.js +48 -10
  21. package/test/e2e/web-ios.spec.js +3 -5
  22. package/test/e2e/web.spec.js +20 -15
  23. package/test/fixtures/android/app-fully-non-scrollable.png +0 -0
  24. package/test/fixtures/android/app-fully-recycler.png +0 -0
  25. package/test/fixtures/android/app-fully-scroll-statusbar.png +0 -0
  26. package/test/fixtures/android/app-fully-scroll.png +0 -0
  27. package/test/fixtures/android/app-statusbar.png +0 -0
  28. package/test/fixtures/android/x-app-fully-collapsing.png +0 -0
  29. package/test/fixtures/android/x-app-fully-recycler.png +0 -0
  30. package/test/fixtures/android/x-element-fully.png +0 -0
  31. package/test/fixtures/image/{house.combined-higher-wider.png → house.framed-higher-wider.png} +0 -0
  32. package/test/fixtures/image/{house.combined-higher.png → house.framed-higher.png} +0 -0
  33. package/test/fixtures/image/house.framed-shorter-thinner.png +0 -0
  34. package/test/fixtures/image/{house.combined-wider.png → house.framed-wider.png} +0 -0
  35. package/test/fixtures/ios/app-fully-collapsing.png +0 -0
  36. package/test/fixtures/ios/app-fully-collection.png +0 -0
  37. package/test/fixtures/ios/app-fully-overlapped-statusbar.png +0 -0
  38. package/test/fixtures/ios/app-fully-overlapped.png +0 -0
  39. package/test/fixtures/ios/app-fully-scroll-statusbar.png +0 -0
  40. package/test/fixtures/ios/app-fully-scroll.png +0 -0
  41. package/test/fixtures/ios/app-fully-superview.png +0 -0
  42. package/test/fixtures/ios/app-fully-table.png +0 -0
  43. package/test/fixtures/ios/app-statusbar.png +0 -0
  44. package/test/fixtures/ios/app.png +0 -0
  45. package/test/fixtures/ios/element-fully.png +0 -0
  46. package/test/fixtures/ios/element.png +0 -0
  47. package/test/fixtures/ios/region.png +0 -0
  48. package/test/fixtures/pattern/iPad_5th_landscape.png +0 -0
  49. package/test/fixtures/pattern/iPad_5th_portrait.png +0 -0
  50. package/test/fixtures/pattern/iPad_9th_landscape.png +0 -0
  51. package/test/fixtures/pattern/iPad_9th_portrait.png +0 -0
  52. package/test/fixtures/pattern/iPhone_11_landscape.png +0 -0
  53. package/test/fixtures/pattern/iPhone_11_portrait.png +0 -0
  54. package/test/fixtures/pattern/iPhone_13_landscape.png +0 -0
  55. package/test/fixtures/pattern/iPhone_13_portrait.png +0 -0
  56. package/test/fixtures/pattern/iPhone_SE_landscape.png +0 -0
  57. package/test/fixtures/pattern/iPhone_SE_portrait.png +0 -0
  58. package/test/fixtures/pattern/iPhone_XS_portrait_noviewport.png +0 -0
  59. package/test/fixtures/web-ios/page-fully.png +0 -0
  60. package/test/fixtures/web-ios/page.png +0 -0
  61. package/test/it/find-pattern.spec.js +16 -11
  62. package/test/it/image.spec.js +42 -15
  63. package/logs/screenshot_2021_11_14_12_35_00_342Z_full_frame_failed.png +0 -0
  64. package/logs/screenshot_2021_11_14_12_38_00_715Z_frame_failed.png +0 -0
  65. package/logs/screenshot_2021_11_14_12_38_03_866Z_frame_failed.png +0 -0
  66. package/logs/screenshot_2021_11_14_13_02_56_464Z_full_app_failed_1636894976464.png +0 -0
  67. package/logs/screenshot_2021_11_14_13_04_27_904Z_full_app_failed_1636895067904.png +0 -0
  68. package/logs/screenshot_2021_11_14_13_06_13_662Z_full_app_failed_1636895173662.png +0 -0
  69. package/logs/screenshot_2021_11_14_13_06_23_745Z_full_app_failed_1636895183745.png +0 -0
  70. package/logs/screenshot_2021_11_14_13_18_31_571Z_full_app_failed_1636895911571.png +0 -0
  71. package/logs/screenshot_2021_11_14_13_25_54_557Z_viewport_failed_1636896354557.png +0 -0
  72. package/logs/screenshot_2021_11_14_13_29_32_326Z_viewport_failed_1636896572326.png +0 -0
  73. package/logs/screenshot_2021_11_14_13_34_22_483Z_viewport_failed_1636896862483.png +0 -0
  74. package/logs/screenshot_2021_11_14_13_37_25_734Z_viewport_failed_1636897045734.png +0 -0
  75. package/logs/screenshot_2021_11_14_13_42_25_024Z_viewport_failed_1636897345024.png +0 -0
  76. package/logs/screenshot_2021_11_14_13_57_24_366Z_full_app_failed_1636898244366.png +0 -0
  77. package/logs/screenshot_2021_11_14_14_20_42_951Z_full_app_failed_1636899642951.png +0 -0
  78. package/logs/screenshot_2021_11_14_14_31_07_853Z_full_app_failed_1636900267853.png +0 -0
  79. package/logs/screenshot_2021_11_14_14_32_07_195Z_full_app_failed_1636900327195.png +0 -0
  80. package/logs/screenshot_2021_11_14_14_42_16_716Z_full_app_failed_1636900936716.png +0 -0
  81. package/logs/screenshot_2021_11_14_14_47_37_646Z_full_app_failed_1636901257646.png +0 -0
  82. package/logs/screenshot_2021_11_14_14_54_18_522Z_full_app_failed_1636901658522.png +0 -0
  83. package/logs/screenshot_2021_11_14_14_55_36_756Z_full_app_failed_1636901736756.png +0 -0
  84. package/logs/screenshot_2021_11_14_15_00_26_000Z_full_app_failed_1636902026000.png +0 -0
  85. package/logs/screenshot_2021_11_14_15_04_13_598Z_full_app_failed_1636902253598.png +0 -0
  86. package/logs/screenshot_2021_11_14_15_07_37_914Z_full_app_failed_1636902457914.png +0 -0
  87. package/logs/screenshot_2021_11_14_15_12_20_039Z_full_app_failed_1636902740039.png +0 -0
  88. package/logs/screenshot_2021_11_14_15_15_44_401Z_full_app_failed_1636902944401.png +0 -0
  89. package/logs/screenshot_2021_11_14_15_26_23_318Z_viewport_failed_1636903583318.png +0 -0
  90. package/src/calculate-screenshot-regions.js +0 -31
  91. package/src/screenshoter.js +0 -159
  92. package/test/fixtures/pattern/iPad_Air_portrait.png +0 -0
  93. package/test/fixtures/pattern/iPhone_5S_landscape.png +0 -0
  94. package/test/fixtures/pattern/iPhone_XR_perfecto_landscape.png +0 -0
  95. package/test/fixtures/pattern/iPhone_XS_Max_perfecto_landscape.png +0 -0
  96. package/test/fixtures/pattern/iPhone_XS_landscape.png +0 -0
  97. package/test/fixtures/pattern/iPhone_XS_portrait.png +0 -0
  98. package/test/fixtures/pattern/iPhone_X_perfecto_portrait.png +0 -0
@@ -1,25 +1,188 @@
1
1
  const utils = require('@applitools/utils')
2
- const makeTakeScreenshot = require('./take-screenshot')
2
+ const snippets = require('@applitools/snippets')
3
+ const findImagePattern = require('./find-image-pattern')
4
+ const makeImage = require('./image')
3
5
 
4
- async function takeViewportScreenshot({context, region, withStatusBar, wait, stabilization, debug = {}, logger}) {
5
- logger.verbose('Taking image of...')
6
+ function makeTakeViewportScreenshot(options) {
7
+ const {driver} = options
8
+ if (driver.isNative) {
9
+ return makeTakeNativeScreenshot(options)
10
+ } else if (driver.browserName === 'Firefox') {
11
+ try {
12
+ const browserVersion = Number.parseInt(driver.browserVersion, 10)
13
+ if (browserVersion >= 48 && browserVersion <= 72) {
14
+ // firefox between versions 48 and 72 takes current frame screenshot only
15
+ return makeTakeMainContextScreenshot(options)
16
+ }
17
+ } 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
+ }
26
+ }
27
+
28
+ return makeTakeDefaultScreenshot(options)
29
+ }
30
+
31
+ function makeTakeDefaultScreenshot({driver, stabilization = {}, debug, logger}) {
32
+ const calculateScaleRatio = makeCalculateScaleRatio({driver})
33
+ return async function takeScreenshot({name} = {}) {
34
+ logger.verbose('Taking screenshot...')
35
+ const image = makeImage(await driver.takeScreenshot())
36
+ await image.debug({...debug, name, suffix: 'original'})
37
+
38
+ if (stabilization.scale) image.scale(stabilization.scale)
39
+ else image.scale(await calculateScaleRatio(image.width))
40
+
41
+ if (stabilization.rotate) image.crop(stabilization.rotate)
42
+
43
+ if (stabilization.crop) image.crop(stabilization.crop)
44
+
45
+ return image
46
+ }
47
+ }
48
+
49
+ function makeTakeMainContextScreenshot({driver, stabilization = {}, debug, logger}) {
50
+ const calculateScaleRatio = makeCalculateScaleRatio({driver})
51
+ return async function takeScreenshot({name} = {}) {
52
+ logger.verbose('Taking screenshot...')
53
+ const originalContext = driver.currentContext
54
+ await driver.mainContext.focus()
55
+ const image = makeImage(await driver.takeScreenshot())
56
+ await originalContext.focus()
57
+ await image.debug({...debug, name, suffix: 'original'})
58
+
59
+ if (stabilization.scale) image.scale(stabilization.scale)
60
+ else image.scale(await calculateScaleRatio(image.width))
6
61
 
7
- const driver = context.driver
8
- const takeScreenshot = makeTakeScreenshot({logger, driver, stabilization, debug})
62
+ if (stabilization.rotate) image.rotate(stabilization.rotate)
63
+
64
+ if (stabilization.crop) image.crop(stabilization.crop)
65
+
66
+ return image
67
+ }
68
+ }
69
+
70
+ function makeTakeSafari11Screenshot({driver, stabilization = {}, debug, logger}) {
71
+ const calculateScaleRatio = makeCalculateScaleRatio({driver})
72
+ let viewportSize
73
+
74
+ return async function takeScreenshot({name} = {}) {
75
+ logger.verbose('Taking safari 11 driver screenshot...')
76
+ const image = makeImage(await driver.takeScreenshot())
77
+ await image.debug({...debug, name, suffix: 'original'})
78
+
79
+ if (stabilization.scale) image.scale(stabilization.scale)
80
+ else image.scale(await calculateScaleRatio(image.width))
81
+
82
+ if (stabilization.rotate) image.rotate(stabilization.rotate)
83
+
84
+ if (stabilization.crop) image.crop(stabilization.crop)
85
+ else {
86
+ if (!viewportSize) viewportSize = await driver.getViewportSize()
87
+ const viewportLocation = await driver.mainContext.execute(snippets.getElementScrollOffset, [])
88
+ image.crop(utils.geometry.region(viewportLocation, viewportSize))
89
+ }
90
+
91
+ return image
92
+ }
93
+ }
9
94
 
10
- await utils.general.sleep(wait)
95
+ function makeTakeMarkedScreenshot({driver, stabilization = {}, debug, logger}) {
96
+ const calculateScaleRatio = makeCalculateScaleRatio({driver})
97
+ let viewportRegion
98
+
99
+ return async function takeScreenshot({name} = {}) {
100
+ logger.verbose('Taking viewport screenshot (using markers)...')
101
+ const image = makeImage(await driver.takeScreenshot())
102
+ await image.debug({...debug, name, suffix: 'original'})
103
+
104
+ if (stabilization.scale) image.scale(stabilization.scale)
105
+ else image.scale(await calculateScaleRatio(image.width))
106
+
107
+ if (stabilization.rotate) image.rotate(stabilization.rotate)
108
+
109
+ if (stabilization.crop) image.crop(stabilization.crop)
110
+ else {
111
+ if (!viewportRegion) viewportRegion = await getViewportRegion()
112
+ if (viewportRegion) image.crop(viewportRegion)
113
+ }
114
+
115
+ await image.debug({...debug, name, suffix: 'viewport'})
116
+
117
+ return image
118
+ }
119
+
120
+ async function getViewportRegion() {
121
+ const marker = await driver.mainContext.execute(snippets.addPageMarker)
122
+ try {
123
+ const image = makeImage(await driver.takeScreenshot())
124
+
125
+ if (stabilization.rotate) await image.rotate(stabilization.rotate)
126
+
127
+ await image.debug({...debug, name: 'marker'})
128
+
129
+ const markerLocation = findImagePattern(await image.toObject(), {...marker, pixelRatio: driver.pixelRatio})
130
+ if (!markerLocation) return null
131
+
132
+ const viewportSize = await driver.getViewportSize()
133
+
134
+ return utils.geometry.region(utils.geometry.scale(markerLocation, 1 / driver.pixelRatio), viewportSize)
135
+ } finally {
136
+ await driver.mainContext.execute(snippets.cleanupPageMarker)
137
+ }
138
+ }
139
+ }
140
+
141
+ function makeTakeNativeScreenshot({driver, stabilization = {}, debug, logger}) {
142
+ return async function takeScreenshot({name, withStatusBar} = {}) {
143
+ logger.verbose('Taking native driver screenshot...')
144
+ const image = makeImage(await driver.takeScreenshot())
145
+ await image.debug({...debug, name, suffix: 'original'})
146
+
147
+ if (stabilization.scale) image.scale(stabilization.scale)
148
+ else image.scale(1 / driver.pixelRatio)
149
+
150
+ if (stabilization.rotate) image.rotate(stabilization.rotate)
151
+
152
+ if (stabilization.crop) image.crop(stabilization.crop)
153
+ else {
154
+ const viewportSize = await driver.getViewportSize()
155
+ const cropRegion = withStatusBar
156
+ ? {x: 0, y: 0, width: viewportSize.width, height: viewportSize.height + driver.statusBarHeight}
157
+ : {top: driver.statusBarHeight, bottom: driver.navigationBarHeight, left: 0, right: 0}
158
+ image.crop(cropRegion)
159
+ }
160
+
161
+ await image.debug({...debug, name, suffix: `viewport${withStatusBar ? '-with-statusbar' : ''}`})
162
+
163
+ return image
164
+ }
165
+ }
11
166
 
12
- const image = await takeScreenshot({withStatusBar})
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
+ }
13
182
 
14
- if (region) {
15
- const cropRegion = await driver.getRegionInViewport(context, region)
16
- if (utils.geometry.isEmpty(cropRegion)) throw new Error('Screenshot region is out of viewport')
17
- image.crop(cropRegion)
18
- await image.debug({path: debug.path, suffix: 'region'})
19
- return {image, region: cropRegion}
20
- } else {
21
- return {image, region: utils.geometry.region({x: 0, y: 0}, image.size)}
183
+ const scaledImageWidth = Math.round(imageWidth / driver.pixelRatio)
184
+ return viewportWidth / scaledImageWidth / driver.pixelRatio
22
185
  }
23
186
  }
24
187
 
25
- module.exports = takeViewportScreenshot
188
+ module.exports = makeTakeViewportScreenshot
@@ -3,7 +3,7 @@ const pixelmatch = require('pixelmatch')
3
3
  const {Driver} = require('@applitools/driver')
4
4
  const spec = require('@applitools/spec-driver-webdriverio')
5
5
  const makeImage = require('../../src/image')
6
- const screenshoter = require('../../index')
6
+ const takeScreenshot = require('../../index')
7
7
 
8
8
  const env = {
9
9
  android: {
@@ -31,10 +31,20 @@ const env = {
31
31
  appiumVersion: '1.20.2',
32
32
  deviceName: 'Google Pixel 3a XL GoogleAPI Emulator',
33
33
  automationName: 'uiautomator2',
34
- app: 'https://applitools.jfrog.io/artifactory/Examples/androidx/1.2.0/app_androidx.apk',
34
+ app: 'https://applitools.jfrog.io/artifactory/Examples/androidx/1.3.1/app_androidx.apk',
35
35
  username: process.env.SAUCE_USERNAME,
36
36
  accessKey: process.env.SAUCE_ACCESS_KEY,
37
37
  },
38
+
39
+ // url: 'http://0.0.0.0:4723/wd/hub',
40
+ // capabilities: {
41
+ // deviceName: 'Google Pixel 3a XL',
42
+ // platformName: 'Android',
43
+ // platformVersion: '10.0',
44
+ // automationName: 'uiautomator2',
45
+ // avd: 'Pixel_3a_XL',
46
+ // app: 'https://applitools.jfrog.io/artifactory/Examples/androidx/1.3.3/app_androidx.apk',
47
+ // },
38
48
  },
39
49
  }
40
50
 
@@ -122,6 +132,14 @@ describe('screenshoter', () => {
122
132
  return fullApp({type: 'recycler', x: true})
123
133
  })
124
134
 
135
+ it('take full app screenshot (collapsing layout)', () => {
136
+ return fullApp({type: 'collapsing', x: true})
137
+ })
138
+
139
+ it.skip('take full app screenshot (overlapped status bar)', () => {
140
+ return fullApp({type: 'overlapped', x: true})
141
+ })
142
+
125
143
  it('take full element screenshot', () => {
126
144
  return fullElement()
127
145
  })
@@ -130,7 +148,7 @@ describe('screenshoter', () => {
130
148
  async function app(options = {}) {
131
149
  const expectedPath = `./test/fixtures/android/app${options.withStatusBar ? '-statusbar' : ''}.png`
132
150
 
133
- const screenshot = await screenshoter({logger, driver, wait: 1500, ...options})
151
+ const screenshot = await takeScreenshot({logger, driver, wait: 1500, ...options})
134
152
  try {
135
153
  if (options.withStatusBar) await sanitizeStatusBar(screenshot.image)
136
154
  const actual = await screenshot.image.toObject()
@@ -142,8 +160,15 @@ describe('screenshoter', () => {
142
160
  }
143
161
  }
144
162
  async function fullApp({type, x, ...options} = {}) {
145
- let buttonSelector, expectedPath
146
- if (type === 'recycler') {
163
+ let buttonSelector, expectedPath, scrollingElementSelector
164
+ if (type === 'overlapped') {
165
+ buttonSelector = {type: 'id', selector: 'btn_recycler_view_under_status_bar_activity'}
166
+ expectedPath = `./test/fixtures/android/x-app-fully-overlapped${options.withStatusBar ? '-statusbar' : ''}.png`
167
+ } else if (type === 'collapsing') {
168
+ buttonSelector = {type: 'id', selector: 'btn_recycler_view_nested_collapsing'}
169
+ scrollingElementSelector = {type: 'id', selector: 'recyclerView'}
170
+ expectedPath = `./test/fixtures/android/x-app-fully-collapsing${options.withStatusBar ? '-statusbar' : ''}.png`
171
+ } else if (type === 'recycler') {
147
172
  if (x) {
148
173
  buttonSelector = {type: 'id', selector: 'btn_recycler_view_activity'}
149
174
  expectedPath = `./test/fixtures/android/x-app-fully-recycler${options.withStatusBar ? '-statusbar' : ''}.png`
@@ -152,7 +177,7 @@ describe('screenshoter', () => {
152
177
  expectedPath = `./test/fixtures/android/app-fully-recycler${options.withStatusBar ? '-statusbar' : ''}.png`
153
178
  }
154
179
  } else if (type === 'non-scrollable') {
155
- buttonSelector = {type: 'id', selector: 'btn_edit_text'}
180
+ buttonSelector = {type: 'id', selector: 'btn_activity_as_dialog'}
156
181
  expectedPath = `./test/fixtures/android/app-fully-non-scrollable${options.withStatusBar ? '-statusbar' : ''}.png`
157
182
  } else {
158
183
  buttonSelector = {type: 'id', selector: 'btn_scroll_view_footer_header'}
@@ -162,7 +187,13 @@ describe('screenshoter', () => {
162
187
  const button = await driver.element(buttonSelector)
163
188
  await button.click()
164
189
 
165
- const screenshot = await screenshoter({
190
+ await driver.init()
191
+
192
+ if (scrollingElementSelector) {
193
+ await driver.currentContext.setScrollingElement(scrollingElementSelector)
194
+ }
195
+
196
+ const screenshot = await takeScreenshot({
166
197
  logger,
167
198
  driver,
168
199
  fully: true,
@@ -182,7 +213,7 @@ describe('screenshoter', () => {
182
213
  }
183
214
  }
184
215
  async function region(options) {
185
- const screenshot = await screenshoter({
216
+ const screenshot = await takeScreenshot({
186
217
  logger,
187
218
  driver,
188
219
  region: {x: 30, y: 500, height: 100, width: 200},
@@ -199,7 +230,7 @@ describe('screenshoter', () => {
199
230
  }
200
231
  }
201
232
  async function fullRegion(options) {
202
- const screenshot = await screenshoter({
233
+ const screenshot = await takeScreenshot({
203
234
  logger,
204
235
  driver,
205
236
  region: {x: 30, y: 10, height: 700, width: 200},
@@ -217,7 +248,7 @@ describe('screenshoter', () => {
217
248
  }
218
249
  }
219
250
  async function element(options) {
220
- const screenshot = await screenshoter({
251
+ const screenshot = await takeScreenshot({
221
252
  logger,
222
253
  driver,
223
254
  region: {type: 'id', selector: 'btn_recycler_view'},
@@ -240,7 +271,7 @@ describe('screenshoter', () => {
240
271
  })
241
272
  await button.click()
242
273
 
243
- const screenshot = await screenshoter({
274
+ const screenshot = await takeScreenshot({
244
275
  logger,
245
276
  driver,
246
277
  region: {type: 'id', selector: 'recyclerView'},
@@ -4,7 +4,7 @@ const {Driver} = require('@applitools/driver')
4
4
  const utils = require('@applitools/utils')
5
5
  const spec = require('@applitools/spec-driver-webdriverio')
6
6
  const makeImage = require('../../src/image')
7
- const screenshoter = require('../../index')
7
+ const takeScreenshot = require('../../index')
8
8
 
9
9
  describe.skip('external tests', () => {
10
10
  const logger = {log: () => {}, warn: () => {}, error: () => {}, verbose: () => {}}
@@ -14,7 +14,7 @@ describe.skip('external tests', () => {
14
14
  await destroyBrowser()
15
15
  })
16
16
 
17
- it('AGL - full app screenshot of the view with animated scroll', async () => {
17
+ it('AGL Android - full app screenshot of the view with animated scroll', async () => {
18
18
  const expectedPath = `./test/fixtures/external/agl.png`
19
19
 
20
20
  ;[browser, destroyBrowser] = await spec.build({
@@ -23,7 +23,7 @@ describe.skip('external tests', () => {
23
23
  platformName: 'android',
24
24
  'appium:platformVersion': '11.0',
25
25
  'appium:deviceName': 'Google Pixel 5',
26
- 'appium:app': 'android_agl_app',
26
+ 'appium:app': 'agl_app_android',
27
27
  'bstack:options': {
28
28
  userName: process.env.BROWSERSTACK_USERNAME,
29
29
  accessKey: process.env.BROWSERSTACK_ACCESS_KEY,
@@ -41,18 +41,17 @@ describe.skip('external tests', () => {
41
41
  await utils.general.sleep(8000)
42
42
  const signinBtn = await browser.$('//android.widget.Button[@text="SIGN IN"]')
43
43
  await signinBtn.click()
44
- await utils.general.sleep(8000)
44
+ await utils.general.sleep(10000)
45
45
  const emailInput = await browser.$('//android.widget.EditText')
46
46
  await emailInput.setValue('daniel.martin@toro.com')
47
47
  const nxtBtn = await browser.$('//android.widget.Button[@text="NEXT"]')
48
48
  await nxtBtn.click()
49
- await utils.general.sleep(8000)
49
+ await utils.general.sleep(10000)
50
50
  const passwordInput = await browser.$('//android.widget.EditText[@password="true"]')
51
51
  await passwordInput.setValue('Welcome@1')
52
52
  const loginBtn = await browser.$('//android.widget.Button[@text="LOGIN"]')
53
53
  await loginBtn.click()
54
54
  await utils.general.sleep(18000)
55
-
56
55
  const skipBtn = await browser.$('//android.widget.Button[@text="SKIP"]')
57
56
  await skipBtn.click()
58
57
  await utils.general.sleep(5000)
@@ -60,11 +59,9 @@ describe.skip('external tests', () => {
60
59
  await finishBtn.click()
61
60
  await utils.general.sleep(15000)
62
61
 
63
- // const billingBtn = await browser.$('//android.widget.FrameLayout[@content-desc="Billing"]')
64
- // await billingBtn.click()
65
- // await utils.general.sleep(25000)
62
+ await driver.init()
66
63
 
67
- const screenshot = await screenshoter({
64
+ const screenshot = await takeScreenshot({
68
65
  logger,
69
66
  driver,
70
67
  fully: true,
@@ -81,4 +78,78 @@ describe.skip('external tests', () => {
81
78
  throw err
82
79
  }
83
80
  })
81
+
82
+ it('AGL iOS - full app screenshot of the view with animated scroll', async () => {
83
+ const expectedPath = `./test/fixtures/external/agl.png`
84
+ ;[browser, destroyBrowser] = await spec.build({
85
+ url: 'https://hub.browserstack.com/wd/hub',
86
+ capabilities: {
87
+ platformName: 'ios',
88
+ 'appium:platformVersion': '14',
89
+ 'appium:deviceName': 'iPhone 12',
90
+ 'appium:app': 'agl_app',
91
+ 'appium:processArguments': `{
92
+ "args": [
93
+ "-dev.environment", "qtrtest",
94
+ "-dev.clear.keychain", "YES",
95
+ "-dev.keychain.shared", "NO",
96
+ "-user.lastQuickTourVersionCompleted", "0"
97
+ ],
98
+ "env": {
99
+ "DYLD_INSERT_LIBRARIES": "@executable_path/Frameworks/EyesiOSHelper.xcframework/ios-arm64/EyesiOSHelper.framework/EyesiOSHelper"
100
+ }
101
+ }`,
102
+ 'bstack:options': {
103
+ userName: process.env.BROWSERSTACK_USERNAME,
104
+ accessKey: process.env.BROWSERSTACK_ACCESS_KEY,
105
+ },
106
+ },
107
+ })
108
+
109
+ await browser.closeApp()
110
+ await browser.launchApp()
111
+ const driver = await new Driver({driver: browser, spec, logger}).init()
112
+
113
+ await browser.acceptAlert()
114
+
115
+ await utils.general.sleep(8000)
116
+ const signinBtn = await browser.$('//XCUIElementTypeButton[@name="SIGN IN"]')
117
+ await signinBtn.click()
118
+ await utils.general.sleep(8000)
119
+ const emailInput = await browser.$('//XCUIElementTypeTextField')
120
+ await emailInput.setValue('daniel.martin@toro.com')
121
+ const nxtBtn = await browser.$('//XCUIElementTypeButton[@name="NEXT"]')
122
+ await nxtBtn.click()
123
+ await utils.general.sleep(8000)
124
+ const passwordInput = await browser.$('//XCUIElementTypeSecureTextField')
125
+ await passwordInput.setValue('Welcome@1')
126
+ const loginBtn = await browser.$('//XCUIElementTypeButton[@name="LOGIN"]')
127
+ await loginBtn.click()
128
+ await utils.general.sleep(18000)
129
+ const skipBtn = await browser.$('//XCUIElementTypeButton[@name="Skip"]')
130
+ await skipBtn.click()
131
+ await utils.general.sleep(5000)
132
+ const finishBtn = await browser.$('//XCUIElementTypeButton[@name="Finish"]')
133
+ await finishBtn.click()
134
+ await utils.general.sleep(20000)
135
+
136
+ await driver.init()
137
+
138
+ const screenshot = await takeScreenshot({
139
+ logger,
140
+ driver,
141
+ fully: true,
142
+ framed: true,
143
+ wait: 1500,
144
+ // debug: {path: './'},
145
+ })
146
+ try {
147
+ const actual = await screenshot.image.toObject()
148
+ const expected = await makeImage(expectedPath).toObject()
149
+ assert.strictEqual(pixelmatch(actual.data, expected.data, null, expected.width, expected.height), 0)
150
+ } catch (err) {
151
+ await screenshot.image.debug({path: './logs', name: 'viewport_failed', suffix: Date.now()})
152
+ throw err
153
+ }
154
+ })
84
155
  })
@@ -3,7 +3,7 @@ const pixelmatch = require('pixelmatch')
3
3
  const {Driver} = require('@applitools/driver')
4
4
  const spec = require('@applitools/spec-driver-webdriverio')
5
5
  const makeImage = require('../../src/image')
6
- const screenshoter = require('../../index')
6
+ const takeScreenshot = require('../../index')
7
7
 
8
8
  const env = {
9
9
  url: 'https://ondemand.saucelabs.com/wd/hub',
@@ -14,10 +14,18 @@ const env = {
14
14
  platformVersion: '13.4',
15
15
  appiumVersion: '1.19.2',
16
16
  automationName: 'XCUITest',
17
- app: 'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.5/app/IOSTestApp-1.5.zip',
17
+ app: 'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.8/app/IOSTestApp.zip',
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
+ // deviceName: 'iPhone 11 Pro',
25
+ // platformName: 'iOS',
26
+ // platformVersion: '14.5',
27
+ // app: '/Users/kyrylo/Downloads/IOSTestApp.zip',
28
+ // },
21
29
  }
22
30
 
23
31
  describe('screenshoter ios', () => {
@@ -77,6 +85,22 @@ describe('screenshoter ios', () => {
77
85
  return fullApp({type: 'collection'})
78
86
  })
79
87
 
88
+ it('take full app screenshot (table view with collapsing header)', () => {
89
+ return fullApp({type: 'collapsing'})
90
+ })
91
+
92
+ it('take full app screenshot (collection view with overlapped status bar)', () => {
93
+ return fullApp({type: 'overlapped'})
94
+ })
95
+
96
+ it('take full app screenshot with status bar (collection view with overlapped status bar)', () => {
97
+ return fullApp({type: 'overlapped', withStatusBar: true})
98
+ })
99
+
100
+ it('take full app screenshot (collection view with superview)', () => {
101
+ return fullApp({type: 'superview'})
102
+ })
103
+
80
104
  it('take region screenshot', () => {
81
105
  return region()
82
106
  })
@@ -96,7 +120,7 @@ describe('screenshoter ios', () => {
96
120
  async function app(options = {}) {
97
121
  const expectedPath = `./test/fixtures/ios/app${options.withStatusBar ? '-statusbar' : ''}.png`
98
122
 
99
- const screenshot = await screenshoter({logger, driver, ...options})
123
+ const screenshot = await takeScreenshot({logger, driver, ...options})
100
124
  try {
101
125
  if (options.withStatusBar) await sanitizeStatusBar(screenshot.image)
102
126
  const actual = await screenshot.image.toObject()
@@ -108,8 +132,19 @@ describe('screenshoter ios', () => {
108
132
  }
109
133
  }
110
134
  async function fullApp({type, ...options} = {}) {
111
- let buttonSelector, expectedPath
112
- if (type === 'collection') {
135
+ let buttonSelector, expectedPath, overlap
136
+ if (type === 'superview') {
137
+ overlap = {top: 200}
138
+ buttonSelector = {type: 'accessibility id', selector: 'Bottom to superview'}
139
+ expectedPath = `./test/fixtures/ios/app-fully-superview${options.withStatusBar ? '-statusbar' : ''}.png`
140
+ } else if (type === 'overlapped') {
141
+ overlap = {top: 200}
142
+ buttonSelector = {type: 'accessibility id', selector: 'Bottom to safe area'}
143
+ expectedPath = `./test/fixtures/ios/app-fully-overlapped${options.withStatusBar ? '-statusbar' : ''}.png`
144
+ } else if (type === 'collapsing') {
145
+ buttonSelector = {type: 'accessibility id', selector: 'Table view with stretchable header'}
146
+ expectedPath = `./test/fixtures/ios/app-fully-collapsing${options.withStatusBar ? '-statusbar' : ''}.png`
147
+ } else if (type === 'collection') {
113
148
  buttonSelector = {type: 'accessibility id', selector: 'Collection view'}
114
149
  expectedPath = `./test/fixtures/ios/app-fully-collection${options.withStatusBar ? '-statusbar' : ''}.png`
115
150
  } else if (type === 'table') {
@@ -123,13 +158,16 @@ describe('screenshoter ios', () => {
123
158
  const button = await driver.element(buttonSelector)
124
159
  await button.click()
125
160
 
126
- const screenshot = await screenshoter({
161
+ await driver.init()
162
+
163
+ const screenshot = await takeScreenshot({
127
164
  logger,
128
165
  driver,
129
166
  fully: true,
130
167
  framed: true,
131
168
  scrollingMode: 'scroll',
132
169
  wait: 1500,
170
+ overlap: {top: 10, bottom: 50, ...overlap},
133
171
  ...options,
134
172
  })
135
173
  try {
@@ -143,7 +181,7 @@ describe('screenshoter ios', () => {
143
181
  }
144
182
  }
145
183
  async function region(options) {
146
- const screenshot = await screenshoter({
184
+ const screenshot = await takeScreenshot({
147
185
  logger,
148
186
  driver,
149
187
  region: {x: 30, y: 500, height: 100, width: 200},
@@ -161,7 +199,7 @@ describe('screenshoter ios', () => {
161
199
  }
162
200
  }
163
201
  async function fullRegion(options) {
164
- const screenshot = await screenshoter({
202
+ const screenshot = await takeScreenshot({
165
203
  logger,
166
204
  driver,
167
205
  region: {x: 30, y: 10, height: 700, width: 200},
@@ -179,7 +217,7 @@ describe('screenshoter ios', () => {
179
217
  }
180
218
  }
181
219
  async function element(options) {
182
- const screenshot = await screenshoter({
220
+ const screenshot = await takeScreenshot({
183
221
  logger,
184
222
  driver,
185
223
  region: {type: 'accessibility id', selector: 'Table view'},
@@ -202,7 +240,7 @@ describe('screenshoter ios', () => {
202
240
  })
203
241
  await button.click()
204
242
 
205
- const screenshot = await screenshoter({
243
+ const screenshot = await takeScreenshot({
206
244
  logger,
207
245
  driver,
208
246
  region: {type: 'xpath', selector: '//XCUIElementTypeTable[1]'},
@@ -2,7 +2,7 @@ const assert = require('assert')
2
2
  const pixelmatch = require('pixelmatch')
3
3
  const {Driver} = require('@applitools/driver')
4
4
  const spec = require('@applitools/spec-driver-webdriverio')
5
- const screenshoter = require('../../index')
5
+ const takeScreenshot = require('../../index')
6
6
  const makeImage = require('../../src/image')
7
7
 
8
8
  const env = {
@@ -20,8 +20,6 @@ const env = {
20
20
  },
21
21
  }
22
22
 
23
- // TODO add tests for page without viewport meta tag
24
-
25
23
  describe('screenshoter web ios', () => {
26
24
  const logger = {log: () => {}, warn: () => {}, error: () => {}, verbose: () => {}}
27
25
  let driver, browser, destroyBrowser
@@ -49,7 +47,7 @@ describe('screenshoter web ios', () => {
49
47
  })
50
48
 
51
49
  async function viewport(options) {
52
- const screenshot = await screenshoter({logger, driver, ...options})
50
+ const screenshot = await takeScreenshot({logger, driver, ...options})
53
51
  try {
54
52
  const actual = await screenshot.image.toObject()
55
53
  const expected = await makeImage('./test/fixtures/web-ios/page.png').toObject()
@@ -61,7 +59,7 @@ describe('screenshoter web ios', () => {
61
59
  }
62
60
 
63
61
  async function fullPage(options) {
64
- const screenshot = await screenshoter({logger, driver, fully: true, ...options})
62
+ const screenshot = await takeScreenshot({logger, driver, fully: true, ...options})
65
63
  try {
66
64
  const actual = await screenshot.image.toObject()
67
65
  const expected = await makeImage('./test/fixtures/web-ios/page-fully.png').toObject()