Skip to content

Commit 5a17d5b

Browse files
committed
bigger refactor of select-plugin and related tests
1 parent 5579ffe commit 5a17d5b

File tree

6 files changed

+637
-38
lines changed

6 files changed

+637
-38
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@
105105
"start-server-and-test": "^1.15.2",
106106
"storybook": "^10.0.2",
107107
"tsd": "^0.25.0",
108-
"typescript": "^5.7.2"
108+
"typescript": "^5.7.2",
109+
"vitest": "^4.0.6"
109110
},
110111
"prettier": {
111112
"bracketSameLine": true,

packages/leva/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"react-colorful": "^5.5.1",
3333
"react-dropzone": "^12.0.0",
3434
"v8n": "^1.3.3",
35+
"zod": "^4.1.12",
3536
"zustand": "^3.6.9"
3637
},
3738
"devDependencies": {
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
import { describe, it, expect, vi } from 'vitest'
2+
import { normalize, schema, sanitize, format } from './select-plugin'
3+
4+
describe('Select Plugin - normalize function', () => {
5+
describe('Array of primitives', () => {
6+
it('should normalize array of strings', () => {
7+
const input = { options: ['x', 'y', 'z'] }
8+
const result = normalize(input)
9+
10+
expect(result.value).toBe('x')
11+
expect(result.settings.keys).toEqual(['x', 'y', 'z'])
12+
expect(result.settings.values).toEqual(['x', 'y', 'z'])
13+
})
14+
15+
it('should normalize array of numbers', () => {
16+
const input = { options: [1, 2, 3] }
17+
const result = normalize(input)
18+
19+
expect(result.value).toBe(1)
20+
expect(result.settings.keys).toEqual(['1', '2', '3'])
21+
expect(result.settings.values).toEqual([1, 2, 3])
22+
})
23+
24+
it('should normalize array of booleans', () => {
25+
const input = { options: [true, false] }
26+
const result = normalize(input)
27+
28+
expect(result.value).toBe(true)
29+
expect(result.settings.keys).toEqual(['true', 'false'])
30+
expect(result.settings.values).toEqual([true, false])
31+
})
32+
33+
it('should normalize array of mixed primitive types', () => {
34+
const input = { options: ['x', 1, true] }
35+
const result = normalize(input)
36+
37+
expect(result.value).toBe('x')
38+
expect(result.settings.keys).toEqual(['x', '1', 'true'])
39+
expect(result.settings.values).toEqual(['x', 1, true])
40+
})
41+
42+
it('should use provided value if it exists in options', () => {
43+
const input = { value: 'y', options: ['x', 'y', 'z'] }
44+
const result = normalize(input)
45+
46+
expect(result.value).toBe('y')
47+
expect(result.settings.keys).toEqual(['x', 'y', 'z'])
48+
expect(result.settings.values).toEqual(['x', 'y', 'z'])
49+
})
50+
})
51+
52+
describe('Object with key-value pairs (key as label)', () => {
53+
it('should normalize object with string values', () => {
54+
const input = { options: { foo: 'bar', baz: 'qux' } }
55+
const result = normalize(input)
56+
57+
expect(result.value).toBe('bar')
58+
expect(result.settings.keys).toEqual(['foo', 'baz'])
59+
expect(result.settings.values).toEqual(['bar', 'qux'])
60+
})
61+
62+
it('should normalize object with number values', () => {
63+
const input = { options: { small: 10, medium: 20, large: 30 } }
64+
const result = normalize(input)
65+
66+
expect(result.value).toBe(10)
67+
expect(result.settings.keys).toEqual(['small', 'medium', 'large'])
68+
expect(result.settings.values).toEqual([10, 20, 30])
69+
})
70+
71+
it('should normalize object with boolean values', () => {
72+
const input = { options: { yes: true, no: false } }
73+
const result = normalize(input)
74+
75+
expect(result.value).toBe(true)
76+
expect(result.settings.keys).toEqual(['yes', 'no'])
77+
expect(result.settings.values).toEqual([true, false])
78+
})
79+
80+
it('should normalize object with mixed value types', () => {
81+
const input = { options: { x: 1, foo: 'bar', z: true } }
82+
const result = normalize(input)
83+
84+
expect(result.value).toBe(1)
85+
expect(result.settings.keys).toEqual(['x', 'foo', 'z'])
86+
expect(result.settings.values).toEqual([1, 'bar', true])
87+
})
88+
89+
it('should use provided value if it exists in options', () => {
90+
const input = { value: 'qux', options: { foo: 'bar', baz: 'qux' } }
91+
const result = normalize(input)
92+
93+
expect(result.value).toBe('qux')
94+
expect(result.settings.keys).toEqual(['foo', 'baz'])
95+
expect(result.settings.values).toEqual(['bar', 'qux'])
96+
})
97+
})
98+
99+
describe('Array of {value, label} objects', () => {
100+
it('should normalize array of value/label objects with all labels', () => {
101+
const input = {
102+
options: [
103+
{ value: '#f00', label: 'Red' },
104+
{ value: '#0f0', label: 'Green' },
105+
{ value: '#00f', label: 'Blue' },
106+
],
107+
}
108+
const result = normalize(input)
109+
110+
expect(result.value).toBe('#f00')
111+
expect(result.settings.keys).toEqual(['Red', 'Green', 'Blue'])
112+
expect(result.settings.values).toEqual(['#f00', '#0f0', '#00f'])
113+
})
114+
115+
it('should normalize array of value/label objects with some labels missing', () => {
116+
const input = {
117+
options: [{ value: '#f00', label: 'Red' }, { value: '#0f0' }, { value: '#00f', label: 'Blue' }],
118+
}
119+
const result = normalize(input)
120+
121+
expect(result.value).toBe('#f00')
122+
expect(result.settings.keys).toEqual(['Red', '#0f0', 'Blue'])
123+
expect(result.settings.values).toEqual(['#f00', '#0f0', '#00f'])
124+
})
125+
126+
it('should normalize array of value/label objects with no labels', () => {
127+
const input = {
128+
options: [{ value: 'x' }, { value: 'y' }, { value: 'z' }],
129+
}
130+
const result = normalize(input)
131+
132+
expect(result.value).toBe('x')
133+
expect(result.settings.keys).toEqual(['x', 'y', 'z'])
134+
expect(result.settings.values).toEqual(['x', 'y', 'z'])
135+
})
136+
137+
it('should normalize with number values', () => {
138+
const input = {
139+
options: [
140+
{ value: 1, label: 'One' },
141+
{ value: 2, label: 'Two' },
142+
],
143+
}
144+
const result = normalize(input)
145+
146+
expect(result.value).toBe(1)
147+
expect(result.settings.keys).toEqual(['One', 'Two'])
148+
expect(result.settings.values).toEqual([1, 2])
149+
})
150+
151+
it('should normalize with boolean values', () => {
152+
const input = {
153+
options: [
154+
{ value: true, label: 'Yes' },
155+
{ value: false, label: 'No' },
156+
],
157+
}
158+
const result = normalize(input)
159+
160+
expect(result.value).toBe(true)
161+
expect(result.settings.keys).toEqual(['Yes', 'No'])
162+
expect(result.settings.values).toEqual([true, false])
163+
})
164+
165+
it('should use provided value if it exists in options', () => {
166+
const input = {
167+
value: '#0f0',
168+
options: [
169+
{ value: '#f00', label: 'Red' },
170+
{ value: '#0f0', label: 'Green' },
171+
{ value: '#00f', label: 'Blue' },
172+
],
173+
}
174+
const result = normalize(input)
175+
176+
expect(result.value).toBe('#0f0')
177+
expect(result.settings.keys).toEqual(['Red', 'Green', 'Blue'])
178+
expect(result.settings.values).toEqual(['#f00', '#0f0', '#00f'])
179+
})
180+
})
181+
182+
describe('Edge cases and backward compatibility', () => {
183+
it('should default to first value when no value is provided', () => {
184+
const input = { options: ['a', 'b', 'c'] }
185+
const result = normalize(input)
186+
187+
expect(result.value).toBe('a')
188+
})
189+
190+
it('should warn and return undefined when value does not exist in options', () => {
191+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
192+
193+
const input = { value: true, options: [false] }
194+
const result = normalize(input)
195+
196+
expect(result.value).toBe(undefined)
197+
expect(result.settings.keys).toEqual(['false'])
198+
expect(result.settings.values).toEqual([false])
199+
expect(consoleWarnSpy).toHaveBeenCalledWith("[Leva] Selected value doesn't exist in Select options ", input)
200+
201+
consoleWarnSpy.mockRestore()
202+
})
203+
204+
it('should handle empty array options', () => {
205+
const input = { options: [] }
206+
const result = normalize(input)
207+
208+
expect(result.value).toBe(undefined)
209+
expect(result.settings.keys).toEqual([])
210+
expect(result.settings.values).toEqual([])
211+
})
212+
213+
it('should handle empty object options', () => {
214+
const input = { options: {} }
215+
const result = normalize(input)
216+
217+
expect(result.value).toBe(undefined)
218+
expect(result.settings.keys).toEqual([])
219+
expect(result.settings.values).toEqual([])
220+
})
221+
222+
it('should handle single option array', () => {
223+
const input = { options: ['only-one'] }
224+
const result = normalize(input)
225+
226+
expect(result.value).toBe('only-one')
227+
expect(result.settings.keys).toEqual(['only-one'])
228+
expect(result.settings.values).toEqual(['only-one'])
229+
})
230+
231+
it('should handle value of 0 correctly', () => {
232+
const input = { value: 0, options: [0, 1, 2] }
233+
const result = normalize(input)
234+
235+
expect(result.value).toBe(0)
236+
expect(result.settings.keys).toEqual(['0', '1', '2'])
237+
expect(result.settings.values).toEqual([0, 1, 2])
238+
})
239+
240+
it('should handle empty string value correctly', () => {
241+
const input = { value: '', options: ['', 'a', 'b'] }
242+
const result = normalize(input)
243+
244+
expect(result.value).toBe('')
245+
expect(result.settings.keys).toEqual(['', 'a', 'b'])
246+
expect(result.settings.values).toEqual(['', 'a', 'b'])
247+
})
248+
})
249+
})
250+
251+
describe('Select Plugin - schema validation', () => {
252+
it('should accept array of primitives', () => {
253+
const result = schema(null, ['x', 'y', 'z'])
254+
expect(result.success).toBe(true)
255+
})
256+
257+
it('should accept object with primitive values', () => {
258+
const result = schema(null, { foo: 'bar', baz: 1 })
259+
expect(result.success).toBe(true)
260+
})
261+
262+
it('should accept array of value/label objects', () => {
263+
const result = schema(null, [{ value: 'x', label: 'X' }, { value: 'y' }])
264+
expect(result.success).toBe(true)
265+
})
266+
267+
it('should reject invalid input', () => {
268+
const result = schema(null, null)
269+
expect(result.success).toBe(false)
270+
})
271+
272+
it('should reject array with non-primitive values', () => {
273+
const result = schema(null, [{ nested: 'object' }, 'string'])
274+
expect(result.success).toBe(false)
275+
})
276+
})
277+
278+
describe('Select Plugin - sanitize function', () => {
279+
it('should pass when value exists in values', () => {
280+
const result = sanitize('x', { keys: ['x', 'y'], values: ['x', 'y'] })
281+
expect(result).toBe('x')
282+
})
283+
284+
it('should throw error when value does not exist in values', () => {
285+
expect(() => {
286+
sanitize('z', { keys: ['x', 'y'], values: ['x', 'y'] })
287+
}).toThrow("Selected value doesn't match Select options")
288+
})
289+
})
290+
291+
describe('Select Plugin - format function', () => {
292+
it('should return the index of the value', () => {
293+
const result = format('y', { keys: ['x', 'y', 'z'], values: ['x', 'y', 'z'] })
294+
expect(result).toBe(1)
295+
})
296+
297+
it('should return 0 for first value', () => {
298+
const result = format('x', { keys: ['x', 'y', 'z'], values: ['x', 'y', 'z'] })
299+
expect(result).toBe(0)
300+
})
301+
302+
it('should return -1 when value not found', () => {
303+
const result = format('notfound', { keys: ['x', 'y', 'z'], values: ['x', 'y', 'z'] })
304+
expect(result).toBe(-1)
305+
})
306+
})

0 commit comments

Comments
 (0)