@applitools/screenshoter
Advanced tools
Comparing version 3.0.8 to 3.1.0
@@ -7,12 +7,18 @@ { | ||
"version": "file:../dry-run.tgz", | ||
"integrity": "sha512-ET3GIWJY2aZvEteoEj3CkP5d5d38EoIj1maN65Sv/F+NusrZHaaCpuc/goOKL1JqFnHM8QiMHVywZlBZ3WGXmg==", | ||
"integrity": "sha512-FDyK0CsvbGvDngoG0ZWcaoBwko0y1SsQKnJrGzsgsw6baI/yYlA2z7V8NdhOhS+js2sQ8YblsMWd+oTj5ZPFZQ==", | ||
"requires": { | ||
"@applitools/utils": "1.2.0", | ||
"@applitools/snippets": "2.1.4", | ||
"@applitools/utils": "1.2.1", | ||
"png-async": "0.9.4" | ||
} | ||
}, | ||
"@applitools/snippets": { | ||
"version": "2.1.4", | ||
"resolved": "https://registry.npmjs.org/@applitools/snippets/-/snippets-2.1.4.tgz", | ||
"integrity": "sha512-Jmp+DM9Kj24+ByMaqoKxhMNsefsSshN5+MLyMyrFurb1FGRGoSMmAT5bZCI7zKFG02pxZmjBmenMhpNQbzZR/A==" | ||
}, | ||
"@applitools/utils": { | ||
"version": "1.2.0", | ||
"resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.0.tgz", | ||
"integrity": "sha512-NzynRgRavbTns5M85DJXXaMY/57xBte3F/tnK5nr3DSBhKTQnkjXIFwryPU5bVwEcpoGEVF0T1Gyq37WnDe38Q==" | ||
"version": "1.2.1", | ||
"resolved": "https://registry.npmjs.org/@applitools/utils/-/utils-1.2.1.tgz", | ||
"integrity": "sha512-lIHYqgirhkyL+xAc9+FwidAvcI7apB/6Y7uyVimHdTRjjIwAqoazymwYiFZiia+q81F/smPLvMw2RUwU6wi13w==" | ||
}, | ||
@@ -19,0 +25,0 @@ "png-async": { |
@@ -7,2 +7,8 @@ | ||
## 3.1.0 - 2021/8/4 | ||
- improve support of native devices | ||
- updated to @applitools/snippets@2.1.4 (from 2.1.3) | ||
- updated to @applitools/utils@1.2.1 (from 1.2.0) | ||
## 3.0.8 - 2021/5/24 | ||
@@ -14,4 +20,4 @@ | ||
fixed image cropping algorithm to not copy data into a heap | ||
optimized image rotation and image copping algorithms | ||
- fixed image cropping algorithm to not copy data into a heap | ||
- optimized image rotation and image copping algorithms | ||
@@ -18,0 +24,0 @@ ## 3.0.6 - 2021/5/11 |
{ | ||
"name": "@applitools/screenshoter", | ||
"version": "3.0.8", | ||
"version": "3.1.0", | ||
"description": "Applitools universal screenshoter for web and native applications", | ||
@@ -34,5 +34,5 @@ "keywords": [ | ||
"lint": "eslint . --ext .js", | ||
"test": "yarn test:it && yarn test:coverage", | ||
"test": "yarn test:it && yarn test:e2e", | ||
"test:it": "mocha --no-timeouts './test/it/*.spec.js'", | ||
"test:coverage": "mocha --no-timeouts './test/coverage/*.spec.js'", | ||
"test:e2e": "mocha --no-timeouts './test/e2e/*.spec.js'", | ||
"docker:setup": "node ../scripts/scripts/generate-docker-compose-config.js && docker-compose up -d", | ||
@@ -52,7 +52,8 @@ "docker:teardown": "docker-compose down", | ||
"dependencies": { | ||
"@applitools/utils": "1.2.0", | ||
"@applitools/snippets": "2.1.4", | ||
"@applitools/utils": "1.2.1", | ||
"png-async": "0.9.4" | ||
}, | ||
"devDependencies": { | ||
"@applitools/driver": "1.0.6", | ||
"@applitools/driver": "1.1.0", | ||
"@applitools/sdk-release-kit": "0.13.0", | ||
@@ -59,0 +60,0 @@ "eslint": "^7.9.0", |
function findPattern(image, pattern) { | ||
for (let pixel = 0; pixel < image.info.width * image.info.height; ++pixel) { | ||
for (let pixel = 0; pixel < image.width * image.height; ++pixel) { | ||
if (isPattern(image, pixel, pattern)) { | ||
return { | ||
x: (pixel % image.info.width) - pattern.offset, | ||
y: Math.floor(pixel / image.info.width) - pattern.offset, | ||
x: (pixel % image.width) - pattern.offset, | ||
y: Math.floor(pixel / image.width) - pattern.offset, | ||
} | ||
@@ -14,2 +14,3 @@ } | ||
function isPattern(image, index, pattern) { | ||
const channels = 4 | ||
const roundNumber = pattern.size - Math.floor(pattern.size / 2) | ||
@@ -20,3 +21,3 @@ for (const [chunkIndex, chunkColor] of pattern.mask.entries()) { | ||
const sideLength = pattern.size - round * 2 | ||
const stepsNumber = sideLength * 4 - 4 | ||
const stepsNumber = sideLength * channels - channels | ||
const threshold = Math.min((roundNumber - round) * 10 + 10, 100) | ||
@@ -47,3 +48,3 @@ for (let step = 0; step < stepsNumber; ++step) { | ||
function pixelColorAt(image, index, threshold = 0) { | ||
const channels = image.info.channels | ||
const channels = 4 | ||
const r = image.data[index * channels] | ||
@@ -50,0 +51,0 @@ const g = image.data[index * channels + 1] |
188
src/image.js
const fs = require('fs') | ||
const stream = require('stream') | ||
const path = require('path') | ||
const png = require('png-async') | ||
@@ -19,8 +20,19 @@ const utils = require('@applitools/utils') | ||
size = extractPngSize(data) | ||
} else { | ||
} else if (data.isImage) { | ||
image = data.toObject() | ||
size = data.size | ||
} else if (utils.types.has(data, ['width', 'height'])) { | ||
image = fromSize(data) | ||
size = data | ||
} else { | ||
throw new Error('Unable to create an image abstraction from unknown data') | ||
} | ||
return { | ||
get isImage() { | ||
return true | ||
}, | ||
get size() { | ||
return image.width && image.height ? {width: image.width, height: image.height} : size | ||
}, | ||
get width() { | ||
@@ -37,3 +49,3 @@ return image.width || size.width | ||
async crop(region) { | ||
image = await crop(await image, region) | ||
image = await extract(await image, region) | ||
return this | ||
@@ -49,2 +61,13 @@ }, | ||
}, | ||
async replace(image2, region) { | ||
image = await replace(await image, image2, region) | ||
return this | ||
}, | ||
async debug(debug) { | ||
if (!debug || !debug.path) return | ||
const timestamp = new Date().toISOString().replace(/[-T:.]/g, '_') | ||
const filename = | ||
['screenshot', timestamp, debug.name, debug.suffix].filter(part => part).join('_') + '.png' | ||
return toFile(await image, path.join(debug.path, filename)).catch(() => null) | ||
}, | ||
async toObject() { | ||
@@ -67,2 +90,8 @@ image = await image | ||
function extractPngSize(buffer) { | ||
return buffer.slice(12, 16).toString('ascii') === 'IHDR' | ||
? {width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20)} | ||
: {width: 0, height: 0} | ||
} | ||
async function fromBuffer(buffer) { | ||
@@ -109,8 +138,2 @@ return new Promise((resolve, reject) => { | ||
function extractPngSize(buffer) { | ||
return buffer.slice(12, 16).toString('ascii') === 'IHDR' | ||
? {width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20)} | ||
: {width: 0, height: 0} | ||
} | ||
async function scale(image, scaleRatio) { | ||
@@ -145,3 +168,3 @@ if (scaleRatio === 1) return image | ||
async function crop(image, region) { | ||
async function extract(image, region) { | ||
if (utils.types.has(region, ['left', 'right', 'top', 'bottom'])) { | ||
@@ -161,25 +184,20 @@ region = { | ||
const extracted = new png.Image({filterType: 4, width: dstWidth, height: dstHeight}) | ||
if (srcX === 0 && dstWidth === image.width) { | ||
const srcOffset = srcY * image.width * 4 | ||
const dstLength = dstWidth * dstHeight * 4 | ||
image.data = image.data.subarray(srcOffset, srcOffset + dstLength) | ||
image.width = dstWidth | ||
image.height = dstHeight | ||
return image | ||
extracted.data.set(image.data.subarray(srcOffset, srcOffset + dstLength)) | ||
} else { | ||
const chunkLength = dstWidth * 4 | ||
for (let chunk = 0; chunk < dstHeight; ++chunk) { | ||
const srcOffset = ((srcY + chunk) * image.width + srcX) * 4 | ||
extracted.data.set( | ||
image.data.subarray(srcOffset, srcOffset + chunkLength), | ||
chunk * chunkLength, | ||
) | ||
} | ||
} | ||
const cropped = Buffer.alloc(dstWidth * dstHeight * 4) | ||
const chunkLength = dstWidth * 4 | ||
for (let chunk = 0; chunk < dstHeight; ++chunk) { | ||
const srcOffset = ((srcY + chunk) * image.width + srcX) * 4 | ||
cropped.set(image.data.subarray(srcOffset, srcOffset + chunkLength), chunk * chunkLength) | ||
} | ||
image.data = cropped | ||
image.width = dstWidth | ||
image.height = dstHeight | ||
return image | ||
return extracted | ||
} | ||
@@ -190,5 +208,4 @@ | ||
const dstImage = { | ||
data: Buffer.alloc(image.data.length), | ||
} | ||
const dstImage = new png.Image({filterType: 4, width: image.width, height: image.height}) | ||
if (degrees === 90) { | ||
@@ -222,10 +239,6 @@ dstImage.width = image.height | ||
} else { | ||
return image | ||
return dstImage.data.set(image.data) | ||
} | ||
image.data = dstImage.data | ||
image.width = dstImage.width | ||
image.height = dstImage.height | ||
return image | ||
return dstImage | ||
} | ||
@@ -242,3 +255,2 @@ | ||
dstImage.data.set(srcImage.data.subarray(0, srcWidth * srcHeight * 4), dstOffset) | ||
return dstImage | ||
@@ -257,2 +269,104 @@ } | ||
async function replace(baseImage, srcImage, region) { | ||
region = utils.geometry.intersect( | ||
{x: 0, y: 0, width: baseImage.width, height: baseImage.height}, | ||
region, | ||
) | ||
if ( | ||
region.x === 0 && | ||
region.y === 0 && | ||
region.width >= baseImage.width && | ||
region.height >= baseImage.height | ||
) { | ||
return srcImage | ||
} | ||
if (region.width === srcImage.width && region.height === srcImage.height) { | ||
await copy(baseImage, srcImage, {x: region.x, y: region.y}) | ||
return baseImage | ||
} | ||
const dstImage = new png.Image({ | ||
filterType: 4, | ||
width: baseImage.width - region.width + srcImage.width, | ||
height: baseImage.height - region.height + srcImage.height, | ||
}) | ||
if (region.width === srcImage.width) { | ||
const topImage = await extract(baseImage, { | ||
x: 0, | ||
y: 0, | ||
width: baseImage.width, | ||
height: region.y + region.height, | ||
}) | ||
await copy(dstImage, topImage, {x: 0, y: 0}) | ||
} else if (region.height === srcImage.height) { | ||
const leftImage = await extract(baseImage, { | ||
x: 0, | ||
y: 0, | ||
width: region.x + region.width, | ||
height: baseImage.height, | ||
}) | ||
await copy(dstImage, leftImage, {x: 0, y: 0}) | ||
} else { | ||
const topLeftImage = await extract(baseImage, { | ||
x: 0, | ||
y: 0, | ||
width: region.x + region.width, | ||
height: region.y + region.height, | ||
}) | ||
await copy(dstImage, topLeftImage, {x: 0, y: 0}) | ||
const topRightImage = await extract(baseImage, { | ||
x: region.x + region.width, | ||
y: 0, | ||
width: baseImage.width - (region.x + region.width), | ||
height: region.y + region.height, | ||
}) | ||
await copy(dstImage, topRightImage, {x: region.x + srcImage.width, y: 0}) | ||
} | ||
await copy(dstImage, srcImage, {x: region.x, y: region.y}) | ||
if (baseImage.height > region.y + region.height) { | ||
if (region.width === srcImage.width) { | ||
const bottomImage = await extract(baseImage, { | ||
x: 0, | ||
y: region.y + region.height, | ||
width: baseImage.width, | ||
height: baseImage.height - (region.y + region.height), | ||
}) | ||
await copy(dstImage, bottomImage, {x: 0, y: region.y + srcImage.height}) | ||
} else if (region.height === srcImage.height) { | ||
const rightImage = await extract(baseImage, { | ||
x: region.x + region.width, | ||
y: 0, | ||
width: baseImage.width - (region.x + region.width), | ||
height: baseImage.height, | ||
}) | ||
await copy(dstImage, rightImage, {x: region.x + srcImage.width, y: 0}) | ||
} else { | ||
const bottomLeftImage = await extract(baseImage, { | ||
x: 0, | ||
y: region.y + region.height, | ||
width: region.x + region.width, | ||
height: baseImage.height - (region.y + region.height), | ||
}) | ||
await copy(dstImage, bottomLeftImage, {x: 0, y: region.y + srcImage.height}) | ||
const bottomRightImage = await extract(baseImage, { | ||
x: region.x + region.width, | ||
y: region.y + region.height, | ||
width: baseImage.width - (region.x + region.width), | ||
height: baseImage.height - (region.y + region.height), | ||
}) | ||
await copy(dstImage, bottomRightImage, { | ||
x: region.x + srcImage.width, | ||
y: region.y + srcImage.height, | ||
}) | ||
} | ||
} | ||
return dstImage | ||
} | ||
function _interpolateCubic(x0, x1, x2, x3, t) { | ||
@@ -259,0 +373,0 @@ const a0 = x3 - x2 - x0 + x1 |
const utils = require('@applitools/utils') | ||
const snippets = require('@applitools/snippets') | ||
const makeScroller = require('./scroller') | ||
@@ -17,4 +18,6 @@ const takeStitchedScreenshot = require('./takeStitchedScreenshot') | ||
overlap, | ||
framed, | ||
wait, | ||
dom, | ||
lazyRestorePageState, | ||
stabilization, | ||
@@ -25,3 +28,2 @@ debug, | ||
const originalContext = driver.currentContext | ||
const defaultScroller = makeScroller({logger, scrollingMode}) | ||
@@ -32,8 +34,9 @@ const targetContext = | ||
: originalContext | ||
const scrollingStates = [] | ||
for (const nextContext of targetContext.path) { | ||
const scrollingElement = await nextContext.getScrollRootElement() | ||
if (hideScrollbars) await scrollingElement.hideScrollbars() | ||
const scrollingState = await defaultScroller.getState(scrollingElement) | ||
scrollingStates.push(scrollingState) | ||
const scrollingElement = await nextContext.getScrollingElement() | ||
if (scrollingElement) { | ||
if (driver.isWeb && hideScrollbars) await scrollingElement.hideScrollbars() | ||
await scrollingElement.preserveState() | ||
} | ||
} | ||
@@ -43,5 +46,7 @@ | ||
const window = !target && (!frames || frames.length === 0) | ||
const {context, scroller, region} = await getTargetArea({ | ||
logger, | ||
context: targetContext, | ||
window, | ||
target, | ||
@@ -52,5 +57,8 @@ fully, | ||
const scrollerState = await scroller.getState() | ||
// IMHO problem with scrollbars should be solved by extracting client size of the content (without scrollbars), | ||
// here we use a historical solution | ||
if (driver.isWeb && (hideScrollbars || fully)) await scroller.element.hideScrollbars() | ||
const scrollerState = await scroller.preserveState() | ||
await scrollIntoViewport({logger, context, scroller, region}) | ||
if (!window) await scrollIntoViewport({logger, context, scroller, region}) | ||
@@ -65,2 +73,3 @@ try { | ||
overlap, | ||
framed: framed && !region, | ||
wait, | ||
@@ -72,16 +81,30 @@ stabilization, | ||
if (!dom) return screenshot | ||
if (dom) { | ||
// temporary solution | ||
if (fully) { | ||
await context.execute(snippets.setElementAttributes, [ | ||
scroller.element, | ||
{'data-applitools-scroll': true}, | ||
]) | ||
} | ||
// temporary solution | ||
if (fully) { | ||
await context.execute( | ||
'arguments[0].setAttribute("data-applitools-scroll", "true")', | ||
scroller.element, | ||
) | ||
const scrollingElement = await context.main.getScrollingElement() | ||
await scroller.moveTo({x: 0, y: 0}, scrollingElement) | ||
screenshot.dom = await takeDomCapture() | ||
} | ||
return {...screenshot, dom: await takeDomCapture()} | ||
// --- | ||
if (lazyRestorePageState) { | ||
screenshot.restorePageState = restorePageState | ||
} | ||
return screenshot | ||
} finally { | ||
await scroller.restoreState(scrollerState) | ||
if (!lazyRestorePageState) await restorePageState() | ||
} | ||
async function restorePageState() { | ||
if (scroller.element) { | ||
await scroller.element.restoreScrollbars() | ||
await scroller.restoreState(scrollerState) | ||
} | ||
@@ -91,6 +114,7 @@ if (hideCaret && activeElement) await targetContext.focusElement(activeElement) | ||
for (const prevContext of targetContext.path.reverse()) { | ||
const scrollingElement = await prevContext.getScrollRootElement() | ||
if (hideScrollbars) await scrollingElement.restoreScrollbars() | ||
const scrollingState = scrollingStates.shift() | ||
await defaultScroller.restoreState(scrollingState, scrollingElement) | ||
const scrollingElement = await prevContext.getScrollingElement() | ||
if (scrollingElement) { | ||
if (driver.isWeb && hideScrollbars) await scrollingElement.restoreScrollbars() | ||
await scrollingElement.restoreState() | ||
} | ||
} | ||
@@ -102,6 +126,12 @@ | ||
async function getTargetArea({logger, context, target, fully, scrollingMode}) { | ||
if (target) { | ||
async function getTargetArea({logger, context, target, window, fully, scrollingMode}) { | ||
if (window) { | ||
const scrollingElement = await context.main.getScrollingElement() | ||
return { | ||
context: context.main, | ||
scroller: makeScroller({logger, element: scrollingElement, scrollingMode}), | ||
} | ||
} else if (target) { | ||
if (utils.types.has(target, ['x', 'y', 'width', 'height'])) { | ||
const scrollingElement = await context.getScrollRootElement() | ||
const scrollingElement = await context.getScrollingElement() | ||
return { | ||
@@ -118,17 +148,20 @@ context, | ||
const isScrollable = await element.isScrollable() | ||
const scrollingElement = isScrollable ? element : await context.getScrollRootElement() | ||
const scrollingElement = isScrollable ? element : await context.getScrollingElement() | ||
return { | ||
context, | ||
region: isScrollable ? null : await element.getRect(), | ||
region: isScrollable ? null : await element.getRegion(), | ||
scroller: makeScroller({ | ||
logger, | ||
element: scrollingElement, | ||
scrollingMode: isScrollable && scrollingMode === 'css' ? 'mixed' : scrollingMode, | ||
scrollingMode: | ||
scrollingMode === 'css' && !(await scrollingElement.isRoot()) | ||
? 'mixed' | ||
: scrollingMode, | ||
}), | ||
} | ||
} else { | ||
const scrollingElement = await context.getScrollRootElement() | ||
const scrollingElement = await context.getScrollingElement() | ||
return { | ||
context, | ||
region: await element.getRect(), | ||
region: await element.getRegion(), | ||
scroller: makeScroller({logger, element: scrollingElement, scrollingMode}), | ||
@@ -139,11 +172,11 @@ } | ||
} else if (!context.isMain && !fully) { | ||
const scrollingElement = await context.parent.getScrollRootElement() | ||
const element = await context.getFrameElement() | ||
const scrollingElement = await context.parent.getScrollingElement() | ||
const element = await context.getContextElement() | ||
return { | ||
context: context.parent, | ||
region: await element.getClientRect(), | ||
region: await element.getRegion(), // IMHO we should use CLIENT (without borders) region here | ||
scroller: makeScroller({logger, element: scrollingElement, scrollingMode}), | ||
} | ||
} else { | ||
const scrollingElement = await context.getScrollRootElement() | ||
const scrollingElement = await context.getScrollingElement() | ||
return { | ||
@@ -150,0 +183,0 @@ context, |
@@ -10,4 +10,4 @@ const utils = require('@applitools/utils') | ||
getInnerOffset, | ||
getSize, | ||
getClientRect, | ||
getContentSize, | ||
getClientRegion, | ||
getScrollOffset, | ||
@@ -19,3 +19,3 @@ getTranslateOffset, | ||
shiftTo, | ||
getState, | ||
preserveState, | ||
restoreState, | ||
@@ -36,3 +36,3 @@ } | ||
async function getSize() { | ||
async function getContentSize() { | ||
const size = await element.getContentSize() | ||
@@ -42,4 +42,4 @@ return size | ||
async function getClientRect() { | ||
const region = await element.getClientRect() | ||
async function getClientRegion() { | ||
const region = await element.getClientRegion() | ||
// const location = await element.context.getLocationInPage() | ||
@@ -85,2 +85,3 @@ return region | ||
try { | ||
// offset = {x: Math.max(offset.x, 0), y: Math.max(offset.y, 0)} | ||
const scrollOffset = await element.scrollTo(offset) | ||
@@ -97,2 +98,3 @@ return scrollOffset | ||
try { | ||
// offset = {x: Math.max(offset.x, 0), y: Math.max(offset.y, 0)} | ||
await element.scrollTo({x: 0, y: 0}) | ||
@@ -110,2 +112,3 @@ const translateOffset = await element.translateTo(offset) | ||
try { | ||
// offset = {x: Math.max(offset.x, 0), y: Math.max(offset.y, 0)} | ||
const scrollOffset = await element.scrollTo(offset) | ||
@@ -123,7 +126,5 @@ const remainingOffset = utils.geometry.offsetNegative(offset, scrollOffset) | ||
async function getState(element = defaultElement) { | ||
async function preserveState(element = defaultElement) { | ||
try { | ||
const scroll = await element.getScrollOffset() | ||
const transforms = await element.getTransforms() | ||
return {scroll, transforms} | ||
return element.preserveState() | ||
} catch (err) { | ||
@@ -137,8 +138,3 @@ logger.verbose(`Failed to get current transforms!.`, err) | ||
try { | ||
if (state.scroll) { | ||
await element.scrollTo(state.scroll) | ||
} | ||
if (state.transforms) { | ||
await element.setTransforms(state.transforms) | ||
} | ||
await element.restoreState(state) | ||
} catch (err) { | ||
@@ -145,0 +141,0 @@ logger.verbose(`Failed to restore state!.`, err) |
@@ -8,19 +8,19 @@ const utils = require('@applitools/utils') | ||
} | ||
const elementContextRect = region ? {...region} : await scroller.getClientRect() | ||
const elementContextRegion = region ? {...region} : await scroller.getClientRegion() | ||
const contextViewportLocation = await context.getLocationInViewport() | ||
const elementViewportRect = utils.geometry.offset(elementContextRect, contextViewportLocation) | ||
const viewportRect = await context.main.getRect() | ||
if (utils.geometry.contains(viewportRect, elementViewportRect)) return {x: 0, y: 0} | ||
const elementViewportRegion = utils.geometry.offset(elementContextRegion, contextViewportLocation) | ||
const viewportRegion = await context.main.getRegion() | ||
if (utils.geometry.contains(viewportRegion, elementViewportRegion)) return {x: 0, y: 0} | ||
let currentContext = context | ||
let remainingOffset = {x: elementContextRect.x, y: elementContextRect.y} | ||
let remainingOffset = {x: elementContextRegion.x, y: elementContextRegion.y} | ||
while (currentContext) { | ||
const scrollRootElement = await currentContext.getScrollRootElement() | ||
const scrollRootOffset = scrollRootElement | ||
? await scrollRootElement.getClientRect().then(rect => ({x: rect.x, y: rect.y})) | ||
const scrollingElement = await currentContext.getScrollingElement() | ||
const scrollingElementOffset = scrollingElement | ||
? utils.geometry.location(await scrollingElement.getClientRegion()) | ||
: {x: 0, y: 0} | ||
const actualOffset = await scroller.moveTo( | ||
utils.geometry.offsetNegative(remainingOffset, scrollRootOffset), | ||
scrollRootElement, | ||
utils.geometry.offsetNegative(remainingOffset, scrollingElementOffset), | ||
scrollingElement, | ||
) | ||
@@ -30,3 +30,3 @@ | ||
utils.geometry.offsetNegative(remainingOffset, actualOffset), | ||
await currentContext.getClientLocation(), | ||
utils.geometry.location(await currentContext.getClientRegion()), | ||
) | ||
@@ -33,0 +33,0 @@ currentContext = currentContext.parent |
const utils = require('@applitools/utils') | ||
const saveScreenshot = require('./saveScreenshot') | ||
const snippets = require('@applitools/snippets') | ||
const findPattern = require('./findPattern') | ||
@@ -11,18 +11,17 @@ const makeCalculateScaleRatio = require('./calculateScaleRatio') | ||
return makeTakeNativeScreenshot(options) | ||
} else if (driver.userAgent) { | ||
if (driver.userAgent.browser === 'Firefox') { | ||
try { | ||
const browserVersion = Number.parseInt(driver.userAgent.browserMajorVersion, 10) | ||
if (browserVersion >= 48 && browserVersion <= 72) { | ||
return makeTakeMainContextScreenshot(options) | ||
} | ||
} catch (ignored) {} | ||
} else if (driver.userAgent.browser === 'Safari') { | ||
if (driver.userAgent.os === 'iOS' || driver.isIOS) { | ||
return makeTakeMarkedScreenshot(options) | ||
} else if (this._driver.userAgent.browserMajorVersion === '11') { | ||
return makeTakeSafari11Screenshot(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) | ||
} | ||
} | ||
return makeTakeDefaultScreenshot(options) | ||
@@ -36,7 +35,7 @@ } | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'original', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'original'}) | ||
if (stabilization.rotate) { | ||
await image.rotate(stabilization.rotate) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'rotated', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'rotated'}) | ||
} | ||
@@ -46,3 +45,3 @@ | ||
await image.crop(stabilization.crop) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'cropped', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'cropped'}) | ||
} | ||
@@ -55,7 +54,7 @@ | ||
const viewportSize = await driver.getViewportSize() | ||
const documentSize = await driver.mainContext.getDocumentSize() | ||
const documentSize = await driver.mainContext.getContentSize() | ||
calculateScaleRatio = makeCalculateScaleRatio({ | ||
viewportWidth: viewportSize.width, | ||
documentWidth: documentSize.width, | ||
pixelRatio: await driver.getPixelRatio(), | ||
pixelRatio: driver.pixelRatio, | ||
}) | ||
@@ -65,3 +64,3 @@ } | ||
} | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'scaled', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'scaled'}) | ||
@@ -80,7 +79,7 @@ return image | ||
await originalContext.focus() | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'original', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'original'}) | ||
if (stabilization.rotate) { | ||
await image.rotate(stabilization.rotate) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'rotated', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'rotated'}) | ||
} | ||
@@ -90,3 +89,3 @@ | ||
await image.crop(stabilization.crop) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'cropped', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'cropped'}) | ||
} | ||
@@ -99,7 +98,7 @@ | ||
const viewportSize = await driver.getViewportSize() | ||
const documentSize = await driver.mainContext.getDocumentSize() | ||
const documentSize = await driver.mainContext.getContentSize() | ||
calculateScaleRatio = makeCalculateScaleRatio({ | ||
viewportWidth: viewportSize.width, | ||
documentWidth: documentSize.width, | ||
pixelRatio: await driver.getPixelRatio(), | ||
pixelRatio: driver.pixelRatio, | ||
}) | ||
@@ -109,3 +108,3 @@ } | ||
} | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'scaled', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'scaled'}) | ||
@@ -117,3 +116,2 @@ return image | ||
function makeTakeSafari11Screenshot({logger, driver, stabilization = {}, debug = {}}) { | ||
let pixelRatio = null | ||
let viewportSize = null | ||
@@ -125,7 +123,7 @@ let calculateScaleRatio = null | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'original', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'original'}) | ||
if (stabilization.rotate) { | ||
await image.rotate(stabilization.rotate) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'rotated', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'rotated'}) | ||
} | ||
@@ -136,8 +134,9 @@ | ||
} else { | ||
if (!pixelRatio) pixelRatio = await driver.getPixelRatio() | ||
if (!viewportSize) viewportSize = await driver.getViewportSize() | ||
const viewportLocation = await driver.mainContext.getScrollOffset() | ||
await image.crop(utils.geometry.scale({...viewportLocation, ...viewportSize}, pixelRatio)) | ||
const viewportLocation = await driver.mainContext.execute(snippets.getElementScrollOffset, []) | ||
await image.crop( | ||
utils.geometry.scale({...viewportLocation, ...viewportSize}, driver.pixelRatio), | ||
) | ||
} | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'cropped', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'cropped'}) | ||
@@ -148,9 +147,8 @@ if (stabilization.scale) { | ||
if (!calculateScaleRatio) { | ||
if (!pixelRatio) pixelRatio = await driver.getPixelRatio() | ||
if (!viewportSize) viewportSize = await driver.getViewportSize() | ||
const documentSize = await driver.mainContext.getDocumentSize() | ||
const documentSize = await driver.mainContext.getContentSize() | ||
calculateScaleRatio = makeCalculateScaleRatio({ | ||
viewportWidth: viewportSize.width, | ||
documentWidth: documentSize.width, | ||
pixelRatio, | ||
pixelRatio: driver.pixelRatio, | ||
}) | ||
@@ -160,3 +158,3 @@ } | ||
} | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'scaled', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'scaled'}) | ||
@@ -174,7 +172,7 @@ return image | ||
const image = makeImage(await driver.takeScreenshot()) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'original', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'original'}) | ||
if (stabilization.rotate) { | ||
await image.rotate(stabilization.rotate) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'rotated', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'rotated'}) | ||
} | ||
@@ -188,3 +186,3 @@ | ||
} | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'cropped', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'cropped'}) | ||
@@ -196,7 +194,7 @@ if (stabilization.scale) { | ||
const viewportSize = await driver.getViewportSize() | ||
const documentSize = await driver.mainContext.getDocumentSize() | ||
const documentSize = await driver.mainContext.getContentSize() | ||
calculateScaleRatio = makeCalculateScaleRatio({ | ||
viewportWidth: viewportSize.width, | ||
documentWidth: documentSize.width, | ||
pixelRatio: await driver.getPixelRatio(), | ||
pixelRatio: driver.pixelRatio, | ||
}) | ||
@@ -206,3 +204,3 @@ } | ||
} | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'scaled', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'scaled'}) | ||
@@ -213,3 +211,3 @@ return image | ||
async function getViewportRegion() { | ||
const marker = await driver.addPageMarker() | ||
const marker = await driver.mainContext.execute(snippets.addPageMarker) | ||
try { | ||
@@ -219,3 +217,3 @@ const image = makeImage(await driver.takeScreenshot()) | ||
await saveScreenshot(image, 'marker') // TODO fix | ||
await image.debug('marker') // TODix | ||
@@ -225,9 +223,8 @@ const markerLocation = findPattern(await image.toObject(), marker) | ||
const pixelRation = await driver.getPixelRatio() | ||
const viewportSize = await driver.getViewportSize() | ||
const scaledViewportSize = utils.geometry.scale(viewportSize, pixelRation) | ||
const scaledViewportSize = utils.geometry.scale(viewportSize, driver.pixelRatio) | ||
return {...markerLocation, ...scaledViewportSize} | ||
} finally { | ||
await driver.cleanupPageMarker() | ||
await driver.mainContext.execute(snippets.cleanupPageMarker) | ||
} | ||
@@ -241,11 +238,9 @@ } | ||
const image = makeImage( | ||
stabilization.crop || process.env.APPLITOOLS_SKIP_MOBILE_NATIVE_SCREENSHOT_HOOK | ||
? await driver.takeScreenshot() | ||
: await takeViewportScreenshot(), | ||
stabilization.crop ? await driver.takeScreenshot() : await takeViewportScreenshot(), | ||
) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'original', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'original'}) | ||
if (stabilization.rotate) { | ||
await image.rotate(stabilization.rotate) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'rotated', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'rotated'}) | ||
} | ||
@@ -255,3 +250,3 @@ | ||
await image.crop(stabilization.crop) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'cropped', logger}) | ||
await image.debug({path: debug.path, name, suffix: 'cropped'}) | ||
} | ||
@@ -261,7 +256,7 @@ | ||
await image.scale(stabilization.scale) | ||
await saveScreenshot(image, {path: debug.path, name, suffix: 'scaled', logger}) | ||
} else { | ||
await image.scale(1 / driver.pixelRatio) | ||
} | ||
await image.debug({path: debug.path, name, suffix: 'scaled'}) | ||
process.env.APPLITOOLS_SKIP_MOBILE_NATIVE_SCREENSHOT_HOOK = undefined // TODO remove | ||
return image | ||
@@ -271,3 +266,3 @@ } | ||
async function takeViewportScreenshot() { | ||
const base64 = await driver.execute('mobile: viewportScreenshot') | ||
const base64 = await driver.execute('mobile:viewportScreenshot') | ||
// trimming line breaks since 3rd party grid providers can return them | ||
@@ -274,0 +269,0 @@ return base64.replace(/\r\n/g, '') |
const utils = require('@applitools/utils') | ||
const makeImage = require('./image') | ||
const makeTakeScreenshot = require('./takeScreenshot') | ||
const saveScreenshot = require('./saveScreenshot') | ||
@@ -12,2 +11,3 @@ async function takeStitchedScreenshot({ | ||
overlap = 50, | ||
framed, | ||
wait, | ||
@@ -19,3 +19,3 @@ stabilization, | ||
const scrollerRegion = utils.geometry.region({x: 0, y: 0}, await scroller.getSize()) | ||
const scrollerRegion = utils.geometry.region({x: 0, y: 0}, await scroller.getContentSize()) | ||
logger.verbose(`Scroller size: ${scrollerRegion}`) | ||
@@ -34,8 +34,17 @@ | ||
let image = await takeScreenshot({name: 'initial'}) | ||
const frameImage = framed ? makeImage(image) : null | ||
const cropRegion = await context.getRegionInViewport(region || (await scroller.getClientRect())) | ||
const targetRegion = region || (await scroller.getClientRegion()) | ||
// TODO the solution should not check driver specifics, | ||
// in this case target region coordinate should be already related to the scrolling element of the context | ||
const cropRegion = driver.isNative | ||
? targetRegion | ||
: await driver.getRegionInViewport(context, targetRegion) | ||
console.log({cropRegion}) | ||
logger.verbose('cropping...') | ||
await image.crop(cropRegion) | ||
await saveScreenshot(image, {path: debug.path, name: 'initial', suffix: 'region', logger}) | ||
await image.debug({path: debug.path, name: 'initial', suffix: 'region'}) | ||
@@ -52,6 +61,5 @@ if (region) region = utils.geometry.intersect(region, scrollerRegion) | ||
const partSize = {width: image.width, height: Math.max(image.height - overlap, 10)} | ||
logger.verbose(`Image part size: ${partSize}`) | ||
const [_, ...partRegions] = utils.geometry.divide(region, partSize) | ||
// TODO padding should be provided from args instead of overlap | ||
const padding = {top: driver.isNative ? overlap : 0, bottom: overlap} | ||
const [initialRegion, ...partRegions] = utils.geometry.divide(region, image.size, padding) | ||
logger.verbose('Part regions', partRegions) | ||
@@ -69,12 +77,13 @@ | ||
for (const partRegion of partRegions) { | ||
const partOffset = {x: partRegion.x, y: partRegion.y} | ||
const partSize = {width: partRegion.width, height: partRegion.height} | ||
const partName = `${partRegion.x}_${partRegion.y}_${partRegion.width}x${partRegion.height}` | ||
logger.verbose(`Processing part ${partName}`) | ||
logger.verbose(`Move to ${partOffset}`) | ||
const actualOffset = await scroller.moveTo(partOffset) | ||
const topPadding = initialRegion.y !== partRegion.y ? padding.top : 0 | ||
const partOffset = utils.geometry.location(partRegion) | ||
const requiredOffset = utils.geometry.offsetNegative(partOffset, {x: 0, y: topPadding}) | ||
logger.verbose(`Move to ${requiredOffset}`) | ||
const actualOffset = await scroller.moveTo(requiredOffset) | ||
const remainingOffset = utils.geometry.offsetNegative( | ||
utils.geometry.offsetNegative(partOffset, actualOffset), | ||
utils.geometry.offsetNegative(requiredOffset, actualOffset), | ||
expectedRemainingOffset, | ||
@@ -84,5 +93,5 @@ ) | ||
x: cropRegion.x + remainingOffset.x, | ||
y: cropRegion.y + remainingOffset.y, | ||
width: Math.min(cropRegion.width, partSize.width), | ||
height: Math.min(cropRegion.height, partSize.height), | ||
y: cropRegion.y + remainingOffset.y + topPadding, | ||
width: Math.min(cropRegion.width, partRegion.width), | ||
height: Math.min(cropRegion.height, partRegion.height), | ||
} | ||
@@ -94,17 +103,17 @@ logger.verbose(`Actual offset is ${actualOffset}, remaining offset is ${remainingOffset}`) | ||
// TODO maybe remove | ||
if (!utils.geometry.isEmpty(cropPartRegion)) { | ||
logger.verbose('Getting image...') | ||
image = await takeScreenshot({name: partName}) | ||
if (utils.geometry.isEmpty(cropPartRegion)) continue | ||
logger.verbose('cropping...') | ||
await image.crop(cropPartRegion) | ||
await saveScreenshot(image, {path: debug.path, name: partName, suffix: 'region', logger}) | ||
logger.verbose('Getting image...') | ||
image = await takeScreenshot({name: partName}) | ||
await composition.copy( | ||
await image.toObject(), | ||
utils.geometry.offsetNegative(partOffset, initialOffset), | ||
) | ||
logger.verbose('cropping...') | ||
await image.crop(cropPartRegion) | ||
await image.debug({path: debug.path, name: partName, suffix: 'region'}) | ||
stitchedSize = {width: partOffset.x + image.width, height: partOffset.y + image.height} | ||
} | ||
await composition.copy( | ||
await image.toObject(), | ||
utils.geometry.offsetNegative(partOffset, initialOffset), | ||
) | ||
stitchedSize = {width: partOffset.x + image.width, height: partOffset.y + image.height} | ||
} | ||
@@ -125,6 +134,25 @@ | ||
await saveScreenshot(composition, {path: debug.path, name: 'stitched', logger}) | ||
return {image: composition, region: cropRegion} | ||
await composition.debug({path: debug.path, name: 'stitched'}) | ||
const locationInMainContext = await context.getLocationInMainContext() | ||
if (frameImage) { | ||
await frameImage.replace(await composition.toObject(), cropRegion) | ||
await frameImage.debug({path: debug.path, name: 'framed'}) | ||
return { | ||
image: frameImage, | ||
region: utils.geometry.region({x: 0, y: 0}, frameImage.size), | ||
} | ||
} else { | ||
return { | ||
image: composition, | ||
region: utils.geometry.region( | ||
utils.geometry.offset(locationInMainContext, targetRegion), | ||
composition.size, | ||
), | ||
} | ||
} | ||
} | ||
module.exports = takeStitchedScreenshot |
const utils = require('@applitools/utils') | ||
const makeTakeScreenshot = require('./takeScreenshot') | ||
const saveScreenshot = require('./saveScreenshot') | ||
@@ -16,8 +15,9 @@ async function takeViewportScreenshot({logger, context, region, wait, stabilization, debug = {}}) { | ||
if (region) { | ||
const cropRegion = await context.getRegionInViewport(region) | ||
const cropRegion = await driver.getRegionInViewport(context, region) | ||
if (utils.geometry.isEmpty(cropRegion)) throw new Error('Screenshot region is out of viewport') | ||
await image.crop(cropRegion) | ||
await saveScreenshot(image, {path: debug.path, suffix: 'region', logger}) | ||
return {image, region: cropRegion} | ||
await image.debug({path: debug.path, suffix: 'region'}) | ||
return {image, region: utils.geometry.offset(region, await context.getLocationInMainContext())} | ||
} else { | ||
return {image, region: {x: 0, y: 0, width: image.width, height: image.height}} | ||
return {image, region: utils.geometry.region(await context.main.getInnerOffset(), image.size)} | ||
} | ||
@@ -24,0 +24,0 @@ } |
@@ -66,3 +66,3 @@ const assert = require('assert') | ||
const composition = makeImage({width: image.width, height: image.height * 2}) | ||
await composition.copy(image, {x: 0, y: 0}) | ||
await composition.copy(image, {x: 0.1, y: 0.2}) | ||
await composition.copy(image, {x: 0, y: image.height}) | ||
@@ -82,2 +82,34 @@ const actual = await composition.toObject() | ||
}) | ||
it('should replace region in image with a higher and wider image', async () => { | ||
const image = await makeImage('./test/fixtures/image/house.png') | ||
const replace = await makeImage({width: 200, height: 200}).toObject() | ||
replace.data.fill(Buffer.from([0xff, 0, 0, 0xff])) | ||
await image.replace(replace, {x: 200, y: 200, width: 100, height: 100}) | ||
const actual = await image.toObject() | ||
const expected = await makeImage( | ||
'./test/fixtures/image/house.replaced-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 () => { | ||
const image = await makeImage('./test/fixtures/image/house.png') | ||
const replace = await makeImage({width: 200, height: 200}).toObject() | ||
replace.data.fill(Buffer.from([0, 0xff, 0, 0xff])) | ||
await image.replace(replace, {x: 200, y: 200, width: 200, height: 100}) | ||
const actual = await image.toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.replaced-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 () => { | ||
const image = await makeImage('./test/fixtures/image/house.png') | ||
const replace = await makeImage({width: 200, height: 200}).toObject() | ||
replace.data.fill(Buffer.from([0, 0, 0xff, 0xff])) | ||
await image.replace(replace, {x: 200, y: 200, width: 100, height: 200}) | ||
const actual = await image.toObject() | ||
const expected = await makeImage('./test/fixtures/image/house.replaced-wider.png').toObject() | ||
assert.ok(pixelmatch(actual.data, expected.data, null, expected.width, expected.height) === 0) | ||
}) | ||
}) |
const webdriverio = require('webdriverio') | ||
const utils = require('@applitools/utils') | ||
const driver = require('@applitools/driver') | ||
const {Driver} = require('@applitools/driver') | ||
@@ -15,7 +15,5 @@ // #region HELPERS | ||
function transformSelector(selector) { | ||
if (utils.types.has(selector, ['type', 'selector'])) { | ||
if (selector.type === 'css') return `css selector:${selector.selector}` | ||
else if (selector.type === 'xpath') return `xpath:${selector.selector}` | ||
} | ||
return selector | ||
if (!utils.types.has(selector, ['type', 'selector'])) return selector | ||
else if (selector.type === 'css') return `css selector:${selector.selector}` | ||
else return `${selector.type}:${selector.selector}` | ||
} | ||
@@ -27,4 +25,5 @@ | ||
function isDriver(page) { | ||
return page.constructor.name === 'Browser' | ||
function isDriver(browser) { | ||
if (!browser) return false | ||
return browser.constructor.name === 'Browser' | ||
} | ||
@@ -90,31 +89,28 @@ function isElement(element) { | ||
} | ||
async function getWindowRect(browser) { | ||
async function getElementRegion(browser, element) { | ||
const extendedElement = await browser.$(element) | ||
if (utils.types.isFunction(extendedElement, 'getRect')) { | ||
return extendedElement.getRect() | ||
} else { | ||
const size = await extendedElement.getSize() | ||
const location = utils.types.has(size, ['x', 'y']) ? size : await extendedElement.getLocation() | ||
return {x: location.x, y: location.y, width: size.width, height: size.height} | ||
} | ||
} | ||
async function getElementAttribute(browser, element, name) { | ||
return browser.getElementAttribute(extractElementId(element), name) | ||
} | ||
async function getWindowSize(browser) { | ||
if (utils.types.isFunction(browser.getWindowRect)) { | ||
return browser.getWindowRect() | ||
} else { | ||
const rect = {x: 0, y: 0, width: 0, height: 0} | ||
if (utils.types.isFunction(browser.getWindowPosition)) { | ||
const location = await browser.getWindowPosition() | ||
rect.x = location.x | ||
rect.y = location.y | ||
} | ||
if (utils.types.isFunction(browser.getWindowSize)) { | ||
const size = await browser.getWindowSize() | ||
rect.width = size.width | ||
rect.height = size.height | ||
} | ||
return rect | ||
} else if (utils.types.isFunction(browser.getWindowSize)) { | ||
return await browser.getWindowSize() | ||
} | ||
} | ||
async function setWindowRect(browser, rect = {}) { | ||
const {x = null, y = null, width = null, height = null} = rect | ||
async function setWindowSize(browser, {width, height} = {}) { | ||
if (utils.types.isFunction(browser.setWindowRect)) { | ||
await browser.setWindowRect(x, y, width, height) | ||
await browser.setWindowRect(0, 0, width, height) | ||
} else { | ||
if (utils.types.isFunction(browser.setWindowPosition) && x !== null && y !== null) { | ||
await browser.setWindowPosition(x, y) | ||
} | ||
if (utils.types.isFunction(browser.setWindowSize) && width !== null && height !== null) { | ||
await browser.setWindowSize(width, height) | ||
} | ||
await browser.setWindowPosition(0, 0) | ||
await browser.setWindowSize(width, height) | ||
} | ||
@@ -127,3 +123,3 @@ } | ||
async function getDriverInfo(browser) { | ||
return { | ||
const driverInfo = { | ||
sessionId: browser.sessionId, | ||
@@ -139,3 +135,23 @@ isMobile: browser.isMobile, | ||
browserVersion: browser.capabilities.browserVersion, | ||
pixelRatio: browser.capabilities.pixelRatio, | ||
} | ||
if (driverInfo.isNative) { | ||
const {pixelRatio, viewportRect} = | ||
browser.capabilities.viewportRect && browser.capabilities.pixelRatio | ||
? browser.capabilities | ||
: await browser.getSession() | ||
driverInfo.pixelRatio = pixelRatio | ||
if (viewportRect) { | ||
driverInfo.viewportRegion = { | ||
x: viewportRect.left, | ||
y: viewportRect.top, | ||
width: viewportRect.width, | ||
height: viewportRect.height, | ||
} | ||
} | ||
} | ||
return driverInfo | ||
} | ||
@@ -150,4 +166,11 @@ async function takeScreenshot(driver) { | ||
if (isSelector(element)) element = await findElement(browser, element) | ||
return element.click() | ||
const extendedElement = await browser.$(element) | ||
await extendedElement.click() | ||
} | ||
async function performAction(browser, actions) { | ||
return browser.touchAction(actions) | ||
} | ||
async function getElementText(browser, element) { | ||
return browser.getElementText(extractElementId(element)) | ||
} | ||
@@ -169,4 +192,6 @@ // #endregion | ||
findElements, | ||
getWindowRect, | ||
setWindowRect, | ||
getElementRegion, | ||
getElementAttribute, | ||
getWindowSize, | ||
setWindowSize, | ||
getOrientation, | ||
@@ -177,21 +202,84 @@ getDriverInfo, | ||
click, | ||
performAction, | ||
getElementText, | ||
} | ||
async function makeDriver() { | ||
const browser = await webdriverio.remote({ | ||
protocol: 'http', | ||
hostname: 'localhost', | ||
path: '/wd/hub', | ||
port: 4444, | ||
logLevel: 'silent', | ||
capabilities: { | ||
browserName: 'chrome', | ||
async function makeDriver({type = 'web'} = {}) { | ||
const capabilities = { | ||
web: { | ||
protocol: 'http', | ||
hostname: 'localhost', | ||
path: '/wd/hub', | ||
port: 4444, | ||
logLevel: 'silent', | ||
capabilities: { | ||
browserName: 'chrome', | ||
}, | ||
}, | ||
}) | ||
android: { | ||
protocol: 'https', | ||
hostname: 'ondemand.saucelabs.com', | ||
path: '/wd/hub', | ||
port: 443, | ||
logLevel: 'silent', | ||
capabilities: { | ||
name: 'Android Screenshoter Test', | ||
browserName: '', | ||
platformName: 'Android', | ||
platformVersion: '7.0', | ||
appiumVersion: '1.20.2', | ||
deviceName: 'Samsung Galaxy S8 FHD GoogleAPI Emulator', | ||
automationName: 'uiautomator2', | ||
app: 'https://applitools.jfrog.io/artifactory/Examples/android/1.3/app-debug.apk', | ||
username: process.env.SAUCE_USERNAME, | ||
accessKey: process.env.SAUCE_ACCESS_KEY, | ||
}, | ||
}, | ||
androidx: { | ||
protocol: 'https', | ||
hostname: 'ondemand.saucelabs.com', | ||
path: '/wd/hub', | ||
port: 443, | ||
logLevel: 'silent', | ||
capabilities: { | ||
name: 'AndroidX Screenshoter Test', | ||
browserName: '', | ||
platformName: 'Android', | ||
platformVersion: '10.0', | ||
appiumVersion: '1.20.2', | ||
deviceName: 'Google Pixel 3a XL GoogleAPI Emulator', | ||
automationName: 'uiautomator2', | ||
app: 'https://applitools.jfrog.io/artifactory/Examples/androidx/1.2.0/app_androidx.apk', | ||
username: process.env.SAUCE_USERNAME, | ||
accessKey: process.env.SAUCE_ACCESS_KEY, | ||
}, | ||
}, | ||
ios: { | ||
protocol: 'https', | ||
hostname: 'ondemand.saucelabs.com', | ||
path: '/wd/hub', | ||
port: 443, | ||
logLevel: 'silent', | ||
capabilities: { | ||
name: 'iOS Screenshoter Test', | ||
deviceName: 'iPhone 11 Pro Simulator', | ||
platformName: 'iOS', | ||
platformVersion: '13.4', | ||
appiumVersion: '1.19.2', | ||
automationName: 'XCUITest', | ||
app: | ||
'https://applitools.jfrog.io/artifactory/Examples/IOSTestApp/1.5/app/IOSTestApp-1.5.zip', | ||
username: process.env.SAUCE_USERNAME, | ||
accessKey: process.env.SAUCE_ACCESS_KEY, | ||
}, | ||
}, | ||
} | ||
const logger = {log: () => null, verbose: () => null} | ||
const browser = await webdriverio.remote(capabilities[type]) | ||
return [driver.makeDriver(spec, logger, browser), () => browser.deleteSession()] | ||
const logger = {log: () => {}, warn: () => {}, error: () => {}} | ||
return [new Driver({spec, logger, driver: browser}), () => browser.deleteSession()] | ||
} | ||
module.exports = makeDriver |
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances 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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
25736084
66
2264
3
8
+ Added@applitools/snippets@2.1.4
+ Added@applitools/snippets@2.1.4(transitive)
+ Added@applitools/utils@1.2.1(transitive)
- Removed@applitools/utils@1.2.0(transitive)
Updated@applitools/utils@1.2.1