x-model.spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. import { beChecked, haveData, haveText, haveValue, html, notBeChecked, test } from '../../utils'
  2. test('The name of the test',
  3. html`<h1 x-data x-text="'HEY'"></h1>`,
  4. ({ get }) => get('h1').should(haveText('HEY'))
  5. )
  6. test('x-model has value binding when initialized',
  7. html`
  8. <div x-data="{ foo: 'bar' }">
  9. <input x-model="foo"></input>
  10. </div>
  11. `,
  12. ({ get }) => { get('input').should(haveValue('bar')) }
  13. )
  14. test('x-model updates value when updated via input event',
  15. html`
  16. <div x-data="{ foo: 'bar' }">
  17. <input x-model="foo"></input>
  18. <span x-text="foo"></span>
  19. </div>
  20. `,
  21. ({ get }) => {
  22. get('span').should(haveText('bar'))
  23. get('input').type('baz')
  24. get('span').should(haveText('barbaz'))
  25. }
  26. )
  27. test('x-model has value binding when updated',
  28. html`
  29. <div x-data="{ foo: 'bar' }">
  30. <input x-model="foo"></input>
  31. <button x-on:click="foo = 'baz'">click me</button>
  32. </div>
  33. `,
  34. ({ get }) => {
  35. get('input').should(haveValue('bar'))
  36. get('button').click()
  37. get('input').should(haveValue('baz'))
  38. }
  39. )
  40. test('x-model casts value to number if number modifier is present',
  41. html`
  42. <div x-data="{ foo: null }">
  43. <input type="number" x-model.number="foo"></input>
  44. </div>
  45. `,
  46. ({ get }) => {
  47. get('input').type('123')
  48. get('div').should(haveData('foo', 123))
  49. }
  50. )
  51. test('x-model with number modifier returns: null if empty, original value if casting fails, numeric value if casting passes',
  52. html`
  53. <div x-data="{ foo: 0, bar: '' }">
  54. <input type="number" x-model.number="foo"></input>
  55. <input x-model.number="bar"></input>
  56. </div>
  57. `,
  58. ({ get }) => {
  59. get('input:nth-of-type(1)').clear()
  60. get('div').should(haveData('foo', null))
  61. get('input:nth-of-type(1)').clear().type('-')
  62. get('div').should(haveData('foo', null))
  63. get('input:nth-of-type(1)').clear().type('-123')
  64. get('div').should(haveData('foo', -123))
  65. get('input:nth-of-type(2)').type(123).clear()
  66. get('div').should(haveData('bar', null))
  67. get('input:nth-of-type(2)').clear().type('-')
  68. get('div').should(haveData('bar', '-'))
  69. get('input:nth-of-type(2)').clear().type('-123')
  70. get('div').should(haveData('bar', -123))
  71. }
  72. )
  73. test('x-model casts value to boolean initially for radios',
  74. html`
  75. <div x-data="{ foo: true }">
  76. <input id="1" type="radio" value="true" name="foo" x-model.boolean="foo">
  77. <input id="2" type="radio" value="false" name="foo" x-model.boolean="foo">
  78. </div>
  79. `,
  80. ({ get }) => {
  81. get('div').should(haveData('foo', true))
  82. get('#1').should(beChecked())
  83. get('#2').should(notBeChecked())
  84. get('#2').click()
  85. get('div').should(haveData('foo', false))
  86. get('#1').should(notBeChecked())
  87. get('#2').should(beChecked())
  88. }
  89. )
  90. test('x-model casts value to boolean if boolean modifier is present',
  91. html`
  92. <div x-data="{ foo: null, bar: null, baz: [] }">
  93. <input type="text" x-model.boolean="foo"></input>
  94. <input type="checkbox" x-model.boolean="foo"></input>
  95. <input type="radio" name="foo" x-model.boolean="foo" value="true"></input>
  96. <input type="radio" name="foo" x-model.boolean="foo" value="false"></input>
  97. <select x-model.boolean="bar">
  98. <option value="true">yes</option>
  99. <option value="false">no</option>
  100. </select>
  101. </div>
  102. `,
  103. ({ get }) => {
  104. get('input[type=text]').type('1')
  105. get('div').should(haveData('foo', true))
  106. get('input[type=text]').clear().type('0')
  107. get('div').should(haveData('foo', false))
  108. get('input[type=checkbox]').check()
  109. get('div').should(haveData('foo', true))
  110. get('input[type=checkbox]').uncheck()
  111. get('div').should(haveData('foo', false))
  112. get('input[type=radio][value="true"]').should(notBeChecked())
  113. get('input[type=radio][value="false"]').should(beChecked())
  114. get('input[type=radio][value="true"]').check()
  115. get('div').should(haveData('foo', true))
  116. get('input[type=radio][value="false"]').check()
  117. get('div').should(haveData('foo', false))
  118. get('select').select('false')
  119. get('div').should(haveData('bar', false))
  120. get('select').select('true')
  121. get('div').should(haveData('bar', true))
  122. }
  123. )
  124. test('x-model with boolean modifier returns: null if empty, original value if casting fails, numeric value if casting passes',
  125. html`
  126. <div x-data="{ foo: 0, bar: '' }">
  127. <input x-model.boolean="foo"></input>
  128. </div>
  129. `,
  130. ({ get }) => {
  131. get('input').clear()
  132. get('div').should(haveData('foo', null))
  133. get('input').clear().type('bar')
  134. get('div').should(haveData('foo', true))
  135. get('input').clear().type('1')
  136. get('div').should(haveData('foo', true))
  137. get('input').clear().type('1').clear()
  138. get('div').should(haveData('foo', null))
  139. get('input').clear().type('0')
  140. get('div').should(haveData('foo', false))
  141. get('input').clear().type('bar')
  142. get('div').should(haveData('foo', true))
  143. get('input').clear().type('0').clear()
  144. get('div').should(haveData('foo', null))
  145. }
  146. )
  147. test('x-model trims value if trim modifier is present',
  148. html`
  149. <div x-data="{ foo: '' }">
  150. <input x-model.trim="foo"></input>
  151. <span x-text="foo"></span>
  152. </div>
  153. `,
  154. ({ get }) => {
  155. get('input').type('bar ')
  156. get('div').should(haveData('foo', 'bar'))
  157. }
  158. )
  159. test('x-model can be accessed programmatically',
  160. html`
  161. <div x-data="{ foo: 'bar' }" x-model="foo">
  162. <input x-model="foo">
  163. <span x-text="$root._x_model.get()"></span>
  164. <button @click="$root._x_model.set('bob')">Set foo to bob</button>
  165. </div>
  166. `,
  167. ({ get }) => {
  168. get('span').should(haveText('bar'))
  169. get('input').type('baz')
  170. get('span').should(haveText('barbaz'))
  171. get('button').click()
  172. get('span').should(haveText('bob'))
  173. }
  174. )
  175. test('x-model updates value when the form is reset',
  176. html`
  177. <div x-data="{ foo: '' }">
  178. <form>
  179. <input x-model="foo"></input>
  180. <button type="reset">Reset</button>
  181. </form>
  182. <span x-text="foo"></span>
  183. </div>
  184. `,
  185. ({ get }) => {
  186. get('span').should(haveText(''))
  187. get('input').type('baz')
  188. get('span').should(haveText('baz'))
  189. get('button').click()
  190. get('span').should(haveText(''))
  191. }
  192. )
  193. test('x-model with fill modifier takes input value on null, empty string or undefined',
  194. html`
  195. <div x-data="{ a: 123, b: 0, c: '', d: null, e: {} }">
  196. <input x-model.fill="a" value="123456" />
  197. <span id="a" x-text="a"></span>
  198. <input x-model.fill="b" value="123456" />
  199. <span id="b" x-text="b"></span>
  200. <input x-model.fill="c" value="123456" />
  201. <span id="c" x-text="c"></span>
  202. <input x-model.fill="d" value="123456" />
  203. <span id="d" x-text="d"></span>
  204. <input x-model.fill="e.a" value="123456" />
  205. <span id="e" x-text="e.a"></span>
  206. </div>
  207. `,
  208. ({ get }) => {
  209. get('#a').should(haveText('123'))
  210. get('#b').should(haveText('0'))
  211. get('#c').should(haveText('123456'))
  212. get('#d').should(haveText('123456'))
  213. get('#e').should(haveText('123456'))
  214. }
  215. )
  216. test('x-model with fill modifier works with select elements',
  217. html`
  218. <div x-data="{ a: null, b: null, c: null, d: null }">
  219. <select x-model.fill="a">
  220. <option value="123">123</option>
  221. <option value="456" selected>456</option>
  222. </select>
  223. <select x-model.fill="b" multiple>
  224. <option value="123" selected>123</option>
  225. <option value="456" selected>456</option>
  226. </select>
  227. </div>
  228. `,
  229. ({ get }) => {
  230. get('[x-data]').should(haveData('a', '456'));
  231. get('[x-data]').should(haveData('b', ['123', '456']));
  232. }
  233. );
  234. test('x-model with fill modifier works with radio elements',
  235. html`
  236. <div x-data="{ a: null, b: null, c: '101112', d: null }">
  237. <input x-model.fill="a" type="radio" value="123" />
  238. <input x-model.fill="a" type="radio" value="456" checked />
  239. <input x-model.fill="a" type="radio" value="789" />
  240. <input x-model.fill="a" type="radio" value="101112" />
  241. <input x-model.fill="a" type="radio" value="131415" />
  242. <input x-model.fill="b" name="b" type="radio" value="123" />
  243. <input x-model.fill="b" name="b" type="radio" value="456" />
  244. <input x-model.fill="b" name="b" type="radio" value="789" checked />
  245. <input x-model.fill="b" name="b" type="radio" value="101112" />
  246. <input x-model.fill="b" name="b" type="radio" value="131415" />
  247. <input x-model.fill="c" type="radio" value="123" />
  248. <input x-model.fill="c" type="radio" value="456" />
  249. <input x-model.fill="c" type="radio" value="789" />
  250. <input x-model.fill="c" type="radio" value="101112" />
  251. <input x-model.fill="c" type="radio" value="131415" />
  252. </div>
  253. `,
  254. ({ get }) => {
  255. get('[x-data]').should(haveData('a', '456'));
  256. get('[x-data]').should(haveData('b', '789'));
  257. get('[x-data]').should(haveData('c', '101112'));
  258. }
  259. );
  260. test('x-model with fill modifier respects number modifier',
  261. html`
  262. <div x-data="{ a: null, b: null, c: null, d: null }">
  263. <input type="text" x-model.fill.number="a" value="456" / >
  264. <select x-model.fill.number="b" multiple>
  265. <option value="123" selected>123</option>
  266. <option value="456" selected>456</option>
  267. </select>
  268. </div>
  269. `,
  270. ({ get }) => {
  271. get('[x-data]').should(haveData('a', 456));
  272. get('[x-data]').should(haveData('b', [123,456]));
  273. }
  274. );
  275. test(
  276. 'x-model with fill applies on checkboxes bound to array',
  277. html`
  278. <div x-data="{ a: ['456'] }">
  279. <input type="checkbox" x-model.fill="a" value="123" checked />
  280. <input type="checkbox" x-model.fill="a" value="456" />
  281. </div>
  282. `,
  283. ({ get }) => {
  284. get('[x-data]').should(haveData('a', ['123']));
  285. }
  286. );
  287. test(
  288. 'x-model with fill and debounce still fills value',
  289. html`
  290. <div x-data="{ a: '' }">
  291. <input type="text" x-model.fill.debounce="a" value="hello" />
  292. </div>
  293. `,
  294. ({ get }) => {
  295. get('[x-data]').should(haveData('a', 'hello'));
  296. }
  297. );