quasar 2.4.13 → 2.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/dist/api/QCheckbox.json +27 -0
  2. package/dist/api/QRadio.json +18 -0
  3. package/dist/api/QTime.json +8 -2
  4. package/dist/api/QToggle.json +24 -24
  5. package/dist/icon-set/bootstrap-icons.umd.prod.js +1 -1
  6. package/dist/icon-set/eva-icons.umd.prod.js +1 -1
  7. package/dist/icon-set/fontawesome-v5-pro.umd.prod.js +1 -1
  8. package/dist/icon-set/fontawesome-v5.umd.prod.js +1 -1
  9. package/dist/icon-set/ionicons-v4.umd.prod.js +1 -1
  10. package/dist/icon-set/line-awesome.umd.prod.js +1 -1
  11. package/dist/icon-set/material-icons-outlined.umd.prod.js +1 -1
  12. package/dist/icon-set/material-icons-round.umd.prod.js +1 -1
  13. package/dist/icon-set/material-icons-sharp.umd.prod.js +1 -1
  14. package/dist/icon-set/material-icons.umd.prod.js +1 -1
  15. package/dist/icon-set/mdi-v3.umd.prod.js +1 -1
  16. package/dist/icon-set/mdi-v4.umd.prod.js +1 -1
  17. package/dist/icon-set/mdi-v5.umd.prod.js +1 -1
  18. package/dist/icon-set/mdi-v6.umd.prod.js +1 -1
  19. package/dist/icon-set/svg-bootstrap-icons.umd.prod.js +1 -1
  20. package/dist/icon-set/svg-eva-icons.umd.prod.js +1 -1
  21. package/dist/icon-set/svg-fontawesome-v5.umd.prod.js +1 -1
  22. package/dist/icon-set/svg-ionicons-v4.umd.prod.js +1 -1
  23. package/dist/icon-set/svg-ionicons-v5.umd.prod.js +1 -1
  24. package/dist/icon-set/svg-ionicons-v6.umd.prod.js +1 -1
  25. package/dist/icon-set/svg-line-awesome.umd.prod.js +1 -1
  26. package/dist/icon-set/svg-material-icons-outlined.umd.prod.js +1 -1
  27. package/dist/icon-set/svg-material-icons-round.umd.prod.js +1 -1
  28. package/dist/icon-set/svg-material-icons-sharp.umd.prod.js +1 -1
  29. package/dist/icon-set/svg-material-icons.umd.prod.js +1 -1
  30. package/dist/icon-set/svg-mdi-v4.umd.prod.js +1 -1
  31. package/dist/icon-set/svg-mdi-v5.umd.prod.js +1 -1
  32. package/dist/icon-set/svg-mdi-v6.umd.prod.js +1 -1
  33. package/dist/icon-set/svg-themify.umd.prod.js +1 -1
  34. package/dist/icon-set/themify.umd.prod.js +1 -1
  35. package/dist/lang/ar.umd.prod.js +1 -1
  36. package/dist/lang/az-Latn.umd.prod.js +1 -1
  37. package/dist/lang/bg.umd.prod.js +1 -1
  38. package/dist/lang/bn.umd.prod.js +1 -1
  39. package/dist/lang/ca.umd.prod.js +1 -1
  40. package/dist/lang/cs.umd.prod.js +1 -1
  41. package/dist/lang/da.umd.prod.js +1 -1
  42. package/dist/lang/de.umd.prod.js +1 -1
  43. package/dist/lang/el.umd.prod.js +1 -1
  44. package/dist/lang/en-GB.umd.prod.js +1 -1
  45. package/dist/lang/en-US.umd.prod.js +1 -1
  46. package/dist/lang/eo.umd.prod.js +1 -1
  47. package/dist/lang/es.umd.prod.js +1 -1
  48. package/dist/lang/et.umd.prod.js +1 -1
  49. package/dist/lang/fa-IR.umd.prod.js +1 -1
  50. package/dist/lang/fa.umd.prod.js +1 -1
  51. package/dist/lang/fi.umd.prod.js +1 -1
  52. package/dist/lang/fr.umd.prod.js +1 -1
  53. package/dist/lang/gn.umd.prod.js +1 -1
  54. package/dist/lang/he.umd.prod.js +1 -1
  55. package/dist/lang/hr.umd.prod.js +1 -1
  56. package/dist/lang/hu.umd.prod.js +1 -1
  57. package/dist/lang/id.umd.prod.js +1 -1
  58. package/dist/lang/is.umd.prod.js +1 -1
  59. package/dist/lang/it.umd.prod.js +1 -1
  60. package/dist/lang/ja.umd.prod.js +1 -1
  61. package/dist/lang/km.umd.prod.js +1 -1
  62. package/dist/lang/ko-KR.umd.prod.js +1 -1
  63. package/dist/lang/kur-CKB.umd.prod.js +1 -1
  64. package/dist/lang/lt.umd.prod.js +1 -1
  65. package/dist/lang/lu.umd.prod.js +1 -1
  66. package/dist/lang/lv.umd.prod.js +1 -1
  67. package/dist/lang/ml.umd.prod.js +1 -1
  68. package/dist/lang/ms.umd.prod.js +1 -1
  69. package/dist/lang/my.umd.prod.js +1 -1
  70. package/dist/lang/nb-NO.umd.prod.js +1 -1
  71. package/dist/lang/nl.umd.prod.js +1 -1
  72. package/dist/lang/pl.umd.prod.js +1 -1
  73. package/dist/lang/pt-BR.umd.prod.js +1 -1
  74. package/dist/lang/pt.umd.prod.js +1 -1
  75. package/dist/lang/ro.umd.prod.js +1 -1
  76. package/dist/lang/ru.umd.prod.js +1 -1
  77. package/dist/lang/sk.umd.prod.js +1 -1
  78. package/dist/lang/sl.umd.prod.js +1 -1
  79. package/dist/lang/sr-CYR.umd.prod.js +1 -1
  80. package/dist/lang/sr.umd.prod.js +1 -1
  81. package/dist/lang/sv.umd.prod.js +1 -1
  82. package/dist/lang/ta.umd.prod.js +1 -1
  83. package/dist/lang/th.umd.prod.js +1 -1
  84. package/dist/lang/tr.umd.prod.js +1 -1
  85. package/dist/lang/ug.umd.prod.js +1 -1
  86. package/dist/lang/uk.umd.prod.js +1 -1
  87. package/dist/lang/vi.umd.prod.js +1 -1
  88. package/dist/lang/zh-CN.umd.prod.js +1 -1
  89. package/dist/lang/zh-TW.umd.prod.js +1 -1
  90. package/dist/quasar.cjs.prod.js +2 -2
  91. package/dist/quasar.css +38 -10
  92. package/dist/quasar.esm.prod.js +2 -2
  93. package/dist/quasar.prod.css +1 -1
  94. package/dist/quasar.rtl.css +55 -23
  95. package/dist/quasar.rtl.prod.css +1 -1
  96. package/dist/quasar.sass +31 -11
  97. package/dist/quasar.umd.js +307 -158
  98. package/dist/quasar.umd.prod.js +2 -2
  99. package/dist/ssr-directives/Morph.js +1 -1
  100. package/dist/transforms/auto-import.json +39 -3
  101. package/dist/transforms/import-map.json +18 -0
  102. package/dist/types/index.d.ts +35 -13
  103. package/dist/types/utils/date.d.ts +25 -13
  104. package/dist/vetur/quasar-attributes.json +32 -12
  105. package/dist/vetur/quasar-tags.json +8 -3
  106. package/dist/web-types/web-types.json +73 -28
  107. package/package.json +13 -3
  108. package/src/components/avatar/__tests__/QAvatar.spec.js +121 -0
  109. package/src/components/badge/__tests__/QBadge.spec.js +74 -0
  110. package/src/components/breadcrumbs/QBreadcrumbs.js +1 -1
  111. package/src/components/btn/__tests__/QBtn.spec.js +55 -0
  112. package/src/components/btn/__tests__/use-btn.spec.js +189 -0
  113. package/src/components/checkbox/QCheckbox.js +34 -3
  114. package/src/components/checkbox/QCheckbox.json +17 -0
  115. package/src/components/checkbox/QCheckbox.sass +17 -5
  116. package/src/components/checkbox/use-checkbox.js +4 -0
  117. package/src/components/checkbox/use-checkbox.json +18 -0
  118. package/src/components/chip/__tests__/QChip.spec.js +155 -0
  119. package/src/components/date/__tests__/QDate.spec.js +189 -0
  120. package/src/components/date/__tests__/use-datetime.spec.js +83 -0
  121. package/src/components/dialog/__tests__/QDialog.spec.js +129 -0
  122. package/src/components/editor/__tests__/QEditor.spec.js +195 -0
  123. package/src/components/field/QField.sass +2 -0
  124. package/src/components/form/QForm.js +26 -24
  125. package/src/components/input/QInput.js +9 -0
  126. package/src/components/input/__tests__/QInput.spec.js +105 -0
  127. package/src/components/input/__tests__/use-mask.spec.js +29 -0
  128. package/src/components/menu/__tests__/QMenu.spec.js +610 -0
  129. package/src/components/menu/__tests__/WrapperOne.vue +51 -0
  130. package/src/components/menu/__tests__/WrapperTwo.vue +38 -0
  131. package/src/components/radio/QRadio.js +26 -1
  132. package/src/components/radio/QRadio.json +16 -0
  133. package/src/components/radio/QRadio.sass +17 -6
  134. package/src/components/select/QSelect.js +1 -1
  135. package/src/components/select/__tests__/QSelect.spec.js +2003 -0
  136. package/src/components/select/__tests__/WrapperOne.vue +28 -0
  137. package/src/components/table/__tests__/QTable.spec.js +635 -0
  138. package/src/components/table/__tests__/QTd.spec.js +35 -0
  139. package/src/components/table/__tests__/QTh.spec.js +27 -0
  140. package/src/components/table/__tests__/QTr.spec.js +27 -0
  141. package/src/components/tabs/__tests__/QRouteTab.spec.js +9 -0
  142. package/src/components/tabs/__tests__/QTab.spec.js +79 -0
  143. package/src/components/tabs/__tests__/QTabs.spec.js +147 -0
  144. package/src/components/time/QTime.json +2 -2
  145. package/src/components/toggle/QToggle.js +5 -13
  146. package/src/components/toggle/QToggle.json +3 -12
  147. package/src/components/tree/QTree.js +39 -41
  148. package/src/components/uploader/__tests__/QUploader.spec.js +161 -0
  149. package/src/composables/private/__tests__/FieldWrapper.vue +39 -0
  150. package/src/composables/private/__tests__/use-anchor.spec.js +99 -0
  151. package/src/composables/private/__tests__/use-field.spec.js +351 -0
  152. package/src/composables/private/__tests__/use-file.spec.js +69 -0
  153. package/src/composables/private/__tests__/use-form.spec.js +11 -0
  154. package/src/composables/private/__tests__/use-fullscreen.spec.js +37 -0
  155. package/src/composables/private/__tests__/use-model-toggle.spec.js +306 -0
  156. package/src/composables/private/__tests__/use-portal.spec.js +4 -0
  157. package/src/composables/private/__tests__/use-router-link.spec.js +55 -0
  158. package/src/composables/private/__tests__/use-size.spec.js +37 -0
  159. package/src/composables/private/__tests__/use-transition.spec.js +108 -0
  160. package/src/composables/private/__tests__/use-validate.spec.js +111 -0
  161. package/src/composables/private/use-fullscreen.js +1 -1
  162. package/src/composables/private/use-model-toggle.js +1 -1
  163. package/src/composables/private/use-validate.js +22 -22
  164. package/src/plugins/Meta.js +1 -1
  165. package/src/plugins/Screen.js +16 -9
  166. package/src/utils/date.js +111 -32
  167. package/src/utils/private/vm.js +14 -8
