@applitools/eyes-testcafe
Version:
Applitools Eyes SDK for TestCafe
246 lines (245 loc) • 8.88 kB
JavaScript
;
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;