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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm test:*)"
5
+ ]
6
+ }
7
+ }
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
@@ -0,0 +1,2 @@
1
+ FROM node:20.17.0
2
+ WORKDIR /app