x-on.spec.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. import { beChecked, contain, notBeChecked, haveAttribute, haveData, haveText, test, beVisible, notBeVisible, html } from '../../utils'
  2. test('data modified in event listener updates affected attribute bindings',
  3. html`
  4. <div x-data="{ foo: 'bar' }">
  5. <button x-on:click="foo = 'baz'"></button>
  6. <span x-bind:foo="foo"></span>
  7. </div>
  8. `,
  9. ({ get }) => {
  10. get('span').should(haveAttribute('foo', 'bar'))
  11. get('button').click()
  12. get('span').should(haveAttribute('foo', 'baz'))
  13. }
  14. )
  15. test('can call a method without parenthesis',
  16. html`
  17. <div x-data="{ foo: 'bar', baz($event) { this.foo = $event.target.dataset.bob } }">
  18. <button x-on:click="baz" data-bob="lob"></button>
  19. <span x-text="foo"></span>
  20. </div>
  21. `,
  22. ({ get }) => {
  23. get('span').should(haveText('bar'))
  24. get('button').click()
  25. get('span').should(haveText('lob'))
  26. }
  27. )
  28. test('event object is not passed if other params are present',
  29. html`
  30. <div x-data="{ foo: 'bar', baz(word) { this.foo = word } }">
  31. <button x-on:click="baz('foo')" data-bob="lob"></button>
  32. <span x-text="foo"></span>
  33. </div>
  34. `,
  35. ({ get }) => {
  36. get('span').should(haveText('bar'))
  37. get('button').click()
  38. get('span').should(haveText('foo'))
  39. }
  40. )
  41. test('nested data modified in event listener updates affected attribute bindings',
  42. html`
  43. <div x-data="{ nested: { foo: 'bar' } }">
  44. <button x-on:click="nested.foo = 'baz'"></button>
  45. <span x-bind:foo="nested.foo"></span>
  46. </div>
  47. `,
  48. ({ get }) => {
  49. get('span').should(haveAttribute('foo', 'bar'))
  50. get('button').click()
  51. get('span').should(haveAttribute('foo', 'baz'))
  52. }
  53. )
  54. test('.passive modifier should disable e.preventDefault()',
  55. html`
  56. <div x-data="{ defaultPrevented: null }">
  57. <button
  58. x-on:mousedown.passive="
  59. $event.preventDefault();
  60. defaultPrevented = $event.defaultPrevented;
  61. "
  62. >
  63. <span></span>
  64. </button>
  65. </div>
  66. `,
  67. ({ get }) => {
  68. get('button').click()
  69. get('div').should(haveData('defaultPrevented', false))
  70. }
  71. )
  72. test('.stop modifier',
  73. html`
  74. <div x-data="{ foo: 'bar' }">
  75. <button x-on:click="foo = 'baz'">
  76. <h1>h1</h1>
  77. <h2 @click.stop>h2</h2>
  78. </button>
  79. </div>
  80. `,
  81. ({ get }) => {
  82. get('div').should(haveData('foo', 'bar'))
  83. get('h2').click()
  84. get('div').should(haveData('foo', 'bar'))
  85. get('h1').click()
  86. get('div').should(haveData('foo', 'baz'))
  87. }
  88. )
  89. test('.stop modifier with a .throttle',
  90. html`
  91. <div x-data="{ foo: 'bar' }">
  92. <button x-on:click="foo = 'baz'">
  93. <h1>h1</h1>
  94. <h2 @click.stop.throttle>h2</h2>
  95. </button>
  96. </div>
  97. `,
  98. ({ get }) => {
  99. get('div').should(haveData('foo', 'bar'))
  100. get('h2').click()
  101. get('h2').click()
  102. get('div').should(haveData('foo', 'bar'))
  103. get('h1').click()
  104. get('div').should(haveData('foo', 'baz'))
  105. }
  106. )
  107. test('.capture modifier',
  108. html`
  109. <div x-data="{ foo: 'bar', count: 0 }">
  110. <button @click.capture="count = count + 1; foo = 'baz'">
  111. <h1>h1</h1>
  112. <h2 @click="foo = 'bob'">h2</h2>
  113. </button>
  114. </div>
  115. `,
  116. ({ get }) => {
  117. get('div').should(haveData('foo', 'bar'))
  118. get('h2').click()
  119. get('div').should(haveData('foo', 'bob'))
  120. get('div').should(haveData('count', 1))
  121. }
  122. )
  123. test('.capture modifier with @keyup',
  124. html`
  125. <div x-data="{ foo: 'bar', count: 0 }">
  126. <span @keyup.capture="count = count + 1; foo = 'span'">
  127. <input type="text" @keyup="foo = 'input'">
  128. </span>
  129. </div>
  130. `,
  131. ({ get }) => {
  132. get('div').should(haveData('foo', 'bar'))
  133. get('input').type('f')
  134. get('div').should(haveData('foo', 'input'))
  135. get('div').should(haveData('count', 1))
  136. }
  137. )
  138. test('.capture modifier with @keyup and specified key',
  139. html`
  140. <div x-data="{ foo: 'bar', count: 0 }">
  141. <span @keyup.enter.capture="count = count + 1; foo = 'span'">
  142. <input type="text" @keyup.enter="foo = 'input'">
  143. </span>
  144. </div>
  145. `,
  146. ({ get }) => {
  147. get('div').should(haveData('foo', 'bar'))
  148. get('input').type('{enter}')
  149. get('div').should(haveData('foo', 'input'))
  150. get('div').should(haveData('count', 1))
  151. }
  152. )
  153. test('.self modifier',
  154. html`
  155. <div x-data="{ foo: 'bar' }">
  156. <h1 x-on:click.self="foo = 'baz'" id="selfTarget">
  157. content
  158. <button>click</button>
  159. content
  160. </h1>
  161. <span x-text="foo"></span>
  162. </div>
  163. `,
  164. ({ get }) => {
  165. get('span').should(haveText('bar'))
  166. get('button').click()
  167. get('span').should(haveText('bar'))
  168. get('h1').click()
  169. get('span').should(haveText('baz'))
  170. }
  171. )
  172. test(
  173. ".self.once modifiers",
  174. html`
  175. <div x-data="{ foo: 'bar' }">
  176. <h1 x-on:click.self.once="foo = 'baz'" id="selfTarget">
  177. content
  178. <button>click</button>
  179. content
  180. </h1>
  181. <span x-text="foo"></span>
  182. </div>
  183. `,
  184. ({ get }) => {
  185. get("span").should(haveText("bar"));
  186. get("button").click();
  187. get("span").should(haveText("bar"));
  188. get("h1").click();
  189. get("span").should(haveText("baz"));
  190. }
  191. );
  192. test('.prevent modifier',
  193. html`
  194. <div x-data="{}">
  195. <input type="checkbox" x-on:click.prevent>
  196. </div>
  197. `,
  198. ({ get }) => {
  199. get('input').check()
  200. get('input').should(notBeChecked())
  201. }
  202. )
  203. test('.prevent modifier with a .debounce',
  204. html`
  205. <div x-data="{}">
  206. <input type="checkbox" x-on:click.prevent.debounce>
  207. </div>
  208. `,
  209. ({ get }) => {
  210. get('input').check()
  211. get('input').check()
  212. get('input').should(notBeChecked())
  213. }
  214. )
  215. test('.window modifier',
  216. html`
  217. <div x-data="{ foo: 'bar' }">
  218. <div x-on:click.window="foo = 'baz'"></div>
  219. <span x-text="foo"></span>
  220. </div>
  221. `,
  222. ({ get }) => {
  223. get('span').should(haveText('bar'))
  224. get('span').click()
  225. get('span').should(haveText('baz'))
  226. }
  227. )
  228. test('expressions can start with if',
  229. html`
  230. <div x-data="{ foo: 'bar' }">
  231. <button @click="if (foo === 'bar') foo = 'baz'">click</button>
  232. <span x-text="foo"></span>
  233. </div>
  234. `,
  235. ({ get }) => {
  236. get('span').should(haveText('bar'))
  237. get('button').click()
  238. get('span').should(haveText('baz'))
  239. }
  240. )
  241. test('unbind global event handler when element is removed',
  242. html`
  243. <div x-data="{ count: 0 }">
  244. <div x-on:click.window="count++" x-ref="rmMe"></div>
  245. <button @click="$refs.rmMe.remove()">click</button>
  246. <span x-text="count"></span>
  247. </div>
  248. `,
  249. ({ get }) => {
  250. get('button').click()
  251. get('span').click()
  252. get('span').should(haveText('1'))
  253. }
  254. )
  255. test('.document modifier',
  256. html`
  257. <div x-data="{ foo: 'bar' }">
  258. <div x-on:click.document="foo = 'baz'"></div>
  259. <span x-text="foo"></span>
  260. </div>
  261. `,
  262. ({ get }) => {
  263. get('span').should(haveText('bar'))
  264. get('span').click()
  265. get('span').should(haveText('baz'))
  266. }
  267. )
  268. test('.once modifier',
  269. html`
  270. <div x-data="{ count: 0 }">
  271. <button x-on:click.once="count = count+1"></button>
  272. <span x-text="count"></span>
  273. </div>
  274. `,
  275. ({ get }) => {
  276. get('span').should(haveText('0'))
  277. get('button').click()
  278. get('span').should(haveText('1'))
  279. get('button').click()
  280. get('span').should(haveText('1'))
  281. }
  282. )
  283. test('.once modifier with @keyup',
  284. html`
  285. <div x-data="{ count: 0 }">
  286. <input type="text" x-on:keyup.once="count = count+1">
  287. <span x-text="count"></span>
  288. </div>
  289. `,
  290. ({ get }) => {
  291. get('span').should(haveText('0'))
  292. get('input').type('f')
  293. get('span').should(haveText('1'))
  294. get('input').type('o')
  295. get('span').should(haveText('1'))
  296. }
  297. )
  298. test('.once modifier with @keyup and specified key',
  299. html`
  300. <div x-data="{ count: 0 }">
  301. <input type="text" x-on:keyup.enter.once="count = count+1">
  302. <span x-text="count"></span>
  303. </div>
  304. `,
  305. ({ get }) => {
  306. get('span').should(haveText('0'))
  307. get('input').type('f')
  308. get('span').should(haveText('0'))
  309. get('input').type('{enter}')
  310. get('span').should(haveText('1'))
  311. get('input').type('{enter}')
  312. get('span').should(haveText('1'))
  313. }
  314. )
  315. test('.debounce modifier',
  316. html`
  317. <div x-data="{ count: 0 }">
  318. <input x-on:input.debounce="count = count+1">
  319. <span x-text="count"></span>
  320. </div>
  321. `,
  322. ({ get }) => {
  323. get('span').should(haveText('0'))
  324. get('input').type('f')
  325. get('span').should(haveText('1'))
  326. get('input').type('ffffffffffff')
  327. get('span').should(haveText('2'))
  328. }
  329. )
  330. test('.throttle modifier',
  331. html`
  332. <div x-data="{ count: 0 }">
  333. <input x-on:keyup.throttle.504ms="count = count+1">
  334. <span x-text="count"></span>
  335. </div>
  336. `,
  337. ({ get }) => {
  338. get('span').should(haveText('0'))
  339. get('input').type('f')
  340. get('span').should(haveText('1'))
  341. get('input').type('ffffffffffff')
  342. get('span').should(haveText('1'))
  343. }
  344. )
  345. test('keydown modifiers',
  346. html`
  347. <div x-data="{ count: 0 }">
  348. <input type="text"
  349. x-on:keydown="count++"
  350. x-on:keydown.enter="count++"
  351. x-on:keydown.space="count++"
  352. x-on:keydown.up="count++"
  353. x-on:keydown.down="count++"
  354. x-on:keydown.right="count++"
  355. x-on:keydown.left="count++"
  356. x-on:keydown.cmd="count++"
  357. x-on:keydown.meta="count++"
  358. x-on:keydown.escape="count++"
  359. x-on:keydown.esc="count++"
  360. x-on:keydown.ctrl="count++"
  361. x-on:keydown.slash="count++"
  362. x-on:keydown.period="count++"
  363. x-on:keydown.equal="count++"
  364. x-on:keydown.comma="count++"
  365. >
  366. <span x-text="count"></span>
  367. </div>
  368. `,
  369. ({ get }) => {
  370. get('span').should(haveText('0'))
  371. get('input').type('f')
  372. get('span').should(haveText('1'))
  373. get('input').type('{enter}')
  374. get('span').should(haveText('3'))
  375. get('input').type(' ')
  376. get('span').should(haveText('5'))
  377. get('input').type('{leftarrow}')
  378. get('span').should(haveText('7'))
  379. get('input').type('{rightarrow}')
  380. get('span').should(haveText('9'))
  381. get('input').type('{uparrow}')
  382. get('span').should(haveText('11'))
  383. get('input').type('{downarrow}')
  384. get('span').should(haveText('13'))
  385. get('input').type('{meta}')
  386. get('span').should(haveText('16'))
  387. get('input').type('{esc}')
  388. get('span').should(haveText('19'))
  389. get('input').type('{ctrl}')
  390. get('span').should(haveText('21'))
  391. get('input').type('/')
  392. get('span').should(haveText('23'))
  393. get('input').type('=')
  394. get('span').should(haveText('25'))
  395. get('input').type('.')
  396. get('span').should(haveText('27'))
  397. get('input').type(',')
  398. get('span').should(haveText('29'))
  399. }
  400. )
  401. test('discerns between space minus underscore',
  402. html`
  403. <div x-data="{ count: 0 }">
  404. <input id="space" type="text" x-on:keydown.space="count++" />
  405. <input id="minus" type="text" x-on:keydown.-="count++" />
  406. <input id="underscore" type="text" x-on:keydown._="count++" />
  407. <span x-text="count"></span>
  408. </div>
  409. `,
  410. ({get}) => {
  411. get('span').should(haveText('0'))
  412. get('#space').type(' ')
  413. get('span').should(haveText('1'))
  414. get('#space').type('-')
  415. get('span').should(haveText('1'))
  416. get('#minus').type('-')
  417. get('span').should(haveText('2'))
  418. get('#minus').type(' ')
  419. get('span').should(haveText('2'))
  420. get('#underscore').type('_')
  421. get('span').should(haveText('3'))
  422. get('#underscore').type(' ')
  423. get('span').should(haveText('3'))
  424. })
  425. test('keydown combo modifiers',
  426. html`
  427. <div x-data="{ count: 0 }">
  428. <input type="text" x-on:keydown.cmd.enter="count++">
  429. <span x-text="count"></span>
  430. </div>
  431. `,
  432. ({ get }) => {
  433. get('span').should(haveText('0'))
  434. get('input').type('f')
  435. get('span').should(haveText('0'))
  436. get('input').type('{cmd}{enter}')
  437. get('span').should(haveText('1'))
  438. }
  439. )
  440. test('keydown with specified key and stop modifier only stops for specified key',
  441. html`
  442. <div x-data="{ count: 0 }">
  443. <article x-on:keydown="count++">
  444. <input type="text" x-on:keydown.enter.stop>
  445. </article>
  446. <span x-text="count"></span>
  447. </div>
  448. `,
  449. ({ get }) => {
  450. get('span').should(haveText('0'))
  451. get('input').type('f')
  452. get('span').should(haveText('1'))
  453. get('input').type('{enter}')
  454. get('span').should(haveText('1'))
  455. }
  456. )
  457. test('@click.away',
  458. html`
  459. <div x-data="{ foo: 'bar' }">
  460. <h1 @click.away="foo = 'baz'">h1</h1>
  461. <h2>h2</h2>
  462. <span x-text="foo"></span>
  463. </div>
  464. `,
  465. ({ get }) => {
  466. get('span').should(haveText('bar'))
  467. get('h1').click()
  468. get('span').should(haveText('bar'))
  469. get('h2').click()
  470. get('span').should(haveText('baz'))
  471. }
  472. )
  473. test('@click.away.once works after clicking inside',
  474. html`
  475. <div x-data="{ foo: 'bar' }">
  476. <h1 @click.away.once="foo = 'baz'">h1</h1>
  477. <h2>h2</h2>
  478. <span x-text="foo"></span>
  479. </div>
  480. `,
  481. ({ get }) => {
  482. get('span').should(haveText('bar'))
  483. get('h1').click()
  484. get('span').should(haveText('bar'))
  485. get('h2').click()
  486. get('span').should(haveText('baz'))
  487. }
  488. )
  489. test('@click.away with x-show (prevent race condition)',
  490. html`
  491. <div x-data="{ show: false }">
  492. <button @click="show = true">Show</button>
  493. <h1 x-show="show" @click.away="show = false">h1</h1>
  494. <h2>h2</h2>
  495. </div>
  496. `,
  497. ({ get }) => {
  498. get('h1').should(notBeVisible())
  499. get('button').click()
  500. get('h1').should(beVisible())
  501. }
  502. )
  503. test('event with colon',
  504. html`
  505. <div x-data="{ foo: 'bar' }">
  506. <div x-on:my:event.document="foo = 'baz'"></div>
  507. <button @click="document.dispatchEvent(new CustomEvent('my:event', { bubbles: true }))">click</button>
  508. <span x-text="foo"></span>
  509. </div>
  510. `,
  511. ({ get }) => {
  512. get('span').should(haveText('bar'))
  513. get('button').click()
  514. get('span').should(haveText('baz'))
  515. }
  516. )
  517. test('event instance can be passed to method reference',
  518. html`
  519. <div x-data="{ foo: 'bar', changeFoo(e) { this.foo = e.target.id } }">
  520. <button x-on:click="changeFoo" id="baz"></button>
  521. <span x-text="foo"></span>
  522. </div>
  523. `,
  524. ({ get }) => {
  525. get('span').should(haveText('bar'))
  526. get('button').click()
  527. get('span').should(haveText('baz'))
  528. }
  529. )
  530. test('.camel modifier correctly binds event listener',
  531. html`
  532. <div x-data="{ foo: 'bar' }" x-on:event-name.camel="foo = 'baz'">
  533. <button x-on:click="$dispatch('eventName')"></button>
  534. <span x-text="foo"></span>
  535. </div>
  536. `,
  537. ({ get }) => {
  538. get('span').should(haveText('bar'))
  539. get('button').click()
  540. get('span').should(haveText('baz'))
  541. }
  542. )
  543. test('.camel modifier correctly binds event listener with namespace',
  544. html`
  545. <div x-data="{ foo: 'bar' }" x-on:ns:event-name.camel="foo = 'baz'">
  546. <button x-on:click="$dispatch('ns:eventName')"></button>
  547. <span x-text="foo"></span>
  548. </div>
  549. `,
  550. ({ get }) => {
  551. get('span').should(haveText('bar'))
  552. get('button').click()
  553. get('span').should(haveText('baz'))
  554. }
  555. )
  556. test('.dot modifier correctly binds event listener',
  557. html`
  558. <div x-data="{ foo: 'bar' }" x-on:event-name.dot="foo = 'baz'">
  559. <button x-on:click="$dispatch('event.name')"></button>
  560. <span x-text="foo"></span>
  561. </div>
  562. `,
  563. ({ get }) => {
  564. get('span').should(haveText('bar'))
  565. get('button').click()
  566. get('span').should(haveText('baz'))
  567. }
  568. )
  569. test('underscores are allowed in event names',
  570. html`
  571. <div x-data="{ foo: 'bar' }" x-on:event_name="foo = 'baz'">
  572. <button x-on:click="$dispatch('event_name')"></button>
  573. <span x-text="foo"></span>
  574. </div>
  575. `,
  576. ({ get }) => {
  577. get('span').should(haveText('bar'))
  578. get('button').click()
  579. get('span').should(haveText('baz'))
  580. }
  581. )
  582. test('.dot modifier correctly binds event listener with namespace',
  583. html`
  584. <div x-data="{ foo: 'bar' }" x-on:ns:event-name.dot="foo = 'baz'">
  585. <button x-on:click="$dispatch('ns:event.name')"></button>
  586. <span x-text="foo"></span>
  587. </div>
  588. `,
  589. ({ get }) => {
  590. get('span').should(haveText('bar'))
  591. get('button').click()
  592. get('span').should(haveText('baz'))
  593. }
  594. )
  595. test('handles await in handlers with invalid right hand expressions',
  596. html`
  597. <div x-data="{ text: 'original' }">
  598. <button @click="let value = 'new string'; text = await Promise.resolve(value)"></button>
  599. <span x-text="text"></span>
  600. </div>
  601. `,
  602. ({ get }) => {
  603. get('span').should(haveText('original'))
  604. get('button').click()
  605. get('span').should(haveText('new string'))
  606. }
  607. )
  608. test(
  609. "handles system modifier keys on key events",
  610. html`
  611. <div x-data="{ keys: {
  612. shift: false,
  613. ctrl: false,
  614. meta: false,
  615. alt: false,
  616. cmd: false
  617. } }">
  618. <input type="text"
  619. @keydown.capture="Object.keys(keys).forEach(key => keys[key] = false)"
  620. @keydown.meta.space="keys.meta = true"
  621. @keydown.ctrl.space="keys.ctrl = true"
  622. @keydown.shift.space="keys.shift = true"
  623. @keydown.alt.space="keys.alt = true"
  624. @keydown.cmd.space="keys.cmd = true"
  625. />
  626. <template x-for="key in Object.keys(keys)" :key="key">
  627. <input type="checkbox" :name="key" x-model="keys[key]">
  628. </template>
  629. </div>
  630. `,({ get }) => {
  631. get("input[name=shift]").as('shift').should(notBeChecked());
  632. get("input[name=ctrl]").as('ctrl').should(notBeChecked());
  633. get("input[name=meta]").as('meta').should(notBeChecked());
  634. get("input[name=alt]").as('alt').should(notBeChecked());
  635. get("input[name=cmd]").as('cmd').should(notBeChecked());
  636. get("input[type=text]").as('input').trigger("keydown", { key: 'space', shiftKey: true });
  637. get('@shift').should(beChecked());
  638. get("@input").trigger("keydown", { key: 'space', ctrlKey: true });
  639. get("@shift").should(notBeChecked());
  640. get("@ctrl").should(beChecked());
  641. get("@input").trigger("keydown", { key: 'space', metaKey: true });
  642. get("@ctrl").should(notBeChecked());
  643. get("@meta").should(beChecked());
  644. get("@cmd").should(beChecked());
  645. get("@input").trigger("keydown", { key: 'space', altKey: true });
  646. get("@meta").should(notBeChecked());
  647. get("@cmd").should(notBeChecked());
  648. get("@alt").should(beChecked());
  649. get("@input").trigger("keydown", { key: 'space' });
  650. get("@alt").should(notBeChecked());
  651. get("@input").trigger("keydown", { key: 'space',
  652. ctrlKey: true, shiftKey: true, metaKey: true, altKey: true });
  653. get("input[name=shift]").as("shift").should(beChecked());
  654. get("input[name=ctrl]").as("ctrl").should(beChecked());
  655. get("input[name=meta]").as("meta").should(beChecked());
  656. get("input[name=alt]").as("alt").should(beChecked());
  657. get("input[name=cmd]").as("cmd").should(beChecked());
  658. }
  659. );
  660. test(
  661. "handles system modifier keys on mouse events",
  662. html`
  663. <div x-data="{ keys: {
  664. shift: false,
  665. ctrl: false,
  666. meta: false,
  667. alt: false,
  668. cmd: false
  669. } }">
  670. <button type=button
  671. @click.capture="Object.keys(keys).forEach(key => keys[key] = false)"
  672. @click.shift="keys.shift = true"
  673. @click.ctrl="keys.ctrl = true"
  674. @click.meta="keys.meta = true"
  675. @click.alt="keys.alt = true"
  676. @click.cmd="keys.cmd = true">
  677. change
  678. </button>
  679. <template x-for="key in Object.keys(keys)" :key="key">
  680. <input type="checkbox" :name="key" x-model="keys[key]">
  681. </template>
  682. </div>
  683. `,({ get }) => {
  684. get("input[name=shift]").as('shift').should(notBeChecked());
  685. get("input[name=ctrl]").as('ctrl').should(notBeChecked());
  686. get("input[name=meta]").as('meta').should(notBeChecked());
  687. get("input[name=alt]").as('alt').should(notBeChecked());
  688. get("input[name=cmd]").as('cmd').should(notBeChecked());
  689. get("button").as('button').trigger("click", { shiftKey: true });
  690. get('@shift').should(beChecked());
  691. get("@button").trigger("click", { ctrlKey: true });
  692. get("@shift").should(notBeChecked());
  693. get("@ctrl").should(beChecked());
  694. get("@button").trigger("click", { metaKey: true });
  695. get("@ctrl").should(notBeChecked());
  696. get("@meta").should(beChecked());
  697. get("@cmd").should(beChecked());
  698. get("@button").trigger("click", { altKey: true });
  699. get("@meta").should(notBeChecked());
  700. get("@cmd").should(notBeChecked());
  701. get("@alt").should(beChecked());
  702. get("@button").trigger("click", {});
  703. get("@alt").should(notBeChecked());
  704. get("@button").trigger("click", { ctrlKey: true, shiftKey: true, metaKey: true, altKey: true });
  705. get("@shift").as("shift").should(beChecked());
  706. get("@ctrl").as("ctrl").should(beChecked());
  707. get("@meta").as("meta").should(beChecked());
  708. get("@alt").as("alt").should(beChecked());
  709. get("@cmd").as("cmd").should(beChecked());
  710. }
  711. );
  712. test(
  713. "handles all mouse events with modifiers",
  714. html`
  715. <div x-data="{ keys: {
  716. shift: false,
  717. ctrl: false,
  718. meta: false,
  719. alt: false,
  720. cmd: false
  721. } }">
  722. <button type=button
  723. @click.capture="Object.keys(keys).forEach(key => keys[key] = false)"
  724. @contextmenu.prevent.shift="keys.shift = true"
  725. @auxclick.ctrl="keys.ctrl = true"
  726. @dblclick.meta="keys.meta = true"
  727. @mouseenter.alt="keys.alt = true"
  728. @mousemove.cmd="keys.cmd = true">
  729. change
  730. </button>
  731. <template x-for="key in Object.keys(keys)" :key="key">
  732. <input type="checkbox" :name="key" x-model="keys[key]">
  733. </template>
  734. </div>
  735. `,({ get }) => {
  736. get("input[name=shift]").as('shift').should(notBeChecked());
  737. get("input[name=ctrl]").as('ctrl').should(notBeChecked());
  738. get("input[name=meta]").as('meta').should(notBeChecked());
  739. get("input[name=alt]").as('alt').should(notBeChecked());
  740. get("input[name=cmd]").as('cmd').should(notBeChecked());
  741. get("button").as('button').trigger("contextmenu", { shiftKey: true });
  742. get('@shift').should(beChecked());
  743. get("@button").trigger("click");
  744. get("@button").trigger("auxclick", { ctrlKey: true });
  745. get("@shift").should(notBeChecked());
  746. get("@ctrl").should(beChecked());
  747. get("@button").trigger("click");
  748. get("@button").trigger("dblclick", { metaKey: true });
  749. get("@ctrl").should(notBeChecked());
  750. get("@meta").should(beChecked());
  751. get("@button").trigger("click");
  752. get("@button").trigger("mouseenter", { altKey: true });
  753. get("@meta").should(notBeChecked());
  754. get("@alt").should(beChecked());
  755. get("@button").trigger("click");
  756. get("@button").trigger("mousemove", { metaKey: true });
  757. get("@alt").should(notBeChecked());
  758. get("@cmd").should(beChecked());
  759. }
  760. );