Use Jest 🃏 and upgrade Webpack and other dependencies

old
Javi Velasco 2017-02-06 09:53:31 +01:00
parent 60b147f891
commit b48c17d1ad
19 changed files with 1536 additions and 1326 deletions

View File

@ -1,5 +1,8 @@
{ {
"extends": "stylelint-config-standard", "extends": "stylelint-config-standard",
"plugins": [
"stylelint-order"
],
"rules": { "rules": {
"at-rule-no-unknown": [true, { "at-rule-no-unknown": [true, {
ignoreAtRules: ["define-mixin", "mixin", "each"] ignoreAtRules: ["define-mixin", "mixin", "each"]
@ -11,7 +14,11 @@
] ]
}], }],
"color-hex-case": "lower", "color-hex-case": "lower",
"declaration-block-properties-order": "alphabetical", "order/declaration-block-order": [
"custom-properties",
"declarations"
],
"order/declaration-block-properties-alphabetical-order": true,
"font-family-name-quotes": "always-where-recommended", "font-family-name-quotes": "always-where-recommended",
"string-quotes": "single", "string-quotes": "single",
"selector-pseudo-class-no-unknown": [ "selector-pseudo-class-no-unknown": [

View File

@ -0,0 +1,7 @@
export function themr() {
return (Component) => {
Component.defaultProps = Component.defaultProps || {}; // eslint-disable-line no-param-reassign
Component.defaultProps.theme = {}; // eslint-disable-line no-param-reassign
return Component;
};
}

View File

@ -1,43 +1,35 @@
/* eslint-disable */
import expect from 'expect';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { mount } from 'enzyme';
import TestUtils from 'react-addons-test-utils'; import { Button } from '../Button';
import theme from '../theme.css'; import theme from '../theme.css';
import Button, { Button as RawButton } from '../Button';
const getRenderedClassName = (tree, Component) => {
const rendered = TestUtils.findRenderedComponentWithType(tree, Component);
return ReactDOM.findDOMNode(rendered).getAttribute('class');
};
describe('Button', () => { describe('Button', () => {
describe('#render', () => { describe('#render', () => {
it('uses flat and neutral styles by default', () => { it('uses flat and neutral styles by default', () => {
const tree = TestUtils.renderIntoDocument(<Button theme={theme} />); const wrapper = mount(<Button theme={theme} />);
const className = getRenderedClassName(tree, RawButton); const { className } = wrapper.find('button').props();
expect(className).toContain(theme.flat); expect(className).toContain(theme.flat);
expect(className).toContain(theme.neutral); expect(className).toContain(theme.neutral);
}); });
it('renders accent button with accent style', () => { it('renders accent button with accent style', () => {
const tree = TestUtils.renderIntoDocument(<Button accent theme={theme} />); const wrapper = mount(<Button accent theme={theme} />);
const className = getRenderedClassName(tree, RawButton); const { className } = wrapper.find('button').props();
expect(className).toContain(theme.flat); expect(className).toContain(theme.flat);
expect(className).toContain(theme.accent); expect(className).toContain(theme.accent);
}); });
it('renders mini button with mini style', () => { it('renders mini button with mini style', () => {
const tree = TestUtils.renderIntoDocument(<Button floating mini theme={theme} />); const wrapper = mount(<Button floating mini theme={theme} />);
const className = getRenderedClassName(tree, RawButton); const { className } = wrapper.find('button').props();
expect(className).toContain(theme.floating); expect(className).toContain(theme.floating);
expect(className).toContain(theme.neutral); expect(className).toContain(theme.neutral);
expect(className).toContain(theme.mini); expect(className).toContain(theme.mini);
}); });
it('renders mini accented button with both styles', () => { it('renders mini accented button with both styles', () => {
const tree = TestUtils.renderIntoDocument(<Button accent mini theme={theme} />); const wrapper = mount(<Button accent mini theme={theme} />);
const className = getRenderedClassName(tree, RawButton); const { className } = wrapper.find('button').props();
expect(className).toContain(theme.flat); expect(className).toContain(theme.flat);
expect(className).toContain(theme.accent); expect(className).toContain(theme.accent);
expect(className).toContain(theme.mini); expect(className).toContain(theme.mini);

View File

@ -1,12 +1,9 @@
/* eslint-disable */
import expect from 'expect';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { mount } from 'enzyme';
import ReactTestUtils from 'react-addons-test-utils';
import { themr } from 'react-css-themr'; import { themr } from 'react-css-themr';
import { CHIP } from '../../identifiers.js'; import { CHIP } from '../../identifiers';
import { chipFactory } from '../Chip';
import { tooltipFactory } from '../../tooltip'; import { tooltipFactory } from '../../tooltip';
import { chipFactory } from '../Chip';
const Avatar = ({ title }) => <span>{title}</span>; // eslint-disable-line react/prop-types const Avatar = ({ title }) => <span>{title}</span>; // eslint-disable-line react/prop-types
const Chip = themr(CHIP)(chipFactory(Avatar)); const Chip = themr(CHIP)(chipFactory(Avatar));
@ -14,40 +11,37 @@ const Chip = themr(CHIP)(chipFactory(Avatar));
describe('Chip', () => { describe('Chip', () => {
describe('with avatar', () => { describe('with avatar', () => {
it('adds the avatar class to the element', () => { it('adds the avatar class to the element', () => {
const tree = ReactTestUtils.renderIntoDocument( const wrapper = mount(
<Chip theme={{ avatar: 'avatar-class' }}> <Chip theme={{ avatar: 'avatar-class' }}>
<Avatar title="Test" /> <Avatar title="Test" />
<span>Test</span> <span>Test</span>
</Chip>, </Chip>,
); );
const chip = ReactTestUtils.findRenderedComponentWithType(tree, Chip); const chipNode = wrapper.find('div').node;
const chipNode = ReactDOM.findDOMNode(chip);
expect(chipNode.className).toMatch(/\bavatar-class\b/); expect(chipNode.className).toMatch(/\bavatar-class\b/);
}); });
it('works with non-flat children', () => { it('works with non-flat children', () => {
const TooltippedChip = tooltipFactory()(Chip); const TooltippedChip = tooltipFactory()(Chip);
const tree = ReactTestUtils.renderIntoDocument( const wrapper = mount(
<TooltippedChip theme={{ avatar: 'avatar-class' }} tooltip="Test tooltip"> <TooltippedChip theme={{ avatar: 'avatar-class' }} tooltip="Test tooltip">
<Avatar title="Test" /> <Avatar title="Test" />
<span>Test</span> <span>Test</span>
</TooltippedChip>, </TooltippedChip>,
); );
const chip = ReactTestUtils.findRenderedComponentWithType(tree, Chip); const chipNode = wrapper.find('div').node;
const chipNode = ReactDOM.findDOMNode(chip);
expect(chipNode.className).toMatch(/\bavatar-class\b/); expect(chipNode.className).toMatch(/\bavatar-class\b/);
}); });
}); });
describe('without avatar', () => { describe('without avatar', () => {
it('does not add avatar class to the element', () => { it('does not add avatar class to the element', () => {
const tree = ReactTestUtils.renderIntoDocument( const wrapper = mount(
<Chip theme={{ avatar: 'avatar-class' }}> <Chip theme={{ avatar: 'avatar-class' }}>
<span>Test</span> <span>Test</span>
</Chip>, </Chip>,
); );
const chip = ReactTestUtils.findRenderedComponentWithType(tree, Chip); const chipNode = wrapper.find('div').node;
const chipNode = ReactDOM.findDOMNode(chip);
expect(chipNode.className).toNotMatch(/\bavatar-class\b/); expect(chipNode.className).toNotMatch(/\bavatar-class\b/);
}); });
}); });

View File

@ -169,4 +169,5 @@ export {
DatePickerDialog, DatePickerDialog,
factory as datePickerFactory, factory as datePickerFactory,
}; };
export { Calendar };
export { DatePicker }; export { DatePicker };

View File

@ -1,15 +1,14 @@
/* eslint-disable */ import React from 'react';
import expect from 'expect'; import { shallow } from 'enzyme';
import theme from '../theme.css'; import theme from '../theme.css';
import { DatePickerDialog } from '../DatePicker'; import { DatePickerDialog, Calendar } from '../DatePicker';
import utils from '../../utils/testing';
describe('DatePickerDialog', () => { describe('DatePickerDialog', () => {
describe('#on mount', () => { describe('#on mount', () => {
it('passes value through to calendar if no maxDate/minDate specified', () => { it('passes value through to calendar if no maxDate/minDate specified', () => {
const value = new Date(2016, 1, 1); const value = new Date(2016, 1, 1);
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { theme, value }); const wrapper = shallow(<DatePickerDialog theme={theme} value={value} />);
expect(getDatePassedToCalendar(wrapper)).toBe(value); expect(wrapper.find(Calendar).props().selectedDate).toBe(value);
}); });
describe('when minDate but not maxDate specified', () => { describe('when minDate but not maxDate specified', () => {
@ -17,15 +16,19 @@ describe('DatePickerDialog', () => {
it('passes through a value after minDate', () => { it('passes through a value after minDate', () => {
const value = new Date(2016, 1, 3); const value = new Date(2016, 1, 3);
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { theme, value, minDate }); const wrapper = shallow(<DatePickerDialog theme={theme} minDate={minDate} value={value} />);
expect(getDatePassedToCalendar(wrapper)).toBe(value); expect(wrapper.find(Calendar).props().selectedDate).toBe(value);
}); });
it('sanitises a value before minDate to minDate', () => { it('sanitises a value before minDate to minDate', () => {
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { const wrapper = shallow(
theme, value: new Date(2016, 1, 1), minDate, <DatePickerDialog
}); theme={theme}
expect(getDatePassedToCalendar(wrapper)).toBe(minDate); minDate={minDate}
value={new Date(2016, 1, 1)}
/>,
);
expect(wrapper.find(Calendar).props().selectedDate).toBe(minDate);
}); });
}); });
@ -34,15 +37,25 @@ describe('DatePickerDialog', () => {
it('passes through a value before maxDate', () => { it('passes through a value before maxDate', () => {
const value = new Date(2016, 1, 1); const value = new Date(2016, 1, 1);
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { theme, value, maxDate }); const wrapper = shallow(
expect(getDatePassedToCalendar(wrapper)).toBe(value); <DatePickerDialog
theme={theme}
maxDate={maxDate}
value={value}
/>,
);
expect(wrapper.find(Calendar).props().selectedDate).toBe(value);
}); });
it('sanitises a value after maxDate to maxDate', () => { it('sanitises a value after maxDate to maxDate', () => {
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { const wrapper = shallow(
theme, value: new Date(2016, 1, 3), maxDate, <DatePickerDialog
}); theme={theme}
expect(getDatePassedToCalendar(wrapper)).toBe(maxDate); maxDate={maxDate}
value={new Date(2016, 1, 3)}
/>,
);
expect(wrapper.find(Calendar).props().selectedDate).toBe(maxDate);
}); });
}); });
@ -51,34 +64,41 @@ describe('DatePickerDialog', () => {
const maxDate = new Date(2016, 1, 4); const maxDate = new Date(2016, 1, 4);
it('sanitises value to minDate if value is before minDate', () => { it('sanitises value to minDate if value is before minDate', () => {
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { const wrapper = shallow(
theme, <DatePickerDialog
value: new Date(2016, 1, 1), theme={theme}
minDate, minDate={minDate}
maxDate, maxDate={maxDate}
}); value={new Date(2016, 1, 1)}
expect(getDatePassedToCalendar(wrapper)).toBe(minDate); />,
);
expect(wrapper.find(Calendar).props().selectedDate).toBe(minDate);
}); });
it('sanitises value to maxDate if value is after maxDate', () => { it('sanitises value to maxDate if value is after maxDate', () => {
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { const wrapper = shallow(
theme, <DatePickerDialog
value: new Date(2016, 1, 5), theme={theme}
minDate, minDate={minDate}
maxDate, maxDate={maxDate}
}); value={new Date(2016, 1, 5)}
expect(getDatePassedToCalendar(wrapper)).toBe(maxDate); />,
);
expect(wrapper.find(Calendar).props().selectedDate).toBe(maxDate);
}); });
it('doesn\'t sanitise when value is between maxDate/minDate', () => { it('doesn\'t sanitise when value is between maxDate/minDate', () => {
const value = new Date(2016, 1, 3); const value = new Date(2016, 1, 3);
const wrapper = utils.shallowRenderComponent(DatePickerDialog, { theme, value, minDate, maxDate }); const wrapper = shallow(
expect(getDatePassedToCalendar(wrapper)).toBe(value); <DatePickerDialog
theme={theme}
minDate={minDate}
maxDate={maxDate}
value={value}
/>,
);
expect(wrapper.find(Calendar).props().selectedDate).toBe(value);
}); });
}); });
function getDatePassedToCalendar(wrapper) {
return wrapper.props.children[1].props.children.props.selectedDate;
}
}); });
}); });

View File

@ -1,44 +0,0 @@
/* eslint-disable */
import expect from 'expect';
import React from 'react';
import {
renderIntoDocument,
scryRenderedDOMComponentsWithClass,
Simulate,
} from 'react-addons-test-utils';
import sinon from 'sinon';
import theme from '../theme.css';
import Dropdown from '../Dropdown';
describe('Dropdown', () => {
describe('#renderValue', () => {
const source = [
{ value: 'EN-gb', label: 'England' },
{ value: 'ES-es', label: 'Spain', disabled: true },
{ value: 'TH-th', label: 'Thailand', disabled: true },
{ value: 'EN-en', label: 'USA' },
];
it('renders dropdown item with disabled style', () => {
const tree = renderIntoDocument(<Dropdown theme={theme} source={source} />);
const disabled = scryRenderedDOMComponentsWithClass(tree, theme.disabled);
expect(disabled.length).toEqual(2);
});
it('does not call onChange callback when disabled dorpdown item is clicked', () => {
const spy = sinon.spy();
const tree = renderIntoDocument(<Dropdown
theme={theme}
source={source}
value={source[0].value}
onChange={spy}
/>);
const disabled = scryRenderedDOMComponentsWithClass(tree, theme.disabled);
expect(spy.called).toEqual(false);
Simulate.click(disabled[0]);
expect(spy.called).toEqual(false);
const selected = scryRenderedDOMComponentsWithClass(tree, theme.selected);
Simulate.click(selected[0]);
expect(spy.called).toEqual(true);
});
});
});

View File

@ -1,30 +1,20 @@
/* eslint-disable */
import expect from 'expect';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { shallow } from 'enzyme';
import ReactTestUtils from 'react-addons-test-utils'; import { Menu } from '../Menu';
import Menu from '../Menu'; import { MenuItem } from '../MenuItem';
import MenuItem, { MenuItem as RawMenuItem } from '../MenuItem';
describe('MenuItem', () => { describe('MenuItem', () => {
describe('#onClick', () => { describe('#onClick', () => {
it('passes to listener the event', () => { it('passes to listener the event', () => {
let listenerCalled = false; const onClick = jest.fn();
const handleClick = function (event) { const wrapper = shallow(
listenerCalled = true;
expect(event).toExist();
expect(event.target).toExist();
};
const tree = ReactTestUtils.renderIntoDocument(
<Menu> <Menu>
<MenuItem key="1" onClick={handleClick} /> <MenuItem key="1" onClick={onClick} />
</Menu>); </Menu>,
);
const menuItem = ReactTestUtils.findRenderedComponentWithType(tree, RawMenuItem); wrapper.find(MenuItem).first().simulate('click', { persist: () => {} });
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(menuItem)); expect(onClick).toHaveBeenCalled();
expect(listenerCalled).toBe(true);
}); });
}); });
}); });

View File

@ -1,70 +1,58 @@
/* eslint-disable */
import React from 'react'; import React from 'react';
import expect from 'expect'; import { mount, shallow } from 'enzyme';
import TestUtils from 'react-addons-test-utils'; import { ProgressBar } from '../ProgressBar';
import ProgressBar, { ProgressBar as RawProgressBar } from '../ProgressBar';
import theme from '../theme.css'; import theme from '../theme.css';
import utils from '../../utils/testing';
describe('ProgressBar', () => { describe('ProgressBar', () => {
let progressBar;
describe('#calculateRatio', () => { describe('#calculateRatio', () => {
before(() => {
const tree = TestUtils.renderIntoDocument(<ProgressBar min={100} max={300} theme={theme} />);
progressBar = TestUtils.findRenderedComponentWithType(tree, RawProgressBar);
});
it('calculates the right ratio', () => { it('calculates the right ratio', () => {
const wrapper = shallow(<ProgressBar min={100} max={300} theme={theme} />);
const progressBar = wrapper.instance();
expect(progressBar.calculateRatio(150)).toEqual(0.25); expect(progressBar.calculateRatio(150)).toEqual(0.25);
}); });
it('gets 0 when value is less than min', () => { it('gets 0 when value is less than min', () => {
const wrapper = shallow(<ProgressBar min={100} max={300} theme={theme} />);
const progressBar = wrapper.instance();
expect(progressBar.calculateRatio(10)).toEqual(0); expect(progressBar.calculateRatio(10)).toEqual(0);
}); });
it('gets 1 when value is more than max', () => { it('gets 1 when value is more than max', () => {
const wrapper = shallow(<ProgressBar min={100} max={300} theme={theme} />);
const progressBar = wrapper.instance();
expect(progressBar.calculateRatio(400)).toEqual(1); expect(progressBar.calculateRatio(400)).toEqual(1);
}); });
}); });
describe('#render', () => { describe('#render', () => {
let buffer,
value,
wrapper,
circle,
strokeLength;
it('renders the value and buffer bars when it is linear', () => { it('renders the value and buffer bars when it is linear', () => {
wrapper = utils.shallowRenderComponent(RawProgressBar, { theme }).props.children; const wrapper = mount(<ProgressBar theme={theme} />);
expect(wrapper.props.children.length).toEqual(2); expect(wrapper.childAt(0).props().children.length).toEqual(2);
}); });
it('renders the value and buffer bars when it is linear', () => { it('renders the value and buffer bars when it is linear', () => {
progressBar = utils.shallowRenderComponent(RawProgressBar, { mode: 'determinate', value: 30, buffer: 60, theme }); const wrapper = mount(<ProgressBar mode="determinate" value={30} buffer={60} theme={theme} />);
buffer = (progressBar.props.children.props.children[0]); const buffer = wrapper.childAt(0).childAt(0);
value = (progressBar.props.children.props.children[1]); const value = wrapper.childAt(0).childAt(1);
expect(buffer.props.style.transform).toEqual(`scaleX(${0.6})`); expect(buffer.props().style.transform).toEqual(`scaleX(${0.6})`);
expect(value.props.style.transform).toEqual(`scaleX(${0.3})`); expect(value.props().style.transform).toEqual(`scaleX(${0.3})`);
}); });
it('renders the svg circle when it is circular', () => { it('renders the svg circle when it is circular', () => {
progressBar = utils.shallowRenderComponent(RawProgressBar, { type: 'circular', theme }); const wrapper = mount(<ProgressBar type="circular" theme={theme} />);
expect(progressBar.props.children.type).toEqual('svg'); expect(wrapper.childAt(0).props().children.type).toEqual('circle');
expect(progressBar.props.children.props.children.type).toEqual('circle');
}); });
it('renders the proper circle length style when it is circular and determinate', () => { it('renders the proper circle length style when it is circular and determinate', () => {
progressBar = utils.shallowRenderComponent(RawProgressBar, { type: 'circular', mode: 'determinate', value: 30, theme }); const wrapper = mount(<ProgressBar type="circular" mode="determinate" value={30} theme={theme} />);
circle = progressBar.props.children.props.children; const circle = wrapper.childAt(0).props().children;
strokeLength = 2 * Math.PI * circle.props.r * 0.3; const strokeLength = 2 * Math.PI * circle.props.r * 0.3;
expect(circle.props.style.strokeDasharray).toEqual(`${strokeLength}, 400`); expect(circle.props.style.strokeDasharray).toEqual(`${strokeLength}, 400`);
}); });
it('contains mode and className in its className', () => { it('contains className in its className', () => {
progressBar = utils.shallowRenderComponent(RawProgressBar, { mode: 'determinate', className: 'tight', theme }); const wrapper = mount(<ProgressBar className="tight" mode="determinate" theme={theme} />);
expect(progressBar.props.className).toContain(theme.determinate); expect(wrapper.props().className).toContain('tight');
expect(progressBar.props.className).toContain(theme.tight);
}); });
}); });
}); });

View File

@ -1,79 +1,64 @@
/* eslint-disable */
import React from 'react'; import React from 'react';
import TestUtils from 'react-addons-test-utils'; import { mount, shallow } from 'enzyme';
import sinon from 'sinon'; import { Input } from '../../input/Input';
import expect from 'expect';
import { ProgressBar } from '../../progress_bar/ProgressBar'; import { ProgressBar } from '../../progress_bar/ProgressBar';
import Input, { Input as RawInput } from '../../input/Input'; import { Slider } from '../Slider';
import Slider, { Slider as RawSlider } from '../Slider';
import utils from '../../utils/testing';
import theme from '../theme.css'; import theme from '../theme.css';
describe('Slider', () => { describe('Slider', () => {
let slider,
progress,
input,
onChange;
describe('#positionToValue', () => { describe('#positionToValue', () => {
before(() => {
const tree = TestUtils.renderIntoDocument(<Slider min={-500} max={500} />);
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
slider.setState({ sliderStart: 500, sliderLength: 100 });
});
it('returns min when position is less than origin', () => { it('returns min when position is less than origin', () => {
expect(slider.positionToValue({ x: 400 })).toEqual(-500); const instance = shallow(<Slider min={-500} max={500} />).instance();
instance.setState({ sliderStart: 500, sliderLength: 100 });
expect(instance.positionToValue({ x: 400 })).toEqual(-500);
}); });
it('returns max when position is more and origin plus length', () => { it('returns max when position is more and origin plus length', () => {
expect(slider.positionToValue({ x: 900 })).toEqual(500); const instance = shallow(<Slider min={-500} max={500} />).instance();
instance.setState({ sliderStart: 500, sliderLength: 100 });
expect(instance.positionToValue({ x: 900 })).toEqual(500);
}); });
it('returns the proper position when the position is inside slider', () => { it('returns the proper position when the position is inside slider', () => {
expect(slider.positionToValue({ x: 520 })).toEqual(-300); const instance = shallow(<Slider min={-500} max={500} />).instance();
instance.setState({ sliderStart: 500, sliderLength: 100 });
expect(instance.positionToValue({ x: 520 })).toEqual(-300);
}); });
}); });
describe('#trimValue', () => { describe('#trimValue', () => {
before(() => {
const tree = TestUtils.renderIntoDocument(<Slider min={0} max={100} step={0.1} />);
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
});
it('rounds to the proper number', () => { it('rounds to the proper number', () => {
expect(slider.trimValue(57.16)).toEqual(57.2); const instance = shallow(<Slider min={0} max={100} step={0.1} />).instance();
expect(slider.trimValue(57.12)).toEqual(57.10); expect(instance.trimValue(57.16)).toEqual(57.2);
expect(instance.trimValue(57.12)).toEqual(57.10);
}); });
it('returns min if number is less than min', () => { it('returns min if number is less than min', () => {
expect(slider.trimValue(-57.16)).toEqual(0); const instance = shallow(<Slider min={0} max={100} step={0.1} />).instance();
expect(instance.trimValue(-57.16)).toEqual(0);
}); });
it('returns max if number is more than max', () => { it('returns max if number is more than max', () => {
expect(slider.trimValue(257.16)).toEqual(100); const instance = shallow(<Slider min={0} max={100} step={0.1} />).instance();
expect(instance.trimValue(257.16)).toEqual(100);
}); });
}); });
describe('#valueForInput', () => { describe('#valueForInput', () => {
before(() => {
const tree = TestUtils.renderIntoDocument(<Slider min={0} max={100} step={0.01} />);
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
});
it('returns a fixed number when an integer is given', () => { it('returns a fixed number when an integer is given', () => {
expect(slider.valueForInput(4)).toEqual('4.00'); const instance = shallow(<Slider min={0} max={100} step={0.01} />).instance();
expect(instance.valueForInput(4)).toEqual('4.00');
}); });
it('returns a fixed number when a float is given', () => { it('returns a fixed number when a float is given', () => {
expect(slider.valueForInput(4.06)).toEqual('4.06'); const instance = shallow(<Slider min={0} max={100} step={0.01} />).instance();
expect(instance.valueForInput(4.06)).toEqual('4.06');
}); });
}); });
describe('#knobOffset', () => { describe('#knobOffset', () => {
it('returns the corresponding offset for a given value and slider length/start', () => { it('returns the corresponding offset for a given value and slider length/start', () => {
const tree = TestUtils.renderIntoDocument(<Slider min={-500} max={500} value={-250} />); const slider = shallow(<Slider min={-500} max={500} value={-250} />).instance();
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
slider.setState({ sliderStart: 500, sliderLength: 100 }); slider.setState({ sliderStart: 500, sliderLength: 100 });
expect(slider.knobOffset()).toEqual(25); expect(slider.knobOffset()).toEqual(25);
}); });
@ -81,91 +66,97 @@ describe('Slider', () => {
describe('#render', () => { describe('#render', () => {
it('contains a linear progress bar with proper properties', () => { it('contains a linear progress bar with proper properties', () => {
const tree = TestUtils.renderIntoDocument(<Slider min={100} max={1000} value={140} />); const wrapper = mount(<Slider min={100} max={1000} value={140} />);
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider); const progress = wrapper.find(ProgressBar);
progress = TestUtils.findRenderedComponentWithType(slider, ProgressBar); expect(progress.props().mode).toEqual('determinate');
expect(progress.props.mode).toEqual('determinate'); expect(progress.props().type).toEqual('linear');
expect(progress.props.type).toEqual('linear'); expect(progress.props().value).toEqual(140);
expect(progress.props.value).toEqual(140); expect(progress.props().min).toEqual(100);
expect(progress.props.min).toEqual(100); expect(progress.props().max).toEqual(1000);
expect(progress.props.max).toEqual(1000);
}); });
it('contains an input component if its editable', () => { it('contains an input component if its editable', () => {
const tree = TestUtils.renderIntoDocument(<Slider editable value={130} />); const wrapper = mount(<Slider editable value={130} />);
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider); const slider = wrapper.instance();
input = TestUtils.findRenderedComponentWithType(slider, Input); const input = wrapper.find(Input);
expect(parseInt(input.props.value)).toEqual(slider.props.value); expect(parseInt(input.props().value, 10)).toEqual(slider.props.value);
}); });
it('contains the proper number of snaps when snapped', () => { it('contains the proper number of snaps when snapped', () => {
slider = utils.shallowRenderComponent(RawSlider, { editable: true, pinned: true, theme }); const wrapper = mount(<Slider editable pinned theme={theme} />);
expect(slider.props.className).toContain(theme.ring); const sliderNode = wrapper.find('div').first();
expect(slider.props.className).toContain(theme.pinned); expect(sliderNode.props().className).toContain(theme.ring);
slider = utils.shallowRenderComponent(RawSlider, { editable: true, value: 50, theme }); expect(sliderNode.props().className).toContain(theme.pinned);
expect(slider.props.className).toNotContain(theme.ring);
}); });
}); });
describe('#events', () => { describe('#events', () => {
beforeEach(() => {
onChange = sinon.spy();
const tree = TestUtils.renderIntoDocument(<Slider min={-500} max={500} onChange={onChange} />);
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider);
slider.setState({ sliderStart: 0, sliderLength: 1000 });
slider.handleResize = (event, callback) => { callback(); };
});
it('sets pressed state when knob is clicked', () => { it('sets pressed state when knob is clicked', () => {
TestUtils.Simulate.mouseDown(slider.knobNode); const onChange = jest.fn();
expect(slider.state.pressed).toEqual(true); const wrapper = mount(<Slider min={-500} max={500} onChange={onChange} />);
const knob = wrapper.childAt(0).childAt(0);
knob.simulate('mouseDown');
expect(wrapper.state().pressed).toEqual(true);
}); });
it('sets pressed state when knob is touched', () => { it('sets pressed state when knob is touched', () => {
TestUtils.Simulate.touchStart(slider.knobNode, { touches: [{ pageX: 200 }] }); const onChange = jest.fn();
expect(slider.state.pressed).toEqual(true); const event = { touches: [{ pageX: 200 }] };
const wrapper = mount(<Slider min={-500} max={500} onChange={onChange} />);
const knob = wrapper.childAt(0).childAt(0);
knob.simulate('touchStart', event);
expect(wrapper.state().pressed).toEqual(true);
}); });
it('sets a proper value when the slider is clicked', () => { it('sets a proper value when the slider is clicked', () => {
TestUtils.Simulate.mouseDown(slider.sliderNode, { pageX: 200 }); const onChange = jest.fn();
expect(onChange.called).toEqual(true); const event = { pageX: 200, pageY: 0 };
expect(onChange.getCall(0).args[0]).toEqual(-300); const wrapper = mount(<Slider min={-500} max={500} onChange={onChange} />);
const instance = wrapper.instance();
instance.setState({ sliderStart: 0, sliderLength: 1000 });
instance.handleResize = (evt, callback) => { callback(); };
wrapper.childAt(0).simulate('mouseDown', event);
expect(onChange).toHaveBeenCalledWith(-300);
}); });
it('sets a proper value when the slider is touched', () => { it('sets a proper value when the slider is touched', () => {
TestUtils.Simulate.touchStart(slider.sliderNode, { touches: [{ pageX: 200, pageY: 0 }] }); const onChange = jest.fn();
expect(onChange.called).toEqual(true); const event = { touches: [{ pageX: 200, pageY: 0 }] };
expect(onChange.getCall(0).args[0]).toEqual(-300); const wrapper = mount(<Slider min={-500} max={500} onChange={onChange} />);
const instance = wrapper.instance();
instance.setState({ sliderStart: 0, sliderLength: 1000 });
instance.handleResize = (evt, callback) => { callback(); };
wrapper.childAt(0).simulate('touchStart', event);
expect(onChange).toHaveBeenCalledWith(-300);
}); });
it('changes input value when slider changes', () => { it('changes input value when slider changes', () => {
const tree = TestUtils.renderIntoDocument(<Slider editable onChange={onChange} />); const onChange = jest.fn();
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider); const event = { pageX: 900 };
slider.setState({ sliderStart: 0, sliderLength: 1000 }); const wrapper = mount(<Slider editable onChange={onChange} />);
slider.handleResize = (event, callback) => { callback(); }; const instance = wrapper.instance();
input = TestUtils.findRenderedComponentWithType(slider, Input); instance.setState({ sliderStart: 0, sliderLength: 1000 });
TestUtils.Simulate.mouseDown(slider.sliderNode, { pageX: 900 }); instance.handleResize = (evt, callback) => { callback(); };
expect(onChange.called).toEqual(true); wrapper.childAt(0).simulate('mouseDown', event);
expect(onChange.getCall(0).args[0]).toEqual(90); expect(onChange).toHaveBeenCalledWith(90);
}); });
it('changes its value when input is blurred', () => { it('changes its value when input is blurred', () => {
const tree = TestUtils.renderIntoDocument(<Slider editable value={50} onChange={onChange} />); const onChange = jest.fn();
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider); const event = { target: { value: '80' } };
input = TestUtils.findRenderedComponentWithType(slider, RawInput); const wrapper = mount(<Slider editable value={50} onChange={onChange} />);
TestUtils.Simulate.change(input.inputNode, { target: { value: '80' } }); wrapper.find('input').simulate('change', event);
TestUtils.Simulate.blur(input.inputNode); wrapper.find('input').simulate('blur');
expect(onChange.called).toEqual(true); expect(onChange).toHaveBeenCalled();
expect(onChange.getCall(0).args[0]).toEqual(80); expect(onChange.mock.calls[0][0]).toEqual(80);
}); });
it('calls onChange callback when the value is changed', () => { it('calls onChange callback when the value is changed', () => {
const onChangeSpy = sinon.spy(); const onChange = jest.fn();
const tree = TestUtils.renderIntoDocument(<Slider onChange={onChangeSpy} />); const wrapper = mount(<Slider editable value={50} onChange={onChange} />);
slider = TestUtils.findRenderedComponentWithType(tree, RawSlider); wrapper.instance().setState({ sliderStart: 0, sliderLength: 1000 });
slider.setState({ sliderStart: 0, sliderLength: 1000 }); wrapper.childAt(0).simulate('mouseDown', { pageX: 900, pageY: 0 });
TestUtils.Simulate.mouseDown(slider.sliderNode, { pageX: 900 }); expect(onChange).toHaveBeenCalled();
expect(onChangeSpy.called).toEqual(true);
}); });
}); });
}); });

View File

@ -1,24 +1,11 @@
/* eslint-disable import/no-named-as-default */
import React, { Component } from 'react'; import React, { Component } from 'react';
import ReactDOM from 'react-dom'; import { mount } from 'enzyme';
import expect from 'expect'; import { Tabs } from '../Tabs';
import ReactTestUtils from 'react-addons-test-utils'; import { Tab } from '../Tab';
import utils from '../../utils/testing'; import { TabContent } from '../TabContent';
import Tabs, { Tabs as RawTabs } from '../Tabs';
import Tab from '../Tab';
import TabContent from '../TabContent';
import theme from '../theme.css'; import theme from '../theme.css';
const getRenderedClassName = (tree, TargetComponent) => {
const rendered = ReactTestUtils.findRenderedComponentWithType(tree, TargetComponent);
return ReactDOM.findDOMNode(rendered).getAttribute('class');
};
describe('Tabs', () => { describe('Tabs', () => {
let tabContents;
let composition;
class Composition extends Component { class Composition extends Component {
constructor() { constructor() {
super(); super();
@ -36,80 +23,46 @@ describe('Tabs', () => {
} }
it('defaults to only rendering the current tab', () => { it('defaults to only rendering the current tab', () => {
// initial render const wrapper = mount(<Composition />);
composition = utils.renderComponent(Composition); expect(wrapper.find(TabContent).length).toEqual(1);
expect(wrapper.find(TabContent).first().prop('tabIndex')).toEqual(0);
tabContents = ReactTestUtils wrapper.instance().setState({ index: 1 });
.scryRenderedComponentsWithType(composition, TabContent); expect(wrapper.find(TabContent).length).toEqual(1);
expect(wrapper.find(TabContent).first().prop('tabIndex')).toEqual(1);
expect(tabContents.length).toEqual(1);
expect(tabContents[0].props.tabIndex).toEqual(0);
// after tab change
composition.setState({ index: 1 });
composition.forceUpdate();
tabContents = ReactTestUtils
.scryRenderedComponentsWithType(composition, TabContent);
expect(tabContents.length).toEqual(1);
expect(tabContents[0].props.tabIndex).toEqual(1);
}); });
it('renders inactive tabs when hideMode is set to display', () => { it('renders inactive tabs when hideMode is set to display', () => {
// initial render const wrapper = mount(<Composition hideMode="display" />);
composition = utils.renderComponent(Composition, { hideMode: 'display' }); expect(wrapper.find(TabContent).length).toEqual(2);
expect(wrapper.find(TabContent).at(0).prop('hidden')).toEqual(false);
expect(wrapper.find(TabContent).at(1).prop('hidden')).toEqual(true);
tabContents = ReactTestUtils wrapper.instance().setState({ index: 1 });
.scryRenderedComponentsWithType(composition, TabContent); expect(wrapper.find(TabContent).length).toEqual(2);
expect(wrapper.find(TabContent).at(0).prop('hidden')).toEqual(true);
expect(tabContents.length).toEqual(2); expect(wrapper.find(TabContent).at(1).prop('hidden')).toEqual(false);
let tabOne = tabContents.find(tab => (tab.props.children === 'tab1'));
let tabTwo = tabContents.find(tab => (tab.props.children === 'tab2'));
expect(tabOne.props.hidden).toEqual(false);
expect(tabTwo.props.hidden).toEqual(true);
// after tab change
composition.setState({ index: 1 });
composition.forceUpdate();
tabContents = ReactTestUtils
.scryRenderedComponentsWithType(composition, TabContent);
expect(tabContents.length).toEqual(2);
tabOne = tabContents.find(tab => (tab.props.children === 'tab1'));
tabTwo = tabContents.find(tab => (tab.props.children === 'tab2'));
expect(tabOne.props.hidden).toEqual(true);
expect(tabTwo.props.hidden).toEqual(false);
}); });
describe('#render', () => { describe('#render', () => {
it('does not use fixed by default', () => { it('does not use fixed by default', () => {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} />); const wrapper = mount(<Tabs theme={theme} />);
const className = getRenderedClassName(tree, RawTabs); expect(wrapper.find('div').first().prop('className')).not.toContain(theme.fixed);
expect(className).toNotContain(theme.fixed);
}); });
it('uses fixed when set', () => { it('uses fixed when set', () => {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} fixed />); const wrapper = mount(<Tabs fixed theme={theme} />);
const className = getRenderedClassName(tree, RawTabs); expect(wrapper.find('div').first().prop('className')).toContain(theme.fixed);
expect(className).toContain(theme.fixed);
}); });
it('does not use inverse by default', () => { it('does not use inverse by default', () => {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} />); const wrapper = mount(<Tabs theme={theme} />);
const className = getRenderedClassName(tree, RawTabs); expect(wrapper.find('div').first().prop('className')).not.toContain(theme.inverse);
expect(className).toNotContain(theme.inverse);
}); });
it('uses inverse when set', () => { it('uses inverse when set', () => {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} inverse />); const wrapper = mount(<Tabs inverse theme={theme} />);
const className = getRenderedClassName(tree, RawTabs); expect(wrapper.find('div').first().prop('className')).toContain(theme.inverse);
expect(className).toContain(theme.inverse);
}); });
}); });
}); });

View File

@ -1,21 +0,0 @@
require('babel-polyfill');
const webpackConfig = require('./webpack.config.test');
module.exports = function (config) {
config.set({
browsers: ['PhantomJS'],
singleRun: true,
frameworks: ['mocha'],
files: [
'./node_modules/phantomjs-polyfill/bind-polyfill.js',
'./node_modules/babel-polyfill/dist/polyfill.js',
'tests.webpack.js',
],
reporters: ['dots'],
preprocessors: { 'tests.webpack.js': ['webpack'] },
webpack: webpackConfig,
webpackServer: {
noInfo: true,
},
});
};

View File

@ -5,21 +5,10 @@
"version": "2.0.0-beta.6", "version": "2.0.0-beta.6",
"main": "./lib", "main": "./lib",
"author": { "author": {
"name": "React Toolbox Team", "name": "Javier Velasco Arjona",
"url": "http://github.com/react-toolbox" "email": "javier.velasco86@gmail.com",
"url": "http://javivelasco.com/"
}, },
"contributors": [
{
"name": "Javi Jimenez Villar",
"email": "javi.jimenez.villar@gmail.com",
"url": "http://soyjavi.com/"
},
{
"name": "Javi Velasco Arjona",
"email": "javier.velasco86@gmail.com",
"url": "http://javivelasco.com/"
}
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/react-toolbox/react-toolbox.git" "url": "git+https://github.com/react-toolbox/react-toolbox.git"
@ -35,6 +24,20 @@
"react-component", "react-component",
"toolkit" "toolkit"
], ],
"jest": {
"modulePaths": [
"<rootDir>"
],
"moduleDirectories": [
"node_modules"
],
"moduleNameMapper": {
"(\\.css$)|(normalize.css/normalize)|(^exports-loader)": "identity-obj-proxy"
},
"snapshotSerializers": [
"enzyme-to-json/serializer"
]
},
"dependencies": { "dependencies": {
"classnames": "^2.2.5", "classnames": "^2.2.5",
"core-js": "^2.4.0", "core-js": "^2.4.0",
@ -58,30 +61,27 @@
"cpx": "^1.5.0", "cpx": "^1.5.0",
"cross-env": "^3.1.3", "cross-env": "^3.1.3",
"css-loader": "^0.26.1", "css-loader": "^0.26.1",
"eslint": "^3.14.1", "enzyme": "^2.7.1",
"eslint-config-airbnb": "^14.0.0", "enzyme-to-json": "^1.4.5",
"eslint": "^3.15.0",
"eslint-config-airbnb": "^14.1.0",
"eslint-import-resolver-webpack": "^0.8.1", "eslint-import-resolver-webpack": "^0.8.1",
"eslint-plugin-import": "^2.2.0", "eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^3.0.2", "eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.9.0", "eslint-plugin-react": "^6.9.0",
"expect": "^1.20.1", "express": "^4.14.1",
"express": "^4.14.0", "extract-text-webpack-plugin": "~2.0.0-rc.2",
"extract-text-webpack-plugin": "^1.0.1",
"git-dirty": "^1.0.2", "git-dirty": "^1.0.2",
"glob": "^7.1.1", "glob": "^7.1.1",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-babel": "^6.1.2", "gulp-babel": "^6.1.2",
"gulp-postcss": "^6.3.0", "gulp-postcss": "^6.3.0",
"identity-obj-proxy": "^3.0.0",
"internal-ip": "^1.2.0", "internal-ip": "^1.2.0",
"karma": "^1.4.0", "jest": "^18.1.0",
"karma-cli": "^1.0.0", "lint-staged": "^3.3.0",
"karma-mocha": "^1.3.0",
"karma-phantomjs-launcher": "^1.0.0",
"karma-webpack": "^2.0.2",
"lint-staged": "^3.2.8",
"mocha": "^3.1.2", "mocha": "^3.1.2",
"normalize.css": "^5.0.0", "normalize.css": "^5.0.0",
"phantomjs-prebuilt": "^2.1.14",
"postcss-cssnext": "^2.8.0", "postcss-cssnext": "^2.8.0",
"postcss-each": "^0.9.3", "postcss-each": "^0.9.3",
"postcss-import": "^9.1.0", "postcss-import": "^9.1.0",
@ -92,17 +92,19 @@
"react": "^15.4.2", "react": "^15.4.2",
"react-addons-test-utils": "^15.4.2", "react-addons-test-utils": "^15.4.2",
"react-dom": "^15.4.2", "react-dom": "^15.4.2",
"react-test-renderer": "^15.4.2",
"react-transform-catch-errors": "^1.0.2", "react-transform-catch-errors": "^1.0.2",
"react-transform-hmr": "^1.0.4", "react-transform-hmr": "^1.0.4",
"redbox-react": "^1.3.2", "redbox-react": "^1.3.2",
"rimraf": "^2.5.2", "rimraf": "^2.5.2",
"sinon": "^2.0.0-pre.2", "sinon": "^2.0.0-pre.2",
"style-loader": "^0.13.1", "style-loader": "^0.13.1",
"stylelint": "^7.5.0", "stylelint": "^7.8.0",
"stylelint-config-standard": "^15.0.0", "stylelint-config-standard": "^16.0.0",
"webpack": "^1.14.0", "stylelint-order": "^0.2.2",
"webpack-dev-middleware": "^1.9.0", "webpack": "~2.2.0",
"webpack-hot-middleware": "^2.13.0" "webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.16.1"
}, },
"scripts": { "scripts": {
"babel": "babel ./components --out-dir ./lib", "babel": "babel ./components --out-dir ./lib",
@ -118,8 +120,8 @@
"prepublish": "npm run build", "prepublish": "npm run build",
"release": "bumped release", "release": "bumped release",
"start": "cross-env NODE_ENV=development UV_THREADPOOL_SIZE=100 node ./server", "start": "cross-env NODE_ENV=development UV_THREADPOOL_SIZE=100 node ./server",
"test": "cross-env NODE_ENV=test karma start", "test": "node --harmony_proxies node_modules/.bin/jest --maxWorkers 4",
"test:watch": "cross-env NODE_ENV=test karma start --no-single-run", "test:watch": "node --harmony_proxies node_modules/.bin/jest --watch --no-watchman",
"tsd": "cpx \"./components/**/*.d.ts\" ./lib" "tsd": "cpx \"./components/**/*.d.ts\" ./lib"
}, },
"license": "MIT", "license": "MIT",

View File

@ -2,7 +2,7 @@ const path = require('path');
const express = require('express'); const express = require('express');
const webpack = require('webpack'); const webpack = require('webpack');
const internalIp = require('internal-ip'); const internalIp = require('internal-ip');
const config = require('./webpack.config.development'); const config = require('./webpack/webpack.config.dev');
const app = express(); const app = express();
const compiler = webpack(config); const compiler = webpack(config);

View File

@ -1,2 +0,0 @@
const context = require.context('./components', true, /.spec\.js$/);
context.keys().forEach(context);

View File

@ -1,59 +0,0 @@
const pkg = require('./package');
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
context: __dirname,
devtool: 'cheap-module-eval-source-map',
entry: [
'webpack-hot-middleware/client',
'babel-polyfill',
'./spec/index.js'
],
output: {
path: path.join(__dirname, 'build'),
filename: 'spec.js',
publicPath: '/build/'
},
resolve: {
extensions: ['', '.css', '.js', '.json'],
packageMains: ['browser', 'web', 'browserify', 'main', 'style']
},
module: {
loaders: [{
test: /\.js$/,
loader: 'babel',
include: [path.join(__dirname, './components'), path.join(__dirname, './spec')]
}, {
test: /\.css$/,
include: /node_modules/,
loader: ExtractTextPlugin.extract('style', 'css')
}, {
test: /\.css$/,
include: [path.join(__dirname, './components'), path.join(__dirname, './spec')],
loader: ExtractTextPlugin.extract('style', 'css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss')
}]
},
postcss () {
return [
require('postcss-import')({
root: __dirname,
path: [path.join(__dirname, './components')]
}),
require('postcss-mixins')(),
require('postcss-each')(),
require('postcss-cssnext')(),
require('postcss-reporter')({ clearMessages: true })
];
},
plugins: [
new ExtractTextPlugin('spec.css', { allChunks: true, disable: true }),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
VERSION: JSON.stringify(pkg.version)
})
]
};

View File

@ -1,40 +0,0 @@
const path = require('path');
const webpack = require('webpack');
module.exports = {
module: {
loaders: [{
test: /\.js$/,
include: [path.join(__dirname, './components'), path.join(__dirname, './spec')],
loader: 'babel'
}, {
test: /\.css$/,
include: /node_modules/,
loaders: ['style-loader', 'css-loader']
}, {
test: /\.css$/,
include: [path.join(__dirname, './components'), path.join(__dirname, './spec')],
loader: 'style!css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss'
}]
},
resolve: {
extensions: ['', '.css', '.js', '.json'],
packageMains: ['browser', 'web', 'browserify', 'main', 'style']
},
watch: true,
postcss () {
return [
require('postcss-import')({
root: __dirname,
path: [path.join(__dirname, './components')]
}),
require('postcss-cssnext')(),
require('postcss-reporter')({ clearMessages: true })
];
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('test')
})
]
};

