Created
November 5, 2021 20:23
-
-
Save moatorres/09c46de6420bbdac424f6030a3c8848d to your computer and use it in GitHub Desktop.
Get styles applied by `styled-components` with `jest` and `react-test-renderer`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { getStyles } from './test-helpers' | |
describe('styled-components', () => { | |
test('extended components keep their styles', () => { | |
const Box = styled.div` | |
margin: 16px; | |
` | |
const Card = styled(Box)` | |
color: tomato; | |
` | |
const styles = getStyles(<Card />) | |
expect(styles).toEqual({ margin: '16px', color: 'tomato' }) | |
}) | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import renderer, { ReactTestRendererJSON } from 'react-test-renderer' | |
export function render(component: React.ReactElement) { | |
return ( | |
renderer | |
.create(component) | |
.toJSON() as ReactTestRendererJSON | |
) | |
} | |
export function renderClasses(component: React.ReactElement): string[] { | |
const { | |
props: { className }, | |
} = render(component) | |
return className ? className.trim().split(' ') : [] | |
} | |
type ComputedStyles = Record<string, string | Record<string, string>> | |
export function getComputedStyles(className: string) { | |
const div = document.createElement('div') | |
div.className = className | |
const computed: ComputedStyles = {} | |
for (const sheet of document.styleSheets) { | |
for (const rule of sheet.cssRules) { | |
if (rule instanceof CSSMediaRule) readMedia(rule) | |
else if (rule instanceof CSSStyleRule) readRule(rule, computed) | |
} | |
} | |
return computed | |
function matchesSafe(node: HTMLDivElement, selector: string) { | |
if (!selector) return false | |
try { | |
return node.matches(selector) | |
} catch (error) { | |
return false | |
} | |
} | |
function readRule(rule: CSSStyleRule, dest: ComputedStyles) { | |
if (matchesSafe(div, rule.selectorText)) { | |
const { style } = rule | |
for (let i = 0; i < style.length; i++) { | |
const prop = style[i] | |
dest[prop] = style.getPropertyValue(prop) | |
} | |
} | |
} | |
function readMedia(mediaRule: CSSMediaRule) { | |
const key = `@media ${mediaRule.media[0]}` | |
const dest = {} | |
for (const rule of mediaRule.cssRules) { | |
if (rule instanceof CSSStyleRule) readRule(rule, dest) | |
} | |
if (Object.keys(dest).length > 0) computed[key] = dest | |
} | |
} | |
/* Returns styles from all classes applied merged into a single object. */ | |
export function getStyles(comp: React.ReactElement) { | |
return renderClasses(comp) | |
.filter(c => !c.includes('sc')) | |
.map(getComputedStyles) | |
.reduce((result, current) => Object.assign(result, current), {}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment