UNPKG

@atom-learning/jest-stitches

Version:
1 lines 17.9 kB
{"version":3,"file":"index.mjs","sources":["../../src/index.js"],"sourcesContent":["/**\n * A style serializer for Stiches. Hopefully we can replace this with\n * an official one in the future.\n */\nimport * as css from 'css'\nimport chalk from 'chalk'\n\n//\n// Utils\nconst flatMap = (arr, iteratee) => [].concat(...arr.map(iteratee))\n\nexport const RULE_TYPES = {\n media: 'media',\n rule: 'rule',\n}\n\nconst getClassNames = (selectors, classes) => {\n return classes ? selectors.concat(classes.split(' ')) : selectors\n}\n\nconst getClassNamesFromTestRenderer = (selectors, {props}) =>\n getClassNames(\n selectors,\n props ? (props.className || props.class || '').toString() : null\n )\n\nconst shouldDive = (node) =>\n typeof node.dive === 'function' && typeof node.type() !== 'string'\n\nconst isTagWithClassName = (node) =>\n node.prop('className') && typeof node.type() === 'string'\n\nconst getClassNamesFromEnzyme = (selectors, node) => {\n // We need to dive if we have selected a styled child from a shallow render\n const actualComponent = shouldDive(node) ? node.dive() : node\n // Find the first node with a className prop\n const components = actualComponent.findWhere(isTagWithClassName)\n const classes =\n components.length && components.first().prop('className')\n ? components.first().prop('className').toString()\n : components.first().prop('className')\n\n return getClassNames(selectors, classes)\n}\n\nconst getClassNamesFromCheerio = (selectors, node) => {\n const classes = node.attr('class')\n return getClassNames(selectors, classes)\n}\n\nconst getClassNamesFromDOMElement = (selectors, node) =>\n getClassNames(selectors, node.getAttribute('class'))\n\nexport const isReactElement = (val) => {\n if (val.$$typeof === Symbol.for('react.test.json')) {\n return true\n } else if (\n val.hasOwnProperty('props') &&\n val.hasOwnProperty('type') &&\n val.hasOwnProperty('ref') &&\n val.hasOwnProperty('key')\n ) {\n // Preact X\n try {\n val.$$typeof = Symbol.for('react.test.json')\n // eslint-disable-next-line no-empty\n } catch (err) {}\n return true\n }\n}\n\nconst domElementPattern = /^((HTML|SVG)\\w*)?Element$/\n\nexport const isDOMElement = (val) =>\n val.nodeType === 1 &&\n val.constructor &&\n val.constructor.name &&\n domElementPattern.test(val.constructor.name)\n\nconst isEnzymeElement = (val) => typeof val.findWhere === 'function'\nconst isCheerioElement = (val) => val.cheerio === '[cheerio object]'\n\nexport const getClassNamesFromNodes = (nodes) =>\n nodes.reduce((selectors, node) => {\n if (isReactElement(node)) {\n return getClassNamesFromTestRenderer(selectors, node)\n } else if (isEnzymeElement(node)) {\n return getClassNamesFromEnzyme(selectors, node)\n } else if (isCheerioElement(node)) {\n return getClassNamesFromCheerio(selectors, node)\n }\n\n return getClassNamesFromDOMElement(selectors, node)\n }, [])\n\nlet keyframesPattern = /^@keyframes\\s+(animation-[^{\\s]+)+/\n\nlet removeCommentPattern = /\\/\\*[\\s\\S]*?\\*\\//g\n\nconst getElementRules = (element) => {\n const nonSpeedyRule = element.textContent\n if (nonSpeedyRule) {\n return [nonSpeedyRule]\n }\n if (!element.sheet) {\n return []\n }\n // $FlowFixMe - flow doesn't know about `cssRules` property\n return [].slice.call(element.sheet.cssRules).map((cssRule) => cssRule.cssText)\n}\n\nexport const getStylesFromClassNames = (classNames, elements) => {\n if (!classNames.length) {\n return ''\n }\n\n let selectorPattern = new RegExp('\\\\.(' + classNames.join('|') + ')')\n let keyframes = {}\n let styles = ''\n\n flatMap(elements, getElementRules).forEach((rule) => {\n if (selectorPattern.test(rule)) {\n styles += rule\n }\n let match = rule.match(keyframesPattern)\n if (match !== null) {\n let name = match[1]\n if (keyframes[name] === undefined) {\n keyframes[name] = ''\n }\n keyframes[name] += rule\n }\n })\n let keyframeNameKeys = Object.keys(keyframes)\n let keyframesStyles = ''\n\n if (keyframeNameKeys.length) {\n let keyframesNamePattern = new RegExp(keyframeNameKeys.join('|'), 'g')\n let keyframesNameCache = {}\n let index = 0\n\n styles = styles.replace(keyframesNamePattern, (name) => {\n if (keyframesNameCache[name] === undefined) {\n keyframesNameCache[name] = `animation-${index++}`\n keyframesStyles += keyframes[name]\n }\n return keyframesNameCache[name]\n })\n\n keyframesStyles = keyframesStyles.replace(keyframesNamePattern, (value) => {\n return keyframesNameCache[value]\n })\n }\n\n return (keyframesStyles + styles).replace(removeCommentPattern, '')\n}\n\nexport const getStyleElements = () =>\n Array.from(document.querySelectorAll('style'))\n\nlet unique = (arr) => Array.from(new Set(arr))\n\nexport const getKeys = (elements) => unique(elements.filter(Boolean))\n\nexport const hasClassNames = (classNames, selectors, target) =>\n selectors.some((selector) => {\n // if no target, use className of the specific css rule and try to find it\n // in the list of received node classNames to make sure this css rule\n // applied for root element\n if (!target) {\n return classNames.includes(selector.slice(1))\n }\n\n // check if selector (className) of specific css rule match target\n return target instanceof RegExp\n ? target.test(selector)\n : minify(selector).includes(minify(target))\n })\n\nexport const getMediaRules = (rules, media) =>\n rules\n .filter((rule) => {\n const isMediaMatch = rule.media\n ? rule.media.replace(/\\s/g, '').includes(media.replace(/\\s/g, ''))\n : false\n return rule.type === RULE_TYPES.media && isMediaMatch\n })\n .reduce((mediaRules, mediaRule) => mediaRules.concat(mediaRule.rules), [])\n\n//\n// Matchers\n/*\n * Taken from\n * https://github.com/facebook/jest/blob/be4bec387d90ac8d6a7596be88bf8e4994bc3ed9/packages/expect/src/jasmine_utils.js#L234\n */\nconst isA = (typeName, value) =>\n Object.prototype.toString.apply(value) === `[object ${typeName}]`\n\n/*\n * Taken from\n * https://github.com/facebook/jest/blob/be4bec387d90ac8d6a7596be88bf8e4994bc3ed9/packages/expect/src/jasmine_utils.js#L36\n */\nconst isAsymmetric = (obj) => obj && isA('Function', obj.asymmetricMatch)\n\nconst valueMatches = (declaration, value) => {\n if (value instanceof RegExp) {\n return value.test(declaration.value)\n }\n\n if (isAsymmetric(value)) {\n return value.asymmetricMatch(declaration.value)\n }\n\n return minify(value) === minify(declaration.value)\n}\n\nconst minLeft = /([:;,([{}>~/\\s]|\\/\\*)\\s+/g\nconst minRight = /\\s+([:;,)\\]{}>~/!]|\\*\\/)/g\nconst minify = (s) => s.trim().replace(minLeft, '$1').replace(minRight, '$1')\n\nconst toHaveStyleRule = (received, property, value, options = {}) => {\n const {target, media} = options\n const classNames = getClassNamesFromNodes([received])\n const cssString = getStylesFromClassNames(classNames, getStyleElements())\n const styles = css.parse(cssString)\n let preparedRules = styles.stylesheet.rules\n if (media) {\n preparedRules = getMediaRules(preparedRules, media)\n }\n\n const declaration = preparedRules\n .filter(\n (rule) =>\n rule.type === RULE_TYPES.rule &&\n hasClassNames(classNames, rule.selectors, target)\n )\n .reduce((decs, rule) => decs.concat(rule.declarations), [])\n .filter(\n (dec) =>\n dec.type === 'declaration' && minify(dec.property) === minify(property)\n )\n .pop()\n\n if (!declaration) {\n return {\n pass: false,\n message: () => `Property not found: ${property}`,\n }\n }\n\n const pass = valueMatches(declaration, value)\n\n const message = () =>\n `Expected ${property}${pass ? ' not ' : ' '}to match:\\n` +\n ` ${chalk.green(value)}\\n` +\n 'Received:\\n' +\n ` ${chalk.red(declaration.value)}`\n\n return {\n pass,\n message,\n }\n}\n\nexport const matchers = {toHaveStyleRule}\n\n//\n// Pretty serialization\nconst componentSelectorClassNamePattern = /^e[a-zA-Z0-9]+[0-9]+$/\nexport const replaceClassNames = (classNames, styles, code) => {\n return classNames.reduce((acc, className) => {\n if (componentSelectorClassNamePattern.test(className)) {\n return acc\n }\n return acc\n }, `${styles}${styles ? '\\n\\n' : ''}${code}`)\n}\n\nconst getNodes = (node, nodes = []) => {\n if (Array.isArray(node)) {\n for (let child of node) {\n getNodes(child, nodes)\n }\n return nodes\n }\n\n let children = node.children || (node.props && node.props.children)\n\n if (children) {\n // fix for Preact X\n children = node.props\n ? Array.isArray(children)\n ? children\n : [children]\n : children\n\n for (let child of children) {\n getNodes(child, nodes)\n }\n }\n\n if (typeof node === 'object') {\n nodes.push(node)\n }\n\n return nodes\n}\n\nconst getPrettyStylesFromClassNames = (classNames, elements) => {\n let styles = getStylesFromClassNames(classNames, elements)\n\n let prettyStyles\n\n const parsedStyles = css.parse(styles)\n\n // ensure document level root custom properties are removed\n parsedStyles.stylesheet.rules = parsedStyles.stylesheet.rules\n .map((rule) => {\n if (rule.type === 'keyframes') {\n let tempRule = null\n parsedStyles.stylesheet.rules.some((otherRule) =>\n otherRule.declarations?.some((declaration) => {\n if (\n (declaration.property === 'animation-name' ||\n declaration.property === 'animation') &&\n declaration.value === rule.name\n ) {\n tempRule = rule\n }\n })\n )\n return tempRule\n }\n if (rule.selectors?.includes(':root')) {\n return null\n }\n return rule\n })\n .filter(Boolean)\n\n try {\n prettyStyles = css.stringify(parsedStyles)\n } catch (e) {\n console.error(e)\n throw new Error(`There was an error parsing the following css: \"${styles}\"`)\n }\n\n return prettyStyles\n}\n\nexport const createSerializer = (opt = {}) => {\n let {DOMElements = true} = opt\n let cache = new WeakSet()\n\n return {\n test(val) {\n return (\n val &&\n !cache.has(val) &&\n (isReactElement(val) || (DOMElements && isDOMElement(val)))\n )\n },\n\n print(val, printer) {\n const nodes = getNodes(val)\n const classNames = getClassNamesFromNodes(nodes)\n let elements = getStyleElements()\n const styles = getPrettyStylesFromClassNames(classNames, elements)\n nodes.forEach(cache.add, cache)\n const printedVal = printer(val)\n nodes.forEach(cache.delete, cache)\n let keys = getKeys(elements)\n return replaceClassNames(classNames, styles, printedVal, keys)\n },\n }\n}\n\nexport const {print, test} = createSerializer()\nexport default {print, test}\n"],"names":["selectors","node","isReactElement","props","getClassNames","className","class","toString","getClassNamesFromTestRenderer","findWhere","components","dive","type","shouldDive","isTagWithClassName","classes","length","first","prop","getClassNamesFromEnzyme","val","cheerio","isCheerioElement","attr","getClassNamesFromCheerio","getAttribute","getClassNamesFromDOMElement","cssRule","cssText","mediaRules","mediaRule","concat","rules","decs","rule","declarations","acc","componentSelectorClassNamePattern","test","RULE_TYPES","media","split","$$typeof","Symbol","for","hasOwnProperty","err","domElementPattern","isDOMElement","nodeType","constructor","name","getClassNamesFromNodes","nodes","reduce","keyframesPattern","removeCommentPattern","getElementRules","element","nonSpeedyRule","textContent","sheet","slice","call","cssRules","map","getStylesFromClassNames","classNames","elements","arr","iteratee","selectorPattern","RegExp","join","keyframes","styles","forEach","match","undefined","keyframeNameKeys","Object","keys","keyframesStyles","keyframesNamePattern","keyframesNameCache","index","replace","value","getStyleElements","Array","from","document","querySelectorAll","getKeys","unique","filter","Boolean","Set","hasClassNames","target","some","selector","minify","includes","getMediaRules","isMediaMatch","minLeft","minRight","s","trim","matchers","toHaveStyleRule","received","property","options","cssString","preparedRules","css","stylesheet","declaration","dec","pop","pass","message","obj","asymmetricMatch","prototype","apply","isAsymmetric","valueMatches","chalk","green","red","replaceClassNames","code","createSerializer","opt","DOMElements","cache","WeakSet","has","print","printer","getNodes","isArray","child","children","push","prettyStyles","parsedStyles","tempRule","otherRule","_otherRule$declaratio","_rule$selectors","e","console","error","Error","getPrettyStylesFromClassNames","add","printedVal","delete"],"mappings":"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"}