diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..683e503
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+ - 6.6
diff --git a/package.json b/package.json
index bf86ab3..5355369 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,9 @@
"clean": "rimraf dist/*",
"copy": "copyfiles -f ./src/index.html ./dist && copyfiles -u 1 \"./src/static/**\" ./dist",
"dist": "npm run clean && npm run copy && webpack --progress --bail --env dist -p",
- "lint": "eslint ./src",
+ "lint": "npm run lint:src && npm run lint:test",
+ "lint:src": "eslint ./src",
+ "lint:test": "eslint ./test",
"posttest": "npm run lint",
"release:major": "npm version prerelease && git push --follow-tags && npm publish --tag beta",
"release:minor": "npm version prerelease && git push --follow-tags && npm publish --tag beta",
@@ -61,7 +63,7 @@
"phantomjs-prebuilt": "^2.1.7",
"react-addons-test-utils": "^15.0.1",
"rimraf": "^2.5.2",
- "sinon": "^1.17.3",
+ "sinon": "^2.3.2",
"style-loader": "^0.13.1",
"stylus": "^0.54.5",
"stylus-loader": "^2.1.0",
diff --git a/src/containers/Palette.js b/src/containers/Palette.js
index 54c7d2a..ff3eb7a 100644
--- a/src/containers/Palette.js
+++ b/src/containers/Palette.js
@@ -5,7 +5,7 @@ import { bindActionCreators } from 'redux';
import { changeColor, changeWidth } from '../actions/';
import Main from '../components/Palette';
-class Palette extends Component {
+export class Palette extends Component {
render() {
return ;
}
@@ -19,11 +19,11 @@ Palette.propTypes = {
palette: PropTypes.object.isRequired
};
-function mapStateToProps(state) {
+export function mapStateToProps(state) {
return { palette: state.palette };
}
-function mapDispatchToProps(dispatch) {
+export function mapDispatchToProps(dispatch) {
const actions = { changeColor, changeWidth };
return { actions: bindActionCreators(actions, dispatch) };
}
diff --git a/test/actions/changeColorTest.js b/test/actions/changeColorTest.js
new file mode 100644
index 0000000..56afc7b
--- /dev/null
+++ b/test/actions/changeColorTest.js
@@ -0,0 +1,12 @@
+import action from 'actions/changeColor';
+import { CHANGE_COLOR } from 'actions/const';
+
+describe('changeColor', () => {
+ it('should create an action to change the color', () => {
+ const color = '#010101';
+ expect(action(color)).to.deep.equal({
+ type: CHANGE_COLOR,
+ color
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/actions/changeWidthTest.js b/test/actions/changeWidthTest.js
new file mode 100644
index 0000000..366f4bb
--- /dev/null
+++ b/test/actions/changeWidthTest.js
@@ -0,0 +1,12 @@
+import action from 'actions/changeWidth';
+import { CHANGE_WIDTH } from 'actions/const';
+
+describe('changeWidth', () => {
+ it('should create an action to change the width', () => {
+ const lineWidth = 40;
+ expect(action(lineWidth)).to.deep.equal({
+ type: CHANGE_WIDTH,
+ width: lineWidth
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/all/changeColorTest.js b/test/all/changeColorTest.js
new file mode 100644
index 0000000..b7079c7
--- /dev/null
+++ b/test/all/changeColorTest.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+import sinon from 'sinon';
+import { shallow, mount } from 'enzyme';
+import Pen from 'components/Pen';
+import App from 'containers/App';
+import configureStore from 'stores';
+import action from 'actions/changeColor';
+import { CHANGE_COLOR } from 'actions/const';
+import reducer from 'reducers/palette';
+
+describe('changeColor', () => {
+ describe('all', () => {
+ let app;
+ const color = '#795548';
+ beforeEach(() => {
+ app = mount(
+
+
+
+ );
+ });
+ it('the canvas should change color when a pen is clicked', () => {
+ const canvas = app.find('Canvas');
+ const pen = app.find('Pen').filterWhere(n => n.prop('color') === color);
+ expect(canvas.node.ctx.strokeStyle).to.not.equal(color);
+ pen.simulate('mousedown');
+ canvas.node.penDown({ clientX: 0, clientY: 0 });
+ expect(canvas.node.ctx.strokeStyle).to.equal(color);
+ });
+ it('the pen should become active when the pen is clicked', () => {
+ const pen = app.find('Pen').filterWhere(n => n.prop('color') === color);
+ expect(pen.node.state.styleName).to.not.have.include('pen-active');
+ pen.simulate('mousedown');
+ expect(pen.node.state.styleName).to.have.include('pen-active');
+ });
+ });
+ it('the action should be created when a pen is clicked', () => {
+ let color = '#010101';
+ let currentColor = '#020202';
+ let onChangeColor = sinon.spy();
+ let pen = shallow(
+
+ );
+ pen.simulate('mousedown');
+ expect(onChangeColor.withArgs(color).calledOnce).to.equal(true);
+ });
+ it('should create an action to change the color', () => {
+ const color = '#010101';
+ expect(action(color)).to.deep.equal({
+ type: CHANGE_COLOR,
+ color
+ });
+ });
+ it('should handle CHANGE_COLOR', () => {
+ const color = '#010101';
+ expect(reducer(void 0, {
+ type: CHANGE_COLOR,
+ color
+ })).to.have.property('color', color);
+ });
+});
\ No newline at end of file
diff --git a/test/components/AppTest.js b/test/components/AppTest.js
index 649c300..a444356 100644
--- a/test/components/AppTest.js
+++ b/test/components/AppTest.js
@@ -1,17 +1,32 @@
import React from 'react';
import { shallow } from 'enzyme';
import App from 'components/App';
+import Palette from 'containers/Palette';
+import Canvas from 'components/Canvas';
describe('', function () {
+ const colorTest = 'red';
+ const widthTest = 20;
+ const paletteTest = {
+ color: colorTest,
+ width: widthTest
+ };
beforeEach(function () {
- this.component = shallow();
+ this.component = shallow();
});
describe('when rendering the component', function () {
-
it('should have a className of "index"', function () {
expect(this.component.hasClass('index')).to.equal(true);
});
+ it('should have a palette', function () {
+ expect(this.component.contains()).to.equal(true);
+ });
+ it('should have a canvas', function () {
+ expect(this.component.contains(
+
+ )).to.equal(true);
+ });
});
});
diff --git a/test/components/CanvasTest.js b/test/components/CanvasTest.js
index f977d00..c88fbe4 100644
--- a/test/components/CanvasTest.js
+++ b/test/components/CanvasTest.js
@@ -1,18 +1,190 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { mount } from 'enzyme';
+import sinon from 'sinon';
import Canvas from 'components/Canvas.js';
describe('', function () {
+ let testColor;
+ let testWidth;
let component;
beforeEach(function () {
- component = shallow();
+ testColor = 'red';
+ testWidth = 20;
+ component = mount();
});
describe('when rendering the component', function () {
+ it('should have a canvas', function () {
+ expect(component.find('canvas')).to.have.length(1);
+ });
+ });
+
+ describe('#getPosition', function () {
+ const clientX = 10;
+ const clientY = 20;
+ it('should return when clientX and clientY', function () {
+ expect(component.instance().getPosition({
+ clientX, clientY
+ })).to.deep.equal({
+ x: clientX,
+ y: clientY
+ });
+ });
+ it('should return when touches', function () {
+ expect(component.instance().getPosition({
+ touches: [{
+ clientX, clientY
+ }]
+ })).to.deep.equal({
+ x: clientX,
+ y: clientY
+ });
+ });
+ });
+
+ describe('#pushPosition', function () {
+ it('should push position to positions', function () {
+ const pos = { x: 10, y: 20 };
+ component.instance().pushPosition(pos.x, pos.y);
+ expect(component.instance().positions).to.have.length(1);
+ expect(component.instance().positions[0]).to.deep.equal(pos);
+ });
+ });
+
+ describe('#penDown', function () {
+ it('should change strokeStyle', function () {
+ const color = '#008000';
+ component.setProps({ color });
+ component.instance().penDown({
+ clientX: 0,
+ clientY: 0
+ });
+ expect(component.instance().ctx.strokeStyle).to.equal(color);
+ });
+ it('should change lineWidth', function () {
+ const width = 200;
+ component.setProps({ width });
+ component.instance().penDown({
+ clientX: 0,
+ clientY: 0
+ });
+ expect(component.instance().ctx.lineWidth).to.equal(width);
+ });
+ it('should set isDownPen to true', function () {
+ component.instance().penDown({
+ clientX: 0,
+ clientY: 0
+ });
+ expect(component.instance().isDownPen).to.equal(true);
+ });
+ it('should update ctx settings', function () {
+ component.instance().penDown({
+ clientX: 0,
+ clientY: 0
+ });
+ expect(component.instance().ctx.lineCap).to.equal('round');
+ expect(component.instance().ctx.lineJoin).to.equal('round');
+ });
+ });
+
+ describe('#penMove', function () {
+ it('should draw a line', function () {
+ const lineToSpy = sinon.spy(component.instance().ctx, 'lineTo');
+ const x = 20;
+ const y = 30;
+ component.instance().isDownPen = true;
+ component.instance().penMove({
+ clientX: x,
+ clientY: y
+ });
+ expect(lineToSpy.withArgs(x, y).calledOnce).to.equal(true);
+ lineToSpy.restore();
+ });
+ });
+
+ describe('#penUp', function () {
+ it('should set isDownPen to false', function () {
+ component.instance().isDownPen = true;
+ component.instance().penUp();
+ expect(component.instance().isDownPen).to.equal(false);
+ });
+ it('should clear positions', function () {
+ component.instance().positions = [{ x: 100, y: 100 }];
+ component.instance().penUp();
+ expect(component.instance().positions).to.have.length(0);
+ });
+ });
+
+ // check overall flow
+
+ describe('mousedown', function () {
+ it('should change strokeStyle', function () {
+ const color = '#010101';
+ component.setProps({ color });
+ component.simulate('mousedown', {
+ clientX: 100,
+ clientY: 100
+ });
+ expect(component.instance().ctx.strokeStyle).to.equal(color);
+ });
+ });
+
+ describe('touchstart', function () {
+ it('should change strokeStyle', function () {
+ const color = '#010101';
+ component.setProps({ color });
+ component.simulate('touchstart', {
+ touches: [{
+ clientX: 100,
+ clientY: 100
+ }]
+ });
+ expect(component.instance().ctx.strokeStyle).to.equal(color);
+ });
+ });
+
+ describe('mousemove', function () {
+ it('should call lineTo', function () {
+ const x = 100;
+ const y = 200;
+ const lineToSpy = sinon.spy(component.instance().ctx, 'lineTo');
+ component.instance().isDownPen = true;
+ component.simulate('mousemove', {
+ clientX: x, clientY: y
+ });
+ expect(lineToSpy.withArgs(x, y).calledOnce).to.equal(true);
+ });
+ });
+
+ describe('touchmove', function () {
+ it('should call lineTo', function () {
+ const x = 100;
+ const y = 200;
+ const lineToSpy = sinon.spy(component.instance().ctx, 'lineTo');
+ component.instance().isDownPen = true;
+ component.simulate('touchmove', {
+ touches: [{
+ clientX: x, clientY: y
+ }]
+ });
+ expect(lineToSpy.withArgs(x, y).calledOnce).to.equal(true);
+ });
+ });
+
+ describe('mouseup', function () {
+ it('should set isDownPen to false', function () {
+ component.instance().isDownPen = true;
+ component.simulate('mouseup');
+ expect(component.instance().isDownPen).to.equal(false);
+ });
+ });
- it('should have a className of "canvas-component"', function () {
- expect(component.hasClass('canvas-component')).to.equal(true);
+ describe('touchend', function () {
+ it('should set isDownPen to false', function () {
+ component.instance().isDownPen = true;
+ component.simulate('touchend');
+ expect(component.instance().isDownPen).to.equal(false);
});
});
});
diff --git a/test/components/LineWidthTest.js b/test/components/LineWidthTest.js
index b21c890..a9c1917 100644
--- a/test/components/LineWidthTest.js
+++ b/test/components/LineWidthTest.js
@@ -1,18 +1,39 @@
import React from 'react';
import { shallow } from 'enzyme';
+import sinon from 'sinon';
import LineWidth from 'components/LineWidth.js';
describe('', function () {
let component;
+ let testWidth;
+ let testOnChangeWidth;
beforeEach(function () {
- component = shallow();
+ testWidth = 10;
+ testOnChangeWidth = () => {};
+ component = shallow();
});
describe('when rendering the component', function () {
+ it('should have an input', function () {
+ expect(component.find('input')).to.have.length(1);
+ });
+ it('should set default value by width', function () {
+ const width = 30;
+ component.setProps({ width });
+ expect(component.find('input').prop('defaultValue')).to.equal(width);
+ });
+ });
- it('should have a className of "linewidth-component"', function () {
- expect(component.hasClass('linewidth-component')).to.equal(true);
+ describe('mouseup', function () {
+ it('should call onChangeWidth with the value', function () {
+ const onChangeWidth = sinon.spy();
+ const value = '40';
+ component.setProps({ onChangeWidth });
+ component.find('input').simulate('mouseup', {
+ target: { value }
+ });
+ expect(onChangeWidth.withArgs(parseInt(value)).calledOnce).to.equal(true);
});
});
});
diff --git a/test/components/PaletteTest.js b/test/components/PaletteTest.js
index 42949d3..3210ebe 100644
--- a/test/components/PaletteTest.js
+++ b/test/components/PaletteTest.js
@@ -1,18 +1,29 @@
import React from 'react';
import { shallow } from 'enzyme';
import Palette from 'components/Palette.js';
+import colors from 'constants/colors';
describe('', function () {
let component;
+ const testActions = {
+ changeWidth() {},
+ changeColor() {}
+ };
+ const testPalette = {
+ color: 'red',
+ width: 30
+ };
beforeEach(function () {
- component = shallow();
+ component = shallow();
});
describe('when rendering the component', function () {
-
- it('should have a className of "palette-component"', function () {
- expect(component.hasClass('palette-component')).to.equal(true);
+ it('should make colors of pens', function () {
+ expect(component.find('Pen')).to.have.length(colors.length);
+ });
+ it('should have a ', function () {
+ expect(component.find('LineWidth')).to.have.length(1);
});
});
});
diff --git a/test/components/PenTest.js b/test/components/PenTest.js
index ba00240..b8300cf 100644
--- a/test/components/PenTest.js
+++ b/test/components/PenTest.js
@@ -1,18 +1,44 @@
import React from 'react';
import { shallow } from 'enzyme';
+import sinon from 'sinon';
import Pen from 'components/Pen.js';
describe('', function () {
let component;
+ let testColor;
+ let testCurrentColor;
+ let testOnChangeColor;
beforeEach(function () {
- component = shallow();
+ testColor = '#010101';
+ testCurrentColor = '#404040';
+ testOnChangeColor = () => {};
+ component = shallow(
+
+ );
});
describe('when rendering the component', function () {
+ it('should set backgroundColor', function () {
+ expect(component.prop('style')).to.have.property('backgroundColor', testColor);
+ });
+ it('should set pen-active when active', function () {
+ const currentColor = testColor;
+ expect(component.prop('className')).to.not.contain('pen-active');
+ component.setProps({ currentColor });
+ expect(component.prop('className')).to.contain('pen-active');
+ });
+ });
- it('should have a className of "pen-component"', function () {
- expect(component.hasClass('pen-component')).to.equal(true);
+ describe('mousedown', function () {
+ it('should call callback', function () {
+ const onChangeColor = sinon.spy();
+ component.setProps({ onChangeColor });
+ component.simulate('mousedown');
+ expect(onChangeColor.withArgs(testColor).calledOnce).to.equal(true);
});
});
});
diff --git a/test/containers/PaletteTest.js b/test/containers/PaletteTest.js
new file mode 100644
index 0000000..5aba9e3
--- /dev/null
+++ b/test/containers/PaletteTest.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import {
+ Palette as PaletteContainer,
+ mapStateToProps,
+ mapDispatchToProps
+} from 'containers/Palette';
+import Palette from 'components/Palette';
+
+describe('containers/Palette', () => {
+ describe('component', () => {
+ const actions = {
+ changeColor() {},
+ changeWidth() {}
+ };
+ const palette = {};
+ const component = shallow();
+ expect(component.contains()).to.equal(true);
+ });
+ describe('#mapStateToProps', () => {
+ it('should return only palette', () => {
+ const palette = { color: 'red' };
+ expect(mapStateToProps({
+ palette,
+ other: {}
+ })).to.deep.equal({ palette });
+ });
+ });
+ describe('#mapDispatchToProps', () => {
+ it('should return actions', () => {
+ expect(mapDispatchToProps({})).to.have.property('actions');
+ expect(Object.keys(mapDispatchToProps({}).actions)).to.have.lengthOf(2);
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/reducers/paletteTest.js b/test/reducers/paletteTest.js
index 0c8e817..f47580b 100644
--- a/test/reducers/paletteTest.js
+++ b/test/reducers/paletteTest.js
@@ -1,12 +1,26 @@
-var reducer = require('../../src/reducers/palette');
+import reducer from 'reducers/palette';
+import { CHANGE_COLOR, CHANGE_WIDTH } from 'actions/const';
+import config from 'config';
describe('palette', () => {
-
- it('should not change the passed state', (done) => {
-
- const state = Object.freeze({});
- reducer(state, {type: 'INVALID'});
-
- done();
+ it('should return the initial state', () => {
+ expect(reducer(void 0, { })).to.deep.equal({
+ color: config.defaultColor,
+ width: config.defaultWidth
+ });
+ });
+ it('should handle CHANGE_COLOR', () => {
+ const color = '#010101';
+ expect(reducer(void 0, {
+ type: CHANGE_COLOR,
+ color
+ })).to.have.property('color', color);
+ });
+ it('should handle CHANGE_WIDTH', () => {
+ const width = 40;
+ expect(reducer(void 0, {
+ type: CHANGE_WIDTH,
+ width
+ })).to.have.property('width', width);
});
});