1
- import { type Component , type Plugin , createVaporApp , inject } from '../src'
2
- ; ``
3
- describe ( 'api: createApp' , ( ) => {
1
+ import {
2
+ type ComponentInternalInstance ,
3
+ type Plugin ,
4
+ createComponent ,
5
+ createTextNode ,
6
+ createVaporApp ,
7
+ defineComponent ,
8
+ getCurrentInstance ,
9
+ inject ,
10
+ provide ,
11
+ resolveComponent ,
12
+ resolveDirective ,
13
+ withDirectives ,
14
+ } from '../src'
15
+ import { warn } from '../src/warning'
16
+ import { makeRender } from './_utils'
17
+
18
+ const define = makeRender ( )
19
+
20
+ describe ( 'api: createVaporApp' , ( ) => {
21
+ test ( 'mount' , ( ) => {
22
+ const Comp = defineComponent ( {
23
+ props : {
24
+ count : { default : 0 } ,
25
+ } ,
26
+ setup ( props ) {
27
+ return createTextNode ( ( ) => [ props . count ] )
28
+ } ,
29
+ } )
30
+
31
+ const root1 = document . createElement ( 'div' )
32
+ createVaporApp ( Comp ) . mount ( root1 )
33
+ expect ( root1 . innerHTML ) . toBe ( `0` )
34
+ //#5571 mount multiple apps to the same host element
35
+ createVaporApp ( Comp ) . mount ( root1 )
36
+ expect (
37
+ `There is already an app instance mounted on the host container` ,
38
+ ) . toHaveBeenWarned ( )
39
+
40
+ // mount with props
41
+ const root2 = document . createElement ( 'div' )
42
+ const app2 = createVaporApp ( Comp , { count : ( ) => 1 } )
43
+ app2 . mount ( root2 )
44
+ expect ( root2 . innerHTML ) . toBe ( `1` )
45
+
46
+ // remount warning
47
+ const root3 = document . createElement ( 'div' )
48
+ app2 . mount ( root3 )
49
+ expect ( root3 . innerHTML ) . toBe ( `` )
50
+ expect ( `already been mounted` ) . toHaveBeenWarned ( )
51
+ } )
52
+
53
+ test ( 'unmount' , ( ) => {
54
+ const Comp = defineComponent ( {
55
+ props : {
56
+ count : { default : 0 } ,
57
+ } ,
58
+ setup ( props ) {
59
+ return createTextNode ( ( ) => [ props . count ] )
60
+ } ,
61
+ } )
62
+
63
+ const root = document . createElement ( 'div' )
64
+ const app = createVaporApp ( Comp )
65
+
66
+ // warning
67
+ app . unmount ( )
68
+ expect ( `that is not mounted` ) . toHaveBeenWarned ( )
69
+
70
+ app . mount ( root )
71
+
72
+ app . unmount ( )
73
+ expect ( root . innerHTML ) . toBe ( `` )
74
+ } )
75
+
76
+ test ( 'provide' , ( ) => {
77
+ const Root = define ( {
78
+ setup ( ) {
79
+ // test override
80
+ provide ( 'foo' , 3 )
81
+ return createComponent ( Child )
82
+ } ,
83
+ } )
84
+
85
+ const Child = defineComponent ( {
86
+ setup ( ) {
87
+ const foo = inject ( 'foo' )
88
+ const bar = inject ( 'bar' )
89
+ try {
90
+ inject ( '__proto__' )
91
+ } catch ( e : any ) { }
92
+ return createTextNode ( ( ) => [ `${ foo } ,${ bar } ` ] )
93
+ } ,
94
+ } )
95
+
96
+ const { app, mount, create, host } = Root . create ( null )
97
+ app . provide ( 'foo' , 1 )
98
+ app . provide ( 'bar' , 2 )
99
+ mount ( )
100
+ expect ( host . innerHTML ) . toBe ( `3,2` )
101
+ expect ( '[Vue warn]: injection "__proto__" not found.' ) . toHaveBeenWarned ( )
102
+
103
+ const { app : app2 } = create ( )
104
+ app2 . provide ( 'bar' , 1 )
105
+ app2 . provide ( 'bar' , 2 )
106
+ expect ( `App already provides property with key "bar".` ) . toHaveBeenWarned ( )
107
+ } )
108
+
109
+ test ( 'runWithContext' , ( ) => {
110
+ const { app } = define ( {
111
+ setup ( ) {
112
+ provide ( 'foo' , 'should not be seen' )
113
+ return document . createElement ( 'div' )
114
+ } ,
115
+ } ) . create ( )
116
+ app . provide ( 'foo' , 1 )
117
+
118
+ expect ( app . runWithContext ( ( ) => inject ( 'foo' ) ) ) . toBe ( 1 )
119
+
120
+ expect (
121
+ app . runWithContext ( ( ) => {
122
+ app . runWithContext ( ( ) => { } )
123
+ return inject ( 'foo' )
124
+ } ) ,
125
+ ) . toBe ( 1 )
126
+
127
+ // ensure the context is restored
128
+ inject ( 'foo' )
129
+ expect ( 'inject() can only be used inside setup' ) . toHaveBeenWarned ( )
130
+ } )
131
+
132
+ test ( 'component' , ( ) => {
133
+ const { app, mount, host } = define ( {
134
+ setup ( ) {
135
+ const FooBar = resolveComponent ( 'foo-bar' )
136
+ const BarBaz = resolveComponent ( 'bar-baz' )
137
+ // @ts -expect-error TODO support string
138
+ return [ createComponent ( FooBar ) , createComponent ( BarBaz ) ]
139
+ } ,
140
+ } ) . create ( )
141
+
142
+ const FooBar = ( ) => createTextNode ( [ 'foobar!' ] )
143
+ app . component ( 'FooBar' , FooBar )
144
+ expect ( app . component ( 'FooBar' ) ) . toBe ( FooBar )
145
+
146
+ app . component ( 'BarBaz' , ( ) => createTextNode ( [ 'barbaz!' ] ) )
147
+ app . component ( 'BarBaz' , ( ) => createTextNode ( [ 'barbaz!' ] ) )
148
+ expect (
149
+ 'Component "BarBaz" has already been registered in target app.' ,
150
+ ) . toHaveBeenWarnedTimes ( 1 )
151
+
152
+ mount ( )
153
+ expect ( host . innerHTML ) . toBe ( `foobar!barbaz!` )
154
+ } )
155
+
156
+ test ( 'directive' , ( ) => {
157
+ const spy1 = vi . fn ( )
158
+ const spy2 = vi . fn ( )
159
+
160
+ const { app, mount } = define ( {
161
+ setup ( ) {
162
+ const FooBar = resolveDirective ( 'foo-bar' )
163
+ const BarBaz = resolveDirective ( 'bar-baz' )
164
+ return withDirectives ( document . createElement ( 'div' ) , [
165
+ [ FooBar ] ,
166
+ [ BarBaz ] ,
167
+ ] )
168
+ } ,
169
+ } ) . create ( )
170
+
171
+ const FooBar = { mounted : spy1 }
172
+ app . directive ( 'FooBar' , FooBar )
173
+ expect ( app . directive ( 'FooBar' ) ) . toBe ( FooBar )
174
+
175
+ app . directive ( 'BarBaz' , { mounted : spy2 } )
176
+ app . directive ( 'BarBaz' , { mounted : spy2 } )
177
+ expect (
178
+ 'Directive "BarBaz" has already been registered in target app.' ,
179
+ ) . toHaveBeenWarnedTimes ( 1 )
180
+
181
+ mount ( )
182
+ expect ( spy1 ) . toHaveBeenCalled ( )
183
+ expect ( spy2 ) . toHaveBeenCalled ( )
184
+
185
+ app . directive ( 'bind' , FooBar )
186
+ expect (
187
+ `Do not use built-in directive ids as custom directive id: bind` ,
188
+ ) . toHaveBeenWarned ( )
189
+ } )
190
+
4
191
test ( 'use' , ( ) => {
5
192
const PluginA : Plugin = app => app . provide ( 'foo' , 1 )
6
193
const PluginB : Plugin = {
@@ -14,22 +201,20 @@ describe('api: createApp', () => {
14
201
}
15
202
const PluginD : any = undefined
16
203
17
- const Root : Component = {
204
+ const { app , host , mount } = define ( {
18
205
setup ( ) {
19
206
const foo = inject ( 'foo' )
20
207
const bar = inject ( 'bar' )
21
208
return document . createTextNode ( `${ foo } ,${ bar } ` )
22
209
} ,
23
- }
210
+ } ) . create ( )
24
211
25
- const app = createVaporApp ( Root )
26
212
app . use ( PluginA )
27
213
app . use ( PluginB , 1 , 1 )
28
214
app . use ( PluginC )
29
215
30
- const root = document . createElement ( 'div' )
31
- app . mount ( root )
32
- expect ( root . innerHTML ) . toBe ( `1,2` )
216
+ mount ( )
217
+ expect ( host . innerHTML ) . toBe ( `1,2` )
33
218
34
219
app . use ( PluginA )
35
220
expect (
@@ -42,4 +227,87 @@ describe('api: createApp', () => {
42
227
`function.` ,
43
228
) . toHaveBeenWarnedTimes ( 1 )
44
229
} )
230
+
231
+ test ( 'config.errorHandler' , ( ) => {
232
+ const error = new Error ( )
233
+ let instance : ComponentInternalInstance
234
+
235
+ const handler = vi . fn ( ( err , _instance , info ) => {
236
+ expect ( err ) . toBe ( error )
237
+ expect ( _instance ) . toBe ( instance )
238
+ expect ( info ) . toBe ( `render function` )
239
+ } )
240
+
241
+ const { app, mount } = define ( {
242
+ setup ( ) {
243
+ instance = getCurrentInstance ( ) !
244
+ } ,
245
+ render ( ) {
246
+ throw error
247
+ } ,
248
+ } ) . create ( )
249
+ app . config . errorHandler = handler
250
+ mount ( )
251
+ expect ( handler ) . toHaveBeenCalled ( )
252
+ } )
253
+
254
+ test ( 'config.warnHandler' , ( ) => {
255
+ let instance : ComponentInternalInstance
256
+
257
+ const handler = vi . fn ( ( msg , _instance , trace ) => {
258
+ expect ( msg ) . toMatch ( `warn message` )
259
+ expect ( _instance ) . toBe ( instance )
260
+ expect ( trace ) . toMatch ( `Hello` )
261
+ } )
262
+
263
+ const { app, mount } = define ( {
264
+ name : 'Hello' ,
265
+ setup ( ) {
266
+ instance = getCurrentInstance ( ) !
267
+ warn ( 'warn message' )
268
+ } ,
269
+ } ) . create ( )
270
+
271
+ app . config . warnHandler = handler
272
+ mount ( )
273
+ expect ( handler ) . toHaveBeenCalledTimes ( 1 )
274
+ } )
275
+
276
+ describe ( 'config.isNativeTag' , ( ) => {
277
+ const isNativeTag = vi . fn ( tag => tag === 'div' )
278
+
279
+ test ( 'Component.name' , ( ) => {
280
+ const { app, mount } = define ( {
281
+ name : 'div' ,
282
+ render ( ) : any { } ,
283
+ } ) . create ( )
284
+
285
+ Object . defineProperty ( app . config , 'isNativeTag' , {
286
+ value : isNativeTag ,
287
+ writable : false ,
288
+ } )
289
+
290
+ mount ( )
291
+ expect (
292
+ `Do not use built-in or reserved HTML elements as component id: div` ,
293
+ ) . toHaveBeenWarned ( )
294
+ } )
295
+
296
+ test ( 'register using app.component' , ( ) => {
297
+ const { app, mount } = define ( {
298
+ render ( ) : any { } ,
299
+ } ) . create ( )
300
+
301
+ Object . defineProperty ( app . config , 'isNativeTag' , {
302
+ value : isNativeTag ,
303
+ writable : false ,
304
+ } )
305
+
306
+ app . component ( 'div' , ( ) => createTextNode ( [ 'div' ] ) )
307
+ mount ( )
308
+ expect (
309
+ `Do not use built-in or reserved HTML elements as component id: div` ,
310
+ ) . toHaveBeenWarned ( )
311
+ } )
312
+ } )
45
313
} )
0 commit comments