@aiready/components 0.13.22 → 0.14.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/components/button.d.ts +1 -1
- package/package.json +2 -2
- package/src/components/__tests__/card.test.tsx +31 -0
- package/src/components/__tests__/checkbox.test.tsx +75 -0
- package/src/components/__tests__/container.test.tsx +70 -0
- package/src/components/__tests__/grid.test.tsx +172 -0
- package/src/components/__tests__/input.test.tsx +46 -0
- package/src/components/__tests__/label.test.tsx +53 -0
- package/src/components/__tests__/modal.test.tsx +88 -0
- package/src/components/__tests__/radio-group.test.tsx +107 -0
- package/src/components/__tests__/select.test.tsx +100 -0
- package/src/components/__tests__/separator.test.tsx +72 -0
- package/src/components/__tests__/stack.test.tsx +197 -0
- package/src/components/__tests__/switch.test.tsx +85 -0
- package/src/components/__tests__/textarea.test.tsx +96 -0
- package/src/hooks/__tests__/useDebounce.test.ts +173 -0
- package/src/utils/__tests__/cn.test.ts +67 -0
- package/src/utils/__tests__/colors.test.ts +94 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { renderHook, act } from '@testing-library/react';
|
|
3
|
+
import { useDebounce } from '../useDebounce';
|
|
4
|
+
|
|
5
|
+
describe('useDebounce', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
vi.useFakeTimers();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
vi.useRealTimers();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should return initial value immediately', () => {
|
|
15
|
+
const { result } = renderHook(() => useDebounce('initial', 300));
|
|
16
|
+
expect(result.current).toBe('initial');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should debounce value updates', () => {
|
|
20
|
+
const { result, rerender } = renderHook(
|
|
21
|
+
({ value, delay }) => useDebounce(value, delay),
|
|
22
|
+
{ initialProps: { value: 'initial', delay: 300 } }
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
expect(result.current).toBe('initial');
|
|
26
|
+
|
|
27
|
+
// Update value
|
|
28
|
+
rerender({ value: 'updated', delay: 300 });
|
|
29
|
+
|
|
30
|
+
// Value should still be initial before delay
|
|
31
|
+
expect(result.current).toBe('initial');
|
|
32
|
+
|
|
33
|
+
// Fast forward time by 300ms
|
|
34
|
+
act(() => {
|
|
35
|
+
vi.advanceTimersByTime(300);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Value should now be updated
|
|
39
|
+
expect(result.current).toBe('updated');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should reset timer on rapid value changes', () => {
|
|
43
|
+
const { result, rerender } = renderHook(
|
|
44
|
+
({ value, delay }) => useDebounce(value, delay),
|
|
45
|
+
{ initialProps: { value: 'initial', delay: 300 } }
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// First update
|
|
49
|
+
rerender({ value: 'first', delay: 300 });
|
|
50
|
+
act(() => {
|
|
51
|
+
vi.advanceTimersByTime(100);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Second update before first delay completes
|
|
55
|
+
rerender({ value: 'second', delay: 300 });
|
|
56
|
+
act(() => {
|
|
57
|
+
vi.advanceTimersByTime(100);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Third update before second delay completes
|
|
61
|
+
rerender({ value: 'third', delay: 300 });
|
|
62
|
+
|
|
63
|
+
// Value should still be initial
|
|
64
|
+
expect(result.current).toBe('initial');
|
|
65
|
+
|
|
66
|
+
// Complete the delay
|
|
67
|
+
act(() => {
|
|
68
|
+
vi.advanceTimersByTime(300);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Should have the last value
|
|
72
|
+
expect(result.current).toBe('third');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should use default delay of 300ms', () => {
|
|
76
|
+
const { result, rerender } = renderHook(({ value }) => useDebounce(value), {
|
|
77
|
+
initialProps: { value: 'initial' },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
rerender({ value: 'updated' });
|
|
81
|
+
|
|
82
|
+
act(() => {
|
|
83
|
+
vi.advanceTimersByTime(299);
|
|
84
|
+
});
|
|
85
|
+
expect(result.current).toBe('initial');
|
|
86
|
+
|
|
87
|
+
act(() => {
|
|
88
|
+
vi.advanceTimersByTime(1);
|
|
89
|
+
});
|
|
90
|
+
expect(result.current).toBe('updated');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should work with different value types', () => {
|
|
94
|
+
// Test with number
|
|
95
|
+
const { result: numberResult, rerender: numberRerender } = renderHook(
|
|
96
|
+
({ value }) => useDebounce(value, 100),
|
|
97
|
+
{ initialProps: { value: 0 } }
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
numberRerender({ value: 42 });
|
|
101
|
+
act(() => {
|
|
102
|
+
vi.advanceTimersByTime(100);
|
|
103
|
+
});
|
|
104
|
+
expect(numberResult.current).toBe(42);
|
|
105
|
+
|
|
106
|
+
// Test with object
|
|
107
|
+
const { result: objectResult, rerender: objectRerender } = renderHook(
|
|
108
|
+
({ value }) => useDebounce(value, 100),
|
|
109
|
+
{ initialProps: { value: { key: 'initial' } } }
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
objectRerender({ value: { key: 'updated' } });
|
|
113
|
+
act(() => {
|
|
114
|
+
vi.advanceTimersByTime(100);
|
|
115
|
+
});
|
|
116
|
+
expect(objectResult.current).toEqual({ key: 'updated' });
|
|
117
|
+
|
|
118
|
+
// Test with array
|
|
119
|
+
const { result: arrayResult, rerender: arrayRerender } = renderHook(
|
|
120
|
+
({ value }) => useDebounce(value, 100),
|
|
121
|
+
{ initialProps: { value: [1, 2, 3] } }
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
arrayRerender({ value: [4, 5, 6] });
|
|
125
|
+
act(() => {
|
|
126
|
+
vi.advanceTimersByTime(100);
|
|
127
|
+
});
|
|
128
|
+
expect(arrayResult.current).toEqual([4, 5, 6]);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should handle delay changes', () => {
|
|
132
|
+
const { result, rerender } = renderHook(
|
|
133
|
+
({ value, delay }) => useDebounce(value, delay),
|
|
134
|
+
{ initialProps: { value: 'initial', delay: 300 } }
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
rerender({ value: 'updated', delay: 300 });
|
|
138
|
+
|
|
139
|
+
// Change delay before first one completes
|
|
140
|
+
rerender({ value: 'updated', delay: 500 });
|
|
141
|
+
|
|
142
|
+
act(() => {
|
|
143
|
+
vi.advanceTimersByTime(300);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Should still be initial because delay changed
|
|
147
|
+
expect(result.current).toBe('initial');
|
|
148
|
+
|
|
149
|
+
act(() => {
|
|
150
|
+
vi.advanceTimersByTime(200);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Now should be updated
|
|
154
|
+
expect(result.current).toBe('updated');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should cleanup timer on unmount', () => {
|
|
158
|
+
const { unmount, rerender } = renderHook(
|
|
159
|
+
({ value }) => useDebounce(value, 300),
|
|
160
|
+
{ initialProps: { value: 'initial' } }
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
rerender({ value: 'updated' });
|
|
164
|
+
|
|
165
|
+
// Unmount before delay completes
|
|
166
|
+
unmount();
|
|
167
|
+
|
|
168
|
+
// Advance timers - should not cause errors
|
|
169
|
+
act(() => {
|
|
170
|
+
vi.advanceTimersByTime(300);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { cn } from '../cn';
|
|
3
|
+
|
|
4
|
+
describe('cn', () => {
|
|
5
|
+
it('should merge class names', () => {
|
|
6
|
+
expect(cn('foo', 'bar')).toBe('foo bar');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should handle conditional classes', () => {
|
|
10
|
+
const showBar = false;
|
|
11
|
+
const showBaz = true;
|
|
12
|
+
expect(cn('foo', showBar && 'bar', 'baz')).toBe('foo baz');
|
|
13
|
+
expect(cn('foo', showBaz && 'bar', 'baz')).toBe('foo bar baz');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should handle undefined and null values', () => {
|
|
17
|
+
expect(cn('foo', undefined, null, 'bar')).toBe('foo bar');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should handle arrays', () => {
|
|
21
|
+
expect(cn(['foo', 'bar'], 'baz')).toBe('foo bar baz');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should handle objects', () => {
|
|
25
|
+
expect(cn({ foo: true, bar: false, baz: true })).toBe('foo baz');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should merge Tailwind classes correctly', () => {
|
|
29
|
+
expect(cn('px-4 py-2', 'px-6')).toBe('py-2 px-6');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should handle empty inputs', () => {
|
|
33
|
+
expect(cn()).toBe('');
|
|
34
|
+
expect(cn('')).toBe('');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should merge conflicting Tailwind padding classes', () => {
|
|
38
|
+
expect(cn('p-4', 'p-6')).toBe('p-6');
|
|
39
|
+
expect(cn('px-4', 'px-6')).toBe('px-6');
|
|
40
|
+
expect(cn('py-2', 'py-4')).toBe('py-4');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should merge conflicting Tailwind margin classes', () => {
|
|
44
|
+
expect(cn('m-4', 'm-6')).toBe('m-6');
|
|
45
|
+
expect(cn('mx-4', 'mx-6')).toBe('mx-6');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should merge conflicting Tailwind text color classes', () => {
|
|
49
|
+
expect(cn('text-red-500', 'text-blue-500')).toBe('text-blue-500');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should merge conflicting Tailwind background classes', () => {
|
|
53
|
+
expect(cn('bg-red-500', 'bg-blue-500')).toBe('bg-blue-500');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should preserve non-conflicting classes', () => {
|
|
57
|
+
expect(cn('flex', 'items-center', 'justify-center')).toBe(
|
|
58
|
+
'flex items-center justify-center'
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should handle mixed Tailwind and custom classes', () => {
|
|
63
|
+
expect(cn('custom-class', 'p-4', 'another-class')).toBe(
|
|
64
|
+
'custom-class p-4 another-class'
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
severityColors,
|
|
4
|
+
domainColors,
|
|
5
|
+
chartColors,
|
|
6
|
+
getDomainColor,
|
|
7
|
+
getSeverityColor,
|
|
8
|
+
hexToRgba,
|
|
9
|
+
} from '../colors';
|
|
10
|
+
|
|
11
|
+
describe('Color Utilities', () => {
|
|
12
|
+
describe('severityColors', () => {
|
|
13
|
+
it('should have correct severity color mappings', () => {
|
|
14
|
+
expect(severityColors.critical).toBe('#ef4444');
|
|
15
|
+
expect(severityColors.major).toBe('#f59e0b');
|
|
16
|
+
expect(severityColors.minor).toBe('#eab308');
|
|
17
|
+
expect(severityColors.info).toBe('#60a5fa');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('domainColors', () => {
|
|
22
|
+
it('should have at least 8 domain colors', () => {
|
|
23
|
+
expect(domainColors.length).toBeGreaterThanOrEqual(8);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should contain valid hex color codes', () => {
|
|
27
|
+
const hexPattern = /^#[0-9a-f]{6}$/i;
|
|
28
|
+
domainColors.forEach((color) => {
|
|
29
|
+
expect(color).toMatch(hexPattern);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('chartColors', () => {
|
|
35
|
+
it('should have correct chart color mappings', () => {
|
|
36
|
+
expect(chartColors.primary).toBe('#3b82f6');
|
|
37
|
+
expect(chartColors.success).toBe('#10b981');
|
|
38
|
+
expect(chartColors.warning).toBe('#f59e0b');
|
|
39
|
+
expect(chartColors.danger).toBe('#ef4444');
|
|
40
|
+
expect(chartColors.info).toBe('#06b6d4');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('getDomainColor', () => {
|
|
45
|
+
it('should return correct color for valid index', () => {
|
|
46
|
+
expect(getDomainColor(0)).toBe(domainColors[0]);
|
|
47
|
+
expect(getDomainColor(1)).toBe(domainColors[1]);
|
|
48
|
+
expect(getDomainColor(2)).toBe(domainColors[2]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should wrap around for indices greater than array length', () => {
|
|
52
|
+
const length = domainColors.length;
|
|
53
|
+
expect(getDomainColor(length)).toBe(domainColors[0]);
|
|
54
|
+
expect(getDomainColor(length + 1)).toBe(domainColors[1]);
|
|
55
|
+
expect(getDomainColor(length * 2)).toBe(domainColors[0]);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('getSeverityColor', () => {
|
|
60
|
+
it('should return correct color for critical severity', () => {
|
|
61
|
+
expect(getSeverityColor('critical')).toBe('#ef4444');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should return correct color for major severity', () => {
|
|
65
|
+
expect(getSeverityColor('major')).toBe('#f59e0b');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return correct color for minor severity', () => {
|
|
69
|
+
expect(getSeverityColor('minor')).toBe('#eab308');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should return correct color for info severity', () => {
|
|
73
|
+
expect(getSeverityColor('info')).toBe('#60a5fa');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('hexToRgba', () => {
|
|
78
|
+
it('should convert hex to rgba with alpha 1', () => {
|
|
79
|
+
expect(hexToRgba('#ff0000', 1)).toBe('rgba(255, 0, 0, 1)');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should convert hex to rgba with alpha 0.5', () => {
|
|
83
|
+
expect(hexToRgba('#00ff00', 0.5)).toBe('rgba(0, 255, 0, 0.5)');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should convert hex to rgba with alpha 0', () => {
|
|
87
|
+
expect(hexToRgba('#0000ff', 0)).toBe('rgba(0, 0, 255, 0)');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should handle mixed hex colors', () => {
|
|
91
|
+
expect(hexToRgba('#3b82f6', 0.8)).toBe('rgba(59, 130, 246, 0.8)');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|