@applitools/screenshoter
Advanced tools
Comparing version 3.2.9 to 3.3.0
@@ -12,8 +12,8 @@ { | ||
"node_modules/@applitools/screenshoter": { | ||
"version": "3.2.8", | ||
"version": "3.2.9", | ||
"resolved": "file:../dry-run.tgz", | ||
"integrity": "sha512-GjN+h/vj5/lKFjJPm302jD0xSFIOoSG7gPU/ogEUcCS9l5FDcMW27PRMA/xGlolKrabmCgDHJqVbAXE1WCgjyQ==", | ||
"integrity": "sha512-a9s+yYo2bQ61Nyt/Enqrjgi7BjHV97c5LrEREO2iFisgdq+SzNHaVOav63UIQ9a5qvMAAetyZgU2NgVtPNNWow==", | ||
"license": "SEE LICENSE IN LICENSE", | ||
"dependencies": { | ||
"@applitools/snippets": "2.1.7", | ||
"@applitools/snippets": "2.1.8", | ||
"@applitools/utils": "1.2.4", | ||
@@ -27,5 +27,5 @@ "png-async": "0.9.4" | ||
"node_modules/@applitools/snippets": { | ||
"version": "2.1.7", | ||
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.7.tgz", | ||
"integrity": "sha512-Tr4Gj7Qov/oPy+8WI4oVmmubxqpOzr8P3Wjzpl6rA57xKLg6/TiIg5oZNb4+jEmO2ShjNYLaEwRWHl7kPgb4fw==", | ||
"version": "2.1.8", | ||
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.8.tgz", | ||
"integrity": "sha512-7CGFsbL9vAd6MBiGLVKHpKYywPXN499/fJMzEAVCLuGCY81CIc/PchqPFWrZZKZOY/IV21RJmE3MvqZAeXTIXA==", | ||
"engines": { | ||
@@ -52,5 +52,5 @@ "node": ">=8.9.0" | ||
"version": "file:../dry-run.tgz", | ||
"integrity": "sha512-GjN+h/vj5/lKFjJPm302jD0xSFIOoSG7gPU/ogEUcCS9l5FDcMW27PRMA/xGlolKrabmCgDHJqVbAXE1WCgjyQ==", | ||
"integrity": "sha512-a9s+yYo2bQ61Nyt/Enqrjgi7BjHV97c5LrEREO2iFisgdq+SzNHaVOav63UIQ9a5qvMAAetyZgU2NgVtPNNWow==", | ||
"requires": { | ||
"@applitools/snippets": "2.1.7", | ||
"@applitools/snippets": "2.1.8", | ||
"@applitools/utils": "1.2.4", | ||
@@ -61,5 +61,5 @@ "png-async": "0.9.4" | ||
"@applitools/snippets": { | ||
"version": "2.1.7", | ||
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.7.tgz", | ||
"integrity": "sha512-Tr4Gj7Qov/oPy+8WI4oVmmubxqpOzr8P3Wjzpl6rA57xKLg6/TiIg5oZNb4+jEmO2ShjNYLaEwRWHl7kPgb4fw==" | ||
"version": "2.1.8", | ||
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.8.tgz", | ||
"integrity": "sha512-7CGFsbL9vAd6MBiGLVKHpKYywPXN499/fJMzEAVCLuGCY81CIc/PchqPFWrZZKZOY/IV21RJmE3MvqZAeXTIXA==" | ||
}, | ||
@@ -66,0 +66,0 @@ "@applitools/utils": { |
@@ -7,2 +7,8 @@ | ||
## 3.3.0 - 2021/12/16 | ||
- fix ios web screenshots on pages without viewport meta tag | ||
- improve native apps support | ||
- updated to @applitools/snippets@2.1.8 (from 2.1.7) | ||
## 3.2.9 - 2021/11/14 | ||
@@ -9,0 +15,0 @@ |
@@ -1,1 +0,2 @@ | ||
module.exports = require('./src/screenshoter') | ||
module.exports = require('./src/take-screenshot') | ||
exports.takeScreenshot = require('./src/take-screenshot') |
{ | ||
"name": "@applitools/screenshoter", | ||
"version": "3.2.9", | ||
"version": "3.3.0", | ||
"description": "Applitools universal screenshoter for web and native applications", | ||
@@ -52,3 +52,3 @@ "keywords": [ | ||
"dependencies": { | ||
"@applitools/snippets": "2.1.7", | ||
"@applitools/snippets": "2.1.8", | ||
"@applitools/utils": "1.2.4", | ||
@@ -58,6 +58,6 @@ "png-async": "0.9.4" | ||
"devDependencies": { | ||
"@applitools/driver": "1.3.2", | ||
"@applitools/driver": "1.4.1", | ||
"@applitools/sdk-release-kit": "0.13.4", | ||
"@applitools/spec-driver-webdriverio": "1.2.0", | ||
"@applitools/test-utils": "1.0.9", | ||
"@applitools/spec-driver-webdriverio": "1.2.2", | ||
"@applitools/test-utils": "1.0.10", | ||
"chromedriver": "^95.0.0", | ||
@@ -64,0 +64,0 @@ "eslint": "^7.9.0", |
function findImagePattern(image, pattern) { | ||
for (let pixel = 0; pixel < image.width * image.height; ++pixel) { | ||
if (isPattern(image, pixel, pattern)) { | ||
return { | ||
x: (pixel % image.width) - pattern.offset, | ||
y: Math.floor(pixel / image.width) - pattern.offset, | ||
} | ||
return {x: (pixel % image.width) - pattern.offset, y: Math.floor(pixel / image.width) - pattern.offset} | ||
} | ||
@@ -14,28 +11,7 @@ } | ||
function isPattern(image, index, pattern) { | ||
const channels = 4 | ||
const roundNumber = pattern.size - Math.floor(pattern.size / 2) | ||
for (const [chunkIndex, chunkColor] of pattern.mask.entries()) { | ||
const pixelOffset = index + image.width * pattern.size * chunkIndex | ||
for (let round = 0; round < roundNumber; ++round) { | ||
const sideLength = pattern.size - round * 2 | ||
const stepsNumber = sideLength * channels - channels | ||
const threshold = Math.min((roundNumber - round) * 10 + 10, 100) | ||
for (let step = 0; step < stepsNumber; ++step) { | ||
let pixelIndex = pixelOffset + round + round * image.width | ||
if (step < sideLength) { | ||
pixelIndex += step | ||
} else if (step < sideLength * 2 - 1) { | ||
pixelIndex += sideLength - 1 + ((step % sideLength) + 1) * image.width | ||
} else if (step < sideLength * 3 - 2) { | ||
pixelIndex += (sideLength - 1) * image.width + (sideLength - (step % sideLength) - 1) | ||
} else { | ||
pixelIndex += (step % sideLength) * image.width | ||
} | ||
const pixelColor = pixelColorAt(image, pixelIndex, threshold) | ||
if (pixelColor !== chunkColor) { | ||
return false | ||
} | ||
} | ||
const itemLength = pattern.size * pattern.pixelRatio | ||
for (const [itemIndex, itemColor] of pattern.mask.entries()) { | ||
for (let partOffset = itemIndex * itemLength; partOffset < (itemIndex + 1) * itemLength; ++partOffset) { | ||
const pixelColor = pixelColorAt(image, index + partOffset) | ||
if (pixelColor !== itemColor) return false | ||
} | ||
@@ -46,3 +22,3 @@ } | ||
function pixelColorAt(image, index, threshold = 0) { | ||
function pixelColorAt(image, index) { | ||
const channels = 4 | ||
@@ -52,12 +28,8 @@ const r = image.data[index * channels] | ||
const b = image.data[index * channels + 2] | ||
const rgb = [r, g, b] | ||
// WHITE | ||
if (rgb.every(sub => sub >= 255 - threshold)) return 1 | ||
// BLACK | ||
else if (rgb.every(sub => sub <= threshold)) return 0 | ||
// OTHER | ||
else return -1 | ||
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b | ||
return luminance < 128 ? /* black */ 1 : /* white */ 0 | ||
} | ||
module.exports = findImagePattern |
168
src/image.js
@@ -9,3 +9,3 @@ const fs = require('fs') | ||
let image, size | ||
let transforms = {rotate: 0, scale: 1, crop: null} | ||
let transforms = {rotate: 0, scale: 1, crop: null, modifiers: []} | ||
@@ -24,5 +24,5 @@ if (utils.types.isBase64(data)) { | ||
} else if (data.isImage) { | ||
transforms = data.transforms | ||
image = data.toRaw() | ||
size = data.size | ||
transforms = data.transforms | ||
size = utils.geometry.scale(data.size, 1 / transforms.scale) | ||
} else if (utils.types.has(data, ['width', 'height'])) { | ||
@@ -32,2 +32,4 @@ image = fromSize(data) | ||
size = {width: data.width, height: data.height} | ||
} else if (data.auto) { | ||
size = {width: -1, height: -1} | ||
} else { | ||
@@ -37,6 +39,2 @@ throw new Error('Unable to create an image abstraction from unknown data') | ||
if (!transforms.crop) { | ||
transforms.crop = utils.geometry.region({x: 0, y: 0}, size) | ||
} | ||
return { | ||
@@ -53,6 +51,6 @@ get isImage() { | ||
get width() { | ||
return size.width | ||
return this.size.width | ||
}, | ||
get height() { | ||
return size.height | ||
return this.size.height | ||
}, | ||
@@ -75,3 +73,5 @@ scale(ratio) { | ||
region = utils.geometry.rotate(region, transforms.rotate) | ||
transforms.crop = utils.geometry.intersect(transforms.crop, utils.geometry.offset(region, transforms.crop)) | ||
transforms.crop = transforms.crop | ||
? utils.geometry.intersect(transforms.crop, utils.geometry.offset(region, transforms.crop)) | ||
: utils.geometry.intersect({x: 0, y: 0, ...size}, region) | ||
@@ -86,14 +86,39 @@ size = utils.geometry.round(utils.geometry.size(transforms.crop)) | ||
}, | ||
async copy(srcImage, offset) { | ||
const [dst, src] = await Promise.all([this.toObject(), srcImage.toObject()]) | ||
image = await copy(dst, src, offset) | ||
copy(srcImage, offset) { | ||
const scale = srcImage.transforms.scale | ||
if (!image) { | ||
size = { | ||
width: Math.max(Math.floor((offset.x + srcImage.width) / scale), size.width), | ||
height: Math.max(Math.floor((offset.y + srcImage.height) / scale), size.height), | ||
} | ||
transforms.scale = Math.min(scale, transforms.scale) | ||
} | ||
transforms.modifiers.push({ | ||
type: 'copy', | ||
image: srcImage.scale(scale === transforms.scale ? 1 / scale : scale / transforms.scale).toObject(), | ||
offset: utils.geometry.scale(offset, 1 / transforms.scale), | ||
}) | ||
return this | ||
}, | ||
async combine(firstImage, lastImage, region) { | ||
const [first, last, src] = await Promise.all([firstImage.toObject(), lastImage.toObject(), this.toObject()]) | ||
image = await combine(first, last, src, region) | ||
size = {width: image.width, height: image.height} | ||
transforms.crop = utils.geometry.region({x: 0, y: 0}, size) | ||
frame(topImage, bottomImage, region) { | ||
const scale = topImage.transforms.scale | ||
const prevSize = size | ||
region = utils.geometry.scale(region, 1 / scale) | ||
size = { | ||
width: Math.floor(topImage.width / scale + Math.max(size.width - region.width, 0)), | ||
height: Math.floor(topImage.height / scale + Math.max(size.height - region.height, 0)), | ||
} | ||
transforms.modifiers.push({ | ||
type: 'frame', | ||
top: topImage.scale(scale === transforms.scale ? 1 / scale : scale / transforms.scale).toObject(), | ||
bottom: bottomImage.scale(scale === transforms.scale ? 1 / scale : scale / transforms.scale).toObject(), | ||
region, | ||
}) | ||
transforms.added = {width: size.width - prevSize.width, height: size.height - prevSize.height} | ||
return this | ||
}, | ||
async toRaw() { | ||
return image | ||
}, | ||
async toBuffer() { | ||
@@ -109,8 +134,5 @@ const image = await this.toObject() | ||
}, | ||
async toRaw() { | ||
return image | ||
}, | ||
async toObject() { | ||
image = await transform(await image, transforms) | ||
transforms = {rotate: 0, scale: 1, crop: utils.geometry.region({x: 0, y: 0}, size)} | ||
image = await transform(image ? await image : size, transforms) | ||
transforms = {crop: null, scale: 1, rotate: 0, modifiers: []} | ||
return image | ||
@@ -122,3 +144,4 @@ }, | ||
const filename = ['screenshot', timestamp, debug.name, debug.suffix].filter(part => part).join('_') + '.png' | ||
return toFile(await transform(await image, transforms), path.join(debug.path, filename)).catch(() => null) | ||
const transformedImage = await transform(image ? await image : size, transforms) | ||
return toFile(transformedImage, path.join(debug.path, filename)).catch(() => null) | ||
}, | ||
@@ -176,6 +199,23 @@ } | ||
async function transform(image, transforms) { | ||
const croppedImage = transforms.crop ? await extract(image, transforms.crop) : image | ||
const scaledImage = transforms.scale !== 1 ? await scale(croppedImage, transforms.scale) : croppedImage | ||
const rotatedImage = transforms.rotate > 0 ? await rotate(scaledImage, transforms.rotate) : scaledImage | ||
return rotatedImage | ||
if (!image.data) { | ||
const size = transforms.added | ||
? {width: image.width - transforms.added.width, height: image.height - transforms.added.height} | ||
: image | ||
image = new png.Image(size) | ||
} | ||
image = await transforms.modifiers.reduce(async (image, modifier) => { | ||
if (modifier.type === 'copy') { | ||
return copy(await image, await modifier.image, modifier.offset) | ||
} else if (modifier.type === 'frame') { | ||
return frame(await modifier.top, await modifier.bottom, await image, modifier.region) | ||
} else { | ||
return image | ||
} | ||
}, image) | ||
image = transforms.rotate > 0 ? await rotate(image, transforms.rotate) : image | ||
image = transforms.crop ? await extract(image, transforms.crop) : image | ||
image = transforms.scale !== 1 ? await scale(image, transforms.scale) : image | ||
return image | ||
} | ||
@@ -301,40 +341,40 @@ | ||
async function combine(firstImage, lastImage, srcImage, region) { | ||
async function frame(topImage, bottomImage, srcImage, region) { | ||
region = utils.geometry.intersect( | ||
{x: 0, y: 0, width: firstImage.width, height: firstImage.height}, | ||
{x: 0, y: 0, width: topImage.width, height: topImage.height}, | ||
utils.geometry.round(region), | ||
) | ||
if (region.x === 0 && region.y === 0 && region.width >= firstImage.width && region.height >= firstImage.height) { | ||
if (region.x === 0 && region.y === 0 && region.width >= topImage.width && region.height >= topImage.height) { | ||
return srcImage | ||
} | ||
if (region.width === srcImage.width && region.height === srcImage.height) { | ||
await copy(firstImage, srcImage, {x: region.x, y: region.y}) | ||
return firstImage | ||
if (region.width >= srcImage.width && region.height >= srcImage.height) { | ||
await copy(topImage, srcImage, {x: region.x, y: region.y}) | ||
return topImage | ||
} | ||
const dstImage = new png.Image({ | ||
width: firstImage.width - region.width + srcImage.width, | ||
height: firstImage.height - region.height + srcImage.height, | ||
width: topImage.width + Math.max(srcImage.width - region.width, 0), | ||
height: topImage.height + Math.max(srcImage.height - region.height, 0), | ||
}) | ||
if (region.width === srcImage.width) { | ||
const topImage = await extract(firstImage, { | ||
const topExtImage = await extract(topImage, { | ||
x: 0, | ||
y: 0, | ||
width: firstImage.width, | ||
width: topImage.width, | ||
height: region.y + region.height, | ||
}) | ||
await copy(dstImage, topImage, {x: 0, y: 0}) | ||
await copy(dstImage, topExtImage, {x: 0, y: 0}) | ||
} else if (region.height === srcImage.height) { | ||
const leftImage = await extract(firstImage, { | ||
const leftExtImage = await extract(topImage, { | ||
x: 0, | ||
y: 0, | ||
width: region.x + region.width, | ||
height: firstImage.height, | ||
height: topImage.height, | ||
}) | ||
await copy(dstImage, leftImage, {x: 0, y: 0}) | ||
await copy(dstImage, leftExtImage, {x: 0, y: 0}) | ||
} else { | ||
const topLeftImage = await extract(firstImage, { | ||
const topLeftExtImage = await extract(topImage, { | ||
x: 0, | ||
@@ -345,8 +385,8 @@ y: 0, | ||
}) | ||
await copy(dstImage, topLeftImage, {x: 0, y: 0}) | ||
await copy(dstImage, topLeftExtImage, {x: 0, y: 0}) | ||
const rightExtImage = await extract(firstImage, { | ||
const rightExtImage = await extract(topImage, { | ||
x: region.x + region.width, | ||
y: 0, | ||
width: firstImage.width - (region.x + region.width), | ||
width: topImage.width - (region.x + region.width), | ||
height: region.y, | ||
@@ -356,7 +396,7 @@ }) | ||
const bottomExtImage = await extract(firstImage, { | ||
const bottomExtImage = await extract(topImage, { | ||
x: 0, | ||
y: region.y + region.height, | ||
width: region.x, | ||
height: firstImage.height - (region.y + region.height), | ||
height: topImage.height - (region.y + region.height), | ||
}) | ||
@@ -366,32 +406,32 @@ await copy(dstImage, bottomExtImage, {x: 0, y: region.y + region.height}) | ||
if (lastImage.height > region.y + region.height || lastImage.width > region.x + region.width) { | ||
if (bottomImage.height > region.y + region.height || bottomImage.width > region.x + region.width) { | ||
// first image might be higher | ||
const yDiff = firstImage.height - lastImage.height | ||
const yDiff = topImage.height - bottomImage.height | ||
if (region.width === srcImage.width) { | ||
const bottomImage = await extract(lastImage, { | ||
const bottomExtImage = await extract(bottomImage, { | ||
x: 0, | ||
y: region.y - yDiff + region.height, | ||
width: lastImage.width, | ||
height: lastImage.height - (region.y - yDiff + region.height), | ||
width: bottomImage.width, | ||
height: bottomImage.height - (region.y - yDiff + region.height), | ||
}) | ||
await copy(dstImage, bottomImage, {x: 0, y: region.y + srcImage.height}) | ||
await copy(dstImage, bottomExtImage, {x: 0, y: region.y + Math.max(srcImage.height, region.height)}) | ||
} else if (region.height === srcImage.height) { | ||
const rightImage = await extract(lastImage, { | ||
const rightExtImage = await extract(bottomImage, { | ||
x: region.x + region.width, | ||
y: 0, | ||
width: lastImage.width - (region.x + region.width), | ||
height: lastImage.height, | ||
width: bottomImage.width - (region.x + region.width), | ||
height: bottomImage.height, | ||
}) | ||
await copy(dstImage, rightImage, {x: region.x + srcImage.width, y: 0}) | ||
await copy(dstImage, rightExtImage, {x: region.x + Math.max(srcImage.width, region.width), y: 0}) | ||
} else { | ||
const bottomRightImage = await extract(lastImage, { | ||
const bottomRightExtImage = await extract(bottomImage, { | ||
x: region.x, | ||
y: region.y - yDiff, | ||
width: lastImage.width - region.x, | ||
height: lastImage.height - (region.y - yDiff), | ||
width: bottomImage.width - region.x, | ||
height: bottomImage.height - (region.y - yDiff), | ||
}) | ||
await copy(dstImage, bottomRightImage, { | ||
x: region.x + srcImage.width - region.width, | ||
y: region.y + srcImage.height - region.height, | ||
await copy(dstImage, bottomRightExtImage, { | ||
x: region.x + Math.max(srcImage.width - region.width, 0), | ||
y: region.y + Math.max(srcImage.height - region.height, 0), | ||
}) | ||
@@ -398,0 +438,0 @@ } |
const utils = require('@applitools/utils') | ||
const snippets = require('@applitools/snippets') | ||
const findImagePattern = require('./find-image-pattern') | ||
const makeImage = require('./image') | ||
const makeScroller = require('./scroller') | ||
const scrollIntoViewport = require('./scroll-into-viewport') | ||
const takeStitchedScreenshot = require('./take-stitched-screenshot') | ||
const takeSimpleScreenshot = require('./take-simple-screenshot') | ||
function makeTakeScreenshot(options) { | ||
const {driver} = options | ||
if (driver.isNative) { | ||
return makeTakeNativeScreenshot(options) | ||
} else if (driver.browserName === 'Firefox') { | ||
try { | ||
const browserVersion = Number.parseInt(driver.browserVersion, 10) | ||
if (browserVersion >= 48 && browserVersion <= 72) { | ||
return makeTakeMainContextScreenshot(options) | ||
} | ||
} catch (ignored) {} | ||
} else if (driver.browserName === 'Safari') { | ||
if (driver.isIOS) { | ||
return makeTakeMarkedScreenshot(options) | ||
} else if (driver.browserVersion === '11') { | ||
return makeTakeSafari11Screenshot(options) | ||
} | ||
} | ||
async function takeScreenshot({ | ||
driver, | ||
frames = [], | ||
region, | ||
fully, | ||
scrollingMode, | ||
hideScrollbars, | ||
hideCaret, | ||
withStatusBar, | ||
overlap, | ||
framed, | ||
wait, | ||
stabilization, | ||
hooks, | ||
debug, | ||
logger, | ||
}) { | ||
// screenshot of a window/app was requested (fully or viewport) | ||
const window = !region && (!frames || frames.length === 0) | ||
// framed screenshots could be taken only when screenshot of window/app fully was requested | ||
framed = framed && fully && window | ||
// screenshots with status bar could be taken only when screenshot of app or framed app fully was requested | ||
withStatusBar = withStatusBar && driver.isNative && window && (!fully || framed) | ||
scrollingMode = driver.isNative ? 'scroll' : scrollingMode | ||
return makeTakeDefaultScreenshot(options) | ||
} | ||
const activeContext = driver.currentContext | ||
const context = | ||
frames.length > 0 | ||
? await activeContext.context(frames.reduce((parent, frame) => ({...frame, parent}), null)) | ||
: activeContext | ||
function makeTakeDefaultScreenshot({driver, stabilization = {}, debug, logger}) { | ||
const calculateScaleRatio = makeCalculateScaleRatio({driver}) | ||
return async function takeScreenshot({name} = {}) { | ||
logger.verbose('Taking screenshot...') | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(await calculateScaleRatio(image.width)) | ||
if (stabilization.rotate) image.crop(stabilization.rotate) | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
return image | ||
// traverse from main context to target context to hide scrollbars and preserve context state (scroll/translate position) | ||
for (const nextContext of context.path) { | ||
const scrollingElement = await nextContext.getScrollingElement() | ||
// unlike web apps, native apps do not always have scrolling element | ||
if (scrollingElement) { | ||
if (driver.isWeb && hideScrollbars) await scrollingElement.hideScrollbars() | ||
await scrollingElement.preserveState() | ||
} | ||
} | ||
} | ||
function makeTakeMainContextScreenshot({driver, stabilization = {}, debug, logger}) { | ||
const calculateScaleRatio = makeCalculateScaleRatio({driver}) | ||
return async function takeScreenshot({name} = {}) { | ||
logger.verbose('Taking screenshot...') | ||
const originalContext = driver.currentContext | ||
await driver.mainContext.focus() | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await originalContext.focus() | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
// blur active element in target context | ||
const activeElement = driver.isWeb && hideCaret ? await context.blurElement() : null | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(await calculateScaleRatio(image.width)) | ||
const target = await getTarget({window, context, region, fully, scrollingMode, logger}) | ||
if (stabilization.rotate) image.rotate(stabilization.rotate) | ||
if (driver.isWeb && hideScrollbars) await target.scroller.hideScrollbars() | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
try { | ||
if (!window) await scrollIntoViewport({...target, logger}) | ||
return image | ||
} | ||
} | ||
const screenshot = | ||
fully && target.scroller | ||
? await takeStitchedScreenshot({...target, withStatusBar, overlap, framed, wait, stabilization, debug, logger}) | ||
: await takeSimpleScreenshot({...target, withStatusBar, wait, stabilization, debug, logger}) | ||
function makeTakeSafari11Screenshot({driver, stabilization = {}, debug, logger}) { | ||
const calculateScaleRatio = makeCalculateScaleRatio({driver}) | ||
let viewportSize | ||
if (hooks && hooks.afterScreenshot) { | ||
// imitate image-like state for the hook | ||
if (window && fully && target.scroller) { | ||
await target.scroller.moveTo({x: 0, y: 0}, await driver.mainContext.getScrollingElement()) | ||
} | ||
await hooks.afterScreenshot({driver, scroller: target.scroller, screenshot}) | ||
} | ||
return async function takeScreenshot({name} = {}) { | ||
logger.verbose('Taking safari 11 driver screenshot...') | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
return screenshot | ||
} finally { | ||
if (target.scroller) { | ||
await target.scroller.restoreScrollbars() | ||
} | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(await calculateScaleRatio(image.width)) | ||
// if there was active element and we have blurred it, then restore focus | ||
if (activeElement) await context.focusElement(activeElement) | ||
if (stabilization.rotate) image.rotate(stabilization.rotate) | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
else { | ||
if (!viewportSize) viewportSize = await driver.getViewportSize() | ||
const viewportLocation = await driver.mainContext.execute(snippets.getElementScrollOffset, []) | ||
image.crop(utils.geometry.region(viewportLocation, viewportSize)) | ||
// traverse from target context to the main context to restore scrollbars and context states | ||
for (const prevContext of context.path.reverse()) { | ||
const scrollingElement = await prevContext.getScrollingElement() | ||
if (scrollingElement) { | ||
if (driver.isWeb && hideScrollbars) await scrollingElement.restoreScrollbars() | ||
await scrollingElement.restoreState() | ||
} | ||
} | ||
return image | ||
// restore focus on original active context | ||
await activeContext.focus() | ||
} | ||
} | ||
function makeTakeMarkedScreenshot({driver, stabilization = {}, debug, logger}) { | ||
const calculateScaleRatio = makeCalculateScaleRatio({driver}) | ||
let viewportRegion | ||
return async function takeScreenshot({name} = {}) { | ||
logger.verbose('Taking viewport screenshot (using markers)...') | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(await calculateScaleRatio(image.width)) | ||
if (stabilization.rotate) image.rotate(stabilization.rotate) | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
else { | ||
if (!viewportRegion) viewportRegion = await getViewportRegion() | ||
image.crop(viewportRegion) | ||
await image.debug({...debug, name, suffix: 'viewport'}) | ||
async function getTarget({window, context, region, fully, scrollingMode, logger}) { | ||
if (window) { | ||
// window/app | ||
const scrollingElement = await context.main.getScrollingElement() | ||
return { | ||
context: context.main, | ||
scroller: scrollingElement ? makeScroller({element: scrollingElement, scrollingMode, logger}) : null, | ||
} | ||
} else if (region) { | ||
if (utils.types.has(region, ['x', 'y', 'width', 'height'])) { | ||
// region by coordinates | ||
const scrollingElement = await context.getScrollingElement() | ||
return { | ||
context, | ||
region, | ||
scroller: scrollingElement ? makeScroller({element: scrollingElement, scrollingMode, logger}) : null, | ||
} | ||
} else { | ||
// region by element or selector | ||
const element = await context.element(region) | ||
if (!element) throw new Error('Element not found!') | ||
return image | ||
} | ||
const elementContext = element.context | ||
async function getViewportRegion() { | ||
const marker = await driver.mainContext.execute(snippets.addPageMarker) | ||
try { | ||
const image = makeImage(await driver.takeScreenshot()) | ||
if (stabilization.rotate) await image.rotate(stabilization.rotate) | ||
await image.debug({...debug, name: 'marker'}) | ||
const markerLocation = findImagePattern(await image.toObject(), marker) | ||
if (!markerLocation) return null | ||
const viewportSize = await driver.getViewportSize() | ||
return utils.geometry.region(utils.geometry.scale(markerLocation, 1 / driver.pixelRatio), viewportSize) | ||
} finally { | ||
await driver.mainContext.execute(snippets.cleanupPageMarker) | ||
if (fully) { | ||
const isScrollable = await element.isScrollable() | ||
// if element is scrollable, then take screenshot of the full element content, otherwise take screenshot of full element | ||
const region = isScrollable ? null : await element.getRegion() | ||
const scrollingElement = isScrollable ? element : await elementContext.getScrollingElement() | ||
// css stitching could be applied only to root element of its context | ||
scrollingMode = scrollingMode === 'css' && !(await scrollingElement.isRoot()) ? 'mixed' : scrollingMode | ||
return { | ||
context: elementContext, | ||
region, | ||
scroller: scrollingElement ? makeScroller({element: scrollingElement, scrollingMode, logger}) : null, | ||
} | ||
} else { | ||
const scrollingElement = await context.getScrollingElement() | ||
return { | ||
context: elementContext, | ||
region: await element.getRegion(), | ||
scroller: scrollingElement ? makeScroller({element: scrollingElement, scrollingMode, logger}) : null, | ||
} | ||
} | ||
} | ||
} | ||
} | ||
function makeTakeNativeScreenshot({driver, stabilization = {}, debug, logger}) { | ||
return async function takeScreenshot({name, withStatusBar} = {}) { | ||
logger.verbose('Taking native driver screenshot...') | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(1 / driver.pixelRatio) | ||
if (stabilization.rotate) image.rotate(stabilization.rotate) | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
else { | ||
const viewportSize = await driver.getViewportSize() | ||
const cropRegion = withStatusBar | ||
? {x: 0, y: 0, width: viewportSize.width, height: viewportSize.height + driver.statusBarHeight} | ||
: {top: driver.statusBarHeight, bottom: driver.navigationBarHeight, left: 0, right: 0} | ||
image.crop(cropRegion) | ||
await image.debug({...debug, name, suffix: `viewport${withStatusBar ? '-with-statusbar' : ''}`}) | ||
} else if (!context.isMain) { | ||
// context | ||
if (fully) { | ||
const scrollingElement = await context.getScrollingElement() | ||
return { | ||
context, | ||
scroller: scrollingElement ? makeScroller({logger, element: scrollingElement, scrollingMode}) : null, | ||
} | ||
} else { | ||
const scrollingElement = await context.parent.getScrollingElement() | ||
const element = await context.getContextElement() | ||
return { | ||
context: context.parent, | ||
region: await element.getRegion(), // IMHO we should use CLIENT (without borders) region here | ||
scroller: scrollingElement ? makeScroller({logger, element: scrollingElement, scrollingMode}) : null, | ||
} | ||
} | ||
return image | ||
} | ||
} | ||
function makeCalculateScaleRatio({driver}) { | ||
let viewportWidth, contentWidth | ||
const VIEWPORT_THRESHOLD = 1 | ||
const CONTENT_THRESHOLD = 10 | ||
return async function calculateScaleRatio(imageWidth) { | ||
if (!viewportWidth) viewportWidth = await driver.getViewportSize().then(size => size.width) | ||
if (!contentWidth) contentWidth = await driver.mainContext.getContentSize().then(size => size.width) | ||
// If the image's width is the same as the viewport's width or the | ||
// top level context's width, no scaling is necessary. | ||
if ( | ||
(imageWidth >= viewportWidth - VIEWPORT_THRESHOLD && imageWidth <= viewportWidth + VIEWPORT_THRESHOLD) || | ||
(imageWidth >= contentWidth - CONTENT_THRESHOLD && imageWidth <= contentWidth + CONTENT_THRESHOLD) | ||
) { | ||
return 1 | ||
} | ||
const scaledImageWidth = Math.round(imageWidth / driver.pixelRatio) | ||
return viewportWidth / scaledImageWidth / driver.pixelRatio | ||
} | ||
} | ||
module.exports = makeTakeScreenshot | ||
module.exports = takeScreenshot |
const utils = require('@applitools/utils') | ||
const makeImage = require('./image') | ||
const makeTakeScreenshot = require('./take-screenshot') | ||
const makeTakeViewportScreenshot = require('./take-viewport-screenshot') | ||
@@ -11,3 +11,3 @@ async function takeStitchedScreenshot({ | ||
withStatusBar, | ||
overlap = 50, | ||
overlap = {top: 10, bottom: 50}, | ||
framed, | ||
@@ -21,3 +21,3 @@ wait, | ||
const driver = context.driver | ||
const takeScreenshot = makeTakeScreenshot({logger, driver, stabilization, debug}) | ||
const takeViewportScreenshot = makeTakeViewportScreenshot({logger, driver, stabilization, debug}) | ||
const scrollerState = await scroller.preserveState() | ||
@@ -34,11 +34,9 @@ | ||
logger.verbose('Getting initial image...') | ||
let image = await takeScreenshot({name: 'initial', withStatusBar}) | ||
let image = await takeViewportScreenshot({name: 'initial', withStatusBar}) | ||
const firstImage = framed ? makeImage(image) : null | ||
const scrollerRegion = await scroller.getClientRegion() | ||
const targetRegion = region | ||
? utils.geometry.intersect( | ||
utils.geometry.region(await scroller.getInnerOffset(), await scroller.getClientRegion()), | ||
region, | ||
) | ||
: await scroller.getClientRegion() | ||
? utils.geometry.intersect(utils.geometry.region(await scroller.getInnerOffset(), scrollerRegion), region) | ||
: scrollerRegion | ||
@@ -61,17 +59,15 @@ // TODO the solution should not check driver specifics, | ||
// TODO padding should be provided from args instead of overlap | ||
const padding = {top: overlap, bottom: overlap} | ||
const [initialRegion, ...partRegions] = utils.geometry.divide(region, utils.geometry.round(image.size), padding) | ||
const [initialRegion, ...partRegions] = utils.geometry.divide(region, image.size, overlap) | ||
logger.verbose('Part regions', partRegions) | ||
logger.verbose('Creating stitched image composition container') | ||
const stitchedImage = makeImage({width: region.width, height: region.height}) | ||
const stitchedImage = makeImage({auto: true}) | ||
logger.verbose('Adding initial image...') | ||
await stitchedImage.copy(image, {x: 0, y: 0}) | ||
stitchedImage.copy(image, {x: 0, y: 0}) | ||
logger.verbose('Getting the rest of the image parts...') | ||
let stitchedSize = {width: image.width, height: image.height} | ||
let lastImage | ||
let lastImage = firstImage | ||
let scrollerRegionShift = {x: 0, y: 0} | ||
for (const partRegion of partRegions) { | ||
@@ -81,29 +77,25 @@ const partName = `${partRegion.x}_${partRegion.y}_${partRegion.width}x${partRegion.height}` | ||
const compensateOffset = {x: 0, y: initialRegion.y !== partRegion.y ? padding.top : 0} | ||
const compensateOffset = {x: 0, y: initialRegion.y !== partRegion.y ? overlap.top : 0} | ||
const requiredOffset = utils.geometry.offsetNegative(utils.geometry.location(partRegion), compensateOffset) | ||
logger.verbose(`Move to ${requiredOffset}`) | ||
const actualOffset = await scroller.moveTo(requiredOffset) | ||
const remainingOffset = utils.geometry.offset( | ||
utils.geometry.offsetNegative( | ||
utils.geometry.offsetNegative(requiredOffset, actualOffset), | ||
expectedRemainingOffset, | ||
), | ||
compensateOffset, | ||
) | ||
let actualOffset = await scroller.moveTo(requiredOffset) | ||
// actual scroll position after scrolling might be not equal to required position due to | ||
// scrollable region shift during scrolling so actual scroll position should be corrected | ||
if (!utils.geometry.equals(actualOffset, requiredOffset) && driver.isNative) { | ||
const actualScrollerRegion = await scroller.getClientRegion() | ||
scrollerRegionShift = {x: scrollerRegion.x - actualScrollerRegion.x, y: scrollerRegion.y - actualScrollerRegion.y} | ||
} | ||
actualOffset = utils.geometry.offset(actualOffset, scrollerRegionShift) | ||
// TODO come up with generic solution | ||
// The problem is that web default scrolling element treated differently than normal scrollable elements on the web and native | ||
const cropRegionOffset = driver.isNative | ||
? utils.geometry.offsetNegative( | ||
utils.geometry.location(await scroller.getClientRegion()), | ||
utils.geometry.location(cropRegion), | ||
) | ||
: {x: 0, y: 0} | ||
const remainingOffset = { | ||
x: requiredOffset.x - actualOffset.x - expectedRemainingOffset.x + compensateOffset.x, | ||
y: requiredOffset.y - actualOffset.y - expectedRemainingOffset.y + compensateOffset.y, | ||
} | ||
const cropPartRegion = { | ||
x: cropRegion.x + remainingOffset.x + cropRegionOffset.x, | ||
y: cropRegion.y + remainingOffset.y + cropRegionOffset.y, | ||
width: partRegion.width - cropRegionOffset.x, | ||
height: partRegion.height - cropRegionOffset.y, | ||
x: cropRegion.x + remainingOffset.x, | ||
y: cropRegion.y + remainingOffset.y, | ||
width: partRegion.width, | ||
height: partRegion.height, | ||
} | ||
@@ -117,3 +109,3 @@ logger.verbose(`Actual offset is ${actualOffset}, remaining offset is ${remainingOffset}`) | ||
logger.verbose('Getting image...') | ||
image = await takeScreenshot({name: partName}) | ||
image = await takeViewportScreenshot({name: partName}) | ||
lastImage = framed ? makeImage(image) : null | ||
@@ -126,7 +118,3 @@ | ||
const pasteOffset = utils.geometry.offsetNegative(utils.geometry.location(partRegion), initialOffset) | ||
pasteOffset.y += cropRegionOffset.y | ||
pasteOffset.x += cropRegionOffset.x | ||
await stitchedImage.copy(image, pasteOffset) | ||
stitchedSize = {width: pasteOffset.x + image.width, height: pasteOffset.y + image.height} | ||
stitchedImage.copy(image, pasteOffset) | ||
} | ||
@@ -136,14 +124,6 @@ | ||
logger.verbose(`Extracted entire size: ${region}`) | ||
logger.verbose(`Actual stitched size: ${stitchedSize}`) | ||
if (stitchedSize.width < stitchedImage.width || stitchedSize.height < stitchedImage.height) { | ||
logger.verbose('Trimming unnecessary margins...') | ||
stitchedImage.crop(utils.geometry.region({x: 0, y: 0}, stitchedSize)) | ||
} | ||
await stitchedImage.debug({...debug, name: 'stitched'}) | ||
if (framed) { | ||
await stitchedImage.combine( | ||
stitchedImage.frame( | ||
firstImage, | ||
@@ -150,0 +130,0 @@ lastImage, |
const utils = require('@applitools/utils') | ||
const makeTakeScreenshot = require('./take-screenshot') | ||
const snippets = require('@applitools/snippets') | ||
const findImagePattern = require('./find-image-pattern') | ||
const makeImage = require('./image') | ||
async function takeViewportScreenshot({context, region, withStatusBar, wait, stabilization, debug = {}, logger}) { | ||
logger.verbose('Taking image of...') | ||
function makeTakeViewportScreenshot(options) { | ||
const {driver} = options | ||
if (driver.isNative) { | ||
return makeTakeNativeScreenshot(options) | ||
} else if (driver.browserName === 'Firefox') { | ||
try { | ||
const browserVersion = Number.parseInt(driver.browserVersion, 10) | ||
if (browserVersion >= 48 && browserVersion <= 72) { | ||
// firefox between versions 48 and 72 takes current frame screenshot only | ||
return makeTakeMainContextScreenshot(options) | ||
} | ||
} catch (ignored) {} | ||
} else if (driver.browserName === 'Safari') { | ||
if (driver.isIOS) { | ||
// safari on ios takes screenshot with browser and os interfaces | ||
return makeTakeMarkedScreenshot(options) | ||
} else if (driver.browserVersion === '11') { | ||
// safari 11 on macs takes full page screenshot | ||
return makeTakeSafari11Screenshot(options) | ||
} | ||
} | ||
const driver = context.driver | ||
const takeScreenshot = makeTakeScreenshot({logger, driver, stabilization, debug}) | ||
return makeTakeDefaultScreenshot(options) | ||
} | ||
await utils.general.sleep(wait) | ||
function makeTakeDefaultScreenshot({driver, stabilization = {}, debug, logger}) { | ||
const calculateScaleRatio = makeCalculateScaleRatio({driver}) | ||
return async function takeScreenshot({name} = {}) { | ||
logger.verbose('Taking screenshot...') | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
const image = await takeScreenshot({withStatusBar}) | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(await calculateScaleRatio(image.width)) | ||
if (region) { | ||
const cropRegion = await driver.getRegionInViewport(context, region) | ||
if (utils.geometry.isEmpty(cropRegion)) throw new Error('Screenshot region is out of viewport') | ||
image.crop(cropRegion) | ||
await image.debug({path: debug.path, suffix: 'region'}) | ||
return {image, region: cropRegion} | ||
} else { | ||
return {image, region: utils.geometry.region({x: 0, y: 0}, image.size)} | ||
if (stabilization.rotate) image.crop(stabilization.rotate) | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
return image | ||
} | ||
} | ||
module.exports = takeViewportScreenshot | ||
function makeTakeMainContextScreenshot({driver, stabilization = {}, debug, logger}) { | ||
const calculateScaleRatio = makeCalculateScaleRatio({driver}) | ||
return async function takeScreenshot({name} = {}) { | ||
logger.verbose('Taking screenshot...') | ||
const originalContext = driver.currentContext | ||
await driver.mainContext.focus() | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await originalContext.focus() | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(await calculateScaleRatio(image.width)) | ||
if (stabilization.rotate) image.rotate(stabilization.rotate) | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
return image | ||
} | ||
} | ||
function makeTakeSafari11Screenshot({driver, stabilization = {}, debug, logger}) { | ||
const calculateScaleRatio = makeCalculateScaleRatio({driver}) | ||
let viewportSize | ||
return async function takeScreenshot({name} = {}) { | ||
logger.verbose('Taking safari 11 driver screenshot...') | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(await calculateScaleRatio(image.width)) | ||
if (stabilization.rotate) image.rotate(stabilization.rotate) | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
else { | ||
if (!viewportSize) viewportSize = await driver.getViewportSize() | ||
const viewportLocation = await driver.mainContext.execute(snippets.getElementScrollOffset, []) | ||
image.crop(utils.geometry.region(viewportLocation, viewportSize)) | ||
} | ||
return image | ||
} | ||
} | ||
function makeTakeMarkedScreenshot({driver, stabilization = {}, debug, logger}) { | ||
const calculateScaleRatio = makeCalculateScaleRatio({driver}) | ||
let viewportRegion | ||
return async function takeScreenshot({name} = {}) { | ||
logger.verbose('Taking viewport screenshot (using markers)...') | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(await calculateScaleRatio(image.width)) | ||
if (stabilization.rotate) image.rotate(stabilization.rotate) | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
else { | ||
if (!viewportRegion) viewportRegion = await getViewportRegion() | ||
if (viewportRegion) image.crop(viewportRegion) | ||
} | ||
await image.debug({...debug, name, suffix: 'viewport'}) | ||
return image | ||
} | ||
async function getViewportRegion() { | ||
const marker = await driver.mainContext.execute(snippets.addPageMarker) | ||
try { | ||
const image = makeImage(await driver.takeScreenshot()) | ||
if (stabilization.rotate) await image.rotate(stabilization.rotate) | ||
await image.debug({...debug, name: 'marker'}) | ||
const markerLocation = findImagePattern(await image.toObject(), {...marker, pixelRatio: driver.pixelRatio}) | ||
if (!markerLocation) return null | ||
const viewportSize = await driver.getViewportSize() | ||
return utils.geometry.region(utils.geometry.scale(markerLocation, 1 / driver.pixelRatio), viewportSize) | ||
} finally { | ||
await driver.mainContext.execute(snippets.cleanupPageMarker) | ||
} | ||
} | ||
} | ||
function makeTakeNativeScreenshot({driver, stabilization = {}, debug, logger}) { | ||
return async function takeScreenshot({name, withStatusBar} = {}) { | ||
logger.verbose('Taking native driver screenshot...') | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await image.debug({...debug, name, suffix: 'original'}) | ||
if (stabilization.scale) image.scale(stabilization.scale) | ||
else image.scale(1 / driver.pixelRatio) | ||
if (stabilization.rotate) image.rotate(stabilization.rotate) | ||
if (stabilization.crop) image.crop(stabilization.crop) | ||
else { | ||
const viewportSize = await driver.getViewportSize() | ||
const cropRegion = withStatusBar | ||
? {x: 0, y: 0, width: viewportSize.width, height: viewportSize.height + driver.statusBarHeight} | ||
: {top: driver.statusBarHeight, bottom: driver.navigationBarHeight, left: 0, right: 0} | ||
image.crop(cropRegion) | ||
} | ||
await image.debug({...debug, name, suffix: `viewport${withStatusBar ? '-with-statusbar' : ''}`}) | ||
return image | ||
} | ||
} | ||
function makeCalculateScaleRatio({driver}) { | ||
let viewportWidth, contentWidth | ||
const VIEWPORT_THRESHOLD = 1 | ||
const CONTENT_THRESHOLD = 10 | ||
return async function calculateScaleRatio(imageWidth) { | ||
if (!viewportWidth) viewportWidth = await driver.getViewportSize().then(size => size.width) | ||
if (!contentWidth) contentWidth = await driver.mainContext.getContentSize().then(size => size.width) | ||
// If the image's width is the same as the viewport's width or the | ||
// top level context's width, no scaling is necessary. | ||
if ( | ||
(imageWidth >= viewportWidth - VIEWPORT_THRESHOLD && imageWidth <= viewportWidth + VIEWPORT_THRESHOLD) || | ||
(imageWidth >= contentWidth - CONTENT_THRESHOLD && imageWidth <= contentWidth + CONTENT_THRESHOLD) | ||
) { | ||
return 1 | ||
} | ||
const scaledImageWidth = Math.round(imageWidth / driver.pixelRatio) | ||
return viewportWidth / scaledImageWidth / driver.pixelRatio | ||
} | ||
} | ||
module.exports = makeTakeViewportScreenshot |
@@ -6,3 +6,3 @@ const assert = require('assert') | ||
const makeImage = require('../../src/image') | ||
const screenshoter = require('../../index') | ||
const takeScreenshot = require('../../index') | ||
@@ -35,6 +35,16 @@ const env = { | ||
automationName: 'uiautomator2', | ||
app: 'https://applitools.jfrog.io/artifactory/Examples/androidx/1.2.0/app_androidx.apk', | ||
app: 'https://applitools.jfrog.io/artifactory/Examples/androidx/1.3.1/app_androidx.apk', | ||
username: process.env.SAUCE_USERNAME, | ||
accessKey: process.env.SAUCE_ACCESS_KEY, | ||
}, | ||
// url: 'http://0.0.0.0:4723/wd/hub', | ||
// capabilities: { | ||
// deviceName: 'Google Pixel 3a XL', | ||
// platformName: 'Android', | ||
// platformVersion: '10.0', | ||
// automationName: 'uiautomator2', | ||
// avd: 'Pixel_3a_XL', | ||
// app: 'https://applitools.jfrog.io/artifactory/Examples/androidx/1.3.3/app_androidx.apk', | ||
// }, | ||
}, | ||
@@ -127,2 +137,10 @@ } | ||
it('take full app screenshot (collapsing layout)', () => { | ||
return fullApp({type: 'collapsing', x: true}) | ||
}) | ||
it.skip('take full app screenshot (overlapped status bar)', () => { | ||
return fullApp({type: 'overlapped', x: true}) | ||
}) | ||
it('take full element screenshot', () => { | ||
@@ -136,3 +154,3 @@ return fullElement() | ||
const screenshot = await screenshoter({logger, driver, wait: 1500, ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, wait: 1500, ...options}) | ||
try { | ||
@@ -149,4 +167,11 @@ if (options.withStatusBar) await sanitizeStatusBar(screenshot.image) | ||
async function fullApp({type, x, ...options} = {}) { | ||
let buttonSelector, expectedPath | ||
if (type === 'recycler') { | ||
let buttonSelector, expectedPath, scrollingElementSelector | ||
if (type === 'overlapped') { | ||
buttonSelector = {type: 'id', selector: 'btn_recycler_view_under_status_bar_activity'} | ||
expectedPath = `./test/fixtures/android/x-app-fully-overlapped${options.withStatusBar ? '-statusbar' : ''}.png` | ||
} else if (type === 'collapsing') { | ||
buttonSelector = {type: 'id', selector: 'btn_recycler_view_nested_collapsing'} | ||
scrollingElementSelector = {type: 'id', selector: 'recyclerView'} | ||
expectedPath = `./test/fixtures/android/x-app-fully-collapsing${options.withStatusBar ? '-statusbar' : ''}.png` | ||
} else if (type === 'recycler') { | ||
if (x) { | ||
@@ -160,3 +185,3 @@ buttonSelector = {type: 'id', selector: 'btn_recycler_view_activity'} | ||
} else if (type === 'non-scrollable') { | ||
buttonSelector = {type: 'id', selector: 'btn_edit_text'} | ||
buttonSelector = {type: 'id', selector: 'btn_activity_as_dialog'} | ||
expectedPath = `./test/fixtures/android/app-fully-non-scrollable${options.withStatusBar ? '-statusbar' : ''}.png` | ||
@@ -171,3 +196,9 @@ } else { | ||
const screenshot = await screenshoter({ | ||
await driver.init() | ||
if (scrollingElementSelector) { | ||
await driver.currentContext.setScrollingElement(scrollingElementSelector) | ||
} | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -192,3 +223,3 @@ driver, | ||
async function region(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -210,3 +241,3 @@ driver, | ||
async function fullRegion(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -229,3 +260,3 @@ driver, | ||
async function element(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -253,3 +284,3 @@ driver, | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -256,0 +287,0 @@ driver, |
@@ -7,3 +7,3 @@ const assert = require('assert') | ||
const makeImage = require('../../src/image') | ||
const screenshoter = require('../../index') | ||
const takeScreenshot = require('../../index') | ||
@@ -18,3 +18,3 @@ describe.skip('external tests', () => { | ||
it('AGL - full app screenshot of the view with animated scroll', async () => { | ||
it('AGL Android - full app screenshot of the view with animated scroll', async () => { | ||
const expectedPath = `./test/fixtures/external/agl.png` | ||
@@ -28,3 +28,3 @@ | ||
'appium:deviceName': 'Google Pixel 5', | ||
'appium:app': 'android_agl_app', | ||
'appium:app': 'agl_app_android', | ||
'bstack:options': { | ||
@@ -47,3 +47,3 @@ userName: process.env.BROWSERSTACK_USERNAME, | ||
await signinBtn.click() | ||
await utils.general.sleep(8000) | ||
await utils.general.sleep(10000) | ||
const emailInput = await browser.$('//android.widget.EditText') | ||
@@ -53,3 +53,3 @@ await emailInput.setValue('daniel.martin@toro.com') | ||
await nxtBtn.click() | ||
await utils.general.sleep(8000) | ||
await utils.general.sleep(10000) | ||
const passwordInput = await browser.$('//android.widget.EditText[@password="true"]') | ||
@@ -60,3 +60,2 @@ await passwordInput.setValue('Welcome@1') | ||
await utils.general.sleep(18000) | ||
const skipBtn = await browser.$('//android.widget.Button[@text="SKIP"]') | ||
@@ -69,7 +68,5 @@ await skipBtn.click() | ||
// const billingBtn = await browser.$('//android.widget.FrameLayout[@content-desc="Billing"]') | ||
// await billingBtn.click() | ||
// await utils.general.sleep(25000) | ||
await driver.init() | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -91,2 +88,76 @@ driver, | ||
}) | ||
it('AGL iOS - full app screenshot of the view with animated scroll', async () => { | ||
const expectedPath = `./test/fixtures/external/agl.png` | ||
;[browser, destroyBrowser] = await spec.build({ | ||
url: 'https://hub.browserstack.com/wd/hub', | ||
capabilities: { | ||
platformName: 'ios', | ||
'appium:platformVersion': '14', | ||
'appium:deviceName': 'iPhone 12', | ||
'appium:app': 'agl_app', | ||
'appium:processArguments': `{ | ||
"args": [ | ||
"-dev.environment", "qtrtest", | ||
"-dev.clear.keychain", "YES", | ||
"-dev.keychain.shared", "NO", | ||
"-user.lastQuickTourVersionCompleted", "0" | ||
], | ||
"env": { | ||
"DYLD_INSERT_LIBRARIES": "@executable_path/Frameworks/EyesiOSHelper.xcframework/ios-arm64/EyesiOSHelper.framework/EyesiOSHelper" | ||
} | ||
}`, | ||
'bstack:options': { | ||
userName: process.env.BROWSERSTACK_USERNAME, | ||
accessKey: process.env.BROWSERSTACK_ACCESS_KEY, | ||
}, | ||
}, | ||
}) | ||
await browser.closeApp() | ||
await browser.launchApp() | ||
const driver = await new Driver({driver: browser, spec, logger}).init() | ||
await browser.acceptAlert() | ||
await utils.general.sleep(8000) | ||
const signinBtn = await browser.$('//XCUIElementTypeButton[@name="SIGN IN"]') | ||
await signinBtn.click() | ||
await utils.general.sleep(8000) | ||
const emailInput = await browser.$('//XCUIElementTypeTextField') | ||
await emailInput.setValue('daniel.martin@toro.com') | ||
const nxtBtn = await browser.$('//XCUIElementTypeButton[@name="NEXT"]') | ||
await nxtBtn.click() | ||
await utils.general.sleep(8000) | ||
const passwordInput = await browser.$('//XCUIElementTypeSecureTextField') | ||
await passwordInput.setValue('Welcome@1') | ||
const loginBtn = await browser.$('//XCUIElementTypeButton[@name="LOGIN"]') | ||
await loginBtn.click() | ||
await utils.general.sleep(18000) | ||
const skipBtn = await browser.$('//XCUIElementTypeButton[@name="Skip"]') | ||
await skipBtn.click() | ||
await utils.general.sleep(5000) | ||
const finishBtn = await browser.$('//XCUIElementTypeButton[@name="Finish"]') | ||
await finishBtn.click() | ||
await utils.general.sleep(20000) | ||
await driver.init() | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
driver, | ||
fully: true, | ||
framed: true, | ||
wait: 1500, | ||
// debug: {path: './'}, | ||
}) | ||
try { | ||
const actual = await screenshot.image.toObject() | ||
const expected = await makeImage(expectedPath).toObject() | ||
assert.strictEqual(pixelmatch(actual.data, expected.data, null, expected.width, expected.height), 0) | ||
} catch (err) { | ||
await screenshot.image.debug({path: './logs', name: 'viewport_failed', suffix: Date.now()}) | ||
throw err | ||
} | ||
}) | ||
}) |
@@ -6,3 +6,3 @@ const assert = require('assert') | ||
const makeImage = require('../../src/image') | ||
const screenshoter = require('../../index') | ||
const takeScreenshot = require('../../index') | ||
@@ -18,6 +18,14 @@ const env = { | ||
automationName: 'XCUITest', | ||
app: 'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.5/app/IOSTestApp-1.5.zip', | ||
app: 'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.8/app/IOSTestApp.zip', | ||
username: process.env.SAUCE_USERNAME, | ||
accessKey: process.env.SAUCE_ACCESS_KEY, | ||
}, | ||
// url: 'http://0.0.0.0:4723/wd/hub', | ||
// capabilities: { | ||
// deviceName: 'iPhone 11 Pro', | ||
// platformName: 'iOS', | ||
// platformVersion: '14.5', | ||
// app: '/Users/kyrylo/Downloads/IOSTestApp.zip', | ||
// }, | ||
} | ||
@@ -82,2 +90,18 @@ | ||
it('take full app screenshot (table view with collapsing header)', () => { | ||
return fullApp({type: 'collapsing'}) | ||
}) | ||
it('take full app screenshot (collection view with overlapped status bar)', () => { | ||
return fullApp({type: 'overlapped'}) | ||
}) | ||
it('take full app screenshot with status bar (collection view with overlapped status bar)', () => { | ||
return fullApp({type: 'overlapped', withStatusBar: true}) | ||
}) | ||
it('take full app screenshot (collection view with superview)', () => { | ||
return fullApp({type: 'superview'}) | ||
}) | ||
it('take region screenshot', () => { | ||
@@ -102,3 +126,3 @@ return region() | ||
const screenshot = await screenshoter({logger, driver, ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, ...options}) | ||
try { | ||
@@ -115,4 +139,15 @@ if (options.withStatusBar) await sanitizeStatusBar(screenshot.image) | ||
async function fullApp({type, ...options} = {}) { | ||
let buttonSelector, expectedPath | ||
if (type === 'collection') { | ||
let buttonSelector, expectedPath, overlap | ||
if (type === 'superview') { | ||
overlap = {top: 200} | ||
buttonSelector = {type: 'accessibility id', selector: 'Bottom to superview'} | ||
expectedPath = `./test/fixtures/ios/app-fully-superview${options.withStatusBar ? '-statusbar' : ''}.png` | ||
} else if (type === 'overlapped') { | ||
overlap = {top: 200} | ||
buttonSelector = {type: 'accessibility id', selector: 'Bottom to safe area'} | ||
expectedPath = `./test/fixtures/ios/app-fully-overlapped${options.withStatusBar ? '-statusbar' : ''}.png` | ||
} else if (type === 'collapsing') { | ||
buttonSelector = {type: 'accessibility id', selector: 'Table view with stretchable header'} | ||
expectedPath = `./test/fixtures/ios/app-fully-collapsing${options.withStatusBar ? '-statusbar' : ''}.png` | ||
} else if (type === 'collection') { | ||
buttonSelector = {type: 'accessibility id', selector: 'Collection view'} | ||
@@ -131,3 +166,5 @@ expectedPath = `./test/fixtures/ios/app-fully-collection${options.withStatusBar ? '-statusbar' : ''}.png` | ||
const screenshot = await screenshoter({ | ||
await driver.init() | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -139,2 +176,3 @@ driver, | ||
wait: 1500, | ||
overlap: {top: 10, bottom: 50, ...overlap}, | ||
...options, | ||
@@ -153,3 +191,3 @@ }) | ||
async function region(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -172,3 +210,3 @@ driver, | ||
async function fullRegion(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -191,3 +229,3 @@ driver, | ||
async function element(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -215,3 +253,3 @@ driver, | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -218,0 +256,0 @@ driver, |
@@ -5,3 +5,3 @@ const assert = require('assert') | ||
const spec = require('@applitools/spec-driver-webdriverio') | ||
const screenshoter = require('../../index') | ||
const takeScreenshot = require('../../index') | ||
const makeImage = require('../../src/image') | ||
@@ -24,4 +24,2 @@ | ||
// TODO add tests for page without viewport meta tag | ||
describe('screenshoter web ios', () => { | ||
@@ -54,3 +52,3 @@ const logger = {log: () => {}, warn: () => {}, error: () => {}, verbose: () => {}} | ||
async function viewport(options) { | ||
const screenshot = await screenshoter({logger, driver, ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, ...options}) | ||
try { | ||
@@ -67,3 +65,3 @@ const actual = await screenshot.image.toObject() | ||
async function fullPage(options) { | ||
const screenshot = await screenshoter({logger, driver, fully: true, ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, fully: true, ...options}) | ||
try { | ||
@@ -70,0 +68,0 @@ const actual = await screenshot.image.toObject() |
@@ -5,3 +5,3 @@ const assert = require('assert') | ||
const spec = require('@applitools/spec-driver-webdriverio') | ||
const screenshoter = require('../../index') | ||
const takeScreenshot = require('../../index') | ||
const makeImage = require('../../src/image') | ||
@@ -132,3 +132,3 @@ | ||
async function viewport(options) { | ||
const screenshot = await screenshoter({logger, driver, ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, ...options}) | ||
try { | ||
@@ -144,3 +144,3 @@ const actual = await screenshot.image.toObject() | ||
async function fullPage(options) { | ||
const screenshot = await screenshoter({logger, driver, fully: true, ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, fully: true, ...options}) | ||
try { | ||
@@ -156,3 +156,8 @@ const actual = await screenshot.image.toObject() | ||
async function frame(options) { | ||
const screenshot = await screenshoter({logger, driver, frames: [{reference: 'iframe[name="frame1"]'}], ...options}) | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
driver, | ||
frames: [{reference: 'iframe[name="frame1"]'}], | ||
...options, | ||
}) | ||
try { | ||
@@ -168,3 +173,3 @@ const actual = await screenshot.image.toObject() | ||
async function fullFrame(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -187,3 +192,3 @@ driver, | ||
const region = {x: 30, y: 500, height: 100, width: 200} | ||
const screenshot = await screenshoter({logger, driver, region, ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, region, ...options}) | ||
try { | ||
@@ -200,3 +205,3 @@ const actual = await screenshot.image.toObject() | ||
const region = {x: 30, y: 500, height: 700, width: 200} | ||
const screenshot = await screenshoter({logger, driver, region, fully: true, ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, region, fully: true, ...options}) | ||
try { | ||
@@ -212,3 +217,3 @@ const actual = await screenshot.image.toObject() | ||
async function element(options) { | ||
const screenshot = await screenshoter({logger, driver, region: '#overflowing-div-image', ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, region: '#overflowing-div-image', ...options}) | ||
try { | ||
@@ -224,3 +229,3 @@ const actual = await screenshot.image.toObject() | ||
async function fullElement(options) { | ||
const screenshot = await screenshoter({logger, driver, region: '#overflowing-div-image', fully: true, ...options}) | ||
const screenshot = await takeScreenshot({logger, driver, region: '#overflowing-div-image', fully: true, ...options}) | ||
try { | ||
@@ -236,3 +241,3 @@ const actual = await screenshot.image.toObject() | ||
async function regionInFrame(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -254,3 +259,3 @@ driver, | ||
async function fullRegionInFrame(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -273,3 +278,3 @@ driver, | ||
async function elementInFrame(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -291,3 +296,3 @@ driver, | ||
async function fullElementInFrame(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -310,3 +315,3 @@ driver, | ||
async function frameInFrame(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -327,3 +332,3 @@ driver, | ||
async function fullFrameInFrame(options) { | ||
const screenshot = await screenshoter({ | ||
const screenshot = await takeScreenshot({ | ||
logger, | ||
@@ -330,0 +335,0 @@ driver, |
@@ -7,19 +7,24 @@ const assert = require('assert') | ||
const fixtures = [ | ||
{name: 'iPhone_5S_landscape', position: {x: 0, y: 100}, pixelRatio: 2}, | ||
{name: 'iPhone_X_perfecto_portrait', position: {x: 0, y: 297}, pixelRatio: 3}, | ||
{name: 'iPhone_XR_perfecto_landscape', position: {x: 88, y: 100}, pixelRatio: 2}, | ||
{name: 'iPhone_XS_Max_perfecto_landscape', position: {x: 132, y: 150}, pixelRatio: 3}, | ||
{name: 'iPhone_XS_landscape', position: {x: 132, y: 150}, pixelRatio: 3}, | ||
{name: 'iPhone_XS_portrait', position: {x: 0, y: 282}, pixelRatio: 3}, | ||
{name: 'iPad_Air_portrait', position: {x: 0, y: 140}, pixelRatio: 2}, | ||
{name: 'iPhone_SE_portrait', position: {x: 0, y: 140}, offset: 0, pixelRatio: 2}, | ||
{name: 'iPhone_SE_landscape', position: {x: 0, y: 100}, offset: 0, pixelRatio: 2}, | ||
{name: 'iPhone_11_portrait', position: {x: 0, y: 282}, offset: 0, pixelRatio: 3}, | ||
{name: 'iPhone_11_landscape', position: {x: 132, y: 150}, offset: 0, pixelRatio: 3}, | ||
{name: 'iPhone_13_portrait', position: {x: 0, y: 141}, offset: 0, pixelRatio: 3}, | ||
{name: 'iPhone_13_landscape', position: {x: 141, y: 144}, offset: 0, pixelRatio: 3}, | ||
{name: 'iPad_5th_portrait', position: {x: 0, y: 140}, offset: 0, pixelRatio: 2}, | ||
{name: 'iPad_5th_landscape', position: {x: 0, y: 140}, offset: 0, pixelRatio: 2}, | ||
{name: 'iPad_9th_portrait', position: {x: 0, y: 136}, offset: 0, pixelRatio: 2}, | ||
{name: 'iPad_9th_landscape', position: {x: 641, y: 137}, offset: 1, pixelRatio: 2}, | ||
{name: 'iPhone_XS_portrait_noviewport', position: {x: 0, y: 282}, offset: 0, pixelRatio: 3}, | ||
{name: 'iPhone_XS_portrait_nomarker', position: null, pixelRatio: 3}, | ||
] | ||
fixtures.forEach(({name, position, pixelRatio}) => { | ||
fixtures.forEach(({name, position, offset, pixelRatio}) => { | ||
it(name, async () => { | ||
const image = await makeImage(`./test/fixtures/pattern/${name}.png`) | ||
const result = findPattern(await image.toObject(), { | ||
offset: 1 * pixelRatio, | ||
size: 3 * pixelRatio, | ||
mask: [0, 1, 0], | ||
pixelRatio, | ||
mask: [1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1], | ||
offset, | ||
size: 1, | ||
}) | ||
@@ -26,0 +31,0 @@ assert.deepStrictEqual(result, position) |
@@ -81,3 +81,3 @@ const assert = require('assert') | ||
it('should replace region in image with a higher and wider image', async () => { | ||
it('should frame image in a higher and wider region', async () => { | ||
const image = makeImage('./test/fixtures/image/house.png') | ||
@@ -89,9 +89,9 @@ const srcImage = makeImage({ | ||
}) | ||
const combinedImage = await srcImage.combine(image, image, {x: 200, y: 200, width: 100, height: 100}) | ||
const combinedImage = await srcImage.frame(image, image, {x: 200, y: 200, width: 100, height: 100}) | ||
const actual = await combinedImage.toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.combined-higher-wider.png').toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.framed-higher-wider.png').toObject() | ||
assert.ok(pixelmatch(actual.data, expected.data, null, expected.width, expected.height) === 0) | ||
}) | ||
it('should replace region in image with a higher image', async () => { | ||
it('should frame image in a higher region', async () => { | ||
const image = await makeImage('./test/fixtures/image/house.png') | ||
@@ -103,20 +103,47 @@ const srcImage = makeImage({ | ||
}) | ||
const combinedImage = await srcImage.combine(image, image, {x: 200, y: 200, width: 200, height: 100}) | ||
const combinedImage = await srcImage.frame(image, image, {x: 200, y: 200, width: 200, height: 100}) | ||
const actual = await combinedImage.toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.combined-higher.png').toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.framed-higher.png').toObject() | ||
assert.ok(pixelmatch(actual.data, expected.data, null, expected.width, expected.height) === 0) | ||
}) | ||
it('should replace region in image with a higher image', async () => { | ||
it('should frame image in a wider region', async () => { | ||
const image = await makeImage('./test/fixtures/image/house.png') | ||
const srcImage = makeImage({ | ||
width: 200, | ||
height: 200, | ||
data: Buffer.alloc(200 * 200 * 4, Buffer.from([0, 0, 0xff, 0xff])), | ||
}) | ||
const combinedImage = await srcImage.combine(image, image, {x: 200, y: 200, width: 100, height: 200}) | ||
const actual = await combinedImage.toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.combined-wider.png').toObject() | ||
const data = Buffer.alloc(200 * 200 * 4, Buffer.from([0, 0, 0xff, 0xff])) | ||
const actual = await makeImage({width: 200, height: 200, data}) | ||
.frame(image, image, {x: 200, y: 200, width: 100, height: 200}) | ||
.toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.framed-wider.png').toObject() | ||
assert.ok(pixelmatch(actual.data, expected.data, null, expected.width, expected.height) === 0) | ||
}) | ||
it('should frame image in a shorter and thinner region', async () => { | ||
const image = await makeImage('./test/fixtures/image/house.png') | ||
const data = Buffer.alloc(200 * 200 * 4, Buffer.from([0xff, 0, 0xff, 0xff])) | ||
const actual = await makeImage({width: 200, height: 200, data}) | ||
.frame(image, image, {x: 100, y: 100, width: 250, height: 250}) | ||
.toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.framed-shorter-thinner.png').toObject() | ||
assert.ok(pixelmatch(actual.data, expected.data, null, expected.width, expected.height) === 0) | ||
}) | ||
it('should frame image in a shorter region', async () => { | ||
const image = await makeImage('./test/fixtures/image/house.png') | ||
const data = Buffer.alloc(200 * 200 * 4, Buffer.from([0xff, 0, 0xff, 0xff])) | ||
const actual = await makeImage({width: 200, height: 200, data}) | ||
.frame(image, image, {x: 100, y: 100, width: 200, height: 250}) | ||
.toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.framed-shorter-thinner.png').toObject() | ||
assert.ok(pixelmatch(actual.data, expected.data, null, expected.width, expected.height) === 0) | ||
}) | ||
it('should frame image in a thinner region', async () => { | ||
const image = await makeImage('./test/fixtures/image/house.png') | ||
const data = Buffer.alloc(200 * 200 * 4, Buffer.from([0xff, 0, 0xff, 0xff])) | ||
const actual = await makeImage({width: 200, height: 200, data}) | ||
.frame(image, image, {x: 100, y: 100, width: 250, height: 200}) | ||
.toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.framed-shorter-thinner.png').toObject() | ||
assert.ok(pixelmatch(actual.data, expected.data, null, expected.width, expected.height) === 0) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
43400030
2400
92
14
+ Added@applitools/snippets@2.1.8(transitive)
- Removed@applitools/snippets@2.1.7(transitive)
Updated@applitools/snippets@2.1.8