modules.spec.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. import Vue from 'vue'
  2. import Vuex from '../../dist/vuex.js'
  3. const TEST = 'TEST'
  4. describe('Modules', () => {
  5. describe('module registration', () => {
  6. it('dynamic module registration', () => {
  7. const store = new Vuex.Store({
  8. strict: true,
  9. modules: {
  10. foo: {
  11. state: { bar: 1 },
  12. mutations: { inc: state => state.bar++ },
  13. actions: { incFoo: ({ commit }) => commit('inc') },
  14. getters: { bar: state => state.bar }
  15. }
  16. }
  17. })
  18. expect(() => {
  19. store.registerModule('hi', {
  20. state: { a: 1 },
  21. mutations: { inc: state => state.a++ },
  22. actions: { inc: ({ commit }) => commit('inc') },
  23. getters: { a: state => state.a }
  24. })
  25. }).not.toThrow()
  26. expect(store._mutations.inc.length).toBe(2)
  27. expect(store.state.hi.a).toBe(1)
  28. expect(store.getters.a).toBe(1)
  29. // assert initial modules work as expected after dynamic registration
  30. expect(store.state.foo.bar).toBe(1)
  31. expect(store.getters.bar).toBe(1)
  32. // test dispatching actions defined in dynamic module
  33. store.dispatch('inc')
  34. expect(store.state.hi.a).toBe(2)
  35. expect(store.getters.a).toBe(2)
  36. expect(store.state.foo.bar).toBe(2)
  37. expect(store.getters.bar).toBe(2)
  38. // unregister
  39. store.unregisterModule('hi')
  40. expect(store.state.hi).toBeUndefined()
  41. expect(store.getters.a).toBeUndefined()
  42. expect(store._mutations.inc.length).toBe(1)
  43. expect(store._actions.inc).toBeUndefined()
  44. // assert initial modules still work as expected after unregister
  45. store.dispatch('incFoo')
  46. expect(store.state.foo.bar).toBe(3)
  47. expect(store.getters.bar).toBe(3)
  48. })
  49. it('dynamic module registration with namespace inheritance', () => {
  50. const store = new Vuex.Store({
  51. modules: {
  52. a: {
  53. namespaced: true
  54. }
  55. }
  56. })
  57. const actionSpy = jasmine.createSpy()
  58. const mutationSpy = jasmine.createSpy()
  59. store.registerModule(['a', 'b'], {
  60. state: { value: 1 },
  61. getters: { foo: state => state.value },
  62. actions: { foo: actionSpy },
  63. mutations: { foo: mutationSpy }
  64. })
  65. expect(store.state.a.b.value).toBe(1)
  66. expect(store.getters['a/foo']).toBe(1)
  67. store.dispatch('a/foo')
  68. expect(actionSpy).toHaveBeenCalled()
  69. store.commit('a/foo')
  70. expect(mutationSpy).toHaveBeenCalled()
  71. })
  72. })
  73. // #524
  74. it('should not fire an unrelated watcher', done => {
  75. const spy = jasmine.createSpy()
  76. const store = new Vuex.Store({
  77. modules: {
  78. a: {
  79. state: { value: 1 }
  80. },
  81. b: {}
  82. }
  83. })
  84. store.watch(state => state.a, spy)
  85. store.registerModule(['b', 'c'], {
  86. state: { value: 2 }
  87. })
  88. Vue.nextTick(() => {
  89. expect(spy).not.toHaveBeenCalled()
  90. done()
  91. })
  92. })
  93. describe('modules usage', () => {
  94. it('module: mutation', function () {
  95. const mutations = {
  96. [TEST] (state, n) {
  97. state.a += n
  98. }
  99. }
  100. const store = new Vuex.Store({
  101. state: {
  102. a: 1
  103. },
  104. mutations,
  105. modules: {
  106. nested: {
  107. state: { a: 2 },
  108. mutations,
  109. modules: {
  110. one: {
  111. state: { a: 3 },
  112. mutations
  113. },
  114. nested: {
  115. modules: {
  116. two: {
  117. state: { a: 4 },
  118. mutations
  119. },
  120. three: {
  121. state: { a: 5 },
  122. mutations
  123. }
  124. }
  125. }
  126. }
  127. },
  128. four: {
  129. state: { a: 6 },
  130. mutations
  131. }
  132. }
  133. })
  134. store.commit(TEST, 1)
  135. expect(store.state.a).toBe(2)
  136. expect(store.state.nested.a).toBe(3)
  137. expect(store.state.nested.one.a).toBe(4)
  138. expect(store.state.nested.nested.two.a).toBe(5)
  139. expect(store.state.nested.nested.three.a).toBe(6)
  140. expect(store.state.four.a).toBe(7)
  141. })
  142. it('module: action', function () {
  143. let calls = 0
  144. const makeAction = n => {
  145. return {
  146. [TEST] ({ state, rootState }) {
  147. calls++
  148. expect(state.a).toBe(n)
  149. expect(rootState).toBe(store.state)
  150. }
  151. }
  152. }
  153. const store = new Vuex.Store({
  154. state: {
  155. a: 1
  156. },
  157. actions: makeAction(1),
  158. modules: {
  159. nested: {
  160. state: { a: 2 },
  161. actions: makeAction(2),
  162. modules: {
  163. one: {
  164. state: { a: 3 },
  165. actions: makeAction(3)
  166. },
  167. nested: {
  168. modules: {
  169. two: {
  170. state: { a: 4 },
  171. actions: makeAction(4)
  172. },
  173. three: {
  174. state: { a: 5 },
  175. actions: makeAction(5)
  176. }
  177. }
  178. }
  179. }
  180. },
  181. four: {
  182. state: { a: 6 },
  183. actions: makeAction(6)
  184. }
  185. }
  186. })
  187. store.dispatch(TEST)
  188. expect(calls).toBe(6)
  189. })
  190. it('module: getters', function () {
  191. const makeGetter = n => ({
  192. [`getter${n}`]: (state, getters, rootState) => {
  193. expect(getters.constant).toBe(0)
  194. expect(rootState).toBe(store.state)
  195. return state.a
  196. }
  197. })
  198. const store = new Vuex.Store({
  199. state: {
  200. a: 1
  201. },
  202. getters: {
  203. constant: () => 0,
  204. ...makeGetter(1)
  205. },
  206. modules: {
  207. nested: {
  208. state: { a: 2 },
  209. getters: makeGetter(2),
  210. modules: {
  211. one: {
  212. state: { a: 3 },
  213. getters: makeGetter(3)
  214. },
  215. nested: {
  216. modules: {
  217. two: {
  218. state: { a: 4 },
  219. getters: makeGetter(4)
  220. },
  221. three: {
  222. state: { a: 5 },
  223. getters: makeGetter(5)
  224. }
  225. }
  226. }
  227. }
  228. },
  229. four: {
  230. state: { a: 6 },
  231. getters: makeGetter(6)
  232. }
  233. }
  234. })
  235. ;[1, 2, 3, 4, 5, 6].forEach(n => {
  236. expect(store.getters[`getter${n}`]).toBe(n)
  237. })
  238. })
  239. it('module: namespace', () => {
  240. const actionSpy = jasmine.createSpy()
  241. const mutationSpy = jasmine.createSpy()
  242. const store = new Vuex.Store({
  243. modules: {
  244. a: {
  245. namespaced: true,
  246. state: {
  247. a: 1
  248. },
  249. getters: {
  250. b: () => 2
  251. },
  252. actions: {
  253. [TEST]: actionSpy
  254. },
  255. mutations: {
  256. [TEST]: mutationSpy
  257. }
  258. }
  259. }
  260. })
  261. expect(store.state.a.a).toBe(1)
  262. expect(store.getters['a/b']).toBe(2)
  263. store.dispatch('a/' + TEST)
  264. expect(actionSpy).toHaveBeenCalled()
  265. store.commit('a/' + TEST)
  266. expect(mutationSpy).toHaveBeenCalled()
  267. })
  268. it('module: nested namespace', () => {
  269. // mock module generator
  270. const actionSpys = []
  271. const mutationSpys = []
  272. const createModule = (name, namespaced, children) => {
  273. const actionSpy = jasmine.createSpy()
  274. const mutationSpy = jasmine.createSpy()
  275. actionSpys.push(actionSpy)
  276. mutationSpys.push(mutationSpy)
  277. return {
  278. namespaced,
  279. state: {
  280. [name]: true
  281. },
  282. getters: {
  283. [name]: state => state[name]
  284. },
  285. actions: {
  286. [name]: actionSpy
  287. },
  288. mutations: {
  289. [name]: mutationSpy
  290. },
  291. modules: children
  292. }
  293. }
  294. // mock module
  295. const modules = {
  296. a: createModule('a', true, { // a/a
  297. b: createModule('b', false, { // a/b - does not add namespace
  298. c: createModule('c', true) // a/c/c
  299. }),
  300. d: createModule('d', true) // a/d/d
  301. })
  302. }
  303. const store = new Vuex.Store({ modules })
  304. const expectedTypes = [
  305. 'a/a', 'a/b', 'a/c/c', 'a/d/d'
  306. ]
  307. // getters
  308. expectedTypes.forEach(type => {
  309. expect(store.getters[type]).toBe(true)
  310. })
  311. // actions
  312. expectedTypes.forEach(type => {
  313. store.dispatch(type)
  314. })
  315. actionSpys.forEach(spy => {
  316. expect(spy.calls.count()).toBe(1)
  317. })
  318. // mutations
  319. expectedTypes.forEach(type => {
  320. store.commit(type)
  321. })
  322. mutationSpys.forEach(spy => {
  323. expect(spy.calls.count()).toBe(1)
  324. })
  325. })
  326. it('module: getters are namespaced in namespaced module', () => {
  327. const store = new Vuex.Store({
  328. state: { value: 'root' },
  329. getters: {
  330. foo: state => state.value
  331. },
  332. modules: {
  333. a: {
  334. namespaced: true,
  335. state: { value: 'module' },
  336. getters: {
  337. foo: state => state.value,
  338. bar: (state, getters) => getters.foo,
  339. baz: (state, getters, rootState, rootGetters) => rootGetters.foo
  340. }
  341. }
  342. }
  343. })
  344. expect(store.getters['a/foo']).toBe('module')
  345. expect(store.getters['a/bar']).toBe('module')
  346. expect(store.getters['a/baz']).toBe('root')
  347. })
  348. it('module: action context is namespaced in namespaced module', done => {
  349. const rootActionSpy = jasmine.createSpy()
  350. const rootMutationSpy = jasmine.createSpy()
  351. const moduleActionSpy = jasmine.createSpy()
  352. const moduleMutationSpy = jasmine.createSpy()
  353. const store = new Vuex.Store({
  354. state: { value: 'root' },
  355. getters: { foo: state => state.value },
  356. actions: { foo: rootActionSpy },
  357. mutations: { foo: rootMutationSpy },
  358. modules: {
  359. a: {
  360. namespaced: true,
  361. state: { value: 'module' },
  362. getters: { foo: state => state.value },
  363. actions: {
  364. foo: moduleActionSpy,
  365. test ({ dispatch, commit, getters, rootGetters }) {
  366. expect(getters.foo).toBe('module')
  367. expect(rootGetters.foo).toBe('root')
  368. dispatch('foo')
  369. expect(moduleActionSpy.calls.count()).toBe(1)
  370. dispatch('foo', null, { root: true })
  371. expect(rootActionSpy.calls.count()).toBe(1)
  372. commit('foo')
  373. expect(moduleMutationSpy.calls.count()).toBe(1)
  374. commit('foo', null, { root: true })
  375. expect(rootMutationSpy.calls.count()).toBe(1)
  376. done()
  377. }
  378. },
  379. mutations: { foo: moduleMutationSpy }
  380. }
  381. }
  382. })
  383. store.dispatch('a/test')
  384. })
  385. it('module: use other module that has same namespace', done => {
  386. const actionSpy = jasmine.createSpy()
  387. const mutationSpy = jasmine.createSpy()
  388. const store = new Vuex.Store({
  389. modules: {
  390. parent: {
  391. namespaced: true,
  392. modules: {
  393. a: {
  394. state: { value: 'a' },
  395. getters: { foo: state => state.value },
  396. actions: { foo: actionSpy },
  397. mutations: { foo: mutationSpy }
  398. },
  399. b: {
  400. state: { value: 'b' },
  401. getters: { bar: (state, getters) => getters.foo },
  402. actions: {
  403. test ({ dispatch, commit, getters }) {
  404. expect(getters.foo).toBe('a')
  405. expect(getters.bar).toBe('a')
  406. dispatch('foo')
  407. expect(actionSpy).toHaveBeenCalled()
  408. commit('foo')
  409. expect(mutationSpy).toHaveBeenCalled()
  410. done()
  411. }
  412. }
  413. }
  414. }
  415. }
  416. }
  417. })
  418. store.dispatch('parent/test')
  419. })
  420. it('dispatching multiple actions in different modules', done => {
  421. const store = new Vuex.Store({
  422. modules: {
  423. a: {
  424. actions: {
  425. [TEST] () {
  426. return 1
  427. }
  428. }
  429. },
  430. b: {
  431. actions: {
  432. [TEST] () {
  433. return new Promise(r => r(2))
  434. }
  435. }
  436. }
  437. }
  438. })
  439. store.dispatch(TEST).then(res => {
  440. expect(res[0]).toBe(1)
  441. expect(res[1]).toBe(2)
  442. done()
  443. })
  444. })
  445. it('plugins', function () {
  446. let initState
  447. const mutations = []
  448. const store = new Vuex.Store({
  449. state: {
  450. a: 1
  451. },
  452. mutations: {
  453. [TEST] (state, n) {
  454. state.a += n
  455. }
  456. },
  457. plugins: [
  458. store => {
  459. initState = store.state
  460. store.subscribe((mut, state) => {
  461. expect(state).toBe(store.state)
  462. mutations.push(mut)
  463. })
  464. }
  465. ]
  466. })
  467. expect(initState).toBe(store.state)
  468. store.commit(TEST, 2)
  469. expect(mutations.length).toBe(1)
  470. expect(mutations[0].type).toBe(TEST)
  471. expect(mutations[0].payload).toBe(2)
  472. })
  473. })
  474. })