puty 0.0.4-rc1 → 0.0.4
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 +32 -10
- package/package.json +1 -1
- package/src/puty.js +71 -22
package/README.md
CHANGED
|
@@ -188,12 +188,12 @@ executions:
|
|
|
188
188
|
- `mode: 'class'` - Indicates this suite tests a class
|
|
189
189
|
- `constructorArgs` - Arguments passed to the class constructor
|
|
190
190
|
- `executions` - Array of method calls to execute in sequence
|
|
191
|
-
- `method` - Name of the method to call
|
|
191
|
+
- `method` - Name of the method to call (supports nested: `user.api.getData`)
|
|
192
192
|
- `in` - Arguments to pass to the method
|
|
193
193
|
- `out` - Expected return value (optional)
|
|
194
194
|
- `asserts` - Assertions to run after the method call
|
|
195
|
-
- Property assertions: Check instance properties
|
|
196
|
-
- Method assertions: Call methods and check their return values
|
|
195
|
+
- Property assertions: Check instance properties (supports nested: `user.profile.name`)
|
|
196
|
+
- Method assertions: Call methods and check their return values (supports nested: `settings.getTheme`)
|
|
197
197
|
|
|
198
198
|
### Error Testing
|
|
199
199
|
|
|
@@ -387,21 +387,43 @@ For class tests:
|
|
|
387
387
|
```yaml
|
|
388
388
|
case: 'test description'
|
|
389
389
|
executions:
|
|
390
|
-
- method: 'methodName'
|
|
390
|
+
- method: 'methodName' # Supports nested: 'user.api.getData'
|
|
391
391
|
in: [arg1]
|
|
392
|
-
out: expectedValue
|
|
393
|
-
throws: 'Error msg'
|
|
392
|
+
out: expectedValue # Optional
|
|
393
|
+
throws: 'Error msg' # Optional
|
|
394
394
|
asserts:
|
|
395
|
-
- property: 'prop'
|
|
396
|
-
op: 'eq'
|
|
395
|
+
- property: 'prop' # Supports nested: 'user.profile.name'
|
|
396
|
+
op: 'eq' # Currently only 'eq' is supported
|
|
397
397
|
value: expected
|
|
398
|
-
- method: 'getter'
|
|
398
|
+
- method: 'getter' # Supports nested: 'settings.ui.getTheme'
|
|
399
399
|
in: []
|
|
400
400
|
out: expected
|
|
401
|
-
mocks:
|
|
401
|
+
mocks: # Optional: Mocks for the entire test case
|
|
402
402
|
mockName:
|
|
403
403
|
calls:
|
|
404
404
|
- in: [args]
|
|
405
405
|
out: result
|
|
406
406
|
```
|
|
407
407
|
|
|
408
|
+
#### Nested Properties and Methods
|
|
409
|
+
|
|
410
|
+
Puty supports accessing nested properties and calling nested methods using dot notation:
|
|
411
|
+
|
|
412
|
+
```yaml
|
|
413
|
+
case: 'test nested access'
|
|
414
|
+
executions:
|
|
415
|
+
- method: 'settings.ui.setTheme' # Call nested method
|
|
416
|
+
in: ['dark']
|
|
417
|
+
out: 'dark'
|
|
418
|
+
asserts:
|
|
419
|
+
- property: 'user.profile.name' # Access nested property
|
|
420
|
+
op: eq
|
|
421
|
+
value: 'John Doe'
|
|
422
|
+
- property: 'user.account.balance' # Deep nested property
|
|
423
|
+
op: eq
|
|
424
|
+
value: 100.50
|
|
425
|
+
- method: 'api.client.get' # Call nested method
|
|
426
|
+
in: ['/users/123']
|
|
427
|
+
out: 'GET /users/123'
|
|
428
|
+
```
|
|
429
|
+
|
package/package.json
CHANGED
package/src/puty.js
CHANGED
|
@@ -11,6 +11,70 @@ import { expect, test, describe } from "vitest";
|
|
|
11
11
|
import { traverseAllFiles, parseWithIncludes } from "./utils.js";
|
|
12
12
|
import { resolveMocks, processMockReferences, createMockFunctions, validateMockCalls } from "./mockResolver.js";
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Resolves a nested property path on an object (e.g., "user.profile.name")
|
|
16
|
+
* @param {Object} obj - The object to traverse
|
|
17
|
+
* @param {string} path - The property path (e.g., "user.profile.name")
|
|
18
|
+
* @returns {any} The value at the path
|
|
19
|
+
* @throws {Error} If any part of the path doesn't exist
|
|
20
|
+
*/
|
|
21
|
+
const getNestedProperty = (obj, path) => {
|
|
22
|
+
const parts = path.split('.');
|
|
23
|
+
let current = obj;
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < parts.length; i++) {
|
|
26
|
+
if (current == null) {
|
|
27
|
+
throw new Error(`Cannot access property '${parts[i]}' of ${current} in path '${path}'`);
|
|
28
|
+
}
|
|
29
|
+
if (!(parts[i] in current)) {
|
|
30
|
+
throw new Error(`Property '${parts[i]}' not found in path '${path}'`);
|
|
31
|
+
}
|
|
32
|
+
current = current[parts[i]];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return current;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolves a nested method path and calls it (e.g., "user.api.getData")
|
|
40
|
+
* @param {Object} obj - The object to traverse
|
|
41
|
+
* @param {string} path - The method path (e.g., "user.api.getData")
|
|
42
|
+
* @param {Array} args - Arguments to pass to the method
|
|
43
|
+
* @returns {any} The result of the method call
|
|
44
|
+
* @throws {Error} If any part of the path doesn't exist or final part is not a function
|
|
45
|
+
*/
|
|
46
|
+
const callNestedMethod = (obj, path, args = []) => {
|
|
47
|
+
const parts = path.split('.');
|
|
48
|
+
const methodName = parts.pop();
|
|
49
|
+
|
|
50
|
+
let current = obj;
|
|
51
|
+
const parentPath = parts.join('.');
|
|
52
|
+
|
|
53
|
+
// Navigate to the parent object
|
|
54
|
+
for (const part of parts) {
|
|
55
|
+
if (current == null) {
|
|
56
|
+
throw new Error(`Cannot access property '${part}' of ${current} in path '${path}'`);
|
|
57
|
+
}
|
|
58
|
+
if (!(part in current)) {
|
|
59
|
+
throw new Error(`Property '${part}' not found in path '${path}'`);
|
|
60
|
+
}
|
|
61
|
+
current = current[part];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check if the method exists and is a function
|
|
65
|
+
if (current == null) {
|
|
66
|
+
throw new Error(`Cannot access method '${methodName}' of ${current} in path '${path}'`);
|
|
67
|
+
}
|
|
68
|
+
if (!(methodName in current)) {
|
|
69
|
+
throw new Error(`Method '${methodName}' not found in path '${path}'`);
|
|
70
|
+
}
|
|
71
|
+
if (typeof current[methodName] !== 'function') {
|
|
72
|
+
throw new Error(`'${methodName}' is not a function in path '${path}'`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return current[methodName](...args);
|
|
76
|
+
};
|
|
77
|
+
|
|
14
78
|
/**
|
|
15
79
|
* File extensions that are recognized as YAML test files
|
|
16
80
|
* @type {string[]}
|
|
@@ -215,16 +279,11 @@ const setupClassTests = (suite) => {
|
|
|
215
279
|
asserts,
|
|
216
280
|
} = execution;
|
|
217
281
|
|
|
218
|
-
//
|
|
219
|
-
if (!instance[method] || typeof instance[method] !== "function") {
|
|
220
|
-
throw new Error(`Method '${method}' not found on class instance`);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Execute the method and check its return value
|
|
282
|
+
// Execute the method and check its return value - supports nested methods
|
|
224
283
|
if (throws) {
|
|
225
|
-
expect(() => instance
|
|
284
|
+
expect(() => callNestedMethod(instance, method, inArg || [])).toThrow(throws);
|
|
226
285
|
} else {
|
|
227
|
-
const result = instance
|
|
286
|
+
const result = callNestedMethod(instance, method, inArg || []);
|
|
228
287
|
if (expectedOut !== undefined) {
|
|
229
288
|
expect(result).toEqual(expectedOut);
|
|
230
289
|
}
|
|
@@ -234,25 +293,15 @@ const setupClassTests = (suite) => {
|
|
|
234
293
|
if (asserts) {
|
|
235
294
|
for (const assertion of asserts) {
|
|
236
295
|
if (assertion.property) {
|
|
237
|
-
// Property assertion
|
|
238
|
-
const actualValue = instance
|
|
296
|
+
// Property assertion - supports nested properties like "user.profile.name"
|
|
297
|
+
const actualValue = getNestedProperty(instance, assertion.property);
|
|
239
298
|
if (assertion.op === "eq") {
|
|
240
299
|
expect(actualValue).toEqual(assertion.value);
|
|
241
300
|
}
|
|
242
301
|
// Add more operators as needed
|
|
243
302
|
} else if (assertion.method) {
|
|
244
|
-
// Method assertion
|
|
245
|
-
|
|
246
|
-
!instance[assertion.method] ||
|
|
247
|
-
typeof instance[assertion.method] !== "function"
|
|
248
|
-
) {
|
|
249
|
-
throw new Error(
|
|
250
|
-
`Method '${assertion.method}' not found on class instance for assertion`,
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
const result = instance[assertion.method](
|
|
254
|
-
...(assertion.in || []),
|
|
255
|
-
);
|
|
303
|
+
// Method assertion - supports nested methods like "user.api.getData"
|
|
304
|
+
const result = callNestedMethod(instance, assertion.method, assertion.in || []);
|
|
256
305
|
expect(result).toEqual(assertion.out);
|
|
257
306
|
}
|
|
258
307
|
}
|