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