8000 tests for async components · Snoopbobb/vue@dffd4c7 · GitHub
[go: up one dir, main page]

Skip to content

Commit dffd4c7

Browse files
committed
tests for async components
1 parent d869ba0 commit dffd4c7

File tree

8 files changed

+306
-11
lines changed

8 files changed

+306
-11
lines changed

gruntfile.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module.exports = function (grunt) {
2626
options: {
2727
frameworks: ['jasmine', 'commonjs'],
2828
files: [
29+
'test/unit/lib/util.js',
2930
'test/unit/lib/jquery.js',
3031
'src/**/*.js',
3132
'test/unit/specs/**/*.js'

src/directives/component.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ module.exports = {
3636
// extract inline template as a DocumentFragment
3737
this.template = _.extractContent(this.el, true)
3838
}
39-
// pending callback for async component resolution
40-
this._pendingCb = null
39+
// component resolution related state
40+
this._pendingCb =
41+
this.ctorId =
42+
this.Ctor = null
4143
// if static, build right now.
4244
if (!this._isDynamicLiteral) {
4345
this.resolveCtor(this.expression, _.bind(function () {

src/directives/repeat.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ module.exports = {
106106
copy._asComponent = false
107107
this._linkFn = compile(this.template, copy)
108108
} else {
109+
this.Ctor = null
109110
this.asComponent = true
110111
// check inline-template
111112
if (this._checkParam('inline-template') !== null) {
@@ -181,6 +182,7 @@ module.exports = {
181182
'Async resolution is not supported for v-repeat ' +
182183
'+ dynamic component. (component: ' + id + ')'
183184
)
185+
return _.Vue
184186
}
185187
return Ctor
186188
},

src/instance/misc.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,17 @@ exports._resolveComponent = function (id, cb) {
3737
if (factory.resolved) {
3838
// cached
3939
cb(factory.resolved)
40-
} else if (factory.pending) {
40+
} else if (factory.requested) {
4141
factory.pendingCallbacks.push(cb)
4242
} else {
43-
factory.pending = true
43+
factory.requested = true
4444
var cbs = factory.pendingCallbacks = [cb]
4545
factory(function resolve (res) {
4646
if (_.isPlainObject(res)) {
4747
res = _.Vue.extend(res)
4848
}
4949
// cache resolved
5050
factory.resolved = res
51-
factory.pending = false
5251
// invoke callbacks
5352
for (var i = 0, l = cbs.length; i < l; i++) {
5453
cbs[i](res)

test/.jshintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"casper": true,
2828
"DocumentFragment": true,
2929
"jQuery": true,
30-
"$": true
30+
"$": true,
31+
"hasWarned": true
3132
}
3233
}

test/unit/lib/util.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
var scope = typeof window === 'undefined'
2+
? global
3+
: window
4+
5+
scope.hasWarned = function (_, msg) {
6+
return _.warn.calls.argsFor(0)[0].indexOf(msg) > -1
7+
}

test/unit/runner.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<script src="lib/jasmine.js"></script>
1010
<script src="lib/jasmine-html.js"></script>
1111
<script src="lib/boot.js"></script>
12+
<script 8000 src="lib/util.js"></script>
1213
<script src="specs.js"></script>
1314
</head>
1415
<body>
Lines changed: 287 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,299 @@
1+
var Vue = require('../../../src/vue')
2+
var _ = Vue.util
3+
14
describe('Async components', function () {
25

6+
var el
7+
beforeEach(function () {
8+
el = document.createElement('div')
9+
document.body.appendChild(el)
10+
spyOn(_, 'warn')
11+
})
12+
13+
afterEach(function () {
14+
document.body.removeChild(el)
15+
})
16+
317
describe('v-component', function () {
4-
// - normal
5-
// - dynamic
6-
// - nested component caching
7-
// - invalidate pending callback on teardown
8-
// - avoid duplicate requests
18+
19+
it('normal', function (done) {
20+
var vm = new Vue({
21+
el: el,
22+
template: '<div v-component="test"></div>',
23+
components: {
24+
test: function (resolve) {
25+
setTimeout(function () {
26+
resolve({
27+
template: 'ok'
28+
})
29+
next()
30+
}, 0)
31+
}
32+
}
33+
})
34+
function next () {
35+
expect(el.textContent).toBe('ok')
36+
done()
37+
}
38+
})
39+
40+
it('dynamic', function (done) {
41+
var vm = new Vue({
42+
el: el,
43+
template: '<div v-component="{{view}}"></div>',
44+
data: {
45+
view: 'a'
46+
},
47+
components: {
48+
a: function (resolve) {
49+
setTimeout(function () {
50+
resolve({
51+
template: 'A'
52+
})
53+
step1()
54+
}, 0)
55+
},
56+
b: function (resolve) {
57+
setTimeout(function () {
58+
resolve({
59+
template: 'B'
60+
})
61+
step2()
62+
}, 0)
63+
}
64+
}
65+
})
66+
var aCalled = false
67+
function step1 () {
68+
// ensure A is resolved only once
69+
expect(aCalled).toBe(false)
70+
aCalled = true
71+
expect(el.textContent).toBe('A')
72+
vm.view = 'b'
73+
}
74+
function step2 () {
75+
expect(el.textContent).toBe('B')
76+
vm.view = 'a'
77+
_.nextTick(function () {
78+
expect(el.textContent).toBe('A')
79+
done()
80+
})
81+
}
82+
})
83+
84+
it('invalidate pending on dynamic switch', function (done) {
85+
var vm = new Vue({
86+
el: el,
87+
template: '<div v-component="{{view}}"></div>',
88+
data: {
89+
view: 'a'
90+
},
91+
components: {
92+
a: function (resolve) {
93+
setTimeout(function () {
94+
resolve({
95+
template: 'A'
96+
})
97+
step1()
98+
}, 100)
99+
},
100+
b: function (resolve) {
101+
setTimeout(function () {
102+
resolve({
103+
template: 'B'
104+
})
105+
step2()
106+
}, 200)
107+
}
108+
}
109+
})
110+
expect(el.textContent).toBe('')
111+
vm.view = 'b'
112+
function step1 () {
113+
// called after A resolves, but A should have been
114+
// invalidated so not cotrId should be set
115+
expect(vm._directives[0].ctorId).toBe(null)
116+
}
117+
function step2 () {
118+
// B should resolve successfully
119+
expect(el.textContent).toBe('B')
120+
done()
121+
}
122+
})
123+
124+
it('invalidate pending on teardown', function (done) {
125+
var vm = new Vue({
126+
el: el,
127+
template: '<div v-component="a"></div>',
128+
data: {
129+
view: 'a'
130+
},
131+
components: {
132+
a: function (resolve) {
133+
setTimeout(function () {
134+
resolve({
135+
template: 'A'
136+
})
137+
next()
138+
}, 100)
139+
}
140+
}
141+
})
142+
expect(el.textContent).toBe('')
143+
// cache directive isntance before destroy
144+
var dir = vm._directives[0]
145+
vm.$destroy()
146+
function next () {
147+
// called after A resolves, but A should have been
148+
// invalidated so not cotrId should be set
149+
expect(dir.ctorId).toBe(null)
150+
done()
151+
}
152+
})
153+
154+
it('avoid duplicate requests', function (done) {
155+
var factoryCallCount = 0
156+
var instanceCount = 0
157+
var vm = new Vue({
158+
el: el,
159+
template:
160+
'<div v-component="a"></div>' +
161+
'<div v-component="a"></div>',
162+
components: {
163+
a: factory
164+
}
165+
})
166+
function factory (resolve) {
167+
factoryCallCount++
168+
setTimeout(function () {
16 67E1 9+
resolve({
170+
template: 'A',
171+
created: function () {
172+
instanceCount++
173+
}
174+
})
175+
next()
176+
}, 0)
177+
}
178+
function next () {
179+
expect(factoryCallCount).toBe(1)
180+
expect(el.textContent).toBe('AA')
181+
expect(instanceCount).toBe(2)
182+
done()
183+
}
184+
})
9185
})
10186

11187
describe('v-repeat', function () {
12188
// - normal
13189
// - invalidate on teardown
14190
// - warn for dynamic
191+
192+
it('normal', function (done) {
193+
var vm = new Vue({
194+
el: el,
195+
template: '<div v-repeat="list" v-component="test"></div>',
196+
data: {
197+
list: [1, 2, 3]
198+
},
199+
components: {
200+
test: function (resolve) {
201+
setTimeout(function () {
202+
resolve({
203+
template: '{{$value}}'
204+
})
205+
next()
206+
}, 0)
207+
}
208+
}
209+
})
210+
function next () {
211+
expect(el.textContent).toBe('123')
212+
done()
213+
}
214+
})
215+
216+
it('only resolve once', function (done) {
217+
var vm = new Vue({
218+
el: el,
219+
template: '<div v-repeat="list" v-component="test"></div>',
220+
data: {
221+
list: [1, 2, 3]
222+
},
223+
components: {
224+
test: function (resolve) {
225+
setTimeout(function () {
226+
resolve({
227+
template: '{{$value}}'
228+
})
229+
next()
230+
}, 100)
231+
}
232+
}
233+
})
234+
// hijack realUpdate - this should only be called once
235+
// spyOn doesn't work here
236+
var update = vm._directives[0].realUpdate
237+
var callCount = 0
238+
vm._directives[0].realUpdate = function () {
239+
callCount++
240+
update.apply(this, arguments)
241+
}
242+
vm.list = [2, 3, 4]
243+
function next () {
244+
expect(el.textContent).toBe('234')
245+
expect(callCount).toBe(1)
246+
done()
247+
}
248+
})
249+
250+
it('invalidate on teardown', function (done) {
251+
var vm = new Vue({
252+
el: el,
253+
template: '<div v-repeat="list" v-component="test"></div>',
254+
data: {
255+
list: [1, 2, 3]
256+
},
257+
components: {
258+
test: function (resolve) {
259+
setTimeout(function () {
260+
resolve({
261+
template: '{{$value}}'
262+
})
263+
next()
264+
}, 0)
265+
}
266+
}
267+
})
268+
var dir = vm._directives[0]
269+
vm.$destroy()
270+
function next () {
271+
expect(el.textContent).toBe('')
272+
expect(dir.Ctor).toBe(null)
273+
done()
274+
}
275+
})
276+
277+
it('warn when used with dynamic v-repeat', function () {
278+
var vm = new Vue({
279+
el: el,
280+
template: '<div v-repeat="list" v-component="{{c}}"></div>',
281+
data: {
282+
list: [1, 2, 3],
283+
c: 'test'
284+
},
285+
components: {
286+
test: function (resolve) {
287+
setTimeout(function () {
288+
resolve({
289+
template: '{{$value}}'
290+
})
291+
}, 0)
292+
}
293+
}
294+
})
295+
expect(hasWarned(_, 'Async resolution is not supported')).toBe(true)
296+
})
15297
})
16298

17299
})

0 commit comments

Comments
 (0)
0