Skip to content

Instantly share code, notes, and snippets.

@moatorres
Created November 5, 2021 20:23
Show Gist options
  • Save moatorres/09c46de6420bbdac424f6030a3c8848d to your computer and use it in GitHub Desktop.
Save moatorres/09c46de6420bbdac424f6030a3c8848d to your computer and use it in GitHub Desktop.
Get styles applied by `styled-components` with `jest` and `react-test-renderer`
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' })
})
})
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