@applitools/screenshoter 3.2.6 → 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.
- package/.bongo/dry-run/package-lock.json +53 -10
- package/.bongo/dry-run/package.json +5 -0
- package/.bongo/dry-run.tgz +0 -0
- package/CHANGELOG.md +18 -0
- package/index.js +2 -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
- package/package.json +11 -7
- package/src/find-image-pattern.js +10 -38
- package/src/image.js +107 -64
- package/src/take-screenshot.js +136 -160
- package/src/take-simple-screenshot.js +25 -0
- package/src/take-stitched-screenshot.js +34 -40
- package/src/take-viewport-screenshot.js +179 -16
- package/test/e2e/android.spec.js +77 -13
- package/test/e2e/external.spec.js +155 -0
- package/test/e2e/ios.spec.js +64 -11
- package/test/e2e/web-ios.spec.js +19 -6
- package/test/e2e/web.spec.js +33 -21
- package/test/fixtures/android/app-fully-non-scrollable.png +0 -0
- package/test/fixtures/android/app-fully-recycler.png +0 -0
- package/test/fixtures/android/app-fully-scroll-statusbar.png +0 -0
- package/test/fixtures/android/app-fully-scroll.png +0 -0
- package/test/fixtures/android/app-statusbar.png +0 -0
- package/test/fixtures/android/region.png +0 -0
- package/test/fixtures/android/x-app-fully-collapsing.png +0 -0
- package/test/fixtures/android/x-app-fully-recycler.png +0 -0
- package/test/fixtures/android/x-element-fully.png +0 -0
- package/test/fixtures/external/agl.png +0 -0
- package/test/fixtures/image/{house.combined-higher-wider.png → house.framed-higher-wider.png} +0 -0
- package/test/fixtures/image/{house.combined-higher.png → house.framed-higher.png} +0 -0
- package/test/fixtures/image/house.framed-shorter-thinner.png +0 -0
- package/test/fixtures/image/{house.combined-wider.png → house.framed-wider.png} +0 -0
- 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/pattern/iPad_5th_landscape.png +0 -0
- package/test/fixtures/pattern/iPad_5th_portrait.png +0 -0
- package/test/fixtures/pattern/iPad_9th_landscape.png +0 -0
- package/test/fixtures/pattern/iPad_9th_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_11_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_11_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_13_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_13_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_SE_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_SE_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_XS_portrait_noviewport.png +0 -0
- package/test/fixtures/web/frame-fully.png +0 -0
- package/test/fixtures/web/frame.png +0 -0
- package/test/fixtures/web/inner-element-fully.png +0 -0
- package/test/fixtures/web/inner-element.png +0 -0
- package/test/fixtures/web/inner-region-fully.png +0 -0
- package/test/fixtures/web/inner-region.png +0 -0
- package/test/fixtures/web/page-fully.png +0 -0
- package/test/fixtures/web/page.png +0 -0
- package/test/fixtures/web/region-fully.png +0 -0
- package/test/fixtures/web/region.png +0 -0
- package/test/fixtures/web-ios/page-fully.png +0 -0
- package/test/fixtures/web-ios/page.png +0 -0
- package/test/it/find-pattern.spec.js +16 -11
- package/test/it/image.spec.js +42 -15
- package/logs/screenshot_2021_10_11_13_22_59_543Z_viewport_failed_1633958579543.png +0 -0
- package/logs/screenshot_2021_10_11_13_28_41_375Z_viewport_failed_1633958921375.png +0 -0
- package/logs/screenshot_2021_10_11_13_36_46_414Z_viewport_failed_1633959406414.png +0 -0
- package/logs/screenshot_2021_10_11_13_54_21_340Z_viewport_failed_1633960461340.png +0 -0
- package/logs/screenshot_2021_10_11_14_32_39_581Z_full_app_failed_1633962759581.png +0 -0
- package/src/calculate-screenshot-regions.js +0 -31
- package/src/screenshoter.js +0 -158
- package/test/fixtures/pattern/iPad_Air_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_5S_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_XR_perfecto_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_XS_Max_perfecto_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_XS_landscape.png +0 -0
- package/test/fixtures/pattern/iPhone_XS_portrait.png +0 -0
- package/test/fixtures/pattern/iPhone_X_perfecto_portrait.png +0 -0
- package/test/util/spec-driver.js +0 -288
|
@@ -1,25 +1,188 @@
|
|
|
1
1
|
const utils = require('@applitools/utils')
|
|
2
|
-
const
|
|
2
|
+
const snippets = require('@applitools/snippets')
|
|
3
|
+
const findImagePattern = require('./find-image-pattern')
|
|
4
|
+
const makeImage = require('./image')
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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 =
|
|
188
|
+
module.exports = makeTakeViewportScreenshot
|
package/test/e2e/android.spec.js
CHANGED
|
@@ -1,9 +1,52 @@
|
|
|
1
1
|
const assert = require('assert')
|
|
2
2
|
const pixelmatch = require('pixelmatch')
|
|
3
3
|
const {Driver} = require('@applitools/driver')
|
|
4
|
-
const spec = require('
|
|
4
|
+
const spec = require('@applitools/spec-driver-webdriverio')
|
|
5
5
|
const makeImage = require('../../src/image')
|
|
6
|
-
const
|
|
6
|
+
const takeScreenshot = require('../../index')
|
|
7
|
+
|
|
8
|
+
const env = {
|
|
9
|
+
android: {
|
|
10
|
+
url: 'https://ondemand.saucelabs.com/wd/hub',
|
|
11
|
+
capabilities: {
|
|
12
|
+
name: 'Android Screenshoter Test',
|
|
13
|
+
browserName: '',
|
|
14
|
+
platformName: 'Android',
|
|
15
|
+
platformVersion: '7.0',
|
|
16
|
+
appiumVersion: '1.20.2',
|
|
17
|
+
deviceName: 'Samsung Galaxy S8 FHD GoogleAPI Emulator',
|
|
18
|
+
automationName: 'uiautomator2',
|
|
19
|
+
app: 'https://applitools.jfrog.io/artifactory/Examples/android/1.3/app-debug.apk',
|
|
20
|
+
username: process.env.SAUCE_USERNAME,
|
|
21
|
+
accessKey: process.env.SAUCE_ACCESS_KEY,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
androidx: {
|
|
25
|
+
url: 'https://ondemand.saucelabs.com/wd/hub',
|
|
26
|
+
capabilities: {
|
|
27
|
+
name: 'AndroidX Screenshoter Test',
|
|
28
|
+
browserName: '',
|
|
29
|
+
platformName: 'Android',
|
|
30
|
+
platformVersion: '10.0',
|
|
31
|
+
appiumVersion: '1.20.2',
|
|
32
|
+
deviceName: 'Google Pixel 3a XL GoogleAPI Emulator',
|
|
33
|
+
automationName: 'uiautomator2',
|
|
34
|
+
app: 'https://applitools.jfrog.io/artifactory/Examples/androidx/1.3.1/app_androidx.apk',
|
|
35
|
+
username: process.env.SAUCE_USERNAME,
|
|
36
|
+
accessKey: process.env.SAUCE_ACCESS_KEY,
|
|
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
|
+
// },
|
|
48
|
+
},
|
|
49
|
+
}
|
|
7
50
|
|
|
8
51
|
describe('screenshoter', () => {
|
|
9
52
|
const logger = {log: () => {}, warn: () => {}, error: () => {}, verbose: () => {}}
|
|
@@ -20,7 +63,7 @@ describe('screenshoter', () => {
|
|
|
20
63
|
|
|
21
64
|
describe('android app', () => {
|
|
22
65
|
before(async () => {
|
|
23
|
-
;[browser, destroyBrowser] = await spec.build(
|
|
66
|
+
;[browser, destroyBrowser] = await spec.build(env.android)
|
|
24
67
|
})
|
|
25
68
|
|
|
26
69
|
after(async () => {
|
|
@@ -72,7 +115,7 @@ describe('screenshoter', () => {
|
|
|
72
115
|
|
|
73
116
|
describe('androidx app', () => {
|
|
74
117
|
before(async () => {
|
|
75
|
-
;[browser, destroyBrowser] = await spec.build(
|
|
118
|
+
;[browser, destroyBrowser] = await spec.build(env.androidx)
|
|
76
119
|
})
|
|
77
120
|
|
|
78
121
|
after(async () => {
|
|
@@ -89,6 +132,14 @@ describe('screenshoter', () => {
|
|
|
89
132
|
return fullApp({type: 'recycler', x: true})
|
|
90
133
|
})
|
|
91
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
|
+
|
|
92
143
|
it('take full element screenshot', () => {
|
|
93
144
|
return fullElement()
|
|
94
145
|
})
|
|
@@ -97,7 +148,7 @@ describe('screenshoter', () => {
|
|
|
97
148
|
async function app(options = {}) {
|
|
98
149
|
const expectedPath = `./test/fixtures/android/app${options.withStatusBar ? '-statusbar' : ''}.png`
|
|
99
150
|
|
|
100
|
-
const screenshot = await
|
|
151
|
+
const screenshot = await takeScreenshot({logger, driver, wait: 1500, ...options})
|
|
101
152
|
try {
|
|
102
153
|
if (options.withStatusBar) await sanitizeStatusBar(screenshot.image)
|
|
103
154
|
const actual = await screenshot.image.toObject()
|
|
@@ -109,8 +160,15 @@ describe('screenshoter', () => {
|
|
|
109
160
|
}
|
|
110
161
|
}
|
|
111
162
|
async function fullApp({type, x, ...options} = {}) {
|
|
112
|
-
let buttonSelector, expectedPath
|
|
113
|
-
if (type === '
|
|
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') {
|
|
114
172
|
if (x) {
|
|
115
173
|
buttonSelector = {type: 'id', selector: 'btn_recycler_view_activity'}
|
|
116
174
|
expectedPath = `./test/fixtures/android/x-app-fully-recycler${options.withStatusBar ? '-statusbar' : ''}.png`
|
|
@@ -119,7 +177,7 @@ describe('screenshoter', () => {
|
|
|
119
177
|
expectedPath = `./test/fixtures/android/app-fully-recycler${options.withStatusBar ? '-statusbar' : ''}.png`
|
|
120
178
|
}
|
|
121
179
|
} else if (type === 'non-scrollable') {
|
|
122
|
-
buttonSelector = {type: 'id', selector: '
|
|
180
|
+
buttonSelector = {type: 'id', selector: 'btn_activity_as_dialog'}
|
|
123
181
|
expectedPath = `./test/fixtures/android/app-fully-non-scrollable${options.withStatusBar ? '-statusbar' : ''}.png`
|
|
124
182
|
} else {
|
|
125
183
|
buttonSelector = {type: 'id', selector: 'btn_scroll_view_footer_header'}
|
|
@@ -129,7 +187,13 @@ describe('screenshoter', () => {
|
|
|
129
187
|
const button = await driver.element(buttonSelector)
|
|
130
188
|
await button.click()
|
|
131
189
|
|
|
132
|
-
|
|
190
|
+
await driver.init()
|
|
191
|
+
|
|
192
|
+
if (scrollingElementSelector) {
|
|
193
|
+
await driver.currentContext.setScrollingElement(scrollingElementSelector)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const screenshot = await takeScreenshot({
|
|
133
197
|
logger,
|
|
134
198
|
driver,
|
|
135
199
|
fully: true,
|
|
@@ -149,7 +213,7 @@ describe('screenshoter', () => {
|
|
|
149
213
|
}
|
|
150
214
|
}
|
|
151
215
|
async function region(options) {
|
|
152
|
-
const screenshot = await
|
|
216
|
+
const screenshot = await takeScreenshot({
|
|
153
217
|
logger,
|
|
154
218
|
driver,
|
|
155
219
|
region: {x: 30, y: 500, height: 100, width: 200},
|
|
@@ -166,7 +230,7 @@ describe('screenshoter', () => {
|
|
|
166
230
|
}
|
|
167
231
|
}
|
|
168
232
|
async function fullRegion(options) {
|
|
169
|
-
const screenshot = await
|
|
233
|
+
const screenshot = await takeScreenshot({
|
|
170
234
|
logger,
|
|
171
235
|
driver,
|
|
172
236
|
region: {x: 30, y: 10, height: 700, width: 200},
|
|
@@ -184,7 +248,7 @@ describe('screenshoter', () => {
|
|
|
184
248
|
}
|
|
185
249
|
}
|
|
186
250
|
async function element(options) {
|
|
187
|
-
const screenshot = await
|
|
251
|
+
const screenshot = await takeScreenshot({
|
|
188
252
|
logger,
|
|
189
253
|
driver,
|
|
190
254
|
region: {type: 'id', selector: 'btn_recycler_view'},
|
|
@@ -207,7 +271,7 @@ describe('screenshoter', () => {
|
|
|
207
271
|
})
|
|
208
272
|
await button.click()
|
|
209
273
|
|
|
210
|
-
const screenshot = await
|
|
274
|
+
const screenshot = await takeScreenshot({
|
|
211
275
|
logger,
|
|
212
276
|
driver,
|
|
213
277
|
region: {type: 'id', selector: 'recyclerView'},
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const assert = require('assert')
|
|
2
|
+
const pixelmatch = require('pixelmatch')
|
|
3
|
+
const {Driver} = require('@applitools/driver')
|
|
4
|
+
const utils = require('@applitools/utils')
|
|
5
|
+
const spec = require('@applitools/spec-driver-webdriverio')
|
|
6
|
+
const makeImage = require('../../src/image')
|
|
7
|
+
const takeScreenshot = require('../../index')
|
|
8
|
+
|
|
9
|
+
describe.skip('external tests', () => {
|
|
10
|
+
const logger = {log: () => {}, warn: () => {}, error: () => {}, verbose: () => {}}
|
|
11
|
+
let browser, destroyBrowser
|
|
12
|
+
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
await destroyBrowser()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('AGL Android - full app screenshot of the view with animated scroll', async () => {
|
|
18
|
+
const expectedPath = `./test/fixtures/external/agl.png`
|
|
19
|
+
|
|
20
|
+
;[browser, destroyBrowser] = await spec.build({
|
|
21
|
+
url: 'https://hub.browserstack.com/wd/hub',
|
|
22
|
+
capabilities: {
|
|
23
|
+
platformName: 'android',
|
|
24
|
+
'appium:platformVersion': '11.0',
|
|
25
|
+
'appium:deviceName': 'Google Pixel 5',
|
|
26
|
+
'appium:app': 'agl_app_android',
|
|
27
|
+
'bstack:options': {
|
|
28
|
+
userName: process.env.BROWSERSTACK_USERNAME,
|
|
29
|
+
accessKey: process.env.BROWSERSTACK_ACCESS_KEY,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
await browser.closeApp()
|
|
35
|
+
await browser.launchApp()
|
|
36
|
+
const driver = await new Driver({driver: browser, spec, logger}).init()
|
|
37
|
+
|
|
38
|
+
const saveBtn = await browser.$('//android.widget.Button')
|
|
39
|
+
await saveBtn.click()
|
|
40
|
+
|
|
41
|
+
await utils.general.sleep(8000)
|
|
42
|
+
const signinBtn = await browser.$('//android.widget.Button[@text="SIGN IN"]')
|
|
43
|
+
await signinBtn.click()
|
|
44
|
+
await utils.general.sleep(10000)
|
|
45
|
+
const emailInput = await browser.$('//android.widget.EditText')
|
|
46
|
+
await emailInput.setValue('daniel.martin@toro.com')
|
|
47
|
+
const nxtBtn = await browser.$('//android.widget.Button[@text="NEXT"]')
|
|
48
|
+
await nxtBtn.click()
|
|
49
|
+
await utils.general.sleep(10000)
|
|
50
|
+
const passwordInput = await browser.$('//android.widget.EditText[@password="true"]')
|
|
51
|
+
await passwordInput.setValue('Welcome@1')
|
|
52
|
+
const loginBtn = await browser.$('//android.widget.Button[@text="LOGIN"]')
|
|
53
|
+
await loginBtn.click()
|
|
54
|
+
await utils.general.sleep(18000)
|
|
55
|
+
const skipBtn = await browser.$('//android.widget.Button[@text="SKIP"]')
|
|
56
|
+
await skipBtn.click()
|
|
57
|
+
await utils.general.sleep(5000)
|
|
58
|
+
const finishBtn = await browser.$('//android.widget.Button[@text="FINISH"]')
|
|
59
|
+
await finishBtn.click()
|
|
60
|
+
await utils.general.sleep(15000)
|
|
61
|
+
|
|
62
|
+
await driver.init()
|
|
63
|
+
|
|
64
|
+
const screenshot = await takeScreenshot({
|
|
65
|
+
logger,
|
|
66
|
+
driver,
|
|
67
|
+
fully: true,
|
|
68
|
+
framed: true,
|
|
69
|
+
stabilization: {crop: {top: 53, bottom: 16, left: 0, right: 0}},
|
|
70
|
+
debug: {path: './'},
|
|
71
|
+
})
|
|
72
|
+
try {
|
|
73
|
+
const actual = await screenshot.image.toObject()
|
|
74
|
+
const expected = await makeImage(expectedPath).toObject()
|
|
75
|
+
assert.strictEqual(pixelmatch(actual.data, expected.data, null, expected.width, expected.height), 0)
|
|
76
|
+
} catch (err) {
|
|
77
|
+
await screenshot.image.debug({path: './logs', name: 'viewport_failed', suffix: Date.now()})
|
|
78
|
+
throw err
|
|
79
|
+
}
|
|
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
|
+
})
|
|
155
|
+
})
|
package/test/e2e/ios.spec.js
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
const assert = require('assert')
|
|
2
2
|
const pixelmatch = require('pixelmatch')
|
|
3
3
|
const {Driver} = require('@applitools/driver')
|
|
4
|
-
const spec = require('
|
|
4
|
+
const spec = require('@applitools/spec-driver-webdriverio')
|
|
5
5
|
const makeImage = require('../../src/image')
|
|
6
|
-
const
|
|
6
|
+
const takeScreenshot = require('../../index')
|
|
7
|
+
|
|
8
|
+
const env = {
|
|
9
|
+
url: 'https://ondemand.saucelabs.com/wd/hub',
|
|
10
|
+
capabilities: {
|
|
11
|
+
name: 'iOS Screenshoter Test',
|
|
12
|
+
deviceName: 'iPhone 11 Pro Simulator',
|
|
13
|
+
platformName: 'iOS',
|
|
14
|
+
platformVersion: '13.4',
|
|
15
|
+
appiumVersion: '1.19.2',
|
|
16
|
+
automationName: 'XCUITest',
|
|
17
|
+
app: 'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.8/app/IOSTestApp.zip',
|
|
18
|
+
username: process.env.SAUCE_USERNAME,
|
|
19
|
+
accessKey: process.env.SAUCE_ACCESS_KEY,
|
|
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
|
+
// },
|
|
29
|
+
}
|
|
7
30
|
|
|
8
31
|
describe('screenshoter ios', () => {
|
|
9
32
|
const logger = {log: () => {}, warn: () => {}, error: () => {}, verbose: () => {}}
|
|
@@ -25,7 +48,7 @@ describe('screenshoter ios', () => {
|
|
|
25
48
|
}
|
|
26
49
|
|
|
27
50
|
before(async () => {
|
|
28
|
-
;[browser, destroyBrowser] = await spec.build(
|
|
51
|
+
;[browser, destroyBrowser] = await spec.build(env)
|
|
29
52
|
})
|
|
30
53
|
|
|
31
54
|
after(async () => {
|
|
@@ -62,6 +85,22 @@ describe('screenshoter ios', () => {
|
|
|
62
85
|
return fullApp({type: 'collection'})
|
|
63
86
|
})
|
|
64
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
|
+
|
|
65
104
|
it('take region screenshot', () => {
|
|
66
105
|
return region()
|
|
67
106
|
})
|
|
@@ -81,7 +120,7 @@ describe('screenshoter ios', () => {
|
|
|
81
120
|
async function app(options = {}) {
|
|
82
121
|
const expectedPath = `./test/fixtures/ios/app${options.withStatusBar ? '-statusbar' : ''}.png`
|
|
83
122
|
|
|
84
|
-
const screenshot = await
|
|
123
|
+
const screenshot = await takeScreenshot({logger, driver, ...options})
|
|
85
124
|
try {
|
|
86
125
|
if (options.withStatusBar) await sanitizeStatusBar(screenshot.image)
|
|
87
126
|
const actual = await screenshot.image.toObject()
|
|
@@ -93,8 +132,19 @@ describe('screenshoter ios', () => {
|
|
|
93
132
|
}
|
|
94
133
|
}
|
|
95
134
|
async function fullApp({type, ...options} = {}) {
|
|
96
|
-
let buttonSelector, expectedPath
|
|
97
|
-
if (type === '
|
|
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') {
|
|
98
148
|
buttonSelector = {type: 'accessibility id', selector: 'Collection view'}
|
|
99
149
|
expectedPath = `./test/fixtures/ios/app-fully-collection${options.withStatusBar ? '-statusbar' : ''}.png`
|
|
100
150
|
} else if (type === 'table') {
|
|
@@ -108,13 +158,16 @@ describe('screenshoter ios', () => {
|
|
|
108
158
|
const button = await driver.element(buttonSelector)
|
|
109
159
|
await button.click()
|
|
110
160
|
|
|
111
|
-
|
|
161
|
+
await driver.init()
|
|
162
|
+
|
|
163
|
+
const screenshot = await takeScreenshot({
|
|
112
164
|
logger,
|
|
113
165
|
driver,
|
|
114
166
|
fully: true,
|
|
115
167
|
framed: true,
|
|
116
168
|
scrollingMode: 'scroll',
|
|
117
169
|
wait: 1500,
|
|
170
|
+
overlap: {top: 10, bottom: 50, ...overlap},
|
|
118
171
|
...options,
|
|
119
172
|
})
|
|
120
173
|
try {
|
|
@@ -128,7 +181,7 @@ describe('screenshoter ios', () => {
|
|
|
128
181
|
}
|
|
129
182
|
}
|
|
130
183
|
async function region(options) {
|
|
131
|
-
const screenshot = await
|
|
184
|
+
const screenshot = await takeScreenshot({
|
|
132
185
|
logger,
|
|
133
186
|
driver,
|
|
134
187
|
region: {x: 30, y: 500, height: 100, width: 200},
|
|
@@ -146,7 +199,7 @@ describe('screenshoter ios', () => {
|
|
|
146
199
|
}
|
|
147
200
|
}
|
|
148
201
|
async function fullRegion(options) {
|
|
149
|
-
const screenshot = await
|
|
202
|
+
const screenshot = await takeScreenshot({
|
|
150
203
|
logger,
|
|
151
204
|
driver,
|
|
152
205
|
region: {x: 30, y: 10, height: 700, width: 200},
|
|
@@ -164,7 +217,7 @@ describe('screenshoter ios', () => {
|
|
|
164
217
|
}
|
|
165
218
|
}
|
|
166
219
|
async function element(options) {
|
|
167
|
-
const screenshot = await
|
|
220
|
+
const screenshot = await takeScreenshot({
|
|
168
221
|
logger,
|
|
169
222
|
driver,
|
|
170
223
|
region: {type: 'accessibility id', selector: 'Table view'},
|
|
@@ -187,7 +240,7 @@ describe('screenshoter ios', () => {
|
|
|
187
240
|
})
|
|
188
241
|
await button.click()
|
|
189
242
|
|
|
190
|
-
const screenshot = await
|
|
243
|
+
const screenshot = await takeScreenshot({
|
|
191
244
|
logger,
|
|
192
245
|
driver,
|
|
193
246
|
region: {type: 'xpath', selector: '//XCUIElementTypeTable[1]'},
|