options_spec.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. var _ = require('src/util')
  2. var Vue = require('src')
  3. var merge = _.mergeOptions
  4. var resolveAsset = _.resolveAsset
  5. describe('Util - Option merging', function () {
  6. it('default strat', function () {
  7. // child undefined
  8. var res = merge({replace: true}, {}).replace
  9. expect(res).toBe(true)
  10. // child overwrite
  11. res = merge({replace: true}, {replace: false}).replace
  12. expect(res).toBe(false)
  13. })
  14. it('hooks', function () {
  15. var fn1 = function () {}
  16. var fn2 = function () {}
  17. var res
  18. // parent undefined
  19. res = merge({}, {created: fn1}).created
  20. expect(Array.isArray(res)).toBe(true)
  21. expect(res.length).toBe(1)
  22. expect(res[0]).toBe(fn1)
  23. // child undefined
  24. res = merge({created: [fn1]}, {}).created
  25. expect(Array.isArray(res)).toBe(true)
  26. expect(res.length).toBe(1)
  27. expect(res[0]).toBe(fn1)
  28. // both defined
  29. res = merge({created: [fn1]}, {created: fn2}).created
  30. expect(Array.isArray(res)).toBe(true)
  31. expect(res.length).toBe(2)
  32. expect(res[0]).toBe(fn1)
  33. expect(res[1]).toBe(fn2)
  34. })
  35. it('events', function () {
  36. // no parent
  37. res = merge({}, {events: 1})
  38. expect(res.events).toBe(1)
  39. // no child
  40. res = merge({events: 1}, {})
  41. expect(res.events).toBe(1)
  42. var fn1 = function () {}
  43. var fn2 = function () {}
  44. var fn3 = function () {}
  45. var parent = {
  46. events: {
  47. 'fn1': [fn1, fn2],
  48. 'fn2': fn2
  49. }
  50. }
  51. var child = {
  52. events: {
  53. 'fn1': fn3,
  54. 'fn2': fn3,
  55. 'fn3': fn3
  56. }
  57. }
  58. var res = merge(parent, child).events
  59. assertRes(res.fn1, [fn1, fn2, fn3])
  60. assertRes(res.fn2, [fn2, fn3])
  61. assertRes(res.fn3, [fn3])
  62. function assertRes (res, expected) {
  63. expect(Array.isArray(res)).toBe(true)
  64. expect(res.length).toBe(expected.length)
  65. var i = expected.length
  66. while (i--) {
  67. expect(res[i]).toBe(expected[i])
  68. }
  69. }
  70. })
  71. it('normal object hashes', function () {
  72. var fn1 = function () {}
  73. var fn2 = function () {}
  74. var res
  75. // parent undefined
  76. res = merge({}, {methods: {test: fn1}}).methods
  77. expect(res.test).toBe(fn1)
  78. // child undefined
  79. res = merge({methods: {test: fn1}}, {}).methods
  80. expect(res.test).toBe(fn1)
  81. // both defined
  82. var parent = {methods: {test: fn1}}
  83. res = merge(parent, {methods: {test2: fn2}}).methods
  84. expect(res.test).toBe(fn1)
  85. expect(res.test2).toBe(fn2)
  86. })
  87. it('assets', function () {
  88. var asset1 = {}
  89. var asset2 = {}
  90. var res = merge(
  91. { directives: { a: asset1 }},
  92. { directives: { b: asset2 }}
  93. ).directives
  94. expect(res.a).toBe(asset1)
  95. expect(res.b).toBe(asset2)
  96. })
  97. it('props', function () {
  98. var res = merge({
  99. props: {
  100. a: null,
  101. d: null
  102. }
  103. }, {
  104. props: {
  105. a: { required: true },
  106. b: Boolean,
  107. c: { type: Array }
  108. }
  109. })
  110. expect(typeof res.props.a).toBe('object')
  111. expect(res.props.a.required).toBe(true)
  112. expect(typeof res.props.b).toBe('object')
  113. expect(res.props.b.type).toBe(Boolean)
  114. expect(typeof res.props.c).toBe('object')
  115. expect(res.props.c.type).toBe(Array)
  116. expect(res.props.d).toBe(null)
  117. // check array syntax
  118. res = merge({
  119. props: {
  120. b: null
  121. }
  122. }, {
  123. props: ['a']
  124. })
  125. expect(res.props.a).toBe(null)
  126. expect(res.props.b).toBe(null)
  127. })
  128. it('guard components', function () {
  129. var res = merge({
  130. components: null
  131. }, {
  132. components: {
  133. test: { template: 'foo' }
  134. }
  135. })
  136. expect(typeof res.components.test).toBe('function')
  137. expect(res.components.test.super).toBe(Vue)
  138. })
  139. it('guard components warn built-in elements', function () {
  140. merge({
  141. components: null
  142. }, {
  143. components: {
  144. a: { template: 'foo' }
  145. }
  146. })
  147. expect('Do not use built-in or reserved HTML elements as component id: a').toHaveBeenWarned()
  148. merge({
  149. components: null
  150. }, {
  151. components: {
  152. slot: { template: 'foo' }
  153. }
  154. })
  155. expect('Do not use built-in or reserved HTML elements as component id: slot').toHaveBeenWarned()
  156. })
  157. it('should ignore non-function el & data in class merge', function () {
  158. var res = merge({}, {el: 1, data: 2})
  159. expect(res.el).toBeUndefined()
  160. expect(res.data).toBeUndefined()
  161. })
  162. it('class el merge', function () {
  163. function fn1 () {}
  164. function fn2 () {}
  165. var res = merge({ el: fn1 }, { el: fn2 })
  166. expect(res.el).toBe(fn2)
  167. })
  168. it('class data merge', function () {
  169. function fn1 () {
  170. return { a: 1, c: 4, d: { e: 1 }}
  171. }
  172. function fn2 () {
  173. return { a: 2, b: 3, d: { f: 2 }}
  174. }
  175. // both present
  176. var res = merge({ data: fn1 }, { data: fn2 }).data()
  177. expect(res.a).toBe(2)
  178. expect(res.b).toBe(3)
  179. expect(res.c).toBe(4)
  180. expect(res.d.e).toBe(1)
  181. expect(res.d.f).toBe(2)
  182. // only parent
  183. res = merge({ data: fn1 }, {}).data()
  184. expect(res.a).toBe(1)
  185. expect(res.b).toBeUndefined()
  186. expect(res.c).toBe(4)
  187. expect(res.d.e).toBe(1)
  188. expect(res.d.f).toBeUndefined()
  189. })
  190. it('instanace el merge', function () {
  191. var vm = {} // mock vm presence
  192. function fn1 () {
  193. expect(this).toBe(vm)
  194. return 1
  195. }
  196. function fn2 () {
  197. expect(this).toBe(vm)
  198. return 2
  199. }
  200. // both functions
  201. var res = merge({ el: fn1 }, { el: fn2 }, vm)
  202. expect(res.el).toBe(2)
  203. // direct instance el
  204. res = merge({ el: fn1 }, { el: 2 }, vm)
  205. expect(res.el).toBe(2)
  206. // no parent
  207. res = merge({}, { el: 2 }, vm)
  208. expect(res.el).toBe(2)
  209. // no child
  210. res = merge({ el: fn1 }, {}, vm)
  211. expect(res.el).toBe(1)
  212. })
  213. it('instance data merge with no instance data', function () {
  214. var res = merge(
  215. {data: function () {
  216. return { a: 1}
  217. }},
  218. {}, // no instance data
  219. {} // mock vm presence
  220. )
  221. expect(res.data().a).toBe(1)
  222. })
  223. it('instance data merge with default data function', function () {
  224. var vm = {} // mock vm presence
  225. var res = merge(
  226. // component default
  227. { data: function () {
  228. expect(this).toBe(vm)
  229. return {
  230. a: 1,
  231. b: 2
  232. }
  233. }},
  234. { data: { a: 2 }}, // instance data
  235. vm
  236. )
  237. var data = res.data()
  238. expect(data.a).toBe(2)
  239. expect(data.b).toBe(2)
  240. })
  241. it('already observed instance data merge with default data', function () {
  242. var observe = require('src/observer').observe
  243. var instanceData = { a: 123 }
  244. // observe it
  245. observe(instanceData)
  246. var res = merge(
  247. {
  248. data: function () {
  249. return { b: 234 }
  250. }
  251. },
  252. {
  253. data: instanceData
  254. },
  255. {}
  256. )
  257. var data = res.data()
  258. expect(data.a).toBe(123)
  259. expect(data.b).toBe(234)
  260. expect(Object.getOwnPropertyDescriptor(data, 'b').get).toBeTruthy()
  261. })
  262. it('extends', function () {
  263. var f1 = function () {}
  264. var f2 = function () {}
  265. var f3 = function () {}
  266. var componentA = Vue.extend({ template: 'foo', methods: { f1: f1, f2: function () {} }})
  267. var componentB = { extends: componentA, methods: { f2: f2 }}
  268. var componentC = { extends: componentB, template: 'bar', methods: { f3: f3 }}
  269. var res = merge({}, componentC)
  270. expect(res.template).toBe('bar')
  271. expect(res.methods.f1).toBe(f1)
  272. expect(res.methods.f2).toBe(f2)
  273. expect(res.methods.f3).toBe(f3)
  274. })
  275. it('mixins', function () {
  276. var a = {}
  277. var b = {}
  278. var c = {}
  279. var d = {}
  280. var f1 = function () {}
  281. var f2 = function () {}
  282. var f3 = function () {}
  283. var f4 = function () {}
  284. var mixinA = { a: 1, directives: { a: a }, created: f2 }
  285. var mixinB = { b: 1, directives: { b: b }, created: f3 }
  286. var res = merge(
  287. { a: 2, directives: { c: c }, created: [f1] },
  288. { directives: { d: d }, mixins: [mixinA, mixinB], created: f4 }
  289. )
  290. expect(res.a).toBe(1)
  291. expect(res.b).toBe(1)
  292. expect(res.directives.a).toBe(a)
  293. expect(res.directives.b).toBe(b)
  294. expect(res.directives.c).toBe(c)
  295. expect(res.directives.d).toBe(d)
  296. expect(res.created[0]).toBe(f1)
  297. expect(res.created[1]).toBe(f2)
  298. expect(res.created[2]).toBe(f3)
  299. expect(res.created[3]).toBe(f4)
  300. })
  301. it('Array assets', function () {
  302. var a = {
  303. components: {
  304. a: Vue.extend({})
  305. }
  306. }
  307. var b = {
  308. components: [{ name: 'b' }]
  309. }
  310. var res = merge(a, b)
  311. expect(res.components.a).toBe(a.components.a)
  312. // b.components is guarded and converted to object hash
  313. expect(res.components.b).toBeTruthy()
  314. expect(res.components.b).toBe(b.components.b)
  315. })
  316. it('warn Array assets without id', function () {
  317. var a = {
  318. components: {
  319. a: Vue.extend({})
  320. }
  321. }
  322. var b = {
  323. components: [{}]
  324. }
  325. merge(a, b)
  326. expect('must provide a "name" or "id" field').toHaveBeenWarned()
  327. })
  328. it('warn Array async component without id', function () {
  329. var a = {
  330. components: {
  331. a: Vue.extend({})
  332. }
  333. }
  334. var b = {
  335. components: [function () {}]
  336. }
  337. merge(a, b)
  338. expect('must provide a "name" or "id" field').toHaveBeenWarned()
  339. })
  340. })
  341. describe('Util - Option resolveAsset', function () {
  342. var vm
  343. beforeEach(function () {
  344. vm = new Vue({
  345. data: {},
  346. components: {
  347. 'hyphenated-component': {
  348. template: 'foo'
  349. },
  350. camelCasedComponent: {
  351. template: 'bar'
  352. },
  353. PascalCasedComponent: {
  354. template: 'baz'
  355. }
  356. }
  357. })
  358. })
  359. it('resolves', function () {
  360. expect(resolveAsset(vm.$options, 'components', 'hyphenated-component')).toBeTruthy()
  361. expect(resolveAsset(vm.$options, 'components', 'camel-cased-component')).toBeTruthy()
  362. expect(resolveAsset(vm.$options, 'components', 'pascal-cased-component')).toBeTruthy()
  363. })
  364. })