options_spec.js 9.2 KB

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