UNPKG

react-ionicons

Version:

A React SVG ionicon component

392 lines (347 loc) 13.9 kB
import React from 'react' import { shallow } from 'enzyme' import { resetStyled, expectCSSMatches, seedNextClassnames } from './utils' import _injectGlobal from '../constructors/injectGlobal' import stringifyRules from '../utils/stringifyRules' import css from '../constructors/css' import _keyframes from '../constructors/keyframes' import StyleSheet, { SC_ATTR, LOCAL_ATTR } from '../models/StyleSheet' const keyframes = _keyframes(hash => `keyframe_${hash%1000}`, stringifyRules, css) const injectGlobal = _injectGlobal(stringifyRules, css) const getStyleTags = () => ( Array.from(document.querySelectorAll('style')).map(el => ({ isLocal: el.getAttribute('data-styled-components-is-local'), css: el.innerHTML.trim().replace(/\s+/mg, ' ') })) ) let styled describe('rehydration', () => { /** * Make sure the setup is the same for every test */ beforeEach(() => { styled = resetStyled() }) describe('with existing styled components', () => { beforeEach(() => { /* Hash 1323611362 is based on name TWO and contents color: red. * Change either and this will break. */ document.head.innerHTML = ` <style ${SC_ATTR}='b' ${LOCAL_ATTR}='true'> /* sc-component-id: TWO */ .TWO {} .b { color: red; } </style> ` StyleSheet.reset() }) it('should preserve the styles', () => { expectCSSMatches('.TWO {} .b { color: red; }') }) it('should append a new component like normal', () => { const Comp = styled.div.withConfig({ componentId: 'ONE' })` color: blue; ` shallow(<Comp />) expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color:blue; }') }) it('should reuse a componentId', () => { const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;` shallow(<A />) const B = styled.div.withConfig({ componentId: 'TWO' })`` shallow(<B />) expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color:blue; }') }) it('should reuse a componentId and generated class', () => { const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;` shallow(<A />) const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;` shallow(<B />) expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color:blue; }') }) it('should reuse a componentId and inject new classes', () => { const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;` shallow(<A />) const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;` shallow(<B />) const C = styled.div.withConfig({ componentId: 'TWO' })`color: green;` shallow(<C />) expectCSSMatches('.TWO {} .b { color: red; } .c { color:green; } .ONE { } .a { color:blue; }') }) }) describe('with styled components with props', () => { beforeEach(() => { /* Hash 1323611362 is based on name TWO and contents color: red. * Change either and this will break. */ document.head.innerHTML = ` <style ${SC_ATTR}='a b' ${LOCAL_ATTR}='true'> /* sc-component-id: ONE */ .ONE {} .a { color: blue; } /* sc-component-id: TWO */ .TWO {} .b { color: red; } </style> ` StyleSheet.reset() }) it('should preserve the styles', () => { expectCSSMatches(` .ONE { } .a { color: blue; } .TWO { } .b { color: red; } `) }) it('should not inject new styles for a component already rendered', () => { const Comp = styled.div.withConfig({ componentId: 'ONE' })` color: ${ props => props.color }; ` shallow(<Comp color="blue"/>) expectCSSMatches(` .ONE { } .a { color: blue; } .TWO { } .b { color: red; } `) }) it('should inject new styles for a new computed style of a component', () => { seedNextClassnames(['x']) const Comp = styled.div.withConfig({ componentId: 'ONE' })` color: ${ props => props.color }; ` shallow(<Comp color="green"/>) expectCSSMatches(` .ONE { } .a { color: blue; } .x { color:green; } .TWO { } .b { color: red; } `) }) }) describe('with inline styles that werent rendered by us', () => { beforeEach(() => { /* Same css as before, but without the data attributes we ignore it */ document.head.innerHTML = ` <style> /* sc-component-id: TWO */ .TWO {} .b { color: red; } </style> ` StyleSheet.reset() }) it('should leave the existing styles there', () => { expectCSSMatches('.TWO {} .b { color: red; }') }) it('should generate new classes, even if they have the same name', () => { const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;` shallow(<A />) const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;` shallow(<B />) expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color:blue; } .TWO {} .b { color:red; } ') }) }) describe('with global styles', () => { beforeEach(() => { /* Adding a non-local stylesheet with a hash 557410406 which is * derived from "body { background: papayawhip; }" so be careful * changing it. */ document.head.innerHTML = ` <style ${SC_ATTR} ${LOCAL_ATTR}='false'> /* sc-component-id: sc-global-557410406 */ body { background: papayawhip; } </style> <style ${SC_ATTR}='b' ${LOCAL_ATTR}='true'> /* sc-component-id: TWO */ .TWO {} .b { color: red; } </style> ` StyleSheet.reset() }) it('should leave the existing styles there', () => { expectCSSMatches('body { background: papayawhip; } .TWO {} .b { color: red; }') }) it('should inject new global styles at the end', () => { injectGlobal` body { color: tomato; } ` expectCSSMatches('body { background: papayawhip; } .TWO {} .b { color: red; } body { color:tomato; }') expect(getStyleTags()).toEqual([ { isLocal: 'false', css: '/* sc-component-id: sc-global-557410406 */ body { background: papayawhip; }', }, { isLocal: 'true', css: '/* sc-component-id: TWO */ .TWO {} .b { color: red; }', }, { isLocal: 'false', css: '/* sc-component-id: sc-global-2299393384 */ body{color:tomato;}', }, ]) }) it('should interleave global and local styles', () => { injectGlobal` body { color: tomato; } ` const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;` shallow(<A />) expectCSSMatches('body { background: papayawhip; } .TWO {} .b { color: red; } body { color:tomato; } .ONE { } .a { color:blue; }') expect(getStyleTags()).toEqual([ { isLocal: 'false', css: '/* sc-component-id: sc-global-557410406 */ body { background: papayawhip; }', }, { isLocal: 'true', css: '/* sc-component-id: TWO */ .TWO {} .b { color: red; }', }, { isLocal: 'false', css: '/* sc-component-id: sc-global-2299393384 */ body{color:tomato;}', }, { isLocal: 'true', css: '/* sc-component-id: ONE */ .ONE {} .a{color:blue;}', }, ]) }) }) describe('with all styles already rendered', () => { let styleTags beforeEach(() => { document.head.innerHTML = ` <style ${SC_ATTR} ${LOCAL_ATTR}='false'> /* sc-component-id: sc-global-1455077013 */ html { font-size: 16px; } /* sc-component-id: sc-global-557410406 */ body { background: papayawhip; } </style> <style ${SC_ATTR}='a b' ${LOCAL_ATTR}='true'> /* sc-component-id: ONE */ .ONE {} .a { color: blue; } /* sc-component-id: TWO */ .TWO {} .b { color: red; } </style> ` styleTags = Array.from(document.querySelectorAll('style')) StyleSheet.reset() }) it('should not touch existing styles', () => { expectCSSMatches(` html { font-size: 16px; } body { background: papayawhip; } .ONE { } .a { color: blue; } .TWO { } .b { color: red; } `) }) it('should replace stylesheets on-demand', () => { const tagsAfterReset = Array.from(document.querySelectorAll('style')) expect(tagsAfterReset[0]).toBe(styleTags[0]) expect(tagsAfterReset[1]).toBe(styleTags[1]) /* Rerendering existing tags doesn't touch the DOM */ const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;` shallow(<A />) const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;` shallow(<B />) const styleTagsAfterRehydration = Array.from(document.querySelectorAll('style')) expect(styleTagsAfterRehydration[0]).toBe(styleTags[0]) expect(styleTagsAfterRehydration[1]).toBe(styleTags[1]) /* Only when new components are introduced (or a previous component * generates a new hash) does the style tag get replaced. */ const C = styled.div.withConfig({ componentId: 'THREE' })`color: green;` shallow(<C />) const styleTagsAfterAddition = Array.from(document.querySelectorAll('style')) /* The first tag is unchanged */ expect(styleTagsAfterAddition[0]).toBe(styleTags[0]) /* The local tag has been replaced */ expect(styleTagsAfterAddition[1]).not.toBe(styleTags[1]) /* But it is identical, except for... */ expect(styleTagsAfterAddition[1].outerHTML).toEqual( styleTags[1].outerHTML /* ...the new data attribute for the new classname "c"... */ .replace(new RegExp(`${SC_ATTR}="a b"`), `${SC_ATTR}="a b c"`) /* ...and the new CSS before the closing tag. */ .replace(/(?=<\/style>)/, '\n/* sc-component-id: THREE */\n.THREE {}\n.c{color:green;}') ) /* Note: any future additions don't replace the style tag */ const D = styled.div.withConfig({ componentId: 'TWO' })`color: tomato;` shallow(<D />) expect(Array.from(document.querySelectorAll('style'))[1]).toBe(styleTagsAfterAddition[1]) /* The point being, now we have a style tag we can inject new styles in the middle! */ expectCSSMatches(` html { font-size: 16px; } body { background: papayawhip; } .ONE { } .a { color: blue; } .TWO { } .b { color: red; } .d { color:tomato; } .THREE { } .c { color:green; } `) }) it('should not change styles if rendered in the same order they were created with', () => { injectGlobal` html { font-size: 16px; } ` injectGlobal` body { background: papayawhip; } ` const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;` shallow(<A />) const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;` shallow(<B />) expectCSSMatches(` html { font-size: 16px; } body { background: papayawhip; } .ONE { } .a { color: blue; } .TWO { } .b { color: red; } `) }) it('should still not change styles if rendered in a different order', () => { const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;` shallow(<B />) injectGlobal` body { background: papayawhip; } ` const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;` shallow(<A />) injectGlobal` html { font-size: 16px; } ` expectCSSMatches(` html { font-size: 16px; } body { background: papayawhip; } .ONE { } .a { color: blue; } .TWO { } .b { color: red; } `) }) }) describe('with keyframes', () => { beforeEach(() => { document.head.innerHTML = ` <style ${SC_ATTR}='keyframe_880' ${LOCAL_ATTR}='false'> /* sc-component-id: sc-keyframes-keyframe_880 */ @-webkit-keyframes keyframe_880 {from {opacity: 0;}}@keyframes keyframe_880 {from {opacity: 0;}} </style> ` StyleSheet.reset() }) it('should not touch existing styles', () => { expectCSSMatches(` @-webkit-keyframes keyframe_880 {from {opacity: 0;}}@keyframes keyframe_880 {from {opacity: 0;}} `) }) it('should not regenerate keyframes', () => { keyframes` from { opacity: 0; } ` expectCSSMatches(` @-webkit-keyframes keyframe_880 {from {opacity: 0;}}@keyframes keyframe_880 {from {opacity: 0;}} `) }) it('should still inject new keyframes', () => { keyframes` from { opacity: 1; } ` expectCSSMatches(` @-webkit-keyframes keyframe_880 {from {opacity: 0;}}@keyframes keyframe_880 {from {opacity: 0;}} @-webkit-keyframes keyframe_144 {from {opacity:1;}}@keyframes keyframe_144 {from {opacity:1;}} `) }) it('should pass the keyframes name along as well', () => { const fadeIn = keyframes` from { opacity: 0; } ` const A = styled.div`animation: ${fadeIn} 1s both;` const fadeOut = keyframes` from { opacity: 1; } ` const B = styled.div`animation: ${fadeOut} 1s both;` /* Purposely rendering out of order to make sure the output looks right */ shallow(<B />) shallow(<A />) expectCSSMatches(` @-webkit-keyframes keyframe_880 {from {opacity: 0;}}@keyframes keyframe_880 {from {opacity: 0;}} .sc-a { } .d { -webkit-animation:keyframe_880 1s both; animation:keyframe_880 1s both; } @-webkit-keyframes keyframe_144 {from {opacity:1;}}@keyframes keyframe_144 {from {opacity:1;}} .sc-b { } .c { -webkit-animation:keyframe_144 1s both; animation:keyframe_144 1s both; } `) }) }) })