@api-client/core 0.18.16 → 0.18.18
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/build/src/{modeling → decorators}/observed.d.ts +3 -3
- package/build/src/decorators/observed.d.ts.map +1 -0
- package/build/src/{modeling → decorators}/observed.js +4 -4
- package/build/src/decorators/observed.js.map +1 -0
- package/build/src/modeling/ApiModel.js +1 -1
- package/build/src/modeling/ApiModel.js.map +1 -1
- package/build/src/modeling/DataDomain.d.ts +7 -2
- package/build/src/modeling/DataDomain.d.ts.map +1 -1
- package/build/src/modeling/DataDomain.js +15 -2
- package/build/src/modeling/DataDomain.js.map +1 -1
- package/build/src/modeling/DomainAssociation.d.ts +7 -0
- package/build/src/modeling/DomainAssociation.d.ts.map +1 -1
- package/build/src/modeling/DomainAssociation.js +44 -1
- package/build/src/modeling/DomainAssociation.js.map +1 -1
- package/build/src/modeling/DomainEntity.d.ts +6 -0
- package/build/src/modeling/DomainEntity.d.ts.map +1 -1
- package/build/src/modeling/DomainEntity.js +21 -1
- package/build/src/modeling/DomainEntity.js.map +1 -1
- package/build/src/modeling/DomainModel.js +1 -1
- package/build/src/modeling/DomainModel.js.map +1 -1
- package/build/src/modeling/DomainNamespace.js +1 -1
- package/build/src/modeling/DomainNamespace.js.map +1 -1
- package/build/src/modeling/DomainProperty.d.ts +5 -0
- package/build/src/modeling/DomainProperty.d.ts.map +1 -1
- package/build/src/modeling/DomainProperty.js +38 -1
- package/build/src/modeling/DomainProperty.js.map +1 -1
- package/build/src/modeling/DomainSerialization.d.ts +6 -3
- package/build/src/modeling/DomainSerialization.d.ts.map +1 -1
- package/build/src/modeling/DomainSerialization.js +374 -52
- package/build/src/modeling/DomainSerialization.js.map +1 -1
- package/build/src/modeling/types.d.ts +69 -2
- package/build/src/modeling/types.d.ts.map +1 -1
- package/build/src/modeling/types.js.map +1 -1
- package/build/src/models/Thing.js +1 -1
- package/build/src/models/Thing.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +10 -10
- package/package.json +2 -1
- package/src/{modeling → decorators}/observed.ts +5 -5
- package/src/modeling/ApiModel.ts +1 -1
- package/src/modeling/DataDomain.ts +24 -3
- package/src/modeling/DomainAssociation.ts +51 -1
- package/src/modeling/DomainEntity.ts +24 -1
- package/src/modeling/DomainModel.ts +1 -1
- package/src/modeling/DomainNamespace.ts +1 -1
- package/src/modeling/DomainProperty.ts +43 -1
- package/src/modeling/DomainSerialization.ts +440 -56
- package/src/modeling/types.ts +73 -2
- package/src/models/Thing.ts +1 -1
- package/tests/unit/decorators/observed.spec.ts +527 -0
- package/tests/unit/modeling/data_domain_serialization.spec.ts +508 -0
- package/tests/unit/modeling/domain_asociation.spec.ts +376 -0
- package/tests/unit/modeling/domain_entity.spec.ts +147 -0
- package/tests/unit/modeling/domain_property.spec.ts +273 -0
- package/build/src/modeling/observed.d.ts.map +0 -1
- package/build/src/modeling/observed.js.map +0 -1
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import { observed, retargetChange, toRaw } from '../../../src/decorators/observed.js'
|
|
3
|
+
|
|
4
|
+
test.group('observed decorator', () => {
|
|
5
|
+
test('should decorate accessor property and track changes', ({ assert }) => {
|
|
6
|
+
let domainNotifyCallCount = 0
|
|
7
|
+
const mockDomain = {
|
|
8
|
+
notifyChange() {
|
|
9
|
+
domainNotifyCallCount++
|
|
10
|
+
},
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class TestClass {
|
|
14
|
+
domain = mockDomain
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
@observed() accessor testProperty: any
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const instance = new TestClass()
|
|
21
|
+
|
|
22
|
+
// Setting property should trigger domain notification
|
|
23
|
+
instance.testProperty = 'test value'
|
|
24
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
25
|
+
assert.equal(instance.testProperty, 'test value')
|
|
26
|
+
|
|
27
|
+
// Setting same value should not trigger notification
|
|
28
|
+
instance.testProperty = 'test value'
|
|
29
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
30
|
+
|
|
31
|
+
// Setting different value should trigger notification
|
|
32
|
+
instance.testProperty = 'new value'
|
|
33
|
+
assert.equal(domainNotifyCallCount, 2)
|
|
34
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
35
|
+
|
|
36
|
+
test('should use notifyChange method on instance if domain is not available', ({ assert }) => {
|
|
37
|
+
let instanceNotifyCallCount = 0
|
|
38
|
+
|
|
39
|
+
class TestClass {
|
|
40
|
+
notifyChange() {
|
|
41
|
+
instanceNotifyCallCount++
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
@observed() accessor testProperty: any
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const instance = new TestClass()
|
|
49
|
+
|
|
50
|
+
instance.testProperty = 'test value'
|
|
51
|
+
assert.equal(instanceNotifyCallCount, 1)
|
|
52
|
+
assert.equal(instance.testProperty, 'test value')
|
|
53
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
54
|
+
|
|
55
|
+
test('should work with setter decorator', ({ assert }) => {
|
|
56
|
+
let domainNotifyCallCount = 0
|
|
57
|
+
const mockDomain = {
|
|
58
|
+
notifyChange() {
|
|
59
|
+
domainNotifyCallCount++
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class TestClass {
|
|
64
|
+
#value: string | undefined
|
|
65
|
+
domain = mockDomain
|
|
66
|
+
|
|
67
|
+
@observed()
|
|
68
|
+
set testProperty(value: string | undefined) {
|
|
69
|
+
this.#value = value
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get testProperty() {
|
|
73
|
+
return this.#value
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const instance = new TestClass()
|
|
78
|
+
|
|
79
|
+
instance.testProperty = 'test value'
|
|
80
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
81
|
+
assert.equal(instance.testProperty, 'test value')
|
|
82
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
83
|
+
|
|
84
|
+
test('should support deep observation of objects', ({ assert }) => {
|
|
85
|
+
let domainNotifyCallCount = 0
|
|
86
|
+
const mockDomain = {
|
|
87
|
+
notifyChange() {
|
|
88
|
+
domainNotifyCallCount++
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class TestClass {
|
|
93
|
+
domain = mockDomain
|
|
94
|
+
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
96
|
+
@observed({ deep: true }) accessor nestedData: any
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const instance = new TestClass()
|
|
100
|
+
const testObj = { nested: { value: 'initial' } }
|
|
101
|
+
|
|
102
|
+
// Setting object should trigger notification
|
|
103
|
+
instance.nestedData = testObj
|
|
104
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
105
|
+
|
|
106
|
+
// Modifying deep property should trigger notification
|
|
107
|
+
instance.nestedData.nested.value = 'changed'
|
|
108
|
+
assert.equal(domainNotifyCallCount, 2)
|
|
109
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
110
|
+
|
|
111
|
+
test('should handle primitive values with deep observation', ({ assert }) => {
|
|
112
|
+
let domainNotifyCallCount = 0
|
|
113
|
+
const mockDomain = {
|
|
114
|
+
notifyChange() {
|
|
115
|
+
domainNotifyCallCount++
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class TestClass {
|
|
120
|
+
domain = mockDomain
|
|
121
|
+
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
123
|
+
@observed({ deep: true }) accessor primitiveValue: any
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const instance = new TestClass()
|
|
127
|
+
|
|
128
|
+
// Setting primitive values should work normally
|
|
129
|
+
instance.primitiveValue = 42
|
|
130
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
131
|
+
assert.equal(instance.primitiveValue, 42)
|
|
132
|
+
|
|
133
|
+
instance.primitiveValue = 'string'
|
|
134
|
+
assert.equal(domainNotifyCallCount, 2)
|
|
135
|
+
assert.equal(instance.primitiveValue, 'string')
|
|
136
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
137
|
+
|
|
138
|
+
test('should handle null and undefined values correctly', ({ assert }) => {
|
|
139
|
+
let domainNotifyCallCount = 0
|
|
140
|
+
const mockDomain = {
|
|
141
|
+
notifyChange() {
|
|
142
|
+
domainNotifyCallCount++
|
|
143
|
+
},
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
class TestClass {
|
|
147
|
+
domain = mockDomain
|
|
148
|
+
|
|
149
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
150
|
+
@observed() accessor nullableValue: any
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const instance = new TestClass()
|
|
154
|
+
|
|
155
|
+
instance.nullableValue = null
|
|
156
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
157
|
+
assert.equal(instance.nullableValue, null)
|
|
158
|
+
|
|
159
|
+
instance.nullableValue = undefined
|
|
160
|
+
assert.equal(domainNotifyCallCount, 2)
|
|
161
|
+
assert.equal(instance.nullableValue, undefined)
|
|
162
|
+
|
|
163
|
+
instance.nullableValue = 'value'
|
|
164
|
+
assert.equal(domainNotifyCallCount, 3)
|
|
165
|
+
assert.equal(instance.nullableValue, 'value')
|
|
166
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
167
|
+
|
|
168
|
+
test('should throw error for unsupported decorator locations', ({ assert }) => {
|
|
169
|
+
assert.throws(() => {
|
|
170
|
+
const decorator = observed()
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
172
|
+
decorator({} as any, { kind: 'field' } as any)
|
|
173
|
+
}, 'Unsupported decorator location: field')
|
|
174
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
175
|
+
|
|
176
|
+
test('should maintain proxy references correctly with deep observation', ({ assert }) => {
|
|
177
|
+
let domainNotifyCallCount = 0
|
|
178
|
+
const mockDomain = {
|
|
179
|
+
notifyChange() {
|
|
180
|
+
domainNotifyCallCount++
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
class TestClass {
|
|
185
|
+
domain = mockDomain
|
|
186
|
+
|
|
187
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
188
|
+
@observed({ deep: true }) accessor complexData: any
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const instance = new TestClass()
|
|
192
|
+
const originalObj = { items: [{ name: 'item1' }] }
|
|
193
|
+
|
|
194
|
+
instance.complexData = originalObj
|
|
195
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
196
|
+
|
|
197
|
+
// Modifying array items should trigger notifications
|
|
198
|
+
instance.complexData.items.push({ name: 'item2' })
|
|
199
|
+
assert.equal(domainNotifyCallCount, 2)
|
|
200
|
+
|
|
201
|
+
// Note: the deep proxy system returns original objects for already-accessed objects
|
|
202
|
+
// so this modification may not trigger additional notifications
|
|
203
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
204
|
+
|
|
205
|
+
test('should handle class without domain or notifyChange', ({ assert }) => {
|
|
206
|
+
class TestClass {
|
|
207
|
+
// @ts-expect-error It is for testing purposes
|
|
208
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
209
|
+
@observed() accessor testProperty: any
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const instance = new TestClass()
|
|
213
|
+
|
|
214
|
+
// Should not throw error when setting value
|
|
215
|
+
assert.doesNotThrow(() => {
|
|
216
|
+
instance.testProperty = 'test value'
|
|
217
|
+
})
|
|
218
|
+
assert.equal(instance.testProperty, 'test value')
|
|
219
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
220
|
+
|
|
221
|
+
test('should handle multiple observed properties on same instance', ({ assert }) => {
|
|
222
|
+
let domainNotifyCallCount = 0
|
|
223
|
+
const mockDomain = {
|
|
224
|
+
notifyChange() {
|
|
225
|
+
domainNotifyCallCount++
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
class TestClass {
|
|
230
|
+
domain = mockDomain
|
|
231
|
+
|
|
232
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
233
|
+
@observed() accessor prop1: any
|
|
234
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
235
|
+
@observed() accessor prop2: any
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const instance = new TestClass()
|
|
239
|
+
|
|
240
|
+
instance.prop1 = 'value1'
|
|
241
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
242
|
+
|
|
243
|
+
instance.prop2 = 'value2'
|
|
244
|
+
assert.equal(domainNotifyCallCount, 2)
|
|
245
|
+
|
|
246
|
+
assert.equal(instance.prop1, 'value1')
|
|
247
|
+
assert.equal(instance.prop2, 'value2')
|
|
248
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
249
|
+
|
|
250
|
+
test('should handle circular references in deep observation', ({ assert }) => {
|
|
251
|
+
let notifyChangeCallCount = 0
|
|
252
|
+
const mockDomain = {
|
|
253
|
+
notifyChange: () => {
|
|
254
|
+
notifyChangeCallCount++
|
|
255
|
+
},
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
class TestClass {
|
|
259
|
+
domain = mockDomain
|
|
260
|
+
|
|
261
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
262
|
+
@observed({ deep: true }) accessor data: any
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const instance = new TestClass()
|
|
266
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
267
|
+
const obj: any = { name: 'test' }
|
|
268
|
+
obj.self = obj // Create circular reference
|
|
269
|
+
|
|
270
|
+
// Should not crash with circular reference
|
|
271
|
+
assert.doesNotThrow(() => {
|
|
272
|
+
instance.data = obj
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
assert.equal(notifyChangeCallCount, 1)
|
|
276
|
+
assert.equal(instance.data.name, 'test')
|
|
277
|
+
// Circular reference is preserved but proxy system prevents infinite loops
|
|
278
|
+
assert.equal(instance.data.self.name, 'test')
|
|
279
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
test.group('retargetChange decorator', () => {
|
|
283
|
+
class MockEventTarget {
|
|
284
|
+
listeners: ((event: Event) => void)[] = []
|
|
285
|
+
|
|
286
|
+
addEventListener(type: string, listener: (event: Event) => void) {
|
|
287
|
+
if (type === 'change') {
|
|
288
|
+
this.listeners.push(listener)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
removeEventListener(type: string, listener: (event: Event) => void) {
|
|
293
|
+
if (type === 'change') {
|
|
294
|
+
const index = this.listeners.indexOf(listener)
|
|
295
|
+
if (index > -1) {
|
|
296
|
+
this.listeners.splice(index, 1)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
triggerChange() {
|
|
302
|
+
const event = new Event('change')
|
|
303
|
+
this.listeners.forEach((listener) => listener(event))
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
test('should retarget change events from EventTarget properties', ({ assert }) => {
|
|
308
|
+
let domainNotifyCallCount = 0
|
|
309
|
+
const mockDomain = {
|
|
310
|
+
notifyChange() {
|
|
311
|
+
domainNotifyCallCount++
|
|
312
|
+
},
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
class TestClass {
|
|
316
|
+
domain = mockDomain
|
|
317
|
+
|
|
318
|
+
@retargetChange()
|
|
319
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
320
|
+
accessor target: any
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const instance = new TestClass()
|
|
324
|
+
const mockTarget = new MockEventTarget()
|
|
325
|
+
|
|
326
|
+
// Set EventTarget (accessor version notifies)
|
|
327
|
+
instance.target = mockTarget
|
|
328
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
329
|
+
|
|
330
|
+
// Trigger change event should notify domain
|
|
331
|
+
mockTarget.triggerChange()
|
|
332
|
+
assert.equal(domainNotifyCallCount, 2)
|
|
333
|
+
}).tags(['@decorators', '@retargetChange', '@core', '@unit', '@fast'])
|
|
334
|
+
|
|
335
|
+
test('should handle setting same EventTarget value', ({ assert }) => {
|
|
336
|
+
let domainNotifyCallCount = 0
|
|
337
|
+
const mockDomain = {
|
|
338
|
+
notifyChange() {
|
|
339
|
+
domainNotifyCallCount++
|
|
340
|
+
},
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
class TestClass {
|
|
344
|
+
domain = mockDomain
|
|
345
|
+
|
|
346
|
+
@retargetChange()
|
|
347
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
348
|
+
accessor target: any
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const instance = new TestClass()
|
|
352
|
+
const mockTarget = new MockEventTarget()
|
|
353
|
+
|
|
354
|
+
// Set EventTarget
|
|
355
|
+
instance.target = mockTarget
|
|
356
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
357
|
+
|
|
358
|
+
// Set same EventTarget should not notify again
|
|
359
|
+
instance.target = mockTarget
|
|
360
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
361
|
+
}).tags(['@decorators', '@retargetChange', '@core', '@unit', '@fast'])
|
|
362
|
+
|
|
363
|
+
test('should work with setter decorator', ({ assert }) => {
|
|
364
|
+
let domainNotifyCallCount = 0
|
|
365
|
+
const mockDomain = {
|
|
366
|
+
notifyChange() {
|
|
367
|
+
domainNotifyCallCount++
|
|
368
|
+
},
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
class TestClass {
|
|
372
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
373
|
+
#target: any
|
|
374
|
+
domain = mockDomain
|
|
375
|
+
|
|
376
|
+
@retargetChange()
|
|
377
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
378
|
+
set target(value: any) {
|
|
379
|
+
this.#target = value
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
383
|
+
get target(): any {
|
|
384
|
+
return this.#target
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const instance = new TestClass()
|
|
389
|
+
const mockTarget = new MockEventTarget()
|
|
390
|
+
|
|
391
|
+
// Set EventTarget (setter doesn't notify by itself)
|
|
392
|
+
instance.target = mockTarget
|
|
393
|
+
assert.equal(domainNotifyCallCount, 0)
|
|
394
|
+
|
|
395
|
+
// Trigger change event should notify domain
|
|
396
|
+
mockTarget.triggerChange()
|
|
397
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
398
|
+
}).tags(['@decorators', '@retargetChange', '@core', '@unit', '@fast'])
|
|
399
|
+
|
|
400
|
+
test('should throw error for unsupported decorator locations', ({ assert }) => {
|
|
401
|
+
assert.throws(() => {
|
|
402
|
+
const decorator = retargetChange()
|
|
403
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
404
|
+
decorator({} as any, { kind: 'field' } as any)
|
|
405
|
+
}, 'Unsupported decorator location: field')
|
|
406
|
+
}).tags(['@decorators', '@retargetChange', '@core', '@unit', '@fast'])
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
test.group('toRaw function', () => {
|
|
410
|
+
test('should return raw object from proxy', ({ assert }) => {
|
|
411
|
+
const mockDomain = {
|
|
412
|
+
notifyChange() {
|
|
413
|
+
// ...
|
|
414
|
+
},
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
class TestClass {
|
|
418
|
+
domain = mockDomain
|
|
419
|
+
|
|
420
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
421
|
+
@observed({ deep: true }) accessor data: any
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const instance = new TestClass()
|
|
425
|
+
const originalObj = { name: 'test' }
|
|
426
|
+
|
|
427
|
+
instance.data = originalObj
|
|
428
|
+
|
|
429
|
+
// toRaw should return the original object
|
|
430
|
+
const raw = toRaw(instance, instance.data)
|
|
431
|
+
assert.strictEqual(raw, originalObj)
|
|
432
|
+
}).tags(['@decorators', '@toRaw', '@core', '@unit', '@fast'])
|
|
433
|
+
|
|
434
|
+
test('should return undefined if no proxy found', ({ assert }) => {
|
|
435
|
+
const instance = {}
|
|
436
|
+
const nonProxiedObj = { name: 'test' }
|
|
437
|
+
|
|
438
|
+
const raw = toRaw(instance, nonProxiedObj)
|
|
439
|
+
assert.equal(raw, undefined)
|
|
440
|
+
}).tags(['@decorators', '@toRaw', '@core', '@unit', '@fast'])
|
|
441
|
+
|
|
442
|
+
test('should return undefined if proxy symbol not found', ({ assert }) => {
|
|
443
|
+
const instance = {}
|
|
444
|
+
const obj = { name: 'test' }
|
|
445
|
+
|
|
446
|
+
const raw = toRaw(instance, obj)
|
|
447
|
+
assert.equal(raw, undefined)
|
|
448
|
+
}).tags(['@decorators', '@toRaw', '@core', '@unit', '@fast'])
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
test.group('edge cases and error conditions', () => {
|
|
452
|
+
test('should handle class without domain or notifyChange', ({ assert }) => {
|
|
453
|
+
class TestClass {
|
|
454
|
+
// @ts-expect-error It is for testing purposes
|
|
455
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
456
|
+
@observed() accessor testProperty: any
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const instance = new TestClass()
|
|
460
|
+
|
|
461
|
+
// Should not throw error when setting value
|
|
462
|
+
assert.doesNotThrow(() => {
|
|
463
|
+
instance.testProperty = 'test value'
|
|
464
|
+
})
|
|
465
|
+
assert.equal(instance.testProperty, 'test value')
|
|
466
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
467
|
+
|
|
468
|
+
test('should handle multiple observed properties on same instance', ({ assert }) => {
|
|
469
|
+
let domainNotifyCallCount = 0
|
|
470
|
+
const mockDomain = {
|
|
471
|
+
notifyChange() {
|
|
472
|
+
domainNotifyCallCount++
|
|
473
|
+
},
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
class TestClass {
|
|
477
|
+
domain = mockDomain
|
|
478
|
+
|
|
479
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
480
|
+
@observed() accessor prop1: any
|
|
481
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
482
|
+
@observed() accessor prop2: any
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const instance = new TestClass()
|
|
486
|
+
|
|
487
|
+
instance.prop1 = 'value1'
|
|
488
|
+
assert.equal(domainNotifyCallCount, 1)
|
|
489
|
+
|
|
490
|
+
instance.prop2 = 'value2'
|
|
491
|
+
assert.equal(domainNotifyCallCount, 2)
|
|
492
|
+
|
|
493
|
+
assert.equal(instance.prop1, 'value1')
|
|
494
|
+
assert.equal(instance.prop2, 'value2')
|
|
495
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
496
|
+
|
|
497
|
+
test('should handle circular references in deep observation', ({ assert }) => {
|
|
498
|
+
let notifyChangeCallCount = 0
|
|
499
|
+
const mockDomain = {
|
|
500
|
+
notifyChange: () => {
|
|
501
|
+
notifyChangeCallCount++
|
|
502
|
+
},
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
class TestClass {
|
|
506
|
+
domain = mockDomain
|
|
507
|
+
|
|
508
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
509
|
+
@observed({ deep: true }) accessor data: any
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const instance = new TestClass()
|
|
513
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
514
|
+
const obj: any = { name: 'test' }
|
|
515
|
+
obj.self = obj // Create circular reference
|
|
516
|
+
|
|
517
|
+
// Should not crash with circular reference
|
|
518
|
+
assert.doesNotThrow(() => {
|
|
519
|
+
instance.data = obj
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
assert.equal(notifyChangeCallCount, 1)
|
|
523
|
+
assert.equal(instance.data.name, 'test')
|
|
524
|
+
// Circular reference is preserved but proxy system prevents infinite loops
|
|
525
|
+
assert.equal(instance.data.self.name, 'test')
|
|
526
|
+
}).tags(['@decorators', '@observed', '@core', '@unit', '@fast'])
|
|
527
|
+
})
|