import type {BrowserCommand} from '@applitools/driver' type EyesSelector = {selector: string; type?: string} export type Selector = (string | EyesSelector) & {__applitoolsBrand?: never} export type Context = Document & {__applitoolsBrand?: never} export type Element = HTMLElement & {__applitoolsBrand?: never} export function executeScript(context: Context, script: string, arg: any): any { let scriptToExecute if (script.includes('dom-snapshot') || script.includes('dom-capture') || script.includes('dom-shared')) { scriptToExecute = script } else if (script.startsWith('applitools:')) { // prevents "SyntaxError: Unexpected token 'return'\n at new Function ()" from appearing in the devtool return } else { const prepScirpt = script.replace('function(arg)', 'function func(arg)') scriptToExecute = prepScirpt.concat(' return func(arg)') } const executor = new context.defaultView.Function('arg', scriptToExecute) try { return executor(arg) } catch (e) { throw new Error(`Failed to execute script: ${e}`) // stringifiying prevents [object Object] errors } } export function mainContext(): Context { //@ts-ignore return cy.state('document') } export function parentContext(context: Context): Context { // because Cypress doesn't support cross origin iframe, then childContext might return null, and then the input to parentContext might be null if (!context) throw new Error('Context is not accessible') return context === mainContext() ? context : context.defaultView.frameElement.ownerDocument } export function childContext(_context: Context, element: HTMLIFrameElement): Context { if (element.contentDocument) return element.contentDocument else { throw new Error('Context is not accessible') } } export function getViewportSize(): {width: number; height: number} { //@ts-ignore const currWindow = cy.state('window') const viewportSize = { width: currWindow.innerWidth || currWindow.document.documentElement.clientWidth || currWindow.document.body.clientWidth, height: currWindow.innerHeight || currWindow.document.documentElement.clientHeight || currWindow.document.body.clientHeight, } return viewportSize } export function setViewportSize(vs: any): void { //@ts-ignore Cypress.action('cy:viewport:changed', {viewportWidth: vs.size.width, viewportHeight: vs.size.height}) } export function transformSelector(selector: Selector): Selector { if ( selector.hasOwnProperty('selector') && (!selector.hasOwnProperty('type') || (selector as EyesSelector).type === 'css') ) { return (selector as EyesSelector).selector } return selector } export function findElement(context: Context, selector: Selector, parent?: Element) { const eyesSelector = selector as EyesSelector const root = parent ?? context const sel = typeof selector === 'string' ? selector : eyesSelector.selector if (typeof selector !== 'string' && eyesSelector.type === 'xpath') { return context.evaluate(sel, context, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue } else { return root.querySelector(sel) } } export function findElements(context: Context, selector: Selector, parent: Element) { const eyesSelector = selector as EyesSelector const root = parent ?? context const sel = typeof selector === 'string' ? selector : eyesSelector.selector if (typeof selector !== 'string' && eyesSelector.type === 'xpath') { const results = [] const queryResult = document.evaluate(sel, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null) for (let i = 0; i < queryResult.snapshotLength; i++) { results.push(queryResult.snapshotItem(i)) } return results } else { return root.querySelectorAll(sel) } } export function getTitle(context: Context): string { return context.title } export function getUrl(context: Context): string { return context.location.href } export function getCookies(): Array { //@ts-ignore return Cypress.automation('get:cookies', {}) } export async function executeBrowserCommands({commands}: {commands: BrowserCommand[]}): Promise { let lastResponse for (const currentCommand of commands) { lastResponse = await Cypress.automation('remote:debugger:protocol', currentCommand) Cypress.log({name: 'executeBrowserCommands', message: {command: currentCommand.command, lastResponse}}) } return lastResponse } export function reload(): Promise { return new Promise(resolve => { cy.once('window:load', resolve) //@ts-ignore return cy.state('window').location.reload() }) } export async function executeUserFunction(fn: any) { if (typeof fn !== 'function') { console.error('Not a function:', fn) throw new Error(`Not a function: ${JSON.stringify(fn)}`) } return fn() } // export function takeScreenshot(page: Driver): Promise; // export function visit(page: Driver, url: string): Promise; (??) // export function isStaleElementError(err: any): boolean;