UNPKG

test-real-styles

Version:

utilities to test real styling of dom elements

273 lines (262 loc) 7.27 kB
// src/launch.ts import playwright from "playwright"; // src/resolveStyleInput.ts async function resolveStyleInput(stylesP) { const styles = await stylesP; if (typeof styles !== "string") { return styles; } if (styles.match(/^http/)) { return { url: styles }; } else if (styles.match(/\{/)) { return { content: styles }; } return { path: styles }; } // src/launch.ts import domToPlaywrightModule from "dom-to-playwright"; // src/getStyles.ts import kebabCase from "lodash.kebabcase"; // src/normalize.ts import rgbHex from "rgb-hex"; import namer from "color-namer"; function normalize(value) { return value.replace(/rgb\([0-9, ]+\)/gi, (match) => { return `#${rgbHex(match)}`; }).replace( /(#([0-9a-f]{6}|[0-9a-f]{3}))([^0-9a-f]|$)/gi, (_, hex, __, delm) => { const namedColor = namer(hex).html[0]; return `${(namedColor.distance === 0 ? namedColor.name : hex).toLowerCase()}${delm}`; } ); } // src/extractStyles.ts function extractStyles(element, { styles, pseudoElt }) { const computed = window.getComputedStyle(element, pseudoElt); return styles.reduce((memo, { key, name }) => { memo[name] = computed.getPropertyValue(key); return memo; }, {}); } // src/getStyles.ts async function getStyles(page, selector, props, { normalize: normalize2 = normalize, pseudoElt } = {}) { const styles = props.map((prop) => { if (typeof prop !== "string") { throw new Error(`Unexpected prop ${prop} of type ${typeof prop}`); } return { name: prop, key: kebabCase(prop).toLowerCase() }; }); const computedStyles = await page.$eval(selector, extractStyles, { styles, pseudoElt }); return Object.keys(computedStyles).reduce((memo, key) => { memo[key] = normalize2(computedStyles[key], key); return memo; }, {}); } // src/withTimeout.ts function withTimeout(promisedValue, message = "Timed out after %n", timeout = 1e4) { return Promise.race([ promisedValue, new Promise( (_, rej) => setTimeout( () => rej( new Error( message.replace( "%n", timeout >= 1e3 ? `${Math.round(timeout / 1e3)}s` : `${timeout}ms` ) ) ), timeout ) ) ]); } // src/launch.ts import { createQueue } from "ichschwoer"; var domToPlaywright = ( /* @ts-ignore */ domToPlaywrightModule.__esModule ? ( /* @ts-ignore */ domToPlaywrightModule.default ) : domToPlaywrightModule ); if (!global.setImmediate) { global.setImmediate = setTimeout; } var PLAYWRIGHT_BROWSERS = [ "chromium", "firefox", "webkit" ]; var cache = {}; var DISABLE_TRANSITIONS = ` * { -moz-transition: none !important; -webkit-transition: none !important; transition: none !important; }`; function launchPage(browserName, stylesInput) { if (!cache[browserName]) { cache[browserName] = withTimeout( playwright[browserName].launch(), `Failed to launch ${browserName} in under %n` ); } const browserP = cache[browserName]; const contextP = withTimeout( browserP.then((b) => b.newContext()), `Failed to create context in ${browserName} in under %n` ); const pageP = withTimeout( contextP.then((c) => c.newPage()), `Failed to create page in ${browserName} in under %n` ); const dtpP = pageP.then(domToPlaywright); const globalStylesP = withTimeout( resolveStyleInput(stylesInput), `Failed to global resolve style input for ${browserName} in under %n` ); const q = createQueue(1); return { name: browserName, async getPlaywright() { return { browser: await browserP, context: await contextP, page: await pageP }; }, updatePage(element, { transitions = false, styles: pageStylesP } = {}) { return q.push(async () => { const { update } = await dtpP; const page = await pageP; const styles = await globalStylesP; await withTimeout( update(element), `Failed to update page within ${browserName} in under %n` ); await withTimeout( page.addStyleTag(styles), `Failed to add global styles to page within ${browserName} in under %n` ); if (pageStylesP) { const pageStyles = await withTimeout( resolveStyleInput(pageStylesP), `Failed to resolve page style input for ${browserName} in under %n` ); await withTimeout( page.addStyleTag(pageStyles), `Failed to add page styles to page within ${browserName} in under %n` ); } if (!transitions) { await withTimeout( page.addStyleTag({ content: DISABLE_TRANSITIONS }), `Failed to disable transitions within ${browserName} in under %n` ); } }); }, getStyles(element, styles, options) { return q.push(async () => { const { select } = await dtpP; const page = await pageP; return withTimeout( getStyles(page, select(element), styles, options), `Failed to get styles within ${browserName} in under %n` ); }); }, hover(element) { return q.push(async () => { const { select } = await dtpP; const page = await pageP; await withTimeout( page.hover(select(element)), `Failed to hover element within ${browserName} in under %n` ); }); }, focus(element) { return q.push(async () => { const { select } = await dtpP; const page = await pageP; await withTimeout( page.focus(select(element)), `Failed to focus element within ${browserName} in under %n` ); }); } }; } async function cleanup() { const toBeCleared = { ...cache }; for (const key in cache) { delete cache[key]; } for (const key in toBeCleared) { const browser = await toBeCleared[key]; for (const context of browser.contexts()) { await context.close(); } await browser.close(); } } if (typeof afterAll === "function") { afterAll(cleanup); } // src/getRealStyles.ts function isDocument(elm) { return Boolean(elm.body); } function getElement(elm) { return isDocument(elm) ? elm.body : elm; } async function getRealStyles({ browser = "chromium", css, doc, element = getElement(doc), hover, focus, getStyles: getStyles2, transitions, options, preparePage }) { const sb = launchPage(browser, css); const { page, context } = await sb.getPlaywright(); await preparePage?.(page); await sb.updatePage(doc, { transitions }); if (focus) { await sb.focus(focus); } if (hover) { await sb.hover(hover); } const styles = await sb.getStyles(element, getStyles2, options); await page.close(); await context.close(); return styles; } // src/toCss.ts import kebabCase2 from "lodash.kebabcase"; function toCss(style) { return Object.keys(style).map((key) => `${kebabCase2(key).toLowerCase()}: ${style[key]};`).join("\n"); } export { PLAYWRIGHT_BROWSERS, cleanup, getRealStyles, launchPage as launch, normalize as normalizeValue, toCss }; //# sourceMappingURL=index.mjs.map