model-select.spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. import Vue from 'vue'
  2. import { looseEqual } from 'shared/util'
  3. /**
  4. * setting <select>'s value in IE9 doesn't work
  5. * we have to manually loop through the options
  6. */
  7. function updateSelect (el, value) {
  8. var options = el.options
  9. var i = options.length
  10. while (i--) {
  11. if (looseEqual(getValue(options[i]), value)) {
  12. options[i].selected = true
  13. break
  14. }
  15. }
  16. }
  17. function getValue (option) {
  18. return '_value' in option
  19. ? option._value
  20. : option.value || option.text
  21. }
  22. describe('Directive v-model select', () => {
  23. it('should work', done => {
  24. const vm = new Vue({
  25. data: {
  26. test: 'b'
  27. },
  28. template:
  29. '<select v-model="test">' +
  30. '<option>a</option>' +
  31. '<option>b</option>' +
  32. '<option>c</option>' +
  33. '</select>'
  34. }).$mount()
  35. document.body.appendChild(vm.$el)
  36. expect(vm.test).toBe('b')
  37. expect(vm.$el.value).toBe('b')
  38. expect(vm.$el.childNodes[1].selected).toBe(true)
  39. vm.test = 'c'
  40. waitForUpdate(function () {
  41. expect(vm.$el.value).toBe('c')
  42. expect(vm.$el.childNodes[2].selected).toBe(true)
  43. updateSelect(vm.$el, 'a')
  44. triggerEvent(vm.$el, 'change')
  45. expect(vm.test).toBe('a')
  46. }).then(done)
  47. })
  48. it('should work with value bindings', done => {
  49. const vm = new Vue({
  50. data: {
  51. test: 2
  52. },
  53. template:
  54. '<select v-model="test">' +
  55. '<option value="1">a</option>' +
  56. '<option :value="2">b</option>' +
  57. '<option :value="3">c</option>' +
  58. '</select>'
  59. }).$mount()
  60. document.body.appendChild(vm.$el)
  61. expect(vm.$el.value).toBe('2')
  62. expect(vm.$el.childNodes[1].selected).toBe(true)
  63. vm.test = 3
  64. waitForUpdate(function () {
  65. expect(vm.$el.value).toBe('3')
  66. expect(vm.$el.childNodes[2].selected).toBe(true)
  67. updateSelect(vm.$el, '1')
  68. triggerEvent(vm.$el, 'change')
  69. expect(vm.test).toBe('1')
  70. }).then(done)
  71. })
  72. it('should work with value bindings (object loose equal)', done => {
  73. const vm = new Vue({
  74. data: {
  75. test: { a: 2 }
  76. },
  77. template:
  78. '<select v-model="test">' +
  79. '<option value="1">a</option>' +
  80. '<option :value="{ a: 2 }">b</option>' +
  81. '<option :value="{ a: 3 }">c</option>' +
  82. '</select>'
  83. }).$mount()
  84. document.body.appendChild(vm.$el)
  85. expect(vm.$el.childNodes[1].selected).toBe(true)
  86. vm.test = { a: 3 }
  87. waitForUpdate(function () {
  88. expect(vm.$el.childNodes[2].selected).toBe(true)
  89. updateSelect(vm.$el, '1')
  90. triggerEvent(vm.$el, 'change')
  91. expect(vm.test).toBe('1')
  92. updateSelect(vm.$el, { a: 2 })
  93. triggerEvent(vm.$el, 'change')
  94. expect(vm.test).toEqual({ a: 2 })
  95. }).then(done)
  96. })
  97. it('should work with v-for', done => {
  98. const vm = new Vue({
  99. data: {
  100. test: 'b',
  101. opts: ['a', 'b', 'c']
  102. },
  103. template:
  104. '<select v-model="test">' +
  105. '<option v-for="o in opts">{{ o }}</option>' +
  106. '</select>'
  107. }).$mount()
  108. document.body.appendChild(vm.$el)
  109. expect(vm.test).toBe('b')
  110. expect(vm.$el.value).toBe('b')
  111. expect(vm.$el.childNodes[1].selected).toBe(true)
  112. vm.test = 'c'
  113. waitForUpdate(function () {
  114. expect(vm.$el.value).toBe('c')
  115. expect(vm.$el.childNodes[2].selected).toBe(true)
  116. updateSelect(vm.$el, 'a')
  117. triggerEvent(vm.$el, 'change')
  118. expect(vm.test).toBe('a')
  119. // update v-for opts
  120. vm.opts = ['d', 'a']
  121. }).then(() => {
  122. expect(vm.$el.childNodes[0].selected).toBe(false)
  123. expect(vm.$el.childNodes[1].selected).toBe(true)
  124. }).then(done)
  125. })
  126. it('should work with v-for & value bindings', done => {
  127. const vm = new Vue({
  128. data: {
  129. test: 2,
  130. opts: [1, 2, 3]
  131. },
  132. template:
  133. '<select v-model="test">' +
  134. '<option v-for="o in opts" :value="o">optio {{ o }}</option>' +
  135. '</select>'
  136. }).$mount()
  137. document.body.appendChild(vm.$el)
  138. expect(vm.$el.value).toBe('2')
  139. expect(vm.$el.childNodes[1].selected).toBe(true)
  140. vm.test = 3
  141. waitForUpdate(function () {
  142. expect(vm.$el.value).toBe('3')
  143. expect(vm.$el.childNodes[2].selected).toBe(true)
  144. updateSelect(vm.$el, 1)
  145. triggerEvent(vm.$el, 'change')
  146. expect(vm.test).toBe(1)
  147. // update v-for opts
  148. vm.opts = [0, 1]
  149. }).then(() => {
  150. expect(vm.$el.childNodes[0].selected).toBe(false)
  151. expect(vm.$el.childNodes[1].selected).toBe(true)
  152. }).then(done)
  153. })
  154. it('should work with select which has no default selected options', (done) => {
  155. const spy = jasmine.createSpy()
  156. const vm = new Vue({
  157. data: {
  158. id: 4,
  159. list: [1, 2, 3],
  160. testChange: 5
  161. },
  162. template:
  163. '<div>' +
  164. '<select @change="test" v-model="id">' +
  165. '<option v-for="item in list" :value="item">{{item}}</option>' +
  166. '</select>' +
  167. '{{testChange}}' +
  168. '</div>',
  169. methods: {
  170. test: spy
  171. }
  172. }).$mount()
  173. document.body.appendChild(vm.$el)
  174. vm.testChange = 10
  175. waitForUpdate(() => {
  176. expect(spy.calls.count()).toBe(0)
  177. }).then(done)
  178. })
  179. it('multiple', done => {
  180. const vm = new Vue({
  181. data: {
  182. test: ['b']
  183. },
  184. template:
  185. '<select v-model="test" multiple>' +
  186. '<option>a</option>' +
  187. '<option>b</option>' +
  188. '<option>c</option>' +
  189. '</select>'
  190. }).$mount()
  191. var opts = vm.$el.options
  192. expect(opts[0].selected).toBe(false)
  193. expect(opts[1].selected).toBe(true)
  194. expect(opts[2].selected).toBe(false)
  195. vm.test = ['a', 'c']
  196. waitForUpdate(() => {
  197. expect(opts[0].selected).toBe(true)
  198. expect(opts[1].selected).toBe(false)
  199. expect(opts[2].selected).toBe(true)
  200. opts[0].selected = false
  201. opts[1].selected = true
  202. triggerEvent(vm.$el, 'change')
  203. expect(vm.test).toEqual(['b', 'c'])
  204. }).then(done)
  205. })
  206. it('multiple with static template', () => {
  207. const vm = new Vue({
  208. template:
  209. '<select multiple>' +
  210. '<option selected>a</option>' +
  211. '<option selected>b</option>' +
  212. '<option selected>c</option>' +
  213. '</select>'
  214. }).$mount()
  215. var opts = vm.$el.options
  216. expect(opts[0].selected).toBe(true)
  217. expect(opts[1].selected).toBe(true)
  218. expect(opts[2].selected).toBe(true)
  219. })
  220. it('multiple + v-for', done => {
  221. const vm = new Vue({
  222. data: {
  223. test: ['b'],
  224. opts: ['a', 'b', 'c']
  225. },
  226. template:
  227. '<select v-model="test" multiple>' +
  228. '<option v-for="o in opts">{{ o }}</option>' +
  229. '</select>'
  230. }).$mount()
  231. var opts = vm.$el.options
  232. expect(opts[0].selected).toBe(false)
  233. expect(opts[1].selected).toBe(true)
  234. expect(opts[2].selected).toBe(false)
  235. vm.test = ['a', 'c']
  236. waitForUpdate(() => {
  237. expect(opts[0].selected).toBe(true)
  238. expect(opts[1].selected).toBe(false)
  239. expect(opts[2].selected).toBe(true)
  240. opts[0].selected = false
  241. opts[1].selected = true
  242. triggerEvent(vm.$el, 'change')
  243. expect(vm.test).toEqual(['b', 'c'])
  244. // update v-for opts
  245. vm.opts = ['c', 'd']
  246. }).then(() => {
  247. expect(opts[0].selected).toBe(true)
  248. expect(opts[1].selected).toBe(false)
  249. expect(vm.test).toEqual(['c']) // should remove 'd' which no longer has a matching option
  250. }).then(done)
  251. })
  252. it('multiple selects', (done) => {
  253. const spy = jasmine.createSpy()
  254. const vm = new Vue({
  255. data: {
  256. selections: ['', ''],
  257. selectBoxes: [
  258. [
  259. { value: 'foo', text: 'foo' },
  260. { value: 'bar', text: 'bar' }
  261. ],
  262. [
  263. { value: 'day', text: 'day' },
  264. { value: 'night', text: 'night' }
  265. ]
  266. ]
  267. },
  268. watch: {
  269. selections: spy
  270. },
  271. template:
  272. '<div>' +
  273. '<select v-for="(item, index) in selectBoxes" v-model="selections[index]">' +
  274. '<option v-for="element in item" v-bind:value="element.value" v-text="element.text"></option>' +
  275. '</select>' +
  276. '<span ref="rs">{{selections}}</span>' +
  277. '</div>'
  278. }).$mount()
  279. document.body.appendChild(vm.$el)
  280. var selects = vm.$el.getElementsByTagName('select')
  281. var select0 = selects[0]
  282. select0.options[0].selected = true
  283. triggerEvent(select0, 'change')
  284. waitForUpdate(() => {
  285. expect(spy).toHaveBeenCalled()
  286. expect(vm.selections).toEqual(['foo', ''])
  287. }).then(done)
  288. })
  289. it('.number modifier', () => {
  290. const vm = new Vue({
  291. data: {
  292. test: 2
  293. },
  294. template:
  295. '<select v-model.number="test">' +
  296. '<option value="1">a</option>' +
  297. '<option :value="2">b</option>' +
  298. '<option :value="3">c</option>' +
  299. '</select>'
  300. }).$mount()
  301. document.body.appendChild(vm.$el)
  302. updateSelect(vm.$el, '1')
  303. triggerEvent(vm.$el, 'change')
  304. expect(vm.test).toBe(1)
  305. })
  306. it('should respect different pritive type value', (done) => {
  307. const vm = new Vue({
  308. data: {
  309. test: 0
  310. },
  311. template:
  312. '<select v-model.number="test">' +
  313. '<option value="">a</option>' +
  314. '<option value="0">b</option>' +
  315. '<option value="1">c</option>' +
  316. '<option value="false">c</option>' +
  317. '<option value="true">c</option>' +
  318. '</select>'
  319. }).$mount()
  320. var opts = vm.$el.options
  321. expect(opts[0].selected).toBe(false)
  322. expect(opts[1].selected).toBe(true)
  323. expect(opts[2].selected).toBe(false)
  324. expect(opts[3].selected).toBe(false)
  325. expect(opts[4].selected).toBe(false)
  326. vm.test = 1
  327. waitForUpdate(() => {
  328. expect(opts[0].selected).toBe(false)
  329. expect(opts[1].selected).toBe(false)
  330. expect(opts[2].selected).toBe(true)
  331. expect(opts[3].selected).toBe(false)
  332. expect(opts[4].selected).toBe(false)
  333. vm.test = ''
  334. }).then(() => {
  335. expect(opts[0].selected).toBe(true)
  336. expect(opts[1].selected).toBe(false)
  337. expect(opts[2].selected).toBe(false)
  338. expect(opts[3].selected).toBe(false)
  339. expect(opts[4].selected).toBe(false)
  340. vm.test = false
  341. }).then(() => {
  342. expect(opts[0].selected).toBe(false)
  343. expect(opts[1].selected).toBe(false)
  344. expect(opts[2].selected).toBe(false)
  345. expect(opts[3].selected).toBe(true)
  346. expect(opts[4].selected).toBe(false)
  347. vm.test = true
  348. }).then(() => {
  349. expect(opts[0].selected).toBe(false)
  350. expect(opts[1].selected).toBe(false)
  351. expect(opts[2].selected).toBe(false)
  352. expect(opts[3].selected).toBe(false)
  353. expect(opts[4].selected).toBe(true)
  354. }).then(done)
  355. })
  356. it('should warn inline selected', () => {
  357. const vm = new Vue({
  358. data: {
  359. test: null
  360. },
  361. template:
  362. '<select v-model="test">' +
  363. '<option selected>a</option>' +
  364. '</select>'
  365. }).$mount()
  366. expect(vm.$el.selectedIndex).toBe(-1)
  367. expect('inline selected attributes on <option> will be ignored when using v-model')
  368. .toHaveBeenWarned()
  369. })
  370. it('should warn multiple with non-Array value', done => {
  371. new Vue({
  372. data: {
  373. test: 'meh'
  374. },
  375. template:
  376. '<select v-model="test" multiple></select>'
  377. }).$mount()
  378. // IE warns on a setTimeout as well
  379. setTimeout(() => {
  380. expect('<select multiple v-model="test"> expects an Array value for its binding, but got String')
  381. .toHaveBeenWarned()
  382. done()
  383. }, 0)
  384. })
  385. })