@startinblox/components-ds4go 2.2.4 → 3.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/.gitlab-ci.yml +8 -2
- package/AGENTS.md +516 -0
- package/cypress/component/no-component-test.cy.ts +9 -0
- package/cypress/e2e/helpers/components/setupCacheInvalidation.cy.ts +512 -0
- package/cypress/e2e/helpers/components/setupCacheOnResourceReady.cy.ts +483 -0
- package/cypress/e2e/helpers/components/setupComponentSubscriptions.cy.ts +239 -0
- package/cypress/e2e/helpers/components/setupOnSaveReset.cy.ts +380 -0
- package/cypress/e2e/helpers/datas/checkValueInIntervalRecursive.cy.ts +563 -0
- package/cypress/e2e/helpers/datas/dataBuilder.cy.ts +508 -0
- package/cypress/e2e/helpers/datas/filterGenerator.cy.ts +285 -0
- package/cypress/e2e/helpers/datas/filterObjectByDateAfter.cy.ts +389 -0
- package/cypress/e2e/helpers/datas/filterObjectByDateInterval.cy.ts +613 -0
- package/cypress/e2e/helpers/datas/filterObjectById.cy.ts +276 -0
- package/cypress/e2e/helpers/datas/filterObjectByInterval.cy.ts +237 -0
- package/cypress/e2e/helpers/datas/filterObjectByNamedValue.cy.ts +299 -0
- package/cypress/e2e/helpers/datas/filterObjectByType.cy.ts +307 -0
- package/cypress/e2e/helpers/datas/filterObjectByValue.cy.ts +375 -0
- package/cypress/e2e/helpers/datas/sort.cy.ts +293 -0
- package/cypress/e2e/helpers/ui/formatDate.cy.ts +233 -0
- package/cypress/e2e/helpers/utils/requestNavigation.cy.ts +257 -0
- package/cypress/e2e/helpers/utils/uniq.cy.ts +160 -0
- package/cypress/support/e2e.ts +1 -0
- package/cypress.config.ts +2 -0
- package/dist/index.js +932 -734
- package/locales/en.xlf +75 -0
- package/package.json +10 -10
- package/src/components/solid-boilerplate.ts +76 -0
- package/src/components/solid-fact-bundle-creation.ts +202 -59
- package/src/helpers/components/componentObjectHandler.ts +5 -7
- package/src/helpers/components/componentObjectsHandler.ts +8 -3
- package/src/helpers/components/orbitComponent.ts +87 -68
- package/src/helpers/components/setupCacheInvalidation.ts +50 -23
- package/src/helpers/components/setupCacheOnResourceReady.ts +42 -23
- package/src/helpers/components/setupComponentSubscriptions.ts +10 -9
- package/src/helpers/components/setupOnSaveReset.ts +27 -5
- package/src/helpers/datas/checkValueInIntervalRecursive.ts +66 -0
- package/src/helpers/datas/dataBuilder.ts +4 -4
- package/src/helpers/datas/filterGenerator.ts +13 -10
- package/src/helpers/datas/filterObjectByDateAfter.ts +3 -3
- package/src/helpers/datas/filterObjectByDateInterval.ts +44 -0
- package/src/helpers/datas/filterObjectById.ts +7 -6
- package/src/helpers/datas/filterObjectByInterval.ts +6 -110
- package/src/helpers/datas/filterObjectByNamedValue.ts +35 -33
- package/src/helpers/datas/filterObjectByType.ts +3 -3
- package/src/helpers/datas/filterObjectByValue.ts +17 -16
- package/src/helpers/datas/sort.ts +50 -23
- package/src/helpers/index.ts +2 -0
- package/src/helpers/ui/formatDate.ts +14 -1
- package/src/helpers/utils/requestNavigation.ts +5 -2
- package/src/helpers/utils/uniq.ts +1 -1
- package/src/styles/fact-bundle-creation.scss +102 -0
- package/cypress/component/solid-boilerplate.cy.ts +0 -9
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import filterObjectByValue from "@helpers/datas/filterObjectByValue";
|
|
2
|
+
|
|
3
|
+
describe("filterObjectByValue helper function", () => {
|
|
4
|
+
describe("Basic functionality", () => {
|
|
5
|
+
it("should filter objects by any property value matching search string", () => {
|
|
6
|
+
const objects = [
|
|
7
|
+
{ name: "Alice Johnson", age: 30 },
|
|
8
|
+
{ name: "Bob Smith", age: 25 },
|
|
9
|
+
{ name: "Charlie Brown", age: 35 },
|
|
10
|
+
];
|
|
11
|
+
const result = filterObjectByValue(objects, "alice");
|
|
12
|
+
expect(result).to.have.length(1);
|
|
13
|
+
expect(result[0].name).to.equal("Alice Johnson");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should return all objects when searchString is empty", () => {
|
|
17
|
+
const objects = [{ name: "Alice" }, { name: "Bob" }];
|
|
18
|
+
const result = filterObjectByValue(objects, "");
|
|
19
|
+
expect(result).to.have.length(2);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should return all objects when searchString is only whitespace", () => {
|
|
23
|
+
const objects = [{ name: "Alice" }, { name: "Bob" }];
|
|
24
|
+
const result = filterObjectByValue(objects, " ");
|
|
25
|
+
expect(result).to.have.length(2);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should return empty array when no match found", () => {
|
|
29
|
+
const objects = [{ name: "Alice" }, { name: "Bob" }];
|
|
30
|
+
const result = filterObjectByValue(objects, "xyz");
|
|
31
|
+
expect(result).to.have.length(0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should search across all properties", () => {
|
|
35
|
+
const objects = [
|
|
36
|
+
{ name: "Alice", email: "alice@example.com" },
|
|
37
|
+
{ name: "Bob", email: "bob@test.com" },
|
|
38
|
+
{ name: "Charlie", email: "charlie@example.com" },
|
|
39
|
+
];
|
|
40
|
+
const result = filterObjectByValue(objects, "example");
|
|
41
|
+
expect(result).to.have.length(2);
|
|
42
|
+
expect(result.map((r) => r.name)).to.deep.equal(["Alice", "Charlie"]);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("Search scoring", () => {
|
|
47
|
+
it("should score matches in name/title higher (score 4)", () => {
|
|
48
|
+
const objects = [
|
|
49
|
+
{ name: "Alice", description: "Developer" },
|
|
50
|
+
{ description: "Alice specialist" },
|
|
51
|
+
];
|
|
52
|
+
const result = filterObjectByValue(objects, "alice");
|
|
53
|
+
expect(result[0].name).to.equal("Alice");
|
|
54
|
+
expect(result[1]).to.not.have.property("name");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should score whole word matches in name/title higher than partial", () => {
|
|
58
|
+
const objects = [
|
|
59
|
+
{ name: "Alice" },
|
|
60
|
+
{ name: "Alice Wonderland" },
|
|
61
|
+
{ description: "Alice specialist" },
|
|
62
|
+
];
|
|
63
|
+
const result = filterObjectByValue(objects, "alice");
|
|
64
|
+
expect(result[0].name).to.equal("Alice");
|
|
65
|
+
expect(result[1].name).to.equal("Alice Wonderland");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should score whole word matches in other properties (score 3)", () => {
|
|
69
|
+
const objects = [
|
|
70
|
+
{ description: "Alice specialist" },
|
|
71
|
+
{ description: "Alice Wonderland" },
|
|
72
|
+
];
|
|
73
|
+
const result = filterObjectByValue(objects, "specialist");
|
|
74
|
+
expect(result[0].description).to.equal("Alice specialist");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should score partial matches in name/title (score 2)", () => {
|
|
78
|
+
const objects = [{ name: "Alice Wonderland" }, { description: "Alice" }];
|
|
79
|
+
const result = filterObjectByValue(objects, "alice");
|
|
80
|
+
expect(result[0].name).to.equal("Alice Wonderland");
|
|
81
|
+
expect(result[1].description).to.equal("Alice");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should score partial matches in other properties (score 1)", () => {
|
|
85
|
+
const objects = [
|
|
86
|
+
{ description: "Alice Wonderland" },
|
|
87
|
+
{ description: "Alice specialist" },
|
|
88
|
+
];
|
|
89
|
+
const result = filterObjectByValue(objects, "ali");
|
|
90
|
+
expect(result).to.have.length(2);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should sort results by relevance score descending", () => {
|
|
94
|
+
const objects = [
|
|
95
|
+
{ name: "Alice", bio: "Developer" },
|
|
96
|
+
{ name: "Bob", bio: "Alice specialist" },
|
|
97
|
+
{ title: "Alice" },
|
|
98
|
+
];
|
|
99
|
+
const result = filterObjectByValue(objects, "alice");
|
|
100
|
+
expect(result).to.have.length(3);
|
|
101
|
+
expect(result[0]).to.have.property("name", "Alice");
|
|
102
|
+
expect(result[1]).to.have.property("title", "Alice");
|
|
103
|
+
expect(result[2]).to.have.property("name", "Bob");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("Case sensitivity", () => {
|
|
108
|
+
it("should be case insensitive", () => {
|
|
109
|
+
const objects = [{ name: "Alice" }, { name: "BOB" }, { name: "Charlie" }];
|
|
110
|
+
const result = filterObjectByValue(objects, "alice");
|
|
111
|
+
expect(result).to.have.length(1);
|
|
112
|
+
expect(result[0].name).to.equal("Alice");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should match uppercase search with lowercase data", () => {
|
|
116
|
+
const objects = [{ name: "alice" }, { name: "bob" }];
|
|
117
|
+
const result = filterObjectByValue(objects, "ALICE");
|
|
118
|
+
expect(result).to.have.length(1);
|
|
119
|
+
expect(result[0].name).to.equal("alice");
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe("Recursive search", () => {
|
|
124
|
+
it("should search recursively within nested objects", () => {
|
|
125
|
+
const objects = [
|
|
126
|
+
{ name: "Alice", profile: { bio: "Software engineer" } },
|
|
127
|
+
{ name: "Bob", profile: { bio: "Designer" } },
|
|
128
|
+
];
|
|
129
|
+
const result = filterObjectByValue(objects, "software");
|
|
130
|
+
expect(result).to.have.length(1);
|
|
131
|
+
expect(result[0].name).to.equal("Alice");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("should search recursively within arrays", () => {
|
|
135
|
+
const objects = [
|
|
136
|
+
{ name: "Alice", skills: ["Python", "JavaScript"] },
|
|
137
|
+
{ name: "Bob", skills: ["Java", "C++"] },
|
|
138
|
+
];
|
|
139
|
+
const result = filterObjectByValue(objects, "python");
|
|
140
|
+
expect(result).to.have.length(1);
|
|
141
|
+
expect(result[0].name).to.equal("Alice");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should search recursively within array of objects", () => {
|
|
145
|
+
const objects = [
|
|
146
|
+
{ name: "Alice", skills: [{ name: "Python" }, { name: "JavaScript" }] },
|
|
147
|
+
{ name: "Bob", skills: [{ name: "Java" }, { name: "C++" }] },
|
|
148
|
+
];
|
|
149
|
+
const result = filterObjectByValue(objects, "python");
|
|
150
|
+
expect(result).to.have.length(1);
|
|
151
|
+
expect(result[0].name).to.equal("Alice");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("should find match at any depth in nested structure", () => {
|
|
155
|
+
const objects = [
|
|
156
|
+
{ name: "Alice", data: { level1: { level2: { level3: "Python" } } } },
|
|
157
|
+
{ name: "Bob", data: { level1: { level2: { level3: "Java" } } } },
|
|
158
|
+
];
|
|
159
|
+
const result = filterObjectByValue(objects, "python");
|
|
160
|
+
expect(result).to.have.length(1);
|
|
161
|
+
expect(result[0].name).to.equal("Alice");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should search across entire object structure", () => {
|
|
165
|
+
const objects = [
|
|
166
|
+
{ name: "Project 1", owner: { name: "Alice" }, tags: ["web", "app"] },
|
|
167
|
+
{ name: "Project 2", owner: { name: "Bob" }, tags: ["api"] },
|
|
168
|
+
];
|
|
169
|
+
const result = filterObjectByValue(objects, "alice");
|
|
170
|
+
expect(result).to.have.length(1);
|
|
171
|
+
expect(result[0].name).to.equal("Project 1");
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe("Special characters in search", () => {
|
|
176
|
+
it("should handle regex special characters in search string", () => {
|
|
177
|
+
const objects = [{ name: "C++ Developer" }, { name: "Java Developer" }];
|
|
178
|
+
const result = filterObjectByValue(objects, "c++");
|
|
179
|
+
expect(result).to.have.length(1);
|
|
180
|
+
expect(result[0].name).to.equal("C++ Developer");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("should handle dots in search string", () => {
|
|
184
|
+
const objects = [{ name: "version.1.0" }, { name: "version.2.0" }];
|
|
185
|
+
const result = filterObjectByValue(objects, "version.1");
|
|
186
|
+
expect(result).to.have.length(1);
|
|
187
|
+
expect(result[0].name).to.equal("version.1.0");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("should handle asterisks in search string", () => {
|
|
191
|
+
const objects = [{ name: "item*1" }, { name: "item*2" }];
|
|
192
|
+
const result = filterObjectByValue(objects, "item*1");
|
|
193
|
+
expect(result).to.have.length(1);
|
|
194
|
+
expect(result[0].name).to.equal("item*1");
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe("Edge cases", () => {
|
|
199
|
+
it("should handle empty array", () => {
|
|
200
|
+
const result = filterObjectByValue([], "test");
|
|
201
|
+
expect(result).to.deep.equal([]);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("should handle objects with null properties", () => {
|
|
205
|
+
const objects = [
|
|
206
|
+
{ name: null, email: "alice@test.com" },
|
|
207
|
+
{ name: "Bob", email: "bob@test.com" },
|
|
208
|
+
];
|
|
209
|
+
const result = filterObjectByValue(objects, "alice");
|
|
210
|
+
expect(result).to.have.length(1);
|
|
211
|
+
expect(result[0].email).to.equal("alice@test.com");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("should handle objects with undefined properties", () => {
|
|
215
|
+
const objects = [
|
|
216
|
+
{ name: undefined, email: "alice@test.com" },
|
|
217
|
+
{ name: "Bob", email: "bob@test.com" },
|
|
218
|
+
];
|
|
219
|
+
const result = filterObjectByValue(objects, "alice");
|
|
220
|
+
expect(result).to.have.length(1);
|
|
221
|
+
expect(result[0].email).to.equal("alice@test.com");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("should handle property values that are numbers", () => {
|
|
225
|
+
const objects = [
|
|
226
|
+
{ name: "Alice", score: 100 },
|
|
227
|
+
{ name: "Bob", score: 200 },
|
|
228
|
+
];
|
|
229
|
+
const result = filterObjectByValue(objects, "100");
|
|
230
|
+
expect(result).to.have.length(1);
|
|
231
|
+
expect(result[0].name).to.equal("Alice");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("should handle property values that are booleans", () => {
|
|
235
|
+
const objects = [
|
|
236
|
+
{ name: "Alice", active: true },
|
|
237
|
+
{ name: "Bob", active: false },
|
|
238
|
+
];
|
|
239
|
+
const result = filterObjectByValue(objects, "true");
|
|
240
|
+
expect(result).to.have.length(1);
|
|
241
|
+
expect(result[0].name).to.equal("Alice");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should handle empty objects", () => {
|
|
245
|
+
const objects = [{}, { name: "Alice" }];
|
|
246
|
+
const result = filterObjectByValue(objects, "alice");
|
|
247
|
+
expect(result).to.have.length(1);
|
|
248
|
+
expect(result[0].name).to.equal("Alice");
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("should handle objects with empty arrays", () => {
|
|
252
|
+
const objects = [
|
|
253
|
+
{ name: "Alice", tags: [] },
|
|
254
|
+
{ name: "Bob", tags: ["developer"] },
|
|
255
|
+
];
|
|
256
|
+
const result = filterObjectByValue(objects, "alice");
|
|
257
|
+
expect(result).to.have.length(1);
|
|
258
|
+
expect(result[0].name).to.equal("Alice");
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe("Mixed data types", () => {
|
|
263
|
+
it("should search through mixed types in nested structure", () => {
|
|
264
|
+
const objects = [
|
|
265
|
+
{
|
|
266
|
+
name: "Alice",
|
|
267
|
+
data: { mixed: ["text", 123, true, { nested: "match" }] },
|
|
268
|
+
},
|
|
269
|
+
{ name: "Bob", data: { mixed: ["other", 456] } },
|
|
270
|
+
];
|
|
271
|
+
const result = filterObjectByValue(objects, "match");
|
|
272
|
+
expect(result).to.have.length(1);
|
|
273
|
+
expect(result[0].name).to.equal("Alice");
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("should search through nested arrays of different types", () => {
|
|
277
|
+
const objects = [
|
|
278
|
+
{ name: "Alice", items: ["string", 100, { key: "value" }, [1, 2, 3]] },
|
|
279
|
+
{ name: "Bob", items: ["other"] },
|
|
280
|
+
];
|
|
281
|
+
const result = filterObjectByValue(objects, "value");
|
|
282
|
+
expect(result).to.have.length(1);
|
|
283
|
+
expect(result[0].name).to.equal("Alice");
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe("Multiple matches", () => {
|
|
288
|
+
it("should return all objects that match", () => {
|
|
289
|
+
const objects = [
|
|
290
|
+
{ name: "Alice", email: "alice@example.com" },
|
|
291
|
+
{ name: "Bob", email: "bob@alice.com" },
|
|
292
|
+
{ name: "Charlie", email: "charlie@test.com" },
|
|
293
|
+
];
|
|
294
|
+
const result = filterObjectByValue(objects, "alice");
|
|
295
|
+
expect(result).to.have.length(2);
|
|
296
|
+
expect(result.map((r) => r.name)).to.deep.equal(["Alice", "Bob"]);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("should score objects with matches in multiple properties", () => {
|
|
300
|
+
const objects = [
|
|
301
|
+
{ name: "Alice", bio: "Alice is a developer" },
|
|
302
|
+
{ name: "Bob", bio: "Bob works with Alice" },
|
|
303
|
+
];
|
|
304
|
+
const result = filterObjectByValue(objects, "alice");
|
|
305
|
+
expect(result[0].name).to.equal("Alice");
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
describe("Whole word matching", () => {
|
|
310
|
+
it("should prioritize whole word matches over partial", () => {
|
|
311
|
+
const objects = [
|
|
312
|
+
{ name: "Alice" },
|
|
313
|
+
{ name: "Alicia" },
|
|
314
|
+
{ name: "Alice Wonderland" },
|
|
315
|
+
];
|
|
316
|
+
const result = filterObjectByValue(objects, "alice");
|
|
317
|
+
expect(result).to.have.length(2);
|
|
318
|
+
expect(result[0].name).to.equal("Alice");
|
|
319
|
+
expect(result[1].name).to.equal("Alice Wonderland");
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("should identify whole words using word boundaries", () => {
|
|
323
|
+
const objects = [{ name: "web developer" }, { name: "website" }];
|
|
324
|
+
const result = filterObjectByValue(objects, "web");
|
|
325
|
+
expect(result[0].name).to.equal("web developer");
|
|
326
|
+
expect(result[1].name).to.equal("website");
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe("Real-world scenarios", () => {
|
|
331
|
+
it("should search across user profiles", () => {
|
|
332
|
+
const users = [
|
|
333
|
+
{
|
|
334
|
+
name: "Alice Johnson",
|
|
335
|
+
email: "alice@example.com",
|
|
336
|
+
bio: "Software engineer",
|
|
337
|
+
},
|
|
338
|
+
{ name: "Bob Smith", email: "bob@test.com", bio: "Designer" },
|
|
339
|
+
{
|
|
340
|
+
name: "Charlie Brown",
|
|
341
|
+
email: "charlie@example.com",
|
|
342
|
+
bio: "Software developer",
|
|
343
|
+
},
|
|
344
|
+
];
|
|
345
|
+
const result = filterObjectByValue(users, "software");
|
|
346
|
+
expect(result).to.have.length(2);
|
|
347
|
+
expect(result.map((u) => u.name)).to.deep.equal([
|
|
348
|
+
"Alice Johnson",
|
|
349
|
+
"Charlie Brown",
|
|
350
|
+
]);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("should search across project data", () => {
|
|
354
|
+
const projects = [
|
|
355
|
+
{
|
|
356
|
+
name: "Website",
|
|
357
|
+
description: "E-commerce platform",
|
|
358
|
+
tags: ["web", "ecommerce"],
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
name: "Mobile App",
|
|
362
|
+
description: "iOS and Android",
|
|
363
|
+
tags: ["mobile", "ios"],
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: "API",
|
|
367
|
+
description: "REST API for web platform",
|
|
368
|
+
tags: ["api", "web"],
|
|
369
|
+
},
|
|
370
|
+
];
|
|
371
|
+
const result = filterObjectByValue(projects, "web");
|
|
372
|
+
expect(result.length).to.be.greaterThan(1);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
});
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import sort from "@helpers/datas/sort";
|
|
2
|
+
|
|
3
|
+
describe("sort helper function", () => {
|
|
4
|
+
describe("getValueFromAnyKey (internal function behavior)", () => {
|
|
5
|
+
it("should get value from single-level key", () => {
|
|
6
|
+
const obj = { name: "Alice", age: 30 };
|
|
7
|
+
const result = sort([obj], "name", "asc");
|
|
8
|
+
expect(result[0].name).to.equal("Alice");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("should get value from nested key using array", () => {
|
|
12
|
+
const obj = { user: { profile: { name: "Alice" } } };
|
|
13
|
+
const result = sort([obj], ["user", "profile", "name"], "asc");
|
|
14
|
+
expect(result[0].user.profile.name).to.equal("Alice");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should return undefined for missing key in non-first elements", () => {
|
|
18
|
+
const arr = [
|
|
19
|
+
{ name: "Alice", age: 30 },
|
|
20
|
+
{ name: "Bob" },
|
|
21
|
+
{ name: "Charlie", age: 25 },
|
|
22
|
+
];
|
|
23
|
+
const result = sort(arr, "age", "asc");
|
|
24
|
+
expect(result[0].name).to.equal("Charlie");
|
|
25
|
+
expect(result[0].age).to.equal(25);
|
|
26
|
+
expect(result[1].age).to.equal(30);
|
|
27
|
+
expect(result[2].name).to.equal("Bob");
|
|
28
|
+
expect(result[2].age).to.be.undefined;
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("String sorting", () => {
|
|
33
|
+
it("should sort strings in ascending order", () => {
|
|
34
|
+
const arr = [{ name: "Charlie" }, { name: "Alice" }, { name: "Bob" }];
|
|
35
|
+
const result = sort(arr, "name", "asc");
|
|
36
|
+
expect(result[0].name).to.equal("Alice");
|
|
37
|
+
expect(result[1].name).to.equal("Bob");
|
|
38
|
+
expect(result[2].name).to.equal("Charlie");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should sort strings in descending order", () => {
|
|
42
|
+
const arr = [{ name: "Charlie" }, { name: "Alice" }, { name: "Bob" }];
|
|
43
|
+
const result = sort(arr, "name", "desc");
|
|
44
|
+
expect(result[0].name).to.equal("Charlie");
|
|
45
|
+
expect(result[1].name).to.equal("Bob");
|
|
46
|
+
expect(result[2].name).to.equal("Alice");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should sort strings with default ascending order", () => {
|
|
50
|
+
const arr = [{ name: "Charlie" }, { name: "Alice" }, { name: "Bob" }];
|
|
51
|
+
const result = sort(arr, "name");
|
|
52
|
+
expect(result[0].name).to.equal("Alice");
|
|
53
|
+
expect(result[1].name).to.equal("Bob");
|
|
54
|
+
expect(result[2].name).to.equal("Charlie");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should use localeCompare for string sorting", () => {
|
|
58
|
+
const arr = [{ name: "Émile" }, { name: "Alice" }, { name: "Zoe" }];
|
|
59
|
+
const result = sort(arr, "name", "asc");
|
|
60
|
+
expect(result[0].name).to.equal("Alice");
|
|
61
|
+
expect(result[1].name).to.equal("Émile");
|
|
62
|
+
expect(result[2].name).to.equal("Zoe");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("Number sorting", () => {
|
|
67
|
+
it("should sort numbers in ascending order", () => {
|
|
68
|
+
const arr = [{ age: 30 }, { age: 20 }, { age: 25 }];
|
|
69
|
+
const result = sort(arr, "age", "asc");
|
|
70
|
+
expect(result[0].age).to.equal(20);
|
|
71
|
+
expect(result[1].age).to.equal(25);
|
|
72
|
+
expect(result[2].age).to.equal(30);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should sort numbers in descending order", () => {
|
|
76
|
+
const arr = [{ age: 30 }, { age: 20 }, { age: 25 }];
|
|
77
|
+
const result = sort(arr, "age", "desc");
|
|
78
|
+
expect(result[0].age).to.equal(30);
|
|
79
|
+
expect(result[1].age).to.equal(25);
|
|
80
|
+
expect(result[2].age).to.equal(20);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should sort negative numbers", () => {
|
|
84
|
+
const arr = [{ value: -10 }, { value: 5 }, { value: -5 }, { value: 0 }];
|
|
85
|
+
const result = sort(arr, "value", "asc");
|
|
86
|
+
expect(result[0].value).to.equal(-10);
|
|
87
|
+
expect(result[1].value).to.equal(-5);
|
|
88
|
+
expect(result[2].value).to.equal(0);
|
|
89
|
+
expect(result[3].value).to.equal(5);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should sort decimal numbers", () => {
|
|
93
|
+
const arr = [{ price: 10.5 }, { price: 5.25 }, { price: 7.75 }];
|
|
94
|
+
const result = sort(arr, "price", "asc");
|
|
95
|
+
expect(result[0].price).to.equal(5.25);
|
|
96
|
+
expect(result[1].price).to.equal(7.75);
|
|
97
|
+
expect(result[2].price).to.equal(10.5);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("Date sorting", () => {
|
|
102
|
+
it("should sort dates in ascending order", () => {
|
|
103
|
+
const arr = [
|
|
104
|
+
{ date: new Date("2023-03-01") },
|
|
105
|
+
{ date: new Date("2023-01-01") },
|
|
106
|
+
{ date: new Date("2023-02-01") },
|
|
107
|
+
];
|
|
108
|
+
const result = sort(arr, "date", "asc");
|
|
109
|
+
expect(result[0].date.toISOString()).to.contain("2023-01");
|
|
110
|
+
expect(result[1].date.toISOString()).to.contain("2023-02");
|
|
111
|
+
expect(result[2].date.toISOString()).to.contain("2023-03");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should sort dates in descending order", () => {
|
|
115
|
+
const arr = [
|
|
116
|
+
{ date: new Date("2023-03-01") },
|
|
117
|
+
{ date: new Date("2023-01-01") },
|
|
118
|
+
{ date: new Date("2023-02-01") },
|
|
119
|
+
];
|
|
120
|
+
const result = sort(arr, "date", "desc");
|
|
121
|
+
expect(result[0].date.toISOString()).to.contain("2023-03");
|
|
122
|
+
expect(result[1].date.toISOString()).to.contain("2023-02");
|
|
123
|
+
expect(result[2].date.toISOString()).to.contain("2023-01");
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("Nested key sorting", () => {
|
|
128
|
+
it("should sort by nested key using array notation", () => {
|
|
129
|
+
const arr = [
|
|
130
|
+
{ user: { profile: { name: "Charlie" } } },
|
|
131
|
+
{ user: { profile: { name: "Alice" } } },
|
|
132
|
+
{ user: { profile: { name: "Bob" } } },
|
|
133
|
+
];
|
|
134
|
+
const result = sort(arr, ["user", "profile", "name"], "asc");
|
|
135
|
+
expect(result[0].user.profile.name).to.equal("Alice");
|
|
136
|
+
expect(result[1].user.profile.name).to.equal("Bob");
|
|
137
|
+
expect(result[2].user.profile.name).to.equal("Charlie");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should not support dot notation for nested keys - use array notation", () => {
|
|
141
|
+
const arr = [
|
|
142
|
+
{ user: { name: "Charlie" } },
|
|
143
|
+
{ user: { name: "Alice" } },
|
|
144
|
+
{ user: { name: "Bob" } },
|
|
145
|
+
];
|
|
146
|
+
expect(() => sort(arr, "user.name", "asc")).to.throw();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should sort by deeply nested numeric key", () => {
|
|
150
|
+
const arr = [
|
|
151
|
+
{ data: { stats: { score: 90 } } },
|
|
152
|
+
{ data: { stats: { score: 70 } } },
|
|
153
|
+
{ data: { stats: { score: 80 } } },
|
|
154
|
+
];
|
|
155
|
+
const result = sort(arr, ["data", "stats", "score"], "asc");
|
|
156
|
+
expect(result[0].data.stats.score).to.equal(70);
|
|
157
|
+
expect(result[1].data.stats.score).to.equal(80);
|
|
158
|
+
expect(result[2].data.stats.score).to.equal(90);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe("Null/undefined handling", () => {
|
|
163
|
+
it("should put null/undefined values at the end for ascending sort", () => {
|
|
164
|
+
const arr = [
|
|
165
|
+
{ name: "Bob" },
|
|
166
|
+
{ name: null },
|
|
167
|
+
{ name: "Alice" },
|
|
168
|
+
{ name: undefined },
|
|
169
|
+
{ name: "Charlie" },
|
|
170
|
+
];
|
|
171
|
+
const result = sort(arr, "name", "asc");
|
|
172
|
+
expect(result[0].name).to.equal("Alice");
|
|
173
|
+
expect(result[1].name).to.equal("Bob");
|
|
174
|
+
expect(result[2].name).to.equal("Charlie");
|
|
175
|
+
expect(result[3].name).to.be.null;
|
|
176
|
+
expect(result[4].name).to.be.undefined;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should put null/undefined values at the beginning for descending sort", () => {
|
|
180
|
+
const arr = [
|
|
181
|
+
{ name: "Bob" },
|
|
182
|
+
{ name: null },
|
|
183
|
+
{ name: "Alice" },
|
|
184
|
+
{ name: undefined },
|
|
185
|
+
{ name: "Charlie" },
|
|
186
|
+
];
|
|
187
|
+
const result = sort(arr, "name", "desc");
|
|
188
|
+
expect(result[0].name).to.satisfy((v: any) => v == null);
|
|
189
|
+
expect(result[1].name).to.satisfy((v: any) => v == null);
|
|
190
|
+
expect(result[2].name).to.equal("Charlie");
|
|
191
|
+
expect(result[3].name).to.equal("Bob");
|
|
192
|
+
expect(result[4].name).to.equal("Alice");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should throw TypeError for arrays with all null values", () => {
|
|
196
|
+
const arr = [{ name: null }, { name: null }, { name: null }];
|
|
197
|
+
expect(() => sort(arr, "name", "asc")).to.throw();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe("Immutability", () => {
|
|
202
|
+
it("should not mutate the original array", () => {
|
|
203
|
+
const arr = [{ name: "Charlie" }, { name: "Alice" }, { name: "Bob" }];
|
|
204
|
+
const originalOrder = arr.map((item) => item.name);
|
|
205
|
+
const result = sort(arr, "name", "asc");
|
|
206
|
+
|
|
207
|
+
expect(arr.map((item) => item.name)).to.deep.equal(originalOrder);
|
|
208
|
+
expect(result.map((item) => item.name)).to.deep.equal([
|
|
209
|
+
"Alice",
|
|
210
|
+
"Bob",
|
|
211
|
+
"Charlie",
|
|
212
|
+
]);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("should create a new array reference", () => {
|
|
216
|
+
const arr = [{ name: "Alice" }, { name: "Bob" }];
|
|
217
|
+
const result = sort(arr, "name", "asc");
|
|
218
|
+
expect(result).to.not.equal(arr);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe("Error handling", () => {
|
|
223
|
+
it("should throw TypeError for object type values", () => {
|
|
224
|
+
const arr = [
|
|
225
|
+
{ data: { foo: { bar: "baz" } } },
|
|
226
|
+
{ data: { foo: { bar: "qux" } } },
|
|
227
|
+
];
|
|
228
|
+
expect(() => sort(arr, ["data", "foo"], "asc")).to.throw();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("should throw TypeError for array type values", () => {
|
|
232
|
+
const arr = [{ items: [1, 2, 3] }, { items: [4, 5, 6] }];
|
|
233
|
+
expect(() => sort(arr, "items", "asc")).to.throw();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("should throw TypeError when first element has undefined value", () => {
|
|
237
|
+
const arr = [{ name: undefined }, { name: "Alice" }];
|
|
238
|
+
expect(() => sort(arr, "name", "asc")).to.throw();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("should throw TypeError when first element has null value", () => {
|
|
242
|
+
const arr = [{ name: null }, { name: "Alice" }];
|
|
243
|
+
expect(() => sort(arr, "name", "asc")).to.throw();
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe("Edge cases", () => {
|
|
248
|
+
it("should throw TypeError for empty array", () => {
|
|
249
|
+
const arr: any[] = [];
|
|
250
|
+
expect(() => sort(arr, "name", "asc")).to.throw();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("should handle single element array", () => {
|
|
254
|
+
const arr = [{ name: "Alice" }];
|
|
255
|
+
const result = sort(arr, "name", "asc");
|
|
256
|
+
expect(result).to.have.length(1);
|
|
257
|
+
expect(result[0].name).to.equal("Alice");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("should handle equal values", () => {
|
|
261
|
+
const arr = [{ name: "Alice" }, { name: "Alice" }, { name: "Alice" }];
|
|
262
|
+
const result = sort(arr, "name", "asc");
|
|
263
|
+
expect(result).to.have.length(3);
|
|
264
|
+
expect(result.every((item) => item.name === "Alice")).to.be.true;
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("should handle objects with missing nested keys", () => {
|
|
268
|
+
const arr = [
|
|
269
|
+
{ user: { name: "Alice" } },
|
|
270
|
+
{ user: { name: "Bob" } },
|
|
271
|
+
{ user: {} },
|
|
272
|
+
];
|
|
273
|
+
const result = sort(arr, ["user", "name"], "asc");
|
|
274
|
+
expect(result[0].user.name).to.equal("Alice");
|
|
275
|
+
expect(result[1].user.name).to.equal("Bob");
|
|
276
|
+
expect(result[2].user.name).to.be.undefined;
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
describe("Mixed data types (same array)", () => {
|
|
281
|
+
it("should sort objects with mixed string properties", () => {
|
|
282
|
+
const arr = [
|
|
283
|
+
{ name: "Charlie", age: 30 },
|
|
284
|
+
{ name: "Alice", age: 20 },
|
|
285
|
+
{ name: "Bob", age: 25 },
|
|
286
|
+
];
|
|
287
|
+
const result = sort(arr, "name", "asc");
|
|
288
|
+
expect(result[0].name).to.equal("Alice");
|
|
289
|
+
expect(result[1].name).to.equal("Bob");
|
|
290
|
+
expect(result[2].name).to.equal("Charlie");
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
});
|