@esmx/router-vue 3.0.0-rc.103
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 +21 -0
- package/README.md +570 -0
- package/README.zh-CN.md +570 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +13 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.mjs +216 -0
- package/dist/plugin.d.ts +61 -0
- package/dist/plugin.mjs +41 -0
- package/dist/plugin.test.d.ts +1 -0
- package/dist/plugin.test.mjs +631 -0
- package/dist/router-link.d.ts +220 -0
- package/dist/router-link.mjs +119 -0
- package/dist/router-link.test.d.ts +1 -0
- package/dist/router-link.test.mjs +663 -0
- package/dist/router-view.d.ts +31 -0
- package/dist/router-view.mjs +15 -0
- package/dist/router-view.test.d.ts +1 -0
- package/dist/router-view.test.mjs +676 -0
- package/dist/run-with-context.test.d.ts +1 -0
- package/dist/run-with-context.test.mjs +57 -0
- package/dist/use.d.ts +260 -0
- package/dist/use.mjs +125 -0
- package/dist/use.test.d.ts +1 -0
- package/dist/use.test.mjs +381 -0
- package/dist/util.d.ts +20 -0
- package/dist/util.mjs +49 -0
- package/dist/util.test.d.ts +4 -0
- package/dist/util.test.mjs +604 -0
- package/dist/vue2.d.ts +15 -0
- package/dist/vue2.mjs +0 -0
- package/dist/vue3.d.ts +13 -0
- package/dist/vue3.mjs +0 -0
- package/package.json +85 -0
- package/src/index.test.ts +273 -0
- package/src/index.ts +15 -0
- package/src/plugin.test.ts +812 -0
- package/src/plugin.ts +107 -0
- package/src/router-link.test.ts +830 -0
- package/src/router-link.ts +172 -0
- package/src/router-view.test.ts +840 -0
- package/src/router-view.ts +59 -0
- package/src/run-with-context.test.ts +64 -0
- package/src/use.test.ts +484 -0
- package/src/use.ts +416 -0
- package/src/util.test.ts +760 -0
- package/src/util.ts +85 -0
- package/src/vue2.ts +18 -0
- package/src/vue3.ts +15 -0
package/src/util.test.ts
ADDED
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment happy-dom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Route, Router } from '@esmx/router';
|
|
6
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import { computed, nextTick, ref, version } from 'vue';
|
|
8
|
+
import {
|
|
9
|
+
createDependentProxy,
|
|
10
|
+
createSymbolProperty,
|
|
11
|
+
defineRouterProperties,
|
|
12
|
+
isESModule,
|
|
13
|
+
isVue2,
|
|
14
|
+
resolveComponent
|
|
15
|
+
} from './util';
|
|
16
|
+
|
|
17
|
+
describe('util.ts - Utility Functions', () => {
|
|
18
|
+
describe('isVue2', () => {
|
|
19
|
+
it('should correctly identify Vue 2', () => {
|
|
20
|
+
expect(isVue2).toBe(version.startsWith('2.'));
|
|
21
|
+
expect(typeof isVue2).toBe('boolean');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should be consistent with Vue version check', () => {
|
|
25
|
+
const expectedResult = version.startsWith('2.');
|
|
26
|
+
expect(isVue2).toBe(expectedResult);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('createSymbolProperty', () => {
|
|
31
|
+
let testSymbol: symbol;
|
|
32
|
+
let symbolProperty: ReturnType<typeof createSymbolProperty>;
|
|
33
|
+
let testInstance: Record<string | symbol, unknown>;
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
testSymbol = Symbol('test-symbol');
|
|
37
|
+
symbolProperty = createSymbolProperty<string>(testSymbol);
|
|
38
|
+
testInstance = {};
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('set method', () => {
|
|
42
|
+
it('should set value using symbol as key', () => {
|
|
43
|
+
const testValue = 'test-value';
|
|
44
|
+
|
|
45
|
+
symbolProperty.set(testInstance, testValue);
|
|
46
|
+
|
|
47
|
+
expect(testInstance[testSymbol]).toBe(testValue);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should handle different value types', () => {
|
|
51
|
+
const stringValue = 'string-value';
|
|
52
|
+
const numberValue = 42;
|
|
53
|
+
const objectValue = { key: 'value' };
|
|
54
|
+
const arrayValue = [1, 2, 3];
|
|
55
|
+
|
|
56
|
+
const stringProperty = createSymbolProperty<string>(
|
|
57
|
+
Symbol('string')
|
|
58
|
+
);
|
|
59
|
+
const numberProperty = createSymbolProperty<number>(
|
|
60
|
+
Symbol('number')
|
|
61
|
+
);
|
|
62
|
+
const objectProperty = createSymbolProperty<object>(
|
|
63
|
+
Symbol('object')
|
|
64
|
+
);
|
|
65
|
+
const arrayProperty = createSymbolProperty<number[]>(
|
|
66
|
+
Symbol('array')
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
stringProperty.set(testInstance, stringValue);
|
|
70
|
+
numberProperty.set(testInstance, numberValue);
|
|
71
|
+
objectProperty.set(testInstance, objectValue);
|
|
72
|
+
arrayProperty.set(testInstance, arrayValue);
|
|
73
|
+
|
|
74
|
+
expect(stringProperty.get(testInstance)).toBe(stringValue);
|
|
75
|
+
expect(numberProperty.get(testInstance)).toBe(numberValue);
|
|
76
|
+
expect(objectProperty.get(testInstance)).toBe(objectValue);
|
|
77
|
+
expect(arrayProperty.get(testInstance)).toBe(arrayValue);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should overwrite existing value', () => {
|
|
81
|
+
const firstValue = 'first-value';
|
|
82
|
+
const secondValue = 'second-value';
|
|
83
|
+
|
|
84
|
+
symbolProperty.set(testInstance, firstValue);
|
|
85
|
+
expect(symbolProperty.get(testInstance)).toBe(firstValue);
|
|
86
|
+
|
|
87
|
+
symbolProperty.set(testInstance, secondValue);
|
|
88
|
+
expect(symbolProperty.get(testInstance)).toBe(secondValue);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('get method', () => {
|
|
93
|
+
it('should return undefined for non-existent symbol', () => {
|
|
94
|
+
const result = symbolProperty.get(testInstance);
|
|
95
|
+
|
|
96
|
+
expect(result).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should return correct value after setting', () => {
|
|
100
|
+
const testValue = 'retrieved-value';
|
|
101
|
+
|
|
102
|
+
symbolProperty.set(testInstance, testValue);
|
|
103
|
+
const result = symbolProperty.get(testInstance);
|
|
104
|
+
|
|
105
|
+
expect(result).toBe(testValue);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should return undefined after value is deleted', () => {
|
|
109
|
+
const testValue = 'temporary-value';
|
|
110
|
+
|
|
111
|
+
symbolProperty.set(testInstance, testValue);
|
|
112
|
+
delete testInstance[testSymbol];
|
|
113
|
+
const result = symbolProperty.get(testInstance);
|
|
114
|
+
|
|
115
|
+
expect(result).toBeUndefined();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('symbol isolation', () => {
|
|
120
|
+
it('should not interfere with different symbols', () => {
|
|
121
|
+
const symbol1 = Symbol('symbol1');
|
|
122
|
+
const symbol2 = Symbol('symbol2');
|
|
123
|
+
const property1 = createSymbolProperty<string>(symbol1);
|
|
124
|
+
const property2 = createSymbolProperty<string>(symbol2);
|
|
125
|
+
|
|
126
|
+
const value1 = 'value1';
|
|
127
|
+
const value2 = 'value2';
|
|
128
|
+
|
|
129
|
+
property1.set(testInstance, value1);
|
|
130
|
+
property2.set(testInstance, value2);
|
|
131
|
+
|
|
132
|
+
expect(property1.get(testInstance)).toBe(value1);
|
|
133
|
+
expect(property2.get(testInstance)).toBe(value2);
|
|
134
|
+
expect(property1.get(testInstance)).not.toBe(value2);
|
|
135
|
+
expect(property2.get(testInstance)).not.toBe(value1);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should work with multiple instances', () => {
|
|
139
|
+
const instance1 = {};
|
|
140
|
+
const instance2 = {};
|
|
141
|
+
const testValue1 = 'instance1-value';
|
|
142
|
+
const testValue2 = 'instance2-value';
|
|
143
|
+
|
|
144
|
+
symbolProperty.set(instance1, testValue1);
|
|
145
|
+
symbolProperty.set(instance2, testValue2);
|
|
146
|
+
|
|
147
|
+
expect(symbolProperty.get(instance1)).toBe(testValue1);
|
|
148
|
+
expect(symbolProperty.get(instance2)).toBe(testValue2);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('type safety', () => {
|
|
153
|
+
it('should maintain type information through generic', () => {
|
|
154
|
+
interface TestInterface {
|
|
155
|
+
name: string;
|
|
156
|
+
count: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const interfaceSymbol = Symbol('interface');
|
|
160
|
+
const interfaceProperty =
|
|
161
|
+
createSymbolProperty<TestInterface>(interfaceSymbol);
|
|
162
|
+
const testObject: TestInterface = { name: 'test', count: 5 };
|
|
163
|
+
|
|
164
|
+
interfaceProperty.set(testInstance, testObject);
|
|
165
|
+
const result = interfaceProperty.get(testInstance);
|
|
166
|
+
|
|
167
|
+
expect(result).toEqual(testObject);
|
|
168
|
+
expect(result?.name).toBe('test');
|
|
169
|
+
expect(result?.count).toBe(5);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('isESModule', () => {
|
|
175
|
+
describe('should return true for ES modules', () => {
|
|
176
|
+
it('should identify module with __esModule property', () => {
|
|
177
|
+
const esModule = { __esModule: true };
|
|
178
|
+
|
|
179
|
+
expect(isESModule(esModule)).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should identify module with Symbol.toStringTag', () => {
|
|
183
|
+
const esModule = { [Symbol.toStringTag]: 'Module' };
|
|
184
|
+
|
|
185
|
+
expect(isESModule(esModule)).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should identify module with both properties', () => {
|
|
189
|
+
const esModule = {
|
|
190
|
+
__esModule: true,
|
|
191
|
+
[Symbol.toStringTag]: 'Module'
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
expect(isESModule(esModule)).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should handle truthy __esModule values', () => {
|
|
198
|
+
const testCases = [
|
|
199
|
+
{ __esModule: true },
|
|
200
|
+
{ __esModule: 1 },
|
|
201
|
+
{ __esModule: 'true' },
|
|
202
|
+
{ __esModule: {} },
|
|
203
|
+
{ __esModule: [] }
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
testCases.forEach((moduleObj) => {
|
|
207
|
+
expect(isESModule(moduleObj)).toBe(true);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('should return false for non-ES modules', () => {
|
|
213
|
+
it('should return false for null', () => {
|
|
214
|
+
expect(isESModule(null)).toBe(false);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should return false for undefined', () => {
|
|
218
|
+
expect(isESModule(undefined)).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should return false for plain objects', () => {
|
|
222
|
+
const plainObject = { key: 'value' };
|
|
223
|
+
|
|
224
|
+
expect(isESModule(plainObject)).toBe(false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should return false for objects with falsy __esModule', () => {
|
|
228
|
+
const testCases = [
|
|
229
|
+
{ __esModule: false },
|
|
230
|
+
{ __esModule: 0 },
|
|
231
|
+
{ __esModule: '' },
|
|
232
|
+
{ __esModule: null },
|
|
233
|
+
{ __esModule: undefined }
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
testCases.forEach((moduleObj) => {
|
|
237
|
+
expect(isESModule(moduleObj)).toBe(false);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should return false for objects with wrong Symbol.toStringTag', () => {
|
|
242
|
+
const testCases = [
|
|
243
|
+
{ [Symbol.toStringTag]: 'Object' },
|
|
244
|
+
{ [Symbol.toStringTag]: 'Function' },
|
|
245
|
+
{ [Symbol.toStringTag]: 'Array' },
|
|
246
|
+
{ [Symbol.toStringTag]: '' },
|
|
247
|
+
{ [Symbol.toStringTag]: null }
|
|
248
|
+
];
|
|
249
|
+
|
|
250
|
+
testCases.forEach((moduleObj) => {
|
|
251
|
+
expect(isESModule(moduleObj)).toBe(false);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should return false for primitive values', () => {
|
|
256
|
+
const primitives = [42, 'string', true, false, Symbol('test')];
|
|
257
|
+
|
|
258
|
+
primitives.forEach((primitive) => {
|
|
259
|
+
expect(isESModule(primitive)).toBe(false);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should return false for functions and arrays', () => {
|
|
264
|
+
const nonObjects = [
|
|
265
|
+
() => {},
|
|
266
|
+
() => {},
|
|
267
|
+
[],
|
|
268
|
+
[1, 2, 3],
|
|
269
|
+
new Date()
|
|
270
|
+
];
|
|
271
|
+
|
|
272
|
+
nonObjects.forEach((nonObj) => {
|
|
273
|
+
expect(isESModule(nonObj)).toBe(false);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe('resolveComponent', () => {
|
|
280
|
+
describe('should return default export or module itself', () => {
|
|
281
|
+
it('should return null for falsy values', () => {
|
|
282
|
+
expect(resolveComponent(null)).toBeNull();
|
|
283
|
+
expect(resolveComponent(undefined)).toBeNull();
|
|
284
|
+
expect(resolveComponent(false)).toBeNull();
|
|
285
|
+
expect(resolveComponent(0)).toBeNull();
|
|
286
|
+
expect(resolveComponent('')).toBeNull();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should return default export if available', () => {
|
|
290
|
+
const defaultExport = { name: 'DefaultComponent' };
|
|
291
|
+
const module = {
|
|
292
|
+
__esModule: true,
|
|
293
|
+
default: defaultExport,
|
|
294
|
+
other: 'value'
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
expect(resolveComponent(module)).toBe(defaultExport);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('should return the module itself if no default export', () => {
|
|
301
|
+
const module = {
|
|
302
|
+
__esModule: true,
|
|
303
|
+
someExport: 'value',
|
|
304
|
+
otherExport: 42
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
expect(resolveComponent(module)).toBe(module);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('should handle modules with Symbol.toStringTag', () => {
|
|
311
|
+
const defaultExport = { name: 'SymbolDefaultComponent' };
|
|
312
|
+
const module = {
|
|
313
|
+
[Symbol.toStringTag]: 'Module',
|
|
314
|
+
default: defaultExport
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
expect(resolveComponent(module)).toBe(defaultExport);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('should return non-module objects as is', () => {
|
|
321
|
+
const nonModule = { prop: 'value' };
|
|
322
|
+
expect(resolveComponent(nonModule)).toBe(nonModule);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('should handle various component types', () => {
|
|
326
|
+
const functionComponent = () => ({ name: 'FunctionComponent' });
|
|
327
|
+
expect(resolveComponent(functionComponent)).toBe(
|
|
328
|
+
functionComponent
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
class ClassComponent {
|
|
332
|
+
name = 'ClassComponent';
|
|
333
|
+
}
|
|
334
|
+
const classInstance = new ClassComponent();
|
|
335
|
+
expect(resolveComponent(classInstance)).toBe(classInstance);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should return default when object has only default key', () => {
|
|
339
|
+
const component = { default: 'DefaultComponent' };
|
|
340
|
+
expect(resolveComponent(component)).toBe('DefaultComponent');
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('should return object when it has multiple keys including default', () => {
|
|
344
|
+
const component = {
|
|
345
|
+
default: 'DefaultComponent',
|
|
346
|
+
other: 'otherValue'
|
|
347
|
+
};
|
|
348
|
+
expect(resolveComponent(component)).toBe(component);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should return object when it has multiple keys without default', () => {
|
|
352
|
+
const component = {
|
|
353
|
+
prop1: 'value1',
|
|
354
|
+
prop2: 'value2'
|
|
355
|
+
};
|
|
356
|
+
expect(resolveComponent(component)).toBe(component);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should return object when it has single key that is not default', () => {
|
|
360
|
+
const component = { custom: 'CustomComponent' };
|
|
361
|
+
expect(resolveComponent(component)).toBe(component);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('should return array as is', () => {
|
|
365
|
+
const component = ['item1', 'item2'];
|
|
366
|
+
expect(resolveComponent(component)).toBe(component);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('should return object with single default key that is null', () => {
|
|
370
|
+
const component = { default: null };
|
|
371
|
+
expect(resolveComponent(component)).toBe(null);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should return object with single default key that is undefined', () => {
|
|
375
|
+
const component = { default: undefined };
|
|
376
|
+
expect(resolveComponent(component)).toBe(undefined);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
describe('createDependentProxy', () => {
|
|
382
|
+
it('should return original property values', () => {
|
|
383
|
+
const original = { foo: 'bar', count: 42 };
|
|
384
|
+
const dep = ref(false);
|
|
385
|
+
const proxy = createDependentProxy(original, dep);
|
|
386
|
+
|
|
387
|
+
expect(proxy.foo).toBe('bar');
|
|
388
|
+
expect(proxy.count).toBe(42);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('should handle method calls correctly', () => {
|
|
392
|
+
const original = {
|
|
393
|
+
items: [1, 2, 3],
|
|
394
|
+
getItems() {
|
|
395
|
+
return this.items;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
const dep = ref(false);
|
|
399
|
+
const proxy = createDependentProxy(original, dep);
|
|
400
|
+
|
|
401
|
+
expect(proxy.getItems()).toEqual([1, 2, 3]);
|
|
402
|
+
expect(proxy.getItems()).toBe(original.items);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should handle nested property access', () => {
|
|
406
|
+
const original = {
|
|
407
|
+
nested: {
|
|
408
|
+
value: 'nested-value'
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
const dep = ref(false);
|
|
412
|
+
const proxy = createDependentProxy(original, dep);
|
|
413
|
+
|
|
414
|
+
expect(proxy.nested.value).toBe('nested-value');
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('should allow property modification', () => {
|
|
418
|
+
const original = { value: 'original' };
|
|
419
|
+
const dep = ref(false);
|
|
420
|
+
const proxy = createDependentProxy(original, dep);
|
|
421
|
+
|
|
422
|
+
proxy.value = 'modified';
|
|
423
|
+
expect(proxy.value).toBe('modified');
|
|
424
|
+
expect(original.value).toBe('modified');
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('should trigger computed updates when dependency changes', async () => {
|
|
428
|
+
const original = { value: 'test' };
|
|
429
|
+
const dep = ref(false);
|
|
430
|
+
const proxy = createDependentProxy(original, dep);
|
|
431
|
+
|
|
432
|
+
const computedValue = computed(() => {
|
|
433
|
+
return proxy.value + '-' + String(dep.value);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
expect(computedValue.value).toBe('test-false');
|
|
437
|
+
|
|
438
|
+
dep.value = true;
|
|
439
|
+
await nextTick();
|
|
440
|
+
expect(computedValue.value).toBe('test-true');
|
|
441
|
+
|
|
442
|
+
proxy.value = 'updated';
|
|
443
|
+
dep.value = false;
|
|
444
|
+
await nextTick();
|
|
445
|
+
expect(computedValue.value).toBe('updated-false');
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('should handle special properties', () => {
|
|
449
|
+
const symbol = Symbol('test');
|
|
450
|
+
const original = {
|
|
451
|
+
[symbol]: 'symbol-value'
|
|
452
|
+
};
|
|
453
|
+
const dep = ref(false);
|
|
454
|
+
const proxy = createDependentProxy(original, dep);
|
|
455
|
+
|
|
456
|
+
expect(proxy[symbol]).toBe('symbol-value');
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('should read the dependency on property access', () => {
|
|
460
|
+
const original = { value: 'test' };
|
|
461
|
+
const dep = ref(false);
|
|
462
|
+
|
|
463
|
+
const spy = vi.fn();
|
|
464
|
+
const depValue = dep.value; // 预先读取一次值
|
|
465
|
+
vi.spyOn(dep, 'value', 'get').mockImplementation(() => {
|
|
466
|
+
spy();
|
|
467
|
+
return depValue;
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const proxy = createDependentProxy(original, dep);
|
|
471
|
+
|
|
472
|
+
proxy.value;
|
|
473
|
+
expect(spy).toHaveBeenCalled();
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
describe('defineRouterProperties', () => {
|
|
478
|
+
it('should define $router and $route properties on target object', () => {
|
|
479
|
+
const target = {};
|
|
480
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
481
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
482
|
+
|
|
483
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
484
|
+
return mockRouter;
|
|
485
|
+
});
|
|
486
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
487
|
+
return mockRoute;
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
491
|
+
|
|
492
|
+
expect(target).toHaveProperty('$router');
|
|
493
|
+
expect(target).toHaveProperty('$route');
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it('should call getter functions when accessing properties', () => {
|
|
497
|
+
const target = {};
|
|
498
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
499
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
500
|
+
|
|
501
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
502
|
+
return mockRouter;
|
|
503
|
+
});
|
|
504
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
505
|
+
return mockRoute;
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
509
|
+
|
|
510
|
+
const router = (target as any).$router;
|
|
511
|
+
const route = (target as any).$route;
|
|
512
|
+
|
|
513
|
+
expect(routerGetter).toHaveBeenCalled();
|
|
514
|
+
expect(routeGetter).toHaveBeenCalled();
|
|
515
|
+
expect(router).toBe(mockRouter);
|
|
516
|
+
expect(route).toBe(mockRoute);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('should use correct this context in getter functions', () => {
|
|
520
|
+
const target = { customProp: 'test' };
|
|
521
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
522
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
523
|
+
|
|
524
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
525
|
+
return this.customProp ? mockRouter : ({} as Router);
|
|
526
|
+
});
|
|
527
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
528
|
+
return this.customProp ? mockRoute : ({} as Route);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
532
|
+
|
|
533
|
+
const router = (target as any).$router;
|
|
534
|
+
const route = (target as any).$route;
|
|
535
|
+
|
|
536
|
+
expect(router).toBe(mockRouter);
|
|
537
|
+
expect(route).toBe(mockRoute);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('should make properties non-enumerable by default', () => {
|
|
541
|
+
const target = {};
|
|
542
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
543
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
544
|
+
|
|
545
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
546
|
+
return mockRouter;
|
|
547
|
+
});
|
|
548
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
549
|
+
return mockRoute;
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
553
|
+
|
|
554
|
+
const descriptors = Object.getOwnPropertyDescriptors(target);
|
|
555
|
+
|
|
556
|
+
expect(descriptors.$router.enumerable).toBe(false);
|
|
557
|
+
expect(descriptors.$route.enumerable).toBe(false);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should make properties non-configurable by default', () => {
|
|
561
|
+
const target = {};
|
|
562
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
563
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
564
|
+
|
|
565
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
566
|
+
return mockRouter;
|
|
567
|
+
});
|
|
568
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
569
|
+
return mockRoute;
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
573
|
+
|
|
574
|
+
const descriptors = Object.getOwnPropertyDescriptors(target);
|
|
575
|
+
|
|
576
|
+
expect(descriptors.$router.configurable).toBe(false);
|
|
577
|
+
expect(descriptors.$route.configurable).toBe(false);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it('should make properties configurable when configurable option is true', () => {
|
|
581
|
+
const target = {};
|
|
582
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
583
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
584
|
+
|
|
585
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
586
|
+
return mockRouter;
|
|
587
|
+
});
|
|
588
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
589
|
+
return mockRoute;
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
defineRouterProperties(target, routerGetter, routeGetter, true);
|
|
593
|
+
|
|
594
|
+
const descriptors = Object.getOwnPropertyDescriptors(target);
|
|
595
|
+
|
|
596
|
+
expect(descriptors.$router.configurable).toBe(true);
|
|
597
|
+
expect(descriptors.$route.configurable).toBe(true);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('should define properties as getters', () => {
|
|
601
|
+
const target = {};
|
|
602
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
603
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
604
|
+
|
|
605
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
606
|
+
return mockRouter;
|
|
607
|
+
});
|
|
608
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
609
|
+
return mockRoute;
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
613
|
+
|
|
614
|
+
const descriptors = Object.getOwnPropertyDescriptors(target);
|
|
615
|
+
|
|
616
|
+
expect(typeof descriptors.$router.get).toBe('function');
|
|
617
|
+
expect(typeof descriptors.$route.get).toBe('function');
|
|
618
|
+
expect(descriptors.$router.set).toBeUndefined();
|
|
619
|
+
expect(descriptors.$route.set).toBeUndefined();
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it('should call getter each time property is accessed', () => {
|
|
623
|
+
const target = {};
|
|
624
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
625
|
+
|
|
626
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
627
|
+
return mockRouter;
|
|
628
|
+
});
|
|
629
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
630
|
+
return { path: '/test' } as Route;
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
634
|
+
|
|
635
|
+
(target as any).$router;
|
|
636
|
+
(target as any).$router;
|
|
637
|
+
(target as any).$router;
|
|
638
|
+
|
|
639
|
+
expect(routerGetter).toHaveBeenCalledTimes(3);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it('should work with different target objects', () => {
|
|
643
|
+
const targets = [
|
|
644
|
+
{},
|
|
645
|
+
{ existingProp: 'value' },
|
|
646
|
+
Object.create(null),
|
|
647
|
+
new (class Test {})()
|
|
648
|
+
];
|
|
649
|
+
|
|
650
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
651
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
652
|
+
|
|
653
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
654
|
+
return mockRouter;
|
|
655
|
+
});
|
|
656
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
657
|
+
return mockRoute;
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
targets.forEach((target) => {
|
|
661
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
662
|
+
|
|
663
|
+
expect(target).toHaveProperty('$router');
|
|
664
|
+
expect(target).toHaveProperty('$route');
|
|
665
|
+
|
|
666
|
+
const router = (target as any).$router;
|
|
667
|
+
const route = (target as any).$route;
|
|
668
|
+
|
|
669
|
+
expect(router).toBe(mockRouter);
|
|
670
|
+
expect(route).toBe(mockRoute);
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it('should preserve existing properties on target object', () => {
|
|
675
|
+
const target = {
|
|
676
|
+
existingProp: 'value',
|
|
677
|
+
$existingRouter: 'should not be affected'
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
681
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
682
|
+
|
|
683
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
684
|
+
return mockRouter;
|
|
685
|
+
});
|
|
686
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
687
|
+
return mockRoute;
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
691
|
+
|
|
692
|
+
expect(target.existingProp).toBe('value');
|
|
693
|
+
expect(target.$existingRouter).toBe('should not be affected');
|
|
694
|
+
expect(target).toHaveProperty('$router');
|
|
695
|
+
expect(target).toHaveProperty('$route');
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
it('should handle getter functions that return undefined', () => {
|
|
699
|
+
const target = {};
|
|
700
|
+
|
|
701
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
702
|
+
return undefined as any;
|
|
703
|
+
});
|
|
704
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
705
|
+
return undefined as any;
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
709
|
+
|
|
710
|
+
const router = (target as any).$router;
|
|
711
|
+
const route = (target as any).$route;
|
|
712
|
+
|
|
713
|
+
expect(router).toBeUndefined();
|
|
714
|
+
expect(route).toBeUndefined();
|
|
715
|
+
expect(routerGetter).toHaveBeenCalled();
|
|
716
|
+
expect(routeGetter).toHaveBeenCalled();
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it('should handle getter functions that throw errors', () => {
|
|
720
|
+
const target = {};
|
|
721
|
+
|
|
722
|
+
const routerGetter = vi.fn(function (this: any) {
|
|
723
|
+
throw new Error('Router error');
|
|
724
|
+
});
|
|
725
|
+
const routeGetter = vi.fn(function (this: any) {
|
|
726
|
+
throw new Error('Route error');
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
defineRouterProperties(target, routerGetter, routeGetter);
|
|
730
|
+
|
|
731
|
+
expect(() => (target as any).$router).toThrow('Router error');
|
|
732
|
+
expect(() => (target as any).$route).toThrow('Route error');
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
it('should work with arrow functions as getters', () => {
|
|
736
|
+
const target = {};
|
|
737
|
+
const mockRouter = { push: vi.fn() } as unknown as Router;
|
|
738
|
+
const mockRoute = { path: '/test' } as unknown as Route;
|
|
739
|
+
const context = { customContext: 'test' };
|
|
740
|
+
|
|
741
|
+
const routerGetter = vi.fn(() => mockRouter);
|
|
742
|
+
const routeGetter = vi.fn(() => mockRoute);
|
|
743
|
+
|
|
744
|
+
defineRouterProperties.call(
|
|
745
|
+
context,
|
|
746
|
+
target,
|
|
747
|
+
routerGetter,
|
|
748
|
+
routeGetter
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
const router = (target as any).$router;
|
|
752
|
+
const route = (target as any).$route;
|
|
753
|
+
|
|
754
|
+
expect(router).toBe(mockRouter);
|
|
755
|
+
expect(route).toBe(mockRoute);
|
|
756
|
+
expect(routerGetter).toHaveBeenCalled();
|
|
757
|
+
expect(routeGetter).toHaveBeenCalled();
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
});
|