modules.spec.js 13 KB

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