From a6c60cc83c3ee91b930f8433b73bb2b47c87270d Mon Sep 17 00:00:00 2001 From: xufei Date: Mon, 3 Apr 2017 21:13:33 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E5=87=A0?= =?UTF-8?q?=E4=B8=AA=E5=A4=8D=E6=9D=82=E5=9C=BA=E6=99=AF=E7=9A=84demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/components/Logger/Logger.js | 25 +++++++++++++ src/components/MainLayout/Header.js | 11 +++++- src/components/Reusable/Reusable.js | 25 +++++++++++++ src/index.js | 5 +++ src/models/a.js | 18 ++++++++++ src/models/b.js | 31 ++++++++++++++++ src/models/logger.js | 21 +++++++++++ src/models/reusable.js | 34 ++++++++++++++++++ src/models/task.js | 56 +++++++++++++++++++++++++++++ src/router.js | 30 ++++++++++++++++ src/routes/A.js | 33 +++++++++++++++++ src/routes/B.js | 23 ++++++++++++ src/routes/Tasks.js | 22 ++++++++++++ src/services/delay.js | 5 +++ 15 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 src/components/Logger/Logger.js create mode 100644 src/components/Reusable/Reusable.js create mode 100644 src/models/a.js create mode 100644 src/models/b.js create mode 100644 src/models/logger.js create mode 100644 src/models/reusable.js create mode 100644 src/models/task.js create mode 100644 src/routes/A.js create mode 100644 src/routes/B.js create mode 100644 src/routes/Tasks.js create mode 100644 src/services/delay.js diff --git a/package.json b/package.json index 91b4c3c..0139322 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "precommit": "npm run lint" }, "dependencies": { - "antd": "^2.6.4", + "antd": "^2.8.3", "babel-runtime": "^6.22.0", "dva": "^1.2.0", "dva-loading": "^0.2.0", diff --git a/src/components/Logger/Logger.js b/src/components/Logger/Logger.js new file mode 100644 index 0000000..544e603 --- /dev/null +++ b/src/components/Logger/Logger.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { connect } from 'dva'; + +function Logger({ logs }) { + return ( +
+
+

Logger

+ +
+ ); +} + +Logger.propTypes = { +}; + +function mapStateToProps(state) { + const { logs } = state.logger; + return { + logs, + }; +} +export default connect(mapStateToProps)(Logger); diff --git a/src/components/MainLayout/Header.js b/src/components/MainLayout/Header.js index e247711..be1aa6f 100644 --- a/src/components/MainLayout/Header.js +++ b/src/components/MainLayout/Header.js @@ -15,10 +15,19 @@ function Header({ location }) { Home + + Tasks + + + A + + + B + 404 - + dva diff --git a/src/components/Reusable/Reusable.js b/src/components/Reusable/Reusable.js new file mode 100644 index 0000000..156850d --- /dev/null +++ b/src/components/Reusable/Reusable.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { connect } from 'dva'; + +function Reusable({ messages }) { + return ( +
+
+

Messages

+
    + { messages.map(message => (
  • {message.text}
  • )) } +
+
+ ); +} + +Reusable.propTypes = { +}; + +function mapStateToProps(state) { + const { messages } = state.reusable; + return { + messages, + }; +} +export default connect(mapStateToProps)(Reusable); diff --git a/src/index.js b/src/index.js index ec545b9..653003e 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,9 @@ import { message } from 'antd'; import './index.html'; import './index.css'; +import logger from './models/logger'; +import reusable from './models/reusable'; + const ERROR_MSG_DURATION = 3; // 3 秒 // 1. Initialize @@ -20,6 +23,8 @@ app.use(createLoading()); // 3. Model // Moved to router.js +app.model(logger); +app.model(reusable); // 4. Router app.router(require('./router')); diff --git a/src/models/a.js b/src/models/a.js new file mode 100644 index 0000000..2cb7ae2 --- /dev/null +++ b/src/models/a.js @@ -0,0 +1,18 @@ +import { delay } from '../services/delay'; + +export default { + namespace: 'a', + state: '', + reducers: { + fetchSuccess(state, { payload }) { + return state + payload; + }, + }, + effects: { + *foo(action, { call, put }) { + yield call(delay, 1000); + yield put({ type: 'reusable/addMessage', payload: { data: '666' } }); + }, + }, +}; + diff --git a/src/models/b.js b/src/models/b.js new file mode 100644 index 0000000..b02bea0 --- /dev/null +++ b/src/models/b.js @@ -0,0 +1,31 @@ +import { delay } from '../services/delay'; + +export default { + namespace: 'b', + state: '', + reducers: { + fooSuccess(state, { payload }) { + return state + payload; + }, + }, + effects: { + addWatcher: [function *watcher({ take, put }) { + /* eslint-disable */ + while (true) { + /* eslint-enable */ + // watch foo action from model a + const data1 = yield take('a/foo'); + yield put({ type: 'logger/addMessage', payload: `observe an action from model A: ${data1}` }); + + // watch addLogSuccess action from model reuseable + const data2 = yield take('reusable/addMessageSuccess'); + yield put({ type: 'reusable/addMessage', payload: `observe an action from model reusable: ${data2}` }); + } + }, { type: 'watcher' }], + + *foo(action, { call, put }) { + yield call(delay, 3000); + yield put({ type: 'reusable/addMessage', payload: { data: '233' } }); + }, + }, +}; diff --git a/src/models/logger.js b/src/models/logger.js new file mode 100644 index 0000000..b0c9b28 --- /dev/null +++ b/src/models/logger.js @@ -0,0 +1,21 @@ +export default { + namespace: 'logger', + state: { + logs: [], + }, + reducers: { + addLog(state, { payload }) { + const log = { + id: state.logs.length, + text: payload, + }; + + const logs = state.logs.concat([log]); + return { + logs, + }; + }, + }, + effects: { + }, +}; diff --git a/src/models/reusable.js b/src/models/reusable.js new file mode 100644 index 0000000..d40b9f0 --- /dev/null +++ b/src/models/reusable.js @@ -0,0 +1,34 @@ +import { delay } from '../services/delay'; + +export default { + namespace: 'reusable', + state: { + messages: [], + }, + reducers: { + addMessageSuccess(state, { payload }) { + const message = { + id: state.messages.length, + text: payload.data, + }; + + const messages = state.messages.concat([message]); + return { + messages, + }; + }, + }, + effects: { + *addMessage(action, { call, put }) { + const { payload } = action; + const { resolve } = payload; + yield call(delay, 1000); + + if (resolve) { + resolve(payload.data); + } + + yield put({ type: 'addMessageSuccess', payload }); + }, + }, +}; diff --git a/src/models/task.js b/src/models/task.js new file mode 100644 index 0000000..caa6e0f --- /dev/null +++ b/src/models/task.js @@ -0,0 +1,56 @@ +import { delay } from '../services/delay'; + +const makeService = (time) => { + return (data) => { + // console.log(`${data} start`); + + return new Promise((resolve) => { + setTimeout(() => { + resolve(data); + // console.log(`${data} end`); + }, time); + }); + }; +}; + +export default { + namespace: 'task', + state: { + }, + reducers: { + }, + effects: { + *sequential(action, { call, put }) { + yield call(delay, 3000); + yield put({ type: 'logger/addLog', payload: 'step1' }); + yield call(delay, 5000); + yield put({ type: 'logger/addLog', payload: 'step2' }); + }, + *parallel(action, { call, put }) { + yield put({ type: 'logger/addLog', payload: 'all started' }); + + // 注意这里不要写yield*,不然会顺序执行的 + yield [ + call(makeService(3000), 'service 1'), + call(makeService(5000), 'service 2'), + call(makeService(7000), 'service 3'), + ]; + + yield put({ type: 'logger/addLog', payload: 'all completed' }); + }, + *race(action, { call, put, race }) { + yield put({ type: 'logger/addLog', payload: 'start race' }); + + const { data, timeout } = yield race({ + data: call(makeService(2000), 'some data'), + timeout: call(delay, 1000), + }); + + if (data) { + yield put({ type: 'logger/addLog', payload: data }); + } else { + yield put({ type: 'logger/addLog', payload: `timeout: ${timeout}` }); + } + }, + }, +}; diff --git a/src/router.js b/src/router.js index d86e921..d596f2d 100644 --- a/src/router.js +++ b/src/router.js @@ -30,6 +30,36 @@ function RouterConfig({ history, app }) { }); }, }, + { + path: '/tasks', + name: 'TasksPage', + getComponent(nextState, cb) { + require.ensure([], (require) => { + cb(null, require('./routes/Tasks')); + registerModel(app, require('./models/task')); + }); + }, + }, + { + path: '/a', + name: 'APage', + getComponent(nextState, cb) { + require.ensure([], (require) => { + cb(null, require('./routes/A')); + registerModel(app, require('./models/a')); + }); + }, + }, + { + path: '/b', + name: 'BPage', + getComponent(nextState, cb) { + require.ensure([], (require) => { + cb(null, require('./routes/B')); + registerModel(app, require('./models/b')); + }); + }, + }, ]; return ; diff --git a/src/routes/A.js b/src/routes/A.js new file mode 100644 index 0000000..8ac2d33 --- /dev/null +++ b/src/routes/A.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { connect } from 'dva'; +import MainLayout from '../components/MainLayout/MainLayout'; +import Reusable from '../components/Reusable/Reusable'; +import Logger from '../components/Logger/Logger'; + +function A({ dispatch, location }) { + const bar = () => { + new Promise((resolve, reject) => { + dispatch({ type: 'reusable/addLog', payload: { data: 9527, resolve, reject } }); + }) + .then(() => { + // console.log(`after a long time, ${data} returns`); + }); + }; + + return ( + +

Component A

+
+ + + + +
+
+ ); +} + +A.propTypes = { +}; + +export default connect()(A); diff --git a/src/routes/B.js b/src/routes/B.js new file mode 100644 index 0000000..c267c85 --- /dev/null +++ b/src/routes/B.js @@ -0,0 +1,23 @@ +import React from 'react'; +import { connect } from 'dva'; +import MainLayout from '../components/MainLayout/MainLayout'; +import Reusable from '../components/Reusable/Reusable'; +import Logger from '../components/Logger/Logger'; + +function B({ dispatch, location }) { + return ( + +

Component B

+
+ + + +
+
+ ); +} + +B.propTypes = { +}; + +export default connect()(B); diff --git a/src/routes/Tasks.js b/src/routes/Tasks.js new file mode 100644 index 0000000..d4a0793 --- /dev/null +++ b/src/routes/Tasks.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { connect } from 'dva'; +import MainLayout from '../components/MainLayout/MainLayout'; +import Logger from '../components/Logger/Logger'; + +function Tasks({ dispatch, location }) { + return ( + +
+ + + +
+ +
+ ); +} + +Tasks.propTypes = { +}; + +export default connect()(Tasks); diff --git a/src/services/delay.js b/src/services/delay.js new file mode 100644 index 0000000..09b2154 --- /dev/null +++ b/src/services/delay.js @@ -0,0 +1,5 @@ +export const delay = (timeout) => { + return new Promise((resolve) => { + setTimeout(resolve, timeout); + }); +}; From df7b5fed8fe9a778b9f802a6a9944192cc1a80bb Mon Sep 17 00:00:00 2001 From: xufei Date: Thu, 6 Apr 2017 11:29:51 +0800 Subject: [PATCH 2/3] add unit tests --- src/models/a.js | 5 +- src/models/b.js | 7 +- src/models/reusable.js | 4 +- src/models/task.js | 16 +--- src/models/users.js | 5 +- src/services/makeService.js | 12 +++ test/a.spec.js | 26 +++++++ test/b.spec.js | 26 +++++++ test/logger.spec.js | 19 +++++ test/reusable.spec.js | 37 ++++++++++ test/task.spec.js | 80 ++++++++++++++++++++ test/user.spec.js | 143 ++++++++++++++++++++++++++++++++++++ 12 files changed, 354 insertions(+), 26 deletions(-) create mode 100644 src/services/makeService.js create mode 100644 test/a.spec.js create mode 100644 test/b.spec.js create mode 100644 test/logger.spec.js create mode 100644 test/reusable.spec.js create mode 100644 test/task.spec.js create mode 100644 test/user.spec.js diff --git a/src/models/a.js b/src/models/a.js index 2cb7ae2..9d2e606 100644 --- a/src/models/a.js +++ b/src/models/a.js @@ -4,14 +4,11 @@ export default { namespace: 'a', state: '', reducers: { - fetchSuccess(state, { payload }) { - return state + payload; - }, }, effects: { *foo(action, { call, put }) { yield call(delay, 1000); - yield put({ type: 'reusable/addMessage', payload: { data: '666' } }); + yield put({ type: 'reusable/addMessage', payload: '666' }); }, }, }; diff --git a/src/models/b.js b/src/models/b.js index b02bea0..5a84177 100644 --- a/src/models/b.js +++ b/src/models/b.js @@ -4,9 +4,6 @@ export default { namespace: 'b', state: '', reducers: { - fooSuccess(state, { payload }) { - return state + payload; - }, }, effects: { addWatcher: [function *watcher({ take, put }) { @@ -15,7 +12,7 @@ export default { /* eslint-enable */ // watch foo action from model a const data1 = yield take('a/foo'); - yield put({ type: 'logger/addMessage', payload: `observe an action from model A: ${data1}` }); + yield put({ type: 'reusable/addMessage', payload: `observe an action from model A: ${data1}` }); // watch addLogSuccess action from model reuseable const data2 = yield take('reusable/addMessageSuccess'); @@ -25,7 +22,7 @@ export default { *foo(action, { call, put }) { yield call(delay, 3000); - yield put({ type: 'reusable/addMessage', payload: { data: '233' } }); + yield put({ type: 'reusable/addMessage', payload: '233' }); }, }, }; diff --git a/src/models/reusable.js b/src/models/reusable.js index d40b9f0..49c7a18 100644 --- a/src/models/reusable.js +++ b/src/models/reusable.js @@ -9,7 +9,7 @@ export default { addMessageSuccess(state, { payload }) { const message = { id: state.messages.length, - text: payload.data, + text: payload, }; const messages = state.messages.concat([message]); @@ -25,7 +25,7 @@ export default { yield call(delay, 1000); if (resolve) { - resolve(payload.data); + resolve(payload); } yield put({ type: 'addMessageSuccess', payload }); diff --git a/src/models/task.js b/src/models/task.js index caa6e0f..5f044ab 100644 --- a/src/models/task.js +++ b/src/models/task.js @@ -1,17 +1,5 @@ import { delay } from '../services/delay'; - -const makeService = (time) => { - return (data) => { - // console.log(`${data} start`); - - return new Promise((resolve) => { - setTimeout(() => { - resolve(data); - // console.log(`${data} end`); - }, time); - }); - }; -}; +import { makeService } from '../services/makeService'; export default { namespace: 'task', @@ -43,7 +31,7 @@ export default { const { data, timeout } = yield race({ data: call(makeService(2000), 'some data'), - timeout: call(delay, 1000), + timeout: call(delay, 3000), }); if (data) { diff --git a/src/models/users.js b/src/models/users.js index 3914c5d..a57627f 100644 --- a/src/models/users.js +++ b/src/models/users.js @@ -1,5 +1,8 @@ import * as usersService from '../services/users'; +// selector放在这里,而且要export出来,不然不好写测试…… +export const pageSelector = state => state.users.page; + export default { namespace: 'users', state: { @@ -37,7 +40,7 @@ export default { yield put({ type: 'reload' }); }, *reload(action, { put, select }) { - const page = yield select(state => state.users.page); + const page = yield select(pageSelector); yield put({ type: 'fetch', payload: { page } }); }, }, diff --git a/src/services/makeService.js b/src/services/makeService.js new file mode 100644 index 0000000..c9783d3 --- /dev/null +++ b/src/services/makeService.js @@ -0,0 +1,12 @@ +export const makeService = (time) => { + return (data) => { + // console.log(`${data} start`); + + return new Promise((resolve) => { + setTimeout(() => { + resolve(data); + // console.log(`${data} end`); + }, time); + }); + }; +}; diff --git a/test/a.spec.js b/test/a.spec.js new file mode 100644 index 0000000..d1bf0ea --- /dev/null +++ b/test/a.spec.js @@ -0,0 +1,26 @@ +import expect from 'expect'; +import { effects } from 'dva/saga'; +import { delay } from '../src/services/delay'; +import a from '../src/models/a'; + +describe('A Model', () => { + it('loads', () => { + expect(a).toExist(); + }); + + describe('effects', () => { + it('foo should work', () => { + const { call, put } = effects; + + const sagas = a.effects; + const saga = sagas.foo; + const generator = saga({ type: 'a/foo' }, { call, put }); + + let next = generator.next(); + expect(next.value).toEqual(call(delay, 1000)); + + next = generator.next(); + expect(next.value).toEqual(put({ type: 'reusable/addMessage', payload: '666' })); + }); + }); +}); diff --git a/test/b.spec.js b/test/b.spec.js new file mode 100644 index 0000000..600f346 --- /dev/null +++ b/test/b.spec.js @@ -0,0 +1,26 @@ +import expect from 'expect'; +import { effects } from 'dva/saga'; +import { delay } from '../src/services/delay'; +import b from '../src/models/b'; + +describe('B Model', () => { + it('loads', () => { + expect(b).toExist(); + }); + + describe('effects', () => { + it('foo should work', () => { + const { call, put } = effects; + + const sagas = b.effects; + const saga = sagas.foo; + const generator = saga({ type: 'b/foo' }, { call, put }); + + let next = generator.next(); + expect(next.value).toEqual(call(delay, 3000)); + + next = generator.next(); + expect(next.value).toEqual(put({ type: 'reusable/addMessage', payload: '233' })); + }); + }); +}); diff --git a/test/logger.spec.js b/test/logger.spec.js new file mode 100644 index 0000000..70ceaba --- /dev/null +++ b/test/logger.spec.js @@ -0,0 +1,19 @@ +import expect from 'expect'; +import logger from '../src/models/logger'; + +describe('Logger Model', () => { + it('loads', () => { + expect(logger).toExist(); + }); + + describe('reducers', () => { + it('addLog should work', () => { + const reducers = logger.reducers; + const reducer = reducers.addLog; + const state = { + logs: [], + }; + expect(reducer(state, { payload: 'log' })).toEqual({ logs: [{ id: 0, text: 'log' }] }); + }); + }); +}); diff --git a/test/reusable.spec.js b/test/reusable.spec.js new file mode 100644 index 0000000..6f37194 --- /dev/null +++ b/test/reusable.spec.js @@ -0,0 +1,37 @@ +import expect from 'expect'; +import { effects } from 'dva/saga'; +import { delay } from '../src/services/delay'; +import reusable from '../src/models/reusable'; + +describe('Reusable Model', () => { + it('loads', () => { + expect(reusable).toExist(); + }); + + describe('reducers', () => { + it('addMessageSuccess should work', () => { + const reducers = reusable.reducers; + const reducer = reducers.addMessageSuccess; + const state = { + messages: [], + }; + expect(reducer(state, { payload: 'message' })).toEqual({ messages: [{ id: 0, text: 'message' }] }); + }); + }); + + describe('effects', () => { + it('addMessage should work', () => { + const { call, put } = effects; + + const sagas = reusable.effects; + const saga = sagas.addMessage; + const generator = saga({ type: 'reusable/addMessage', payload: '666' }, { call, put }); + + let next = generator.next(); + expect(next.value).toEqual(call(delay, 1000)); + + next = generator.next(); + expect(next.value).toEqual(put({ type: 'addMessageSuccess', payload: '666' })); + }); + }); +}); diff --git a/test/task.spec.js b/test/task.spec.js new file mode 100644 index 0000000..5beb6a2 --- /dev/null +++ b/test/task.spec.js @@ -0,0 +1,80 @@ +import expect from 'expect'; +import { effects } from 'dva/saga'; + +import { delay } from '../src/services/delay'; +import { makeService } from '../src/services/makeService'; + +import task from '../src/models/task'; + +describe('Task Model', () => { + it('loads', () => { + expect(task).toExist(); + }); + + describe('effects', () => { + it('sequential should work', () => { + const { call, put } = effects; + + const sagas = task.effects; + const saga = sagas.sequential; + const generator = saga({ type: 'task/sequential' }, { call, put }); + + let next = generator.next(); + expect(next.value).toEqual(call(delay, 3000)); + + next = generator.next(); + expect(next.value).toEqual(put({ type: 'logger/addLog', payload: 'step1' })); + + next = generator.next(); + expect(next.value).toEqual(call(delay, 5000)); + + next = generator.next(); + expect(next.value).toEqual(put({ type: 'logger/addLog', payload: 'step2' })); + }); + + it('parallel should work', () => { + const { call, put } = effects; + + const sagas = task.effects; + const saga = sagas.parallel; + const generator = saga({ type: 'task/parallel' }, { call, put }); + + let next = generator.next(); + expect(next.value).toEqual(put({ type: 'logger/addLog', payload: 'all started' })); + + next = generator.next(); + expect(next.value).toEqual([ + call(makeService(3000), 'service 1'), + call(makeService(5000), 'service 2'), + call(makeService(7000), 'service 3'), + ]); + + next = generator.next(); + expect(next.value).toEqual(put({ type: 'logger/addLog', payload: 'all completed' })); + }); + + it('race shold work', () => { + const { call, put, race } = effects; + + const sagas = task.effects; + const saga = sagas.race; + const generator = saga({ type: 'task/race' }, { call, put, race }); + + let next = generator.next(); + expect(next.value).toEqual(put({ type: 'logger/addLog', payload: 'start race' })); + + next = generator.next(); + expect(next.value).toEqual(race({ + data: call(makeService(2000), 'some data'), + timeout: call(delay, 3000), + })); + + // 因为在单元测试里,跑不了被测逻辑的那个if + // 那个if是依赖于实际执行逻辑的,但saga的单元测试只是测action的序列化结果是否一致,并不跑真实逻辑 + // 所以要手动注入一个结果进去 + const data = 'some data'; + next = generator.next({ data }); + expect(next.value).toEqual(put({ type: 'logger/addLog', payload: data })); + }); + }); +}); diff --git a/test/user.spec.js b/test/user.spec.js new file mode 100644 index 0000000..e489f71 --- /dev/null +++ b/test/user.spec.js @@ -0,0 +1,143 @@ +import expect from 'expect'; +import { effects } from 'dva/saga'; + +import * as usersService from '../src/services/users'; + +import user, { pageSelector } from '../src/models/users'; + +describe('User Model', () => { + it('loads', () => { + expect(user).toExist(); + }); + + describe('reducers', () => { + it('save should work', () => { + const reducers = user.reducers; + const reducer = reducers.save; + const state = { + list: [], + total: null, + page: null, + }; + + const action = { + type: 'save', + payload: { + data: [{ id: '01' }, { id: '02' }], + total: 111, + page: 1, + }, + }; + + expect(reducer(state, action)).toEqual({ + list: [{ id: '01' }, { id: '02' }], + total: 111, + page: 1, + }); + }); + }); + + describe('effects', () => { + it('fetch should work', () => { + const { call, put } = effects; + + const sagas = user.effects; + const saga = sagas.fetch; + + const generator = saga({ type: 'fetch', payload: { page: 1 } }, { call, put }); + + let next = generator.next(); + const page = 1; + expect(next.value).toEqual(call(usersService.fetch, { page })); + + next = generator.next({ data: [], + headers: { + 'x-total-count': '111', + }, + }); + + expect(next.value).toEqual(put({ + type: 'save', + payload: { + data: [], + total: 111, + page: 1, + }, + })); + }); + + it('remove should work', () => { + const { call, put } = effects; + + const sagas = user.effects; + const saga = sagas.remove; + + const id = 'id111'; + + const generator = saga({ type: 'remove', payload: id }, { call, put }); + + let next = generator.next(); + expect(next.value).toEqual(call(usersService.remove, id)); + + next = generator.next(); + expect(next.value).toEqual(put({ type: 'reload' })); + }); + + it('patch should work', () => { + const { call, put } = effects; + + const sagas = user.effects; + const saga = sagas.patch; + + const id = 'id111'; + const values = []; + + const generator = saga({ type: 'patch', payload: { id, values } }, { call, put }); + + let next = generator.next(); + expect(next.value).toEqual(call(usersService.patch, id, values)); + + next = generator.next(); + expect(next.value).toEqual(put({ type: 'reload' })); + }); + + it('create should work', () => { + const { call, put } = effects; + + const sagas = user.effects; + const saga = sagas.create; + + const values = []; + + const generator = saga({ type: 'create', payload: values }, { call, put }); + + let next = generator.next(); + expect(next.value).toEqual(call(usersService.create, values)); + + next = generator.next(); + expect(next.value).toEqual(put({ type: 'reload' })); + }); + + it('reload should work', () => { + const { select, put } = effects; + + const sagas = user.effects; + const saga = sagas.reload; + + const generator = saga({ type: 'reload' }, { select, put }); + + const fakeState = { + users: { + page: 1, + }, + }; + let next = generator.next(fakeState); + // 下面这句的select,源码和测试两边需要是同一个selector函数才可以…… + // 然而,如果源model里面不把这个selector命名并且export出来,这里肯定过不了 + expect(next.value).toEqual(select(pageSelector)); + + next = generator.next(2); + expect(next.value).toEqual(put({ type: 'fetch', payload: { page: 2 } })); + }); + }); +}); From 541139d53cac2f5ae5153dcbdb958b59a2c34327 Mon Sep 17 00:00:00 2001 From: xufei Date: Fri, 7 Apr 2017 14:15:22 +0800 Subject: [PATCH 3/3] fix user model --- src/models/users.js | 2 +- src/router.js | 2 +- test/user.spec.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/models/users.js b/src/models/users.js index a57627f..a066b6d 100644 --- a/src/models/users.js +++ b/src/models/users.js @@ -3,7 +3,7 @@ import * as usersService from '../services/users'; // selector放在这里,而且要export出来,不然不好写测试…… export const pageSelector = state => state.users.page; -export default { +export const user = { namespace: 'users', state: { list: [], diff --git a/src/router.js b/src/router.js index d596f2d..dfd6525 100644 --- a/src/router.js +++ b/src/router.js @@ -25,7 +25,7 @@ function RouterConfig({ history, app }) { name: 'UsersPage', getComponent(nextState, cb) { require.ensure([], (require) => { - registerModel(app, require('./models/users')); + registerModel(app, require('./models/users').user); cb(null, require('./routes/Users')); }); }, diff --git a/test/user.spec.js b/test/user.spec.js index e489f71..768a96d 100644 --- a/test/user.spec.js +++ b/test/user.spec.js @@ -3,7 +3,7 @@ import { effects } from 'dva/saga'; import * as usersService from '../src/services/users'; -import user, { pageSelector } from '../src/models/users'; +import { user, pageSelector } from '../src/models/users'; describe('User Model', () => { it('loads', () => {