component-async.spec.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. import Vue from 'vue'
  2. import { Promise } from 'es6-promise'
  3. describe('Component async', () => {
  4. it('normal', done => {
  5. const vm = new Vue({
  6. template: '<div><test></test></div>',
  7. components: {
  8. test: (resolve) => {
  9. setTimeout(() => {
  10. resolve({
  11. template: '<div>hi</div>'
  12. })
  13. // wait for parent update
  14. Vue.nextTick(next)
  15. }, 0)
  16. }
  17. }
  18. }).$mount()
  19. expect(vm.$el.innerHTML).toBe('<!---->')
  20. expect(vm.$children.length).toBe(0)
  21. function next () {
  22. expect(vm.$el.innerHTML).toBe('<div>hi</div>')
  23. expect(vm.$children.length).toBe(1)
  24. done()
  25. }
  26. })
  27. it('resolve ES module default', done => {
  28. const vm = new Vue({
  29. template: '<div><test></test></div>',
  30. components: {
  31. test: (resolve) => {
  32. setTimeout(() => {
  33. resolve({
  34. __esModule: true,
  35. default: {
  36. template: '<div>hi</div>'
  37. }
  38. })
  39. // wait for parent update
  40. Vue.nextTick(next)
  41. }, 0)
  42. }
  43. }
  44. }).$mount()
  45. expect(vm.$el.innerHTML).toBe('<!---->')
  46. expect(vm.$children.length).toBe(0)
  47. function next () {
  48. expect(vm.$el.innerHTML).toBe('<div>hi</div>')
  49. expect(vm.$children.length).toBe(1)
  50. done()
  51. }
  52. })
  53. it('as root', done => {
  54. const vm = new Vue({
  55. template: '<test></test>',
  56. components: {
  57. test: resolve => {
  58. setTimeout(() => {
  59. resolve({
  60. template: '<div>hi</div>'
  61. })
  62. // wait for parent update
  63. Vue.nextTick(next)
  64. }, 0)
  65. }
  66. }
  67. }).$mount()
  68. expect(vm.$el.nodeType).toBe(8)
  69. expect(vm.$children.length).toBe(0)
  70. function next () {
  71. expect(vm.$el.nodeType).toBe(1)
  72. expect(vm.$el.outerHTML).toBe('<div>hi</div>')
  73. expect(vm.$children.length).toBe(1)
  74. done()
  75. }
  76. })
  77. it('dynamic', done => {
  78. var vm = new Vue({
  79. template: '<component :is="view"></component>',
  80. data: {
  81. view: 'view-a'
  82. },
  83. components: {
  84. 'view-a': resolve => {
  85. setTimeout(() => {
  86. resolve({
  87. template: '<div>A</div>'
  88. })
  89. Vue.nextTick(step1)
  90. }, 0)
  91. },
  92. 'view-b': resolve => {
  93. setTimeout(() => {
  94. resolve({
  95. template: '<p>B</p>'
  96. })
  97. Vue.nextTick(step2)
  98. }, 0)
  99. }
  100. }
  101. }).$mount()
  102. var aCalled = false
  103. function step1 () {
  104. // ensure A is resolved only once
  105. expect(aCalled).toBe(false)
  106. aCalled = true
  107. expect(vm.$el.tagName).toBe('DIV')
  108. expect(vm.$el.textContent).toBe('A')
  109. vm.view = 'view-b'
  110. }
  111. function step2 () {
  112. expect(vm.$el.tagName).toBe('P')
  113. expect(vm.$el.textContent).toBe('B')
  114. vm.view = 'view-a'
  115. waitForUpdate(function () {
  116. expect(vm.$el.tagName).toBe('DIV')
  117. expect(vm.$el.textContent).toBe('A')
  118. }).then(done)
  119. }
  120. })
  121. it('warn reject', () => {
  122. new Vue({
  123. template: '<test></test>',
  124. components: {
  125. test: (resolve, reject) => {
  126. reject('nooooo')
  127. }
  128. }
  129. }).$mount()
  130. expect('Reason: nooooo').toHaveBeenWarned()
  131. })
  132. it('with v-for', done => {
  133. const vm = new Vue({
  134. template: '<div><test v-for="n in list" :key="n" :n="n"></test></div>',
  135. data: {
  136. list: [1, 2, 3]
  137. },
  138. components: {
  139. test: resolve => {
  140. setTimeout(() => {
  141. resolve({
  142. props: ['n'],
  143. template: '<div>{{n}}</div>'
  144. })
  145. Vue.nextTick(next)
  146. }, 0)
  147. }
  148. }
  149. }).$mount()
  150. function next () {
  151. expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
  152. done()
  153. }
  154. })
  155. it('returning Promise', done => {
  156. const vm = new Vue({
  157. template: '<div><test></test></div>',
  158. components: {
  159. test: () => {
  160. return new Promise(resolve => {
  161. setTimeout(() => {
  162. resolve({
  163. template: '<div>hi</div>'
  164. })
  165. // wait for promise resolve and then parent update
  166. Promise.resolve().then(() => {
  167. Vue.nextTick(next)
  168. })
  169. }, 0)
  170. })
  171. }
  172. }
  173. }).$mount()
  174. expect(vm.$el.innerHTML).toBe('<!---->')
  175. expect(vm.$children.length).toBe(0)
  176. function next () {
  177. expect(vm.$el.innerHTML).toBe('<div>hi</div>')
  178. expect(vm.$children.length).toBe(1)
  179. done()
  180. }
  181. })
  182. describe('loading/error/timeout', () => {
  183. it('with loading component', done => {
  184. const vm = new Vue({
  185. template: `<div><test/></div>`,
  186. components: {
  187. test: () => ({
  188. component: new Promise(resolve => {
  189. setTimeout(() => {
  190. resolve({ template: '<div>hi</div>' })
  191. // wait for promise resolve and then parent update
  192. Promise.resolve().then(() => {
  193. Vue.nextTick(next)
  194. })
  195. }, 50)
  196. }),
  197. loading: { template: `<div>loading</div>` },
  198. delay: 1
  199. })
  200. }
  201. }).$mount()
  202. expect(vm.$el.innerHTML).toBe('<!---->')
  203. let loadingAsserted = false
  204. setTimeout(() => {
  205. Vue.nextTick(() => {
  206. loadingAsserted = true
  207. expect(vm.$el.textContent).toBe('loading')
  208. })
  209. }, 1)
  210. function next () {
  211. expect(loadingAsserted).toBe(true)
  212. expect(vm.$el.textContent).toBe('hi')
  213. done()
  214. }
  215. })
  216. it('with loading component (0 delay)', done => {
  217. const vm = new Vue({
  218. template: `<div><test/></div>`,
  219. components: {
  220. test: () => ({
  221. component: new Promise(resolve => {
  222. setTimeout(() => {
  223. resolve({ template: '<div>hi</div>' })
  224. // wait for promise resolve and then parent update
  225. Promise.resolve().then(() => {
  226. Vue.nextTick(next)
  227. })
  228. }, 50)
  229. }),
  230. loading: { template: `<div>loading</div>` },
  231. delay: 0
  232. })
  233. }
  234. }).$mount()
  235. expect(vm.$el.textContent).toBe('loading')
  236. function next () {
  237. expect(vm.$el.textContent).toBe('hi')
  238. done()
  239. }
  240. })
  241. it('with error component', done => {
  242. const vm = new Vue({
  243. template: `<div><test/></div>`,
  244. components: {
  245. test: () => ({
  246. component: new Promise((resolve, reject) => {
  247. setTimeout(() => {
  248. reject()
  249. // wait for promise resolve and then parent update
  250. Promise.resolve().then(() => {
  251. Vue.nextTick(next)
  252. })
  253. }, 50)
  254. }),
  255. loading: { template: `<div>loading</div>` },
  256. error: { template: `<div>error</div>` },
  257. delay: 0
  258. })
  259. }
  260. }).$mount()
  261. expect(vm.$el.textContent).toBe('loading')
  262. function next () {
  263. expect(`Failed to resolve async component`).toHaveBeenWarned()
  264. expect(vm.$el.textContent).toBe('error')
  265. done()
  266. }
  267. })
  268. it('with error component + timeout', done => {
  269. const vm = new Vue({
  270. template: `<div><test/></div>`,
  271. components: {
  272. test: () => ({
  273. component: new Promise((resolve, reject) => {
  274. setTimeout(() => {
  275. resolve({ template: '<div>hi</div>' })
  276. // wait for promise resolve and then parent update
  277. Promise.resolve().then(() => {
  278. Vue.nextTick(next)
  279. })
  280. }, 50)
  281. }),
  282. loading: { template: `<div>loading</div>` },
  283. error: { template: `<div>error</div>` },
  284. delay: 0,
  285. timeout: 1
  286. })
  287. }
  288. }).$mount()
  289. expect(vm.$el.textContent).toBe('loading')
  290. setTimeout(() => {
  291. Vue.nextTick(() => {
  292. expect(`Failed to resolve async component`).toHaveBeenWarned()
  293. expect(vm.$el.textContent).toBe('error')
  294. })
  295. }, 1)
  296. function next () {
  297. expect(vm.$el.textContent).toBe('error') // late resolve ignored
  298. done()
  299. }
  300. })
  301. it('should not trigger timeout if resolved', done => {
  302. const vm = new Vue({
  303. template: `<div><test/></div>`,
  304. components: {
  305. test: () => ({
  306. component: new Promise((resolve, reject) => {
  307. setTimeout(() => {
  308. resolve({ template: '<div>hi</div>' })
  309. }, 10)
  310. }),
  311. error: { template: `<div>error</div>` },
  312. timeout: 20
  313. })
  314. }
  315. }).$mount()
  316. setTimeout(() => {
  317. expect(vm.$el.textContent).toBe('hi')
  318. expect(`Failed to resolve async component`).not.toHaveBeenWarned()
  319. done()
  320. }, 50)
  321. })
  322. })
  323. })