@taicode/common-web 1.1.20 → 1.1.21
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/output/catalyst/alert.jsx +55 -0
- package/output/catalyst/auth-layout.jsx +7 -0
- package/output/catalyst/avatar.jsx +45 -0
- package/output/catalyst/badge.jsx +53 -0
- package/output/catalyst/button.jsx +187 -0
- package/output/catalyst/checkbox.jsx +105 -0
- package/output/catalyst/combobox.jsx +120 -0
- package/output/catalyst/description-list.jsx +24 -0
- package/output/catalyst/dialog.jsx +55 -0
- package/output/catalyst/divider.jsx +16 -0
- package/output/catalyst/dropdown.jsx +102 -0
- package/output/catalyst/fieldset.jsx +41 -0
- package/output/catalyst/heading.jsx +22 -0
- package/output/catalyst/input.jsx +73 -0
- package/output/catalyst/link.jsx +14 -0
- package/output/catalyst/listbox.jsx +120 -0
- package/output/catalyst/navbar.jsx +67 -0
- package/output/catalyst/pagination.jsx +52 -0
- package/output/catalyst/radio.jsx +103 -0
- package/output/catalyst/select.jsx +59 -0
- package/output/catalyst/sidebar-layout.jsx +58 -0
- package/output/catalyst/sidebar.jsx +85 -0
- package/output/catalyst/stacked-layout.jsx +55 -0
- package/output/catalyst/switch.jsx +161 -0
- package/output/catalyst/table.jsx +68 -0
- package/output/catalyst/text.jsx +29 -0
- package/output/catalyst/textarea.jsx +49 -0
- package/output/helpers/cache-api/cache-api.d.ts +13 -0
- package/output/helpers/cache-api/cache-api.d.ts.map +1 -0
- package/output/helpers/cache-api/cache-api.js +114 -0
- package/output/helpers/cache-api/cache-api.test.d.ts +2 -0
- package/output/helpers/cache-api/cache-api.test.d.ts.map +1 -0
- package/output/helpers/cache-api/cache-api.test.js +348 -0
- package/output/helpers/cache-api/index.d.ts +2 -0
- package/output/helpers/cache-api/index.d.ts.map +1 -0
- package/output/helpers/cache-api/index.js +1 -0
- package/output/helpers/service/index.d.ts +1 -0
- package/output/helpers/service/index.d.ts.map +1 -0
- package/output/helpers/service/index.js +1 -0
- package/output/helpers/service/service.d.ts +5 -0
- package/output/helpers/service/service.d.ts.map +1 -0
- package/output/helpers/service/service.js +2 -0
- package/output/helpers/side-cache/index.d.ts +2 -0
- package/output/helpers/side-cache/index.d.ts.map +1 -0
- package/output/helpers/side-cache/index.js +1 -0
- package/output/helpers/side-cache/side-cache.d.ts +10 -0
- package/output/helpers/side-cache/side-cache.d.ts.map +1 -0
- package/output/helpers/side-cache/side-cache.js +137 -0
- package/output/helpers/side-cache/side-cache.test.d.ts +2 -0
- package/output/helpers/side-cache/side-cache.test.d.ts.map +1 -0
- package/output/helpers/side-cache/side-cache.test.js +179 -0
- package/output/helpers/use-observer/index.d.ts +2 -0
- package/output/helpers/use-observer/index.d.ts.map +1 -0
- package/output/helpers/use-observer/index.js +1 -0
- package/output/helpers/use-observer/use-observer.d.ts +3 -0
- package/output/helpers/use-observer/use-observer.d.ts.map +1 -0
- package/output/helpers/use-observer/use-observer.js +16 -0
- package/output/helpers/use-observer/use-observer.test.d.ts +2 -0
- package/output/helpers/use-observer/use-observer.test.d.ts.map +1 -0
- package/output/helpers/use-observer/use-observer.test.jsx +134 -0
- package/output/service/service.test.jsx +367 -0
- package/output/signin/index.d.ts +2 -2
- package/output/signin/index.d.ts.map +1 -1
- package/output/signin/index.js +1 -1
- package/output/signin/service.d.ts.map +1 -1
- package/output/signin/service.js +1 -2
- package/output/toaster/index.d.ts +1 -2
- package/output/toaster/index.d.ts.map +1 -1
- package/output/toaster/index.js +0 -6
- package/output/use-observer/use-observer.test.jsx +134 -0
- package/package.json +1 -1
- package/output/loading-button/LoadingButtonDemo.d.ts +0 -5
- package/output/loading-button/LoadingButtonDemo.d.ts.map +0 -1
- package/output/loading-button/LoadingButtonDemo.js +0 -33
- package/output/loading-button/index.test.d.ts +0 -2
- package/output/loading-button/index.test.d.ts.map +0 -1
- package/output/loading-button/index.test.js +0 -129
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
36
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
37
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
38
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
39
|
+
};
|
|
40
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
41
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
42
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
43
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
44
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
45
|
+
};
|
|
46
|
+
import React, { act } from 'react';
|
|
47
|
+
import { Service } from '@taicode/common-base';
|
|
48
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
49
|
+
import { render, renderHook, waitFor } from '@testing-library/react';
|
|
50
|
+
import { observable, makeObservable, runInAction, action } from 'mobx';
|
|
51
|
+
import { useService, ServiceProvider } from './service';
|
|
52
|
+
// 创建测试用的服务类
|
|
53
|
+
let TestService = (() => {
|
|
54
|
+
var _a, _TestService_count_accessor_storage, _TestService_users_accessor_storage, _TestService_inited_accessor_storage;
|
|
55
|
+
let _classSuper = Service;
|
|
56
|
+
let _instanceExtraInitializers = [];
|
|
57
|
+
let _count_decorators;
|
|
58
|
+
let _count_initializers = [];
|
|
59
|
+
let _count_extraInitializers = [];
|
|
60
|
+
let _users_decorators;
|
|
61
|
+
let _users_initializers = [];
|
|
62
|
+
let _users_extraInitializers = [];
|
|
63
|
+
let _inited_decorators;
|
|
64
|
+
let _inited_initializers = [];
|
|
65
|
+
let _inited_extraInitializers = [];
|
|
66
|
+
let _increment_decorators;
|
|
67
|
+
let _addUser_decorators;
|
|
68
|
+
return _a = class TestService extends _classSuper {
|
|
69
|
+
get count() { return __classPrivateFieldGet(this, _TestService_count_accessor_storage, "f"); }
|
|
70
|
+
set count(value) { __classPrivateFieldSet(this, _TestService_count_accessor_storage, value, "f"); }
|
|
71
|
+
get users() { return __classPrivateFieldGet(this, _TestService_users_accessor_storage, "f"); }
|
|
72
|
+
set users(value) { __classPrivateFieldSet(this, _TestService_users_accessor_storage, value, "f"); }
|
|
73
|
+
get inited() { return __classPrivateFieldGet(this, _TestService_inited_accessor_storage, "f"); }
|
|
74
|
+
set inited(value) { __classPrivateFieldSet(this, _TestService_inited_accessor_storage, value, "f"); }
|
|
75
|
+
constructor() {
|
|
76
|
+
super();
|
|
77
|
+
_TestService_count_accessor_storage.set(this, (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _count_initializers, 0)));
|
|
78
|
+
_TestService_users_accessor_storage.set(this, (__runInitializers(this, _count_extraInitializers), __runInitializers(this, _users_initializers, [])));
|
|
79
|
+
_TestService_inited_accessor_storage.set(this, (__runInitializers(this, _users_extraInitializers), __runInitializers(this, _inited_initializers, false)));
|
|
80
|
+
__runInitializers(this, _inited_extraInitializers);
|
|
81
|
+
makeObservable(this);
|
|
82
|
+
}
|
|
83
|
+
async init() {
|
|
84
|
+
runInAction(() => {
|
|
85
|
+
this.users = ['Alice', 'Bob'];
|
|
86
|
+
});
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
increment() {
|
|
90
|
+
this.count++;
|
|
91
|
+
}
|
|
92
|
+
addUser(name) {
|
|
93
|
+
this.users.push(name);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
_TestService_count_accessor_storage = new WeakMap(),
|
|
97
|
+
_TestService_users_accessor_storage = new WeakMap(),
|
|
98
|
+
_TestService_inited_accessor_storage = new WeakMap(),
|
|
99
|
+
(() => {
|
|
100
|
+
var _b;
|
|
101
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
102
|
+
_count_decorators = [observable];
|
|
103
|
+
_users_decorators = [observable];
|
|
104
|
+
_inited_decorators = [observable];
|
|
105
|
+
_increment_decorators = [action];
|
|
106
|
+
_addUser_decorators = [action];
|
|
107
|
+
__esDecorate(_a, null, _count_decorators, { kind: "accessor", name: "count", static: false, private: false, access: { has: obj => "count" in obj, get: obj => obj.count, set: (obj, value) => { obj.count = value; } }, metadata: _metadata }, _count_initializers, _count_extraInitializers);
|
|
108
|
+
__esDecorate(_a, null, _users_decorators, { kind: "accessor", name: "users", static: false, private: false, access: { has: obj => "users" in obj, get: obj => obj.users, set: (obj, value) => { obj.users = value; } }, metadata: _metadata }, _users_initializers, _users_extraInitializers);
|
|
109
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
110
|
+
__esDecorate(_a, null, _increment_decorators, { kind: "method", name: "increment", static: false, private: false, access: { has: obj => "increment" in obj, get: obj => obj.increment }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
111
|
+
__esDecorate(_a, null, _addUser_decorators, { kind: "method", name: "addUser", static: false, private: false, access: { has: obj => "addUser" in obj, get: obj => obj.addUser }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
112
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
113
|
+
})(),
|
|
114
|
+
_a;
|
|
115
|
+
})();
|
|
116
|
+
// 另一个测试服务类
|
|
117
|
+
let AnotherService = (() => {
|
|
118
|
+
var _a, _AnotherService_value_accessor_storage;
|
|
119
|
+
let _instanceExtraInitializers = [];
|
|
120
|
+
let _value_decorators;
|
|
121
|
+
let _value_initializers = [];
|
|
122
|
+
let _value_extraInitializers = [];
|
|
123
|
+
let _setValue_decorators;
|
|
124
|
+
return _a = class AnotherService {
|
|
125
|
+
get value() { return __classPrivateFieldGet(this, _AnotherService_value_accessor_storage, "f"); }
|
|
126
|
+
set value(value) { __classPrivateFieldSet(this, _AnotherService_value_accessor_storage, value, "f"); }
|
|
127
|
+
constructor() {
|
|
128
|
+
_AnotherService_value_accessor_storage.set(this, (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _value_initializers, 'initial')));
|
|
129
|
+
__runInitializers(this, _value_extraInitializers);
|
|
130
|
+
makeObservable(this);
|
|
131
|
+
}
|
|
132
|
+
setValue(newValue) {
|
|
133
|
+
this.value = newValue;
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
_AnotherService_value_accessor_storage = new WeakMap(),
|
|
137
|
+
(() => {
|
|
138
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
139
|
+
_value_decorators = [observable];
|
|
140
|
+
_setValue_decorators = [action];
|
|
141
|
+
__esDecorate(_a, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
142
|
+
__esDecorate(_a, null, _setValue_decorators, { kind: "method", name: "setValue", static: false, private: false, access: { has: obj => "setValue" in obj, get: obj => obj.setValue }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
143
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
144
|
+
})(),
|
|
145
|
+
_a;
|
|
146
|
+
})();
|
|
147
|
+
// 没有 init 方法的服务类
|
|
148
|
+
let SimpleService = (() => {
|
|
149
|
+
var _a, _SimpleService_data_accessor_storage;
|
|
150
|
+
let _data_decorators;
|
|
151
|
+
let _data_initializers = [];
|
|
152
|
+
let _data_extraInitializers = [];
|
|
153
|
+
return _a = class SimpleService {
|
|
154
|
+
get data() { return __classPrivateFieldGet(this, _SimpleService_data_accessor_storage, "f"); }
|
|
155
|
+
set data(value) { __classPrivateFieldSet(this, _SimpleService_data_accessor_storage, value, "f"); }
|
|
156
|
+
constructor() {
|
|
157
|
+
_SimpleService_data_accessor_storage.set(this, __runInitializers(this, _data_initializers, 'simple'));
|
|
158
|
+
__runInitializers(this, _data_extraInitializers);
|
|
159
|
+
makeObservable(this);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
_SimpleService_data_accessor_storage = new WeakMap(),
|
|
163
|
+
(() => {
|
|
164
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
165
|
+
_data_decorators = [observable];
|
|
166
|
+
__esDecorate(_a, null, _data_decorators, { kind: "accessor", name: "data", static: false, private: false, access: { has: obj => "data" in obj, get: obj => obj.data, set: (obj, value) => { obj.data = value; } }, metadata: _metadata }, _data_initializers, _data_extraInitializers);
|
|
167
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
168
|
+
})(),
|
|
169
|
+
_a;
|
|
170
|
+
})();
|
|
171
|
+
// 创建测试用的 Provider 组件
|
|
172
|
+
function createTestProvider(services) {
|
|
173
|
+
return function TestProvider({ children }) {
|
|
174
|
+
return (<ServiceProvider services={services}>
|
|
175
|
+
{children}
|
|
176
|
+
</ServiceProvider>);
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
describe('service integration', () => {
|
|
180
|
+
beforeEach(() => {
|
|
181
|
+
vi.clearAllMocks();
|
|
182
|
+
});
|
|
183
|
+
describe('useService', () => {
|
|
184
|
+
it('应该能够获取服务实例并选择数据', () => {
|
|
185
|
+
const TestProvider = createTestProvider([TestService]);
|
|
186
|
+
const { result } = renderHook(() => useService(TestService, service => service.count), { wrapper: TestProvider });
|
|
187
|
+
expect(result.current).toBe(0);
|
|
188
|
+
});
|
|
189
|
+
it('应该在服务数据变化时触发重新渲染', async () => {
|
|
190
|
+
const TestProvider = createTestProvider([TestService]);
|
|
191
|
+
const { result } = renderHook(() => ({
|
|
192
|
+
count: useService(TestService, service => service.count),
|
|
193
|
+
service: useService(TestService, service => service)
|
|
194
|
+
}), { wrapper: TestProvider });
|
|
195
|
+
expect(result.current.count).toBe(0);
|
|
196
|
+
// 修改服务数据
|
|
197
|
+
act(() => {
|
|
198
|
+
result.current.service.increment();
|
|
199
|
+
});
|
|
200
|
+
await waitFor(() => {
|
|
201
|
+
expect(result.current.count).toBe(1);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
it('应该能够选择复杂数据结构', async () => {
|
|
205
|
+
const TestProvider = createTestProvider([TestService]);
|
|
206
|
+
const { result } = renderHook(() => ({
|
|
207
|
+
userCount: useService(TestService, service => service.users.length),
|
|
208
|
+
service: useService(TestService, service => service)
|
|
209
|
+
}), { wrapper: TestProvider });
|
|
210
|
+
// 等待初始化完成
|
|
211
|
+
await waitFor(() => {
|
|
212
|
+
expect(result.current.userCount).toBe(2);
|
|
213
|
+
});
|
|
214
|
+
// 添加新用户
|
|
215
|
+
act(() => {
|
|
216
|
+
result.current.service.addUser('Charlie');
|
|
217
|
+
});
|
|
218
|
+
await waitFor(() => {
|
|
219
|
+
expect(result.current.userCount).toBe(3);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
it('应该在没有 ServiceProvider 时抛出错误', () => {
|
|
223
|
+
expect(() => {
|
|
224
|
+
renderHook(() => useService(TestService, service => service.count));
|
|
225
|
+
}).toThrow('Must be a child of ServiceProvider.');
|
|
226
|
+
});
|
|
227
|
+
it('应该能够处理多个服务', () => {
|
|
228
|
+
const TestProvider = createTestProvider([TestService, AnotherService]);
|
|
229
|
+
const { result } = renderHook(() => ({
|
|
230
|
+
count: useService(TestService, service => service.count),
|
|
231
|
+
value: useService(AnotherService, service => service.value)
|
|
232
|
+
}), { wrapper: TestProvider });
|
|
233
|
+
expect(result.current.count).toBe(0);
|
|
234
|
+
expect(result.current.value).toBe('initial');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
describe('ServiceProvider', () => {
|
|
238
|
+
it('应该能够渲染子组件', () => {
|
|
239
|
+
const { container } = render(<ServiceProvider services={[TestService]}>
|
|
240
|
+
<div data-testid="child">Test Child</div>
|
|
241
|
+
</ServiceProvider>);
|
|
242
|
+
expect(container.querySelector('[data-testid="child"]')).toBeTruthy();
|
|
243
|
+
});
|
|
244
|
+
it('应该能够嵌套使用', () => {
|
|
245
|
+
const ParentProvider = createTestProvider([TestService]);
|
|
246
|
+
const ChildProvider = createTestProvider([AnotherService]);
|
|
247
|
+
const { result } = renderHook(() => ({
|
|
248
|
+
count: useService(TestService, service => service.count),
|
|
249
|
+
value: useService(AnotherService, service => service.value)
|
|
250
|
+
}), {
|
|
251
|
+
wrapper: ({ children }) => (<ParentProvider>
|
|
252
|
+
<ChildProvider>
|
|
253
|
+
{children}
|
|
254
|
+
</ChildProvider>
|
|
255
|
+
</ParentProvider>)
|
|
256
|
+
});
|
|
257
|
+
expect(result.current.count).toBe(0);
|
|
258
|
+
expect(result.current.value).toBe('initial');
|
|
259
|
+
});
|
|
260
|
+
it('应该自动初始化实现了 Service 接口的服务', async () => {
|
|
261
|
+
const TestProvider = createTestProvider([TestService]);
|
|
262
|
+
const { result } = renderHook(() => useService(TestService, service => ({
|
|
263
|
+
users: service.users,
|
|
264
|
+
inited: service.inited
|
|
265
|
+
})), { wrapper: TestProvider });
|
|
266
|
+
// 等待初始化完成
|
|
267
|
+
await waitFor(() => {
|
|
268
|
+
expect(result.current.inited).toBe(true);
|
|
269
|
+
expect(result.current.users).toEqual(['Alice', 'Bob']);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
it('应该能够处理没有 init 方法的服务', () => {
|
|
273
|
+
const TestProvider = createTestProvider([SimpleService]);
|
|
274
|
+
const { result } = renderHook(() => useService(SimpleService, service => service.data), { wrapper: TestProvider });
|
|
275
|
+
expect(result.current).toBe('simple');
|
|
276
|
+
});
|
|
277
|
+
it('应该能够处理空的服务数组', () => {
|
|
278
|
+
const { container } = render(<ServiceProvider services={[]}>
|
|
279
|
+
<div data-testid="child">Test Child</div>
|
|
280
|
+
</ServiceProvider>);
|
|
281
|
+
expect(container.querySelector('[data-testid="child"]')).toBeTruthy();
|
|
282
|
+
});
|
|
283
|
+
it('应该能够处理初始化失败的服务', async () => {
|
|
284
|
+
// 创建一个初始化会失败的服务
|
|
285
|
+
let FailingService = (() => {
|
|
286
|
+
var _a, _FailingService_inited_accessor_storage;
|
|
287
|
+
let _classSuper = Service;
|
|
288
|
+
let _inited_decorators;
|
|
289
|
+
let _inited_initializers = [];
|
|
290
|
+
let _inited_extraInitializers = [];
|
|
291
|
+
return _a = class FailingService extends _classSuper {
|
|
292
|
+
get inited() { return __classPrivateFieldGet(this, _FailingService_inited_accessor_storage, "f"); }
|
|
293
|
+
set inited(value) { __classPrivateFieldSet(this, _FailingService_inited_accessor_storage, value, "f"); }
|
|
294
|
+
constructor() {
|
|
295
|
+
super();
|
|
296
|
+
_FailingService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
297
|
+
__runInitializers(this, _inited_extraInitializers);
|
|
298
|
+
makeObservable(this);
|
|
299
|
+
}
|
|
300
|
+
async init() {
|
|
301
|
+
throw new Error('Init failed');
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
_FailingService_inited_accessor_storage = new WeakMap(),
|
|
305
|
+
(() => {
|
|
306
|
+
var _b;
|
|
307
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
308
|
+
_inited_decorators = [observable];
|
|
309
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
310
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
311
|
+
})(),
|
|
312
|
+
_a;
|
|
313
|
+
})();
|
|
314
|
+
const TestProvider = createTestProvider([FailingService]);
|
|
315
|
+
const { result } = renderHook(() => useService(FailingService, service => service.inited), { wrapper: TestProvider });
|
|
316
|
+
// 等待初始化处理完成,即使失败也应该保持 inited 为 false
|
|
317
|
+
await waitFor(() => {
|
|
318
|
+
expect(result.current).toBe(false); // 因为初始化失败,inited 应该保持 false
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
describe('边界情况', () => {
|
|
323
|
+
it('useService 应该能够处理选择器返回 undefined 的情况', () => {
|
|
324
|
+
const TestProvider = createTestProvider([TestService]);
|
|
325
|
+
const { result } = renderHook(() => useService(TestService, () => undefined), { wrapper: TestProvider });
|
|
326
|
+
expect(result.current).toBeUndefined();
|
|
327
|
+
});
|
|
328
|
+
it('应该能够处理 selector 函数变化的情况', async () => {
|
|
329
|
+
const TestProvider = createTestProvider([TestService]);
|
|
330
|
+
let useCount = true;
|
|
331
|
+
const { result, rerender } = renderHook(() => ({
|
|
332
|
+
selected: useService(TestService, service => useCount ? service.count : service.users.length),
|
|
333
|
+
service: useService(TestService, service => service)
|
|
334
|
+
}), { wrapper: TestProvider });
|
|
335
|
+
expect(result.current.selected).toBe(0);
|
|
336
|
+
// 修改 selector
|
|
337
|
+
useCount = false;
|
|
338
|
+
rerender();
|
|
339
|
+
await waitFor(() => {
|
|
340
|
+
expect(result.current.selected).toBe(2); // users 初始化后包含 ['Alice', 'Bob'],长度为 2
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
describe('性能测试', () => {
|
|
345
|
+
it('useService 应该在 selector 返回相同值时不触发重渲染', async () => {
|
|
346
|
+
const TestProvider = createTestProvider([TestService]);
|
|
347
|
+
let renderCount = 0;
|
|
348
|
+
const { result } = renderHook(() => {
|
|
349
|
+
renderCount++;
|
|
350
|
+
return {
|
|
351
|
+
constValue: useService(TestService, () => 'constant'),
|
|
352
|
+
service: useService(TestService, service => service)
|
|
353
|
+
};
|
|
354
|
+
}, { wrapper: TestProvider });
|
|
355
|
+
const initialRenderCount = renderCount;
|
|
356
|
+
// 修改服务但选择器返回常量
|
|
357
|
+
act(() => {
|
|
358
|
+
result.current.service.increment();
|
|
359
|
+
});
|
|
360
|
+
// 等待一段时间确保没有额外渲染
|
|
361
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
362
|
+
// 由于选择器返回常量,理论上不应该有额外渲染
|
|
363
|
+
// 但 Mobx reaction 的具体行为可能会有差异
|
|
364
|
+
expect(renderCount).toBeGreaterThanOrEqual(initialRenderCount);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
});
|
package/output/signin/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { SigninApi } from './types';
|
|
3
|
+
export { useSignin } from './context';
|
|
4
|
+
export type { SigninApi, SigninStatus, SigninApiError, ApplySigninResult, CheckStatusResult, } from './types';
|
|
3
5
|
/**
|
|
4
6
|
* 登录弹窗文案配置
|
|
5
7
|
*/
|
|
@@ -53,6 +55,4 @@ interface SigninDialogProviderProps {
|
|
|
53
55
|
export declare const SigninDialogProvider: ((props: SigninDialogProviderProps) => import("react/jsx-runtime").JSX.Element) & {
|
|
54
56
|
displayName: string;
|
|
55
57
|
};
|
|
56
|
-
export type { SigninApi, SigninApiError, ApplySigninResult, CheckStatusResult, SigninStatus } from './types';
|
|
57
|
-
export { useSignin as useSignin } from './context';
|
|
58
58
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/signin/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAIxC;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,cAAc;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc;IACd,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iBAAiB;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,mBAAmB;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,aAAa;IACb,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,eAAe;IACf,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,cAAc;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,mBAAmB;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AA+UD;;;GAGG;AACH,UAAU,yBAAyB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,yBAAyB;IACzB,IAAI,EAAE,SAAS,CAAA;IACf,mBAAmB;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAA;CAC1B;AAED,eAAO,MAAM,oBAAoB,WAAoB,yBAAyB;;CAmC5E,CAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/signin/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAIxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAGrC,YAAY,EACV,SAAS,EACT,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,SAAS,CAAA;AAEhB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,cAAc;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc;IACd,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iBAAiB;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,mBAAmB;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,aAAa;IACb,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,eAAe;IACf,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,cAAc;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,mBAAmB;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AA+UD;;;GAGG;AACH,UAAU,yBAAyB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,yBAAyB;IACzB,IAAI,EAAE,SAAS,CAAA;IACf,mBAAmB;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAA;CAC1B;AAED,eAAO,MAAM,oBAAoB,WAAoB,yBAAyB;;CAmC5E,CAAA"}
|
package/output/signin/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { Button } from '../catalyst/button';
|
|
|
8
8
|
import { SigninService } from './service';
|
|
9
9
|
import { SigninContext } from './context';
|
|
10
10
|
import { ServiceProvider } from '../service';
|
|
11
|
+
export { useSignin } from './context';
|
|
11
12
|
/**
|
|
12
13
|
* 默认文案
|
|
13
14
|
*/
|
|
@@ -114,4 +115,3 @@ export const SigninDialogProvider = observer((props) => {
|
|
|
114
115
|
}), [handleSignin]);
|
|
115
116
|
return (_jsx(SigninContext.Provider, { value: contextValue, children: _jsxs(ServiceProvider, { services: [{ provide: SigninService, useValue: signinService.current }], children: [children, signinService.current && _jsx(SigninDialog, { service: signinService.current, texts: texts })] }) }));
|
|
116
117
|
});
|
|
117
|
-
export { useSignin as useSignin } from './context';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../source/signin/service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAsB,MAAM,sBAAsB,CAAA;AAElE,OAAO,KAAK,EAAE,SAAS,EAAwC,YAAY,EAAE,MAAM,SAAS,CAAA;AAE5F,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,CAAA;AAE/G;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6BAA6B;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,6BAA6B;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAA;CAChC;AAoBD,qBAAa,aAAc,SAAQ,OAAO;IACxC,SACgB,MAAM,EAAE,OAAO,CAAQ;IAEvC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,YAAY,CAA6C;IAGjE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IAGvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IAGxD,SACgB,YAAY,EAAE,kBAAkB,CAAS;IAEzD,SACgB,YAAY,EAAE,MAAM,CAAK;gBAE7B,SAAS,EAAE,SAAS,EAAE,MAAM,GAAE,mBAAwB;IAMlE,IACW,MAAM,IAAI,YAAY,GAAG,IAAI,CAIvC;IAED,IACW,SAAS,IAAI,MAAM,GAAG,IAAI,CAEpC;IAED,IACW,YAAY,IAAI,OAAO,CAEjC;IAED,IACW,eAAe,IAAI,OAAO,CAEpC;IAED;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,OAAO,CAAC,SAAS;IAgBjB;;OAEG;IACH,OAAO,CAAC,YAAY;IAuCpB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAU3B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAehB;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;YACW,iBAAiB;IAyB/B;;OAEG;YACW,iBAAiB;IA4G/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;IAEG,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA+CrC;;OAEG;IAEG,WAAW;IASjB;;OAEG;IAEG,MAAM;IAqBZ;;OAEG;IAEU,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../source/signin/service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAsB,MAAM,sBAAsB,CAAA;AAElE,OAAO,KAAK,EAAE,SAAS,EAAwC,YAAY,EAAE,MAAM,SAAS,CAAA;AAE5F,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,CAAA;AAE/G;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6BAA6B;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,6BAA6B;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAA;CAChC;AAoBD,qBAAa,aAAc,SAAQ,OAAO;IACxC,SACgB,MAAM,EAAE,OAAO,CAAQ;IAEvC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,YAAY,CAA6C;IAGjE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IAGvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IAGxD,SACgB,YAAY,EAAE,kBAAkB,CAAS;IAEzD,SACgB,YAAY,EAAE,MAAM,CAAK;gBAE7B,SAAS,EAAE,SAAS,EAAE,MAAM,GAAE,mBAAwB;IAMlE,IACW,MAAM,IAAI,YAAY,GAAG,IAAI,CAIvC;IAED,IACW,SAAS,IAAI,MAAM,GAAG,IAAI,CAEpC;IAED,IACW,YAAY,IAAI,OAAO,CAEjC;IAED,IACW,eAAe,IAAI,OAAO,CAEpC;IAED;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,OAAO,CAAC,SAAS;IAgBjB;;OAEG;IACH,OAAO,CAAC,YAAY;IAuCpB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAU3B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAehB;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;YACW,iBAAiB;IAyB/B;;OAEG;YACW,iBAAiB;IA4G/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;IAEG,KAAK,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA+CrC;;OAEG;IAEG,WAAW;IASjB;;OAEG;IAEG,MAAM;IAqBZ;;OAEG;IAEU,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IA2BrC;;OAEG;IACH,OAAO,CAAC,OAAO;IAWf;;OAEG;IACI,OAAO;CAIf"}
|
package/output/signin/service.js
CHANGED
|
@@ -449,8 +449,7 @@ let SigninService = (() => {
|
|
|
449
449
|
if (this.ticket || this.dialogStatus !== 'idle') {
|
|
450
450
|
this.saveState();
|
|
451
451
|
}
|
|
452
|
-
}, { delay: 100 }
|
|
453
|
-
));
|
|
452
|
+
}, { delay: 100 }));
|
|
454
453
|
// 尝试恢复持久化状态
|
|
455
454
|
const restored = this.restoreState();
|
|
456
455
|
if (restored) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { SystemErrorType, UserErrorType } from '@taicode/common-base';
|
|
3
2
|
import 'react-toastify/dist/ReactToastify.css';
|
|
4
3
|
interface ToastCatcherProps {
|
|
5
4
|
defaultMessage?: string;
|
|
6
5
|
children: React.ReactNode;
|
|
7
|
-
messages: Partial<Record<
|
|
6
|
+
messages: Partial<Record<string, string>>;
|
|
8
7
|
}
|
|
9
8
|
/** 自动捕获和处理异步错误,无法识别的错误会保持原样 */
|
|
10
9
|
export declare function Toaster(props: ToastCatcherProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/toaster/index.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/toaster/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAA;AAIhD,OAAO,uCAAuC,CAAA;AAE9C,UAAU,iBAAiB;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAC1C;AAED,+BAA+B;AAC/B,wBAAgB,OAAO,CAAC,KAAK,EAAE,iBAAiB,2CAuE/C;yBAvEe,OAAO;wBAgFE,KAAK,CAAC,SAAS;yBAId,KAAK,CAAC,SAAS;2BAIb,KAAK,CAAC,SAAS;2BAIf,KAAK,CAAC,SAAS"}
|
package/output/toaster/index.js
CHANGED
|
@@ -56,12 +56,6 @@ export function Toaster(props) {
|
|
|
56
56
|
};
|
|
57
57
|
}, []);
|
|
58
58
|
return (_jsxs(_Fragment, { children: [props.children, _jsx(ToastContainer, { newestOnTop: true, hideProgressBar: true, closeButton: false, position: "top-center" })] }));
|
|
59
|
-
function ErrorIcon() {
|
|
60
|
-
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", className: "size-6 text-red-500", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" }) }));
|
|
61
|
-
}
|
|
62
|
-
function ErrorToast(props) {
|
|
63
|
-
return (_jsxs("div", { className: "flex items-center space-x-3", role: "alert", children: [_jsx(ErrorIcon, {}), _jsx("div", { className: "text-sm font-medium text-gray-900 dark:text-gray-100" })] }));
|
|
64
|
-
}
|
|
65
59
|
}
|
|
66
60
|
Toaster.info = (content) => {
|
|
67
61
|
baseToast.info(content);
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { renderHook, act } from '@testing-library/react';
|
|
3
|
+
import { observable, action } from 'mobx';
|
|
4
|
+
import { useObserver } from './use-observer';
|
|
5
|
+
describe('useObserver', () => {
|
|
6
|
+
it('应该返回初始值当没有selector时', () => {
|
|
7
|
+
const initialValue = '初始值';
|
|
8
|
+
const { result } = renderHook(() => useObserver(initialValue));
|
|
9
|
+
expect(result.current).toBe(initialValue);
|
|
10
|
+
});
|
|
11
|
+
it('应该使用selector转换值', () => {
|
|
12
|
+
const initialValue = { count: 5 };
|
|
13
|
+
const selector = (value) => value.count * 2;
|
|
14
|
+
const { result } = renderHook(() => useObserver(initialValue, selector));
|
|
15
|
+
expect(result.current).toBe(10);
|
|
16
|
+
});
|
|
17
|
+
it('应该在观察对象变化时重新渲染', async () => {
|
|
18
|
+
const observableStore = observable({
|
|
19
|
+
count: 0,
|
|
20
|
+
increment: action(() => {
|
|
21
|
+
observableStore.count++;
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
const { result } = renderHook(() => useObserver(observableStore, (store) => store.count));
|
|
25
|
+
expect(result.current).toBe(0);
|
|
26
|
+
// 改变observable的值
|
|
27
|
+
act(() => {
|
|
28
|
+
observableStore.increment();
|
|
29
|
+
});
|
|
30
|
+
// 等待React更新
|
|
31
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
32
|
+
expect(result.current).toBe(1);
|
|
33
|
+
});
|
|
34
|
+
it('应该处理复杂的selector函数', () => {
|
|
35
|
+
const store = observable({
|
|
36
|
+
users: [
|
|
37
|
+
{ id: 1, name: '张三', active: true },
|
|
38
|
+
{ id: 2, name: '李四', active: false },
|
|
39
|
+
{ id: 3, name: '王五', active: true }
|
|
40
|
+
]
|
|
41
|
+
});
|
|
42
|
+
const selector = (store) => store.users.filter((user) => user.active).map((user) => user.name);
|
|
43
|
+
const { result } = renderHook(() => useObserver(store, selector));
|
|
44
|
+
expect(result.current).toEqual(['张三', '王五']);
|
|
45
|
+
});
|
|
46
|
+
it('应该在observable数组变化时重新渲染', async () => {
|
|
47
|
+
const store = observable({
|
|
48
|
+
items: ['项目1', '项目2'],
|
|
49
|
+
addItem: action((item) => {
|
|
50
|
+
store.items.push(item);
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
const { result } = renderHook(() => useObserver(store, (store) => store.items.length));
|
|
54
|
+
expect(result.current).toBe(2);
|
|
55
|
+
act(() => {
|
|
56
|
+
store.addItem('项目3');
|
|
57
|
+
});
|
|
58
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
59
|
+
expect(result.current).toBe(3);
|
|
60
|
+
});
|
|
61
|
+
it('应该处理null值的初始值', () => {
|
|
62
|
+
const { result } = renderHook(() => useObserver(null));
|
|
63
|
+
expect(result.current).toBeNull();
|
|
64
|
+
});
|
|
65
|
+
it('应该处理undefined值的初始值', () => {
|
|
66
|
+
const { result } = renderHook(() => useObserver(undefined));
|
|
67
|
+
expect(result.current).toBeUndefined();
|
|
68
|
+
});
|
|
69
|
+
it('应该在selector为null时返回原值', () => {
|
|
70
|
+
const initialValue = { data: '测试数据' };
|
|
71
|
+
const { result } = renderHook(() => useObserver(initialValue, null));
|
|
72
|
+
expect(result.current).toBe(initialValue);
|
|
73
|
+
});
|
|
74
|
+
it('应该处理嵌套observable对象', async () => {
|
|
75
|
+
const store = observable({
|
|
76
|
+
user: {
|
|
77
|
+
profile: {
|
|
78
|
+
name: '测试用户',
|
|
79
|
+
email: 'test@example.com'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
updateName: action((name) => {
|
|
83
|
+
store.user.profile.name = name;
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
const { result } = renderHook(() => useObserver(store, (store) => store.user.profile.name));
|
|
87
|
+
expect(result.current).toBe('测试用户');
|
|
88
|
+
act(() => {
|
|
89
|
+
store.updateName('新用户名');
|
|
90
|
+
});
|
|
91
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
92
|
+
expect(result.current).toBe('新用户名');
|
|
93
|
+
});
|
|
94
|
+
it('应该处理多个observable属性的变化', async () => {
|
|
95
|
+
const store = observable({
|
|
96
|
+
firstName: '张',
|
|
97
|
+
lastName: '三',
|
|
98
|
+
updateFirstName: action((name) => {
|
|
99
|
+
store.firstName = name;
|
|
100
|
+
}),
|
|
101
|
+
updateLastName: action((name) => {
|
|
102
|
+
store.lastName = name;
|
|
103
|
+
})
|
|
104
|
+
});
|
|
105
|
+
const { result } = renderHook(() => useObserver(store, (store) => `${store.firstName}${store.lastName}`));
|
|
106
|
+
expect(result.current).toBe('张三');
|
|
107
|
+
act(() => {
|
|
108
|
+
store.updateFirstName('李');
|
|
109
|
+
});
|
|
110
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
111
|
+
expect(result.current).toBe('李三');
|
|
112
|
+
act(() => {
|
|
113
|
+
store.updateLastName('四');
|
|
114
|
+
});
|
|
115
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
116
|
+
expect(result.current).toBe('李四');
|
|
117
|
+
});
|
|
118
|
+
it('应该在组件卸载时清理reaction', () => {
|
|
119
|
+
const store = observable({ count: 0 });
|
|
120
|
+
const { unmount } = renderHook(() => useObserver(store, (store) => store.count));
|
|
121
|
+
// 卸载组件应该不会抛出错误
|
|
122
|
+
expect(() => unmount()).not.toThrow();
|
|
123
|
+
});
|
|
124
|
+
it('应该处理boolean类型的返回值', () => {
|
|
125
|
+
const store = observable({ isActive: true });
|
|
126
|
+
const { result } = renderHook(() => useObserver(store, (store) => store.isActive));
|
|
127
|
+
expect(result.current).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
it('应该处理数字类型的返回值', () => {
|
|
130
|
+
const store = observable({ count: 42 });
|
|
131
|
+
const { result } = renderHook(() => useObserver(store, (store) => store.count));
|
|
132
|
+
expect(result.current).toBe(42);
|
|
133
|
+
});
|
|
134
|
+
});
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LoadingButtonDemo.d.ts","sourceRoot":"","sources":["../../source/loading-button/LoadingButtonDemo.tsx"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,iBAAiB,4CAsKhC"}
|