lambder 1.0.131 → 1.0.133
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 +24 -37
- package/dist/LambderMSW.d.ts +48 -101
- package/dist/LambderMSW.js +105 -172
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -2
- package/docs/LAMBDER_MSW.md +430 -0
- package/docs/TYPE_SAFE_QUICK_START.md +33 -0
- package/examples/msw-testing-example.ts +277 -0
- package/package.json +1 -1
- package/src/LambderMSW.ts +172 -281
- package/src/index.ts +1 -10
- package/docs/MSW_IMPLEMENTATION_COMPLETE.md +0 -191
- package/docs/MSW_MOCKING.md +0 -606
- package/examples/msw-correct-usage.ts +0 -136
- package/examples/msw-mocking-example.ts +0 -241
- package/tests/msw-integration.test.ts +0 -45
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CORRECT USAGE: Lambder MSW Integration Example
|
|
3
|
-
*
|
|
4
|
-
* This demonstrates the proper way to use MSW with Lambder.
|
|
5
|
-
* Each mockLambderApi call returns an MSW RequestHandler that can be used directly.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Step 1: Define your API contract
|
|
9
|
-
import type { ApiContractShape } from '../src/index.js';
|
|
10
|
-
|
|
11
|
-
const ApiContract = {
|
|
12
|
-
'public.getInitialPageData': {
|
|
13
|
-
input: null,
|
|
14
|
-
output: {
|
|
15
|
-
userLocationData: {
|
|
16
|
-
country: '' as string,
|
|
17
|
-
region: '' as string,
|
|
18
|
-
regionCode: '' as string,
|
|
19
|
-
city: '' as string,
|
|
20
|
-
zipCode: '' as string,
|
|
21
|
-
lat: 0 as number,
|
|
22
|
-
lng: 0 as number,
|
|
23
|
-
},
|
|
24
|
-
sessionUser: null as null | {
|
|
25
|
-
id: string;
|
|
26
|
-
username: string;
|
|
27
|
-
email: string;
|
|
28
|
-
name: string;
|
|
29
|
-
bio: string;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
'user.updateProfile': {
|
|
34
|
-
input: {
|
|
35
|
-
name: '' as string,
|
|
36
|
-
bio: '' as string
|
|
37
|
-
},
|
|
38
|
-
output: {
|
|
39
|
-
success: true as boolean
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
} satisfies ApiContractShape;
|
|
43
|
-
|
|
44
|
-
type MyApiContract = typeof ApiContract;
|
|
45
|
-
|
|
46
|
-
// Step 2: Create mock handlers using Lambder's utility functions
|
|
47
|
-
import { mockLambderApi } from '../src/index.js';
|
|
48
|
-
|
|
49
|
-
export const handlers = [
|
|
50
|
-
// Each mockLambderApi call returns a proper MSW RequestHandler
|
|
51
|
-
// You can use them directly in setupWorker/setupServer
|
|
52
|
-
mockLambderApi<MyApiContract, 'public.getInitialPageData'>(
|
|
53
|
-
'public.getInitialPageData',
|
|
54
|
-
() => ({
|
|
55
|
-
userLocationData: {
|
|
56
|
-
country: 'United States',
|
|
57
|
-
region: 'California',
|
|
58
|
-
regionCode: 'CA',
|
|
59
|
-
city: 'San Francisco',
|
|
60
|
-
zipCode: '94102',
|
|
61
|
-
lat: 37.7749,
|
|
62
|
-
lng: -122.4194,
|
|
63
|
-
},
|
|
64
|
-
sessionUser: {
|
|
65
|
-
id: 'mock-user-123',
|
|
66
|
-
username: 'mockuser',
|
|
67
|
-
email: 'mock@example.com',
|
|
68
|
-
name: 'Mock User',
|
|
69
|
-
bio: 'This is a mock user for development',
|
|
70
|
-
},
|
|
71
|
-
})
|
|
72
|
-
),
|
|
73
|
-
|
|
74
|
-
mockLambderApi<MyApiContract, 'user.updateProfile'>(
|
|
75
|
-
'user.updateProfile',
|
|
76
|
-
(input) => {
|
|
77
|
-
console.log('Updating profile:', input);
|
|
78
|
-
return {
|
|
79
|
-
success: true
|
|
80
|
-
};
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
delay: 500 // Optional: simulate network delay
|
|
84
|
-
}
|
|
85
|
-
),
|
|
86
|
-
];
|
|
87
|
-
|
|
88
|
-
// Step 3: Set up MSW
|
|
89
|
-
// For Browser (development):
|
|
90
|
-
/**
|
|
91
|
-
* ```typescript
|
|
92
|
-
* // src/mocks/browser.ts
|
|
93
|
-
* import { setupWorker } from 'msw/browser';
|
|
94
|
-
* import { handlers } from './handlers';
|
|
95
|
-
*
|
|
96
|
-
* export const worker = setupWorker(...handlers);
|
|
97
|
-
*
|
|
98
|
-
* // src/main.tsx
|
|
99
|
-
* if (import.meta.env.DEV) {
|
|
100
|
-
* const { worker } = await import('./mocks/browser');
|
|
101
|
-
* worker.start();
|
|
102
|
-
* }
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
|
-
|
|
106
|
-
// For Node.js (testing):
|
|
107
|
-
/**
|
|
108
|
-
* ```typescript
|
|
109
|
-
* // src/mocks/server.ts
|
|
110
|
-
* import { setupServer } from 'msw/node';
|
|
111
|
-
* import { handlers } from './handlers';
|
|
112
|
-
*
|
|
113
|
-
* export const server = setupServer(...handlers);
|
|
114
|
-
*
|
|
115
|
-
* // vitest.setup.ts
|
|
116
|
-
* import { server } from './mocks/server';
|
|
117
|
-
*
|
|
118
|
-
* beforeAll(() => server.listen());
|
|
119
|
-
* afterEach(() => server.resetHandlers());
|
|
120
|
-
* afterAll(() => server.close());
|
|
121
|
-
* ```
|
|
122
|
-
*/
|
|
123
|
-
|
|
124
|
-
// Type verification: handlers is an array of MSW RequestHandler
|
|
125
|
-
// TypeScript should infer this correctly without needing explicit types
|
|
126
|
-
export type HandlersType = typeof handlers; // RequestHandler[]
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* KEY POINTS:
|
|
130
|
-
*
|
|
131
|
-
* 1. mockLambderApi returns a proper MSW RequestHandler (from http.post)
|
|
132
|
-
* 2. You can spread handlers directly into setupWorker/setupServer
|
|
133
|
-
* 3. Each handler checks body.apiName and only responds to its specific API
|
|
134
|
-
* 4. Full type safety with your API contract
|
|
135
|
-
* 5. No need to import HttpHandler type - MSW provides RequestHandler
|
|
136
|
-
*/
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example: Using MSW to mock Lambder API endpoints
|
|
3
|
-
*
|
|
4
|
-
* This example demonstrates how to use Mock Service Worker (MSW) with Lambder
|
|
5
|
-
* to create type-safe mocks for your API endpoints.
|
|
6
|
-
*
|
|
7
|
-
* Prerequisites:
|
|
8
|
-
* - Install MSW: npm install --save-dev msw
|
|
9
|
-
* - Define your API contract
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
mockLambderApi,
|
|
14
|
-
mockLambderApiError,
|
|
15
|
-
mockLambderSessionExpired,
|
|
16
|
-
mockLambderNotAuthorized,
|
|
17
|
-
mockLambderVersionExpired,
|
|
18
|
-
type ApiContractShape
|
|
19
|
-
} from '../src/index.js';
|
|
20
|
-
|
|
21
|
-
// 1. Define your API contract
|
|
22
|
-
const myApiContract = {
|
|
23
|
-
'user.getProfile': {
|
|
24
|
-
input: { userId: '' as string },
|
|
25
|
-
output: { name: '' as string, email: '' as string, role: '' as string }
|
|
26
|
-
},
|
|
27
|
-
'user.updateProfile': {
|
|
28
|
-
input: { userId: '' as string, name: '' as string, email: '' as string },
|
|
29
|
-
output: { success: true as boolean, message: '' as string }
|
|
30
|
-
},
|
|
31
|
-
'user.deleteAccount': {
|
|
32
|
-
input: { userId: '' as string },
|
|
33
|
-
output: { success: true as boolean }
|
|
34
|
-
},
|
|
35
|
-
'admin.listUsers': {
|
|
36
|
-
input: { page: 0 as number, limit: 0 as number },
|
|
37
|
-
output: { users: [] as Array<{ id: string; name: string }>, total: 0 as number }
|
|
38
|
-
}
|
|
39
|
-
} satisfies ApiContractShape;
|
|
40
|
-
|
|
41
|
-
type MyApiContract = typeof myApiContract;
|
|
42
|
-
|
|
43
|
-
// 2. Create mock handlers
|
|
44
|
-
|
|
45
|
-
// Basic success mock
|
|
46
|
-
export const getUserProfileHandler = mockLambderApi<MyApiContract, 'user.getProfile'>(
|
|
47
|
-
'user.getProfile',
|
|
48
|
-
(input) => {
|
|
49
|
-
console.log('Mocking user.getProfile for userId:', input.userId);
|
|
50
|
-
|
|
51
|
-
// Return mock data with full type safety
|
|
52
|
-
return {
|
|
53
|
-
name: 'John Doe',
|
|
54
|
-
email: 'john@example.com',
|
|
55
|
-
role: 'user'
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
// Mock with dynamic response based on input
|
|
61
|
-
export const updateProfileHandler = mockLambderApi<MyApiContract, 'user.updateProfile'>(
|
|
62
|
-
'user.updateProfile',
|
|
63
|
-
(input) => {
|
|
64
|
-
console.log('Mocking user.updateProfile:', input);
|
|
65
|
-
|
|
66
|
-
// Validate input
|
|
67
|
-
if (!input.name || input.name.length < 2) {
|
|
68
|
-
throw new Error('Name must be at least 2 characters');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
success: true,
|
|
73
|
-
message: `Profile updated for ${input.name}`
|
|
74
|
-
};
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
delay: 500 // Simulate network delay
|
|
78
|
-
}
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// Mock with error response
|
|
82
|
-
export const updateProfileErrorHandler = mockLambderApiError<MyApiContract, 'user.updateProfile'>(
|
|
83
|
-
'user.updateProfile',
|
|
84
|
-
'Failed to update profile due to server error',
|
|
85
|
-
{
|
|
86
|
-
delay: 300
|
|
87
|
-
}
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
// Mock session expired
|
|
91
|
-
export const deleteAccountSessionExpiredHandler = mockLambderSessionExpired<MyApiContract, 'user.deleteAccount'>(
|
|
92
|
-
'user.deleteAccount'
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
// Mock not authorized
|
|
96
|
-
export const listUsersNotAuthorizedHandler = mockLambderNotAuthorized<MyApiContract, 'admin.listUsers'>(
|
|
97
|
-
'admin.listUsers'
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
// Mock version expired
|
|
101
|
-
export const getProfileVersionExpiredHandler = mockLambderVersionExpired<MyApiContract, 'user.getProfile'>(
|
|
102
|
-
'user.getProfile'
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
// Mock with pagination
|
|
106
|
-
export const listUsersHandler = mockLambderApi<MyApiContract, 'admin.listUsers'>(
|
|
107
|
-
'admin.listUsers',
|
|
108
|
-
(input) => {
|
|
109
|
-
// Generate mock paginated data
|
|
110
|
-
const allUsers = Array.from({ length: 50 }, (_, i) => ({
|
|
111
|
-
id: `user-${i + 1}`,
|
|
112
|
-
name: `User ${i + 1}`
|
|
113
|
-
}));
|
|
114
|
-
|
|
115
|
-
const start = input.page * input.limit;
|
|
116
|
-
const end = start + input.limit;
|
|
117
|
-
const users = allUsers.slice(start, end);
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
users,
|
|
121
|
-
total: allUsers.length
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// 3. Set up MSW handlers
|
|
127
|
-
|
|
128
|
-
// For Node.js testing (Vitest, Jest, etc.)
|
|
129
|
-
/**
|
|
130
|
-
* Setup example for Node.js tests:
|
|
131
|
-
*
|
|
132
|
-
* ```typescript
|
|
133
|
-
* // test-setup.ts
|
|
134
|
-
* import { setupServer } from 'msw/node';
|
|
135
|
-
* import { getUserProfileHandler, updateProfileHandler } from './msw-example';
|
|
136
|
-
*
|
|
137
|
-
* export const server = setupServer(
|
|
138
|
-
* getUserProfileHandler,
|
|
139
|
-
* updateProfileHandler,
|
|
140
|
-
* listUsersHandler
|
|
141
|
-
* );
|
|
142
|
-
*
|
|
143
|
-
* // vitest.setup.ts
|
|
144
|
-
* beforeAll(() => server.listen());
|
|
145
|
-
* afterEach(() => server.resetHandlers());
|
|
146
|
-
* afterAll(() => server.close());
|
|
147
|
-
* ```
|
|
148
|
-
*/
|
|
149
|
-
|
|
150
|
-
// For browser development/Storybook
|
|
151
|
-
/**
|
|
152
|
-
* Setup example for browser:
|
|
153
|
-
*
|
|
154
|
-
* ```typescript
|
|
155
|
-
* // mocks/browser.ts
|
|
156
|
-
* import { setupWorker } from 'msw/browser';
|
|
157
|
-
* import { getUserProfileHandler, updateProfileHandler } from './msw-example';
|
|
158
|
-
*
|
|
159
|
-
* export const worker = setupWorker(
|
|
160
|
-
* getUserProfileHandler,
|
|
161
|
-
* updateProfileHandler,
|
|
162
|
-
* listUsersHandler
|
|
163
|
-
* );
|
|
164
|
-
*
|
|
165
|
-
* // main.tsx
|
|
166
|
-
* if (import.meta.env.DEV) {
|
|
167
|
-
* const { worker } = await import('./mocks/browser');
|
|
168
|
-
* worker.start();
|
|
169
|
-
* }
|
|
170
|
-
* ```
|
|
171
|
-
*/
|
|
172
|
-
|
|
173
|
-
// 4. Using in tests
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Test example:
|
|
177
|
-
*
|
|
178
|
-
* ```typescript
|
|
179
|
-
* import { describe, it, expect } from 'vitest';
|
|
180
|
-
* import { server } from './test-setup';
|
|
181
|
-
* import { caller } from './api-client';
|
|
182
|
-
*
|
|
183
|
-
* describe('User Profile', () => {
|
|
184
|
-
* it('should get user profile', async () => {
|
|
185
|
-
* const profile = await caller.api('user.getProfile', { userId: '123' });
|
|
186
|
-
*
|
|
187
|
-
* expect(profile).toEqual({
|
|
188
|
-
* name: 'John Doe',
|
|
189
|
-
* email: 'john@example.com',
|
|
190
|
-
* role: 'user'
|
|
191
|
-
* });
|
|
192
|
-
* });
|
|
193
|
-
*
|
|
194
|
-
* it('should handle update errors', async () => {
|
|
195
|
-
* // Override handler for this test
|
|
196
|
-
* server.use(updateProfileErrorHandler);
|
|
197
|
-
*
|
|
198
|
-
* const result = await caller.api('user.updateProfile', {
|
|
199
|
-
* userId: '123',
|
|
200
|
-
* name: 'New Name',
|
|
201
|
-
* email: 'new@example.com'
|
|
202
|
-
* });
|
|
203
|
-
*
|
|
204
|
-
* expect(result).toBeNull(); // Error handler returns null
|
|
205
|
-
* });
|
|
206
|
-
*
|
|
207
|
-
* it('should handle session expiry', async () => {
|
|
208
|
-
* server.use(deleteAccountSessionExpiredHandler);
|
|
209
|
-
*
|
|
210
|
-
* const result = await caller.api('user.deleteAccount', { userId: '123' });
|
|
211
|
-
*
|
|
212
|
-
* expect(result).toBeNull();
|
|
213
|
-
* // Verify sessionExpiredHandler was called if configured
|
|
214
|
-
* });
|
|
215
|
-
*
|
|
216
|
-
* it('should handle not authorized', async () => {
|
|
217
|
-
* server.use(listUsersNotAuthorizedHandler);
|
|
218
|
-
*
|
|
219
|
-
* const result = await caller.api('admin.listUsers', { page: 0, limit: 10 });
|
|
220
|
-
*
|
|
221
|
-
* expect(result).toBeNull();
|
|
222
|
-
* // Verify notAuthorizedHandler was called if configured
|
|
223
|
-
* });
|
|
224
|
-
* });
|
|
225
|
-
* ```
|
|
226
|
-
*/
|
|
227
|
-
|
|
228
|
-
// Export all handlers for easy setup
|
|
229
|
-
export const handlers = [
|
|
230
|
-
getUserProfileHandler,
|
|
231
|
-
updateProfileHandler,
|
|
232
|
-
listUsersHandler,
|
|
233
|
-
];
|
|
234
|
-
|
|
235
|
-
// Export error handlers separately
|
|
236
|
-
export const errorHandlers = [
|
|
237
|
-
updateProfileErrorHandler,
|
|
238
|
-
deleteAccountSessionExpiredHandler,
|
|
239
|
-
listUsersNotAuthorizedHandler,
|
|
240
|
-
getProfileVersionExpiredHandler,
|
|
241
|
-
];
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example test file demonstrating MSW integration with Lambder
|
|
3
|
-
*
|
|
4
|
-
* To run this test:
|
|
5
|
-
* 1. Install MSW: npm install --save-dev msw
|
|
6
|
-
* 2. Remove the .skip from test cases
|
|
7
|
-
* 3. Run: npm test
|
|
8
|
-
*
|
|
9
|
-
* This file is skipped by default since MSW is an optional dependency
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { describe, it, expect } from 'vitest';
|
|
13
|
-
import type { ApiContractShape } from '../src/index.js';
|
|
14
|
-
|
|
15
|
-
// Define test API contract
|
|
16
|
-
const testApiContract = {
|
|
17
|
-
'test.echo': {
|
|
18
|
-
input: { message: '' as string },
|
|
19
|
-
output: { echo: '' as string }
|
|
20
|
-
},
|
|
21
|
-
'test.add': {
|
|
22
|
-
input: { a: 0 as number, b: 0 as number },
|
|
23
|
-
output: { result: 0 as number }
|
|
24
|
-
},
|
|
25
|
-
'test.getUser': {
|
|
26
|
-
input: { userId: '' as string },
|
|
27
|
-
output: { id: '' as string, name: '' as string }
|
|
28
|
-
}
|
|
29
|
-
} satisfies ApiContractShape;
|
|
30
|
-
|
|
31
|
-
type TestApiContract = typeof testApiContract;
|
|
32
|
-
|
|
33
|
-
describe('Lambder MSW Integration (Requires MSW to be installed)', () => {
|
|
34
|
-
it('should verify MSW utilities are exported', async () => {
|
|
35
|
-
// This test just verifies the exports exist
|
|
36
|
-
// Actual MSW functionality requires MSW to be installed
|
|
37
|
-
const lambder = await import('../dist/index.js');
|
|
38
|
-
|
|
39
|
-
expect(lambder.mockLambderApi).toBeDefined();
|
|
40
|
-
expect(lambder.mockLambderApiError).toBeDefined();
|
|
41
|
-
expect(lambder.mockLambderSessionExpired).toBeDefined();
|
|
42
|
-
expect(lambder.mockLambderNotAuthorized).toBeDefined();
|
|
43
|
-
expect(lambder.mockLambderVersionExpired).toBeDefined();
|
|
44
|
-
});
|
|
45
|
-
});
|