@applitools/screenshoter 3.2.3 → 3.2.7

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 (30) hide show
  1. package/.bongo/dry-run/package-lock.json +53 -10
  2. package/.bongo/dry-run/package.json +5 -0
  3. package/.bongo/dry-run.tgz +0 -0
  4. package/CHANGELOG.md +18 -0
  5. package/logs/screenshot_2021_10_13_05_50_49_159Z_full_app_failed_1634104249159.png +0 -0
  6. package/package.json +5 -5
  7. package/src/image.js +1 -1
  8. package/src/screenshoter.js +19 -14
  9. package/src/take-screenshot.js +1 -0
  10. package/src/take-stitched-screenshot.js +1 -1
  11. package/test/e2e/android.spec.js +8 -1
  12. package/test/fixtures/android/app-fully-non-scrollable.png +0 -0
  13. package/test/fixtures/android/region.png +0 -0
  14. package/test/util/spec-driver.js +12 -13
  15. package/logs/screenshot_2021_08_12_10_21_06_033Z_full_app_failed_1628763666033.png +0 -0
  16. package/logs/screenshot_2021_08_12_10_34_27_290Z_ios_viewport_failed.png +0 -0
  17. package/logs/screenshot_2021_08_12_10_34_28_434Z_ios_full_page_failed.png +0 -0
  18. package/logs/screenshot_2021_08_12_10_43_02_793Z_full_app_failed_1628764982793.png +0 -0
  19. package/logs/screenshot_2021_08_12_11_03_20_837Z_region_failed_1628766200837.png +0 -0
  20. package/logs/screenshot_2021_08_12_11_11_33_054Z_full_app_failed_1628766693054.png +0 -0
  21. package/logs/screenshot_2021_08_12_12_46_06_592Z_viewport_failed_1628772366592.png +0 -0
  22. package/logs/screenshot_2021_08_12_12_46_15_832Z_viewport_failed_1628772375832.png +0 -0
  23. package/logs/screenshot_2021_08_12_12_49_45_671Z_viewport_failed_1628772585671.png +0 -0
  24. package/logs/screenshot_2021_08_12_12_49_56_939Z_viewport_failed_1628772596939.png +0 -0
  25. package/logs/screenshot_2021_08_12_12_51_32_701Z_full_app_failed_1628772692701.png +0 -0
  26. package/logs/screenshot_2021_08_12_12_58_47_681Z_region_failed_1628773127681.png +0 -0
  27. package/logs/screenshot_2021_08_12_13_11_30_839Z_viewport_failed_1628773890839.png +0 -0
  28. package/logs/screenshot_2021_08_12_13_11_54_642Z_viewport_failed_1628773914642.png +0 -0
  29. package/logs/screenshot_2021_08_12_13_16_08_255Z_element_failed.png +0 -0
  30. package/logs/screenshot_2021_08_12_13_38_10_302Z_region_failed_1628775490302.png +0 -0
