@applitools/screenshoter
Version:
Applitools universal screenshoter for web and native applications
155 lines (130 loc) • 5.53 kB
JavaScript
const {makeLogger} = require('@applitools/logger')
const getTarget = require('./get-target')
const scrollIntoViewport = require('./scroll-into-viewport')
const takeStitchedScreenshot = require('./take-stitched-screenshot')
const takeSimpleScreenshot = require('./take-simple-screenshot')
const extractCoordinatesForSelectorsAndElements = require('./extract-coordinates-for-selectors-and-elements')
async function takeScreenshot({
driver,
frames = [],
region,
regionsToCalculate = [],
fully,
scrollingMode,
hideScrollbars,
hideCaret,
captureStatusBar,
keepNavigationBar,
overlap,
framed,
wait,
stabilization,
hooks,
debug,
logger,
lazyLoad,
webview,
}) {
debug =
debug ||
(process.env.APPLITOOLS_DEBUG_SCREENSHOTS_DIR ? {path: process.env.APPLITOOLS_DEBUG_SCREENSHOTS_DIR} : debug)
logger = logger ? logger.extend({label: 'screenshoter'}) : makeLogger({label: 'screenshoter'})
// screenshot of a window/app was requested (fully or viewport)
const window = !region && (!frames || frames.length === 0)
const originalEnvironment = await driver.getEnvironment()
// world should be switched to webview if webview screenshot or the window screenshot is requested
// in case of window screenshot we will be switching to native app view
let activeWorld
if (originalEnvironment.isNative && (webview || (window && originalEnvironment.isWeb))) {
if (webview === true) {
const worlds = await driver.getWorlds()
webview = worlds && worlds.find(name => name.includes('WEBVIEW'))
}
activeWorld = await driver.getCurrentWorld()
if (activeWorld !== webview) await driver.switchWorld(webview)
else activeWorld = undefined
}
const environment = await driver.getEnvironment()
// 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
captureStatusBar = captureStatusBar && environment.isNative && window && (!fully || framed)
scrollingMode = !environment.isWeb ? 'scroll' : scrollingMode
const activeContext = driver.currentContext
const context =
frames.length > 0
? await activeContext.context(frames.reduce((parent, frame) => ({...frame, parent}), null))
: activeContext
// 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 (environment.isWeb && hideScrollbars) await scrollingElement.hideScrollbars()
// this is unwanted but necessary side effect, because it is not possible to extract initial scroll position
if (!environment.isWeb && !window) await scrollingElement.scrollTo({x: 0, y: 0}, {force: true})
await scrollingElement.preserveState()
}
}
// blur active element in target context
const activeElement = environment.isWeb && hideCaret ? await context.blurElement() : null
const target = await getTarget({window, context, region, fully, scrollingMode, logger})
if (target.scroller) {
await target.scroller.preserveState()
if (environment.isWeb && hideScrollbars) await target.scroller.hideScrollbars()
}
if (!window && environment.isWeb) await scrollIntoViewport({...target, logger})
if (fully && !target.region && target.scroller) await target.scroller.moveTo({x: 0, y: 0})
const screenshot =
fully && target.scroller
? await takeStitchedScreenshot({
...target,
captureStatusBar,
overlap,
framed,
wait,
stabilization,
debug,
logger,
lazyLoad,
})
: await takeSimpleScreenshot({...target, captureStatusBar, keepNavigationBar, wait, stabilization, debug, logger})
const viewport = await driver.getViewport()
screenshot.image.scale(viewport.viewportScale)
const calculatedRegions = await extractCoordinatesForSelectorsAndElements({
regionsToCalculate,
screenshot,
context,
logger,
})
if (hooks && hooks.afterScreenshot) {
await hooks.afterScreenshot({driver, scroller: target.scroller, screenshot})
}
return {
...screenshot,
element: target.element,
scrollingElement: target.scroller && target.scroller.element,
calculatedRegions,
async restoreState() {
if (target.scroller) {
await target.scroller.restoreScrollbars()
await target.scroller.restoreState()
}
// if there was active element and we have blurred it, then restore focus
if (activeElement) await context.focusElement(activeElement)
// 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 (environment.isWeb && hideScrollbars) await scrollingElement.restoreScrollbars()
await scrollingElement.restoreState()
}
}
// restore focus on original active context
await activeContext.focus()
// return driver to previous app world if switched
if (activeWorld) await driver.switchWorld(activeWorld)
},
}
}
module.exports = takeScreenshot