Skip to content

Commit 86a3bab

Browse files
[Test Improver] Add unit tests for sort.js utility functions (#17790)
* test: add unit tests for sort.js utility functions Add 47 unit tests covering the previously untested functions in shell/utils/sort.js: - typeOf: type classification for null, undefined, primitives, arrays, objects, Error/Date instances - spaceship: three-way comparison (-1, 0, 1) - compare: cross-type ordering and same-type value comparisons for booleans, numbers, strings, arrays, and dates - parseField: field descriptor parsing with optional ':desc' suffix - sortableNumericSuffix: numeric-padded string normalization - isNumeric: numeric-string detection The existing sort.test.ts covered only sortBy; these tests target the lower-level helpers that sortBy relies on. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger checks --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 42583b6 commit 86a3bab

1 file changed

Lines changed: 301 additions & 0 deletions

File tree

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
import {
2+
typeOf,
3+
spaceship,
4+
compare,
5+
parseField,
6+
sortableNumericSuffix,
7+
isNumeric,
8+
} from '@shell/utils/sort';
9+
10+
describe('sort utils', () => {
11+
describe('typeOf', () => {
12+
it.each([
13+
{
14+
desc: 'null',
15+
input: null,
16+
expected: 'null',
17+
},
18+
{
19+
desc: 'undefined',
20+
input: undefined,
21+
expected: 'undefined',
22+
},
23+
{
24+
desc: 'boolean true',
25+
input: true,
26+
expected: 'boolean',
27+
},
28+
{
29+
desc: 'boolean false',
30+
input: false,
31+
expected: 'boolean',
32+
},
33+
{
34+
desc: 'number 0',
35+
input: 0,
36+
expected: 'number',
37+
},
38+
{
39+
desc: 'number 42',
40+
input: 42,
41+
expected: 'number',
42+
},
43+
{
44+
desc: 'string',
45+
input: 'hello',
46+
expected: 'string',
47+
},
48+
{
49+
desc: 'array',
50+
input: [1, 2, 3],
51+
expected: 'array',
52+
},
53+
{
54+
desc: 'plain object',
55+
input: { a: 1 },
56+
expected: 'object',
57+
},
58+
{
59+
desc: 'function',
60+
input: () => {},
61+
expected: 'function',
62+
},
63+
{
64+
desc: 'regexp',
65+
input: /abc/,
66+
expected: 'regexp',
67+
},
68+
{
69+
desc: 'error instance',
70+
input: new Error('test'),
71+
expected: 'error',
72+
},
73+
{
74+
desc: 'date instance',
75+
input: new Date('2024-01-01'),
76+
expected: 'date',
77+
},
78+
])('returns "$expected" for $desc', ({ input, expected }) => {
79+
expect(typeOf(input)).toStrictEqual(expected);
80+
});
81+
});
82+
83+
describe('spaceship', () => {
84+
it.each([
85+
{
86+
desc: 'a greater than b',
87+
a: 5,
88+
b: 3,
89+
expected: 1,
90+
},
91+
{
92+
desc: 'a less than b',
93+
a: 2,
94+
b: 7,
95+
expected: -1,
96+
},
97+
{
98+
desc: 'a equals b',
99+
a: 4,
100+
b: 4,
101+
expected: 0,
102+
},
103+
])('returns $expected when $desc', ({ a, b, expected }) => {
104+
expect(spaceship(a, b)).toStrictEqual(expected);
105+
});
106+
});
107+
108+
describe('compare', () => {
109+
describe('same-type comparisons', () => {
110+
it.each([
111+
{
112+
desc: 'numbers ascending',
113+
a: 1,
114+
b: 2,
115+
expected: -1,
116+
},
117+
{
118+
desc: 'numbers descending',
119+
a: 9,
120+
b: 3,
121+
expected: 1,
122+
},
123+
{
124+
desc: 'numbers equal',
125+
a: 5,
126+
b: 5,
127+
expected: 0,
128+
},
129+
{
130+
desc: 'booleans false before true',
131+
a: false,
132+
b: true,
133+
expected: -1,
134+
},
135+
{
136+
desc: 'booleans equal',
137+
a: true,
138+
b: true,
139+
expected: 0,
140+
},
141+
{
142+
desc: 'strings alphabetical',
143+
a: 'apple',
144+
b: 'banana',
145+
expected: -1,
146+
},
147+
{
148+
desc: 'strings equal',
149+
a: 'foo',
150+
b: 'foo',
151+
expected: 0,
152+
},
153+
])('$desc', ({ a, b, expected }) => {
154+
expect(compare(a, b)).toStrictEqual(expected);
155+
});
156+
});
157+
158+
describe('cross-type ordering', () => {
159+
it('sorts null before boolean', () => {
160+
expect(compare(null, true)).toStrictEqual(-1);
161+
});
162+
163+
it('sorts undefined before null', () => {
164+
expect(compare(undefined, null)).toStrictEqual(-1);
165+
});
166+
167+
it('sorts number before string', () => {
168+
expect(compare(1, 'a')).toStrictEqual(-1);
169+
});
170+
171+
it('sorts string before array', () => {
172+
expect(compare('z', [1])).toStrictEqual(-1);
173+
});
174+
});
175+
176+
describe('array comparison', () => {
177+
it('compares arrays element by element', () => {
178+
expect(compare([1, 2], [1, 3])).toStrictEqual(-1);
179+
});
180+
181+
it('returns 0 for equal arrays', () => {
182+
expect(compare([1, 2], [1, 2])).toStrictEqual(0);
183+
});
184+
185+
it('shorter array sorts before longer equal-prefix array', () => {
186+
expect(compare([1], [1, 2])).toStrictEqual(-1);
187+
});
188+
189+
it('longer array sorts after shorter equal-prefix array', () => {
190+
expect(compare([1, 2], [1])).toStrictEqual(1);
191+
});
192+
});
193+
194+
describe('date comparison', () => {
195+
it('earlier date sorts before later date', () => {
196+
const earlier = new Date('2020-01-01');
197+
const later = new Date('2023-01-01');
198+
199+
expect(compare(earlier, later)).toStrictEqual(-1);
200+
});
201+
202+
it('same dates compare as equal', () => {
203+
const d1 = new Date('2022-06-15');
204+
const d2 = new Date('2022-06-15');
205+
206+
expect(compare(d1, d2)).toStrictEqual(0);
207+
});
208+
});
209+
});
210+
211+
describe('parseField', () => {
212+
it.each([
213+
{
214+
desc: 'plain field name',
215+
input: 'name',
216+
expected: { field: 'name', reverse: false },
217+
},
218+
{
219+
desc: 'field with desc suffix',
220+
input: 'name:desc',
221+
expected: { field: 'name', reverse: true },
222+
},
223+
{
224+
desc: 'nested field path',
225+
input: 'metadata.name',
226+
expected: { field: 'metadata.name', reverse: false },
227+
},
228+
{
229+
desc: 'nested field with desc suffix',
230+
input: 'metadata.name:desc',
231+
expected: { field: 'metadata.name', reverse: true },
232+
},
233+
])('$desc', ({ input, expected }) => {
234+
expect(parseField(input)).toStrictEqual(expected);
235+
});
236+
});
237+
238+
describe('sortableNumericSuffix', () => {
239+
it.each([
240+
{
241+
desc: 'string with numeric suffix',
242+
input: 'foo1',
243+
expected: 'foo0000000001',
244+
},
245+
{
246+
desc: 'string with multi-digit numeric part',
247+
input: 'pod42',
248+
expected: 'pod0000000042',
249+
},
250+
{
251+
desc: 'string with mixed numbers',
252+
input: 'foo1-bar2',
253+
expected: 'foo0000000001-bar0000000002',
254+
},
255+
{
256+
desc: 'string with no numbers',
257+
input: 'foobar',
258+
expected: 'foobar',
259+
},
260+
{
261+
desc: 'non-string passthrough (number)',
262+
input: 42 as any,
263+
expected: 42,
264+
},
265+
])('$desc', ({ input, expected }) => {
266+
expect(sortableNumericSuffix(input)).toStrictEqual(expected);
267+
});
268+
});
269+
270+
describe('isNumeric', () => {
271+
it.each([
272+
{
273+
desc: 'numeric string',
274+
input: '123',
275+
expected: true,
276+
},
277+
{
278+
desc: 'zero string',
279+
input: '0',
280+
expected: true,
281+
},
282+
{
283+
desc: 'non-numeric string',
284+
input: 'abc',
285+
expected: false,
286+
},
287+
{
288+
desc: 'mixed alphanumeric string',
289+
input: 'foo1',
290+
expected: false,
291+
},
292+
{
293+
desc: 'numeric number',
294+
input: 42,
295+
expected: true,
296+
},
297+
])('$desc', ({ input, expected }) => {
298+
expect(isNumeric(input)).toStrictEqual(expected);
299+
});
300+
});
301+
});

0 commit comments

Comments
 (0)