View File

@ -0,0 +1,83 @@
const pkg = require('../package');
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
target: 'web',
context: path.join(__dirname, '../'),
devtool: 'cheap-module-eval-source-map',
entry: [
'webpack-hot-middleware/client',
'babel-polyfill',
'./spec/index.js'
],
output: {
path: path.join(__dirname, 'build'),
filename: 'spec.js',
publicPath: '/build/'
},
resolve: {
extensions: ['.js', '.css', '.json'],
modules: ['node_modules']
},
module: {
rules: [{
test: /\.js$/,
loader: 'babel-loader',
include: [
path.join(__dirname, '../components'),
path.join(__dirname, '../spec')
]
}, {
test: /\.css$/,
include: /node_modules/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
loader: 'css-loader',
})
}, {
test: /\.css$/,
include: [
path.join(__dirname, '../components'),
path.join(__dirname, '../spec')
],
use: ['style-loader', {
loader: 'css-loader',
query: {
modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]',
sourceMap: true
},
}, 'postcss-loader']
}]
},
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
context: path.join(__dirname, '../'),
postcss () {
return [
require('postcss-import')({
root: path.join(__dirname, '../'),
path: [path.join(__dirname, '../components')]
}),
require('postcss-mixins')(),
require('postcss-each')(),
require('postcss-cssnext')(),
require('postcss-reporter')({
clearMessages: true
})
];
}
}
}),
new ExtractTextPlugin({ filename: 'spec.css', allChunks: true }),
new webpack.HotModuleReplacementPlugin(),
new webpack.EvalSourceMapDevToolPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
VERSION: JSON.stringify(pkg.version)
})
]
};

2008
yarn.lock

File diff suppressed because it is too large Load Diff