@applitools/screenshoter 3.3.9 → 3.3.12

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/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@
4
4
  ## Unreleased
5
5
 
6
6
 
7
+ ## 3.3.12 - 2022/3/14
8
+
9
+ - update snippets
10
+ - updated to @applitools/snippets@2.2.2 (from 2.2.1)
11
+
12
+ ## 3.3.11 - 2022/3/12
13
+
14
+ - disable scroll to 0,0 before taking stitched screenshot
15
+ - add better error handling when taking a stitched screenshot
16
+ - updated to @applitools/snippets@2.2.1 (from 2.2.0)
17
+
18
+ ## 3.3.10 - 2022/2/16
19
+
20
+ - do not perform initial scrolling in `takeStitchedScreenshot`
21
+
7
22
  ## 3.3.9 - 2022/2/16
8
23
 
9
24
  - remove prevention of translation in `mixed` scrolling mode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/screenshoter",
3
- "version": "3.3.9",
3
+ "version": "3.3.12",
4
4
  "description": "Applitools universal screenshoter for web and native applications",
5
5
  "keywords": [
6
6
  "applitools",
@@ -33,15 +33,15 @@
33
33
  "scripts": {
34
34
  "lint": "eslint . --ext .js",
35
35
  "test": "yarn test:it && yarn test:e2e",
36
+ "test:unit": "mocha ./test/unit/*.spec.js --no-timeouts",
36
37
  "test:it": "mocha ./test/it/*.spec.js --no-timeouts",
37
38
  "test:e2e": "mocha ./test/e2e/**/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-1}",
38
39
  "test:e2e:web": "mocha ./test/e2e/web/*.spec.js --no-timeouts -r @applitools/test-utils/mocha-hooks/docker --parallel --jobs ${MOCHA_JOBS:-15}",
39
- "test:e2e:android": "mocha ./test/e2e/android*/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-1}",
40
- "test:e2e:ios": "mocha ./test/e2e/ios*/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-1}",
41
- "setup": "yarn docker:setup && yarn android:setup && yarn ios:setup && yarn appium:setup",
40
+ "test:e2e:android": "APPLITOOLS_TEST_REMOTE=sauce mocha ./test/e2e/android*/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-2}",
41
+ "test:e2e:ios": "APPLITOOLS_TEST_REMOTE=sauce mocha ./test/e2e/ios*/*.spec.js --no-timeouts --parallel --jobs ${MOCHA_JOBS:-2}",
42
42
  "setup:web": "yarn docker:setup",
43
- "setup:android": "yarn android:setup && yarn appium:setup",
44
- "setup:ios": "yarn ios:setup && yarn appium:setup",
43
+ "setup:android": "echo \"setup:android - do nothing\"",
44
+ "setup:ios": "echo \"setup:ios - do nothing\"",
45
45
  "android:setup": "node ./scripts/android-emulator.js",
46
46
  "android:shutdown": "adb -s emulator-5555 emu kill || true && adb -s emulator-5557 emu kill || true",
47
47
  "docker:setup": "node ../scripts/scripts/generate-docker-compose-config.js && docker-compose up -d",
@@ -60,15 +60,16 @@
60
60
  }
61
61
  },
62
62
  "dependencies": {
63
- "@applitools/snippets": "2.1.15",
63
+ "@applitools/snippets": "2.2.2",
64
64
  "@applitools/utils": "1.2.13",
65
65
  "png-async": "0.9.4"
66
66
  },
