diff --git a/.babelrc b/.babelrc index 1c4057a..3f0ca7d 100644 --- a/.babelrc +++ b/.babelrc @@ -3,7 +3,10 @@ "plugins": ["transform-object-rest-spread", "add-module-exports"], "env": { "test": { - "plugins": [["__coverage__", { "only": "src/GoGame" }]] + "plugins": [ + ["__coverage__", { "only": "src/GoGame" }], + "rewire", + ] } } } diff --git a/.travis.yml b/.travis.yml index 169f5df..f84ae85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,19 @@ sudo: false language: node_js -cache: - directories: - - node_modules notifications: email: false node_js: - '6' + - '5' + - '4' before_script: - npm prune script: - - npm run test + - npm run test:with-coverage + - npm run test:check-coverage after_success: - # No need to build as it is done in the prepublish script - npm run test:publish-coverage + # No need to build as it is done in the prepublish script - npm run semantic-release branches: only: diff --git a/package.json b/package.json index 0d9bb66..8a22bb5 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,8 @@ "test": "test" }, "scripts": { - "test": "npm run clean:coverage && npm run test:run && npm run test:check-coverage", - "test:run": "cross-env BABEL_ENV=test nyc --reporter=lcov --reporter=clover mocha --compilers js:babel-core/register --recursive", - "test:debug": "mocha --recursive --compilers js:babel-core/register --debug-brk", + "test": "cross-env BABEL_ENV=test mocha --compilers js:babel-core/register --recursive", + "test:with-coverage": "npm run clean:coverage && cross-env BABEL_ENV=test nyc --reporter=lcov mocha --compilers js:babel-core/register --recursive", "test:check-coverage": "nyc check-coverage --statements 100 --branches 100 --functions 100 --lines 100", "test:publish-coverage": "cat ./coverage/lcov.info | codecov", "prebuild": "npm run clean", @@ -48,11 +47,11 @@ "babel-loader": "^6.2.4", "babel-plugin-__coverage__": "^1.11.111", "babel-plugin-add-module-exports": "^0.2.1", + "babel-plugin-rewire": "^1.0.0-rc-3", "babel-plugin-transform-object-rest-spread": "^6.8.0", "babel-preset-es2015": "^6.9.0", "babel-preset-react": "^6.5.0", "chai": "^3.5.0", - "chai-enzyme": "^0.4.2", "codecov.io": "^0.1.6", "commitizen": "^2.8.2", "cross-env": "^1.0.8", @@ -63,11 +62,15 @@ "eslint-plugin-import": "^1.8.0", "eslint-plugin-jsx-a11y": "^1.2.2", "eslint-plugin-react": "^5.1.1", + "ghooks": "^1.2.4", "mocha": "^2.5.1", "npm-run-all": "^2.1.2", "nyc": "^6.4.4", "rimraf": "^2.5.2", "semantic-release": "^4.3.5", + "sinon": "^1.17.4", + "sinon-chai": "^2.8.0", + "validate-commit-msg": "^2.6.1", "webpack": "^1.13.1", "webpack-dev-server": "^1.14.1" }, @@ -82,6 +85,10 @@ "rx": "^4.1.0" }, "config": { + "ghooks": { + "pre-commit": "npm run test:with-coverage -- -R dot && npm run test:check-coverage", + "commit-msg": "validate-commit-msg" + }, "commitizen": { "path": "./node_modules/cz-conventional-changelog" } diff --git a/src/GoGame/GoGame.js b/src/GoGame/GoGame.js index f9c9f9e..b1330b8 100644 --- a/src/GoGame/GoGame.js +++ b/src/GoGame/GoGame.js @@ -1,3 +1,5 @@ +import _forOwn from 'lodash/forOwn'; + import goGameReducer from './reducer'; import actions from './actions'; @@ -19,4 +21,12 @@ GoGame.prototype = { }, }; +_forOwn(actions, (actionCreator, actionName) => { + if (actionName !== 'init') { + GoGame.prototype[actionName] = function goGameShortcutMethod(...args) { + return new GoGame(goGameReducer(this, actionCreator(...args))); + }; + } +}); + export default GoGame; diff --git a/src/GoGame/reducer/index.js b/src/GoGame/reducer/index.js index 603880c..5de0303 100644 --- a/src/GoGame/reducer/index.js +++ b/src/GoGame/reducer/index.js @@ -5,7 +5,7 @@ import playMoveReducer from './playMoveReducer'; import setMarkReducer from './setMarkReducer'; const initialGame = { - board: _map(Array(19), () => _map(Array(19), {})), + board: _map(Array(19), () => _map(Array(19), () => ({}))), moves: [], koCoordinates: null, actions: [], diff --git a/test/game/GoGame.spec.js b/test/game/GoGame.spec.js index 0fe9c09..fc3a840 100644 --- a/test/game/GoGame.spec.js +++ b/test/game/GoGame.spec.js @@ -1,16 +1,18 @@ /* eslint-env mocha */ import _ from 'lodash'; -import { expect } from 'chai'; +import chai, { expect } from 'chai'; +import sinon from 'sinon'; +import sinonChai from 'sinon-chai'; +chai.use(sinonChai); -import { GoGame } from '../../src'; +import { GoGame, actions, goGameReducer } from '../../src'; describe('GoGame', () => { describe('default / empty GoGame object', () => { - it('Should contain a 19x19 array of empty objects', () => { + it('Should return the result of the reducer with init action', () => { const game = new GoGame(); - const expected = _.map(Array(19), () => _.map(Array(19), {})); - expect(game.board).to.deep.equal(expected); + expect(game).to.deep.equal(new GoGame(goGameReducer(undefined, actions.init()))); }); it('Should have the ko property set to false', () => { @@ -49,4 +51,45 @@ describe('GoGame', () => { expect(game.turn).to.equal('BLACK'); }); }); + + /* eslint-disable no-underscore-dangle */ + describe('Shortcuts methods', () => { + const resultGame = { board: [], moves: [], actions: [] }; + const reducerStub = sinon.stub().returns(resultGame); + before(() => { + GoGame.__Rewire__('goGameReducer', reducerStub); + }); + after(() => { + GoGame.__ResetDependency__('goGameReducer'); + }); + + it('Should not include the init() action', () => { + const game = new GoGame(); + expect(game.init).to.not.exist; + }); + + const methods = { + playMove: [3, 3], + pass: [], + setMark: [{ i: 3, j: 3 }, 'test'], + }; + + _.forOwn(methods, (actionArgs, action) => { + describe(`${action} method`, () => { + it(`Should call the reducer with itself and actions.${action}(), passing its args`, () => { + const game = new GoGame(); + game[action](...actionArgs); + + expect(reducerStub).to.have.been.calledWith(game, actions[action](...actionArgs)); + }); + + it('Should return the result of the reducer as a GoGame object', () => { + const game = new GoGame(); + const actual = game[action](...actionArgs); + + expect(actual).to.deep.equal(new GoGame(resultGame)); + }); + }); + }); + }); }); diff --git a/test/game/gameReducer.spec.js b/test/game/gameReducer.spec.js index 867d790..249d71f 100644 --- a/test/game/gameReducer.spec.js +++ b/test/game/gameReducer.spec.js @@ -13,6 +13,18 @@ describe('Game Reducer', () => { expect(result).to.equal(state); }); + + it('Should return an object with a correct board', () => { + const newGame = goGameReducer(undefined, actions.init()); + + expect(newGame.board).to.exist; + newGame.board.forEach((row) => { + expect(row).to.exist.and.have.length(19); + row.forEach((intersection) => { + expect(intersection).to.deep.equal({}); + }); + }); + }); }); describe('With the playMove(i,j) action', () => {