befly-shared 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/LICENSE +201 -0
- package/README.md +442 -0
- package/dist/addonHelper.js +83 -0
- package/dist/arrayKeysToCamel.js +18 -0
- package/dist/arrayToTree.js +23 -0
- package/dist/calcPerfTime.js +13 -0
- package/dist/configTypes.js +1 -0
- package/dist/defineAddonConfig.js +40 -0
- package/dist/fieldClear.js +57 -0
- package/dist/genShortId.js +12 -0
- package/dist/index.js +25 -0
- package/dist/keysToCamel.js +21 -0
- package/dist/keysToSnake.js +21 -0
- package/dist/layouts.js +59 -0
- package/dist/pickFields.js +16 -0
- package/dist/redisKeys.js +34 -0
- package/dist/regex.js +200 -0
- package/dist/scanConfig.js +82 -0
- package/dist/scanFiles.js +39 -0
- package/dist/scanViews.js +48 -0
- package/dist/types.js +46 -0
- package/package.json +40 -0
- package/src/addonHelper.ts +88 -0
- package/src/arrayKeysToCamel.ts +18 -0
- package/src/arrayToTree.ts +31 -0
- package/src/calcPerfTime.ts +13 -0
- package/src/configTypes.ts +27 -0
- package/src/defineAddonConfig.ts +45 -0
- package/src/fieldClear.ts +75 -0
- package/src/genShortId.ts +12 -0
- package/src/index.ts +28 -0
- package/src/keysToCamel.ts +22 -0
- package/src/keysToSnake.ts +22 -0
- package/src/layouts.ts +90 -0
- package/src/pickFields.ts +19 -0
- package/src/redisKeys.ts +44 -0
- package/src/regex.ts +223 -0
- package/src/scanConfig.ts +104 -0
- package/src/scanFiles.ts +49 -0
- package/src/scanViews.ts +55 -0
- package/src/types.ts +338 -0
- package/tests/addonHelper.test.ts +55 -0
- package/tests/arrayKeysToCamel.test.ts +21 -0
- package/tests/arrayToTree.test.ts +98 -0
- package/tests/calcPerfTime.test.ts +19 -0
- package/tests/fieldClear.test.ts +39 -0
- package/tests/keysToCamel.test.ts +22 -0
- package/tests/keysToSnake.test.ts +22 -0
- package/tests/layouts.test.ts +93 -0
- package/tests/pickFields.test.ts +22 -0
- package/tests/regex.test.ts +308 -0
- package/tests/scanFiles.test.ts +58 -0
- package/tests/types.test.ts +283 -0
- package/types/addonConfigMerge.d.ts +17 -0
- package/types/addonHelper.d.ts +24 -0
- package/types/arrayKeysToCamel.d.ts +13 -0
- package/types/arrayToTree.d.ts +8 -0
- package/types/calcPerfTime.d.ts +4 -0
- package/types/configMerge.d.ts +49 -0
- package/types/configTypes.d.ts +26 -0
- package/types/defineAddonConfig.d.ts +19 -0
- package/types/fieldClear.d.ts +16 -0
- package/types/genShortId.d.ts +10 -0
- package/types/index.d.ts +22 -0
- package/types/keysToCamel.d.ts +10 -0
- package/types/keysToSnake.d.ts +10 -0
- package/types/layouts.d.ts +29 -0
- package/types/loadAndMergeConfig.d.ts +7 -0
- package/types/mergeConfig.d.ts +7 -0
- package/types/pickFields.d.ts +4 -0
- package/types/redisKeys.d.ts +34 -0
- package/types/regex.d.ts +143 -0
- package/types/scanConfig.d.ts +7 -0
- package/types/scanFiles.d.ts +12 -0
- package/types/scanViews.d.ts +11 -0
- package/types/types.d.ts +274 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test';
|
|
2
|
+
import { Layouts, type RouteConfig } from '../src/layouts';
|
|
3
|
+
|
|
4
|
+
describe('Layouts', () => {
|
|
5
|
+
it('should handle simple routes with default layout', () => {
|
|
6
|
+
const routes: RouteConfig[] = [{ path: 'home', component: 'HomeComponent' }];
|
|
7
|
+
const result = Layouts(routes);
|
|
8
|
+
expect(result).toEqual([{ path: 'home', layoutName: 'default', component: 'HomeComponent', meta: undefined }]);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should handle routes with specified layout suffix', () => {
|
|
12
|
+
const routes: RouteConfig[] = [{ path: 'login_1', component: 'LoginComponent' }];
|
|
13
|
+
const result = Layouts(routes);
|
|
14
|
+
expect(result).toEqual([{ path: 'login', layoutName: '1', component: 'LoginComponent', meta: undefined }]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should handle index routes correctly', () => {
|
|
18
|
+
const routes: RouteConfig[] = [{ path: 'index', component: 'IndexComponent' }];
|
|
19
|
+
const result = Layouts(routes);
|
|
20
|
+
expect(result).toEqual([{ path: '', layoutName: 'default', component: 'IndexComponent', meta: undefined }]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should handle index routes with layout suffix', () => {
|
|
24
|
+
const routes: RouteConfig[] = [{ path: 'index_2', component: 'IndexComponent' }];
|
|
25
|
+
const result = Layouts(routes);
|
|
26
|
+
expect(result).toEqual([{ path: '', layoutName: '2', component: 'IndexComponent', meta: undefined }]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should handle nested routes and inherit layout', () => {
|
|
30
|
+
const routes: RouteConfig[] = [
|
|
31
|
+
{
|
|
32
|
+
path: 'admin_3',
|
|
33
|
+
children: [
|
|
34
|
+
{ path: 'dashboard', component: 'DashboardComponent' },
|
|
35
|
+
{ path: 'settings_4', component: 'SettingsComponent' }
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
];
|
|
39
|
+
const result = Layouts(routes);
|
|
40
|
+
expect(result).toHaveLength(2);
|
|
41
|
+
|
|
42
|
+
// Inherited layout '3'
|
|
43
|
+
expect(result[0]).toEqual({
|
|
44
|
+
path: 'admin/dashboard',
|
|
45
|
+
layoutName: '3',
|
|
46
|
+
component: 'DashboardComponent',
|
|
47
|
+
meta: undefined
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Overridden layout '4'
|
|
51
|
+
expect(result[1]).toEqual({
|
|
52
|
+
path: 'admin/settings',
|
|
53
|
+
layoutName: '4',
|
|
54
|
+
component: 'SettingsComponent',
|
|
55
|
+
meta: undefined
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should handle deep nesting', () => {
|
|
60
|
+
const routes: RouteConfig[] = [
|
|
61
|
+
{
|
|
62
|
+
path: 'a_1',
|
|
63
|
+
children: [
|
|
64
|
+
{
|
|
65
|
+
path: 'b',
|
|
66
|
+
children: [{ path: 'c', component: 'CComponent' }]
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
];
|
|
71
|
+
const result = Layouts(routes);
|
|
72
|
+
expect(result).toEqual([{ path: 'a/b/c', layoutName: '1', component: 'CComponent', meta: undefined }]);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should handle path cleaning correctly', () => {
|
|
76
|
+
const routes: RouteConfig[] = [
|
|
77
|
+
{
|
|
78
|
+
path: 'users_5',
|
|
79
|
+
children: [
|
|
80
|
+
{ path: 'list', component: 'ListComponent' },
|
|
81
|
+
{ path: 'detail_6', component: 'DetailComponent' }
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
const result = Layouts(routes);
|
|
86
|
+
|
|
87
|
+
expect(result[0].path).toBe('users/list');
|
|
88
|
+
expect(result[0].layoutName).toBe('5');
|
|
89
|
+
|
|
90
|
+
expect(result[1].path).toBe('users/detail');
|
|
91
|
+
expect(result[1].layoutName).toBe('6');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { pickFields } from '../src/pickFields';
|
|
3
|
+
|
|
4
|
+
describe('pickFields', () => {
|
|
5
|
+
test('should pick specified fields', () => {
|
|
6
|
+
const input = { a: 1, b: 2, c: 3 };
|
|
7
|
+
const keys = ['a', 'c'];
|
|
8
|
+
const expected = { a: 1, c: 3 };
|
|
9
|
+
expect(pickFields(input, keys)).toEqual(expected);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('should ignore non-existent keys', () => {
|
|
13
|
+
const input = { a: 1 };
|
|
14
|
+
const keys = ['a', 'b'];
|
|
15
|
+
const expected = { a: 1 };
|
|
16
|
+
expect(pickFields(input, keys)).toEqual(expected);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should return empty object for invalid input', () => {
|
|
20
|
+
expect(pickFields(null as any, ['a'])).toEqual({});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 正则表达式别名测试
|
|
3
|
+
*/
|
|
4
|
+
import { describe, expect, it } from 'bun:test';
|
|
5
|
+
|
|
6
|
+
import { getRegex, matchRegex, RegexAliases } from '../src/regex.js';
|
|
7
|
+
|
|
8
|
+
describe('RegexAliases - 正则别名常量', () => {
|
|
9
|
+
describe('数字类型', () => {
|
|
10
|
+
it('number - 正整数', () => {
|
|
11
|
+
expect(new RegExp(RegexAliases.number).test('123')).toBe(true);
|
|
12
|
+
expect(new RegExp(RegexAliases.number).test('0')).toBe(true);
|
|
13
|
+
expect(new RegExp(RegexAliases.number).test('-123')).toBe(false);
|
|
14
|
+
expect(new RegExp(RegexAliases.number).test('12.3')).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('integer - 整数(含负数)', () => {
|
|
18
|
+
expect(new RegExp(RegexAliases.integer).test('123')).toBe(true);
|
|
19
|
+
expect(new RegExp(RegexAliases.integer).test('-123')).toBe(true);
|
|
20
|
+
expect(new RegExp(RegexAliases.integer).test('0')).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('float - 浮点数', () => {
|
|
24
|
+
expect(new RegExp(RegexAliases.float).test('12.34')).toBe(true);
|
|
25
|
+
expect(new RegExp(RegexAliases.float).test('-12.34')).toBe(true);
|
|
26
|
+
expect(new RegExp(RegexAliases.float).test('123')).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('positive - 正整数(不含0)', () => {
|
|
30
|
+
expect(new RegExp(RegexAliases.positive).test('123')).toBe(true);
|
|
31
|
+
expect(new RegExp(RegexAliases.positive).test('0')).toBe(false);
|
|
32
|
+
expect(new RegExp(RegexAliases.positive).test('-1')).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('字符串类型', () => {
|
|
37
|
+
it('word - 纯字母', () => {
|
|
38
|
+
expect(new RegExp(RegexAliases.word).test('Hello')).toBe(true);
|
|
39
|
+
expect(new RegExp(RegexAliases.word).test('Hello123')).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('alphanumeric - 字母和数字', () => {
|
|
43
|
+
expect(new RegExp(RegexAliases.alphanumeric).test('Hello123')).toBe(true);
|
|
44
|
+
expect(new RegExp(RegexAliases.alphanumeric).test('Hello_123')).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('alphanumericUnderscore - 字母、数字和下划线', () => {
|
|
48
|
+
expect(new RegExp(RegexAliases.alphanumericUnderscore).test('Hello_123')).toBe(true);
|
|
49
|
+
expect(new RegExp(RegexAliases.alphanumericUnderscore).test('Hello-123')).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('中文', () => {
|
|
54
|
+
it('chinese - 纯中文', () => {
|
|
55
|
+
expect(new RegExp(RegexAliases.chinese).test('你好')).toBe(true);
|
|
56
|
+
expect(new RegExp(RegexAliases.chinese).test('你好World')).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('chineseWord - 中文和字母', () => {
|
|
60
|
+
expect(new RegExp(RegexAliases.chineseWord).test('你好World')).toBe(true);
|
|
61
|
+
expect(new RegExp(RegexAliases.chineseWord).test('你好123')).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('常用格式', () => {
|
|
66
|
+
it('email - 邮箱地址', () => {
|
|
67
|
+
expect(new RegExp(RegexAliases.email).test('test@example.com')).toBe(true);
|
|
68
|
+
expect(new RegExp(RegexAliases.email).test('test.name@example.co.uk')).toBe(true);
|
|
69
|
+
expect(new RegExp(RegexAliases.email).test('invalid-email')).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('phone - 中国大陆手机号', () => {
|
|
73
|
+
expect(new RegExp(RegexAliases.phone).test('13812345678')).toBe(true);
|
|
74
|
+
expect(new RegExp(RegexAliases.phone).test('12345678901')).toBe(false);
|
|
75
|
+
expect(new RegExp(RegexAliases.phone).test('1381234567')).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('url - URL 地址', () => {
|
|
79
|
+
expect(new RegExp(RegexAliases.url).test('https://example.com')).toBe(true);
|
|
80
|
+
expect(new RegExp(RegexAliases.url).test('http://example.com')).toBe(true);
|
|
81
|
+
expect(new RegExp(RegexAliases.url).test('ftp://example.com')).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('ip - IPv4 地址', () => {
|
|
85
|
+
expect(new RegExp(RegexAliases.ip).test('192.168.1.1')).toBe(true);
|
|
86
|
+
expect(new RegExp(RegexAliases.ip).test('255.255.255.255')).toBe(true);
|
|
87
|
+
expect(new RegExp(RegexAliases.ip).test('256.1.1.1')).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('特殊格式', () => {
|
|
92
|
+
it('uuid - UUID', () => {
|
|
93
|
+
expect(new RegExp(RegexAliases.uuid).test('550e8400-e29b-41d4-a716-446655440000')).toBe(true);
|
|
94
|
+
expect(new RegExp(RegexAliases.uuid).test('invalid-uuid')).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('md5 - MD5 哈希', () => {
|
|
98
|
+
expect(new RegExp(RegexAliases.md5).test('d41d8cd98f00b204e9800998ecf8427e')).toBe(true);
|
|
99
|
+
expect(new RegExp(RegexAliases.md5).test('invalid')).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('日期时间', () => {
|
|
104
|
+
it('date - 日期 YYYY-MM-DD', () => {
|
|
105
|
+
expect(new RegExp(RegexAliases.date).test('2024-01-15')).toBe(true);
|
|
106
|
+
expect(new RegExp(RegexAliases.date).test('2024/01/15')).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('time - 时间 HH:MM:SS', () => {
|
|
110
|
+
expect(new RegExp(RegexAliases.time).test('12:30:45')).toBe(true);
|
|
111
|
+
expect(new RegExp(RegexAliases.time).test('12:30')).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('代码相关', () => {
|
|
116
|
+
it('variable - 变量名', () => {
|
|
117
|
+
expect(new RegExp(RegexAliases.variable).test('myVar')).toBe(true);
|
|
118
|
+
expect(new RegExp(RegexAliases.variable).test('_private')).toBe(true);
|
|
119
|
+
expect(new RegExp(RegexAliases.variable).test('123var')).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('constant - 常量名', () => {
|
|
123
|
+
expect(new RegExp(RegexAliases.constant).test('MY_CONST')).toBe(true);
|
|
124
|
+
expect(new RegExp(RegexAliases.constant).test('myConst')).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('证件相关', () => {
|
|
129
|
+
it('idCard - 中国身份证号', () => {
|
|
130
|
+
expect(new RegExp(RegexAliases.idCard).test('11010119900101001X')).toBe(true);
|
|
131
|
+
expect(new RegExp(RegexAliases.idCard).test('110101199001010019')).toBe(true);
|
|
132
|
+
expect(new RegExp(RegexAliases.idCard).test('1234567890')).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('getRegex - 获取正则表达式', () => {
|
|
138
|
+
it('以 @ 开头返回别名对应的正则', () => {
|
|
139
|
+
expect(getRegex('@email')).toBe(RegexAliases.email);
|
|
140
|
+
expect(getRegex('@phone')).toBe(RegexAliases.phone);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('未知别名返回原字符串', () => {
|
|
144
|
+
expect(getRegex('@unknown')).toBe('@unknown');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('不以 @ 开头返回原字符串', () => {
|
|
148
|
+
expect(getRegex('^\\d+$')).toBe('^\\d+$');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('matchRegex - 验证值是否匹配', () => {
|
|
153
|
+
it('使用别名验证', () => {
|
|
154
|
+
expect(matchRegex('test@example.com', '@email')).toBe(true);
|
|
155
|
+
expect(matchRegex('invalid', '@email')).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('使用自定义正则验证', () => {
|
|
159
|
+
expect(matchRegex('123', '^\\d+$')).toBe(true);
|
|
160
|
+
expect(matchRegex('abc', '^\\d+$')).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('手机号验证', () => {
|
|
164
|
+
expect(matchRegex('13812345678', '@phone')).toBe(true);
|
|
165
|
+
expect(matchRegex('12345678901', '@phone')).toBe(false);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('RegexAliases - 新增正则别名', () => {
|
|
170
|
+
describe('账号相关', () => {
|
|
171
|
+
it('bankCard - 银行卡号', () => {
|
|
172
|
+
expect(new RegExp(RegexAliases.bankCard).test('6222020111122223333')).toBe(true);
|
|
173
|
+
expect(new RegExp(RegexAliases.bankCard).test('622202011112222')).toBe(false); // 15位太短
|
|
174
|
+
expect(new RegExp(RegexAliases.bankCard).test('62220201111222233334444')).toBe(false); // 20位太长
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('wechat - 微信号', () => {
|
|
178
|
+
expect(new RegExp(RegexAliases.wechat).test('abc123')).toBe(true);
|
|
179
|
+
expect(new RegExp(RegexAliases.wechat).test('test_user-1')).toBe(true);
|
|
180
|
+
expect(new RegExp(RegexAliases.wechat).test('123abc')).toBe(false); // 数字开头
|
|
181
|
+
expect(new RegExp(RegexAliases.wechat).test('ab')).toBe(false); // 太短
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('qq - QQ号', () => {
|
|
185
|
+
expect(new RegExp(RegexAliases.qq).test('12345')).toBe(true);
|
|
186
|
+
expect(new RegExp(RegexAliases.qq).test('123456789')).toBe(true);
|
|
187
|
+
expect(new RegExp(RegexAliases.qq).test('01234')).toBe(false); // 0开头
|
|
188
|
+
expect(new RegExp(RegexAliases.qq).test('1234')).toBe(false); // 太短
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('alipay - 支付宝账号', () => {
|
|
192
|
+
expect(new RegExp(RegexAliases.alipay).test('13812345678')).toBe(true);
|
|
193
|
+
expect(new RegExp(RegexAliases.alipay).test('test@example.com')).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('密码强度', () => {
|
|
198
|
+
it('passwordWeak - 弱密码', () => {
|
|
199
|
+
expect(new RegExp(RegexAliases.passwordWeak).test('123456')).toBe(true);
|
|
200
|
+
expect(new RegExp(RegexAliases.passwordWeak).test('12345')).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('passwordMedium - 中等密码', () => {
|
|
204
|
+
expect(new RegExp(RegexAliases.passwordMedium).test('abc12345')).toBe(true);
|
|
205
|
+
expect(new RegExp(RegexAliases.passwordMedium).test('12345678')).toBe(false); // 无字母
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('passwordStrong - 强密码', () => {
|
|
209
|
+
expect(new RegExp(RegexAliases.passwordStrong).test('Abc12345!')).toBe(true);
|
|
210
|
+
expect(new RegExp(RegexAliases.passwordStrong).test('abc12345!')).toBe(false); // 无大写
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('其他常用', () => {
|
|
215
|
+
it('postalCode - 邮政编码', () => {
|
|
216
|
+
expect(new RegExp(RegexAliases.postalCode).test('518000')).toBe(true);
|
|
217
|
+
expect(new RegExp(RegexAliases.postalCode).test('51800')).toBe(false);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('semver - 语义化版本号', () => {
|
|
221
|
+
expect(new RegExp(RegexAliases.semver).test('1.0.0')).toBe(true);
|
|
222
|
+
expect(new RegExp(RegexAliases.semver).test('1.0.0-beta.1')).toBe(true);
|
|
223
|
+
expect(new RegExp(RegexAliases.semver).test('1.0.0+build.123')).toBe(true);
|
|
224
|
+
expect(new RegExp(RegexAliases.semver).test('v1.0.0')).toBe(false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('colorHex - 十六进制颜色', () => {
|
|
228
|
+
expect(new RegExp(RegexAliases.colorHex).test('#fff')).toBe(true);
|
|
229
|
+
expect(new RegExp(RegexAliases.colorHex).test('#ffffff')).toBe(true);
|
|
230
|
+
expect(new RegExp(RegexAliases.colorHex).test('#FFFFFF')).toBe(true);
|
|
231
|
+
expect(new RegExp(RegexAliases.colorHex).test('ffffff')).toBe(false);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('ipv6 - IPv6 地址', () => {
|
|
235
|
+
expect(new RegExp(RegexAliases.ipv6).test('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe(true);
|
|
236
|
+
expect(new RegExp(RegexAliases.ipv6).test('192.168.1.1')).toBe(false);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('username - 用户名', () => {
|
|
240
|
+
expect(new RegExp(RegexAliases.username).test('admin123')).toBe(true);
|
|
241
|
+
expect(new RegExp(RegexAliases.username).test('test_user')).toBe(true);
|
|
242
|
+
expect(new RegExp(RegexAliases.username).test('123admin')).toBe(false); // 数字开头
|
|
243
|
+
expect(new RegExp(RegexAliases.username).test('abc')).toBe(false); // 太短
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('nickname - 昵称', () => {
|
|
247
|
+
expect(new RegExp(RegexAliases.nickname).test('用户昵称')).toBe(true);
|
|
248
|
+
expect(new RegExp(RegexAliases.nickname).test('test123')).toBe(true);
|
|
249
|
+
expect(new RegExp(RegexAliases.nickname).test('a')).toBe(false); // 太短
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('licensePlate - 车牌号', () => {
|
|
253
|
+
expect(new RegExp(RegexAliases.licensePlate).test('京A12345')).toBe(true);
|
|
254
|
+
expect(new RegExp(RegexAliases.licensePlate).test('粤B88888')).toBe(true);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe('正则缓存功能', () => {
|
|
260
|
+
// 导入缓存相关函数
|
|
261
|
+
const { getCompiledRegex, clearRegexCache, getRegexCacheSize } = require('../src/regex.js');
|
|
262
|
+
|
|
263
|
+
it('getCompiledRegex - 返回 RegExp 对象', () => {
|
|
264
|
+
const regex = getCompiledRegex('@email');
|
|
265
|
+
expect(regex).toBeInstanceOf(RegExp);
|
|
266
|
+
expect(regex.test('test@example.com')).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('getCompiledRegex - 缓存命中返回相同对象', () => {
|
|
270
|
+
clearRegexCache();
|
|
271
|
+
const regex1 = getCompiledRegex('@phone');
|
|
272
|
+
const regex2 = getCompiledRegex('@phone');
|
|
273
|
+
expect(regex1).toBe(regex2); // 同一个对象
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('getCompiledRegex - 支持 flags 参数', () => {
|
|
277
|
+
const regexI = getCompiledRegex('^hello$', 'i');
|
|
278
|
+
expect(regexI.test('HELLO')).toBe(true);
|
|
279
|
+
|
|
280
|
+
const regexNoFlag = getCompiledRegex('^hello$');
|
|
281
|
+
expect(regexNoFlag.test('HELLO')).toBe(false);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('clearRegexCache - 清除缓存', () => {
|
|
285
|
+
getCompiledRegex('@email');
|
|
286
|
+
expect(getRegexCacheSize()).toBeGreaterThan(0);
|
|
287
|
+
clearRegexCache();
|
|
288
|
+
expect(getRegexCacheSize()).toBe(0);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('缓存性能 - 避免重复编译', () => {
|
|
292
|
+
clearRegexCache();
|
|
293
|
+
const pattern = '@email';
|
|
294
|
+
|
|
295
|
+
// 验证缓存命中时返回相同对象
|
|
296
|
+
const regex1 = getCompiledRegex(pattern);
|
|
297
|
+
const regex2 = getCompiledRegex(pattern);
|
|
298
|
+
expect(regex1).toBe(regex2);
|
|
299
|
+
|
|
300
|
+
// 验证缓存大小
|
|
301
|
+
expect(getRegexCacheSize()).toBe(1);
|
|
302
|
+
|
|
303
|
+
// 添加更多缓存
|
|
304
|
+
getCompiledRegex('@phone');
|
|
305
|
+
getCompiledRegex('@url');
|
|
306
|
+
expect(getRegexCacheSize()).toBe(3);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'bun:test';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'node:fs';
|
|
4
|
+
import { scanFiles } from '../src/scanFiles';
|
|
5
|
+
|
|
6
|
+
const TEST_DIR = join(process.cwd(), 'temp_test_scanFiles');
|
|
7
|
+
|
|
8
|
+
describe('scanFiles', () => {
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
if (existsSync(TEST_DIR)) {
|
|
11
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
12
|
+
}
|
|
13
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
14
|
+
|
|
15
|
+
// Create test files
|
|
16
|
+
writeFileSync(join(TEST_DIR, 'a.ts'), '');
|
|
17
|
+
writeFileSync(join(TEST_DIR, 'b.js'), '');
|
|
18
|
+
writeFileSync(join(TEST_DIR, 'c.txt'), ''); // Should be ignored by default pattern
|
|
19
|
+
writeFileSync(join(TEST_DIR, '_ignored.ts'), ''); // Should be ignored by ignoreUnderline
|
|
20
|
+
|
|
21
|
+
mkdirSync(join(TEST_DIR, 'sub'));
|
|
22
|
+
writeFileSync(join(TEST_DIR, 'sub/d.ts'), '');
|
|
23
|
+
|
|
24
|
+
mkdirSync(join(TEST_DIR, '_sub_ignored'));
|
|
25
|
+
writeFileSync(join(TEST_DIR, '_sub_ignored/e.ts'), ''); // Should be ignored by ignoreUnderline
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterAll(() => {
|
|
29
|
+
if (existsSync(TEST_DIR)) {
|
|
30
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should scan ts and js files by default', async () => {
|
|
35
|
+
const files = await scanFiles(TEST_DIR);
|
|
36
|
+
const fileNames = files.map((f) => f.fileName).sort();
|
|
37
|
+
expect(fileNames).toEqual(['a', 'b', 'd']);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should respect custom pattern', async () => {
|
|
41
|
+
const files = await scanFiles(TEST_DIR, '**/*.txt');
|
|
42
|
+
const fileNames = files.map((f) => f.fileName).sort();
|
|
43
|
+
expect(fileNames).toEqual(['c']);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should include underline files when ignoreUnderline is false', async () => {
|
|
47
|
+
const files = await scanFiles(TEST_DIR, '**/*.{ts,js}', false);
|
|
48
|
+
const fileNames = files.map((f) => f.fileName).sort();
|
|
49
|
+
expect(fileNames).toEqual(['_ignored', 'a', 'b', 'd', 'e']);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should return correct relative paths', async () => {
|
|
53
|
+
const files = await scanFiles(TEST_DIR);
|
|
54
|
+
const dFile = files.find((f) => f.fileName === 'd');
|
|
55
|
+
expect(dFile).toBeDefined();
|
|
56
|
+
expect(dFile?.relativePath).toBe('sub/d');
|
|
57
|
+
});
|
|
58
|
+
});
|