UNPKG

@applitools/eyes-testcafe

Version:

Applitools Eyes SDK for TestCafe

246 lines (245 loc) 8.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.takeScreenshot = exports.visit = exports.getUrl = exports.getTitle = exports.setViewportSize = exports.getDriverInfo = exports.childContext = exports.mainContext = exports.hover = exports.setElementText = exports.click = exports.findElements = exports.findElement = exports.executeScript = exports.extractSelector = exports.toSelector = exports.toElement = exports.isSelector = exports.isSecondaryElement = exports.isElement = exports.isDriver = void 0; const testcafe = require("testcafe"); const fs = require("fs"); const utils = require("@applitools/utils"); function XPathSelector(selector, options) { const getElementsByXPath = testcafe.Selector(xpath => { /* eslint-disable no-undef */ const iterator = document.evaluate(xpath, document, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null); /* eslint-enable */ const items = []; let item = iterator.iterateNext(); while (item) { items.push(item); item = iterator.iterateNext(); } return items; }, options); return testcafe.Selector(getElementsByXPath(selector), options); } function deserializeResult(result, elements) { if (!result) { return result; } else if (result.isElement) { return elements.shift(); } else if (utils.types.isArray(result)) { return result.map(result => deserializeResult(result, elements)); } else if (utils.types.isObject(result)) { return Object.entries(result).reduce((object, [key, value]) => { return Object.assign(object, { [key]: deserializeResult(value, elements) }); }, {}); } else { return result; } } const scriptRunner = testcafe.ClientFunction(() => { // @ts-ignore const { script, arg } = input; const func = new Function(script.startsWith('function') ? `return (${script}).apply(null, arguments)` : script); const elements = []; const result = serializeResult(func(deserializeArg(arg))); const resultId = elements.length > 0 ? String(Math.floor(Math.random() * 1000)) : null; if (resultId) { const APPLITOOLS_NAMESPACE = '__TESTCAFE_EYES_APPLITOOLS__'; const global = window; if (!global[APPLITOOLS_NAMESPACE]) global[APPLITOOLS_NAMESPACE] = {}; global[APPLITOOLS_NAMESPACE][resultId] = elements; } return { result, resultId, elementsCount: elements.length }; function deserializeArg(arg) { if (!arg) { return arg; } else if (typeof arg === 'function') { return arg(); } else if (Array.isArray(arg)) { return arg.map(deserializeArg); } else if (typeof arg === 'object') { return Object.entries(arg).reduce((object, [key, value]) => { return Object.assign(object, { [key]: deserializeArg(value) }); }, {}); } else { return arg; } } function serializeResult(result) { if (!result) { return result; } else if (result instanceof window.HTMLElement) { elements.push(result); return { isElement: true }; } else if (Array.isArray(result)) { return result.map(serializeResult); } else if (typeof result === 'object') { return Object.entries(result).reduce((object, [key, value]) => { return Object.assign(object, { [key]: serializeResult(value) }); }, {}); } else { return result; } } }); const elementsExtractor = testcafe.Selector(() => { // @ts-ignore const { resultId } = input; const APPLITOOLS_NAMESPACE = '__TESTCAFE_EYES_APPLITOOLS__'; const global = window; if (!global[APPLITOOLS_NAMESPACE] || !global[APPLITOOLS_NAMESPACE][resultId]) return []; const elements = global[APPLITOOLS_NAMESPACE][resultId]; return elements; }); function isDriver(t) { return utils.types.instanceOf(t, 'TestController'); } exports.isDriver = isDriver; function isElement(element) { if (!element) return false; return !!(element.addCustomMethods && element.find && element.parent); } exports.isElement = isElement; function isSecondaryElement(element) { if (!element) return false; return !!(element.nodeType && isElement(element.selector)); } exports.isSecondaryElement = isSecondaryElement; function isSelector(selector) { if (!selector) return false; return Boolean(selector.addCustomMethods && selector.find && selector.parent); } exports.isSelector = isSelector; function toElement(element) { if (utils.types.isFunction(element.selector)) return element.selector; return element; } exports.toElement = toElement; function toSelector(selector) { if (utils.types.has(selector, 'selector')) { let current = selector; let transformed = selector.type === 'xpath' ? XPathSelector(current.selector) : testcafe.Selector(current.selector); while (current.child || current.shadow) { if (current.child) { current = utils.types.has(current.child, 'selector') ? current.child : { selector: current.child }; transformed = transformed.find(current.selector); } else if (current.shadow) { current = utils.types.has(current.shadow, 'selector') ? current.shadow : { selector: current.shadow }; transformed = transformed.shadowRoot().find(current.selector); } } return transformed; } return testcafe.Selector(selector); } exports.toSelector = toSelector; function extractSelector(element) { return utils.types.isFunction(element.selector) ? element.selector : element; } exports.extractSelector = extractSelector; async function executeScript(t, script, arg) { script = utils.types.isFunction(script) ? script.toString() : script; const { result, resultId, elementsCount } = await scriptRunner.with({ boundTestRun: t, dependencies: { input: { script, arg } }, })(); if (!result || !resultId) return result; const elements = elementsExtractor.with({ boundTestRun: t, dependencies: { input: { resultId } }, }); return deserializeResult(result, Array.from({ length: elementsCount }, (_, index) => elements.nth(index))); } exports.executeScript = executeScript; async function findElement(t, selector) { const element = await selector.with({ boundTestRun: t })(); return element ? element.selector : null; } exports.findElement = findElement; async function findElements(t, selector) { const elements = selector.with({ boundTestRun: t }); return Array.from({ length: await elements.count }, (_, index) => elements.nth(index)); } exports.findElements = findElements; async function click(t, element) { await t.click(element); } exports.click = click; async function setElementText(t, element, text) { await t.typeText(element, text); } exports.setElementText = setElementText; async function hover(t, element) { await t.hover(element); } exports.hover = hover; async function mainContext(t) { await t.switchToMainWindow(); return t; } exports.mainContext = mainContext; async function childContext(t, element) { await t.switchToIframe(element); return t; } exports.childContext = childContext; async function getDriverInfo(_t) { return { features: { nestedSelectors: true } }; } exports.getDriverInfo = getDriverInfo; async function setViewportSize(t, size) { await t.resizeWindow(size.width, size.height); } exports.setViewportSize = setViewportSize; async function getTitle(t) { try { return await testcafe.Selector('title', { boundTestRun: t }).innerText; } catch (error) { return ''; } } exports.getTitle = getTitle; async function getUrl(t) { return testcafe.ClientFunction(() => document.location.href, { boundTestRun: t })(); } exports.getUrl = getUrl; async function visit(t, url) { await t.navigateTo(url); } exports.visit = visit; async function takeScreenshot(t) { // NOTE: // Since we are constrained to saving screenshots to disk, we place each screenshot in its own // dot-folder which has a GUID prefix (e.g., .applitools-guide/screenshot.png). // We then read the file from disk, return the buffer, and delete the folder. const screenshotPath = await t.takeScreenshot({ // thumbnails: false, path: `.applitools/${utils.general.guid()}.png`, }); try { return fs.readFileSync(screenshotPath); } finally { fs.unlinkSync(screenshotPath); } } exports.takeScreenshot = takeScreenshot;