diff --git a/App/Reducers/OutputReducer.js b/App/Reducers/OutputReducer.js index 9eefd23..d67b27f 100644 --- a/App/Reducers/OutputReducer.js +++ b/App/Reducers/OutputReducer.js @@ -7,12 +7,12 @@ export const INITIAL_STATE = immutable({ raw: '', contents: '', format: DIFF_FORMAT.html, - split: 'side-by-side', + split: 'line-by-line', }); const outputResult = (state, action) => state.merge({ - raw: action.diff, + raw: action.raw, contents: action.contents, }); diff --git a/App/Sagas/InputSaga.js b/App/Sagas/InputSaga.js index 6e078c4..9db8ed8 100644 --- a/App/Sagas/InputSaga.js +++ b/App/Sagas/InputSaga.js @@ -4,12 +4,12 @@ import { left, right } from '../Selectors/Input'; import { format, split } from '../Selectors/Output'; import Types from '../Actions/Types'; import Actions from '../Actions/Creators'; -import createDiff from '../Services/CalculateDiff'; +import { createDiff } from '../Services/CalculateDiff'; // 間引き時間 // const debouncing = ms => new Promise(resolve => setTimeout(resolve, ms)); -function* worker() { +export function* worker() { // Debouncing(間引き)処理 // yield call(debouncing, 1000); diff --git a/App/Sagas/StartupSaga.js b/App/Sagas/StartupSaga.js index cecf0e6..0495e1a 100644 --- a/App/Sagas/StartupSaga.js +++ b/App/Sagas/StartupSaga.js @@ -7,7 +7,6 @@ import THEMES from '../Config/Theme'; export default function* watcher() { yield take(Types.STARTUP); // マウント前に設定するものがあればここに記述 - console.log('startup'); /* eslint-disable global-require */ /* eslint-disable import/no-dynamic-require */ diff --git a/App/Services/CalculateDiff.js b/App/Services/CalculateDiff.js index bcb9e39..920704c 100644 --- a/App/Services/CalculateDiff.js +++ b/App/Services/CalculateDiff.js @@ -2,7 +2,8 @@ import { createPatch } from 'diff'; import diff2html from 'diff2html'; import DIFF_FORMAT from '../Config/DiffFormat'; -export default function createDiff(left, right, format, split) { +/* eslint-disable import/prefer-default-export*/ +export function createDiff(left, right, format, split) { const rawDiff = createPatch('result', left, right, 'before', 'after'); let contents = ''; if (format === DIFF_FORMAT.unified) { diff --git a/Tests/Reducers/InputReducerTest.js b/Tests/Reducers/InputReducerTest.js new file mode 100644 index 0000000..32d8a1f --- /dev/null +++ b/Tests/Reducers/InputReducerTest.js @@ -0,0 +1,39 @@ +import test from 'ava'; +import reducer, { INITIAL_STATE } from '../../App/Reducers/InputReducer'; +import Actions from '../../App/Actions/Creators'; + +test('changeLeft', (t) => { + const input = 'New Text'; + const state = reducer(INITIAL_STATE, Actions.inputLeftChange(input)); + t.is(state.left, input); + t.is(state.right, INITIAL_STATE.right); + t.is(state.language, INITIAL_STATE.language); + t.is(state.theme, INITIAL_STATE.theme); +}); + +test('changeRight', (t) => { + const input = 'New Text'; + const state = reducer(INITIAL_STATE, Actions.inputRightChange(input)); + t.is(state.left, INITIAL_STATE.left); + t.is(state.right, input); + t.is(state.language, INITIAL_STATE.language); + t.is(state.theme, INITIAL_STATE.theme); +}); + +test('changeLanguage', (t) => { + const language = 'java'; + const state = reducer(INITIAL_STATE, Actions.inputLanguageChange(language)); + t.is(state.left, INITIAL_STATE.left); + t.is(state.right, INITIAL_STATE.right); + t.is(state.language, language); + t.is(state.theme, INITIAL_STATE.theme); +}); + +test('changeTheme', (t) => { + const theme = 'monokai'; + const state = reducer(INITIAL_STATE, Actions.inputThemeChange(theme)); + t.is(state.left, INITIAL_STATE.left); + t.is(state.right, INITIAL_STATE.right); + t.is(state.language, INITIAL_STATE.language); + t.is(state.theme, theme); +}); diff --git a/Tests/Reducers/OutputReducerTest.js b/Tests/Reducers/OutputReducerTest.js new file mode 100644 index 0000000..37a6e56 --- /dev/null +++ b/Tests/Reducers/OutputReducerTest.js @@ -0,0 +1,31 @@ +import test from 'ava'; +import reducer, { INITIAL_STATE } from '../../App/Reducers/OutputReducer'; +import Actions from '../../App/Actions/Creators'; + +test('outputResult', (t) => { + const raw = 'raw output'; + const contents = 'contents'; + const state = reducer(INITIAL_STATE, Actions.outputDiffResult(raw, contents)); + t.is(state.raw, raw); + t.is(state.contents, contents); + t.is(state.format, INITIAL_STATE.format); + t.is(state.split, INITIAL_STATE.split); +}); + +test('changeFormat', (t) => { + const format = 0; + const state = reducer(INITIAL_STATE, Actions.outputFormatChange(format)); + t.is(state.raw, INITIAL_STATE.raw); + t.is(state.contents, INITIAL_STATE.contents); + t.is(state.format, format); + t.is(state.split, INITIAL_STATE.split); +}); + +test('changeSplit', (t) => { + const split = 'side-by-side'; + const state = reducer(INITIAL_STATE, Actions.outputSplitChange(split)); + t.is(state.raw, INITIAL_STATE.raw); + t.is(state.contents, INITIAL_STATE.contents); + t.is(state.format, INITIAL_STATE.format); + t.is(state.split, split); +}); diff --git a/Tests/Sagas/InputSagaTest.js b/Tests/Sagas/InputSagaTest.js new file mode 100644 index 0000000..bcc7895 --- /dev/null +++ b/Tests/Sagas/InputSagaTest.js @@ -0,0 +1,46 @@ +import test from 'ava'; +import { take, select, fork } from 'redux-saga/effects'; +import { left, right } from '../../App/Selectors/Input'; +import { format, split } from '../../App/Selectors/Output'; +import Types from '../../App/Actions/Types'; +import { leftWatcher, rightWatcher, formatWatcher, splitWatcher, worker } from '../../App/Sagas/InputSaga'; + +// 呼び出すたびにGeneratorのstepを1つ進める +const stepper = fn => mock => fn.next(mock).value; + +test('leftWatcher step test', (t) => { + const step = stepper(leftWatcher()); + // takeEveryのステップとworker起動のテスト + t.deepEqual(step(), take(Types.INPUT_LEFT_CHANGE)); + t.deepEqual(step(), fork(worker, undefined)); + t.deepEqual(step(), take(Types.INPUT_LEFT_CHANGE)); +}); + +test('rightWatcher step test', (t) => { + const step = stepper(rightWatcher()); + t.deepEqual(step(), take(Types.INPUT_RIGHT_CHANGE)); + t.deepEqual(step(), fork(worker, undefined)); + t.deepEqual(step(), take(Types.INPUT_RIGHT_CHANGE)); +}); + +test('formatWatcher step test', (t) => { + const step = stepper(formatWatcher()); + t.deepEqual(step(), take(Types.OUTPUT_FORMAT_CHANGE)); + t.deepEqual(step(), fork(worker, undefined)); + t.deepEqual(step(), take(Types.OUTPUT_FORMAT_CHANGE)); +}); + +test('splitWatcher step test', (t) => { + const step = stepper(splitWatcher()); + t.deepEqual(step(), take(Types.OUTPUT_SPLIT_CHANGE)); + t.deepEqual(step(), fork(worker, undefined)); + t.deepEqual(step(), take(Types.OUTPUT_SPLIT_CHANGE)); +}); + +test('worker step test', (t) => { + const step = stepper(worker()); + t.deepEqual(step(), select(left)); + t.deepEqual(step(), select(right)); + t.deepEqual(step(), select(format)); + t.deepEqual(step(), select(split)); +}); diff --git a/Tests/Services/CalculateDiffTest.js b/Tests/Services/CalculateDiffTest.js new file mode 100644 index 0000000..552ed91 --- /dev/null +++ b/Tests/Services/CalculateDiffTest.js @@ -0,0 +1,37 @@ +import test from 'ava'; +import proxyquire from 'proxyquire'; +import DIFF_FORMAT from '../../App/Config/DiffFormat'; + +test('createDiff() フォーマットがunifiedならコンテンツはunified生データが返却される', (t) => { + const diffStub = { + createPatch() { + return 'rawDiff'; + }, + }; + const createDiffMock = proxyquire('../../App/Services/CalculateDiff', { + diff: diffStub, + }); + const expected = ['rawDiff', 'rawDiff']; + t.deepEqual(createDiffMock.createDiff('left', 'right', DIFF_FORMAT.unified, 'line-by-line'), expected); +}); + +test('createDiff() フォーマットがhtmlならコンテンツはhtml変換後のデータが返却される', (t) => { + const diffStub = { + createPatch() { + return 'rawDiff'; + }, + }; + const diff2htmlStub = { + Diff2Html: { + getPrettyHtml() { + return 'contents'; + }, + }, + }; + const createDiffMock = proxyquire('../../App/Services/CalculateDiff', { + diff: diffStub, + diff2html: diff2htmlStub, + }); + const expected = ['rawDiff', 'contents']; + t.deepEqual(createDiffMock.createDiff('left', 'right', DIFF_FORMAT.html, 'line-by-line'), expected); +}); diff --git a/package.json b/package.json index 1353901..9f45a55 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "concurrently --kill-others \"npm run hot-server\" \"npm run start-hot\"", "hot-server": "cross-env NODE_ENV=development node -r babel-register server.js", "start-hot": "cross-env HOT=1 NODE_ENV=development electron -r babel-register -r babel-polyfill ./main.development", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "ava", + "test:watch": "ava --watch" }, "repository": { "type": "git", @@ -24,6 +25,7 @@ }, "homepage": "https://github.com/romiogaku/difftron#readme", "devDependencies": { + "ava": "^0.17.0", "babel-core": "^6.17.0", "babel-eslint": "^7.0.0", "babel-loader": "^6.2.5", @@ -55,6 +57,7 @@ "eslint-plugin-react": "^6.4.1", "express": "^4.14.0", "extract-text-webpack-plugin": "^1.0.1", + "proxyquire": "^1.7.10", "style-loader": "^0.13.1", "webpack": "^1.13.2", "webpack-dev-middleware": "^1.8.4", @@ -77,5 +80,16 @@ "redux-saga": "^0.12.1", "reduxsauce": "^0.2.0", "seamless-immutable": "^6.3.0" + }, + "ava": { + "files": [ + "Tests/**/*.js", + "!Tests/Setup.js" + ], + "require": [ + "babel-register", + "babel-polyfill" + ], + "babel": "inherit" } }