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+
4191 test ( 'use' , ( ) => {
5192 const PluginA : Plugin = app => app . provide ( 'foo' , 1 )
6193 const PluginB : Plugin = {
@@ -14,22 +201,20 @@ describe('api: createApp', () => {
14201 }
15202 const PluginD : any = undefined
16203
17- const Root : Component = {
204+ const { app , host , mount } = define ( {
18205 setup ( ) {
19206 const foo = inject ( 'foo' )
20207 const bar = inject ( 'bar' )
21208 return document . createTextNode ( `${ foo } ,${ bar } ` )
22209 } ,
23- }
210+ } ) . create ( )
24211
25- const app = createVaporApp ( Root )
26212 app . use ( PluginA )
27213 app . use ( PluginB , 1 , 1 )
28214 app . use ( PluginC )
29215
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` )
33218
34219 app . use ( PluginA )
35220 expect (
@@ -42,4 +227,87 @@ describe('api: createApp', () => {
42227 `function.` ,
43228 ) . toHaveBeenWarnedTimes ( 1 )
44229 } )
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+ } )
45313} )
0 commit comments