67
67
  "devDependencies": {
68
- "@applitools/driver": "1.4.16",
68
+ "@applitools/driver": "1.5.4",
69
+ "@applitools/scripts": "1.1.0",
69
70
  "@applitools/sdk-release-kit": "0.13.11",
70
71
  "@applitools/spec-driver-webdriverio": "1.2.7",
71
- "@applitools/test-utils": "1.0.12",
72
+ "@applitools/test-utils": "1.1.5",
72
73
  "appium": "^1.22.2",
73
74
  "chromedriver": "^95.0.0",
74
75
  "eslint": "^7.9.0",
@@ -0,0 +1,17 @@
1
+ const utils = require('@applitools/utils')
2
+
3
+ function calculateScreenshotRegion({cropRegion, stitchedImage, preMoveOffset, postMoveOffset} = {}) {
4
+ cropRegion = !!cropRegion ? cropRegion : {x: 0, y: 0}
5
+ const screenshotRegion = utils.geometry.region(cropRegion, stitchedImage.size)
6
+
7
+ if (JSON.stringify(preMoveOffset) === JSON.stringify(postMoveOffset)) return screenshotRegion
8
+ const moveOffset = utils.geometry.offsetNegative(postMoveOffset, preMoveOffset)
9
+ const compensatedScreenshotRegion = utils.geometry.offset(screenshotRegion, moveOffset)
10
+
11
+ if (preMoveOffset.y === postMoveOffset.y && preMoveOffset.x !== postMoveOffset.x)
12
+ compensatedScreenshotRegion.x = preMoveOffset.x
13
+
14
+ return compensatedScreenshotRegion
15
+ }
16
+
17
+ module.exports = calculateScreenshotRegion
package/src/image.js CHANGED
@@ -84,6 +84,7 @@ function makeImage(data) {
84
84
  ? utils.geometry.intersect(transforms.crop, utils.geometry.offset(region, transforms.crop))
85
85
  : utils.geometry.intersect({x: 0, y: 0, ...size}, region)
86
86
  transforms.crop = region
87
+ size = utils.geometry.size(transforms.crop)
87
88
  return this
88
89
  },
89
90
  copy(srcImage, offset) {
package/src/scroller.js CHANGED
@@ -25,12 +25,14 @@ function makeScroller({logger, element, scrollingMode = 'mixed'}) {
25
25
  if (scrollingMode === 'scroll') return scrollTo(offset, element)
26
26
  if (scrollingMode === 'css') return translateTo(offset, element)
27
27
  if (scrollingMode === 'mixed') return shiftTo(offset, element)
28
+ if (scrollingMode === 'mixed+') return shiftTo(offset, element, {formerlyCssScrollingMode: true})
28
29
  }
29
30
 
30
31
  async function getInnerOffset(element = defaultElement) {
31
32
  if (scrollingMode === 'scroll') return getScrollOffset(element)
32
33
  if (scrollingMode === 'css') return getTranslateOffset(element)
33
34
  if (scrollingMode === 'mixed') return getShiftOffset(element)
35
+ if (scrollingMode === 'mixed+') return getShiftOffset(element)
34
36
  }
35
37
 
36
38
  async function getContentSize() {
@@ -101,9 +103,10 @@ function makeScroller({logger, element, scrollingMode = 'mixed'}) {
101
103
  }
102
104
  }
103
105
 
104
- async function shiftTo(offset, element = defaultElement) {
106
+ async function shiftTo(offset, element = defaultElement, {formerlyCssScrollingMode} = {}) {
105
107
  try {
106
108
  const scrollOffset = await element.scrollTo(offset)
109
+ if (utils.geometry.equals(scrollOffset, offset) && !formerlyCssScrollingMode) return scrollOffset
107
110
 
108
111
  // there is a "bug" in iOS that will not move a root element if it already scrolled, so it should be translated all the way
109
112
  if (element.driver.isIOS && (await element.isRoot())) {
@@ -50,7 +50,10 @@ async function takeScreenshot({
50
50
 
51
51
  const target = await getTarget({window, context, region, fully, scrollingMode, logger})
52
52
 
53
- if (driver.isWeb && hideScrollbars) await target.scroller.hideScrollbars()
53
+ if (target.scroller) {
54
+ await target.scroller.preserveState()
55
+ if (driver.isWeb && hideScrollbars) await target.scroller.hideScrollbars()
56
+ }
54
57
 
55
58
  try {
56
59
  if (!window) await scrollIntoViewport({...target, logger})
@@ -63,10 +66,6 @@ async function takeScreenshot({
63
66
  screenshot.image.scale(driver.viewportScale)
64
67
 
65
68
  if (hooks && hooks.afterScreenshot) {
66
- // imitate image-like state for the hook
67
- if (window && fully && target.scroller) {
68
- await target.scroller.moveTo({x: 0, y: 0}, await driver.mainContext.getScrollingElement())
69
- }
70
69
  await hooks.afterScreenshot({driver, scroller: target.scroller, screenshot})
71
70
  }
72
71
 
@@ -74,6 +73,7 @@ async function takeScreenshot({
74
73
  } finally {
75
74
  if (target.scroller) {
76
75
  await target.scroller.restoreScrollbars()
76
+ await target.scroller.restoreState()
77
77
  }
78
78
 
79
79
  // if there was active element and we have blurred it, then restore focus
@@ -123,7 +123,7 @@ async function getTarget({window, context, region, fully, scrollingMode, logger}
123
123
  const region = isScrollable ? null : await element.getRegion()
124
124
  const scrollingElement = isScrollable ? element : await elementContext.getScrollingElement()
125
125
  // css stitching could be applied only to root element of its context
126
- scrollingMode = scrollingMode === 'css' && !(await scrollingElement.isRoot()) ? 'mixed' : scrollingMode
126
+ scrollingMode = scrollingMode === 'css' && !(await scrollingElement.isRoot()) ? 'mixed+' : scrollingMode
127
127
  return {
128
128
  context: elementContext,
129
129
  region,
@@ -1,6 +1,7 @@
1
1
  const utils = require('@applitools/utils')
2
2
  const makeImage = require('./image')
3
3
  const makeTakeViewportScreenshot = require('./take-viewport-screenshot')
4
+ const calculateScreenshotRegion = require('./calculate-screenshot-region')
4
5
 
5
6
  async function takeStitchedScreenshot({
6
7
  logger,
@@ -21,32 +22,45 @@ async function takeStitchedScreenshot({
21
22
  const scrollerState = await scroller.preserveState()
22
23
 
23
24
  const initialOffset = region ? utils.geometry.location(region) : {x: 0, y: 0}
24
- const actualOffset = await scroller.moveTo(initialOffset)
25
- const expectedRemainingOffset = utils.geometry.offsetNegative(initialOffset, actualOffset)
25
+ const preMoveOffset = await scroller.getInnerOffset()
26
+ const postMoveOffset = await scroller.moveTo(initialOffset)
27
+ const expectedRemainingOffset = utils.geometry.offsetNegative(initialOffset, postMoveOffset)
26
28
 
27
29
  await utils.general.sleep(wait)
28
30
 
29
31
  const contentSize = await scroller.getContentSize()
30
32
 
33
+ logger.verbose(
34
+ 'preMoveOffset',
35
+ preMoveOffset,
36
+ 'initialOffset',
37
+ initialOffset,
38
+ 'postMoveOffset',
39
+ postMoveOffset,
40
+ 'context.isMain',
41
+ context.isMain,
42
+ )
43
+
31
44
  logger.verbose('Getting initial image...')
32
45
  let image = await takeViewportScreenshot({name: 'initial', withStatusBar})
33
46
  const firstImage = framed ? makeImage(image) : null
34
47
 
35
48
  const scrollerRegion = await scroller.getClientRegion()
36
49
  const targetRegion = region
37
- ? utils.geometry.intersect(utils.geometry.region(await scroller.getInnerOffset(), scrollerRegion), region)
50
+ ? utils.geometry.intersect(utils.geometry.region(postMoveOffset, scrollerRegion), region)
38
51
  : scrollerRegion
39
52
 
40
53
  // TODO the solution should not check driver specifics,
41
54
  // in this case target region coordinate should be already related to the scrolling element of the context
42
55
  let cropRegion = driver.isNative ? targetRegion : await driver.getRegionInViewport(context, targetRegion)
56
+ if (utils.geometry.isEmpty(cropRegion)) throw new Error('Screenshot region is out of viewport')
43
57
 
44
- logger.verbose('cropping...')
58
+ logger.verbose('cropping... cropRegion is', cropRegion)
45
59
  image.crop(withStatusBar ? utils.geometry.offset(cropRegion, {x: 0, y: driver.statusBarHeight}) : cropRegion)
46
60
  await image.debug({...debug, name: 'initial', suffix: 'region'})
47
61
 
48
62
  const contentRegion = utils.geometry.region({x: 0, y: 0}, contentSize)
49
- logger.verbose(`Scroller size: ${contentRegion}`)
63
+ logger.verbose('Scroller size:', contentRegion)
50
64
 
51
65
  if (region) region = utils.geometry.intersect(region, contentRegion)
52
66
  else region = contentRegion
@@ -73,7 +87,7 @@ async function takeStitchedScreenshot({
73
87
  const compensateOffset = {x: 0, y: initialRegion.y !== partRegion.y ? overlap.top : 0}
74
88
  const requiredOffset = utils.geometry.offsetNegative(utils.geometry.location(partRegion), compensateOffset)
75
89
 
76
- logger.verbose(`Move to ${requiredOffset}`)
90
+ logger.verbose('Move to', requiredOffset)
77
91
  let actualOffset = await scroller.moveTo(requiredOffset)
78
92
  // actual scroll position after scrolling might be not equal to required position due to
79
93
  // scrollable region shift during scrolling so actual scroll position should be corrected
@@ -94,7 +108,8 @@ async function takeStitchedScreenshot({
94
108
  width: partRegion.width,
95
109
  height: partRegion.height,
96
110
  }
97
- logger.verbose(`Actual offset is ${actualOffset}, remaining offset is ${remainingOffset}`)
111
+ logger.verbose('Actual offset is', actualOffset, ', remaining offset is', remainingOffset)
112
+ logger.verbose('cropPartRegion is', cropPartRegion)
98
113
 
99
114
  await utils.general.sleep(wait)
100
115
 
@@ -126,12 +141,12 @@ async function takeStitchedScreenshot({
126
141
 
127
142
  return {
128
143
  image: stitchedImage,
129
- region: utils.geometry.region({x: 0, y: 0}, stitchedImage.size),
144
+ region: calculateScreenshotRegion({stitchedImage, preMoveOffset, postMoveOffset}),
130
145
  }
131
146
  } else {
132
147
  return {
133
148
  image: stitchedImage,
134
- region: utils.geometry.region(cropRegion, stitchedImage.size),
149
+ region: calculateScreenshotRegion({cropRegion, stitchedImage, preMoveOffset, postMoveOffset}),
135
150
  }
136
151
  }
137
152
  }