UNPKG

expect-webdriverio

Version:

WebdriverIO Assertion Library

264 lines (263 loc) 8.47 kB
import isEqual from 'lodash.isequal'; import { expect } from './index.js'; import { DEFAULT_OPTIONS } from './constants.js'; import { wrapExpectedWithArray } from './util/elementsUtil.js'; import { executeCommand } from './util/executeCommand.js'; import { enhanceError, enhanceErrorBe, numberError } from './util/formatMessage.js'; const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const asymmetricMatcher = typeof Symbol === 'function' && Symbol.for ? Symbol.for('jest.asymmetricMatcher') : 1267621; export function isAsymmeyricMatcher(expected) { return (typeof expected === 'object' && typeof expected === 'object' && expected && '$$typeof' in expected && 'asymmetricMatch' in expected && expected.$$typeof === asymmetricMatcher && Boolean(expected.asymmetricMatch)); } function isStringContainingMatcher(expected) { return isAsymmeyricMatcher(expected) && ['StringContaining', 'StringNotContaining'].includes(expected.toString()); } const waitUntil = async (condition, isNot = false, { wait = DEFAULT_OPTIONS.wait, interval = DEFAULT_OPTIONS.interval } = {}) => { if (wait === 0) { return await condition(); } let error; try { const start = Date.now(); while (true) { if (Date.now() - start > wait) { throw new Error('timeout'); } error = undefined; try { const result = isNot !== (await condition()); error = undefined; if (result) { break; } await sleep(interval); } catch (err) { error = err; await sleep(interval); } } if (error) { throw error; } return !isNot; } catch { if (error) { throw error; } return isNot; } }; async function executeCommandBe(received, command, options) { const { isNot, expectation, verb = 'be' } = this; let el = await received?.getElement(); const pass = await waitUntil(async () => { const result = await executeCommand.call(this, el, async (element) => ({ result: await command(element) }), options); el = result.el; return result.success; }, isNot, options); const message = enhanceErrorBe(el, pass, this, verb, expectation, options); return { pass, message: () => message, }; } const compareNumbers = (actual, options = {}) => { if (typeof options.eq === 'number') { return actual === options.eq; } if (typeof options.gte === 'number' && typeof options.lte === 'number') { return actual >= options.gte && actual <= options.lte; } if (typeof options.gte === 'number') { return actual >= options.gte; } if (typeof options.lte === 'number') { return actual <= options.lte; } return false; }; export const compareText = (actual, expected, { ignoreCase = false, trim = true, containing = false, atStart = false, atEnd = false, atIndex, replace, }) => { if (typeof actual !== 'string') { return { value: actual, result: false, }; } if (trim) { actual = actual.trim(); } if (Array.isArray(replace)) { actual = replaceActual(replace, actual); } if (ignoreCase) { actual = actual.toLowerCase(); if (typeof expected === 'string') { expected = expected.toLowerCase(); } else if (isStringContainingMatcher(expected)) { expected = (expected.toString() === 'StringContaining' ? expect.stringContaining(expected.sample?.toString().toLowerCase()) : expect.not.stringContaining(expected.sample?.toString().toLowerCase())); } } if (isAsymmeyricMatcher(expected)) { const result = expected.asymmetricMatch(actual); return { value: actual, result }; } expected = expected; if (expected instanceof RegExp) { return { value: actual, result: !!actual.match(expected), }; } if (containing) { return { value: actual, result: actual.includes(expected), }; } if (atStart) { return { value: actual, result: actual.startsWith(expected), }; } if (atEnd) { return { value: actual, result: actual.endsWith(expected), }; } if (atIndex) { return { value: actual, result: actual.substring(atIndex, actual.length).startsWith(expected), }; } return { value: actual, result: actual === expected, }; }; export const compareTextWithArray = (actual, expectedArray, { ignoreCase = false, trim = false, containing = false, atStart = false, atEnd = false, atIndex, replace, }) => { if (typeof actual !== 'string') { return { value: actual, result: false, }; } if (trim) { actual = actual.trim(); } if (Array.isArray(replace)) { actual = replaceActual(replace, actual); } if (ignoreCase) { actual = actual.toLowerCase(); expectedArray = expectedArray.map((item) => { if (typeof item === 'string') { return item.toLowerCase(); } if (isStringContainingMatcher(item)) { return (item.toString() === 'StringContaining' ? expect.stringContaining(item.sample?.toString().toLowerCase()) : expect.not.stringContaining(item.sample?.toString().toLowerCase())); } return item; }); } const textInArray = expectedArray.some((expected) => { if (expected instanceof RegExp) { return !!actual.match(expected); } if (isAsymmeyricMatcher(expected)) { return expected.asymmetricMatch(actual); } if (containing) { return actual.includes(expected); } if (atStart) { return actual.startsWith(expected); } if (atEnd) { return actual.endsWith(expected); } if (atIndex) { return actual.substring(atIndex, actual.length).startsWith(expected); } return actual === expected; }); return { value: actual, result: textInArray, }; }; export const compareObject = (actual, expected) => { if (typeof actual !== 'object' || Array.isArray(actual)) { return { value: actual, result: false, }; } return { value: actual, result: isEqual(actual, expected), }; }; export const compareStyle = async (actualEl, style, { ignoreCase = true, trim = false }) => { let result = true; const actual = {}; for (const key in style) { const css = await actualEl.getCSSProperty(key); let actualVal = css.value; let expectedVal = style[key]; if (trim) { actualVal = actualVal.trim(); expectedVal = expectedVal.trim(); } if (ignoreCase) { actualVal = actualVal.toLowerCase(); expectedVal = expectedVal.toLowerCase(); } result = result && actualVal === expectedVal; actual[key] = css.value; } return { value: actual, result, }; }; function aliasFn(fn, { verb, expectation, } = {}, ...args) { this.verb = verb; this.expectation = expectation; return fn.apply(this, args); } export { aliasFn, compareNumbers, enhanceError, executeCommand, executeCommandBe, numberError, waitUntil, wrapExpectedWithArray }; function replaceActual(replace, actual) { const hasMultipleReplacers = replace.every((r) => Array.isArray(r)); const replacers = hasMultipleReplacers ? replace : [replace]; if (replacers.some((r) => Array.isArray(r) && r.length !== 2)) { throw new Error('Replacers need to have a searchValue and a replaceValue'); } for (const replacer of replacers) { const [searchValue, replaceValue] = replacer; actual = actual.replace(searchValue, replaceValue); } return actual; }