@ooneex/utils 0.0.3 → 0.0.4
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/package.json +2 -2
- package/src/capitalizeWord.ts +3 -0
- package/src/dataURLtoFile.ts +12 -0
- package/src/formatRelativeNumber.ts +7 -0
- package/src/index.ts +15 -0
- package/src/millisecondsToHMS.ts +16 -0
- package/src/parseEnvVars.ts +14 -0
- package/src/parseString.ts +47 -0
- package/src/random.ts +13 -0
- package/src/secondsToHMS.ts +16 -0
- package/src/secondsToMS.ts +5 -0
- package/src/sleep.ts +3 -0
- package/src/splitToWords.ts +14 -0
- package/src/toCamelCase.ts +8 -0
- package/src/toKebabCase.ts +6 -0
- package/src/toPascalCase.ts +7 -0
- package/src/trim.ts +8 -0
- package/tests/capitalizeWord.spec.ts +163 -0
- package/tests/dataURLtoFile.spec.ts +472 -0
- package/tests/formatRelativeNumber.spec.ts +303 -0
- package/tests/millisecondsToHMS.spec.ts +209 -0
- package/tests/parseEnvVars.spec.ts +468 -0
- package/tests/parseString.spec.ts +377 -0
- package/tests/random.spec.ts +422 -0
- package/tests/secondsToHMS.spec.ts +341 -0
- package/tests/secondsToMS.spec.ts +467 -0
- package/tests/splitToWords.spec.ts +359 -0
- package/tests/toCamelCase.spec.ts +526 -0
- package/tests/toKebabCase.spec.ts +664 -0
- package/tests/toPascalCase.spec.ts +721 -0
- package/tests/trim.spec.ts +486 -0
- package/tsconfig.json +11 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{index.js → dist/index.js} +0 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { random } from "@/index";
|
|
3
|
+
|
|
4
|
+
describe("random", () => {
|
|
5
|
+
describe("nanoid", () => {
|
|
6
|
+
describe("basic functionality", () => {
|
|
7
|
+
test("should return string with default length of 10", () => {
|
|
8
|
+
const result = random.nanoid();
|
|
9
|
+
expect(typeof result).toBe("string");
|
|
10
|
+
expect(result).toHaveLength(10);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("should return string with specified length", () => {
|
|
14
|
+
const result = random.nanoid(5);
|
|
15
|
+
expect(typeof result).toBe("string");
|
|
16
|
+
expect(result).toHaveLength(5);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("should return string with custom length", () => {
|
|
20
|
+
const result = random.nanoid(20);
|
|
21
|
+
expect(typeof result).toBe("string");
|
|
22
|
+
expect(result).toHaveLength(20);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("should only contain valid characters (0-9, a-f)", () => {
|
|
26
|
+
const result = random.nanoid(100);
|
|
27
|
+
const validChars = /^[0-9a-f]+$/;
|
|
28
|
+
expect(validChars.test(result)).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("should generate different values on consecutive calls", () => {
|
|
32
|
+
const result1 = random.nanoid();
|
|
33
|
+
const result2 = random.nanoid();
|
|
34
|
+
expect(result1).not.toBe(result2);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("edge cases", () => {
|
|
39
|
+
test("should handle zero length", () => {
|
|
40
|
+
const result = random.nanoid(0);
|
|
41
|
+
expect(typeof result).toBe("string");
|
|
42
|
+
expect(result).toHaveLength(0);
|
|
43
|
+
expect(result).toBe("");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("should handle very large lengths", () => {
|
|
47
|
+
const result = random.nanoid(1000);
|
|
48
|
+
expect(typeof result).toBe("string");
|
|
49
|
+
expect(result).toHaveLength(1000);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("should handle undefined size parameter", () => {
|
|
53
|
+
const result = random.nanoid(undefined);
|
|
54
|
+
expect(typeof result).toBe("string");
|
|
55
|
+
expect(result).toHaveLength(10);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("randomness quality", () => {
|
|
60
|
+
test("should generate sufficiently random strings", () => {
|
|
61
|
+
const results = new Set();
|
|
62
|
+
for (let i = 0; i < 100; i++) {
|
|
63
|
+
results.add(random.nanoid());
|
|
64
|
+
}
|
|
65
|
+
// All 100 generated strings should be unique
|
|
66
|
+
expect(results.size).toBe(100);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("should have good character distribution", () => {
|
|
70
|
+
const result = random.nanoid(1000);
|
|
71
|
+
const charCounts = new Map<string, number>();
|
|
72
|
+
|
|
73
|
+
for (const char of result) {
|
|
74
|
+
charCounts.set(char, (charCounts.get(char) || 0) + 1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Should have multiple different characters
|
|
78
|
+
expect(charCounts.size).toBeGreaterThan(10);
|
|
79
|
+
|
|
80
|
+
// No single character should dominate too much (allow some variance)
|
|
81
|
+
for (const count of charCounts.values()) {
|
|
82
|
+
expect(count).toBeLessThan(200); // Less than 20% of total
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("parametrized tests", () => {
|
|
88
|
+
test.each([[1], [5], [10], [16], [32], [64], [128]])("should generate string of length %i", (length) => {
|
|
89
|
+
const result = random.nanoid(length);
|
|
90
|
+
expect(result).toHaveLength(length);
|
|
91
|
+
expect(/^[0-9a-f]*$/.test(result)).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("stringInt", () => {
|
|
97
|
+
describe("basic functionality", () => {
|
|
98
|
+
test("should return string with default length of 10", () => {
|
|
99
|
+
const result = random.stringInt();
|
|
100
|
+
expect(typeof result).toBe("string");
|
|
101
|
+
expect(result).toHaveLength(10);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("should return string with specified length", () => {
|
|
105
|
+
const result = random.stringInt(5);
|
|
106
|
+
expect(typeof result).toBe("string");
|
|
107
|
+
expect(result).toHaveLength(5);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("should return string with custom length", () => {
|
|
111
|
+
const result = random.stringInt(20);
|
|
112
|
+
expect(typeof result).toBe("string");
|
|
113
|
+
expect(result).toHaveLength(20);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("should only contain digits (0-9)", () => {
|
|
117
|
+
const result = random.stringInt(100);
|
|
118
|
+
const validChars = /^[0-9]+$/;
|
|
119
|
+
expect(validChars.test(result)).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("should generate different values on consecutive calls", () => {
|
|
123
|
+
const result1 = random.stringInt();
|
|
124
|
+
const result2 = random.stringInt();
|
|
125
|
+
expect(result1).not.toBe(result2);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe("edge cases", () => {
|
|
130
|
+
test("should handle zero length", () => {
|
|
131
|
+
const result = random.stringInt(0);
|
|
132
|
+
expect(typeof result).toBe("string");
|
|
133
|
+
expect(result).toHaveLength(0);
|
|
134
|
+
expect(result).toBe("");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("should handle very large lengths", () => {
|
|
138
|
+
const result = random.stringInt(1000);
|
|
139
|
+
expect(typeof result).toBe("string");
|
|
140
|
+
expect(result).toHaveLength(1000);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("should handle undefined size parameter", () => {
|
|
144
|
+
const result = random.stringInt(undefined);
|
|
145
|
+
expect(typeof result).toBe("string");
|
|
146
|
+
expect(result).toHaveLength(10);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("numeric properties", () => {
|
|
151
|
+
test("should be parseable as integer when not too large", () => {
|
|
152
|
+
const result = random.stringInt(15); // Safe integer range
|
|
153
|
+
expect(Number.isNaN(Number(result))).toBe(false);
|
|
154
|
+
expect(Number.isInteger(Number(result))).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("should not have leading zeros unless it's all zeros", () => {
|
|
158
|
+
const results = [];
|
|
159
|
+
for (let i = 0; i < 100; i++) {
|
|
160
|
+
results.push(random.stringInt(10));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const withLeadingZero = results.filter((r) => r.length > 1 && r[0] === "0");
|
|
164
|
+
// Some may have leading zeros, but not all
|
|
165
|
+
expect(withLeadingZero.length).toBeLessThan(results.length);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe("randomness quality", () => {
|
|
170
|
+
test("should generate sufficiently random strings", () => {
|
|
171
|
+
const results = new Set();
|
|
172
|
+
for (let i = 0; i < 100; i++) {
|
|
173
|
+
results.add(random.stringInt());
|
|
174
|
+
}
|
|
175
|
+
// All 100 generated strings should be unique
|
|
176
|
+
expect(results.size).toBe(100);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("should have good digit distribution", () => {
|
|
180
|
+
const result = random.stringInt(1000);
|
|
181
|
+
const digitCounts = new Map<string, number>();
|
|
182
|
+
|
|
183
|
+
for (const digit of result) {
|
|
184
|
+
digitCounts.set(digit, (digitCounts.get(digit) || 0) + 1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Should have multiple different digits
|
|
188
|
+
expect(digitCounts.size).toBeGreaterThan(5);
|
|
189
|
+
|
|
190
|
+
// No single digit should dominate too much (allow some variance)
|
|
191
|
+
for (const count of digitCounts.values()) {
|
|
192
|
+
expect(count).toBeLessThan(200); // Less than 20% of total
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("parametrized tests", () => {
|
|
198
|
+
test.each([[1], [5], [10], [15], [20], [50], [100]])("should generate numeric string of length %i", (length) => {
|
|
199
|
+
const result = random.stringInt(length);
|
|
200
|
+
expect(result).toHaveLength(length);
|
|
201
|
+
expect(/^[0-9]*$/.test(result)).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe("nanoidFactory", () => {
|
|
207
|
+
describe("basic functionality", () => {
|
|
208
|
+
test("should return a function", () => {
|
|
209
|
+
const factory = random.nanoidFactory();
|
|
210
|
+
expect(typeof factory).toBe("function");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("should return function that generates strings with default length", () => {
|
|
214
|
+
const factory = random.nanoidFactory();
|
|
215
|
+
const result = factory();
|
|
216
|
+
expect(typeof result).toBe("string");
|
|
217
|
+
expect(result).toHaveLength(10);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("should return function that generates strings with factory-specified length", () => {
|
|
221
|
+
const factory = random.nanoidFactory(15);
|
|
222
|
+
const result = factory();
|
|
223
|
+
expect(typeof result).toBe("string");
|
|
224
|
+
expect(result).toHaveLength(15);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("should return function that can override factory length", () => {
|
|
228
|
+
const factory = random.nanoidFactory(10);
|
|
229
|
+
const result = factory(5);
|
|
230
|
+
expect(typeof result).toBe("string");
|
|
231
|
+
expect(result).toHaveLength(5);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("should generate strings with valid characters", () => {
|
|
235
|
+
const factory = random.nanoidFactory();
|
|
236
|
+
const result = factory(50);
|
|
237
|
+
const validChars = /^[0-9a-f]+$/;
|
|
238
|
+
expect(validChars.test(result)).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("factory behavior", () => {
|
|
243
|
+
test("should create reusable generator", () => {
|
|
244
|
+
const factory = random.nanoidFactory(8);
|
|
245
|
+
const result1 = factory();
|
|
246
|
+
const result2 = factory();
|
|
247
|
+
|
|
248
|
+
expect(result1).toHaveLength(8);
|
|
249
|
+
expect(result2).toHaveLength(8);
|
|
250
|
+
expect(result1).not.toBe(result2);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("should maintain factory size as default", () => {
|
|
254
|
+
const factory = random.nanoidFactory(12);
|
|
255
|
+
|
|
256
|
+
const result1 = factory();
|
|
257
|
+
const result2 = factory(undefined);
|
|
258
|
+
|
|
259
|
+
expect(result1).toHaveLength(12);
|
|
260
|
+
expect(result2).toHaveLength(12);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("should allow per-call size override", () => {
|
|
264
|
+
const factory = random.nanoidFactory(10);
|
|
265
|
+
|
|
266
|
+
const defaultSize = factory();
|
|
267
|
+
const customSize = factory(20);
|
|
268
|
+
const anotherDefault = factory();
|
|
269
|
+
|
|
270
|
+
expect(defaultSize).toHaveLength(10);
|
|
271
|
+
expect(customSize).toHaveLength(20);
|
|
272
|
+
expect(anotherDefault).toHaveLength(10);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe("edge cases", () => {
|
|
277
|
+
test("should handle zero factory size", () => {
|
|
278
|
+
const factory = random.nanoidFactory(0);
|
|
279
|
+
const result = factory();
|
|
280
|
+
expect(result).toHaveLength(0);
|
|
281
|
+
expect(result).toBe("");
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("should handle undefined factory size", () => {
|
|
285
|
+
const factory = random.nanoidFactory(undefined);
|
|
286
|
+
const result = factory();
|
|
287
|
+
expect(result).toHaveLength(10);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("should handle zero override size", () => {
|
|
291
|
+
const factory = random.nanoidFactory(10);
|
|
292
|
+
const result = factory(0);
|
|
293
|
+
expect(result).toHaveLength(0);
|
|
294
|
+
expect(result).toBe("");
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
describe("performance and reusability", () => {
|
|
299
|
+
test("should be efficient for multiple calls", () => {
|
|
300
|
+
const factory = random.nanoidFactory(10);
|
|
301
|
+
const results = [];
|
|
302
|
+
|
|
303
|
+
for (let i = 0; i < 100; i++) {
|
|
304
|
+
results.push(factory());
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// All should be strings of correct length
|
|
308
|
+
expect(results.every((r) => typeof r === "string" && r.length === 10)).toBe(true);
|
|
309
|
+
|
|
310
|
+
// All should be unique
|
|
311
|
+
const uniqueResults = new Set(results);
|
|
312
|
+
expect(uniqueResults.size).toBe(100);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("should maintain separate state for different factories", () => {
|
|
316
|
+
const factory1 = random.nanoidFactory(5);
|
|
317
|
+
const factory2 = random.nanoidFactory(15);
|
|
318
|
+
|
|
319
|
+
const result1 = factory1();
|
|
320
|
+
const result2 = factory2();
|
|
321
|
+
|
|
322
|
+
expect(result1).toHaveLength(5);
|
|
323
|
+
expect(result2).toHaveLength(15);
|
|
324
|
+
expect(result1).not.toBe(result2);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
describe("parametrized tests", () => {
|
|
329
|
+
test.each([[1], [5], [10], [16], [32], [64]])("should create factory with size %i", (size) => {
|
|
330
|
+
const factory = random.nanoidFactory(size);
|
|
331
|
+
const result = factory();
|
|
332
|
+
expect(result).toHaveLength(size);
|
|
333
|
+
expect(/^[0-9a-f]*$/.test(result)).toBe(true);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe("integration tests", () => {
|
|
339
|
+
test("all methods should produce different results", () => {
|
|
340
|
+
const nanoidResult = random.nanoid(10);
|
|
341
|
+
const stringIntResult = random.stringInt(10);
|
|
342
|
+
const factoryResult = random.nanoidFactory(10)();
|
|
343
|
+
|
|
344
|
+
// All should be strings of same length
|
|
345
|
+
expect(nanoidResult).toHaveLength(10);
|
|
346
|
+
expect(stringIntResult).toHaveLength(10);
|
|
347
|
+
expect(factoryResult).toHaveLength(10);
|
|
348
|
+
|
|
349
|
+
// stringInt should only contain digits
|
|
350
|
+
expect(/^[0-9]+$/.test(stringIntResult)).toBe(true);
|
|
351
|
+
|
|
352
|
+
// nanoid and factory should contain hex characters
|
|
353
|
+
expect(/^[0-9a-f]+$/.test(nanoidResult)).toBe(true);
|
|
354
|
+
expect(/^[0-9a-f]+$/.test(factoryResult)).toBe(true);
|
|
355
|
+
|
|
356
|
+
// All should be different (very high probability)
|
|
357
|
+
expect(nanoidResult).not.toBe(stringIntResult);
|
|
358
|
+
expect(nanoidResult).not.toBe(factoryResult);
|
|
359
|
+
expect(stringIntResult).not.toBe(factoryResult);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("should handle concurrent usage", () => {
|
|
363
|
+
const results = [];
|
|
364
|
+
const factory = random.nanoidFactory(8);
|
|
365
|
+
|
|
366
|
+
// Simulate concurrent calls
|
|
367
|
+
for (let i = 0; i < 50; i++) {
|
|
368
|
+
results.push(random.nanoid(8));
|
|
369
|
+
results.push(random.stringInt(8));
|
|
370
|
+
results.push(factory());
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// All should be unique
|
|
374
|
+
const uniqueResults = new Set(results);
|
|
375
|
+
expect(uniqueResults.size).toBe(150);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
describe("real world scenarios", () => {
|
|
380
|
+
test("should generate suitable IDs for database records", () => {
|
|
381
|
+
const id = random.nanoid(12);
|
|
382
|
+
expect(id).toHaveLength(12);
|
|
383
|
+
expect(/^[0-9a-f]+$/.test(id)).toBe(true);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("should generate suitable numeric codes", () => {
|
|
387
|
+
const code = random.stringInt(6);
|
|
388
|
+
expect(code).toHaveLength(6);
|
|
389
|
+
expect(/^[0-9]+$/.test(code)).toBe(true);
|
|
390
|
+
expect(Number.isInteger(Number(code))).toBe(true);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test("should create factory for session tokens", () => {
|
|
394
|
+
const createSessionToken = random.nanoidFactory(32);
|
|
395
|
+
|
|
396
|
+
const token1 = createSessionToken();
|
|
397
|
+
const token2 = createSessionToken();
|
|
398
|
+
|
|
399
|
+
expect(token1).toHaveLength(32);
|
|
400
|
+
expect(token2).toHaveLength(32);
|
|
401
|
+
expect(token1).not.toBe(token2);
|
|
402
|
+
expect(/^[0-9a-f]+$/.test(token1)).toBe(true);
|
|
403
|
+
expect(/^[0-9a-f]+$/.test(token2)).toBe(true);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test("should handle batch ID generation", () => {
|
|
407
|
+
const batchSize = 1000;
|
|
408
|
+
const ids = [];
|
|
409
|
+
|
|
410
|
+
for (let i = 0; i < batchSize; i++) {
|
|
411
|
+
ids.push(random.nanoid(16));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// All unique
|
|
415
|
+
const uniqueIds = new Set(ids);
|
|
416
|
+
expect(uniqueIds.size).toBe(batchSize);
|
|
417
|
+
|
|
418
|
+
// All valid format
|
|
419
|
+
expect(ids.every((id) => /^[0-9a-f]{16}$/.test(id))).toBe(true);
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
});
|