@@ -1,25 +1,68 @@
1
1
  {
2
+ "name": "dry-run",
3
+ "lockfileVersion": 2,
2
4
  "requires": true,
3
- "lockfileVersion": 1,
5
+ "packages": {
6
+ "": {
7
+ "dependencies": {
8
+ "@applitools/screenshoter": "file:../dry-run.tgz"
9
+ }
10
+ },
11
+ "node_modules/@applitools/screenshoter": {
12
+ "version": "3.2.6",
13
+ "resolved": "file:../dry-run.tgz",
14
+ "integrity": "sha512-WzfjtYPRtHz35A5qzYaJbVWkyB17lnzu1JHun6I8U6NvBIOIoxKRiT3H/9YSZqr/WR+eGAXRLV7gYi53/nsmKw==",
15
+ "license": "SEE LICENSE IN LICENSE",
16
+ "dependencies": {
17
+ "@applitools/snippets": "2.1.7",
18
+ "@applitools/utils": "1.2.3",
19
+ "png-async": "0.9.4"
20
+ },
21
+ "engines": {
22
+ "node": ">= 8.9.0"
23
+ }
24
+ },
25
+ "node_modules/@applitools/snippets": {
26
+ "version": "2.1.7",
27
+ "resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.7.tgz",
28
+ "integrity": "sha512-Tr4Gj7Qov/oPy+8WI4oVmmubxqpOzr8P3Wjzpl6rA57xKLg6/TiIg5oZNb4+jEmO2ShjNYLaEwRWHl7kPgb4fw==",
29
+ "engines": {
30
+ "node": ">=8.9.0"
31
+ }
32
+ },
33
+ "node_modules/@applitools/utils": {
34
+ "version": "1.2.3",
35
+ "resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.3.tgz",
36
+ "integrity": "sha512-MZXsrzeHTvjFLzpfyKRDUmZWzNxH3gWd3reqYf+1kYimALKB3CO82VDNmkaGJykrRbxEP03Yqha7fHJj9eKslQ==",
37
+ "engines": {
38
+ "node": ">= 8.9.0"
39
+ }
40
+ },
41
+ "node_modules/png-async": {
42
+ "version": "0.9.4",
43
+ "resolved": "https://registry.npmjs.org/png-async/-/png-async-0.9.4.tgz",
44
+ "integrity": "sha512-B//AXX9TkneKfgtOpT1mdUnnhk2BImGD+a98vImsMU8uo1dBeHyW/kM2erWZ/CsYteTPU/xKG+t6T62heHkC3A=="
45
+ }
46
+ },
4
47
  "dependencies": {
5
48
  "@applitools/screenshoter": {
6
49
  "version": "file:../dry-run.tgz",
7
- "integrity": "sha512-g1lcC02PDAyrfC+1pqfHV9TMoDFoudaxC8XJ70F8HblyeIacygUhZbeKERi8nWG9t8Wuztsn/XIGC0N4vcJzjA==",
50
+ "integrity": "sha512-WzfjtYPRtHz35A5qzYaJbVWkyB17lnzu1JHun6I8U6NvBIOIoxKRiT3H/9YSZqr/WR+eGAXRLV7gYi53/nsmKw==",
8
51
  "requires": {
9
- "@applitools/snippets": "2.1.4",
10
- "@applitools/utils": "1.2.2",
52
+ "@applitools/snippets": "2.1.7",
53
+ "@applitools/utils": "1.2.3",
11
54
  "png-async": "0.9.4"
12
55
  }
13
56
  },
14
57
  "@applitools/snippets": {
15
- "version": "2.1.4",
16
- "resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.4.tgz",
17
- "integrity": "sha512-Jmp+DM9Kj24+ByMaqoKxhMNsefsSshN5+MLyMyrFurb1FGRGoSMmAT5bZCI7zKFG02pxZmjBmenMhpNQbzZR/A=="
58
+ "version": "2.1.7",
59
+ "resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.7.tgz",
60
+ "integrity": "sha512-Tr4Gj7Qov/oPy+8WI4oVmmubxqpOzr8P3Wjzpl6rA57xKLg6/TiIg5oZNb4+jEmO2ShjNYLaEwRWHl7kPgb4fw=="
18
61
  },
19
62
  "@applitools/utils": {
20
- "version": "1.2.2",
21
- "resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.2.tgz",
22
- "integrity": "sha512-SyHY41J+hkZR9gma/M8gjbJXLXeLoVS5DQVYd/RinzWAq62Yvg9eA97+8oHDl8s+6ELK6wMcs4yajjqCPdpF0A=="
63
+ "version": "1.2.3",
64
+ "resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.3.tgz",
65
+ "integrity": "sha512-MZXsrzeHTvjFLzpfyKRDUmZWzNxH3gWd3reqYf+1kYimALKB3CO82VDNmkaGJykrRbxEP03Yqha7fHJj9eKslQ=="
23
66
  },
24
67
  "png-async": {
25
68
  "version": "0.9.4",
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "@applitools/screenshoter": "file:../dry-run.tgz"
4
+ }
5
+ }
Binary file
package/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@
4
4
  ## Unreleased
5
5
 
6
6
 
7
+ ## 3.2.7 - 2021/10/13
8
+
9
+ - handle a case when scrolling element does not exist
10
+
11
+ ## 3.2.6 - 2021/10/12
12
+
13
+ - handle a case when scrolling element does not exist
14
+
15
+ ## 3.2.5 - 2021/10/5
16
+
17
+ - fix issue with fractional image size after scaling
18
+
19
+ ## 3.2.4 - 2021/9/9
20
+
21
+ - handle selectors that evaluate to elements from a different context
22
+ - updated to @applitools/snippets@2.1.7 (from 2.1.4)
23
+ - updated to @applitools/utils@1.2.3 (from 1.2.2)
24
+
7
25
  ## 3.2.3 - 2021/8/13
8
26
 
9
27
  - remove base64 sanitizing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/screenshoter",
3
- "version": "3.2.3",
3
+ "version": "3.2.7",
4
4
  "description": "Applitools universal screenshoter for web and native applications",
5
5
  "keywords": [
6
6
  "applitools",
@@ -48,13 +48,13 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@applitools/snippets": "2.1.4",
52
- "@applitools/utils": "1.2.2",
51
+ "@applitools/snippets": "2.1.7",
52
+ "@applitools/utils": "1.2.3",
53
53
  "png-async": "0.9.4"
54
54
  },
55
55
  "devDependencies": {
56
- "@applitools/driver": "1.1.3",
57
- "@applitools/sdk-release-kit": "0.13.0",
56
+ "@applitools/driver": "1.2.6",
57
+ "@applitools/sdk-release-kit": "0.13.3",
58
58
  "eslint": "^7.9.0",
59
59
  "eslint-plugin-mocha-no-only": "^1.1.1",
60
60
  "eslint-plugin-node": "^11.1.0",
package/src/image.js CHANGED
@@ -40,7 +40,7 @@ function makeImage(data) {
40
40
  return true
41
41
  },
42
42
  get size() {
43
- return {...utils.geometry.scale(size, transforms.scale)}
43
+ return utils.geometry.round(utils.geometry.scale(size, transforms.scale))
44
44
  },
45
45
  get transforms() {
46
46
  return {...transforms}
@@ -54,13 +54,14 @@ async function screenshoter({
54
54
  try {
55
55
  if (!window) await scrollIntoViewport({...target, logger})
56
56
 
57
- const screenshot = fully
58
- ? await takeStitchedScreenshot({...target, withStatusBar, overlap, framed, wait, stabilization, debug, logger})
59
- : await takeViewportScreenshot({...target, withStatusBar, wait, stabilization, debug, logger})
57
+ const screenshot =
58
+ fully && target.scroller
59
+ ? await takeStitchedScreenshot({...target, withStatusBar, overlap, framed, wait, stabilization, debug, logger})
60
+ : await takeViewportScreenshot({...target, withStatusBar, wait, stabilization, debug, logger})
60
61
 
61
62
  if (hooks && hooks.afterScreenshot) {
62
63
  // imitate image-like state for the hook
63
- if (window && fully) {
64
+ if (window && fully && target.scroller) {
64
65
  await target.scroller.moveTo({x: 0, y: 0}, await driver.mainContext.getScrollingElement())
65
66
  }
66
67
  await hooks.afterScreenshot({driver, scroller: target.scroller, screenshot})
@@ -68,7 +69,9 @@ async function screenshoter({
68
69
 
69
70
  return screenshot
70
71
  } finally {
71
- await target.scroller.restoreScrollbars()
72
+ if (target.scroller) {
73
+ await target.scroller.restoreScrollbars()
74
+ }
72
75
 
73
76
  // if there was active element and we have blurred it, then restore focus
74
77
  if (activeElement) await context.focusElement(activeElement)
@@ -93,7 +96,7 @@ async function getTarget({window, context, region, fully, scrollingMode, logger}
93
96
  const scrollingElement = await context.main.getScrollingElement()
94
97
  return {
95
98
  context: context.main,
96
- scroller: makeScroller({element: scrollingElement, scrollingMode, logger}),
99
+ scroller: scrollingElement ? makeScroller({element: scrollingElement, scrollingMode, logger}) : null,
97
100
  }
98
101
  } else if (region) {
99
102
  if (utils.types.has(region, ['x', 'y', 'width', 'height'])) {
@@ -102,31 +105,33 @@ async function getTarget({window, context, region, fully, scrollingMode, logger}
102
105
  return {
103
106
  context,
104
107
  region,
105
- scroller: makeScroller({element: scrollingElement, scrollingMode, logger}),
108
+ scroller: scrollingElement ? makeScroller({element: scrollingElement, scrollingMode, logger}) : null,
106
109
  }
107
110
  } else {
108
111
  // region by element or selector
109
112
  const element = await context.element(region)
110
113
  if (!element) throw new Error('Element not found!')
111
114
 
115
+ const elementContext = element.context
116
+
112
117
  if (fully) {
113
118
  const isScrollable = await element.isScrollable()
114
119
  // if element is scrollable, then take screenshot of the full element content, otherwise take screenshot of full element
115
120
  const region = isScrollable ? null : await element.getRegion()
116
- const scrollingElement = isScrollable ? element : await context.getScrollingElement()
121
+ const scrollingElement = isScrollable ? element : await elementContext.getScrollingElement()
117
122
  // css stitching could be applied only to root element of its context
118
123
  scrollingMode = scrollingMode === 'css' && !(await scrollingElement.isRoot()) ? 'mixed' : scrollingMode
119
124
  return {
120
- context,
125
+ context: elementContext,
121
126
  region,
122
- scroller: makeScroller({element: scrollingElement, scrollingMode, logger}),
127
+ scroller: scrollingElement ? makeScroller({element: scrollingElement, scrollingMode, logger}) : null,
123
128
  }
124
129
  } else {
125
130
  const scrollingElement = await context.getScrollingElement()
126
131
  return {
127
- context,
132
+ context: elementContext,
128
133
  region: await element.getRegion(),
129
- scroller: makeScroller({element: scrollingElement, scrollingMode, logger}),
134
+ scroller: scrollingElement ? makeScroller({element: scrollingElement, scrollingMode, logger}) : null,
130
135
  }
131
136
  }
132
137
  }
@@ -136,7 +141,7 @@ async function getTarget({window, context, region, fully, scrollingMode, logger}
136
141
  const scrollingElement = await context.getScrollingElement()
137
142
  return {
138
143
  context,
139
- scroller: makeScroller({logger, element: scrollingElement, scrollingMode}),
144
+ scroller: scrollingElement ? makeScroller({logger, element: scrollingElement, scrollingMode}) : null,
140
145
  }
141
146
  } else {
142
147
  const scrollingElement = await context.parent.getScrollingElement()
@@ -144,7 +149,7 @@ async function getTarget({window, context, region, fully, scrollingMode, logger}
144
149
  return {
145
150
  context: context.parent,
146
151
  region: await element.getRegion(), // IMHO we should use CLIENT (without borders) region here
147
- scroller: makeScroller({logger, element: scrollingElement, scrollingMode}),
152
+ scroller: scrollingElement ? makeScroller({logger, element: scrollingElement, scrollingMode}) : null,
148
153
  }
149
154
  }
150
155
  }
@@ -107,6 +107,7 @@ function makeTakeMarkedScreenshot({driver, stabilization = {}, debug, logger}) {
107
107
  else {
108
108
  if (!viewportRegion) viewportRegion = await getViewportRegion()
109
109
  image.crop(viewportRegion)
110
+ await image.debug({...debug, name, suffix: 'viewport'})
110
111
  }
111
112
 
112
113
  return image
@@ -55,7 +55,7 @@ async function takeStitchedScreenshot({
55
55
 
56
56
  // TODO padding should be provided from args instead of overlap
57
57
  const padding = {top: overlap, bottom: overlap}
58
- const [initialRegion, ...partRegions] = utils.geometry.divide(region, image.size, padding)
58
+ const [initialRegion, ...partRegions] = utils.geometry.divide(region, utils.geometry.round(image.size), padding)
59
59
  logger.verbose('Part regions', partRegions)
60
60
 
61
61
  logger.verbose('Creating stitched image composition container')
@@ -53,8 +53,12 @@ describe('screenshoter', () => {
53
53
  return fullApp({type: 'recycler'})
54
54
  })
55
55
 
56
+ it('take full app screenshot (non-scrollable)', () => {
57
+ return fullApp({type: 'non-scrollable'})
58
+ })
59
+
56
60
  it('take region screenshot', () => {
57
- region()
61
+ return region()
58
62
  })
59
63
 
60
64
  it.skip('take full region screenshot', () => {
@@ -114,6 +118,9 @@ describe('screenshoter', () => {
114
118
  buttonSelector = {type: 'id', selector: 'btn_recycler_view'}
115
119
  expectedPath = `./test/fixtures/android/app-fully-recycler${options.withStatusBar ? '-statusbar' : ''}.png`
116
120
  }
121
+ } else if (type === 'non-scrollable') {
122
+ buttonSelector = {type: 'id', selector: 'btn_edit_text'}
123
+ expectedPath = `./test/fixtures/android/app-fully-non-scrollable${options.withStatusBar ? '-statusbar' : ''}.png`
117
124
  } else {
118
125
  buttonSelector = {type: 'id', selector: 'btn_scroll_view_footer_header'}
119
126
  expectedPath = `./test/fixtures/android/app-fully-scroll${options.withStatusBar ? '-statusbar' : ''}.png`
Binary file
@@ -10,12 +10,6 @@ function extractElementId(element) {
10
10
  return element.elementId || element[ELEMENT_ID] || element[LEGACY_ELEMENT_ID]
11
11
  }
12
12
 
13
- function transformSelector(selector) {
14
- if (!utils.types.has(selector, ['type', 'selector'])) return selector
15
- else if (selector.type === 'css') return `css selector:${selector.selector}`
16
- else return `${selector.type}:${selector.selector}`
17
- }
18
-
19
13
  // #endregion
20
14
 
21
15
  // #region UTILITY
@@ -29,16 +23,20 @@ function isElement(element) {
29
23
  return Boolean(element.elementId || element[ELEMENT_ID] || element[LEGACY_ELEMENT_ID])
30
24
  }
31
25
  function isSelector(selector) {
32
- return (
33
- utils.types.isString(selector) ||
34
- utils.types.isFunction(selector) ||
35
- utils.types.has(selector, ['type', 'selector'])
36
- )
26
+ return utils.types.isString(selector) || utils.types.isFunction(selector)
37
27
  }
38
28
  function transformElement(element) {
39
29
  const elementId = extractElementId(element)
40
30
  return {[ELEMENT_ID]: elementId, [LEGACY_ELEMENT_ID]: elementId}
41
31
  }
32
+ function transformSelector(selector) {
33
+ if (utils.types.has(selector, 'selector')) {
34
+ if (!utils.types.has(selector, 'type')) return selector.selector
35
+ if (selector.type === 'css') return `css selector:${selector.selector}`
36
+ else return `${selector.type}:${selector.selector}`
37
+ }
38
+ return selector
39
+ }
42
40
  function extractSelector(element) {
43
41
  return element.selector
44
42
  }
@@ -74,11 +72,11 @@ async function childContext(browser, element) {
74
72
  return browser
75
73
  }
76
74
  async function findElement(browser, selector) {
77
- const element = await browser.$(transformSelector(selector))
75
+ const element = await browser.$(selector)
78
76
  return !element.error ? element : null
79
77
  }
80
78
  async function findElements(browser, selector) {
81
- const elements = await browser.$$(transformSelector(selector))
79
+ const elements = await browser.$$(selector)
82
80
  return Array.from(elements)
83
81
  }
84
82
  async function getElementRegion(browser, element) {
@@ -265,6 +263,7 @@ module.exports = {
265
263
  isElement,
266
264
  isSelector,
267
265
  transformElement,
266
+ transformSelector,
268
267
  extractSelector,
269
268
  isEqualElements,
270
269
  executeScript,