lil-mocky 1.4.0
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/.claude/settings.local.json +7 -0
- package/CLAUDE.md +191 -0
- package/Dockerfile +2 -0
- package/README.md +705 -0
- package/TODO +57 -0
- package/init-docker.sh +4 -0
- package/lil-mocky.sublime-project +8 -0
- package/lil-mocky.sublime-workspace +2405 -0
- package/package.json +14 -0
- package/src/lil-mocky.js +329 -0
- package/test/lilMockyTest.js +867 -0
- package/test-docker.sh +3 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
lil-mocky is a lightweight JavaScript mocking library for testing. It provides builders for creating mock functions, objects, and classes with call tracking and return value control. It also includes a spy function for tracking calls to existing methods.
|
|
8
|
+
|
|
9
|
+
## Development Commands
|
|
10
|
+
|
|
11
|
+
### Testing
|
|
12
|
+
```bash
|
|
13
|
+
npm test # Run all tests with Mocha
|
|
14
|
+
npm test -- --grep "pattern" # Run specific tests matching pattern
|
|
15
|
+
./test-docker.sh # Run tests in Docker
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Docker
|
|
19
|
+
```bash
|
|
20
|
+
docker build -t lil-mocky .
|
|
21
|
+
docker run lil-mocky npm test
|
|
22
|
+
./test-docker.sh # Convenience script
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Architecture
|
|
26
|
+
|
|
27
|
+
### Core Builder Pattern
|
|
28
|
+
|
|
29
|
+
The library uses a fluent builder pattern where each builder type (function, object, class) implements a `build()` method that returns a "wired" mock with testing utilities.
|
|
30
|
+
|
|
31
|
+
**Builder Flow**: `builder.build() -> wire*(mock, state) -> mock`
|
|
32
|
+
|
|
33
|
+
**Standard usage**: Call `.build()` on any builder to create a mock:
|
|
34
|
+
```javascript
|
|
35
|
+
const mock = mocky.function().args('x').build();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Each builder:
|
|
39
|
+
1. Accepts configuration through chainable methods
|
|
40
|
+
2. Creates the underlying mock structure via `build()`
|
|
41
|
+
3. Gets "wired" with helper methods (`ret()`, `calls()`, `reset()`, etc.)
|
|
42
|
+
4. Returns a mock that tracks interactions
|
|
43
|
+
|
|
44
|
+
### Function Mocks (functionBuilder)
|
|
45
|
+
|
|
46
|
+
Function mocks support:
|
|
47
|
+
- **Named arguments**: `.args('arg1', { arg2: 'default' })` - Maps positional arguments to named properties
|
|
48
|
+
- **Argument selection**: `.argSelect(0, 2)` - Only capture specific argument positions
|
|
49
|
+
- **Custom body**: Pass a function receiving `{self, state, call, args, rawArgs, original, ret}` context
|
|
50
|
+
- **Async functions**: `.async()` flag creates async mock
|
|
51
|
+
- **Original function**: `.original(fn)` - Stores the original function in context (used by spy)
|
|
52
|
+
|
|
53
|
+
**Context object properties**:
|
|
54
|
+
- `self` - The `this` context
|
|
55
|
+
- `state` - Internal state object
|
|
56
|
+
- `call` - Call number (1-indexed)
|
|
57
|
+
- `args` - Processed arguments (based on `.args()` configuration)
|
|
58
|
+
- `rawArgs` - Unprocessed arguments array
|
|
59
|
+
- `original` - Original function (if set via `.original()`)
|
|
60
|
+
- `ret` - Configured return value (if set via `.ret()`)
|
|
61
|
+
|
|
62
|
+
**Call tracking**: Function state includes `state.calls` array where each entry contains the processed arguments for that call.
|
|
63
|
+
|
|
64
|
+
**Return values**: Set via `mock.ret(value, callIndex)` or `mock.onRet(handler, callIndex)`. Call index 0 is the default for all calls.
|
|
65
|
+
|
|
66
|
+
### Object Mocks (objectBuilder)
|
|
67
|
+
|
|
68
|
+
Objects are containers for properties that can be:
|
|
69
|
+
- Nested builders (function, object) - automatically built
|
|
70
|
+
- Plain values - assigned directly
|
|
71
|
+
|
|
72
|
+
The `reset()` method:
|
|
73
|
+
- Recursively calls `reset()` on all nested mocks
|
|
74
|
+
- Restores plain properties to their initial values
|
|
75
|
+
- Deletes properties added after creation
|
|
76
|
+
|
|
77
|
+
### Class Mocks (classBuilder)
|
|
78
|
+
|
|
79
|
+
Classes use a special instance management system:
|
|
80
|
+
- **Instance descriptions**: `state.descriptions[index]` holds the member structure for each instance
|
|
81
|
+
- **Lazy creation**: Descriptions are created on-demand when an instance is constructed or accessed via `Mock.inst(index)`
|
|
82
|
+
- **Shared state per instance**: All instances share the same descriptions array, allowing pre-configuration before instantiation
|
|
83
|
+
|
|
84
|
+
Example: `Mock.inst(0).method.ret('value')` configures the first instance's method before calling `new Mock()`.
|
|
85
|
+
|
|
86
|
+
**Direct instance access**: Mock helpers (`.calls()`, `.ret()`, `.reset()`) are accessible directly on instance methods:
|
|
87
|
+
```javascript
|
|
88
|
+
const inst = new Mock();
|
|
89
|
+
inst.method('test');
|
|
90
|
+
inst.method.calls(0); // Works! Returns call arguments
|
|
91
|
+
inst.method.ret('value'); // Configure via instance
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### State Management
|
|
95
|
+
|
|
96
|
+
Each mock type maintains internal state through closures:
|
|
97
|
+
- **Function state**: `{calls: [], rets: [], retHandlers: [], data: {}}`
|
|
98
|
+
- **Class state**: `{descriptions: [], numInstances: 0, data: {}}`
|
|
99
|
+
- **Object state**: Tracks `initialMocks` (Set) and `initialValues` (Map) for reset functionality
|
|
100
|
+
|
|
101
|
+
The `state.data` object is available for custom builders to store arbitrary data.
|
|
102
|
+
|
|
103
|
+
### Argument Processing
|
|
104
|
+
|
|
105
|
+
`getFunctionArgs()` handles four modes:
|
|
106
|
+
1. **Single select**: `.argSelect(2)` returns `callArgs[2]` directly
|
|
107
|
+
2. **Named args**: `.args('a', 'b')` returns `{a: callArgs[0], b: callArgs[1]}`
|
|
108
|
+
3. **Named with selection**: `.args('a', 'b').argSelect(1)` returns `{b: callArgs[1]}`
|
|
109
|
+
4. **No configuration**: Returns raw arguments as array (e.g., `['val1', 'val2']`)
|
|
110
|
+
|
|
111
|
+
**Breaking change**: Previously, no configuration returned `null`. Now returns an array to support spy functionality. This may affect existing code that expects `null` in `calls()` for unconfigured functions.
|
|
112
|
+
|
|
113
|
+
Arguments are deep cloned before storage to prevent mutation issues.
|
|
114
|
+
|
|
115
|
+
### Spy Function
|
|
116
|
+
|
|
117
|
+
The `spy()` function is a non-builder API for tracking calls to existing methods while optionally replacing their implementation.
|
|
118
|
+
|
|
119
|
+
**Signature**: `mocky.spy(object, methodName, replacement?)`
|
|
120
|
+
|
|
121
|
+
**Parameters**:
|
|
122
|
+
- `object` - The object containing the method to spy on
|
|
123
|
+
- `methodName` - The name of the method to spy on
|
|
124
|
+
- `replacement` - Optional `functionBuilder` to replace the method
|
|
125
|
+
|
|
126
|
+
**Behavior**:
|
|
127
|
+
- Without replacement: Calls through to original method by default
|
|
128
|
+
- The spy IS a mock function with all standard methods (`.ret()`, `.calls()`, `.reset()`)
|
|
129
|
+
- Setting `.ret()` overrides the call-through behavior
|
|
130
|
+
- The `replacement` builder (if provided) receives `context.original` automatically
|
|
131
|
+
- Call `.restore()` to put back the original method
|
|
132
|
+
|
|
133
|
+
**Examples**:
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
// Basic spy - tracks calls, calls through to original
|
|
137
|
+
const spy = mocky.spy(obj, 'method');
|
|
138
|
+
obj.method('test');
|
|
139
|
+
spy.calls(0); // ['test']
|
|
140
|
+
|
|
141
|
+
// Override return value
|
|
142
|
+
spy.ret('mocked');
|
|
143
|
+
|
|
144
|
+
// Spy with custom replacement
|
|
145
|
+
const spy = mocky.spy(obj, 'method', mocky.function((context) => {
|
|
146
|
+
const result = context.original.apply(context.self, context.rawArgs);
|
|
147
|
+
return result * 2; // Modify original behavior
|
|
148
|
+
}).args('x', 'y'));
|
|
149
|
+
|
|
150
|
+
// Restore original
|
|
151
|
+
spy.restore();
|
|
152
|
+
|
|
153
|
+
// Works with prototypes, classes, etc.
|
|
154
|
+
mocky.spy(MyClass.prototype, 'method');
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Use cases**:
|
|
158
|
+
- Track calls to existing methods without modifying behavior
|
|
159
|
+
- Temporarily override method behavior for testing
|
|
160
|
+
- Spy on class prototype methods to affect all instances
|
|
161
|
+
- Wrap existing functions with additional logic
|
|
162
|
+
|
|
163
|
+
## Testing Patterns
|
|
164
|
+
|
|
165
|
+
Tests use Chai's `expect` assertions. Common patterns:
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
// Create and configure mocks
|
|
169
|
+
const mock = mocky.function().args('arg').build();
|
|
170
|
+
mock.ret('testRet');
|
|
171
|
+
|
|
172
|
+
// Verify calls
|
|
173
|
+
expect(mock.calls(0)).to.deep.equal({ arg: 'value' });
|
|
174
|
+
expect(mock.calls().length).to.equal(1);
|
|
175
|
+
|
|
176
|
+
// Class instance access
|
|
177
|
+
const Mock = mocky.class({ method: mocky.function() }).build();
|
|
178
|
+
Mock.inst().method.ret('value'); // Configure first instance
|
|
179
|
+
expect(Mock.inst().constructor.calls(0)).to.deep.equal({ initArg: 'value' });
|
|
180
|
+
|
|
181
|
+
// Spy on existing method
|
|
182
|
+
const spy = mocky.spy(obj, 'method');
|
|
183
|
+
obj.method('test');
|
|
184
|
+
expect(spy.calls(0)).to.deep.equal(['test']);
|
|
185
|
+
spy.restore();
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## File Structure
|
|
189
|
+
|
|
190
|
+
- `src/lil-mocky.js` - All implementation code (single file architecture)
|
|
191
|
+
- `test/lilMockyTest.js` - Mocha/Chai test suite
|
package/Dockerfile
ADDED