@blac/core 2.0.0-rn-12 → 2.0.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/README.md +178 -244
- package/dist/index.cjs +2993 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1895 -0
- package/dist/index.d.ts +1715 -644
- package/dist/index.js +2884 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -18
- package/dist/index.cjs.js +0 -11
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.es.js +0 -1588
- package/dist/index.es.js.map +0 -1
- package/src/Blac.ts +0 -789
- package/src/BlacObserver.ts +0 -186
- package/src/Bloc.ts +0 -138
- package/src/BlocBase.ts +0 -589
- package/src/Cubit.ts +0 -83
- package/src/__tests__/Blac.config.test.ts +0 -86
- package/src/__tests__/Bloc.event.test.ts +0 -419
- package/src/__tests__/BlocBase.lifecycle.test.ts +0 -441
- package/src/__tests__/Cubit.test.ts +0 -473
- package/src/__tests__/props.test.ts +0 -256
- package/src/adapter/BlacAdapter.ts +0 -327
- package/src/adapter/ConsumerTracker.ts +0 -241
- package/src/adapter/ProxyFactory.ts +0 -308
- package/src/adapter/__tests__/BlacAdapter.test.ts +0 -561
- package/src/adapter/__tests__/ConsumerTracker.test.ts +0 -562
- package/src/adapter/__tests__/ProxyFactory.test.ts +0 -655
- package/src/adapter/index.ts +0 -3
- package/src/events.ts +0 -3
- package/src/index.ts +0 -17
- package/src/testing.ts +0 -252
- package/src/types.ts +0 -91
- package/src/utils/shallowEqual.ts +0 -40
- package/src/utils/uuid.ts +0 -22
|
@@ -1,561 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { BlacAdapter } from '../BlacAdapter';
|
|
3
|
-
import { Cubit } from '../../Cubit';
|
|
4
|
-
import { Blac } from '../../Blac';
|
|
5
|
-
|
|
6
|
-
// Test Cubit implementations
|
|
7
|
-
class CounterCubit extends Cubit<number> {
|
|
8
|
-
static isolated = false;
|
|
9
|
-
static keepAlive = false;
|
|
10
|
-
|
|
11
|
-
constructor() {
|
|
12
|
-
super(0);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
increment = () => this.emit(this.state + 1);
|
|
16
|
-
decrement = () => this.emit(this.state - 1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface UserState {
|
|
20
|
-
name: string;
|
|
21
|
-
age: number;
|
|
22
|
-
profile: {
|
|
23
|
-
email: string;
|
|
24
|
-
preferences: {
|
|
25
|
-
theme: string;
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
class UserCubit extends Cubit<UserState> {
|
|
31
|
-
constructor() {
|
|
32
|
-
super({
|
|
33
|
-
name: 'John',
|
|
34
|
-
age: 30,
|
|
35
|
-
profile: {
|
|
36
|
-
email: 'john@example.com',
|
|
37
|
-
preferences: {
|
|
38
|
-
theme: 'light',
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
updateName = (name: string) => {
|
|
45
|
-
this.patch({ name });
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
updateTheme = (theme: string) => {
|
|
49
|
-
this.emit({
|
|
50
|
-
...this.state,
|
|
51
|
-
profile: {
|
|
52
|
-
...this.state.profile,
|
|
53
|
-
preferences: { theme },
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
describe('BlacAdapter', () => {
|
|
60
|
-
let blacInstance: Blac;
|
|
61
|
-
let componentRef: { current: object };
|
|
62
|
-
|
|
63
|
-
beforeEach(() => {
|
|
64
|
-
blacInstance = new Blac({ __unsafe_ignore_singleton: true });
|
|
65
|
-
componentRef = { current: {} };
|
|
66
|
-
vi.clearAllMocks();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
afterEach(() => {
|
|
70
|
-
vi.restoreAllMocks();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe('Initialization and Instance Management', () => {
|
|
74
|
-
it('should create adapter with proper initialization', () => {
|
|
75
|
-
const adapter = new BlacAdapter(
|
|
76
|
-
{ componentRef, blocConstructor: CounterCubit },
|
|
77
|
-
{ id: 'test-counter' },
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
expect(adapter.id).toMatch(/^consumer-/);
|
|
81
|
-
expect(adapter.blocConstructor).toBe(CounterCubit);
|
|
82
|
-
expect(adapter.componentRef).toBe(componentRef);
|
|
83
|
-
expect(adapter.blocInstance).toBeInstanceOf(CounterCubit);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should retrieve existing bloc instance when not isolated', () => {
|
|
87
|
-
// Create first adapter
|
|
88
|
-
const adapter1 = new BlacAdapter(
|
|
89
|
-
{ componentRef, blocConstructor: CounterCubit },
|
|
90
|
-
{ id: 'shared-counter' },
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
// Create second adapter with same id
|
|
94
|
-
const componentRef2 = { current: {} };
|
|
95
|
-
const adapter2 = new BlacAdapter(
|
|
96
|
-
{ componentRef: componentRef2, blocConstructor: CounterCubit },
|
|
97
|
-
{ id: 'shared-counter' },
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
// Should share the same bloc instance
|
|
101
|
-
expect(adapter1.blocInstance).toBe(adapter2.blocInstance);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should pass props to bloc constructor', () => {
|
|
105
|
-
class PropsCubit extends Cubit<string, { initialValue: string }> {
|
|
106
|
-
constructor(
|
|
107
|
-
public override props: { initialValue: string } | null = null,
|
|
108
|
-
) {
|
|
109
|
-
super(props?.initialValue || 'default');
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const adapter = new BlacAdapter(
|
|
114
|
-
{ componentRef, blocConstructor: PropsCubit as any },
|
|
115
|
-
{ props: { initialValue: 'custom' } },
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
expect(adapter.blocInstance.state).toBe('custom');
|
|
119
|
-
expect(adapter.blocInstance.props).toEqual({ initialValue: 'custom' });
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe('Dependency Tracking - Explicit Dependencies', () => {
|
|
124
|
-
it('should track dependencies when dependencies function is provided', () => {
|
|
125
|
-
const dependencyFn = vi.fn((bloc: UserCubit) => [
|
|
126
|
-
bloc.state.name,
|
|
127
|
-
bloc.state.age,
|
|
128
|
-
]);
|
|
129
|
-
|
|
130
|
-
const adapter = new BlacAdapter(
|
|
131
|
-
{ componentRef, blocConstructor: UserCubit },
|
|
132
|
-
{ dependencies: dependencyFn },
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
expect(dependencyFn).toHaveBeenCalledWith(adapter.blocInstance);
|
|
136
|
-
expect(adapter.options?.dependencies).toBe(dependencyFn);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('should only trigger onChange when dependencies change', () => {
|
|
140
|
-
const onChange = vi.fn();
|
|
141
|
-
const adapter = new BlacAdapter(
|
|
142
|
-
{ componentRef, blocConstructor: UserCubit },
|
|
143
|
-
{ dependencies: (bloc) => [bloc.state.name] },
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
const unsubscribe = adapter.createSubscription({ onChange });
|
|
147
|
-
|
|
148
|
-
// Change name - should trigger
|
|
149
|
-
adapter.blocInstance.updateName('Jane');
|
|
150
|
-
expect(onChange).toHaveBeenCalledTimes(1);
|
|
151
|
-
|
|
152
|
-
// Change theme - should NOT trigger (not in dependencies)
|
|
153
|
-
onChange.mockClear();
|
|
154
|
-
adapter.blocInstance.updateTheme('dark');
|
|
155
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
156
|
-
|
|
157
|
-
unsubscribe();
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('should handle dependency array changes correctly', () => {
|
|
161
|
-
const onChange = vi.fn();
|
|
162
|
-
let depCount = 1;
|
|
163
|
-
|
|
164
|
-
const adapter = new BlacAdapter(
|
|
165
|
-
{ componentRef, blocConstructor: CounterCubit },
|
|
166
|
-
{
|
|
167
|
-
dependencies: (bloc) => {
|
|
168
|
-
// Variable length dependency array
|
|
169
|
-
const deps = [];
|
|
170
|
-
for (let i = 0; i < depCount; i++) {
|
|
171
|
-
deps.push(bloc.state);
|
|
172
|
-
}
|
|
173
|
-
return deps;
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
const unsubscribe = adapter.createSubscription({ onChange });
|
|
179
|
-
|
|
180
|
-
// Initial change
|
|
181
|
-
adapter.blocInstance.increment();
|
|
182
|
-
expect(onChange).toHaveBeenCalledTimes(1);
|
|
183
|
-
|
|
184
|
-
// Change dependency array length
|
|
185
|
-
depCount = 2;
|
|
186
|
-
onChange.mockClear();
|
|
187
|
-
adapter.blocInstance.increment();
|
|
188
|
-
expect(onChange).toHaveBeenCalledTimes(1); // Length change triggers update
|
|
189
|
-
|
|
190
|
-
unsubscribe();
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe('Dependency Tracking - Proxy-based (Automatic)', () => {
|
|
195
|
-
it('should track state access through proxy when no explicit dependencies', () => {
|
|
196
|
-
const adapter = new BlacAdapter({
|
|
197
|
-
componentRef,
|
|
198
|
-
blocConstructor: UserCubit,
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// Access state through proxy
|
|
202
|
-
const proxyState = adapter.getProxyState(adapter.blocInstance.state);
|
|
203
|
-
const name = proxyState.name;
|
|
204
|
-
const email = proxyState.profile.email;
|
|
205
|
-
|
|
206
|
-
// Check tracked dependencies
|
|
207
|
-
const dependencies = adapter.getConsumerDependencies(
|
|
208
|
-
componentRef.current,
|
|
209
|
-
);
|
|
210
|
-
expect(dependencies?.statePaths).toContain('name');
|
|
211
|
-
expect(dependencies?.statePaths).toContain('profile.email');
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it('should track class property access through proxy', () => {
|
|
215
|
-
class GetterCubit extends Cubit<number> {
|
|
216
|
-
constructor() {
|
|
217
|
-
super(0);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
get doubled() {
|
|
221
|
-
return this.state * 2;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
get isPositive() {
|
|
225
|
-
return this.state > 0;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const adapter = new BlacAdapter({
|
|
230
|
-
componentRef,
|
|
231
|
-
blocConstructor: GetterCubit,
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
// Access getters through proxy
|
|
235
|
-
const proxyBloc = adapter.getProxyBlocInstance();
|
|
236
|
-
const doubled = proxyBloc.doubled;
|
|
237
|
-
const isPositive = proxyBloc.isPositive;
|
|
238
|
-
|
|
239
|
-
// Check tracked dependencies
|
|
240
|
-
const dependencies = adapter.getConsumerDependencies(
|
|
241
|
-
componentRef.current,
|
|
242
|
-
);
|
|
243
|
-
expect(dependencies?.classPaths).toContain('doubled');
|
|
244
|
-
expect(dependencies?.classPaths).toContain('isPositive');
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it('should only notify when tracked values change', () => {
|
|
248
|
-
const onChange = vi.fn();
|
|
249
|
-
const adapter = new BlacAdapter({
|
|
250
|
-
componentRef,
|
|
251
|
-
blocConstructor: UserCubit,
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// Track specific property access
|
|
255
|
-
adapter.resetConsumerTracking();
|
|
256
|
-
const proxyState = adapter.getProxyState(adapter.blocInstance.state);
|
|
257
|
-
const name = proxyState.name; // Only track name
|
|
258
|
-
|
|
259
|
-
// Mark as rendered to enable dependency checking
|
|
260
|
-
adapter.updateLastNotified(componentRef.current);
|
|
261
|
-
|
|
262
|
-
const unsubscribe = adapter.createSubscription({ onChange });
|
|
263
|
-
|
|
264
|
-
// Change tracked property - should notify
|
|
265
|
-
adapter.blocInstance.updateName('Jane');
|
|
266
|
-
expect(onChange).toHaveBeenCalledTimes(1);
|
|
267
|
-
|
|
268
|
-
// Change untracked property - should NOT notify
|
|
269
|
-
onChange.mockClear();
|
|
270
|
-
adapter.blocInstance.updateTheme('dark');
|
|
271
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
272
|
-
|
|
273
|
-
unsubscribe();
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it('should handle nested property tracking', () => {
|
|
277
|
-
const adapter = new BlacAdapter({
|
|
278
|
-
componentRef,
|
|
279
|
-
blocConstructor: UserCubit,
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
// Access nested property
|
|
283
|
-
const proxyState = adapter.getProxyState(adapter.blocInstance.state);
|
|
284
|
-
const theme = proxyState.profile.preferences.theme;
|
|
285
|
-
|
|
286
|
-
// Check tracked path
|
|
287
|
-
const dependencies = adapter.getConsumerDependencies(
|
|
288
|
-
componentRef.current,
|
|
289
|
-
);
|
|
290
|
-
expect(dependencies?.statePaths).toContain('profile.preferences.theme');
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
describe('Lifecycle Management', () => {
|
|
295
|
-
it('should handle mount and unmount correctly', () => {
|
|
296
|
-
const onMount = vi.fn();
|
|
297
|
-
const onUnmount = vi.fn();
|
|
298
|
-
|
|
299
|
-
const adapter = new BlacAdapter(
|
|
300
|
-
{ componentRef, blocConstructor: CounterCubit },
|
|
301
|
-
{ onMount, onUnmount },
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
// Mount
|
|
305
|
-
adapter.mount();
|
|
306
|
-
expect(onMount).toHaveBeenCalledWith(adapter.blocInstance);
|
|
307
|
-
expect(adapter.calledOnMount).toBe(true);
|
|
308
|
-
expect(adapter.blocInstance._consumers.has(adapter.id)).toBe(true);
|
|
309
|
-
|
|
310
|
-
// Mount again - should not call onMount twice
|
|
311
|
-
adapter.mount();
|
|
312
|
-
expect(onMount).toHaveBeenCalledTimes(1);
|
|
313
|
-
|
|
314
|
-
// Unmount
|
|
315
|
-
adapter.unmount();
|
|
316
|
-
expect(onUnmount).toHaveBeenCalledWith(adapter.blocInstance);
|
|
317
|
-
expect(adapter.blocInstance._consumers.has(adapter.id)).toBe(false);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
it('should handle onMount errors', () => {
|
|
321
|
-
const onMount = vi.fn(() => {
|
|
322
|
-
throw new Error('Mount error');
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
const adapter = new BlacAdapter(
|
|
326
|
-
{ componentRef, blocConstructor: CounterCubit },
|
|
327
|
-
{ onMount },
|
|
328
|
-
);
|
|
329
|
-
|
|
330
|
-
expect(() => adapter.mount()).toThrow('Mount error');
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('should not throw on unmount errors', () => {
|
|
334
|
-
const onUnmount = vi.fn(() => {
|
|
335
|
-
throw new Error('Unmount error');
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
const adapter = new BlacAdapter(
|
|
339
|
-
{ componentRef, blocConstructor: CounterCubit },
|
|
340
|
-
{ onUnmount },
|
|
341
|
-
);
|
|
342
|
-
|
|
343
|
-
adapter.mount();
|
|
344
|
-
|
|
345
|
-
// Should not throw
|
|
346
|
-
expect(() => adapter.unmount()).not.toThrow();
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
it('should refresh dependencies on mount', () => {
|
|
350
|
-
const dependencies = vi.fn((bloc: CounterCubit) => [bloc.state]);
|
|
351
|
-
|
|
352
|
-
const adapter = new BlacAdapter(
|
|
353
|
-
{ componentRef, blocConstructor: CounterCubit },
|
|
354
|
-
{ dependencies },
|
|
355
|
-
);
|
|
356
|
-
|
|
357
|
-
expect(dependencies).toHaveBeenCalledTimes(1); // Initial call
|
|
358
|
-
|
|
359
|
-
adapter.mount();
|
|
360
|
-
expect(dependencies).toHaveBeenCalledTimes(2); // Called again on mount
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
describe('Subscription Management', () => {
|
|
365
|
-
it('should create and cleanup subscriptions', () => {
|
|
366
|
-
const adapter = new BlacAdapter({
|
|
367
|
-
componentRef,
|
|
368
|
-
blocConstructor: CounterCubit,
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
const onChange = vi.fn();
|
|
372
|
-
const unsubscribe = adapter.createSubscription({ onChange });
|
|
373
|
-
|
|
374
|
-
expect(adapter.blocInstance._observer.size).toBe(1);
|
|
375
|
-
|
|
376
|
-
// Trigger state change
|
|
377
|
-
adapter.blocInstance.increment();
|
|
378
|
-
expect(onChange).toHaveBeenCalled();
|
|
379
|
-
|
|
380
|
-
// Cleanup
|
|
381
|
-
unsubscribe();
|
|
382
|
-
expect(adapter.blocInstance._observer.size).toBe(0);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it('should handle first render without dependencies check', () => {
|
|
386
|
-
const onChange = vi.fn();
|
|
387
|
-
const adapter = new BlacAdapter({
|
|
388
|
-
componentRef,
|
|
389
|
-
blocConstructor: UserCubit,
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
// Don't mark as rendered yet
|
|
393
|
-
const unsubscribe = adapter.createSubscription({ onChange });
|
|
394
|
-
|
|
395
|
-
// First state change should always notify
|
|
396
|
-
adapter.blocInstance.updateName('Jane');
|
|
397
|
-
expect(onChange).toHaveBeenCalledTimes(1);
|
|
398
|
-
|
|
399
|
-
unsubscribe();
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
describe('Proxy Creation and Management', () => {
|
|
404
|
-
it('should return raw state when using explicit dependencies', () => {
|
|
405
|
-
const adapter = new BlacAdapter(
|
|
406
|
-
{ componentRef, blocConstructor: UserCubit },
|
|
407
|
-
{ dependencies: (bloc) => [bloc.state.name] },
|
|
408
|
-
);
|
|
409
|
-
|
|
410
|
-
const state = adapter.getProxyState(adapter.blocInstance.state);
|
|
411
|
-
|
|
412
|
-
// Should be raw state, not proxy
|
|
413
|
-
expect(state).toBe(adapter.blocInstance.state);
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
it('should return raw bloc instance when using explicit dependencies', () => {
|
|
417
|
-
const adapter = new BlacAdapter(
|
|
418
|
-
{ componentRef, blocConstructor: CounterCubit },
|
|
419
|
-
{ dependencies: (bloc) => [bloc.state] },
|
|
420
|
-
);
|
|
421
|
-
|
|
422
|
-
const blocInstance = adapter.getProxyBlocInstance();
|
|
423
|
-
|
|
424
|
-
// Should be raw instance, not proxy
|
|
425
|
-
expect(blocInstance).toBe(adapter.blocInstance);
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it('should create proxies when not using explicit dependencies', () => {
|
|
429
|
-
const adapter = new BlacAdapter({
|
|
430
|
-
componentRef,
|
|
431
|
-
blocConstructor: UserCubit,
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
const proxyState = adapter.getProxyState(adapter.blocInstance.state);
|
|
435
|
-
const proxyBloc = adapter.getProxyBlocInstance();
|
|
436
|
-
|
|
437
|
-
// Should be proxies
|
|
438
|
-
expect(proxyState).not.toBe(adapter.blocInstance.state);
|
|
439
|
-
expect(proxyBloc).not.toBe(adapter.blocInstance);
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
describe('Consumer Tracking Integration', () => {
|
|
444
|
-
it('should properly track and validate consumers', () => {
|
|
445
|
-
const adapter = new BlacAdapter({
|
|
446
|
-
componentRef,
|
|
447
|
-
blocConstructor: CounterCubit,
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
adapter.mount();
|
|
451
|
-
|
|
452
|
-
// Consumer should be tracked
|
|
453
|
-
const hasConsumer = adapter.shouldNotifyConsumer(
|
|
454
|
-
componentRef.current,
|
|
455
|
-
new Set(['state']),
|
|
456
|
-
);
|
|
457
|
-
expect(hasConsumer).toBe(true); // First render always notifies
|
|
458
|
-
|
|
459
|
-
// Update tracking info
|
|
460
|
-
adapter.updateLastNotified(componentRef.current);
|
|
461
|
-
|
|
462
|
-
// Now should use dependency tracking
|
|
463
|
-
const shouldNotify = adapter.shouldNotifyConsumer(
|
|
464
|
-
componentRef.current,
|
|
465
|
-
new Set(['untracked']),
|
|
466
|
-
);
|
|
467
|
-
expect(shouldNotify).toBe(false);
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
it('should reset consumer tracking', () => {
|
|
471
|
-
const adapter = new BlacAdapter({
|
|
472
|
-
componentRef,
|
|
473
|
-
blocConstructor: UserCubit,
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// Track some accesses
|
|
477
|
-
adapter.trackAccess(componentRef.current, 'state', 'name', 'John');
|
|
478
|
-
adapter.trackAccess(componentRef.current, 'state', 'age', 30);
|
|
479
|
-
|
|
480
|
-
const depsBefore = adapter.getConsumerDependencies(componentRef.current);
|
|
481
|
-
expect(depsBefore?.statePaths.length).toBe(2);
|
|
482
|
-
|
|
483
|
-
// Reset tracking
|
|
484
|
-
adapter.resetConsumerTracking();
|
|
485
|
-
|
|
486
|
-
const depsAfter = adapter.getConsumerDependencies(componentRef.current);
|
|
487
|
-
expect(depsAfter?.statePaths.length).toBe(0);
|
|
488
|
-
});
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
describe('Options Updates', () => {
|
|
492
|
-
it('should update options after initialization', () => {
|
|
493
|
-
const adapter = new BlacAdapter({
|
|
494
|
-
componentRef,
|
|
495
|
-
blocConstructor: CounterCubit,
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
expect(adapter.options).toBeUndefined();
|
|
499
|
-
|
|
500
|
-
const newOptions = {
|
|
501
|
-
dependencies: (bloc: CounterCubit) => [bloc.state],
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
adapter.options = newOptions;
|
|
505
|
-
expect(adapter.options).toBe(newOptions);
|
|
506
|
-
});
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
describe('Edge Cases and Error Handling', () => {
|
|
510
|
-
it('should handle missing componentRef gracefully', () => {
|
|
511
|
-
// Can't use null as WeakMap key, so test with an empty object instead
|
|
512
|
-
const emptyRef = {};
|
|
513
|
-
const adapter = new BlacAdapter({
|
|
514
|
-
componentRef: { current: emptyRef },
|
|
515
|
-
blocConstructor: CounterCubit,
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
// Should not throw
|
|
519
|
-
expect(() => adapter.mount()).not.toThrow();
|
|
520
|
-
expect(() =>
|
|
521
|
-
adapter.trackAccess(emptyRef, 'state', 'test'),
|
|
522
|
-
).not.toThrow();
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
it('should handle rapid mount/unmount cycles', () => {
|
|
526
|
-
const adapter = new BlacAdapter({
|
|
527
|
-
componentRef,
|
|
528
|
-
blocConstructor: CounterCubit,
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
// Rapid mount/unmount
|
|
532
|
-
for (let i = 0; i < 10; i++) {
|
|
533
|
-
adapter.mount();
|
|
534
|
-
adapter.unmount();
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
expect(adapter.blocInstance._consumers.size).toBe(0);
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
it('should handle value changes with Object.is semantics', () => {
|
|
541
|
-
const onChange = vi.fn();
|
|
542
|
-
const adapter = new BlacAdapter(
|
|
543
|
-
{ componentRef, blocConstructor: CounterCubit },
|
|
544
|
-
{ dependencies: (bloc) => [bloc.state] },
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
const unsubscribe = adapter.createSubscription({ onChange });
|
|
548
|
-
|
|
549
|
-
// Set to NaN
|
|
550
|
-
adapter.blocInstance.emit(NaN);
|
|
551
|
-
expect(onChange).toHaveBeenCalledTimes(1);
|
|
552
|
-
|
|
553
|
-
// Set to NaN again - should not trigger (Object.is(NaN, NaN) === true)
|
|
554
|
-
onChange.mockClear();
|
|
555
|
-
adapter.blocInstance.emit(NaN);
|
|
556
|
-
expect(onChange).not.toHaveBeenCalled();
|
|
557
|
-
|
|
558
|
-
unsubscribe();
|
|
559
|
-
});
|
|
560
|
-
});
|
|
561
|
-
});
|