diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..facd1809 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "react"] +} \ No newline at end of file diff --git a/package.json b/package.json index 3190fdc5..2c5130de 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"", "lint": "eslint . --ext .js,.jsx", "start": "webpack-dev-server", - "tap": "./node_modules/.bin/tap ./test/*.js", - "test": "npm run lint && npm run build && npm run tap", + "test": "npm run lint && npm run build && npm run unit-test", + "unit": "jest", "watch": "webpack --progress --colors --watch" }, "author": "Massachusetts Institute of Technology", @@ -28,17 +28,22 @@ "autoprefixer": "7.1.1", "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", + "babel-jest": "^20.0.3", "babel-loader": "^7.0.0", "babel-plugin-transform-object-rest-spread": "^6.22.0", "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.22.0", "classnames": "2.2.5", "css-loader": "0.28.3", + "enzyme": "^2.8.2", "eslint": "^3.16.1", + "eslint-config-import": "^0.13.0", "eslint-config-scratch": "^3.0.0", + "eslint-plugin-import": "^2.7.0", "eslint-plugin-react": "^7.0.1", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", "html-webpack-plugin": "2.28.0", + "jest": "^20.0.4", "lodash.defaultsdeep": "4.6.0", "minilog": "3.1.0", "mkdirp": "^0.5.1", @@ -51,12 +56,21 @@ "react-dom": "15.5.4", "react-intl": "2.3.0", "react-redux": "5.0.5", + "react-test-renderer": "^15.5.4", "redux": "3.6.0", + "redux-mock-store": "^1.2.3", "redux-throttle": "0.1.1", + "regenerator-runtime": "^0.10.5", "rimraf": "^2.6.1", "style-loader": "^0.18.0", "tap": "^10.2.0", "webpack": "^2.4.1", "webpack-dev-server": "^2.4.1" + }, + "jest": { + "moduleNameMapper": { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/__mocks__/fileMock.js", + "\\.(css|less)$": "/test/__mocks__/styleMock.js" + } } } diff --git a/src/.eslintrc.js b/src/.eslintrc.js index 7ff859ca..d5985f2d 100644 --- a/src/.eslintrc.js +++ b/src/.eslintrc.js @@ -1,7 +1,13 @@ module.exports = { root: true, - extends: ['scratch', 'scratch/es6', 'scratch/react'], + extends: ['scratch', 'scratch/es6', 'scratch/react', 'import'], env: { browser: true + }, + rules: { + 'import/no-mutable-exports': 'error', + 'import/no-commonjs': 'error', + 'import/no-amd': 'error', + 'import/no-nodejs-modules': 'error' } }; diff --git a/src/components/paint-editor.jsx b/src/components/paint-editor.jsx index db93953f..a22a01e6 100644 --- a/src/components/paint-editor.jsx +++ b/src/components/paint-editor.jsx @@ -14,5 +14,4 @@ PaintEditorComponent.propTypes = { }) }; - -module.exports = PaintEditorComponent; +export default PaintEditorComponent; diff --git a/src/containers/paint-editor.jsx b/src/containers/paint-editor.jsx index efe05522..4e219058 100644 --- a/src/containers/paint-editor.jsx +++ b/src/containers/paint-editor.jsx @@ -42,7 +42,7 @@ const mapDispatchToProps = dispatch => ({ } }); -module.exports = connect( +export default connect( mapStateToProps, mapDispatchToProps )(PaintEditor); diff --git a/src/containers/paper-canvas.jsx b/src/containers/paper-canvas.jsx index 5d813a0f..5eb3a9aa 100644 --- a/src/containers/paper-canvas.jsx +++ b/src/containers/paper-canvas.jsx @@ -44,4 +44,4 @@ PaperCanvas.propTypes = { }) }; -module.exports = PaperCanvas; +export default PaperCanvas; diff --git a/src/log/log.js b/src/log/log.js index 9b991a58..5ea3bb07 100644 --- a/src/log/log.js +++ b/src/log/log.js @@ -1,4 +1,4 @@ -const minilog = require('minilog'); +import minilog from 'minilog'; minilog.enable(); -module.exports = minilog('paint-editor'); +export default minilog('paint-editor'); diff --git a/src/reducers/combine-reducers.js b/src/reducers/combine-reducers.js index a533b763..985c9b5b 100644 --- a/src/reducers/combine-reducers.js +++ b/src/reducers/combine-reducers.js @@ -1,5 +1,6 @@ import {combineReducers} from 'redux'; +import toolReducer from './tools'; -module.exports = combineReducers({ - tool: require('./tools') +export default combineReducers({ + tool: toolReducer }); diff --git a/src/reducers/tools.js b/src/reducers/tools.js index 59cf1afb..28832833 100644 --- a/src/reducers/tools.js +++ b/src/reducers/tools.js @@ -1,5 +1,5 @@ -const ToolTypes = require('../tools/tool-types'); -const log = require('../log/log'); +import ToolTypes from '../tools/tool-types'; +import log from '../log/log'; const CHANGE_TOOL = 'scratch-paint/tools/CHANGE_TOOL'; const initialState = ToolTypes.BRUSH; @@ -29,4 +29,4 @@ reducer.changeTool = function (tool) { }; }; -module.exports = reducer; +export default reducer; diff --git a/src/tools/tool-types.js b/src/tools/tool-types.js index 12c8b0f4..61524cb7 100644 --- a/src/tools/tool-types.js +++ b/src/tools/tool-types.js @@ -9,4 +9,4 @@ class ToolTypes { ToolTypes.BRUSH = new ToolTypes('BRUSH'); ToolTypes.ERASER = new ToolTypes('ERASER'); -module.exports = ToolTypes; +export default ToolTypes; diff --git a/test/__mocks__/fileMock.js b/test/__mocks__/fileMock.js new file mode 100644 index 00000000..59890f6a --- /dev/null +++ b/test/__mocks__/fileMock.js @@ -0,0 +1,3 @@ +// __mocks__/fileMock.js + +module.exports = 'test-file-stub'; diff --git a/test/__mocks__/styleMock.js b/test/__mocks__/styleMock.js new file mode 100644 index 00000000..d988e23b --- /dev/null +++ b/test/__mocks__/styleMock.js @@ -0,0 +1,3 @@ +// __mocks__/styleMock.js + +module.exports = {}; diff --git a/test/tools-reducer-test.js b/test/tools-reducer-test.js deleted file mode 100644 index 3da061d1..00000000 --- a/test/tools-reducer-test.js +++ /dev/null @@ -1,25 +0,0 @@ -const test = require('tap').test; -const ToolTypes = require('../src/tools/tool-types'); -const reducer = require('../src/reducers/tools'); - -test('initialState', t => { - let defaultState; - t.assert(reducer(defaultState /* state */, {type: 'anything'} /* action */) instanceof ToolTypes); - t.end(); -}); - -test('changeTool', t => { - let defaultState; - t.assert(reducer(defaultState /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */), ToolTypes.ERASER); - t.assert( - reducer(ToolTypes.ERASER /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */), ToolTypes.ERASER); - t.assert(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */), ToolTypes.ERASER); - t.end(); -}); - -test('invalidChangeTool', t => { - t.assert( - reducer(ToolTypes.BRUSH /* state */, reducer.changeTool('non-existant tool') /* action */), ToolTypes.BRUSH); - t.assert(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool() /* action */), ToolTypes.BRUSH); - t.end(); -}); diff --git a/test/unit/tools-reducer.test.js b/test/unit/tools-reducer.test.js new file mode 100644 index 00000000..f0c729f2 --- /dev/null +++ b/test/unit/tools-reducer.test.js @@ -0,0 +1,23 @@ +/* eslint-env jest */ +import ToolTypes from '../../src/tools/tool-types'; +import reducer from '../../src/reducers/tools'; + +test('initialState', () => { + let defaultState; + expect(reducer(defaultState /* state */, {type: 'anything'} /* action */) instanceof ToolTypes).toBeTruthy(); +}); + +test('changeTool', () => { + let defaultState; + expect(reducer(defaultState /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */)).toBe(ToolTypes.ERASER); + expect(reducer(ToolTypes.ERASER /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */)) + .toBe(ToolTypes.ERASER); + expect(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */)) + .toBe(ToolTypes.ERASER); +}); + +test('invalidChangeTool', () => { + expect(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool('non-existant tool') /* action */)) + .toBe(ToolTypes.BRUSH); + expect(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool() /* action */)).toBe(ToolTypes.BRUSH); +});