model_spec.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. var _ = require('../../../../src/util')
  2. var Vue = require('../../../../src/vue')
  3. /**
  4. * Mock event helper
  5. */
  6. function trigger (target, event, process) {
  7. var e = document.createEvent('HTMLEvents')
  8. e.initEvent(event, true, true)
  9. if (process) process(e)
  10. target.dispatchEvent(e)
  11. }
  12. /**
  13. * setting <select>'s value in IE9 doesn't work
  14. * we have to manually loop through the options
  15. */
  16. function updateSelect (el, value) {
  17. /* jshint eqeqeq: false */
  18. var options = el.options
  19. var i = options.length
  20. while (i--) {
  21. if (options[i].value == value) {
  22. options[i].selected = true
  23. break
  24. }
  25. }
  26. }
  27. if (_.inBrowser) {
  28. describe('v-model', function () {
  29. var el
  30. beforeEach(function () {
  31. el = document.createElement('div')
  32. el.style.display = 'none'
  33. document.body.appendChild(el)
  34. spyOn(_, 'warn')
  35. })
  36. it('radio buttons', function (done) {
  37. var vm = new Vue({
  38. el: el,
  39. data: {
  40. test: 'a'
  41. },
  42. template:
  43. '<input type="radio" value="a" v-model="test" name="test">' +
  44. '<input type="radio" value="b" v-model="test" name="test">'
  45. })
  46. expect(el.childNodes[0].checked).toBe(true)
  47. expect(el.childNodes[1].checked).toBe(false)
  48. vm.test = 'b'
  49. _.nextTick(function () {
  50. expect(el.childNodes[0].checked).toBe(false)
  51. expect(el.childNodes[1].checked).toBe(true)
  52. el.childNodes[0].click()
  53. expect(el.childNodes[0].checked).toBe(true)
  54. expect(el.childNodes[1].checked).toBe(false)
  55. expect(vm.test).toBe('a')
  56. vm._directives[1].unbind()
  57. el.childNodes[1].click()
  58. expect(vm.test).toBe('a')
  59. done()
  60. })
  61. })
  62. it('radio default value', function () {
  63. var vm = new Vue({
  64. el: el,
  65. data: {},
  66. template: '<input type="radio" checked value="a" v-model="test">'
  67. })
  68. expect(vm.test).toBe('a')
  69. })
  70. it('checkbox', function (done) {
  71. var vm = new Vue({
  72. el: el,
  73. data: {
  74. test: true
  75. },
  76. template: '<input type="checkbox" v-model="test">'
  77. })
  78. expect(el.firstChild.checked).toBe(true)
  79. vm.test = false
  80. _.nextTick(function () {
  81. expect(el.firstChild.checked).toBe(false)
  82. expect(vm.test).toBe(false)
  83. el.firstChild.click()
  84. expect(el.firstChild.checked).toBe(true)
  85. expect(vm.test).toBe(true)
  86. vm._directives[0].unbind()
  87. el.firstChild.click()
  88. expect(el.firstChild.checked).toBe(false)
  89. expect(vm.test).toBe(true)
  90. done()
  91. })
  92. })
  93. it('checkbox default value', function () {
  94. var vm = new Vue({
  95. el: el,
  96. data: {},
  97. template: '<input type="checkbox" checked v-model="test">'
  98. })
  99. expect(vm.test).toBe(true)
  100. })
  101. it('select', function (done) {
  102. var vm = new Vue({
  103. el: el,
  104. data: {
  105. test: 'b'
  106. },
  107. template:
  108. '<select v-model="test">' +
  109. '<option>a</option>' +
  110. '<option>b</option>' +
  111. '<option>c</option>' +
  112. '</select>'
  113. })
  114. expect(vm.test).toBe('b')
  115. expect(el.firstChild.value).toBe('b')
  116. expect(el.firstChild.childNodes[1].selected).toBe(true)
  117. vm.test = 'c'
  118. _.nextTick(function () {
  119. expect(el.firstChild.value).toBe('c')
  120. expect(el.firstChild.childNodes[2].selected).toBe(true)
  121. updateSelect(el.firstChild, 'a')
  122. trigger(el.firstChild, 'change')
  123. expect(vm.test).toBe('a')
  124. done()
  125. })
  126. })
  127. it('select default value', function () {
  128. var vm = new Vue({
  129. el: el,
  130. data: {
  131. test: 'a'
  132. },
  133. template:
  134. '<select v-model="test">' +
  135. '<option>a</option>' +
  136. '<option selected>b</option>' +
  137. '</select>'
  138. })
  139. expect(vm.test).toBe('b')
  140. expect(el.firstChild.value).toBe('b')
  141. expect(el.firstChild.childNodes[1].selected).toBe(true)
  142. })
  143. it('select + multiple', function (done) {
  144. var vm = new Vue({
  145. el: el,
  146. data: {
  147. test: [2] // test number soft equal
  148. },
  149. template:
  150. '<select v-model="test" multiple>' +
  151. '<option>1</option>' +
  152. '<option>2</option>' +
  153. '<option>3</option>' +
  154. '</select>'
  155. })
  156. var opts = el.firstChild.options
  157. expect(opts[0].selected).toBe(false)
  158. expect(opts[1].selected).toBe(true)
  159. expect(opts[2].selected).toBe(false)
  160. vm.test = [1, '3'] // mix of number/string
  161. _.nextTick(function () {
  162. expect(opts[0].selected).toBe(true)
  163. expect(opts[1].selected).toBe(false)
  164. expect(opts[2].selected).toBe(true)
  165. opts[0].selected = false
  166. opts[1].selected = true
  167. trigger(el.firstChild, 'change')
  168. expect(vm.test[0]).toBe('2')
  169. expect(vm.test[1]).toBe('3')
  170. done()
  171. })
  172. })
  173. it('select + multiple default value', function () {
  174. var vm = new Vue({
  175. el: el,
  176. data: {},
  177. template:
  178. '<select v-model="test" multiple>' +
  179. '<option>a</option>' +
  180. '<option selected>b</option>' +
  181. '<option selected>c</option>' +
  182. '</select>'
  183. })
  184. expect(vm.test[0]).toBe('b')
  185. expect(vm.test[1]).toBe('c')
  186. })
  187. it('select + options', function (done) {
  188. var vm = new Vue({
  189. el: el,
  190. data: {
  191. test: 'b',
  192. opts: ['a', 'b', 'c']
  193. },
  194. template: '<select v-model="test" options="opts"></select>'
  195. })
  196. var opts = el.firstChild.options
  197. expect(opts.length).toBe(3)
  198. expect(opts[0].selected).toBe(false)
  199. expect(opts[1].selected).toBe(true)
  200. expect(opts[2].selected).toBe(false)
  201. vm.opts = ['b', 'c']
  202. _.nextTick(function () {
  203. expect(opts.length).toBe(2)
  204. expect(opts[0].selected).toBe(true)
  205. expect(opts[1].selected).toBe(false)
  206. // should teardown option watcher when unbind
  207. expect(vm._watcherList.length).toBe(2)
  208. vm._directives[0].unbind()
  209. expect(vm._watcherList.length).toBe(0)
  210. done()
  211. })
  212. })
  213. it('select + options + text', function () {
  214. var vm = new Vue({
  215. el: el,
  216. data: {
  217. test: 'b',
  218. opts: [
  219. { text: 'A', value: 'a' },
  220. { text: 'B', value: 'b' }
  221. ]
  222. },
  223. template: '<select v-model="test" options="opts"></select>'
  224. })
  225. expect(el.firstChild.innerHTML).toBe(
  226. '<option value="a">A</option>' +
  227. '<option value="b">B</option>'
  228. )
  229. var opts = el.firstChild.options
  230. expect(opts[0].selected).toBe(false)
  231. expect(opts[1].selected).toBe(true)
  232. })
  233. it('select + options + optgroup', function () {
  234. var vm = new Vue({
  235. el: el,
  236. data: {
  237. test: 'b',
  238. opts: [
  239. { label: 'A', options: ['a','b'] },
  240. { label: 'B', options: ['c'] }
  241. ]
  242. },
  243. template: '<select v-model="test" options="opts"></select>'
  244. })
  245. expect(el.firstChild.innerHTML).toBe(
  246. '<optgroup label="A">' +
  247. '<option value="a">a</option><option value="b">b</option>' +
  248. '</optgroup>' +
  249. '<optgroup label="B">' +
  250. '<option value="c">c</option>' +
  251. '</optgroup>'
  252. )
  253. var opts = el.firstChild.options
  254. expect(opts[0].selected).toBe(false)
  255. expect(opts[1].selected).toBe(true)
  256. expect(opts[2].selected).toBe(false)
  257. })
  258. it('text', function (done) {
  259. var vm = new Vue({
  260. el: el,
  261. data: {
  262. test: 'b'
  263. },
  264. template: '<input v-model="test">'
  265. })
  266. expect(el.firstChild.value).toBe('b')
  267. vm.test = 'a'
  268. _.nextTick(function () {
  269. expect(el.firstChild.value).toBe('a')
  270. el.firstChild.value = 'c'
  271. trigger(el.firstChild, 'input')
  272. expect(vm.test).toBe('c')
  273. vm._directives[0].unbind()
  274. el.firstChild.value = 'd'
  275. trigger(el.firstChild, 'input')
  276. expect(vm.test).toBe('c')
  277. done()
  278. })
  279. })
  280. it('text default value', function () {
  281. var vm = new Vue({
  282. el: el,
  283. data: {
  284. test: 'b'
  285. },
  286. template: '<input v-model="test | test" value="a">',
  287. filters: {
  288. test: {
  289. read: function (v) {
  290. return v.slice(0, -1)
  291. },
  292. write: function (v) {
  293. return v + 'c'
  294. }
  295. }
  296. }
  297. })
  298. expect(vm.test).toBe('ac')
  299. expect(el.firstChild.value).toBe('a')
  300. })
  301. it('text lazy', function () {
  302. var vm = new Vue({
  303. el: el,
  304. data: {
  305. test: 'b'
  306. },
  307. template: '<input v-model="test" lazy>'
  308. })
  309. expect(el.firstChild.value).toBe('b')
  310. expect(vm.test).toBe('b')
  311. el.firstChild.value = 'c'
  312. trigger(el.firstChild, 'input')
  313. expect(vm.test).toBe('b')
  314. trigger(el.firstChild, 'change')
  315. expect(vm.test).toBe('c')
  316. })
  317. it('text with filters', function (done) {
  318. var vm = new Vue({
  319. el: el,
  320. data: {
  321. test: 'b'
  322. },
  323. filters: {
  324. test: {
  325. write: function (val) {
  326. return val.toUpperCase()
  327. }
  328. }
  329. },
  330. template: '<input v-model="test | uppercase | test">'
  331. })
  332. expect(el.firstChild.value).toBe('B')
  333. el.firstChild.value = 'cc'
  334. trigger(el.firstChild, 'input')
  335. _.nextTick(function () {
  336. expect(el.firstChild.value).toBe('CC')
  337. expect(vm.test).toBe('CC')
  338. done()
  339. })
  340. })
  341. it('number', function () {
  342. var vm = new Vue({
  343. el: el,
  344. data: {
  345. test: 1
  346. },
  347. template: '<input v-model="test" value="2" number>'
  348. })
  349. expect(vm.test).toBe(2)
  350. el.firstChild.value = 3
  351. trigger(el.firstChild, 'input')
  352. expect(vm.test).toBe(3)
  353. })
  354. it('IE9 cut and delete', function (done) {
  355. var ie9 = _.isIE9
  356. _.isIE9 = true
  357. var vm = new Vue({
  358. el: el,
  359. data: {
  360. test: 'aaa'
  361. },
  362. template: '<input v-model="test">'
  363. })
  364. var input = el.firstChild
  365. input.value = 'aa'
  366. trigger(input, 'cut')
  367. _.nextTick(function () {
  368. expect(vm.test).toBe('aa')
  369. input.value = 'a'
  370. trigger(input, 'keyup', function (e) {
  371. e.keyCode = 8
  372. })
  373. expect(vm.test).toBe('a')
  374. // teardown
  375. vm._directives[0].unbind()
  376. input.value = 'bbb'
  377. trigger(input, 'keyup', function (e) {
  378. e.keyCode = 8
  379. })
  380. expect(vm.test).toBe('a')
  381. _.isIE9 = ie9
  382. done()
  383. })
  384. })
  385. it('text + compositionevents', function (done) {
  386. var vm = new Vue({
  387. el: el,
  388. data: {
  389. test: 'aaa',
  390. test2: 'bbb'
  391. },
  392. template: '<input v-model="test"><input v-model="test2 | uppsercase">'
  393. })
  394. var input = el.firstChild
  395. var input2 = el.childNodes[1]
  396. trigger(input, 'compositionstart')
  397. trigger(input2, 'compositionstart')
  398. input.value = input2.value = 'ccc'
  399. // input before composition unlock should not call set
  400. trigger(input, 'input')
  401. trigger(input2, 'input')
  402. expect(vm.test).toBe('aaa')
  403. expect(vm.test2).toBe('bbb')
  404. // after composition unlock it should work
  405. trigger(input, 'compositionend')
  406. trigger(input2, 'compositionend')
  407. trigger(input, 'input')
  408. trigger(input2, 'input')
  409. expect(vm.test).toBe('ccc')
  410. expect(vm.test2).toBe('ccc')
  411. // IE complains about "unspecified error" when calling
  412. // setSelectionRange() on an input element that's been
  413. // removed from the DOM, so we wait until the
  414. // selection range callback has fired to end this test.
  415. _.nextTick(done)
  416. })
  417. it('textarea', function () {
  418. var vm = new Vue({
  419. el: el,
  420. data: {
  421. test: 'b',
  422. b: 'BB'
  423. },
  424. template: '<textarea v-model="test">a {{b}} c</textarea>'
  425. })
  426. expect(vm.test).toBe('a BB c')
  427. expect(el.firstChild.value).toBe('a BB c')
  428. })
  429. it('warn invalid tag', function () {
  430. var vm = new Vue({
  431. el: el,
  432. template: '<div v-model="test"></div>'
  433. })
  434. expect(_.warn).toHaveBeenCalled()
  435. })
  436. it('warn invalid option value', function () {
  437. var vm = new Vue({
  438. el: el,
  439. data: { a: 123 },
  440. template: '<select v-model="test" options="a"></select>'
  441. })
  442. expect(_.warn).toHaveBeenCalled()
  443. })
  444. it('warn read-only filters', function () {
  445. var vm = new Vue({
  446. el: el,
  447. template: '<input v-model="abc | test">',
  448. filters: {
  449. test: function (v) {
  450. return v
  451. }
  452. }
  453. })
  454. expect(_.warn).toHaveBeenCalled()
  455. })
  456. })
  457. }