@@ -0,0 +1,2003 @@
1
+ /* eslint-disable no-unused-expressions */
2
+ import { mount } from '@cypress/vue'
3
+ import { ref, h } from 'vue'
4
+ import WrapperOne from './WrapperOne.vue'
5
+
6
+ // QSelect does not set the `data-cy` attribute on the root element, but on the `.q-field__native` element
7
+ // This means we cannot use data-cy everywhere, but instead use a custom class `select-root` for this purpose
8
+ describe('QSelect API', () => {
9
+ describe('Props', () => {
10
+ describe('Category: behavior', () => {
11
+ describe('(prop): fill-input', () => {
12
+ it.skip(' ', () => {
13
+ //
14
+ })
15
+ })
16
+ describe('(prop): new-value-mode', () => {
17
+ it.skip(' ', () => {
18
+ //
19
+ })
20
+ })
21
+ describe('(prop): autocomplete', () => {
22
+ it.skip(' ', () => {
23
+ //
24
+ })
25
+ })
26
+ describe('(prop): transition-show', () => {
27
+ it.skip(' ', () => {
28
+ //
29
+ })
30
+ })
31
+ describe('(prop): transition-hide', () => {
32
+ it.skip(' ', () => {
33
+ //
34
+ })
35
+ })
36
+ describe('(prop): transition-duration', () => {
37
+ it.skip(' ', () => {
38
+ //
39
+ })
40
+ })
41
+ describe('(prop): behavior', () => {
42
+ it.skip(' ', () => {
43
+ //
44
+ })
45
+ })
46
+ })
47
+
48
+ describe('Category: content', () => {
49
+ describe('(prop): dropdown-icon', () => {
50
+ it('should use the dropdown-icon supplied', () => {
51
+ const icon = 'map'
52
+ mount(WrapperOne, {
53
+ attrs: {
54
+ dropdownIcon: icon
55
+ }
56
+ })
57
+ cy.get('.select-root')
58
+ .get(`div:contains(${ icon })`)
59
+ .should('exist')
60
+ })
61
+ })
62
+
63
+ describe('(prop): use-input', () => {
64
+ it('should render an input inside the select', () => {
65
+ mount(WrapperOne, {
66
+ attrs: {
67
+ useInput: true
68
+ }
69
+ })
70
+ cy.get('.select-root')
71
+ .get('input')
72
+ .should('exist')
73
+ })
74
+
75
+ it('should not render an input by default', () => {
76
+ mount(WrapperOne)
77
+ cy.get('.select-root')
78
+ .get('input')
79
+ .should('not.exist')
80
+ })
81
+ })
82
+
83
+ describe('(prop): input-debounce', () => {
84
+ it('should use an input-debounce of 500ms by default', () => {
85
+ const fn = cy.stub()
86
+ const text = 'Hello there'
87
+ mount(WrapperOne, {
88
+ attrs: {
89
+ useInput: true,
90
+ onFilter: fn
91
+ }
92
+ })
93
+ cy.get('.select-root')
94
+ .get('input')
95
+ .type(text)
96
+ .then(() => {
97
+ expect(fn).not.to.be.calledWith(text)
98
+ })
99
+ .wait(500)
100
+ .then(() => {
101
+ expect(fn).to.be.calledWith(text)
102
+ })
103
+ })
104
+
105
+ it('should use a custom input-debounce', () => {
106
+ const fn = cy.stub()
107
+ const text = 'Hello there'
108
+ mount(WrapperOne, {
109
+ attrs: {
110
+ useInput: true,
111
+ onFilter: fn,
112
+ inputDebounce: 800
113
+ }
114
+ })
115
+ cy.get('.select-root')
116
+ .get('input')
117
+ .type(text)
118
+ .wait(500)
119
+ .then(() => {
120
+ expect(fn).not.to.be.calledWith(text)
121
+ })
122
+ .wait(300)
123
+ .then(() => {
124
+ expect(fn).to.be.calledWith(text)
125
+ })
126
+ })
127
+ })
128
+ })
129
+
130
+ describe('Category: content|behavior', () => {
131
+ describe('(prop): hide-dropdown-icon', () => {
132
+ it('should show the dropdown-icon when this property is not supplied', () => {
133
+ mount(WrapperOne)
134
+ cy.get('.select-root')
135
+ .get('.q-icon')
136
+ .should('exist')
137
+ })
138
+
139
+ it('should hide the dropdown-icon when this property is true', () => {
140
+ mount(WrapperOne, {
141
+ attrs: {
142
+ hideDropdownIcon: true
143
+ }
144
+ })
145
+ cy.get('.select-root')
146
+ .get('.q-icon')
147
+ .should('not.exist')
148
+ })
149
+ })
150
+ })
151
+
152
+ describe('Category: general', () => {
153
+ describe('(prop): tabindex', () => {
154
+ it('should have a default tabindex of 0', () => {
155
+ mount(WrapperOne)
156
+ cy.get('.select-root [tabindex="0"]')
157
+ .should('exist')
158
+ })
159
+
160
+ it('should set the tabindex to the supplied value', () => {
161
+ const tabindex = 2
162
+ mount(WrapperOne, {
163
+ attrs: {
164
+ tabindex
165
+ }
166
+ })
167
+ cy.get(`.select-root [tabindex="${ tabindex }"]`)
168
+ .should('exist')
169
+ cy.get('.select-root [tabindex="0"]')
170
+ .should('not.exist')
171
+ })
172
+ })
173
+ })
174
+
175
+ describe('Category: model', () => {
176
+ describe('(prop): model-value', () => {
177
+ it('should have the option selected passed in the model-value', () => {
178
+ const modelValue = 'Option 1'
179
+ mount(WrapperOne, {
180
+ attrs: {
181
+ modelValue,
182
+ options: [ 'Option 1', 'Option 2', 'Option 3' ]
183
+ }
184
+ })
185
+ cy.get('.select-root')
186
+ .should('include.text', modelValue)
187
+ })
188
+ })
189
+
190
+ describe('(prop): emit-value', () => {
191
+ it('should emit the value under the value key, if options are objects', () => {
192
+ const fn = cy.stub()
193
+ mount(WrapperOne, {
194
+ attrs: {
195
+ emitValue: true,
196
+ 'onUpdate:modelValue': fn,
197
+ options: [ { label: 'Option 1', value: 1 }, { label: 'Option 2', value: 2 } ]
198
+ }
199
+ })
200
+ cy.get('.select-root')
201
+ .click()
202
+ cy.get('.q-menu')
203
+ .contains('Option 1')
204
+ .click()
205
+ .then(() => {
206
+ expect(fn).to.have.been.calledWith(1)
207
+ })
208
+ })
209
+
210
+ it('should emit the whole object by default if options are objects', () => {
211
+ const fn = cy.stub()
212
+ const options = [ { label: 'Option 1', value: 1 }, { label: 'Option 2', value: 2 } ]
213
+ mount(WrapperOne, {
214
+ attrs: {
215
+ 'onUpdate:modelValue': fn,
216
+ options
217
+ }
218
+ })
219
+ cy.get('.select-root')
220
+ .click()
221
+ cy.get('.q-menu')
222
+ .contains('Option 1')
223
+ .click()
224
+ .then(() => {
225
+ expect(fn).to.have.been.calledWith(options[ 0 ])
226
+ })
227
+ })
228
+ })
229
+ })
230
+
231
+ describe('Category: model|selection', () => {
232
+ describe('(prop): multiple', () => {
233
+ it('should select only one option by default', () => {
234
+ const options = [ 'Option 1', 'Option 2' ]
235
+ const model = ref(null)
236
+ mount(WrapperOne, {
237
+ attrs: {
238
+ modelValue: model,
239
+ 'onUpdate:modelValue': (val) => {
240
+ model.value = val
241
+ },
242
+ options
243
+ }
244
+ })
245
+ cy.get('.select-root')
246
+ .click()
247
+ cy.get('.q-menu')
248
+ .contains('Option 1')
249
+ .click()
250
+ .then(() => {
251
+ expect(model.value).to.equal(options[ 0 ])
252
+ })
253
+ cy.get('.q-menu')
254
+ .contains('Option 2')
255
+ .click()
256
+ .then(() => {
257
+ expect(model.value).to.equal(options[ 1 ])
258
+ })
259
+ })
260
+
261
+ it('should select multiple options if multiple is true', () => {
262
+ const options = [ 'Option 1', 'Option 2' ]
263
+ const model = ref([])
264
+ mount(WrapperOne, {
265
+ attrs: {
266
+ multiple: true,
267
+ modelValue: model,
268
+ 'onUpdate:modelValue': (val) => {
269
+ model.value = val
270
+ },
271
+ options
272
+ }
273
+ })
274
+ cy.get('.select-root')
275
+ .click()
276
+ cy.get('.q-menu')
277
+ .contains('Option 1')
278
+ .click()
279
+ .then(() => {
280
+ expect(model.value).to.eql([ options[ 0 ] ])
281
+ })
282
+ cy.get('.q-menu')
283
+ .contains('Option 2')
284
+ .click()
285
+ .then(() => {
286
+ expect(model.value).to.eql(options)
287
+ })
288
+ })
289
+ })
290
+ })
291
+
292
+ describe('Category: options', () => {
293
+ describe('(prop): options', () => {
294
+ it('should show each option when opening the dropdown', () => {
295
+ const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ]
296
+ mount(WrapperOne, {
297
+ attrs: {
298
+ options
299
+ }
300
+ })
301
+ cy.get('.select-root')
302
+ .click()
303
+ cy.get('.q-menu')
304
+ .children()
305
+ .should('contain', options[ 0 ])
306
+ .and('contain', options[ 1 ])
307
+ .and('contain', options[ 2 ])
308
+ .and('contain', options[ 3 ])
309
+ })
310
+ })
311
+
312
+ describe('(prop): option-value', () => {
313
+ it('should use the value key as option-value by default', () => {
314
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
315
+ const model = ref(null)
316
+ mount(WrapperOne, {
317
+ attrs: {
318
+ options,
319
+ emitValue: true,
320
+ modelValue: model,
321
+ 'onUpdate:modelValue': (val) => {
322
+ model.value = val
323
+ }
324
+ }
325
+ })
326
+ cy.get('.select-root')
327
+ .click()
328
+ cy.get('.q-menu')
329
+ .contains(options[ 0 ].label)
330
+ .click()
331
+ .then(() => {
332
+ expect(model.value).to.equal(options[ 0 ].value)
333
+ })
334
+ })
335
+
336
+ it('should use a custom key supplied by option-value', () => {
337
+ const options = [ { label: 'Option one', test: 1 }, { label: 'Option two', test: 2 } ]
338
+ const model = ref(null)
339
+ mount(WrapperOne, {
340
+ attrs: {
341
+ options,
342
+ emitValue: true,
343
+ optionValue: 'test',
344
+ modelValue: model,
345
+ 'onUpdate:modelValue': (val) => {
346
+ model.value = val
347
+ }
348
+ }
349
+ })
350
+ cy.get('.select-root')
351
+ .click()
352
+ cy.get('.q-menu')
353
+ .contains(options[ 0 ].label)
354
+ .click()
355
+ .then(() => {
356
+ expect(model.value).to.equal(options[ 0 ].test)
357
+ })
358
+ })
359
+
360
+ it('should accept a function as option-value', () => {
361
+ const options = [ { label: 'Option one', test: 1 }, { label: 'Option two', test: 2 } ]
362
+ const model = ref(null)
363
+ mount(WrapperOne, {
364
+ attrs: {
365
+ options,
366
+ emitValue: true,
367
+ optionValue: (val) => val.test,
368
+ modelValue: model,
369
+ 'onUpdate:modelValue': (val) => {
370
+ model.value = val
371
+ }
372
+ }
373
+ })
374
+ cy.get('.select-root')
375
+ .click()
376
+ cy.get('.q-menu')
377
+ .contains(options[ 0 ].label)
378
+ .click()
379
+ .then(() => {
380
+ expect(model.value).to.equal(options[ 0 ].test)
381
+ })
382
+ })
383
+ })
384
+
385
+ describe('(prop): option-label', () => {
386
+ it('should use the "label" key by default as option-label', () => {
387
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
388
+ mount(WrapperOne, {
389
+ attrs: {
390
+ options
391
+ }
392
+ })
393
+ cy.get('.select-root')
394
+ .click()
395
+ cy.get('.q-menu')
396
+ .children()
397
+ .should('contain', options[ 0 ].label)
398
+ .and('contain', options[ 1 ].label)
399
+ })
400
+
401
+ it('should use the key supplied by option-label', () => {
402
+ const options = [ { test: 'Option one', value: 1 }, { test: 'Option two', value: 2 } ]
403
+ mount(WrapperOne, {
404
+ attrs: {
405
+ options,
406
+ optionLabel: 'test'
407
+ }
408
+ })
409
+ cy.get('.select-root')
410
+ .click()
411
+ cy.get('.q-menu')
412
+ .children()
413
+ .should('contain', options[ 0 ].test)
414
+ .and('contain', options[ 1 ].test)
415
+ })
416
+
417
+ it('should accept a function as option-label', () => {
418
+ const options = [ { test: 'Option one', value: 1 }, { test: 'Option two', value: 2 } ]
419
+ mount(WrapperOne, {
420
+ attrs: {
421
+ options,
422
+ optionLabel: (item) => (item === null ? 'Null' : item.test)
423
+ }
424
+ })
425
+ cy.get('.select-root')
426
+ .click()
427
+ cy.get('.q-menu')
428
+ .children()
429
+ .should('contain', options[ 0 ].test)
430
+ .and('contain', options[ 1 ].test)
431
+ })
432
+ })
433
+ describe('(prop): option-disable', () => {
434
+ it('should use the "disable" key by default as option-disable', () => {
435
+ const options = [ { label: 'Option one', value: 1, disable: true }, { label: 'Option two', value: 2, disable: true } ]
436
+ mount(WrapperOne, {
437
+ attrs: {
438
+ options
439
+ }
440
+ })
441
+ cy.get('.select-root')
442
+ .click()
443
+ cy.get('.q-menu')
444
+ .get('[role="option"][aria-disabled="true"]')
445
+ .should('have.length', 2)
446
+ })
447
+
448
+ it('should use the key supplied by option-disable', () => {
449
+ const options = [ { label: 'Option one', value: 1, test: true }, { label: 'Option two', value: 2, disable: true } ]
450
+ mount(WrapperOne, {
451
+ attrs: {
452
+ options,
453
+ optionDisable: 'test'
454
+ }
455
+ })
456
+ cy.get('.select-root')
457
+ .click()
458
+ cy.get('.q-menu')
459
+ .get('[role="option"][aria-disabled="true"]')
460
+ .should('have.length', 1)
461
+ .should('have.text', options[ 0 ].label)
462
+ })
463
+
464
+ it('should accept a function as option-disable', () => {
465
+ const options = [ { label: 'Option one', value: 1, test: true }, { label: 'Option two', value: 2, disable: true } ]
466
+ mount(WrapperOne, {
467
+ attrs: {
468
+ options,
469
+ optionDisable: (item) => (item === null ? true : item.test)
470
+ }
471
+ })
472
+ cy.get('.select-root')
473
+ .click()
474
+ cy.get('.q-menu')
475
+ .get('[role="option"][aria-disabled="true"]')
476
+ .should('have.length', 1)
477
+ .should('have.text', options[ 0 ].label)
478
+ })
479
+ })
480
+
481
+ describe('(prop): options-dense', () => {
482
+ it('should show options list dense', () => {
483
+ const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ]
484
+ mount(WrapperOne, {
485
+ attrs: {
486
+ options,
487
+ optionsDense: true
488
+ }
489
+ })
490
+ cy.get('.select-root')
491
+ .click()
492
+ cy.get('.q-menu')
493
+ .get('.q-item')
494
+ .should('have.class', 'q-item--dense')
495
+ })
496
+ })
497
+
498
+ describe('(prop): options-dark', () => {
499
+ it('should show options list in dark mode', () => {
500
+ const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ]
501
+ mount(WrapperOne, {
502
+ attrs: {
503
+ options,
504
+ optionsDark: true
505
+ }
506
+ })
507
+ cy.get('.select-root')
508
+ .click()
509
+ cy.get('.q-menu')
510
+ .get('.q-item')
511
+ .should('have.class', 'q-item--dark')
512
+ })
513
+ })
514
+
515
+ describe('(prop): options-selected-class', () => {
516
+ it('should have text-{color} applied as selected by default', () => {
517
+ const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ]
518
+ mount(WrapperOne, {
519
+ attrs: {
520
+ options,
521
+ modelValue: 'Option 1',
522
+ color: 'orange'
523
+ }
524
+ })
525
+ cy.get('.select-root')
526
+ .click()
527
+ cy.get('.q-menu')
528
+ .contains('[role="option"]', options[ 0 ])
529
+ .should('have.class', 'text-orange')
530
+ })
531
+
532
+ it('should not have default active class when passed option is empty', () => {
533
+ const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ]
534
+ mount(WrapperOne, {
535
+ attrs: {
536
+ options,
537
+ modelValue: 'Option 1',
538
+ optionsSelectedClass: '',
539
+ color: 'orange'
540
+ }
541
+ })
542
+ cy.get('.select-root')
543
+ .click()
544
+ cy.get('.q-menu')
545
+ .contains('[role="option"]', options[ 0 ])
546
+ .should('not.have.class', 'text-orange')
547
+ })
548
+
549
+ it('should have class name supplied by options-selected-class on active item', () => {
550
+ const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ]
551
+ mount(WrapperOne, {
552
+ attrs: {
553
+ options,
554
+ modelValue: 'Option 1',
555
+ optionsSelectedClass: 'test-class',
556
+ color: 'orange'
557
+ }
558
+ })
559
+ cy.get('.select-root')
560
+ .click()
561
+ cy.get('.q-menu')
562
+ .contains('[role="option"]', options[ 0 ])
563
+ .should('not.have.class', 'text-orange')
564
+ .should('have.class', 'test-class')
565
+ cy.get('.q-menu')
566
+ .contains('[role="option"]', options[ 1 ])
567
+ .should('not.have.class', 'text-orange')
568
+ .should('not.have.class', 'test-class')
569
+ })
570
+ })
571
+
572
+ describe('(prop): options-html', () => {
573
+ it('should not render options with html by default', () => {
574
+ const options = [ '<b style="color: red">Option 1</b>', 'Option 2' ]
575
+ mount(WrapperOne, {
576
+ attrs: {
577
+ options
578
+ }
579
+ })
580
+ cy.get('.select-root')
581
+ .click()
582
+ cy.get('.q-menu')
583
+ .contains('Option 1')
584
+ .should('have.color', 'black')
585
+ .should('not.have.css', 'font-weight', '700')
586
+ })
587
+
588
+ it('should render options with html when options-html is true', () => {
589
+ const options = [ '<b style="color: red">Option 1</b>', 'Option 2' ]
590
+ mount(WrapperOne, {
591
+ attrs: {
592
+ options,
593
+ optionsHtml: true
594
+ }
595
+ })
596
+ cy.get('.select-root')
597
+ .click()
598
+ cy.get('.q-menu')
599
+ .contains('Option 1')
600
+ .should('have.color', 'red')
601
+ .should('have.css', 'font-weight', '700')
602
+ })
603
+ })
604
+
605
+ describe('(prop): options-cover', () => {
606
+ it('should make the popup menu cover the select', (done) => {
607
+ const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ]
608
+ mount(WrapperOne, {
609
+ attrs: {
610
+ options,
611
+ optionsCover: true
612
+ }
613
+ })
614
+ cy.get('.select-root')
615
+ .click()
616
+ .isNotActionable(done)
617
+ })
618
+
619
+ it('should not make the popup menu cover the select when use-input is used', () => {
620
+ const options = [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ]
621
+ mount(WrapperOne, {
622
+ attrs: {
623
+ options,
624
+ optionsCover: true,
625
+ useInput: true
626
+ }
627
+ })
628
+ cy.get('.select-root')
629
+ .click()
630
+ .click({ timeout: 100 })
631
+ })
632
+ })
633
+
634
+ describe('(prop): menu-shrink', () => {
635
+ it('should shrink the menu', () => {
636
+ const options = [ '1', '2', '3', '4' ]
637
+ mount(WrapperOne, {
638
+ attrs: {
639
+ options,
640
+ menuShrink: true
641
+ }
642
+ })
643
+ let selectWidth = 0
644
+ cy.get('.select-root')
645
+ .then(($el) => {
646
+ selectWidth = $el[ 0 ].clientWidth
647
+ })
648
+ .click()
649
+ cy.get('.q-menu')
650
+ .then(($el) => {
651
+ expect($el[ 0 ].clientWidth).to.below(selectWidth)
652
+ })
653
+ })
654
+ })
655
+
656
+ describe('(prop): map-options', () => {
657
+ it('should display the label of the selected value instead of the value itself', () => {
658
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
659
+ mount(WrapperOne, {
660
+ attrs: {
661
+ options,
662
+ modelValue: 1,
663
+ mapOptions: true
664
+ }
665
+ })
666
+ cy.get('.select-root')
667
+ .contains(options[ 0 ].label)
668
+ })
669
+
670
+ it('should display the selected value as string by default', () => {
671
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
672
+ mount(WrapperOne, {
673
+ attrs: {
674
+ options,
675
+ modelValue: 1
676
+ }
677
+ })
678
+ cy.get('.select-root')
679
+ .contains(options[ 0 ].value)
680
+ })
681
+ })
682
+ })
683
+
684
+ describe('Category: position', () => {
685
+ describe.skip('(prop): menu-anchor', () => {
686
+ // This is a menu property which is tested in QMenu.spec.js
687
+ })
688
+
689
+ describe.skip('(prop): menu-self', () => {
690
+ // This is a menu property which is tested in QMenu.spec.js
691
+ })
692
+
693
+ describe.skip('(prop): menu-offset', () => {
694
+ // This is a menu property which is tested in QMenu.spec.js
695
+ })
696
+ })
697
+
698
+ describe('Category: selection', () => {
699
+ describe('(prop): display-value', () => {
700
+ it('should override the default selection string', () => {
701
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
702
+ mount(WrapperOne, {
703
+ attrs: {
704
+ options,
705
+ modelValue: 1,
706
+ displayValue: 'Test'
707
+ }
708
+ })
709
+ cy.get('.select-root')
710
+ .should('not.contain', options[ 0 ].value)
711
+ .should('contain', 'Test')
712
+ })
713
+
714
+ it('should not override the default selection string when using `use-chips`', () => {
715
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
716
+ mount(WrapperOne, {
717
+ attrs: {
718
+ options,
719
+ modelValue: 1,
720
+ displayValue: 'Test',
721
+ useChips: true
722
+ }
723
+ })
724
+ cy.get('.select-root')
725
+ .should('contain', options[ 0 ].value)
726
+ .should('not.contain', 'Test')
727
+ })
728
+
729
+ it('should not override the default selection string when using `selected` slot', () => {
730
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
731
+ mount(WrapperOne, {
732
+ attrs: {
733
+ options,
734
+ modelValue: 1,
735
+ displayValue: 'Test'
736
+ },
737
+ slots: {
738
+ selected: () => 'Hello there'
739
+ }
740
+ })
741
+ cy.get('.select-root')
742
+ .should('not.contain', options[ 0 ].value)
743
+ .should('not.contain', 'Test')
744
+ .should('contain', 'Hello there')
745
+ })
746
+ })
747
+
748
+ describe('(prop): display-value-html', () => {
749
+ it('should render the selected option as html', () => {
750
+ const options = [ '<b style="color: red">Option 1</b>', 'Option 2' ]
751
+ mount(WrapperOne, {
752
+ attrs: {
753
+ options,
754
+ modelValue: options[ 0 ],
755
+ displayValueHtml: true
756
+ }
757
+ })
758
+ cy.get('.select-root')
759
+ .contains('Option 1')
760
+ .should('have.color', 'red')
761
+ .should('have.css', 'font-weight', '700')
762
+ })
763
+
764
+ it('should not render the selected option as html when using `selected` slot', () => {
765
+ const html = '<b style="color: red">Option 1</b>'
766
+ const options = [ 'Option 1', 'Option 2' ]
767
+ mount(WrapperOne, {
768
+ attrs: {
769
+ options,
770
+ modelValue: options[ 0 ],
771
+ displayValueHtml: true
772
+ },
773
+ slots: {
774
+ selected: () => html
775
+ }
776
+ })
777
+ cy.get('.select-root')
778
+ .contains(html)
779
+ })
780
+
781
+ it('should not render the selected option as html when using `selected-item` slot', () => {
782
+ const html = '<b style="color: red">Option 1</b>'
783
+ const options = [ 'Option 1', 'Option 2' ]
784
+ mount(WrapperOne, {
785
+ attrs: {
786
+ options,
787
+ modelValue: options[ 0 ],
788
+ displayValueHtml: true
789
+ },
790
+ slots: {
791
+ 'selected-item': () => html
792
+ }
793
+ })
794
+ cy.get('.select-root')
795
+ .contains(html)
796
+ })
797
+ })
798
+
799
+ describe('(prop): hide-selected', () => {
800
+ it('should not show the selected value', () => {
801
+ const options = [ 'Option 1', 'Option 2' ]
802
+ mount(WrapperOne, {
803
+ attrs: {
804
+ options,
805
+ modelValue: options[ 0 ],
806
+ hideSelected: true
807
+ }
808
+ })
809
+ cy.get('.select-root')
810
+ .should('not.contain', options[ 0 ])
811
+ })
812
+
813
+ it('should set the value on the underlying input when using hide-selected', () => {
814
+ // Todo: it its not really clear from the docs that you need to use `useInput` and `fillInput` together with this prop to achieve this
815
+ const options = [ 'Option 1', 'Option 2' ]
816
+ mount(WrapperOne, {
817
+ attrs: {
818
+ options,
819
+ modelValue: options[ 0 ],
820
+ hideSelected: true,
821
+ fillInput: true,
822
+ useInput: true
823
+ }
824
+ })
825
+ cy.get('.select-root')
826
+ .get('input')
827
+ .should('have.value', options[ 0 ])
828
+ })
829
+ })
830
+
831
+ describe('(prop): max-values', () => {
832
+ it('should allow a maximum number of selections', () => {
833
+ const max = 3
834
+ const options = [ '1', '2', '3', '4', '5' ]
835
+ const modelValue = ref([])
836
+ mount(WrapperOne, {
837
+ attrs: {
838
+ options,
839
+ modelValue,
840
+ maxValues: max,
841
+ 'onUpdate:modelValue': (val) => {
842
+ modelValue.value = val
843
+ },
844
+ multiple: true
845
+ }
846
+ })
847
+ cy.get('.select-root')
848
+ .click()
849
+ cy.get('.q-menu')
850
+ .get('[role="option"]')
851
+ .click({ multiple: true })
852
+ .then(() => {
853
+ expect(modelValue.value.length).to.equal(max)
854
+ })
855
+ })
856
+ })
857
+
858
+ describe('(prop): use-chips', () => {
859
+ it('should use QChips to show the selected value', () => {
860
+ const options = [ 'Option 1', 'Option 2' ]
861
+ mount(WrapperOne, {
862
+ attrs: {
863
+ options,
864
+ modelValue: options[ 0 ],
865
+ useChips: true
866
+ }
867
+ })
868
+ cy.get('.select-root')
869
+ .get('.q-chip')
870
+ .should('contain', options[ 0 ])
871
+ })
872
+ })
873
+ })
874
+
875
+ describe('Category: style', () => {
876
+ describe('(prop): popup-content-class', () => {
877
+ it('should apply the class to the popup element', () => {
878
+ const className = 'test-class'
879
+ mount(WrapperOne, {
880
+ attrs: {
881
+ options: [ '1', '2 ' ],
882
+ popupContentClass: className
883
+ }
884
+ })
885
+ cy.get('.select-root')
886
+ .click()
887
+ cy.get('.q-menu')
888
+ .should('have.class', className)
889
+ })
890
+ })
891
+
892
+ describe('(prop): popup-content-style', () => {
893
+ it('should apply the style definitions to the popup element', () => {
894
+ const style = 'background: red;'
895
+ mount(WrapperOne, {
896
+ attrs: {
897
+ options: [ '1', '2 ' ],
898
+ popupContentStyle: style
899
+ }
900
+ })
901
+ cy.get('.select-root')
902
+ .click()
903
+ cy.get('.q-menu')
904
+ .should('have.backgroundColor', 'red')
905
+ })
906
+ })
907
+
908
+ describe('(prop): input-class', () => {
909
+ it('should apply a class to the input element when using `useInput`', () => {
910
+ const className = 'test-class'
911
+ mount(WrapperOne, {
912
+ attrs: {
913
+ useInput: true,
914
+ inputClass: className
915
+ }
916
+ })
917
+ cy.get('.select-root')
918
+ .get('input')
919
+ .should('have.class', className)
920
+ })
921
+ })
922
+
923
+ describe('(prop): input-style', () => {
924
+ it('should apply a style to the input element when using `useInput`', () => {
925
+ const style = 'font-size: 30px'
926
+ mount(WrapperOne, {
927
+ attrs: {
928
+ useInput: true,
929
+ inputStyle: style
930
+ }
931
+ })
932
+ cy.get('.select-root')
933
+ .get('input')
934
+ .should('have.css', 'font-size', '30px')
935
+ })
936
+ })
937
+ })
938
+ })
939
+
940
+ describe('Slots', () => {
941
+ describe('(slot): selected', () => {
942
+ it('should display when something is selected', () => {
943
+ const selectedString = 'Test slot selected'
944
+ const options = [ 'Option 1', 'Option 2' ]
945
+ mount(WrapperOne, {
946
+ attrs: {
947
+ options,
948
+ modelValue: options[ 0 ]
949
+ },
950
+ slots: {
951
+ selected: () => selectedString
952
+ }
953
+ })
954
+ cy.get('.select-root')
955
+ .should('contain', selectedString)
956
+ })
957
+ })
958
+
959
+ describe('(slot): loading', () => {
960
+ it('should display when element is loading', () => {
961
+ const loadingString = 'Test slot loading'
962
+ mount(WrapperOne, {
963
+ attrs: {
964
+ loading: true
965
+ },
966
+ slots: {
967
+ loading: () => loadingString
968
+ }
969
+ })
970
+ cy.get('.select-root')
971
+ .should('contain', loadingString)
972
+ })
973
+
974
+ it('should not display when element is loading', () => {
975
+ const loadingString = 'Test slot loading'
976
+ mount(WrapperOne, {
977
+ attrs: {
978
+ loading: false
979
+ },
980
+ slots: {
981
+ loading: () => loadingString
982
+ }
983
+ })
984
+ cy.get('.select-root')
985
+ .should('not.contain', loadingString)
986
+ })
987
+ })
988
+
989
+ describe('(slot): before-options', () => {
990
+ it('should display the slot content before the options', () => {
991
+ mount(WrapperOne, {
992
+ attrs: {
993
+ options: [ '1', '2', '3' ]
994
+ },
995
+ slots: {
996
+ 'before-options': () => h('div', { class: 'dummyClass' }, 'Hello')
997
+ }
998
+ })
999
+ cy.get('.select-root')
1000
+ .click()
1001
+ cy.get('.q-menu')
1002
+ .children().first()
1003
+ .should('have.class', 'dummyClass')
1004
+ })
1005
+ })
1006
+
1007
+ describe('(slot): after-options', () => {
1008
+ it('should display the slot content after the options', () => {
1009
+ mount(WrapperOne, {
1010
+ attrs: {
1011
+ options: [ '1', '2', '3' ]
1012
+ },
1013
+ slots: {
1014
+ 'after-options': () => h('div', { class: 'dummyClass' }, 'Hello')
1015
+ }
1016
+ })
1017
+ cy.get('.select-root')
1018
+ .click()
1019
+ cy.get('.q-menu')
1020
+ .children().last()
1021
+ .should('have.class', 'dummyClass')
1022
+ })
1023
+ })
1024
+
1025
+ describe('(slot): no-option', () => {
1026
+ it('should display the slot content when there are no options', () => {
1027
+ const compareString = 'No options :('
1028
+ mount(WrapperOne, {
1029
+ attrs: {
1030
+ options: [ ]
1031
+ },
1032
+ slots: {
1033
+ 'no-option': () => compareString
1034
+ }
1035
+ })
1036
+ cy.get('.select-root')
1037
+ .click()
1038
+ cy.get('.q-menu')
1039
+ .should('contain', compareString)
1040
+ })
1041
+
1042
+ it('should pass the inputValue to the slot scope', () => {
1043
+ const compareString = 'No options :('
1044
+ mount(WrapperOne, {
1045
+ attrs: {
1046
+ options: [ ],
1047
+ useInput: true
1048
+ },
1049
+ slots: {
1050
+ 'no-option': (scope) => compareString + scope.inputValue
1051
+ }
1052
+ })
1053
+ cy.get('.select-root')
1054
+ .click()
1055
+ .type('Hello')
1056
+ cy.get('.q-menu')
1057
+ .should('contain', compareString + 'Hello')
1058
+ })
1059
+
1060
+ it('should not display the slot content when there are options', () => {
1061
+ const compareString = 'No options :('
1062
+ mount(WrapperOne, {
1063
+ attrs: {
1064
+ options: [ '1', '2', '3' ]
1065
+ },
1066
+ slots: {
1067
+ 'no-option': () => compareString
1068
+ }
1069
+ })
1070
+ cy.get('.select-root')
1071
+ .click()
1072
+ cy.get('.q-menu')
1073
+ .should('not.contain', compareString)
1074
+ })
1075
+ })
1076
+
1077
+ describe('(slot): selected-item', () => {
1078
+ it('should override the default selection slot', () => {
1079
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
1080
+ mount(WrapperOne, {
1081
+ attrs: {
1082
+ options,
1083
+ modelValue: 1
1084
+ },
1085
+ slots: {
1086
+ 'selected-item': () => 'Test'
1087
+ }
1088
+ })
1089
+ cy.get('.select-root')
1090
+ .should('not.contain', options[ 0 ].value)
1091
+ .should('contain', 'Test')
1092
+ })
1093
+
1094
+ it('should pass the selected option index to the slot scope', () => {
1095
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
1096
+ mount(WrapperOne, {
1097
+ attrs: {
1098
+ options,
1099
+ modelValue: 1
1100
+ },
1101
+ slots: {
1102
+ 'selected-item': (scope) => 'Test' + scope.index
1103
+ }
1104
+ })
1105
+ cy.get('.select-root')
1106
+ .should('contain', 'Test0')
1107
+ })
1108
+
1109
+ it('should pass the selected option value to the slot scope', () => {
1110
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
1111
+ mount(WrapperOne, {
1112
+ attrs: {
1113
+ options,
1114
+ modelValue: 1
1115
+ },
1116
+ slots: {
1117
+ 'selected-item': (scope) => 'Test' + scope.opt
1118
+ }
1119
+ })
1120
+ cy.get('.select-root')
1121
+ .should('contain', 'Test1')
1122
+ })
1123
+
1124
+ it('should pass a removeAtIndex function to the slot scope', () => {
1125
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
1126
+ const model = ref(1)
1127
+ mount(WrapperOne, {
1128
+ attrs: {
1129
+ options,
1130
+ modelValue: model,
1131
+ 'onUpdate:modelValue': (val) => {
1132
+ model.value = val
1133
+ }
1134
+ },
1135
+ slots: {
1136
+ 'selected-item': (scope) => h('button', { class: 'remove', onClick: () => scope.removeAtIndex(scope.index) }, 'Remove')
1137
+ }
1138
+ })
1139
+ cy.get('.select-root')
1140
+ .get('button.remove')
1141
+ .click()
1142
+ cy.get('.select-root')
1143
+ .get('button.remove')
1144
+ .should('not.exist')
1145
+ })
1146
+
1147
+ it('should pass a toggleOption function to the slot scope', () => {
1148
+ const options = [ { label: 'Option one', value: 1 }, { label: 'Option two', value: 2 } ]
1149
+ const model = ref(1)
1150
+ mount(WrapperOne, {
1151
+ attrs: {
1152
+ options,
1153
+ modelValue: model,
1154
+ 'onUpdate:modelValue': (val) => {
1155
+ model.value = val
1156
+ }
1157
+ },
1158
+ slots: {
1159
+ 'selected-item': (scope) => h('button', { class: 'toggle', onClick: () => scope.toggleOption(2) }, 'Toggle' + scope.opt)
1160
+ }
1161
+ })
1162
+ cy.get('.select-root')
1163
+ .get('button.toggle')
1164
+ .should('contain', 'Toggle1')
1165
+ .click()
1166
+ cy.get('.select-root')
1167
+ .get('button.toggle')
1168
+ .should('contain', 'Toggle2')
1169
+ })
1170
+ })
1171
+
1172
+ describe('(slot): option', () => {
1173
+ it('should render a list of the provided slot as options', () => {
1174
+ const options = [ '1', '2', '3' ]
1175
+ mount(WrapperOne, {
1176
+ attrs: {
1177
+ options
1178
+ },
1179
+ slots: {
1180
+ option: (scope) => h('div', { class: 'custom-option' }, scope.opt)
1181
+ }
1182
+ })
1183
+ cy.get('.select-root')
1184
+ .click()
1185
+ cy.get('.q-menu')
1186
+ .get('.custom-option')
1187
+ .should('have.length', options.length)
1188
+ })
1189
+
1190
+ it('should have a selected property in the scope', () => {
1191
+ const options = [ '1', '2', '3' ]
1192
+ mount(WrapperOne, {
1193
+ attrs: {
1194
+ modelValue: '1',
1195
+ options
1196
+ },
1197
+ slots: {
1198
+ option: (scope) => h('div', { class: `custom-option-${ scope.selected }` }, scope.opt + scope.selected)
1199
+ }
1200
+ })
1201
+ cy.get('.select-root')
1202
+ .click()
1203
+ cy.get('.q-menu')
1204
+ .get('.custom-option-true')
1205
+ .should('have.length', 1)
1206
+ .should('contain', options[ 0 ])
1207
+ })
1208
+ })
1209
+ })
1210
+
1211
+ describe('Events', () => {
1212
+ describe('(event): update:model-value', () => {
1213
+ it('should emit event when model value changes', () => {
1214
+ const fn = cy.stub()
1215
+ mount(WrapperOne, {
1216
+ attrs: {
1217
+ options: [ '1', '2', '3' ],
1218
+ modelValue: null,
1219
+ 'onUpdate:modelValue': fn
1220
+ }
1221
+ })
1222
+
1223
+ expect(fn).not.to.be.called
1224
+ cy.get('.select-root')
1225
+ .click()
1226
+ cy.get('.q-menu')
1227
+ .get('[role="option"]')
1228
+ .first()
1229
+ .click()
1230
+ .then(() => {
1231
+ expect(fn).to.be.calledWith('1')
1232
+ })
1233
+ })
1234
+ })
1235
+
1236
+ describe('(event): input-value', () => {
1237
+ it('should emit event when text input changes', () => {
1238
+ const fn = cy.stub()
1239
+ mount(WrapperOne, {
1240
+ attrs: {
1241
+ modelValue: null,
1242
+ onInputValue: fn,
1243
+ useInput: true
1244
+ }
1245
+ })
1246
+
1247
+ expect(fn).not.to.be.called
1248
+ cy.get('.select-root')
1249
+ .get('input')
1250
+ .type('h')
1251
+ .then(() => {
1252
+ expect(fn).to.be.calledWith('h')
1253
+ })
1254
+ })
1255
+ })
1256
+
1257
+ describe('(event): remove', () => {
1258
+ it('should emit event when a selected item is removed from selection', () => {
1259
+ const fn = cy.stub()
1260
+ const model = ref([ '2', '3' ])
1261
+ mount(WrapperOne, {
1262
+ attrs: {
1263
+ onRemove: fn,
1264
+ multiple: true,
1265
+ modelValue: model,
1266
+ 'onUpdate:modelValue': (val) => {
1267
+ model.value = val
1268
+ },
1269
+ options: [ '1', '2', '3' ]
1270
+ }
1271
+ })
1272
+
1273
+ expect(fn).not.to.be.called
1274
+ cy.get('.select-root')
1275
+ .click()
1276
+ cy.get('.q-menu')
1277
+ .get('[role="option"]')
1278
+ .first()
1279
+ .click()
1280
+ .then(() => {
1281
+ expect(fn).not.to.be.called
1282
+ })
1283
+ cy.get('.q-menu')
1284
+ .get('[role="option"]')
1285
+ .first()
1286
+ .click()
1287
+ .then(() => {
1288
+ // Item is added in the previous step at the end of the array, so at index 2
1289
+ expect(fn).to.be.calledWith({ index: 2, value: '1' })
1290
+ })
1291
+ })
1292
+ })
1293
+
1294
+ describe('(event): add', () => {
1295
+ it('should emit event when an option is added to the selection', () => {
1296
+ const fn = cy.stub()
1297
+ const model = ref([ '2' ])
1298
+ mount(WrapperOne, {
1299
+ attrs: {
1300
+ onAdd: fn,
1301
+ multiple: true,
1302
+ modelValue: model,
1303
+ 'onUpdate:modelValue': (val) => {
1304
+ model.value = val
1305
+ },
1306
+ options: [ '1', '2', '3' ]
1307
+ }
1308
+ })
1309
+
1310
+ expect(fn).not.to.be.called
1311
+ cy.get('.select-root')
1312
+ .click()
1313
+ cy.get('.q-menu')
1314
+ .get('[role="option"]')
1315
+ .first()
1316
+ .click()
1317
+ .then(() => {
1318
+ // Item is added in the previous step at the end of the array, so at index 2
1319
+ expect(fn).to.be.calledWith({ index: 1, value: '1' })
1320
+ })
1321
+ })
1322
+ })
1323
+
1324
+ describe('(event): new-value', () => {
1325
+ it('should emit event when something is typed into the input field and enter is pressed', () => {
1326
+ const fn = cy.stub()
1327
+ const model = ref([ '2' ])
1328
+ mount(WrapperOne, {
1329
+ attrs: {
1330
+ onNewValue: fn,
1331
+ multiple: true,
1332
+ useInput: true,
1333
+ modelValue: model,
1334
+ 'onUpdate:modelValue': (val) => {
1335
+ model.value = val
1336
+ },
1337
+ hideDropdownIcon: true
1338
+ }
1339
+ })
1340
+
1341
+ expect(fn).not.to.be.called
1342
+ cy.get('.select-root')
1343
+ .get('input')
1344
+ .type('100')
1345
+ .then(() => {
1346
+ expect(fn).not.to.be.called
1347
+ })
1348
+ .type('{enter}')
1349
+ .then(() => {
1350
+ expect(fn).to.be.calledWith('100')
1351
+ })
1352
+ })
1353
+
1354
+ it('should add the value to the model when the doneFn is called', () => {
1355
+ const model = ref([ '2' ])
1356
+ mount(WrapperOne, {
1357
+ attrs: {
1358
+ onNewValue: (val, doneFn) => {
1359
+ doneFn(val)
1360
+ },
1361
+ multiple: true,
1362
+ useInput: true,
1363
+ modelValue: model,
1364
+ 'onUpdate:modelValue': (val) => {
1365
+ model.value = val
1366
+ },
1367
+ hideDropdownIcon: true
1368
+ }
1369
+ })
1370
+
1371
+ cy.get('.select-root')
1372
+ .get('input')
1373
+ .type('100')
1374
+ .type('{enter}')
1375
+ .then(() => {
1376
+ expect(model.value).includes('100')
1377
+ })
1378
+ })
1379
+ })
1380
+
1381
+ describe('(event): filter', () => {
1382
+ it('should emit event when something is typed into the input field', () => {
1383
+ const fn = cy.stub()
1384
+ mount(WrapperOne, {
1385
+ attrs: {
1386
+ onFilter: fn,
1387
+ useInput: true,
1388
+ inputDebounce: 0
1389
+ }
1390
+ })
1391
+
1392
+ expect(fn).not.to.be.called
1393
+ cy.get('.select-root')
1394
+ .get('input')
1395
+ .type('h')
1396
+ .then(() => {
1397
+ expect(fn).to.be.calledWith('h')
1398
+ })
1399
+ })
1400
+ })
1401
+
1402
+ describe('(event): filter-abort', () => {
1403
+ it('should emit event when the the filterFn has not called the doneFn yet and a new filter is requested', () => {
1404
+ const fn = cy.stub()
1405
+ const filterFn = cy.stub()
1406
+ mount(WrapperOne, {
1407
+ attrs: {
1408
+ onFilter: filterFn,
1409
+ onFilterAbort: fn,
1410
+ useInput: true,
1411
+ inputDebounce: 0
1412
+ }
1413
+ })
1414
+
1415
+ expect(fn).not.to.be.called
1416
+ cy.get('.select-root')
1417
+ .get('input')
1418
+ .click()
1419
+ .then(() => {
1420
+ expect(filterFn).to.be.calledOnce
1421
+ expect(fn).not.to.be.called
1422
+ })
1423
+ .type('h')
1424
+ .then(() => {
1425
+ expect(fn).to.be.calledOnce
1426
+ })
1427
+ })
1428
+
1429
+ it('should not emit event when the filter has called its doneFn', () => {
1430
+ const fn = cy.stub()
1431
+ mount(WrapperOne, {
1432
+ attrs: {
1433
+ onFilter: (val, doneFn) => {
1434
+ doneFn()
1435
+ },
1436
+ onFilterAbort: fn,
1437
+ useInput: true,
1438
+ inputDebounce: 0
1439
+ }
1440
+ })
1441
+
1442
+ expect(fn).not.to.be.called
1443
+ cy.get('.select-root')
1444
+ .get('input')
1445
+ .click()
1446
+ .then(() => {
1447
+ expect(fn).not.to.be.called
1448
+ })
1449
+ .type('h')
1450
+ .then(() => {
1451
+ expect(fn).not.to.be.called
1452
+ })
1453
+ })
1454
+ })
1455
+
1456
+ describe('(event): popup-show', () => {
1457
+ it('should emit event when the options are shown', () => {
1458
+ const fn = cy.stub()
1459
+ mount(WrapperOne, {
1460
+ attrs: {
1461
+ onPopupShow: fn,
1462
+ options: [ '1', '2', '3' ]
1463
+ }
1464
+ })
1465
+
1466
+ expect(fn).not.to.be.called
1467
+ cy.get('.select-root')
1468
+ .click()
1469
+ .then(() => {
1470
+ expect(fn).to.be.called
1471
+ })
1472
+ })
1473
+ })
1474
+
1475
+ describe('(event): popup-hide', () => {
1476
+ it('should emit event when the options are hidden', () => {
1477
+ const fn = cy.stub()
1478
+ mount(WrapperOne, {
1479
+ attrs: {
1480
+ onPopupHide: fn,
1481
+ options: [ '1', '2', '3' ]
1482
+ }
1483
+ })
1484
+
1485
+ expect(fn).not.to.be.called
1486
+ cy.get('.select-root')
1487
+ .click()
1488
+ .then(() => {
1489
+ expect(fn).not.to.be.called
1490
+ })
1491
+ .type('{esc}')
1492
+ .then(() => {
1493
+ expect(fn).to.be.called
1494
+ })
1495
+ })
1496
+ })
1497
+
1498
+ describe('(event): virtual-scroll', () => {
1499
+ it.skip('', () => {
1500
+ // The virtual scroll code is tested as a composable
1501
+ // The property is included in the QSelect.json to add typings
1502
+ // for the QSelect ref passed in this event.
1503
+ // I think testing the component ref that is passed is of type QSelect is out of scope for unit tests.
1504
+ })
1505
+ })
1506
+ })
1507
+
1508
+ describe('Methods', () => {
1509
+ describe('(method): focus', () => {
1510
+ it('should focus the component', () => {
1511
+ mount(WrapperOne)
1512
+
1513
+ cy.dataCy('select')
1514
+ .get('[tabindex="0"]')
1515
+ .should('not.have.focus')
1516
+ cy.dataCy('select')
1517
+ .then(() => {
1518
+ Cypress.vueWrapper.vm.compRef.focus()
1519
+ })
1520
+ cy.dataCy('select')
1521
+ .get('[tabindex="0"]')
1522
+ .should('have.focus')
1523
+ })
1524
+ })
1525
+
1526
+ describe('(method): showPopup', () => {
1527
+ it('should open the popup and focus the component', () => {
1528
+ mount(WrapperOne, {
1529
+ attrs: {
1530
+ options: [ '1', '2' ]
1531
+ }
1532
+ })
1533
+
1534
+ cy.get('.q-menu')
1535
+ .should('not.exist')
1536
+ .then(() => {
1537
+ Cypress.vueWrapper.vm.compRef.showPopup()
1538
+ })
1539
+ cy.get('.q-menu')
1540
+ .should('be.visible')
1541
+ cy.dataCy('select')
1542
+ .get('[tabindex="0"]')
1543
+ .should('have.focus')
1544
+ })
1545
+ })
1546
+
1547
+ describe('(method): hidePopup', () => {
1548
+ it('should hide the popup', () => {
1549
+ mount(WrapperOne, {
1550
+ attrs: {
1551
+ options: [ '1', '2' ]
1552
+ }
1553
+ })
1554
+
1555
+ cy.get('.select-root')
1556
+ .click()
1557
+ cy.get('.q-menu')
1558
+ .should('be.visible')
1559
+ .then(() => {
1560
+ Cypress.vueWrapper.vm.compRef.hidePopup()
1561
+ })
1562
+ cy.get('.q-menu')
1563
+ .should('not.exist')
1564
+ })
1565
+ })
1566
+
1567
+ describe('(method): removeAtIndex', () => {
1568
+ it('should remove a selected option at the correct index', () => {
1569
+ const options = [ '1', '2', '3', '4' ]
1570
+ const model = ref([ '1', '2', '4' ])
1571
+ mount(WrapperOne, {
1572
+ attrs: {
1573
+ modelValue: model,
1574
+ 'onUpdate:modelValue': (val) => {
1575
+ model.value = val
1576
+ },
1577
+ multiple: true,
1578
+ options
1579
+ }
1580
+ })
1581
+ .then(() => {
1582
+ expect(model.value.includes('4')).to.be.true
1583
+ Cypress.vueWrapper.vm.compRef.removeAtIndex(2)
1584
+ expect(model.value.includes('4')).to.be.false
1585
+ })
1586
+ })
1587
+ })
1588
+
1589
+ describe('(method): add', () => {
1590
+ it('should add a selected option', () => {
1591
+ const model = ref([ '1', '2' ])
1592
+ mount(WrapperOne, {
1593
+ attrs: {
1594
+ modelValue: model,
1595
+ 'onUpdate:modelValue': (val) => {
1596
+ model.value = val
1597
+ },
1598
+ multiple: true
1599
+ }
1600
+ })
1601
+ .then(() => {
1602
+ expect(model.value.includes('100')).to.be.false
1603
+ Cypress.vueWrapper.vm.compRef.add('100')
1604
+ expect(model.value.includes('100')).to.be.true
1605
+ })
1606
+ })
1607
+
1608
+ it('should not add a duplicate option when unique is true', () => {
1609
+ const model = ref([ '1', '2' ])
1610
+ mount(WrapperOne, {
1611
+ attrs: {
1612
+ modelValue: model,
1613
+ 'onUpdate:modelValue': (val) => {
1614
+ model.value = val
1615
+ },
1616
+ multiple: true
1617
+ }
1618
+ })
1619
+ .then(() => {
1620
+ expect(model.value.length).to.be.equal(2)
1621
+ Cypress.vueWrapper.vm.compRef.add('2', true)
1622
+ expect(model.value.length).to.be.equal(2)
1623
+ Cypress.vueWrapper.vm.compRef.add('2')
1624
+ expect(model.value.length).to.be.equal(3)
1625
+ })
1626
+ })
1627
+ })
1628
+
1629
+ describe('(method): toggleOption', () => {
1630
+ it('should toggle an option', () => {
1631
+ const model = ref([ '1', '2' ])
1632
+ mount(WrapperOne, {
1633
+ attrs: {
1634
+ modelValue: model,
1635
+ 'onUpdate:modelValue': (val) => {
1636
+ model.value = val
1637
+ },
1638
+ multiple: true
1639
+ }
1640
+ })
1641
+ .then(() => {
1642
+ expect(model.value.length).to.be.equal(2)
1643
+ Cypress.vueWrapper.vm.compRef.toggleOption('2')
1644
+ expect(model.value.length).to.be.equal(1)
1645
+ })
1646
+ // When not using this wait this test will succeed on `open-ct` but fail on `run-ct`
1647
+ .wait(50)
1648
+ .then(() => {
1649
+ Cypress.vueWrapper.vm.compRef.toggleOption('2')
1650
+ expect(model.value.length).to.be.equal(2)
1651
+ })
1652
+ })
1653
+
1654
+ // Todo: toggleOption argument keepOpen only does something when using single select. This is not clear from the docs.
1655
+ // should this be consistent? E.g. use `true` as argument when multiple is true by default but make sure it can be overridden.
1656
+ it('should close the menu and clear the filter', () => {
1657
+ const model = ref('1')
1658
+ mount(WrapperOne, {
1659
+ attrs: {
1660
+ modelValue: model,
1661
+ 'onUpdate:modelValue': (val) => {
1662
+ model.value = val
1663
+ },
1664
+ options: [ '1', '2' ],
1665
+ useInput: true
1666
+ }
1667
+ })
1668
+
1669
+ cy.get('.select-root')
1670
+ .click()
1671
+ .get('input')
1672
+ .type('h')
1673
+ cy.get('.q-menu')
1674
+ .should('be.visible')
1675
+ .then(() => {
1676
+ Cypress.vueWrapper.vm.compRef.toggleOption('2')
1677
+ })
1678
+ cy.get('.q-menu')
1679
+ .should('not.exist')
1680
+ cy.get('input')
1681
+ .should('have.value', '')
1682
+ })
1683
+
1684
+ it('should not close the menu and clear the filter when keepOpen is true', () => {
1685
+ const model = ref('1')
1686
+ mount(WrapperOne, {
1687
+ attrs: {
1688
+ modelValue: model,
1689
+ 'onUpdate:modelValue': (val) => {
1690
+ model.value = val
1691
+ },
1692
+ options: [ '1', '2' ],
1693
+ useInput: true
1694
+ }
1695
+ })
1696
+
1697
+ cy.get('.select-root')
1698
+ .click()
1699
+ .get('input')
1700
+ .type('h')
1701
+ cy.get('.q-menu')
1702
+ .should('be.visible')
1703
+ .then(() => {
1704
+ Cypress.vueWrapper.vm.compRef.toggleOption('2', true)
1705
+ })
1706
+ cy.get('.q-menu')
1707
+ .should('be.visible')
1708
+ cy.get('input')
1709
+ .should('have.value', 'h')
1710
+ })
1711
+ })
1712
+
1713
+ describe('(method): setOptionIndex', () => {
1714
+ it('should set an option from the menu dropdown as focused', () => {
1715
+ const options = [ '1', '2', '3', '4' ]
1716
+ mount(WrapperOne, {
1717
+ attrs: {
1718
+ options
1719
+ }
1720
+ })
1721
+ cy.get('.select-root')
1722
+ .click()
1723
+ .then(() => {
1724
+ Cypress.vueWrapper.vm.compRef.setOptionIndex(0)
1725
+ })
1726
+ .get('[role="option"]')
1727
+ .first()
1728
+ .should('have.class', 'q-manual-focusable--focused')
1729
+ })
1730
+ })
1731
+
1732
+ describe('(method): moveOptionSelection', () => {
1733
+ it('should move the optionSelection by some index offset', () => {
1734
+ const options = [ '1', '2', '3', '4' ]
1735
+ mount(WrapperOne, {
1736
+ attrs: {
1737
+ options
1738
+ }
1739
+ })
1740
+ cy.get('.select-root')
1741
+ .click()
1742
+ .then(() => {
1743
+ Cypress.vueWrapper.vm.compRef.setOptionIndex(0)
1744
+ })
1745
+ .get('[role="option"]')
1746
+ .first()
1747
+ .should('have.class', 'q-manual-focusable--focused')
1748
+ .then(() => {
1749
+ Cypress.vueWrapper.vm.compRef.moveOptionSelection(3)
1750
+ })
1751
+ .get('[role="option"]')
1752
+ .last()
1753
+ .should('have.class', 'q-manual-focusable--focused')
1754
+ })
1755
+ })
1756
+
1757
+ describe('(method): filter', () => {
1758
+ it('should filter the options list', () => {
1759
+ const options = [ '1', '2', '3', '4' ]
1760
+ const fn = cy.stub()
1761
+ const text = 'test'
1762
+ mount(WrapperOne, {
1763
+ attrs: {
1764
+ options,
1765
+ useInput: true,
1766
+ onFilter: fn
1767
+ }
1768
+ })
1769
+ cy.get('.select-root')
1770
+ .click()
1771
+ .then(() => {
1772
+ expect(fn).not.to.be.calledWith(text)
1773
+ Cypress.vueWrapper.vm.compRef.filter(text)
1774
+ expect(fn).to.be.calledWith(text)
1775
+ })
1776
+ })
1777
+ })
1778
+
1779
+ describe('(method): updateMenuPosition', () => {
1780
+ it.skip(' ', () => {
1781
+ // Not sure in what scenario this is needed, there is also some auto repositioning going on
1782
+ })
1783
+ })
1784
+
1785
+ describe('(method): updateInputValue', () => {
1786
+ it('should update the input value', () => {
1787
+ const options = [ '1', '2', '3', '4' ]
1788
+ const fn = cy.stub()
1789
+ const text = 'test'
1790
+ mount(WrapperOne, {
1791
+ attrs: {
1792
+ options,
1793
+ useInput: true,
1794
+ onFilter: fn
1795
+ }
1796
+ })
1797
+ cy.get('.select-root')
1798
+ .click()
1799
+ .then(() => {
1800
+ expect(fn).not.to.be.calledWith(text)
1801
+ Cypress.vueWrapper.vm.compRef.updateInputValue(text)
1802
+ expect(fn).to.be.calledWith(text)
1803
+ })
1804
+ .get('input')
1805
+ .should('have.value', text)
1806
+ })
1807
+
1808
+ it('should not trigger the filter when specified', () => {
1809
+ const options = [ '1', '2', '3', '4' ]
1810
+ const fn = cy.stub()
1811
+ const text = 'test'
1812
+ mount(WrapperOne, {
1813
+ attrs: {
1814
+ options,
1815
+ useInput: true,
1816
+ onFilter: fn
1817
+ }
1818
+ })
1819
+ cy.get('.select-root')
1820
+ .click()
1821
+ .then(() => {
1822
+ expect(fn).not.to.be.calledWith(text)
1823
+ Cypress.vueWrapper.vm.compRef.updateInputValue(text, true)
1824
+ expect(fn).not.to.be.calledWith(text)
1825
+ })
1826
+ .get('input')
1827
+ .should('have.value', text)
1828
+ })
1829
+ })
1830
+
1831
+ describe('(method): isOptionSelected', () => {
1832
+ it('should tell when an option is selected', () => {
1833
+ const options = [ '1', '2', '3', '4' ]
1834
+ const model = ref([ '1', '2', '4' ])
1835
+ mount(WrapperOne, {
1836
+ attrs: {
1837
+ modelValue: model,
1838
+ multiple: true,
1839
+ options
1840
+ }
1841
+ })
1842
+ .then(() => {
1843
+ expect(Cypress.vueWrapper.vm.compRef.isOptionSelected(options[ 0 ])).to.be.true
1844
+ expect(Cypress.vueWrapper.vm.compRef.isOptionSelected(options[ 2 ])).to.be.false
1845
+ })
1846
+ })
1847
+ })
1848
+
1849
+ describe('(method): getEmittingOptionValue', () => {
1850
+ it('should return the emit value with plain options', () => {
1851
+ const options = [ '1', '2', '3', '4' ]
1852
+ const model = ref('1')
1853
+ mount(WrapperOne, {
1854
+ attrs: {
1855
+ modelValue: model,
1856
+ options
1857
+ }
1858
+ })
1859
+ .then(() => {
1860
+ expect(Cypress.vueWrapper.vm.compRef.getEmittingOptionValue(options[ 2 ])).to.equal(options[ 2 ])
1861
+ })
1862
+ })
1863
+
1864
+ it('should return the emit value with object options', () => {
1865
+ const options = [ { label: '1', value: 1 }, { label: '2', value: 2 }, { label: '3', value: 3 } ]
1866
+ const model = ref(options[ 0 ])
1867
+ mount(WrapperOne, {
1868
+ attrs: {
1869
+ modelValue: model,
1870
+ options
1871
+ }
1872
+ })
1873
+ .then(() => {
1874
+ expect(Cypress.vueWrapper.vm.compRef.getEmittingOptionValue(options[ 2 ])).to.equal(options[ 2 ])
1875
+ })
1876
+ })
1877
+
1878
+ it('should respect emit-value when using options', () => {
1879
+ const options = [ { label: '1', value: 1 }, { label: '2', value: 2 }, { label: '3', value: 3 } ]
1880
+ const model = ref(options[ 0 ])
1881
+ mount(WrapperOne, {
1882
+ attrs: {
1883
+ modelValue: model,
1884
+ options,
1885
+ emitValue: true
1886
+ }
1887
+ })
1888
+ .then(() => {
1889
+ expect(Cypress.vueWrapper.vm.compRef.getEmittingOptionValue(options[ 2 ])).to.equal(options[ 2 ].value)
1890
+ })
1891
+ })
1892
+ })
1893
+
1894
+ describe('(method): getOptionValue', () => {
1895
+ it('should return the option value with plain options', () => {
1896
+ const options = [ '1', '2', '3', '4' ]
1897
+ const model = ref('1')
1898
+ mount(WrapperOne, {
1899
+ attrs: {
1900
+ modelValue: model,
1901
+ options
1902
+ }
1903
+ })
1904
+ .then(() => {
1905
+ expect(Cypress.vueWrapper.vm.compRef.getOptionValue(options[ 2 ])).to.equal(options[ 2 ])
1906
+ })
1907
+ })
1908
+
1909
+ it('should return the option value with object options (value by default)', () => {
1910
+ const options = [ { label: '1', value: 1 }, { label: '2', value: 2 }, { label: '3', value: 3 } ]
1911
+ const model = ref(options[ 0 ])
1912
+ mount(WrapperOne, {
1913
+ attrs: {
1914
+ modelValue: model,
1915
+ options
1916
+ }
1917
+ })
1918
+ .then(() => {
1919
+ expect(Cypress.vueWrapper.vm.compRef.getOptionValue(options[ 2 ])).to.equal(options[ 2 ].value)
1920
+ })
1921
+ })
1922
+
1923
+ it('should respect the option-value option', () => {
1924
+ const options = [ { label: '1', test: 1 }, { label: '2', test: 2 }, { label: '3', test: 3 } ]
1925
+ const model = ref(options[ 0 ])
1926
+ mount(WrapperOne, {
1927
+ attrs: {
1928
+ modelValue: model,
1929
+ options,
1930
+ optionValue: 'test'
1931
+ }
1932
+ })
1933
+ .then(() => {
1934
+ expect(Cypress.vueWrapper.vm.compRef.getOptionValue(options[ 2 ])).to.equal(options[ 2 ].test)
1935
+ })
1936
+ })
1937
+ })
1938
+
1939
+ describe('(method): getOptionLabel', () => {
1940
+ it('should return the option label with plain options', () => {
1941
+ const options = [ '1', '2', '3', '4' ]
1942
+ const model = ref('1')
1943
+ mount(WrapperOne, {
1944
+ attrs: {
1945
+ modelValue: model,
1946
+ options
1947
+ }
1948
+ })
1949
+ .then(() => {
1950
+ expect(Cypress.vueWrapper.vm.compRef.getOptionLabel(options[ 2 ])).to.equal(options[ 2 ])
1951
+ })
1952
+ })
1953
+
1954
+ it('should return the option label with object options (label by default)', () => {
1955
+ const options = [ { label: '1', value: 1 }, { label: '2', value: 2 }, { label: '3', value: 3 } ]
1956
+ const model = ref(options[ 0 ])
1957
+ mount(WrapperOne, {
1958
+ attrs: {
1959
+ modelValue: model,
1960
+ options
1961
+ }
1962
+ })
1963
+ .then(() => {
1964
+ expect(Cypress.vueWrapper.vm.compRef.getOptionLabel(options[ 2 ])).to.equal(options[ 2 ].label)
1965
+ })
1966
+ })
1967
+
1968
+ it('should respect the option-value option', () => {
1969
+ const options = [ { test: '1', value: 1 }, { test: '2', value: 2 }, { test: '3', value: 3 } ]
1970
+ const model = ref(options[ 0 ])
1971
+ mount(WrapperOne, {
1972
+ attrs: {
1973
+ modelValue: model,
1974
+ options,
1975
+ optionLabel: 'test'
1976
+ }
1977
+ })
1978
+ .then(() => {
1979
+ expect(Cypress.vueWrapper.vm.compRef.getOptionLabel(options[ 2 ])).to.equal(options[ 2 ].test)
1980
+ })
1981
+ })
1982
+ })
1983
+
1984
+ describe('(method): isOptionDisabled', () => {
1985
+ it('should return if an option is disabled correctly', () => {
1986
+ const options = [ { label: '1', value: 1, disable: true }, { label: '2', value: 2 }, { label: '3', value: 3 } ]
1987
+ const model = ref(options[ 0 ])
1988
+ mount(WrapperOne, {
1989
+ attrs: {
1990
+ modelValue: model,
1991
+ options
1992
+ }
1993
+ })
1994
+ .then(() => {
1995
+ expect(Cypress.vueWrapper.vm.compRef.isOptionDisabled(options[ 0 ])).to.be.true
1996
+ // This currently fails: https://github.com/quasarframework/quasar/issues/12046
1997
+ // expect(Cypress.vueWrapper.vm.compRef.isOptionDisabled(options[ 1 ])).to.be.false
1998
+ // expect(Cypress.vueWrapper.vm.compRef.isOptionDisabled(options[ 2 ])).to.be.false
1999
+ })
2000
+ })
2001
+ })
2002
+ })
2003
+ })