@@ -3,6 +3,13 @@ import VNode from 'core/vdom/vnode'
33import { patch } from 'web/runtime/patch'
44import { SSR_ATTR } from 'shared/constants'
55
6+ function createMockSSRDOM ( innerHTML ) {
7+ const dom = document . createElement ( 'div' )
8+ dom . setAttribute ( SSR_ATTR , 'true' )
9+ dom . innerHTML = innerHTML
10+ return dom
11+ }
12+
613describe ( 'vdom patch: hydration' , ( ) => {
714 let vnode0
815 beforeEach ( ( ) => {
@@ -89,9 +96,7 @@ describe('vdom patch: hydration', () => {
8996
9097 // component hydration is better off with a more e2e approach
9198 it ( 'should hydrate components when server-rendered DOM tree is same as virtual DOM tree' , done => {
92- const dom = document . createElement ( 'div' )
93- dom . setAttribute ( SSR_ATTR , 'true' )
94- dom . innerHTML = '<span>foo</span><div class="b a"><span>foo qux</span></div><!---->'
99+ const dom = createMockSSRDOM ( '<span>foo</span><div class="b a"><span>foo qux</span></div><!---->' )
95100 const originalNode1 = dom . children [ 0 ]
96101 const originalNode2 = dom . children [ 1 ]
97102
@@ -131,9 +136,7 @@ describe('vdom patch: hydration', () => {
131136 } )
132137
133138 it ( 'should warn failed hydration for non-matching DOM in child component' , ( ) => {
134- const dom = document . createElement ( 'div' )
135- dom . setAttribute ( SSR_ATTR , 'true' )
136- dom . innerHTML = '<div><span></span></div>'
139+ const dom = createMockSSRDOM ( '<div><span></span></div>' )
137140
138141 new Vue ( {
139142 template : '<div><test></test></div>' ,
@@ -148,9 +151,7 @@ describe('vdom patch: hydration', () => {
148151 } )
149152
150153 it ( 'should overwrite textNodes in the correct position but with mismatching text without warning' , ( ) => {
151- const dom = document . createElement ( 'div' )
152- dom . setAttribute ( SSR_ATTR , 'true' )
153- dom . innerHTML = '<div><span>foo</span></div>'
154+ const dom = createMockSSRDOM ( '<div><span>foo</span></div>' )
154155
155156 new Vue ( {
156157 template : '<div><test></test></div>' ,
@@ -169,9 +170,7 @@ describe('vdom patch: hydration', () => {
169170 } )
170171
171172 it ( 'should pick up elements with no children and populate without warning' , done => {
172- const dom = document . createElement ( 'div' )
173- dom . setAttribute ( SSR_ATTR , 'true' )
174- dom . innerHTML = '<div><span></span></div>'
173+ const dom = createMockSSRDOM ( '<div><span></span></div>' )
175174 const span = dom . querySelector ( 'span' )
176175
177176 const vm = new Vue ( {
@@ -195,4 +194,107 @@ describe('vdom patch: hydration', () => {
195194 expect ( vm . $el . innerHTML ) . toBe ( '<div><span>foo</span></div>' )
196195 } ) . then ( done )
197196 } )
197+
198+ it ( 'should hydrate async component' , done => {
199+ const dom = createMockSSRDOM ( '<span>foo</span>' )
200+ const span = dom . querySelector ( 'span' )
201+
202+ const Foo = resolve => setTimeout ( ( ) => {
203+ resolve ( {
204+ data : ( ) => ( { msg : 'foo' } ) ,
205+ template : `<span>{{ msg }}</span>`
206+ } )
207+ } , 0 )
208+
209+ const vm = new Vue ( {
210+ template : '<div><foo ref="foo" /></div>' ,
211+ components : { Foo }
212+ } ) . $mount ( dom )
213+
214+ expect ( 'not matching server-rendered content' ) . not . toHaveBeenWarned ( )
215+ expect ( dom . innerHTML ) . toBe ( '<span>foo</span>' )
216+ expect ( vm . $refs . foo ) . toBeUndefined ( )
217+
218+ setTimeout ( ( ) => {
219+ expect ( dom . innerHTML ) . toBe ( '<span>foo</span>' )
220+ expect ( vm . $refs . foo ) . not . toBeUndefined ( )
221+ vm . $refs . foo . msg = 'bar'
222+ waitForUpdate ( ( ) => {
223+ expect ( dom . innerHTML ) . toBe ( '<span>bar</span>' )
224+ expect ( dom . querySelector ( 'span' ) ) . toBe ( span )
225+ } ) . then ( done )
226+ } , 0 )
227+ } )
228+
229+ it ( 'should hydrate async component without showing loading' , done => {
230+ const dom = createMockSSRDOM ( '<span>foo</span>' )
231+ const span = dom . querySelector ( 'span' )
232+
233+ const Foo = ( ) => ( {
234+ component : new Promise ( resolve => {
235+ setTimeout ( ( ) => {
236+ resolve ( {
237+ data : ( ) => ( { msg : 'foo' } ) ,
238+ template : `<span>{{ msg }}</span>`
239+ } )
240+ } , 10 )
241+ } ) ,
242+ delay : 1 ,
243+ loading : {
244+ render : h => h ( 'span' , 'loading' )
245+ }
246+ } )
247+
248+ const vm = new Vue ( {
249+ template : '<div><foo ref="foo" /></div>' ,
250+ components : { Foo }
251+ } ) . $mount ( dom )
252+
253+ expect ( 'not matching server-rendered content' ) . not . toHaveBeenWarned ( )
254+ expect ( dom . innerHTML ) . toBe ( '<span>foo</span>' )
255+ expect ( vm . $refs . foo ) . toBeUndefined ( )
256+
257+ setTimeout ( ( ) => {
258+ expect ( dom . innerHTML ) . toBe ( '<span>foo</span>' )
259+ } , 1 )
260+
261+ setTimeout ( ( ) => {
262+ expect ( dom . innerHTML ) . toBe ( '<span>foo</span>' )
263+ expect ( vm . $refs . foo ) . not . toBeUndefined ( )
264+ vm . $refs . foo . msg = 'bar'
265+ waitForUpdate ( ( ) => {
266+ expect ( dom . innerHTML ) . toBe ( '<span>bar</span>' )
267+ expect ( dom . querySelector ( 'span' ) ) . toBe ( span )
268+ } ) . then ( done )
269+ } , 10 )
270+ } )
271+
272+ it ( 'should hydrate async component by replacing DOM if error occurs' , done => {
273+ const dom = createMockSSRDOM ( '<span>foo</span>' )
274+
275+ const Foo = ( ) => ( {
276+ component : new Promise ( ( resolve , reject ) => {
277+ setTimeout ( ( ) => {
278+ reject ( 'something went wrong' )
279+ } , 10 )
280+ } ) ,
281+ error : {
282+ render : h => h ( 'span' , 'error' )
283+ }
284+ } )
285+
286+ new Vue ( {
287+ template : '<div><foo ref="foo" /></div>' ,
288+ components : { Foo }
289+ } ) . $mount ( dom )
290+
291+ expect ( 'not matching server-rendered content' ) . not . toHaveBeenWarned ( )
292+ expect ( dom . innerHTML ) . toBe ( '<span>foo</span>' )
293+
294+ setTimeout ( ( ) => {
295+ expect ( 'Failed to resolve async' ) . toHaveBeenWarned ( )
296+ expect ( dom . innerHTML ) . toBe ( '<span>error</span>' )
297+ done ( )
298+ } , 10 )
299+ } )
198300} )
0 commit comments