@cloudcome/utils-core 1.1.0 → 1.1.1
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/CHANGELOG.md +52 -0
- package/LICENSE +21 -0
- package/package.json +279 -1
- package/src/array.ts +312 -0
- package/src/async.ts +379 -0
- package/src/base64.ts +20 -0
- package/src/cache.ts +146 -0
- package/{dist/color/contrast.d.ts → src/color/contrast.ts} +12 -2
- package/src/color/distance.ts +28 -0
- package/src/color/helpers.ts +23 -0
- package/src/color/hex-hsl.ts +11 -0
- package/{dist/color/hex-hsv.d.ts → src/color/hex-hsv.ts} +11 -3
- package/{dist/color/hex-hwb.d.ts → src/color/hex-hwb.ts} +11 -3
- package/src/color/hex-rgb.ts +39 -0
- package/{dist/color/hsl-lighten.d.ts → src/color/hsl-lighten.ts} +7 -2
- package/{dist/color/hsv-brighten.d.ts → src/color/hsv-brighten.ts} +7 -2
- package/{dist/color/luminance.d.ts → src/color/luminance.ts} +9 -2
- package/{dist/color/mix.d.ts → src/color/mix.ts} +10 -2
- package/src/color/rgb-hsl.ts +53 -0
- package/{dist/color/rgb-hsv.d.ts → src/color/rgb-hsv.ts} +30 -3
- package/src/color/rgb-hwb.ts +56 -0
- package/{dist/color/rgb-lab.d.ts → src/color/rgb-lab.ts} +11 -3
- package/src/color/rgb-whiter.ts +22 -0
- package/src/color/rgb-xyz.ts +62 -0
- package/{dist/color/types.d.ts → src/color/types.ts} +12 -30
- package/{dist/color/xyz-lab.d.ts → src/color/xyz-lab.ts} +32 -3
- package/src/crypto/md5.mjs +357 -0
- package/src/crypto/sha1.mjs +300 -0
- package/src/crypto/sha256.mjs +310 -0
- package/src/crypto/sha512.mjs +459 -0
- package/{dist/crypto.d.ts → src/crypto.ts} +20 -4
- package/src/date/const.ts +6 -0
- package/src/date/core.ts +162 -0
- package/src/date/days.ts +51 -0
- package/{dist/date/is.d.ts → src/date/is.ts} +102 -8
- package/src/date/relative.ts +92 -0
- package/src/date/start-end.ts +246 -0
- package/src/date/timezone.ts +220 -0
- package/src/date/weeks.ts +100 -0
- package/src/dts/global.d.ts +27 -0
- package/src/easing.ts +166 -0
- package/src/emitter.ts +117 -0
- package/src/enum.ts +171 -0
- package/{dist/env.d.ts → src/env.ts} +30 -6
- package/{dist/error.d.ts → src/error.ts} +12 -3
- package/src/exception.ts +68 -0
- package/src/fn.ts +197 -0
- package/src/index.ts +1 -0
- package/src/number.ts +236 -0
- package/{dist/object/each.d.ts → src/object/each.ts} +23 -3
- package/src/object/get-set.ts +273 -0
- package/src/object/is.ts +128 -0
- package/src/object/merge.ts +180 -0
- package/{dist/object/process.d.ts → src/object/process.ts} +38 -4
- package/src/path.ts +188 -0
- package/{dist/promise.d.ts → src/promise.ts} +67 -6
- package/{dist/qs.d.ts → src/qs.ts} +60 -3
- package/src/regexp.ts +156 -0
- package/src/string.ts +146 -0
- package/src/time/from.ts +57 -0
- package/src/time/to.ts +106 -0
- package/{dist/timer.mjs → src/timer.ts} +124 -17
- package/{dist/tree.d.ts → src/tree.ts} +225 -41
- package/{dist/type.d.ts → src/type.ts} +96 -20
- package/{dist/types.d.ts → src/types.ts} +33 -12
- package/src/unique.ts +77 -0
- package/src/url.ts +93 -0
- package/src/version.ts +71 -0
- package/test/array.test.ts +332 -0
- package/test/async-real.test.ts +39 -0
- package/test/async.test.ts +375 -0
- package/test/base64.test.ts +32 -0
- package/test/cache.test.ts +83 -0
- package/test/color.test.ts +163 -0
- package/test/crypto.test.ts +34 -0
- package/test/date-tz.test.ts +206 -0
- package/test/date.test.ts +353 -0
- package/test/easing.test.ts +33 -0
- package/test/emitter.test.ts +71 -0
- package/test/enum.test.ts +113 -0
- package/test/env.test.ts +69 -0
- package/test/error.test.ts +58 -0
- package/test/exception.test.ts +43 -0
- package/test/fn.test.ts +263 -0
- package/test/helpers.ts +23 -0
- package/test/index.test.ts +6 -0
- package/test/number.test.ts +213 -0
- package/test/object.test.ts +309 -0
- package/test/path.test.ts +156 -0
- package/test/promise.test.ts +199 -0
- package/test/qs.test.ts +79 -0
- package/test/regexp.test.ts +97 -0
- package/test/string.test.ts +150 -0
- package/test/time.test.ts +214 -0
- package/test/timer.test.ts +114 -0
- package/test/tree.test.ts +348 -0
- package/test/type.test.ts +226 -0
- package/test/unique.test.ts +71 -0
- package/test/url.test.ts +136 -0
- package/test/version.test.ts +52 -0
- package/tsconfig.json +31 -0
- package/vite.config.mts +114 -0
- package/dist/array.cjs +0 -129
- package/dist/array.cjs.map +0 -1
- package/dist/array.d.ts +0 -171
- package/dist/array.mjs +0 -129
- package/dist/array.mjs.map +0 -1
- package/dist/async.cjs +0 -219
- package/dist/async.cjs.map +0 -1
- package/dist/async.d.ts +0 -137
- package/dist/async.mjs +0 -219
- package/dist/async.mjs.map +0 -1
- package/dist/base64.cjs +0 -16
- package/dist/base64.cjs.map +0 -1
- package/dist/base64.d.ts +0 -7
- package/dist/base64.mjs +0 -16
- package/dist/base64.mjs.map +0 -1
- package/dist/cache.cjs +0 -79
- package/dist/cache.cjs.map +0 -1
- package/dist/cache.d.ts +0 -90
- package/dist/cache.mjs +0 -79
- package/dist/cache.mjs.map +0 -1
- package/dist/color/distance.d.ts +0 -8
- package/dist/color/helpers.d.ts +0 -2
- package/dist/color/hex-hsl.d.ts +0 -3
- package/dist/color/hex-rgb.d.ts +0 -18
- package/dist/color/rgb-hsl.d.ts +0 -23
- package/dist/color/rgb-hwb.d.ts +0 -29
- package/dist/color/rgb-whiter.d.ts +0 -12
- package/dist/color/rgb-xyz.d.ts +0 -22
- package/dist/color.cjs +0 -250
- package/dist/color.cjs.map +0 -1
- package/dist/color.mjs +0 -250
- package/dist/color.mjs.map +0 -1
- package/dist/const.cjs +0 -14
- package/dist/const.cjs.map +0 -1
- package/dist/const.mjs +0 -15
- package/dist/const.mjs.map +0 -1
- package/dist/core.cjs +0 -250
- package/dist/core.cjs.map +0 -1
- package/dist/core.mjs +0 -251
- package/dist/core.mjs.map +0 -1
- package/dist/crypto/md5.d.mts +0 -1
- package/dist/crypto/sha1.d.mts +0 -1
- package/dist/crypto/sha256.d.mts +0 -1
- package/dist/crypto/sha512.d.mts +0 -1
- package/dist/crypto.cjs +0 -812
- package/dist/crypto.cjs.map +0 -1
- package/dist/crypto.mjs +0 -812
- package/dist/crypto.mjs.map +0 -1
- package/dist/date/const.d.ts +0 -6
- package/dist/date/core.d.ts +0 -52
- package/dist/date/days.d.ts +0 -23
- package/dist/date/relative.d.ts +0 -44
- package/dist/date/start-end.d.ts +0 -73
- package/dist/date/timezone.d.ts +0 -67
- package/dist/date/weeks.d.ts +0 -72
- package/dist/date.cjs +0 -239
- package/dist/date.cjs.map +0 -1
- package/dist/date.mjs +0 -241
- package/dist/date.mjs.map +0 -1
- package/dist/dict.cjs +0 -2
- package/dist/dict.cjs.map +0 -1
- package/dist/dict.mjs +0 -2
- package/dist/dict.mjs.map +0 -1
- package/dist/each.cjs +0 -18
- package/dist/each.cjs.map +0 -1
- package/dist/each.mjs +0 -19
- package/dist/each.mjs.map +0 -1
- package/dist/easing.cjs +0 -151
- package/dist/easing.cjs.map +0 -1
- package/dist/easing.d.ts +0 -46
- package/dist/easing.mjs +0 -151
- package/dist/easing.mjs.map +0 -1
- package/dist/emitter.cjs +0 -94
- package/dist/emitter.cjs.map +0 -1
- package/dist/emitter.d.ts +0 -68
- package/dist/emitter.mjs +0 -94
- package/dist/emitter.mjs.map +0 -1
- package/dist/enum.cjs +0 -58
- package/dist/enum.cjs.map +0 -1
- package/dist/enum.d.ts +0 -68
- package/dist/enum.mjs +0 -58
- package/dist/enum.mjs.map +0 -1
- package/dist/env.cjs +0 -28
- package/dist/env.cjs.map +0 -1
- package/dist/env.mjs +0 -28
- package/dist/env.mjs.map +0 -1
- package/dist/error.cjs +0 -12
- package/dist/error.cjs.map +0 -1
- package/dist/error.mjs +0 -12
- package/dist/error.mjs.map +0 -1
- package/dist/exception.cjs +0 -22
- package/dist/exception.cjs.map +0 -1
- package/dist/exception.d.ts +0 -31
- package/dist/exception.mjs +0 -22
- package/dist/exception.mjs.map +0 -1
- package/dist/fn.cjs +0 -76
- package/dist/fn.cjs.map +0 -1
- package/dist/fn.d.ts +0 -102
- package/dist/fn.mjs +0 -76
- package/dist/fn.mjs.map +0 -1
- package/dist/index.cjs +0 -5
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.mjs +0 -5
- package/dist/index.mjs.map +0 -1
- package/dist/merge.cjs +0 -87
- package/dist/merge.cjs.map +0 -1
- package/dist/merge.mjs +0 -88
- package/dist/merge.mjs.map +0 -1
- package/dist/number.cjs +0 -11
- package/dist/number.cjs.map +0 -1
- package/dist/number.d.ts +0 -137
- package/dist/number.mjs +0 -11
- package/dist/number.mjs.map +0 -1
- package/dist/object/get-set.d.ts +0 -111
- package/dist/object/is.d.ts +0 -32
- package/dist/object/merge.d.ts +0 -72
- package/dist/object.cjs +0 -130
- package/dist/object.cjs.map +0 -1
- package/dist/object.mjs +0 -130
- package/dist/object.mjs.map +0 -1
- package/dist/path.cjs +0 -77
- package/dist/path.cjs.map +0 -1
- package/dist/path.d.ts +0 -82
- package/dist/path.mjs +0 -77
- package/dist/path.mjs.map +0 -1
- package/dist/promise.cjs +0 -62
- package/dist/promise.cjs.map +0 -1
- package/dist/promise.mjs +0 -62
- package/dist/promise.mjs.map +0 -1
- package/dist/qs.cjs +0 -47
- package/dist/qs.cjs.map +0 -1
- package/dist/qs.mjs +0 -47
- package/dist/qs.mjs.map +0 -1
- package/dist/regexp.cjs +0 -66
- package/dist/regexp.cjs.map +0 -1
- package/dist/regexp.d.ts +0 -65
- package/dist/regexp.mjs +0 -66
- package/dist/regexp.mjs.map +0 -1
- package/dist/string.cjs +0 -16
- package/dist/string.cjs.map +0 -1
- package/dist/string.d.ts +0 -80
- package/dist/string.mjs +0 -16
- package/dist/string.mjs.map +0 -1
- package/dist/string2.cjs +0 -147
- package/dist/string2.cjs.map +0 -1
- package/dist/string2.mjs +0 -148
- package/dist/string2.mjs.map +0 -1
- package/dist/time/from.d.ts +0 -14
- package/dist/time/to.d.ts +0 -38
- package/dist/time.cjs +0 -82
- package/dist/time.cjs.map +0 -1
- package/dist/time.mjs +0 -82
- package/dist/time.mjs.map +0 -1
- package/dist/timer.cjs +0 -119
- package/dist/timer.cjs.map +0 -1
- package/dist/timer.d.ts +0 -96
- package/dist/timer.mjs.map +0 -1
- package/dist/tree.cjs +0 -125
- package/dist/tree.cjs.map +0 -1
- package/dist/tree.mjs +0 -125
- package/dist/tree.mjs.map +0 -1
- package/dist/type.cjs +0 -78
- package/dist/type.cjs.map +0 -1
- package/dist/type.mjs +0 -78
- package/dist/type.mjs.map +0 -1
- package/dist/types.cjs +0 -2
- package/dist/types.cjs.map +0 -1
- package/dist/types.mjs +0 -2
- package/dist/types.mjs.map +0 -1
- package/dist/unique.cjs +0 -46
- package/dist/unique.cjs.map +0 -1
- package/dist/unique.d.ts +0 -22
- package/dist/unique.mjs +0 -46
- package/dist/unique.mjs.map +0 -1
- package/dist/url.cjs +0 -37
- package/dist/url.cjs.map +0 -1
- package/dist/url.d.ts +0 -53
- package/dist/url.mjs +0 -37
- package/dist/url.mjs.map +0 -1
- package/dist/version.cjs +0 -33
- package/dist/version.cjs.map +0 -1
- package/dist/version.d.ts +0 -32
- package/dist/version.mjs +0 -33
- package/dist/version.mjs.map +0 -1
- /package/{dist/color.d.ts → src/color.ts} +0 -0
- /package/{dist/date.d.ts → src/date.ts} +0 -0
- /package/{dist/dict.d.ts → src/dict.ts} +0 -0
- /package/{dist/object.d.ts → src/object.ts} +0 -0
- /package/{dist/time.d.ts → src/time.ts} +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMinDelayPromise,
|
|
3
|
+
isPromiseLike,
|
|
4
|
+
promiseDelay,
|
|
5
|
+
promiseShared,
|
|
6
|
+
promiseTimeout,
|
|
7
|
+
promiseWhen,
|
|
8
|
+
} from '@/promise';
|
|
9
|
+
import { describe, expect, it } from 'vitest';
|
|
10
|
+
|
|
11
|
+
describe('promiseDelay', () => {
|
|
12
|
+
it('应在指定时间后解决 Promise', async () => {
|
|
13
|
+
const startTime = Date.now();
|
|
14
|
+
await promiseDelay(100);
|
|
15
|
+
const endTime = Date.now();
|
|
16
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(0);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('如果 ms 为 0,应立即解决 Promise', async () => {
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
await promiseDelay(0);
|
|
22
|
+
const endTime = Date.now();
|
|
23
|
+
expect(endTime - startTime).toBeLessThan(10);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('应在调用 abort 后解决 Promise', async () => {
|
|
27
|
+
const ctrl = new AbortController();
|
|
28
|
+
const startTime = Date.now();
|
|
29
|
+
const promise = promiseDelay(1000, ctrl);
|
|
30
|
+
ctrl.abort();
|
|
31
|
+
await promise;
|
|
32
|
+
const endTime = Date.now();
|
|
33
|
+
expect(endTime - startTime).toBeLessThan(1010);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('promiseTimeout', () => {
|
|
38
|
+
it('如果 Promise 在指定时间内解决,应返回其结果', async () => {
|
|
39
|
+
const result = await promiseTimeout(Promise.resolve('success'), 100);
|
|
40
|
+
expect(result).toBe('success');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('如果 Promise 在指定时间内未解决,应抛出 "timeout" 错误', async () => {
|
|
44
|
+
await expect(promiseTimeout(promiseDelay(100), 0)).rejects.toThrow('timeout');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('如果 Promise 在指定时间内恰好解决,应返回其结果', async () => {
|
|
48
|
+
const result = await promiseTimeout(Promise.resolve('success'), 10);
|
|
49
|
+
expect(result).toBe('success');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('promiseWhen', () => {
|
|
54
|
+
it('如果条件初始为真,应立即解决 Promise', async () => {
|
|
55
|
+
const startTime = Date.now();
|
|
56
|
+
await promiseWhen(() => true, 100);
|
|
57
|
+
const endTime = Date.now();
|
|
58
|
+
expect(endTime - startTime).toBeLessThan(10);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('应在条件变为真后解决 Promise', async () => {
|
|
62
|
+
let conditionMet = false;
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
conditionMet = true;
|
|
65
|
+
}, 100);
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
await promiseWhen(() => conditionMet, 10);
|
|
68
|
+
const endTime = Date.now();
|
|
69
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(100);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('应使用较小的时间间隔检查条件', async () => {
|
|
73
|
+
let conditionMet = false;
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
conditionMet = true;
|
|
76
|
+
}, 50);
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
await promiseWhen(() => conditionMet, 10);
|
|
79
|
+
const endTime = Date.now();
|
|
80
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(50);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('isPromiseLike', () => {
|
|
85
|
+
it('应正确判断 Promise 类型', async () => {
|
|
86
|
+
try {
|
|
87
|
+
const p1 = Promise.resolve();
|
|
88
|
+
expect(isPromiseLike(p1)).toBe(true);
|
|
89
|
+
await p1;
|
|
90
|
+
|
|
91
|
+
const p2 = Promise.reject();
|
|
92
|
+
expect(isPromiseLike(p2)).toBe(true);
|
|
93
|
+
// 这里需要执行掉,否会打印未捕获的 promise 错误
|
|
94
|
+
await p2;
|
|
95
|
+
|
|
96
|
+
const p3 = new Promise<void>((r) => r());
|
|
97
|
+
expect(isPromiseLike(p3)).toBe(true);
|
|
98
|
+
await p3;
|
|
99
|
+
} catch (cause) {
|
|
100
|
+
//
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('应正确判断 Promise 类似对象', () => {
|
|
105
|
+
// biome-ignore lint/suspicious/noThenProperty: <explanation>
|
|
106
|
+
expect(isPromiseLike({ then: () => {} })).toBe(true);
|
|
107
|
+
// biome-ignore lint/suspicious/noThenProperty: <explanation>
|
|
108
|
+
expect(isPromiseLike({ then: 'not a function' })).toBe(false);
|
|
109
|
+
expect(isPromiseLike({})).toBe(false);
|
|
110
|
+
expect(isPromiseLike(null)).toBe(false);
|
|
111
|
+
expect(isPromiseLike(undefined)).toBe(false);
|
|
112
|
+
expect(isPromiseLike('string')).toBe(false);
|
|
113
|
+
expect(isPromiseLike(42)).toBe(false);
|
|
114
|
+
expect(isPromiseLike(true)).toBe(false);
|
|
115
|
+
expect(isPromiseLike(Symbol('sym'))).toBe(false);
|
|
116
|
+
expect(isPromiseLike(BigInt(123))).toBe(false);
|
|
117
|
+
expect(isPromiseLike(Number.NaN)).toBe(false);
|
|
118
|
+
expect(isPromiseLike(new Error('error'))).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('sharedPromise', () => {
|
|
123
|
+
it('应共享原始 Promise 的成功状态', async () => {
|
|
124
|
+
const value = Math.random();
|
|
125
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
126
|
+
const { promise: status, resolve: done } = Promise.withResolvers<void>();
|
|
127
|
+
const shared1 = promiseShared(promise);
|
|
128
|
+
|
|
129
|
+
setTimeout(async () => {
|
|
130
|
+
// 在完成之前共享
|
|
131
|
+
resolve(value);
|
|
132
|
+
|
|
133
|
+
// 在完成之后共享
|
|
134
|
+
await expect(promiseShared(promise)).resolves.toBe(value);
|
|
135
|
+
done();
|
|
136
|
+
}, 10);
|
|
137
|
+
|
|
138
|
+
await expect(shared1).resolves.toBe(value);
|
|
139
|
+
await expect(promise).resolves.toBe(value);
|
|
140
|
+
|
|
141
|
+
await expect(status).resolves.toBe(undefined);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('应共享原始 Promise 的拒绝状态', async () => {
|
|
145
|
+
const value = Math.random();
|
|
146
|
+
const { promise, reject } = Promise.withResolvers();
|
|
147
|
+
const { promise: status, resolve: done } = Promise.withResolvers<void>();
|
|
148
|
+
const shared1 = promiseShared(promise);
|
|
149
|
+
|
|
150
|
+
setTimeout(async () => {
|
|
151
|
+
// 在完成之前共享
|
|
152
|
+
reject(value);
|
|
153
|
+
|
|
154
|
+
// 在完成之后共享
|
|
155
|
+
await expect(promiseShared(promise)).rejects.toBe(value);
|
|
156
|
+
done();
|
|
157
|
+
}, 10);
|
|
158
|
+
|
|
159
|
+
await expect(shared1).rejects.toBe(value);
|
|
160
|
+
await expect(promise).rejects.toBe(value);
|
|
161
|
+
|
|
162
|
+
await expect(status).resolves.toBe(undefined);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('createMinDelayPromise', () => {
|
|
167
|
+
it('当实际执行时间小于最小等待时间时,应等待剩余时间', async () => {
|
|
168
|
+
const minWait = 100;
|
|
169
|
+
const end = createMinDelayPromise(minWait);
|
|
170
|
+
const startTime = Date.now();
|
|
171
|
+
await promiseDelay(50); // 模拟操作耗时
|
|
172
|
+
await end();
|
|
173
|
+
const endTime = Date.now();
|
|
174
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(minWait - 10);
|
|
175
|
+
expect(endTime - startTime).toBeLessThanOrEqual(minWait + 10);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('当实际执行时间大于最小等待时间时,应立即返回', async () => {
|
|
179
|
+
const minWait = 50;
|
|
180
|
+
const end = createMinDelayPromise(minWait);
|
|
181
|
+
const startTime = Date.now();
|
|
182
|
+
await promiseDelay(100); // 模拟操作耗时
|
|
183
|
+
await end();
|
|
184
|
+
const endTime = Date.now();
|
|
185
|
+
// 实际情况下,这个时间是接近 100,可能是 99,也有可能是 101
|
|
186
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(90);
|
|
187
|
+
expect(endTime - startTime).toBeLessThanOrEqual(110);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('当最小等待时间为 0 时,应立即返回', async () => {
|
|
191
|
+
const minWait = 0;
|
|
192
|
+
const end = createMinDelayPromise(minWait);
|
|
193
|
+
const startTime = Date.now();
|
|
194
|
+
await end();
|
|
195
|
+
const endTime = Date.now();
|
|
196
|
+
expect(endTime - startTime).toBeLessThanOrEqual(10);
|
|
197
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(0);
|
|
198
|
+
});
|
|
199
|
+
});
|
package/test/qs.test.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { dateFormat } from '@/date';
|
|
2
|
+
import { type QSReader, type QSWriter, qsParse, qsStringify } from '@/qs';
|
|
3
|
+
import { isArray, isBoolean, isDate, isFunction, isNull, isNumber, isString, isUndefined } from '@/type';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
describe('qsParse', () => {
|
|
7
|
+
it('默认 reader', () => {
|
|
8
|
+
expect(qsParse('?a=1')).toEqual({ a: '1' });
|
|
9
|
+
expect(qsParse('?a=1&b=2')).toEqual({ a: '1', b: '2' });
|
|
10
|
+
expect(qsParse('?a=1&b=1&b=2')).toEqual({ a: '1', b: ['1', '2'] });
|
|
11
|
+
expect(qsParse('?a=1&a=2&b=1')).toEqual({ a: ['1', '2'], b: '1' });
|
|
12
|
+
expect(qsParse('?a=1&a=2&a=3&a=4')).toEqual({ a: ['1', '2', '3', '4'] });
|
|
13
|
+
expect(qsParse('xx?a=1&a=2&a=3&a=4')).toEqual({ a: ['1', '2', '3', '4'] });
|
|
14
|
+
expect(qsParse('xx??a=1&a=2&a=3&a=4')).toEqual({ a: ['1', '2', '3', '4'] });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('自定义 reader', () => {
|
|
18
|
+
const qsReader: QSReader<Record<string, string | number | boolean | Array<string | number | boolean>>> = (
|
|
19
|
+
value,
|
|
20
|
+
key,
|
|
21
|
+
qsObject,
|
|
22
|
+
) => {
|
|
23
|
+
if (value === '1') {
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (value === 'true') {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (value.includes(',')) {
|
|
32
|
+
if (Object.hasOwn(qsObject, key)) {
|
|
33
|
+
if (!isArray(qsObject[key])) qsObject[key] = [qsObject[key]];
|
|
34
|
+
} else {
|
|
35
|
+
qsObject[key] = [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
qsObject[key].push(...value.split(','));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return value;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
expect(qsParse('?a=1', qsReader)).toEqual({ a: 1 });
|
|
46
|
+
expect(qsParse('?a=1&b=2', qsReader)).toEqual({ a: 1, b: '2' });
|
|
47
|
+
expect(qsParse('?a=1&b=2&a=3', qsReader)).toEqual({ a: [1, '3'], b: '2' });
|
|
48
|
+
expect(qsParse('?a=1&b=2&a=3&b=4,5', qsReader)).toEqual({ a: [1, '3'], b: ['2', '4', '5'] });
|
|
49
|
+
expect(qsParse('?a=1&b=2&a=3&c=4,5', qsReader)).toEqual({ a: [1, '3'], b: '2', c: ['4', '5'] });
|
|
50
|
+
expect(qsParse('?x=true', qsReader)).toEqual({ x: true });
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('qsStringify', () => {
|
|
55
|
+
it('qsStringify 默认 writer', () => {
|
|
56
|
+
const i = new Date(2020, 0, 1, 0, 0, 0, 0);
|
|
57
|
+
const query = { a: 1, b: [2, 3], c: '4', d: undefined, e: null, f: true, g: false, i };
|
|
58
|
+
const string = 'a=1&b=2&b=3&c=4&f=true&g=false&i=2019-12-31T16%3A00%3A00.000Z';
|
|
59
|
+
expect(qsStringify(query)).toBe(string);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('qsStringify 复写 writer', () => {
|
|
63
|
+
const qsWriter: QSWriter = (value, key, qsObject) => {
|
|
64
|
+
if (isString(value)) return `string-${value}`;
|
|
65
|
+
if (isNumber(value)) return `number-${value}`;
|
|
66
|
+
if (isBoolean(value)) return `boolean-${value ? 'true' : 'false'}`;
|
|
67
|
+
if (isUndefined(value)) return 'undefined';
|
|
68
|
+
if (isNull(value)) return 'null';
|
|
69
|
+
if (isDate(value)) return `date-${dateFormat(value, 'YYYY-MM-DD HH:mm:ss')}`;
|
|
70
|
+
return null;
|
|
71
|
+
};
|
|
72
|
+
const i = new Date(2020, 0, 1, 0, 0, 0, 0);
|
|
73
|
+
const query = { a: 1, b: [2, 3], c: '4', d: undefined, e: null, f: true, g: false, i };
|
|
74
|
+
const string =
|
|
75
|
+
'a=number-1&b=number-2&b=number-3&c=string-4&d=undefined&e=null&' +
|
|
76
|
+
'f=boolean-true&g=boolean-false&i=date-2020-01-01+00%3A00%3A00';
|
|
77
|
+
expect(qsStringify(query, qsWriter)).toBe(string);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isDigit,
|
|
3
|
+
isEmail,
|
|
4
|
+
isFloat,
|
|
5
|
+
isIDNo,
|
|
6
|
+
isIPV4,
|
|
7
|
+
isInteger,
|
|
8
|
+
isNumerical,
|
|
9
|
+
isPhone,
|
|
10
|
+
isURL,
|
|
11
|
+
regexpEscape,
|
|
12
|
+
} from '@/regexp';
|
|
13
|
+
import { expect, test } from 'vitest';
|
|
14
|
+
|
|
15
|
+
test('reEscape', () => {
|
|
16
|
+
const str = 'a*';
|
|
17
|
+
const re = new RegExp(regexpEscape(str), 'i');
|
|
18
|
+
|
|
19
|
+
expect(re.source).toEqual('a\\*');
|
|
20
|
+
expect(re.test('a*')).toEqual(true);
|
|
21
|
+
expect(re.test('A*')).toEqual(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('isURL', () => {
|
|
25
|
+
expect(isURL('')).toBe(false);
|
|
26
|
+
expect(isURL('http://aba.com')).toBe(true);
|
|
27
|
+
expect(isURL("http://aba.com:8080/'1@!.+%a?a(x)")).toBe(true);
|
|
28
|
+
expect(isURL("http://192.168.0.1/'1@!.+%a?a(x)")).toBe(true);
|
|
29
|
+
expect(isURL("http://192.168.0.1:8080/'1@!.+%a?a(x)")).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('isEmail', () => {
|
|
33
|
+
expect(isEmail('')).toBe(false);
|
|
34
|
+
expect(isEmail('http://aba.com')).toBe(false);
|
|
35
|
+
expect(isEmail('http@aba.com')).toBe(true);
|
|
36
|
+
expect(isEmail('ht.tp@aba.com')).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('isPhone', () => {
|
|
40
|
+
expect(isPhone('')).toBe(false);
|
|
41
|
+
expect(isPhone('19912345678')).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('isIPV4', () => {
|
|
45
|
+
expect(isIPV4('')).toBe(false);
|
|
46
|
+
expect(isIPV4('11.11.111.11')).toBe(true);
|
|
47
|
+
expect(isIPV4('222.233.455.11')).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('isIdNo', () => {
|
|
51
|
+
// http://id.8684.cn/
|
|
52
|
+
expect(isIDNo('350213197706189461')).toBe(true);
|
|
53
|
+
expect(isIDNo('350213197706189462')).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('isInteger', () => {
|
|
57
|
+
expect(isInteger('1')).toBe(true);
|
|
58
|
+
expect(isInteger('-1')).toBe(true);
|
|
59
|
+
expect(isInteger('12300')).toBe(true);
|
|
60
|
+
expect(isInteger('-12300')).toBe(true);
|
|
61
|
+
expect(isInteger('0')).toBe(true);
|
|
62
|
+
expect(isInteger('0.0')).toBe(false);
|
|
63
|
+
expect(isInteger('0.1')).toBe(false);
|
|
64
|
+
expect(isInteger('-0')).toBe(false);
|
|
65
|
+
expect(isInteger('-0123')).toBe(false);
|
|
66
|
+
expect(isInteger('0123')).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('isFloat', () => {
|
|
70
|
+
expect(isFloat('0')).toBe(false);
|
|
71
|
+
expect(isFloat('0.1')).toBe(true);
|
|
72
|
+
expect(isFloat('1.1')).toBe(true);
|
|
73
|
+
expect(isFloat('1.01')).toBe(true);
|
|
74
|
+
expect(isFloat('10.01')).toBe(true);
|
|
75
|
+
expect(isFloat('-10.01')).toBe(true);
|
|
76
|
+
expect(isFloat('010.01')).toBe(false);
|
|
77
|
+
expect(isFloat('10.010')).toBe(true);
|
|
78
|
+
expect(isFloat('10.0')).toBe(true);
|
|
79
|
+
expect(isFloat('10.')).toBe(false);
|
|
80
|
+
expect(isFloat('10')).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('.isNumrical', () => {
|
|
84
|
+
expect(isNumerical('0')).toBe(true);
|
|
85
|
+
expect(isNumerical('0.0')).toBe(true);
|
|
86
|
+
expect(isNumerical('0.1')).toBe(true);
|
|
87
|
+
expect(isNumerical('1.1')).toBe(true);
|
|
88
|
+
expect(isNumerical('-1.1')).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('isDigit', () => {
|
|
92
|
+
expect(isDigit('1')).toBe(true);
|
|
93
|
+
expect(isDigit('12')).toBe(true);
|
|
94
|
+
expect(isDigit('012')).toBe(true);
|
|
95
|
+
expect(isDigit('+012')).toBe(false);
|
|
96
|
+
expect(isDigit('-012')).toBe(false);
|
|
97
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { randomString, randomUUID4, stringCamelCase, stringFormat, stringKebabCase, stringify } from '../src/string';
|
|
3
|
+
|
|
4
|
+
describe('stringCamelCase', () => {
|
|
5
|
+
it('应将字符串转换为驼峰命名', () => {
|
|
6
|
+
expect(stringCamelCase('hello world')).toBe('helloWorld');
|
|
7
|
+
expect(stringCamelCase('hello_world')).toBe('helloWorld');
|
|
8
|
+
expect(stringCamelCase('hello-world')).toBe('helloWorld');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('如果 bigger 为 true,应将首字母大写', () => {
|
|
12
|
+
expect(stringCamelCase('hello world', true)).toBe('HelloWorld');
|
|
13
|
+
expect(stringCamelCase('hello_world', true)).toBe('HelloWorld');
|
|
14
|
+
expect(stringCamelCase('hello-world', true)).toBe('HelloWorld');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('stringKebabCase', () => {
|
|
19
|
+
it('应将字符串转换为默认分隔符的短横线命名', () => {
|
|
20
|
+
expect(stringKebabCase('helloWorld')).toBe('hello-world');
|
|
21
|
+
expect(stringKebabCase('HelloWorld')).toBe('-hello-world');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('应将字符串转换为自定义分隔符的短横线命名', () => {
|
|
25
|
+
expect(stringKebabCase('helloWorld', '_')).toBe('hello_world');
|
|
26
|
+
expect(stringKebabCase('HelloWorld', '_')).toBe('_hello_world');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('randomString', () => {
|
|
31
|
+
it('应生成指定长度的随机字符串', () => {
|
|
32
|
+
const result = randomString(10);
|
|
33
|
+
expect(result.length).toBe(10);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('应使用自定义字符字典生成随机字符串', () => {
|
|
37
|
+
const dict = 'ABCDEF';
|
|
38
|
+
const result = randomString(8, dict);
|
|
39
|
+
expect(result).toHaveLength(8);
|
|
40
|
+
for (const char of result) {
|
|
41
|
+
expect(dict).toContain(char);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('stringFormat', () => {
|
|
47
|
+
it('应支持索引方式格式化字符串', () => {
|
|
48
|
+
const result = stringFormat('你好 {0}!我的名字是 {1}。', '张三', '李四');
|
|
49
|
+
expect(result).toBe('你好 张三!我的名字是 李四。');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('应支持对象方式格式化字符串', () => {
|
|
53
|
+
const result = stringFormat('{greet}!我的名字是 {name}。', { greet: '你好', name: '王五' });
|
|
54
|
+
expect(result).toBe('你好!我的名字是 王五。');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('应支持带回退值的对象方式格式化字符串', () => {
|
|
58
|
+
const result = stringFormat('{greet}!我的名字是 {name}。', { greet: '你好' }, '未知');
|
|
59
|
+
expect(result).toBe('你好!我的名字是 未知。');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('应支持带回退函数的对象方式格式化字符串', () => {
|
|
63
|
+
const result = stringFormat('{greet}!我的名字是 {name}。', { greet: '你好' }, (key) => `默认${key}`);
|
|
64
|
+
expect(result).toBe('你好!我的名字是 默认name。');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('应处理未找到的键值对', () => {
|
|
68
|
+
const result = stringFormat('{greet}!我的名字是 {name}。', { greet: '你好' });
|
|
69
|
+
expect(result).toBe('你好!我的名字是 name。');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('应处理空字符串', () => {
|
|
73
|
+
const result = stringFormat('', { greet: '你好' });
|
|
74
|
+
expect(result).toBe('');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('应处理未传递参数的情况', () => {
|
|
78
|
+
const result = stringFormat('{greet}!我的名字是 {name}。');
|
|
79
|
+
expect(result).toBe('greet!我的名字是 name。');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('边界,值为 falsy 值', () => {
|
|
83
|
+
const result = stringFormat('{days}天{hours}时{minutes}分{seconds}秒', {
|
|
84
|
+
days: 0,
|
|
85
|
+
hours: 1,
|
|
86
|
+
minutes: 2,
|
|
87
|
+
seconds: 3,
|
|
88
|
+
});
|
|
89
|
+
expect(result).toBe('0天1时2分3秒');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('randomUUID4', () => {
|
|
94
|
+
it('验证长度', () => {
|
|
95
|
+
for (let i = 0; i < 10; i++) {
|
|
96
|
+
const uuid = randomUUID4();
|
|
97
|
+
|
|
98
|
+
// 验证长度
|
|
99
|
+
expect(uuid.length).toBe(36);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('验证分隔符位置', () => {
|
|
104
|
+
for (let i = 0; i < 10; i++) {
|
|
105
|
+
const uuid = randomUUID4();
|
|
106
|
+
|
|
107
|
+
// 验证分隔符位置
|
|
108
|
+
expect(uuid[8]).toBe('-');
|
|
109
|
+
expect(uuid[13]).toBe('-');
|
|
110
|
+
expect(uuid[18]).toBe('-');
|
|
111
|
+
expect(uuid[23]).toBe('-');
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('验证版本号为 4', () => {
|
|
116
|
+
for (let i = 0; i < 10; i++) {
|
|
117
|
+
const uuid = randomUUID4();
|
|
118
|
+
|
|
119
|
+
// 验证版本号为 4
|
|
120
|
+
expect(uuid[14]).toBe('4');
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('验证变体', () => {
|
|
125
|
+
for (let i = 0; i < 10; i++) {
|
|
126
|
+
const uuid = randomUUID4();
|
|
127
|
+
|
|
128
|
+
// 验证变体符合 RFC 4122 (第19位为 '8', '9', 'a', 或 'b')
|
|
129
|
+
const variantChar = uuid[19];
|
|
130
|
+
expect(['8', '9', 'a', 'b']).toContain(variantChar);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('stringify', () => {
|
|
136
|
+
it('应将 null 转换为空字符串', () => {
|
|
137
|
+
expect(stringify(null)).toBe('');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('应将 undefined 转换为空字符串', () => {
|
|
141
|
+
expect(stringify(undefined)).toBe('');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('应将其他值转换为字符串', () => {
|
|
145
|
+
expect(stringify(123)).toBe('123');
|
|
146
|
+
expect(stringify(true)).toBe('true');
|
|
147
|
+
expect(stringify({})).toBe('[object Object]');
|
|
148
|
+
expect(stringify('hello')).toBe('hello');
|
|
149
|
+
});
|
|
150
|
+
});
|