apiApp.spec.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. import {
  2. createApp,
  3. h,
  4. nodeOps,
  5. serializeInner,
  6. mockWarn,
  7. provide,
  8. inject,
  9. resolveComponent,
  10. resolveDirective,
  11. withDirectives,
  12. Plugin,
  13. ref,
  14. getCurrentInstance
  15. } from '@vue/runtime-test'
  16. describe('api: createApp', () => {
  17. mockWarn()
  18. test('mount', () => {
  19. const Comp = {
  20. props: {
  21. count: {
  22. default: 0
  23. }
  24. },
  25. setup(props: { count: number }) {
  26. return () => props.count
  27. }
  28. }
  29. const root1 = nodeOps.createElement('div')
  30. createApp().mount(Comp, root1)
  31. expect(serializeInner(root1)).toBe(`0`)
  32. // mount with props
  33. const root2 = nodeOps.createElement('div')
  34. const app2 = createApp()
  35. app2.mount(Comp, root2, { count: 1 })
  36. expect(serializeInner(root2)).toBe(`1`)
  37. // remount warning
  38. const root3 = nodeOps.createElement('div')
  39. app2.mount(Comp, root3)
  40. expect(serializeInner(root3)).toBe(``)
  41. expect(`already been mounted`).toHaveBeenWarned()
  42. })
  43. test('provide', () => {
  44. const app = createApp()
  45. app.provide('foo', 1)
  46. app.provide('bar', 2)
  47. const Root = {
  48. setup() {
  49. // test override
  50. provide('foo', 3)
  51. return () => h(Child)
  52. }
  53. }
  54. const Child = {
  55. setup() {
  56. const foo = inject('foo')
  57. const bar = inject('bar')
  58. return () => `${foo},${bar}`
  59. }
  60. }
  61. const root = nodeOps.createElement('div')
  62. app.mount(Root, root)
  63. expect(serializeInner(root)).toBe(`3,2`)
  64. })
  65. test('component', () => {
  66. const app = createApp()
  67. const FooBar = () => 'foobar!'
  68. app.component('FooBar', FooBar)
  69. expect(app.component('FooBar')).toBe(FooBar)
  70. app.component('BarBaz', () => 'barbaz!')
  71. const Root = {
  72. // local override
  73. components: {
  74. BarBaz: () => 'barbaz-local!'
  75. },
  76. setup() {
  77. // resolve in setup
  78. const FooBar = resolveComponent('foo-bar') as any
  79. return () => {
  80. // resolve in render
  81. const BarBaz = resolveComponent('bar-baz') as any
  82. return h('div', [h(FooBar), h(BarBaz)])
  83. }
  84. }
  85. }
  86. const root = nodeOps.createElement('div')
  87. app.mount(Root, root)
  88. expect(serializeInner(root)).toBe(`<div>foobar!barbaz-local!</div>`)
  89. })
  90. test('directive', () => {
  91. const app = createApp()
  92. const spy1 = jest.fn()
  93. const spy2 = jest.fn()
  94. const spy3 = jest.fn()
  95. const FooBar = { mounted: spy1 }
  96. app.directive('FooBar', FooBar)
  97. expect(app.directive('FooBar')).toBe(FooBar)
  98. app.directive('BarBaz', {
  99. mounted: spy2
  100. })
  101. const Root = {
  102. // local override
  103. directives: {
  104. BarBaz: { mounted: spy3 }
  105. },
  106. setup() {
  107. // resolve in setup
  108. const FooBar = resolveDirective('foo-bar')!
  109. return () => {
  110. // resolve in render
  111. const BarBaz = resolveDirective('bar-baz')!
  112. return withDirectives(h('div'), [[FooBar], [BarBaz]])
  113. }
  114. }
  115. }
  116. const root = nodeOps.createElement('div')
  117. app.mount(Root, root)
  118. expect(spy1).toHaveBeenCalled()
  119. expect(spy2).not.toHaveBeenCalled()
  120. expect(spy3).toHaveBeenCalled()
  121. app.directive('bind', FooBar)
  122. expect(
  123. `Do not use built-in directive ids as custom directive id: bind`
  124. ).toHaveBeenWarned()
  125. })
  126. test('mixin', () => {
  127. const calls: string[] = []
  128. const mixinA = {
  129. data() {
  130. return {
  131. a: 1
  132. }
  133. },
  134. created(this: any) {
  135. calls.push('mixinA created')
  136. expect(this.a).toBe(1)
  137. expect(this.b).toBe(2)
  138. expect(this.c).toBe(3)
  139. },
  140. mounted() {
  141. calls.push('mixinA mounted')
  142. }
  143. }
  144. const mixinB = {
  145. data() {
  146. return {
  147. b: 2
  148. }
  149. },
  150. created(this: any) {
  151. calls.push('mixinB created')
  152. expect(this.a).toBe(1)
  153. expect(this.b).toBe(2)
  154. expect(this.c).toBe(3)
  155. },
  156. mounted() {
  157. calls.push('mixinB mounted')
  158. }
  159. }
  160. const Comp = {
  161. data() {
  162. return {
  163. c: 3
  164. }
  165. },
  166. created(this: any) {
  167. calls.push('comp created')
  168. expect(this.a).toBe(1)
  169. expect(this.b).toBe(2)
  170. expect(this.c).toBe(3)
  171. },
  172. mounted() {
  173. calls.push('comp mounted')
  174. },
  175. render(this: any) {
  176. return `${this.a}${this.b}${this.c}`
  177. }
  178. }
  179. const app = createApp()
  180. app.mixin(mixinA)
  181. app.mixin(mixinB)
  182. const root = nodeOps.createElement('div')
  183. app.mount(Comp, root)
  184. expect(serializeInner(root)).toBe(`123`)
  185. expect(calls).toEqual([
  186. 'mixinA created',
  187. 'mixinB created',
  188. 'comp created',
  189. 'mixinA mounted',
  190. 'mixinB mounted',
  191. 'comp mounted'
  192. ])
  193. })
  194. test('use', () => {
  195. const PluginA: Plugin = app => app.provide('foo', 1)
  196. const PluginB: Plugin = {
  197. install: app => app.provide('bar', 2)
  198. }
  199. const app = createApp()
  200. app.use(PluginA)
  201. app.use(PluginB)
  202. const Root = {
  203. setup() {
  204. const foo = inject('foo')
  205. const bar = inject('bar')
  206. return () => `${foo},${bar}`
  207. }
  208. }
  209. const root = nodeOps.createElement('div')
  210. app.mount(Root, root)
  211. expect(serializeInner(root)).toBe(`1,2`)
  212. })
  213. test('config.errorHandler', () => {
  214. const app = createApp()
  215. const error = new Error()
  216. const count = ref(0)
  217. const handler = (app.config.errorHandler = jest.fn(
  218. (err, instance, info) => {
  219. expect(err).toBe(error)
  220. expect((instance as any).count).toBe(count.value)
  221. expect(info).toBe(`render function`)
  222. }
  223. ))
  224. const Root = {
  225. setup() {
  226. const count = ref(0)
  227. return {
  228. count
  229. }
  230. },
  231. render() {
  232. throw error
  233. }
  234. }
  235. app.mount(Root, nodeOps.createElement('div'))
  236. expect(handler).toHaveBeenCalled()
  237. })
  238. test('config.warnHandler', () => {
  239. const app = createApp()
  240. let ctx: any
  241. const handler = (app.config.warnHandler = jest.fn(
  242. (msg, instance, trace) => {
  243. expect(msg).toMatch(`Component is missing template or render function`)
  244. expect(instance).toBe(ctx.renderProxy)
  245. expect(trace).toMatch(`Hello`)
  246. }
  247. ))
  248. const Root = {
  249. name: 'Hello',
  250. setup() {
  251. ctx = getCurrentInstance()
  252. }
  253. }
  254. app.mount(Root, nodeOps.createElement('div'))
  255. expect(handler).toHaveBeenCalledTimes(1)
  256. })
  257. describe('config.isNativeTag', () => {
  258. const isNativeTag = jest.fn(tag => tag === 'div')
  259. test('Component.name', () => {
  260. const app = createApp()
  261. Object.defineProperty(app.config, 'isNativeTag', {
  262. value: isNativeTag,
  263. writable: false
  264. })
  265. const Root = {
  266. name: 'div',
  267. setup() {
  268. return {
  269. count: ref(0)
  270. }
  271. },
  272. render() {
  273. return null
  274. }
  275. }
  276. app.mount(Root, nodeOps.createElement('div'))
  277. expect(
  278. `Do not use built-in or reserved HTML elements as component id: div`
  279. ).toHaveBeenWarned()
  280. })
  281. test('Component.components', () => {
  282. const app = createApp()
  283. Object.defineProperty(app.config, 'isNativeTag', {
  284. value: isNativeTag,
  285. writable: false
  286. })
  287. const Root = {
  288. components: {
  289. div: () => 'div'
  290. },
  291. setup() {
  292. return {
  293. count: ref(0)
  294. }
  295. },
  296. render() {
  297. return null
  298. }
  299. }
  300. app.mount(Root, nodeOps.createElement('div'))
  301. expect(
  302. `Do not use built-in or reserved HTML elements as component id: div`
  303. ).toHaveBeenWarned()
  304. })
  305. test('Component.directives', () => {
  306. const app = createApp()
  307. Object.defineProperty(app.config, 'isNativeTag', {
  308. value: isNativeTag,
  309. writable: false
  310. })
  311. const Root = {
  312. directives: {
  313. bind: () => {}
  314. },
  315. setup() {
  316. return {
  317. count: ref(0)
  318. }
  319. },
  320. render() {
  321. return null
  322. }
  323. }
  324. app.mount(Root, nodeOps.createElement('div'))
  325. expect(
  326. `Do not use built-in directive ids as custom directive id: bind`
  327. ).toHaveBeenWarned()
  328. })
  329. test('register using app.component', () => {
  330. const app = createApp()
  331. Object.defineProperty(app.config, 'isNativeTag', {
  332. value: isNativeTag,
  333. writable: false
  334. })
  335. const Root = {
  336. setup() {
  337. return {
  338. count: ref(0)
  339. }
  340. },
  341. render() {
  342. return null
  343. }
  344. }
  345. app.component('div', () => 'div')
  346. app.mount(Root, nodeOps.createElement('div'))
  347. expect(
  348. `Do not use built-in or reserved HTML elements as component id: div`
  349. ).toHaveBeenWarned()
  350. })
  351. })
  352. })