1
- import Vue from 'vue'
2
- import Vuex , { mapState } from 'vuex'
3
- import VueRouter from 'vue-router'
1
+ import { createApp , defineComponent , h , computed , nextTick } from 'vue'
2
+ import { createStore , useStore } from 'vuex'
3
+ import { createRouter , createMemoryHistory , RouterView } from 'vue-router'
4
4
import { sync } from '@/index'
5
5
6
- Vue . use ( Vuex )
7
- Vue . use ( VueRouter )
6
+ async function run ( originalModuleName : string , done : Function ) : Promise < void > {
7
+ const moduleName = originalModuleName || 'route'
8
8
9
- function run ( originalModuleName : string , done : Function ) : void {
10
- const moduleName : string = originalModuleName || 'route'
11
-
12
- const store = new Vuex . Store ( {
13
- state : { msg : 'foo' }
9
+ const store = createStore ( {
10
+ state ( ) {
11
+ return { msg : 'foo' }
12
+ }
14
13
} )
15
14
16
- const Home = Vue . extend ( {
17
- computed : mapState ( moduleName , {
18
- path : ( state : any ) => state . fullPath ,
19
- foo : ( state : any ) => state . params . foo ,
20
- bar : ( state : any ) => state . params . bar
21
- } ) ,
22
- render ( h ) {
23
- return h ( 'div' , [ this . path , ' ' , this . foo , ' ' , this . bar ] )
15
+ const Home = defineComponent ( {
16
+ setup ( ) {
17
+ const store = useStore ( )
18
+ const path = computed ( ( ) => store . state [ moduleName ] . fullPath )
19
+ const foo = computed ( ( ) => store . state [ moduleName ] . params . foo )
20
+ const bar = computed ( ( ) => store . state [ moduleName ] . params . bar )
21
+ return ( ) => h ( 'div' , [ path . value , ' ' , foo . value , ' ' , bar . value ] )
24
22
}
25
23
} )
26
24
27
- const router = new VueRouter ( {
28
- mode : 'abstract' ,
29
- routes : [ { path : '/:foo/:bar' , component : Home } ]
25
+ const router = createRouter ( {
26
+ history : createMemoryHistory ( ) ,
27
+ routes : [
28
+ {
29
+ path : '/' ,
30
+ component : {
31
+ template : 'root'
32
+ }
33
+ } ,
34
+ { path : '/:foo/:bar' , component : Home }
35
+ ]
30
36
} )
31
37
32
- sync ( store , router , {
33
- moduleName : originalModuleName
34
- } )
38
+ originalModuleName
39
+ ? sync ( store , router , { moduleName : originalModuleName } )
40
+ : sync ( store , router )
35
41
36
42
router . push ( '/a/b' )
43
+ await router . isReady ( )
37
44
expect ( ( store . state as any ) [ moduleName ] . fullPath ) . toBe ( '/a/b' )
38
45
expect ( ( store . state as any ) [ moduleName ] . params ) . toEqual ( {
39
46
foo : 'a' ,
40
47
bar : 'b'
41
48
} )
42
49
43
- const app = new Vue ( {
44
- store,
45
- router,
46
- render : ( h ) => h ( 'router-view' )
47
- } ) . $mount ( )
50
+ const rootEl = document . createElement ( 'div' )
51
+ document . body . appendChild ( rootEl )
48
52
49
- expect ( app . $el . textContent ) . toBe ( '/a/b a b' )
53
+ const app = createApp ( {
54
+ render : ( ) => h ( RouterView )
55
+ } )
56
+ app . use ( store )
57
+ app . use ( router )
58
+ app . mount ( rootEl )
50
59
51
- router . push ( '/c/d?n=1#hello' )
60
+ expect ( rootEl . textContent ) . toBe ( '/a/b a b' )
61
+ await router . push ( '/c/d?n=1#hello' )
52
62
expect ( ( store . state as any ) [ moduleName ] . fullPath ) . toBe ( '/c/d?n=1#hello' )
53
63
expect ( ( store . state as any ) [ moduleName ] . params ) . toEqual ( {
54
64
foo : 'c' ,
@@ -57,49 +67,120 @@ function run(originalModuleName: string, done: Function): void {
57
67
expect ( ( store . state as any ) [ moduleName ] . query ) . toEqual ( { n : '1' } )
58
68
expect ( ( store . state as any ) [ moduleName ] . hash ) . toEqual ( '#hello' )
59
69
60
- Vue . nextTick ( ( ) => {
61
- expect ( app . $el . textContent ) . toBe ( '/c/d?n=1#hello c d' )
70
+ nextTick ( ( ) => {
71
+ expect ( rootEl . textContent ) . toBe ( '/c/d?n=1#hello c d' )
62
72
done ( )
63
73
} )
64
74
}
65
75
66
- test ( 'default usage' , ( done ) => {
67
- run ( '' , done )
76
+ test ( 'default usage' , async ( done ) => {
77
+ await run ( '' , done )
68
78
} )
69
79
70
- test ( 'with custom moduleName' , ( done ) => {
71
- run ( 'moduleName' , done )
80
+ test ( 'with custom moduleName' , async ( done ) => {
81
+ await run ( 'moduleName' , done )
72
82
} )
73
83
74
- test ( 'unsync' , ( done ) => {
75
- const store = new Vuex . Store ( { } )
84
+ test ( 'unsync' , async ( done ) => {
85
+ const store = createStore ( {
86
+ state ( ) {
87
+ return { msg : 'foo' }
88
+ }
89
+ } )
90
+
76
91
spyOn ( store , 'watch' ) . and . callThrough ( )
77
92
78
- const router = new VueRouter ( )
93
+ const router = createRouter ( {
94
+ history : createMemoryHistory ( ) ,
95
+ routes : [
96
+ {
97
+ path : '/' ,
98
+ component : {
99
+ template : 'root'
100
+ }
101
+ }
102
+ ]
103
+ } )
79
104
80
105
const moduleName = 'testDesync'
81
106
const unsync = sync ( store , router , {
82
107
moduleName : moduleName
83
108
} )
84
109
85
110
expect ( unsync ) . toBeInstanceOf ( Function )
86
-
87
111
// Test module registered, store watched, router hooked
88
112
expect ( ( store as any ) . state [ moduleName ] ) . toBeDefined ( )
89
113
expect ( ( store as any ) . watch ) . toHaveBeenCalled ( )
90
- expect ( ( store as any ) . _watcherVM ) . toBeDefined ( )
91
- expect ( ( store as any ) . _watcherVM . _watchers ) . toBeDefined ( )
92
- expect ( ( store as any ) . _watcherVM . _watchers . length ) . toBe ( 1 )
93
- expect ( ( router as any ) . afterHooks ) . toBeDefined ( )
94
- expect ( ( router as any ) . afterHooks . length ) . toBe ( 1 )
95
114
96
115
// Now unsync vuex-router-sync
97
116
unsync ( )
98
117
99
- // Ensure router unhooked, store-unwatched, module unregistered
100
- expect ( ( router as any ) . afterHooks . length ) . toBe ( 0 )
101
- expect ( ( store as any ) . _watcherVm ) . toBeUndefined ( )
118
+ // Ensure module unregistered, no store change
119
+ router . push ( '/' )
120
+ await router . isReady ( )
102
121
expect ( ( store as any ) . state [ moduleName ] ) . toBeUndefined ( )
103
-
122
+ expect ( ( store as any ) . state ) . toEqual ( { msg : 'foo' } )
104
123
done ( )
105
124
} )
125
+
126
+ test ( 'time traveling' , async ( ) => {
127
+ const store = createStore ( {
128
+ state ( ) {
129
+ return { msg : 'foo' }
130
+ }
131
+ } )
132
+
133
+ const router = createRouter ( {
134
+ history : createMemoryHistory ( ) ,
135
+ routes : [
136
+ {
137
+ path : '/' ,
138
+ component : {
139
+ template : 'root'
140
+ }
141
+ } ,
142
+ {
143
+ path : '/a' ,
144
+ component : {
145
+ template : 'a'
146
+ }
147
+ }
148
+ ]
149
+ } )
150
+
151
+ sync ( store , router )
152
+
153
+ const state1 = clone ( store . state )
154
+
155
+ // time travel before any route change so that we can test `currentPath`
156
+ // being `undefined`
157
+ store . replaceState ( state1 )
158
+
159
+ expect ( ( store . state as any ) . route . path ) . toBe ( '/' )
160
+
161
+ // change route, save new state to time travel later on
162
+ await router . push ( '/a' )
163
+
164
+ expect ( ( store . state as any ) . route . path ) . toBe ( '/a' )
165
+
166
+ const state2 = clone ( store . state )
167
+
168
+ // change route again so that we're on different route than `state2`
169
+ await router . push ( '/' )
170
+
171
+ expect ( ( store . state as any ) . route . path ) . toBe ( '/' )
172
+
173
+ // time travel to check we go back to the old route
174
+ store . replaceState ( state2 )
175
+
176
+ expect ( ( store . state as any ) . route . path ) . toBe ( '/a' )
177
+
178
+ // final push to the route to fire `afterEach` hook on router
179
+ await router . push ( '/a' )
180
+
181
+ expect ( ( store . state as any ) . route . path ) . toBe ( '/a' )
182
+ } )
183
+
184
+ function clone ( state : any ) {
185
+ return JSON . parse ( JSON . stringify ( state ) )
186
+ }
0 commit comments