test-real-styles
Version:
utilities to test real styling of dom elements
8 lines (7 loc) • 16.2 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../src/launch.ts", "../src/resolveStyleInput.ts", "../src/getStyles.ts", "../src/normalize.ts", "../src/extractStyles.ts", "../src/withTimeout.ts", "../src/getRealStyles.ts", "../src/toCss.ts"],
"sourcesContent": ["import playwright, { Browser, BrowserContext, Page } from 'playwright';\nimport { resolveStyleInput, Styles } from './resolveStyleInput';\nimport domToPlaywrightModule from 'dom-to-playwright';\nimport { getStyles, Options } from './getStyles';\nimport { withTimeout } from './withTimeout';\nimport { createQueue } from 'ichschwoer';\n\nconst domToPlaywright: typeof domToPlaywrightModule =\n /* @ts-ignore */\n domToPlaywrightModule.__esModule\n ? /* @ts-ignore */\n domToPlaywrightModule.default\n : domToPlaywrightModule;\n\nif (!(global as any).setImmediate) {\n /** @see https://github.com/microsoft/playwright/issues/18243 */\n (global as any).setImmediate = setTimeout;\n}\n\nexport const PLAYWRIGHT_BROWSERS = [\n 'chromium' as const,\n 'firefox' as const,\n 'webkit' as const,\n];\n\nexport type PlaywrightBrowser = (typeof PLAYWRIGHT_BROWSERS)[0];\nexport interface LaunchedPage {\n name: PlaywrightBrowser;\n getPlaywright(): Promise<{\n browser: Browser;\n context: BrowserContext;\n page: Page;\n }>;\n updatePage(\n element: HTMLElement | Document,\n options?: { transitions?: boolean; styles?: Styles | Promise<Styles> },\n ): Promise<void>;\n getStyles<T extends (keyof CSSStyleDeclaration)[]>(\n element: HTMLElement,\n styles: T,\n options?: Options,\n ): Promise<{ [key in T[0]]: string }>;\n hover(element: HTMLElement): Promise<void>;\n focus(element: HTMLElement): Promise<void>;\n}\n\nconst cache: { [key: string]: Promise<Browser> } = {};\n\nconst DISABLE_TRANSITIONS = `\n* {\n -moz-transition: none !important;\n -webkit-transition: none !important;\n transition: none !important;\n}`;\n\nexport default function launchPage(\n browserName: PlaywrightBrowser,\n stylesInput: Styles | Promise<Styles>,\n): LaunchedPage {\n if (!cache[browserName]) {\n cache[browserName] = withTimeout(\n playwright[browserName].launch(),\n `Failed to launch ${browserName} in under %n`,\n );\n }\n const browserP = cache[browserName];\n const contextP = withTimeout(\n browserP.then((b) => b.newContext()),\n `Failed to create context in ${browserName} in under %n`,\n );\n\n const pageP = withTimeout(\n contextP.then((c) => c.newPage()),\n `Failed to create page in ${browserName} in under %n`,\n );\n const dtpP = pageP.then(domToPlaywright);\n const globalStylesP = withTimeout(\n resolveStyleInput(stylesInput),\n `Failed to global resolve style input for ${browserName} in under %n`,\n );\n const q = createQueue(1);\n\n return {\n name: browserName,\n async getPlaywright() {\n return {\n browser: await browserP,\n context: await contextP,\n page: await pageP,\n };\n },\n updatePage(element, { transitions = false, styles: pageStylesP } = {}) {\n return q.push(async () => {\n const { update } = await dtpP;\n const page = await pageP;\n const styles = await globalStylesP;\n\n await withTimeout(\n update(element),\n `Failed to update page within ${browserName} in under %n`,\n );\n\n await withTimeout(\n page.addStyleTag(styles),\n `Failed to add global styles to page within ${browserName} in under %n`,\n );\n\n if (pageStylesP) {\n const pageStyles = await withTimeout(\n resolveStyleInput(pageStylesP),\n `Failed to resolve page style input for ${browserName} in under %n`,\n );\n\n await withTimeout(\n page.addStyleTag(pageStyles),\n `Failed to add page styles to page within ${browserName} in under %n`,\n );\n }\n\n if (!transitions) {\n await withTimeout(\n page.addStyleTag({ content: DISABLE_TRANSITIONS }),\n `Failed to disable transitions within ${browserName} in under %n`,\n );\n }\n });\n },\n\n getStyles(element, styles, options) {\n return q.push(async () => {\n const { select } = await dtpP;\n const page = await pageP;\n\n return withTimeout(\n getStyles(page, select(element), styles, options),\n `Failed to get styles within ${browserName} in under %n`,\n );\n });\n },\n hover(element) {\n return q.push(async () => {\n const { select } = await dtpP;\n const page = await pageP;\n\n await withTimeout(\n page.hover(select(element)),\n `Failed to hover element within ${browserName} in under %n`,\n );\n });\n },\n focus(element) {\n return q.push(async () => {\n const { select } = await dtpP;\n const page = await pageP;\n\n await withTimeout(\n page.focus(select(element)),\n `Failed to focus element within ${browserName} in under %n`,\n );\n });\n },\n };\n}\n\nexport async function cleanup() {\n const toBeCleared = { ...cache };\n for (const key in cache) {\n delete cache[key];\n }\n\n for (const key in toBeCleared) {\n const browser = await toBeCleared[key];\n for (const context of browser.contexts()) {\n await context.close();\n }\n await browser.close();\n }\n}\n\nif (typeof afterAll === 'function') {\n afterAll(cleanup);\n}\n", "export type Styles =\n | string\n | {\n url?: string;\n path?: string;\n content?: string;\n };\n\nexport async function resolveStyleInput(stylesP: Styles | Promise<Styles>) {\n const styles = await stylesP;\n if (typeof styles !== 'string') {\n return styles;\n }\n\n if (styles.match(/^http/)) {\n return { url: styles };\n } else if (styles.match(/\\{/)) {\n return { content: styles };\n }\n return { path: styles };\n}\n", "import { Page } from 'playwright';\nimport kebabCase from 'lodash.kebabcase';\nimport { normalize as defaultNormalize } from './normalize';\nimport { extractStyles } from './extractStyles';\n\nexport type Options = {\n normalize?: (value: string, prop: keyof CSSStyleDeclaration) => string;\n pseudoElt?: string;\n};\n\nexport async function getStyles<T extends (keyof CSSStyleDeclaration)[]>(\n page: Page,\n selector: string,\n props: T,\n { normalize = defaultNormalize, pseudoElt }: Options = {},\n): Promise<{ [key in T[0]]: string }> {\n const styles = props.map((prop: any) => {\n if (typeof prop !== 'string') {\n throw new Error(`Unexpected prop ${prop} of type ${typeof prop}`);\n }\n return {\n name: prop,\n key: kebabCase(prop).toLowerCase(),\n };\n });\n\n const computedStyles = await page.$eval(selector, extractStyles, {\n styles,\n pseudoElt,\n });\n\n return Object.keys(computedStyles).reduce((memo, key) => {\n memo[key] = normalize(computedStyles[key], key as any);\n return memo;\n }, {} as any);\n}\n", "import rgbHex from 'rgb-hex';\nimport namer from 'color-namer';\n\nexport function normalize(value: string): string {\n return value\n .replace(/rgb\\([0-9, ]+\\)/gi, (match) => {\n return `#${rgbHex(match)}`;\n })\n .replace(\n /(#([0-9a-f]{6}|[0-9a-f]{3}))([^0-9a-f]|$)/gi,\n (_, hex, __, delm) => {\n const namedColor = namer(hex).html[0];\n return `${(namedColor.distance === 0\n ? namedColor.name\n : hex\n ).toLowerCase()}${delm}`;\n },\n );\n}\n", "type Styles = { [key: string]: string };\n\n/* This is executed in the playwright browser\n * Therefore we can not have it instrumented for coverage reports\n * But the effects are covered in the other tests anyways */\n/* istanbul ignore next */\nexport function extractStyles(\n element: Element,\n { styles, pseudoElt }: { styles: Styles[]; pseudoElt?: string },\n): Styles {\n const computed = window.getComputedStyle(element, pseudoElt);\n\n return styles.reduce((memo, { key, name }) => {\n memo[name] = computed.getPropertyValue(key);\n return memo;\n }, {} as Styles);\n}\n", "export function withTimeout<PromisedValue extends any>(\n promisedValue: Promise<PromisedValue>,\n message: string = 'Timed out after %n',\n timeout: number = 10_000,\n): Promise<PromisedValue> {\n return Promise.race([\n promisedValue,\n new Promise<never>((_, rej) =>\n setTimeout(\n () =>\n rej(\n new Error(\n message.replace(\n '%n',\n timeout >= 1000\n ? `${Math.round(timeout / 1000)}s`\n : `${timeout}ms`,\n ),\n ),\n ),\n timeout,\n ),\n ),\n ]);\n}\n", "import type { Page } from 'playwright';\nimport { Styles } from './resolveStyleInput';\nimport { Options as GetStyleOptions } from './getStyles';\nimport launchPage, { PlaywrightBrowser } from './launch';\n\ntype Options<S> = {\n /**\n * the styles to be applied to the doc\n */\n css: Styles | Promise<Styles>;\n /**\n * The base document or html element that should be tested\n */\n doc: HTMLElement | Document;\n /**\n * The style declarations that we're interested in\n */\n getStyles: S;\n /**\n * Options for style-extraction\n */\n options?: GetStyleOptions;\n /**\n * The element of which the styles should be returned\n * @default {doc}\n */\n element?: HTMLElement;\n /**\n * An optional element that should be hovered\n */\n hover?: HTMLElement;\n /**\n * An optional element that should be focussed\n */\n focus?: HTMLElement;\n /**\n * The browser to test the styles in\n */\n browser?: PlaywrightBrowser;\n /**\n * CSS transitions are disabled by default, set true to enable them\n */\n transitions?: boolean;\n /**\n * Optionally prepare the playwright page\n */\n preparePage?: (page: Page) => void | Promise<void>;\n};\n\nfunction isDocument(elm: HTMLElement | Document): elm is Document {\n return Boolean((elm as any).body);\n}\n\nfunction getElement(elm: HTMLElement | Document) {\n return isDocument(elm) ? elm.body : elm;\n}\n\nexport default async function getRealStyles<\n T extends (keyof CSSStyleDeclaration)[],\n>({\n browser = 'chromium',\n css,\n doc,\n element = getElement(doc),\n hover,\n focus,\n getStyles,\n transitions,\n options,\n preparePage,\n}: Options<T>): Promise<{ [key in T[0]]: string }> {\n const sb = launchPage(browser, css);\n\n const { page, context } = await sb.getPlaywright();\n\n await preparePage?.(page);\n\n await sb.updatePage(doc, { transitions });\n\n if (focus) {\n await sb.focus(focus);\n }\n if (hover) {\n await sb.hover(hover);\n }\n\n const styles = await sb.getStyles(element, getStyles, options);\n\n await page.close();\n await context.close();\n\n return styles;\n}\n", "import kebabCase from 'lodash.kebabcase';\n\nexport function toCss(style: { [key: string]: string }): string {\n return Object.keys(style)\n .map((key) => `${kebabCase(key).toLowerCase()}: ${style[key]};`)\n .join('\\n');\n}\n"],
"mappings": "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",
"names": ["normalize", "getStyles", "kebabCase"]
}