@fiscozen/checkbox 1.0.0-next.0 → 1.0.0
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.
- package/dist/checkbox.js +2376 -0
- package/dist/checkbox.umd.cjs +13 -0
- package/dist/index.d.ts +1 -0
- package/dist/src/FzCheckbox.vue.d.ts +102 -0
- package/dist/src/FzCheckboxGroup.vue.d.ts +72 -0
- package/dist/src/__tests__/FzCheckbox.spec.d.ts +1 -0
- package/dist/src/__tests__/FzCheckboxGroup.spec.d.ts +1 -0
- package/dist/src/common.d.ts +23 -0
- package/dist/src/components/ErrorAlert.vue.d.ts +34 -0
- package/dist/src/components/FzCheckboxGroupOption.vue.d.ts +86 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/types.d.ts +192 -0
- package/dist/src/utils.d.ts +24 -0
- package/dist/style.css +1 -0
- package/package.json +9 -13
- package/src/__tests__/FzCheckbox.spec.ts +748 -0
- package/src/__tests__/FzCheckboxGroup.spec.ts +764 -0
- package/src/__tests__/__snapshots__/FzCheckbox.spec.ts.snap +427 -0
- package/src/__tests__/__snapshots__/FzCheckboxGroup.spec.ts.snap +805 -0
- package/src/components/ErrorAlert.vue +1 -1
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +9 -1
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -1031
- package/coverage/coverage-final.json +0 -7
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -131
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -196
- package/coverage/src/FzCheckbox.vue.html +0 -1489
- package/coverage/src/FzCheckboxGroup.vue.html +0 -682
- package/coverage/src/common.ts.html +0 -157
- package/coverage/src/components/ErrorAlert.vue.html +0 -268
- package/coverage/src/components/FzCheckboxGroupOption.vue.html +0 -652
- package/coverage/src/components/index.html +0 -131
- package/coverage/src/index.html +0 -161
- package/coverage/src/utils.ts.html +0 -265
- package/src/__test__/FzCheckbox.test.ts +0 -206
- package/src/__test__/FzCheckboxGroup.test.ts +0 -263
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { mount } from "@vue/test-utils";
|
|
3
|
+
import FzCheckbox from "../FzCheckbox.vue";
|
|
4
|
+
|
|
5
|
+
const MAX_CHECKBOX = 200;
|
|
6
|
+
|
|
7
|
+
describe("FzCheckbox", () => {
|
|
8
|
+
// ============================================
|
|
9
|
+
// RENDERING TESTS
|
|
10
|
+
// ============================================
|
|
11
|
+
describe("Rendering", () => {
|
|
12
|
+
it("should render with default props", async () => {
|
|
13
|
+
const wrapper = mount(FzCheckbox, {
|
|
14
|
+
props: {
|
|
15
|
+
label: "Test Checkbox",
|
|
16
|
+
value: "test",
|
|
17
|
+
modelValue: false,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await wrapper.vm.$nextTick();
|
|
22
|
+
expect(wrapper.exists()).toBe(true);
|
|
23
|
+
expect(wrapper.html()).toContain("Test Checkbox");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should render label when provided", async () => {
|
|
27
|
+
const wrapper = mount(FzCheckbox, {
|
|
28
|
+
props: {
|
|
29
|
+
label: "Custom Label",
|
|
30
|
+
value: "test",
|
|
31
|
+
modelValue: false,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await wrapper.vm.$nextTick();
|
|
36
|
+
expect(wrapper.text()).toContain("Custom Label");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should render checkbox icon", async () => {
|
|
40
|
+
const wrapper = mount(FzCheckbox, {
|
|
41
|
+
props: {
|
|
42
|
+
label: "Test Checkbox",
|
|
43
|
+
value: "test",
|
|
44
|
+
modelValue: false,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await wrapper.vm.$nextTick();
|
|
49
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
50
|
+
expect(input.exists()).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should not render label text when standalone", async () => {
|
|
54
|
+
const wrapper = mount(FzCheckbox, {
|
|
55
|
+
props: {
|
|
56
|
+
label: "Test Checkbox",
|
|
57
|
+
value: "test",
|
|
58
|
+
modelValue: false,
|
|
59
|
+
standalone: true,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await wrapper.vm.$nextTick();
|
|
64
|
+
const label = wrapper.find("label");
|
|
65
|
+
expect(label.text()).not.toContain("Test Checkbox");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// ============================================
|
|
70
|
+
// PROPS TESTS
|
|
71
|
+
// ============================================
|
|
72
|
+
describe("Props", () => {
|
|
73
|
+
describe("modelValue prop", () => {
|
|
74
|
+
it("should be checked when modelValue is true", async () => {
|
|
75
|
+
const wrapper = mount(FzCheckbox, {
|
|
76
|
+
props: {
|
|
77
|
+
label: "Test Checkbox",
|
|
78
|
+
value: "test",
|
|
79
|
+
modelValue: true,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
await wrapper.vm.$nextTick();
|
|
83
|
+
expect(wrapper.find("input").element.checked).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should be unchecked when modelValue is false", async () => {
|
|
87
|
+
const wrapper = mount(FzCheckbox, {
|
|
88
|
+
props: {
|
|
89
|
+
label: "Test Checkbox",
|
|
90
|
+
value: "test",
|
|
91
|
+
modelValue: false,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await wrapper.vm.$nextTick();
|
|
96
|
+
expect(wrapper.find("input").element.checked).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should handle array modelValue", async () => {
|
|
100
|
+
const wrapper = mount(FzCheckbox, {
|
|
101
|
+
props: {
|
|
102
|
+
label: "Test Checkbox",
|
|
103
|
+
value: "test",
|
|
104
|
+
modelValue: ["test", "other"],
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await wrapper.vm.$nextTick();
|
|
109
|
+
expect(wrapper.find("input").element.checked).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should be unchecked when value not in array", async () => {
|
|
113
|
+
const wrapper = mount(FzCheckbox, {
|
|
114
|
+
props: {
|
|
115
|
+
label: "Test Checkbox",
|
|
116
|
+
value: "test",
|
|
117
|
+
modelValue: ["other"],
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await wrapper.vm.$nextTick();
|
|
122
|
+
expect(wrapper.find("input").element.checked).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe("disabled prop", () => {
|
|
127
|
+
it("should apply disabled attribute when true", async () => {
|
|
128
|
+
const wrapper = mount(FzCheckbox, {
|
|
129
|
+
props: {
|
|
130
|
+
label: "Test Checkbox",
|
|
131
|
+
value: "test",
|
|
132
|
+
modelValue: false,
|
|
133
|
+
disabled: true,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
await wrapper.vm.$nextTick();
|
|
137
|
+
expect(wrapper.find("input").element.disabled).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should not be disabled by default", async () => {
|
|
141
|
+
const wrapper = mount(FzCheckbox, {
|
|
142
|
+
props: {
|
|
143
|
+
label: "Test Checkbox",
|
|
144
|
+
value: "test",
|
|
145
|
+
modelValue: false,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
await wrapper.vm.$nextTick();
|
|
149
|
+
expect(wrapper.find("input").element.disabled).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe("emphasis prop", () => {
|
|
154
|
+
it("should apply emphasis classes when true", async () => {
|
|
155
|
+
const wrapper = mount(FzCheckbox, {
|
|
156
|
+
props: {
|
|
157
|
+
label: "Test Checkbox",
|
|
158
|
+
value: "test",
|
|
159
|
+
modelValue: false,
|
|
160
|
+
emphasis: true,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
await wrapper.vm.$nextTick();
|
|
164
|
+
expect(wrapper.find("label").classes()).toContain(
|
|
165
|
+
"peer-checked:[&_div]:text-blue-500",
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe("indeterminate prop", () => {
|
|
171
|
+
it("should show indeterminate state when true", async () => {
|
|
172
|
+
const wrapper = mount(FzCheckbox, {
|
|
173
|
+
props: {
|
|
174
|
+
label: "Test Checkbox",
|
|
175
|
+
value: "test",
|
|
176
|
+
modelValue: false,
|
|
177
|
+
indeterminate: true,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
await wrapper.vm.$nextTick();
|
|
181
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
182
|
+
expect(input.attributes("aria-checked")).toBe("mixed");
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe("required prop", () => {
|
|
187
|
+
it("should apply required attribute when true", async () => {
|
|
188
|
+
const wrapper = mount(FzCheckbox, {
|
|
189
|
+
props: {
|
|
190
|
+
label: "Test Checkbox",
|
|
191
|
+
value: "test",
|
|
192
|
+
modelValue: false,
|
|
193
|
+
required: true,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
await wrapper.vm.$nextTick();
|
|
197
|
+
expect(wrapper.find("input").attributes("required")).toBeDefined();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe("standalone prop", () => {
|
|
202
|
+
it("should use aria-label when standalone", async () => {
|
|
203
|
+
const wrapper = mount(FzCheckbox, {
|
|
204
|
+
props: {
|
|
205
|
+
label: "Test Checkbox",
|
|
206
|
+
value: "test",
|
|
207
|
+
modelValue: false,
|
|
208
|
+
standalone: true,
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
await wrapper.vm.$nextTick();
|
|
212
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
213
|
+
expect(input.attributes("aria-label")).toBe("Test Checkbox");
|
|
214
|
+
expect(input.attributes("aria-labelledby")).toBeUndefined();
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe("error prop", () => {
|
|
219
|
+
it("should apply error styling when true", async () => {
|
|
220
|
+
const wrapper = mount(FzCheckbox, {
|
|
221
|
+
props: {
|
|
222
|
+
label: "Test Checkbox",
|
|
223
|
+
value: "test",
|
|
224
|
+
modelValue: false,
|
|
225
|
+
error: true,
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
await wrapper.vm.$nextTick();
|
|
229
|
+
const label = wrapper.find("label");
|
|
230
|
+
expect(label.classes()).toContain("text-semantic-error-200");
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// ============================================
|
|
236
|
+
// EVENTS TESTS
|
|
237
|
+
// ============================================
|
|
238
|
+
describe("Events", () => {
|
|
239
|
+
it("should emit change event when clicked", async () => {
|
|
240
|
+
const wrapper = mount(FzCheckbox, {
|
|
241
|
+
props: {
|
|
242
|
+
label: "Test Checkbox",
|
|
243
|
+
value: "test",
|
|
244
|
+
modelValue: false,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
await wrapper.vm.$nextTick();
|
|
249
|
+
await wrapper.find("input").trigger("change");
|
|
250
|
+
|
|
251
|
+
expect(wrapper.emitted("change")).toBeDefined();
|
|
252
|
+
expect(wrapper.emitted("change")).toHaveLength(1);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should update modelValue when clicked", async () => {
|
|
256
|
+
const wrapper = mount(FzCheckbox, {
|
|
257
|
+
props: {
|
|
258
|
+
label: "Test Checkbox",
|
|
259
|
+
value: "test",
|
|
260
|
+
modelValue: false,
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
await wrapper.vm.$nextTick();
|
|
265
|
+
const input = wrapper.find("input");
|
|
266
|
+
await input.setValue(true);
|
|
267
|
+
|
|
268
|
+
expect(wrapper.emitted("update:modelValue")).toBeDefined();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("should not emit change event when disabled", async () => {
|
|
272
|
+
const wrapper = mount(FzCheckbox, {
|
|
273
|
+
props: {
|
|
274
|
+
label: "Test Checkbox",
|
|
275
|
+
value: "test",
|
|
276
|
+
modelValue: false,
|
|
277
|
+
disabled: true,
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
await wrapper.vm.$nextTick();
|
|
282
|
+
const input = wrapper.find("input");
|
|
283
|
+
await input.trigger("change");
|
|
284
|
+
|
|
285
|
+
// Change event may still fire from native input, but modelValue should not update
|
|
286
|
+
// The disabled state prevents actual interaction
|
|
287
|
+
expect(input.element.disabled).toBe(true);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// ============================================
|
|
292
|
+
// ACCESSIBILITY TESTS
|
|
293
|
+
// ============================================
|
|
294
|
+
describe("Accessibility", () => {
|
|
295
|
+
describe("ARIA attributes", () => {
|
|
296
|
+
it("should have aria-checked matching checked state", async () => {
|
|
297
|
+
const wrapper = mount(FzCheckbox, {
|
|
298
|
+
props: {
|
|
299
|
+
label: "Test Checkbox",
|
|
300
|
+
value: "test",
|
|
301
|
+
modelValue: true,
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
await wrapper.vm.$nextTick();
|
|
305
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
306
|
+
expect(input.attributes("aria-checked")).toBe("true");
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("should have aria-checked='false' when unchecked", async () => {
|
|
310
|
+
const wrapper = mount(FzCheckbox, {
|
|
311
|
+
props: {
|
|
312
|
+
label: "Test Checkbox",
|
|
313
|
+
value: "test",
|
|
314
|
+
modelValue: false,
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
await wrapper.vm.$nextTick();
|
|
318
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
319
|
+
expect(input.attributes("aria-checked")).toBe("false");
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("should have aria-checked='mixed' when indeterminate", async () => {
|
|
323
|
+
const wrapper = mount(FzCheckbox, {
|
|
324
|
+
props: {
|
|
325
|
+
label: "Test Checkbox",
|
|
326
|
+
value: "test",
|
|
327
|
+
modelValue: false,
|
|
328
|
+
indeterminate: true,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
await wrapper.vm.$nextTick();
|
|
332
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
333
|
+
expect(input.attributes("aria-checked")).toBe("mixed");
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("should have aria-labelledby linking to label (not standalone)", async () => {
|
|
337
|
+
const wrapper = mount(FzCheckbox, {
|
|
338
|
+
props: {
|
|
339
|
+
label: "Test Checkbox",
|
|
340
|
+
value: "test",
|
|
341
|
+
modelValue: false,
|
|
342
|
+
required: true,
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
await wrapper.vm.$nextTick();
|
|
346
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
347
|
+
const id = input.attributes("id");
|
|
348
|
+
expect(input.attributes("aria-labelledby")).toBe(`${id}-label`);
|
|
349
|
+
expect(wrapper.find(`#${id}-label`).exists()).toBe(true);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("should have aria-label when standalone", async () => {
|
|
353
|
+
const wrapper = mount(FzCheckbox, {
|
|
354
|
+
props: {
|
|
355
|
+
label: "Test Checkbox",
|
|
356
|
+
value: "test",
|
|
357
|
+
modelValue: false,
|
|
358
|
+
standalone: true,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
await wrapper.vm.$nextTick();
|
|
362
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
363
|
+
expect(input.attributes("aria-label")).toBe("Test Checkbox");
|
|
364
|
+
expect(input.attributes("aria-labelledby")).toBeUndefined();
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("should have aria-required when required", async () => {
|
|
368
|
+
const wrapper = mount(FzCheckbox, {
|
|
369
|
+
props: {
|
|
370
|
+
label: "Test Checkbox",
|
|
371
|
+
value: "test",
|
|
372
|
+
modelValue: false,
|
|
373
|
+
required: true,
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
await wrapper.vm.$nextTick();
|
|
377
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
378
|
+
expect(input.attributes("aria-required")).toBe("true");
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it("should have aria-invalid when error is present", async () => {
|
|
382
|
+
const wrapper = mount(FzCheckbox, {
|
|
383
|
+
props: {
|
|
384
|
+
label: "Test Checkbox",
|
|
385
|
+
value: "test",
|
|
386
|
+
modelValue: false,
|
|
387
|
+
error: true,
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
await wrapper.vm.$nextTick();
|
|
391
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
392
|
+
expect(input.attributes("aria-invalid")).toBe("true");
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("should have aria-describedby linking to error message", async () => {
|
|
396
|
+
const wrapper = mount(FzCheckbox, {
|
|
397
|
+
props: {
|
|
398
|
+
label: "Test Checkbox",
|
|
399
|
+
value: "test",
|
|
400
|
+
modelValue: false,
|
|
401
|
+
error: true,
|
|
402
|
+
},
|
|
403
|
+
slots: {
|
|
404
|
+
error: "Test error message",
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
await wrapper.vm.$nextTick();
|
|
408
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
409
|
+
const id = input.attributes("id");
|
|
410
|
+
expect(input.attributes("aria-describedby")).toBe(`${id}-error`);
|
|
411
|
+
expect(wrapper.find(`#${id}-error`).exists()).toBe(true);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("should not have aria-describedby when error prop is true but no error slot", async () => {
|
|
415
|
+
const wrapper = mount(FzCheckbox, {
|
|
416
|
+
props: {
|
|
417
|
+
label: "Test Checkbox",
|
|
418
|
+
value: "test",
|
|
419
|
+
modelValue: false,
|
|
420
|
+
error: true,
|
|
421
|
+
},
|
|
422
|
+
});
|
|
423
|
+
await wrapper.vm.$nextTick();
|
|
424
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
425
|
+
expect(input.attributes("aria-describedby")).toBeUndefined();
|
|
426
|
+
expect(wrapper.find("[role='alert']").exists()).toBe(false);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it("should have aria-owns when provided", async () => {
|
|
430
|
+
const wrapper = mount(FzCheckbox, {
|
|
431
|
+
props: {
|
|
432
|
+
label: "Test Checkbox",
|
|
433
|
+
value: "test",
|
|
434
|
+
modelValue: false,
|
|
435
|
+
ariaOwns: "child-1 child-2",
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
await wrapper.vm.$nextTick();
|
|
439
|
+
const input = wrapper.find("input[type='checkbox']");
|
|
440
|
+
expect(input.attributes("aria-owns")).toBe("child-1 child-2");
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
describe("Keyboard navigation", () => {
|
|
445
|
+
it("should be focusable when not disabled", async () => {
|
|
446
|
+
const wrapper = mount(FzCheckbox, {
|
|
447
|
+
props: {
|
|
448
|
+
label: "Test Checkbox",
|
|
449
|
+
value: "test",
|
|
450
|
+
modelValue: false,
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
await wrapper.vm.$nextTick();
|
|
454
|
+
const input = wrapper.find("input");
|
|
455
|
+
expect(input.attributes("disabled")).toBeUndefined();
|
|
456
|
+
// Native checkbox is always focusable unless disabled
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it("should not be focusable when disabled", async () => {
|
|
460
|
+
const wrapper = mount(FzCheckbox, {
|
|
461
|
+
props: {
|
|
462
|
+
label: "Test Checkbox",
|
|
463
|
+
value: "test",
|
|
464
|
+
modelValue: false,
|
|
465
|
+
disabled: true,
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
await wrapper.vm.$nextTick();
|
|
469
|
+
const input = wrapper.find("input");
|
|
470
|
+
expect(input.element.disabled).toBe(true);
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
describe("Decorative elements", () => {
|
|
475
|
+
it("should hide decorative checkbox icon from screen readers", async () => {
|
|
476
|
+
const wrapper = mount(FzCheckbox, {
|
|
477
|
+
props: {
|
|
478
|
+
label: "Test Checkbox",
|
|
479
|
+
value: "test",
|
|
480
|
+
modelValue: false,
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
await wrapper.vm.$nextTick();
|
|
484
|
+
const icon = wrapper.findComponent({ name: "FzIcon" });
|
|
485
|
+
expect(icon.attributes("aria-hidden")).toBe("true");
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
// ============================================
|
|
491
|
+
// CSS CLASSES TESTS
|
|
492
|
+
// ============================================
|
|
493
|
+
describe("CSS Classes", () => {
|
|
494
|
+
it("should apply static base classes to input", async () => {
|
|
495
|
+
const wrapper = mount(FzCheckbox, {
|
|
496
|
+
props: {
|
|
497
|
+
label: "Test Checkbox",
|
|
498
|
+
value: "test",
|
|
499
|
+
modelValue: false,
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
await wrapper.vm.$nextTick();
|
|
503
|
+
const input = wrapper.find("input");
|
|
504
|
+
expect(input.classes()).toContain("peer");
|
|
505
|
+
expect(input.classes()).toContain("fz-hidden-input");
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it("should apply static base classes to label", async () => {
|
|
509
|
+
const wrapper = mount(FzCheckbox, {
|
|
510
|
+
props: {
|
|
511
|
+
label: "Test Checkbox",
|
|
512
|
+
value: "test",
|
|
513
|
+
modelValue: false,
|
|
514
|
+
},
|
|
515
|
+
});
|
|
516
|
+
await wrapper.vm.$nextTick();
|
|
517
|
+
const label = wrapper.find("label");
|
|
518
|
+
expect(label.classes()).toContain("flex");
|
|
519
|
+
expect(label.classes()).toContain("gap-6");
|
|
520
|
+
expect(label.classes()).toContain("items-start");
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it("should apply emphasis classes when emphasis prop is true", async () => {
|
|
524
|
+
const wrapper = mount(FzCheckbox, {
|
|
525
|
+
props: {
|
|
526
|
+
label: "Test Checkbox",
|
|
527
|
+
value: "test",
|
|
528
|
+
modelValue: false,
|
|
529
|
+
emphasis: true,
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
await wrapper.vm.$nextTick();
|
|
533
|
+
const label = wrapper.find("label");
|
|
534
|
+
expect(label.classes()).toContain(
|
|
535
|
+
"peer-checked:[&_div]:text-blue-500",
|
|
536
|
+
);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it("should apply error classes when error prop is true", async () => {
|
|
540
|
+
const wrapper = mount(FzCheckbox, {
|
|
541
|
+
props: {
|
|
542
|
+
label: "Test Checkbox",
|
|
543
|
+
value: "test",
|
|
544
|
+
modelValue: false,
|
|
545
|
+
error: true,
|
|
546
|
+
},
|
|
547
|
+
});
|
|
548
|
+
await wrapper.vm.$nextTick();
|
|
549
|
+
const label = wrapper.find("label");
|
|
550
|
+
expect(label.classes()).toContain("text-semantic-error-200");
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it("should apply disabled classes when disabled prop is true", async () => {
|
|
554
|
+
const wrapper = mount(FzCheckbox, {
|
|
555
|
+
props: {
|
|
556
|
+
label: "Test Checkbox",
|
|
557
|
+
value: "test",
|
|
558
|
+
modelValue: false,
|
|
559
|
+
disabled: true,
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
await wrapper.vm.$nextTick();
|
|
563
|
+
const label = wrapper.find("label");
|
|
564
|
+
expect(label.classes()).toContain("text-grey-300");
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// ============================================
|
|
569
|
+
// EDGE CASES
|
|
570
|
+
// ============================================
|
|
571
|
+
describe("Edge Cases", () => {
|
|
572
|
+
it("should generate unique IDs for multiple instances", async () => {
|
|
573
|
+
const checkboxes = Array.from({ length: MAX_CHECKBOX }).map((_) => {
|
|
574
|
+
return mount(FzCheckbox, {
|
|
575
|
+
props: {
|
|
576
|
+
label: "Test Checkbox",
|
|
577
|
+
value: "test",
|
|
578
|
+
modelValue: false,
|
|
579
|
+
disabled: true,
|
|
580
|
+
},
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
await Promise.all(checkboxes.map((c) => c.vm.$nextTick()));
|
|
584
|
+
const ids = checkboxes.map((c) => c.find("input").attributes("id"));
|
|
585
|
+
const labelFor = checkboxes.map((c) => c.find("label").attributes("for"));
|
|
586
|
+
expect(new Set(ids).size).toBe(MAX_CHECKBOX);
|
|
587
|
+
expect(new Set(labelFor).size).toBe(MAX_CHECKBOX);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it("should handle undefined modelValue gracefully", async () => {
|
|
591
|
+
const wrapper = mount(FzCheckbox, {
|
|
592
|
+
props: {
|
|
593
|
+
label: "Test Checkbox",
|
|
594
|
+
value: "test",
|
|
595
|
+
modelValue: undefined,
|
|
596
|
+
},
|
|
597
|
+
});
|
|
598
|
+
await wrapper.vm.$nextTick();
|
|
599
|
+
expect(wrapper.find("input").element.checked).toBe(false);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it("should handle null modelValue gracefully", async () => {
|
|
603
|
+
const wrapper = mount(FzCheckbox, {
|
|
604
|
+
props: {
|
|
605
|
+
label: "Test Checkbox",
|
|
606
|
+
value: "test",
|
|
607
|
+
modelValue: null,
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
await wrapper.vm.$nextTick();
|
|
611
|
+
expect(wrapper.find("input").element.checked).toBe(false);
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it("should handle empty array modelValue", async () => {
|
|
615
|
+
const wrapper = mount(FzCheckbox, {
|
|
616
|
+
props: {
|
|
617
|
+
label: "Test Checkbox",
|
|
618
|
+
value: "test",
|
|
619
|
+
modelValue: [],
|
|
620
|
+
},
|
|
621
|
+
});
|
|
622
|
+
await wrapper.vm.$nextTick();
|
|
623
|
+
expect(wrapper.find("input").element.checked).toBe(false);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it("should use label as value when value prop is not provided", async () => {
|
|
627
|
+
// When value prop is not provided, currentValue computed falls back to label
|
|
628
|
+
// This is tested implicitly through the component's internal logic
|
|
629
|
+
// The actual v-model binding behavior is tested in other tests
|
|
630
|
+
const wrapper = mount(FzCheckbox, {
|
|
631
|
+
props: {
|
|
632
|
+
label: "Test Checkbox",
|
|
633
|
+
modelValue: false,
|
|
634
|
+
},
|
|
635
|
+
});
|
|
636
|
+
await wrapper.vm.$nextTick();
|
|
637
|
+
// Component should render without errors when value is not provided
|
|
638
|
+
expect(wrapper.exists()).toBe(true);
|
|
639
|
+
const input = wrapper.find("input");
|
|
640
|
+
expect(input.exists()).toBe(true);
|
|
641
|
+
// The input's value attribute should be set (even if undefined, it falls back to label internally)
|
|
642
|
+
expect(input.attributes("value")).toBeDefined();
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// ============================================
|
|
647
|
+
// SNAPSHOTS
|
|
648
|
+
// ============================================
|
|
649
|
+
describe("Snapshots", () => {
|
|
650
|
+
it("should match snapshot - default state", () => {
|
|
651
|
+
const wrapper = mount(FzCheckbox, {
|
|
652
|
+
props: {
|
|
653
|
+
label: "Test Checkbox",
|
|
654
|
+
value: "test",
|
|
655
|
+
modelValue: false,
|
|
656
|
+
},
|
|
657
|
+
});
|
|
658
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it("should match snapshot - checked state", () => {
|
|
662
|
+
const wrapper = mount(FzCheckbox, {
|
|
663
|
+
props: {
|
|
664
|
+
label: "Test Checkbox",
|
|
665
|
+
value: "test",
|
|
666
|
+
modelValue: true,
|
|
667
|
+
},
|
|
668
|
+
});
|
|
669
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it("should match snapshot - disabled state", () => {
|
|
673
|
+
const wrapper = mount(FzCheckbox, {
|
|
674
|
+
props: {
|
|
675
|
+
label: "Test Checkbox",
|
|
676
|
+
value: "test",
|
|
677
|
+
modelValue: false,
|
|
678
|
+
disabled: true,
|
|
679
|
+
},
|
|
680
|
+
});
|
|
681
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it("should match snapshot - error state", () => {
|
|
685
|
+
const wrapper = mount(FzCheckbox, {
|
|
686
|
+
props: {
|
|
687
|
+
label: "Test Checkbox",
|
|
688
|
+
value: "test",
|
|
689
|
+
modelValue: false,
|
|
690
|
+
error: true,
|
|
691
|
+
},
|
|
692
|
+
slots: {
|
|
693
|
+
error: "Error message",
|
|
694
|
+
},
|
|
695
|
+
});
|
|
696
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("should match snapshot - indeterminate state", () => {
|
|
700
|
+
const wrapper = mount(FzCheckbox, {
|
|
701
|
+
props: {
|
|
702
|
+
label: "Test Checkbox",
|
|
703
|
+
value: "test",
|
|
704
|
+
modelValue: false,
|
|
705
|
+
indeterminate: true,
|
|
706
|
+
},
|
|
707
|
+
});
|
|
708
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it("should match snapshot - standalone mode", () => {
|
|
712
|
+
const wrapper = mount(FzCheckbox, {
|
|
713
|
+
props: {
|
|
714
|
+
label: "Test Checkbox",
|
|
715
|
+
value: "test",
|
|
716
|
+
modelValue: false,
|
|
717
|
+
standalone: true,
|
|
718
|
+
},
|
|
719
|
+
});
|
|
720
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
it("should match snapshot - with emphasis", () => {
|
|
724
|
+
const wrapper = mount(FzCheckbox, {
|
|
725
|
+
props: {
|
|
726
|
+
label: "Test Checkbox",
|
|
727
|
+
value: "test",
|
|
728
|
+
modelValue: true,
|
|
729
|
+
emphasis: true,
|
|
730
|
+
},
|
|
731
|
+
});
|
|
732
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
it("should match snapshot - required", () => {
|
|
736
|
+
const wrapper = mount(FzCheckbox, {
|
|
737
|
+
props: {
|
|
738
|
+
label: "Test Checkbox",
|
|
739
|
+
value: "test",
|
|
740
|
+
modelValue: false,
|
|
741
|
+
required: true,
|
|
742
|
+
},
|
|
743
|
+
});
|
|
744
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
745
|
+
});
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
|