kitchensink
Version:
Dispatch's awesome components and style guide
811 lines (662 loc) • 20.7 kB
JavaScript
/* eslint-disable react/prop-types */
import Radium from 'index.js';
import MouseUpListener from 'plugins/mouse-up-listener.js';
import React, {Component, PropTypes} from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import {getRenderOutput, getElement} from 'test-helpers';
describe('Radium blackbox tests', () => {
it('merges styles', () => {
class TestComponent extends Component {
render() {
return (
<div style={[
{color: 'blue'},
{background: 'red'}
]} />
);
}
}
const output = getRenderOutput(<TestComponent />);
expect(output.props.style).to.deep.equal(
{color: 'blue', background: 'red'}
);
});
it('merges nested styles', () => {
class TestComponent extends Component {
render() {
return (
<div style={[
[{color: 'blue'}, [{height: '2px', padding: '9px'}]],
{background: 'red'}
]} />
);
}
}
const output = getRenderOutput(<TestComponent />);
expect(output.props.style).to.deep.equal(
{color: 'blue', background: 'red', height: '2px', padding: '9px'}
);
});
it('resolves styles on props', () => {
class InnerComponent extends Component {}
class TestComponent extends Component {
render() {
return (
<InnerComponent header={
<div style={[{color: 'blue'}, {background: 'red'} ]}/>
} />
);
}
}
const output = getRenderOutput(<TestComponent />);
expect(output.props.header.props.style).to.deep.equal(
{color: 'blue', background: 'red'}
);
});
it('resolves styles on props', () => {
class InnerComponent extends Component {
render() {
return this.props.stuff;
}
}
class TestComponent extends Component {
render() {
return (
<InnerComponent stuff={
<div style={[
{color: 'blue'},
{background: 'red', ':active': {color: 'green'}}
]} />
} />
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
expect(div.style.color).to.equal('blue');
expect(div.style.background).to.equal('red');
TestUtils.Simulate.mouseDown(div);
expect(div.style.color).to.equal('green');
});
it('resolves styles on functions', () => {
class InnerComponent extends Component {
render() {
return this.props.children('arg');
}
}
class TestComponent extends Component {
render() {
return (
<InnerComponent>{arg =>
<div style={[
{color: 'blue'},
{background: 'red', ':active': {color: 'green'}}
]}>
{arg}
</div>
}</InnerComponent>
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
expect(div.style.color).to.equal('blue');
expect(div.style.background).to.equal('red');
expect(div.textContent).to.equal('arg');
TestUtils.Simulate.mouseDown(div);
expect(div.style.color).to.equal('green');
});
it('adds hover styles', () => {
class TestComponent extends Component {
render() {
return (
<div style={{
background: 'red',
color: 'blue',
':hover': {color: 'green'}
}} />
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
expect(div.style.color).to.equal('blue');
expect(div.style.background).to.equal('red');
TestUtils.SimulateNative.mouseOver(div);
expect(div.style.color).to.equal('green');
});
it('adds active styles', () => {
class TestComponent extends Component {
render() {
return (
<div style={{
background: 'red',
color: 'blue',
':active': {color: 'green'}
}} />
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
expect(div.style.color).to.equal('blue');
expect(div.style.background).to.equal('red');
TestUtils.Simulate.mouseDown(div);
expect(div.style.color).to.equal('green');
});
it('removes active styles on mouseup', () => {
class TestComponent extends Component {
render() {
return (
<div>
<span key="a" style={{
background: 'red',
color: 'blue',
':active': {color: 'green'}
}} />
<button key="b" style={{
background: 'red',
color: 'blue',
':active': {color: 'green'}
}} />
<nav key="c" style={{
background: 'red',
color: 'blue',
':active': {color: 'green'}
}} />
</div>
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const span = getElement(output, 'span');
const button = getElement(output, 'button');
const nav = getElement(output, 'nav');
expect(span.style.color).to.equal('blue');
expect(button.style.color).to.equal('blue');
expect(nav.style.color).to.equal('blue');
TestUtils.Simulate.mouseDown(span);
expect(span.style.color).to.equal('green');
MouseUpListener.__triggerForTests();
expect(span.style.color).to.equal('blue');
TestUtils.Simulate.mouseDown(button);
expect(button.style.color).to.equal('green');
MouseUpListener.__triggerForTests();
expect(button.style.color).to.equal('blue');
TestUtils.Simulate.mouseDown(nav);
expect(nav.style.color).to.equal('green');
MouseUpListener.__triggerForTests();
expect(nav.style.color).to.equal('blue');
});
it('resolves styles on multiple elements nested far down, Issue #307', () => {
class TestComponent extends Component {
render() {
return (
<section>
<section>
<section>
<header key="header" style={{
color: 'yellow',
':hover': { color: 'blue' }
}} />
<footer key="footer" style={{
color: 'green',
':hover': { color: 'red' }
}} />
</section>
</section>
</section>
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const header = getElement(output, 'header');
expect(header.style.color).to.equal('yellow');
const footer = getElement(output, 'footer');
expect(footer.style.color).to.equal('green');
TestUtils.SimulateNative.mouseOver(header);
TestUtils.SimulateNative.mouseOver(footer);
expect(header.style.color).to.equal('blue');
expect(footer.style.color).to.equal('red');
});
it('resolves styles if an element has element children and spreads props', () => {
class Inner extends Component {
static propTypes = { children: PropTypes.node };
render() {
return (
<div {...this.props} style={[{color: 'blue'}, {background: 'red'}]}>
{this.props.children}
</div>
);
}
}
class Outer extends Component {
render() {
return (
<Inner>
<span>We will break you.</span>
</Inner>
);
}
}
const output = TestUtils.renderIntoDocument(<Outer />);
const div = getElement(output, 'div');
expect(div.style.color).to.equal('blue');
expect(div.style.background).to.equal('red');
});
it('calls toString on object values', () => {
class TestComponent extends Component {
render() {
return (
<div style={{
background: {toString: () => 'red'}
}} />
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
expect(div.style.background).to.equal('red');
});
it('accepts a config', () => {
const truthyMatchMedia = function() {
return {
matches: true,
addListener: function() {},
removeListener: function() {}
};
};
({
matchMedia: truthyMatchMedia
})
class TestComponent extends Component {
render() {
return (
<div style={{
'@media (min-width: 600px)': {':hover': {color: 'blue'}}
}} />
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
TestUtils.SimulateNative.mouseOver(div);
expect(div.style.color).to.equal('blue');
});
it('transforms fallback values', () => {
()
class TestComponent extends Component {
render() {
return (
<div style={{
height: ['100%', '100vh']
}} />
);
}
}
const output = getRenderOutput(<TestComponent />);
expect(output.props.style).to.deep.equal(
{height: '100%;height:100vh'}
);
});
it('adds active styles on space', () => {
class TestComponent extends Component {
render() {
return (
<div style={{
background: 'red',
color: 'blue',
':active': {color: 'green'}
}} />
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
expect(div.style.color).to.equal('blue');
expect(div.style.background).to.equal('red');
TestUtils.SimulateNative.keyDown(div, {key: ' '});
expect(div.style.color).to.equal('green');
TestUtils.SimulateNative.keyUp(div, {key: ' '});
expect(div.style.color).to.equal('blue');
});
it('works with children as keyed object ala React Router', () => {
class TestComponent extends Component {
render() {
return (
<div>
{this.props.children.nav}
{this.props.children.main}
</div>
);
}
}
const output = TestUtils.renderIntoDocument(
<TestComponent>
{{
nav: <nav>nav</nav>,
main: <main>main</main>
}}
</TestComponent>
);
const nav = getElement(output, 'nav');
expect(nav.innerText).to.equal('nav');
const main = getElement(output, 'main');
expect(main.innerText).to.equal('main');
});
it('preserves array children as arrays', () => {
class TestComponent extends Component {
render() {
expect(Array.isArray(this.props.children)).to.equal(true);
return (
<div>
{this.props.children}
</div>
);
}
}
const output = TestUtils.renderIntoDocument(
<TestComponent>
{[
<nav key="nav">nav</nav>,
<main key="main">main</main>
]}
</TestComponent>
);
const nav = getElement(output, 'nav');
expect(nav.innerText).to.equal('nav');
const main = getElement(output, 'main');
expect(main.innerText).to.equal('main');
});
it('calls existing onMouseEnter handler', () => {
const handleMouseEnter = sinon.spy();
class TestComponent extends Component {
render() {
return (
<div
onMouseEnter={handleMouseEnter}
style={{':hover': {color: 'red'}}}
/>
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
TestUtils.SimulateNative.mouseOver(div);
expect(handleMouseEnter).to.have.been.called;
});
it('calls existing onMouseLeave handler', () => {
const handleMouseLeave = sinon.spy();
class TestComponent extends Component {
render() {
return (
<div
onMouseLeave={handleMouseLeave}
style={{':hover': {color: 'red'}}}
/>
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
TestUtils.SimulateNative.mouseOut(div);
expect(handleMouseLeave).to.have.been.called;
});
it('calls existing onMouseDown handler', () => {
const handleMouseDown = sinon.spy();
class TestComponent extends Component {
render() {
return (
<div
onMouseDown={handleMouseDown}
style={{':active': {color: 'red'}}}
/>
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
TestUtils.SimulateNative.mouseDown(div);
expect(handleMouseDown).to.have.been.called;
});
it('calls existing onFocus handler', () => {
const handleFocus = sinon.spy();
class TestComponent extends Component {
render() {
return (
<input
onFocus={handleFocus}
style={{':focus': {color: 'red'}}}
/>
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const input = getElement(output, 'input');
TestUtils.SimulateNative.focus(input);
expect(handleFocus).to.have.been.called;
});
it('calls existing onBlur handler', () => {
const handleBlur = sinon.spy();
class TestComponent extends Component {
render() {
return (
<input
onBlur={handleBlur}
style={{':focus': {color: 'red'}}}
/>
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const input = getElement(output, 'input');
TestUtils.SimulateNative.blur(input);
expect(handleBlur).to.have.been.called;
});
it('ignores callback refs', () => {
class TestComponent extends Component {
render() {
return (
<div>
<span key="a" ref={() => {}} style={{':hover': {color: 'red'}}} />
<nav key="b" ref={() => {}} style={{':hover': {color: 'red'}}} />
</div>
);
}
}
const output = TestUtils.renderIntoDocument(<TestComponent />);
const span = getElement(output, 'span');
const nav = getElement(output, 'nav');
TestUtils.SimulateNative.mouseOver(span);
expect(span.style.color).to.equal('red');
expect(nav.style.color).to.equal('');
TestUtils.SimulateNative.mouseOver(nav);
expect(nav.style.color).to.equal('red');
});
describe('plugins', () => {
it('runs a custom plugin', () => {
const makeItRedPlugin = () => ({style: {color: 'red'}});
class TestComponent extends Component {
render() {
return <div style={{}} />;
}
}
const output = TestUtils.renderIntoDocument(
<TestComponent radiumConfig={{plugins: [makeItRedPlugin]}} />
);
const div = getElement(output, 'div');
expect(div.style.color).to.equal('red');
});
});
/* eslint-disable no-console */
it('doesn\'t try to setState if not mounted', () => {
sinon.stub(console, 'error');
sinon.stub(console, 'warn');
let setStateCaptured;
const plugin = function({setState}) {
setStateCaptured = setState;
};
({plugins: [plugin]})
class TestComponent extends Component {
render() {
return <div style={{color: 'blue'}} />;
}
}
const output = TestUtils.renderIntoDocument(<TestComponent/>);
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(output).parentNode);
setStateCaptured('whatever');
expect(console.error).not.to.have.been.called;
expect(console.warn).not.to.have.been.called;
console.error.restore();
console.warn.restore();
});
/* eslint-enable no-console */
it('works with stateless components', () => {
let MyStatelessComponent = props => (
<div style={{color: 'blue', ':hover': {color: 'red'}}}>
{props.children}
</div>
);
// Babel is forced to use regular functions when defining arrow functions.
// Arrow functions should not technically have prototypes,
// so remove it here to make sure Radium doesn't fail with real arrow functions.
MyStatelessComponent.prototype = undefined;
MyStatelessComponent = Radium(MyStatelessComponent);
const output = TestUtils.renderIntoDocument(
<MyStatelessComponent>hello world</MyStatelessComponent>
);
const div = getElement(output, 'div');
expect(div.style.color).to.equal('blue');
expect(div.innerText).to.equal('hello world');
TestUtils.SimulateNative.mouseOver(div);
expect(div.style.color).to.equal('red');
});
it('works fine if passing null, undefined, or false in style', () => {
const TestComponent = Radium(() => (
<div style={{background: undefined, border: false, color: null}} />
));
const output = TestUtils.renderIntoDocument(<TestComponent />);
const div = getElement(output, 'div');
expect(div.style.background).to.equal('');
expect(div.style.border).to.equal('');
expect(div.style.color).to.equal('');
});
it('works with stateless components with context', () => {
let MyStatelessComponent = (props, context) => (
<div style={{color: 'blue', ':hover': {color: context.hoverColor}}}>
{props.children}
</div>
);
MyStatelessComponent.contextTypes = {
hoverColor: PropTypes.string
};
MyStatelessComponent = Radium(MyStatelessComponent);
class ContextGivingWrapper extends Component {
getChildContext() {
return {
hoverColor: 'green'
};
}
render() {
return this.props.children;
}
}
ContextGivingWrapper.childContextTypes = {
hoverColor: PropTypes.string
};
const output = TestUtils.renderIntoDocument(
<ContextGivingWrapper>
<MyStatelessComponent>hello world</MyStatelessComponent>
</ContextGivingWrapper>
);
const div = getElement(output, 'div');
expect(div.style.color).to.equal('blue');
expect(div.innerText).to.equal('hello world');
TestUtils.SimulateNative.mouseOver(div);
expect(div.style.color).to.equal('green');
});
it('transfers defaultProps for stateless components', () => {
const defaultProps = {foo: PropTypes.string};
let MyStatelessComponent = () => <div />;
MyStatelessComponent.defaultProps = defaultProps;
MyStatelessComponent = Radium(MyStatelessComponent);
expect(MyStatelessComponent.defaultProps).to.equal(defaultProps);
});
/* eslint-disable no-console */
it('replaces style propType with array or object', () => {
sinon.stub(console, 'error');
sinon.stub(console, 'warn');
class TestComponent extends Component {
render() {
return <div {...this.props} />;
}
}
TestComponent.propTypes = {style: PropTypes.object};
TestComponent = Radium(TestComponent);
TestUtils.renderIntoDocument(
<TestComponent style={[]} />
);
expect(console.error).not.to.have.been.called;
expect(console.warn).not.to.have.been.called;
console.error.restore();
console.warn.restore();
});
/* eslint-enable no-console */
describe('config', () => {
it('receives config from radiumConfig prop', () => {
const plugin = sinon.spy();
class TestComponent extends Component {
render() {
return <div style={{}} />;
}
}
TestUtils.renderIntoDocument(
<TestComponent radiumConfig={{plugins: [plugin]}} />
);
expect(plugin).to.have.been.called;
});
it('receives config from context', () => {
const plugin = sinon.spy();
class ParentComponent extends Component {
render() {
return <div style={{}}><ChildComponent /></div>;
}
}
class ChildComponent extends Component {
render() {
return <div style={{}} />;
}
}
TestUtils.renderIntoDocument(
<ParentComponent radiumConfig={{plugins: [plugin]}} />
);
expect(plugin).to.have.callCount(2);
});
});
});