vitest-websocket-mock 0.1.2 → 0.2.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/LICENSE +1 -23
- package/README.md +7 -11
- package/dist/index.d.ts +1 -4
- package/dist/index.js +50 -33
- package/package.json +10 -8
package/LICENSE
CHANGED
|
@@ -1,27 +1,5 @@
|
|
|
1
|
-
Copyright 2023 Akiomi Kamakura
|
|
2
|
-
|
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
-
in the Software without restriction, including without limitation the rights
|
|
6
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
-
furnished to do so, subject to the following conditions:
|
|
9
|
-
|
|
10
|
-
The above copyright notice and this permission notice shall be included in all
|
|
11
|
-
copies or substantial portions of the Software.
|
|
12
|
-
|
|
13
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
-
SOFTWARE.
|
|
20
|
-
|
|
21
|
-
The major part of vitest-websocket-mock implementation is based on jest-webosocket-mock, which is subject to the same license.
|
|
22
|
-
Here is the original copyright notice for jest-webosocket-mock:
|
|
23
|
-
|
|
24
1
|
Copyright 2018 Romain Bertrand
|
|
2
|
+
Copyright 2023 Akiomi Kamakura
|
|
25
3
|
|
|
26
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
27
5
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -8,13 +8,11 @@ A set of utilities and Vitest matchers to help testing complex websocket interac
|
|
|
8
8
|
A patched fork of [romgain/jest-websocket-mock](https://github.com/romgain/jest-websocket-mock).
|
|
9
9
|
|
|
10
10
|
**Examples:**
|
|
11
|
-
|
|
12
|
-
In particular
|
|
11
|
+
Several examples are provided in the [examples folder](https://github.com/akiomik/vitest-websocket-mock/blob/main/examples/).
|
|
12
|
+
In particular:
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- [testing a redux saga that manages a websocket connection](https://github.com/akiomik/vitest-websocket-mock/blob/main/examples/redux-saga/src/__tests__/saga.test.js)
|
|
17
|
-
- [testing a component using the saga above](https://github.com/akiomik/vitest-websocket-mock/blob/main/examples/redux-saga/src/__tests__/App.test.js)
|
|
14
|
+
- [testing a redux saga that manages a websocket connection](https://github.com/akiomik/vitest-websocket-mock/blob/main/examples/redux-saga/src/__tests__/saga.test.ts)
|
|
15
|
+
- [testing a component using the saga above](https://github.com/akiomik/vitest-websocket-mock/blob/main/examples/redux-saga/src/__tests__/App.test.tsx)
|
|
18
16
|
- [testing a component that manages a websocket connection using react hooks](https://github.com/akiomik/vitest-websocket-mock/blob/main/examples/hooks/src/App.test.tsx)
|
|
19
17
|
|
|
20
18
|
## Install
|
|
@@ -292,7 +290,7 @@ afterEach(() => {
|
|
|
292
290
|
|
|
293
291
|
## Known issues
|
|
294
292
|
|
|
295
|
-
`mock-socket` has a strong usage of delays (`setTimeout` to be more specific). This means using `
|
|
293
|
+
`mock-socket` has a strong usage of delays (`setTimeout` to be more specific). This means using `vi.useFakeTimers();` will cause issues such as the client appearing to never connect to the server.
|
|
296
294
|
|
|
297
295
|
While running the websocket server from tests within the vitest-dom environment (as opposed to node)
|
|
298
296
|
you may see errors of the nature:
|
|
@@ -343,8 +341,6 @@ restrict yourself to the browser APIs!
|
|
|
343
341
|
|
|
344
342
|
## Examples
|
|
345
343
|
|
|
346
|
-
|
|
344
|
+
For a real life example, see the
|
|
347
345
|
[examples directory](https://github.com/akiomik/vitest-websocket-mock/tree/main/examples),
|
|
348
|
-
and in particular the saga tests
|
|
349
|
-
|
|
350
|
-
NOTE: These examples are currently not working :cry:
|
|
346
|
+
and in particular the saga tests.
|
package/dist/index.d.ts
CHANGED
|
@@ -20,9 +20,6 @@ interface WSOptions extends ServerOptions {
|
|
|
20
20
|
jsonProtocol?: boolean;
|
|
21
21
|
}
|
|
22
22
|
type DeserializedMessage<TMessage = object> = string | TMessage;
|
|
23
|
-
interface MockWebSocket extends Omit<Client, 'close'> {
|
|
24
|
-
close(options?: CloseOptions): void;
|
|
25
|
-
}
|
|
26
23
|
declare class WS {
|
|
27
24
|
server: Server;
|
|
28
25
|
serializer: (deserializedMessage: DeserializedMessage) => string;
|
|
@@ -37,7 +34,7 @@ declare class WS {
|
|
|
37
34
|
get connected(): Promise<Client>;
|
|
38
35
|
get closed(): Promise<void>;
|
|
39
36
|
get nextMessage(): Promise<unknown>;
|
|
40
|
-
on(eventName: 'connection' | 'message' | 'close', callback: (socket:
|
|
37
|
+
on(eventName: 'connection' | 'message' | 'close', callback: (socket: Client) => void): void;
|
|
41
38
|
send(message: DeserializedMessage): void;
|
|
42
39
|
close(options?: CloseOptions): void;
|
|
43
40
|
error(options?: CloseOptions): void;
|
package/dist/index.js
CHANGED
|
@@ -71,7 +71,8 @@ var WS = class _WS {
|
|
|
71
71
|
const { jsonProtocol = false, ...serverOptions } = opts;
|
|
72
72
|
this.serializer = jsonProtocol ? JSON.stringify : identity;
|
|
73
73
|
this.deserializer = jsonProtocol ? JSON.parse : identity;
|
|
74
|
-
let connectionResolver
|
|
74
|
+
let connectionResolver;
|
|
75
|
+
let closedResolver;
|
|
75
76
|
this._isConnected = new Promise((done) => connectionResolver = done);
|
|
76
77
|
this._isClosed = new Promise((done) => closedResolver = done);
|
|
77
78
|
this.server = new Server(url, serverOptions);
|
|
@@ -136,7 +137,7 @@ var WS = class _WS {
|
|
|
136
137
|
|
|
137
138
|
// src/matchers.ts
|
|
138
139
|
var WAIT_DELAY = 1e3;
|
|
139
|
-
var TIMEOUT = Symbol("
|
|
140
|
+
var TIMEOUT = Symbol("timeout");
|
|
140
141
|
var makeInvalidWsMessage = function makeInvalidWsMessage2(ws, matcher) {
|
|
141
142
|
return this.utils.matcherHint(this.isNot ? `.not.${matcher}` : `.${matcher}`, "WS", "expected") + `
|
|
142
143
|
|
|
@@ -144,14 +145,14 @@ Expected the websocket object to be a valid WS mock.
|
|
|
144
145
|
Received: ${typeof ws}
|
|
145
146
|
${this.utils.printReceived(ws)}`;
|
|
146
147
|
};
|
|
147
|
-
|
|
148
|
-
async
|
|
148
|
+
var deriveToReceiveMessage = (name, fn) => {
|
|
149
|
+
return async function(ws, expected, options) {
|
|
149
150
|
const isWS = ws instanceof WS;
|
|
150
151
|
if (!isWS) {
|
|
151
152
|
return {
|
|
152
153
|
pass: this.isNot,
|
|
153
154
|
// always fail
|
|
154
|
-
message: makeInvalidWsMessage.bind(this, ws,
|
|
155
|
+
message: makeInvalidWsMessage.bind(this, ws, name)
|
|
155
156
|
};
|
|
156
157
|
}
|
|
157
158
|
const waitDelay = options?.timeout ?? WAIT_DELAY;
|
|
@@ -163,22 +164,28 @@ expect.extend({
|
|
|
163
164
|
return {
|
|
164
165
|
pass: this.isNot,
|
|
165
166
|
// always fail
|
|
166
|
-
message: () => this.utils.matcherHint(this.isNot ? ".not
|
|
167
|
+
message: () => this.utils.matcherHint(`${this.isNot ? ".not" : ""}.${name}`, "WS", "expected") + `
|
|
167
168
|
|
|
168
169
|
Expected the websocket server to receive a message,
|
|
169
170
|
but it didn't receive anything in ${waitDelay}ms.`
|
|
170
171
|
};
|
|
172
|
+
} else {
|
|
173
|
+
const received = messageOrTimeout;
|
|
174
|
+
const result = await Promise.resolve(fn.call(this, received, expected, options));
|
|
175
|
+
return { name, ...result };
|
|
171
176
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
var toReceiveMessage = deriveToReceiveMessage("toReceiveMessage", function(received, expected) {
|
|
180
|
+
const pass = this.equals(received, expected);
|
|
181
|
+
const message = pass ? () => this.utils.matcherHint(".not.toReceiveMessage", "WS", "expected") + `
|
|
175
182
|
|
|
176
183
|
Expected the next received message to not equal:
|
|
177
184
|
${this.utils.printExpected(expected)}
|
|
178
185
|
Received:
|
|
179
186
|
${this.utils.printReceived(received)}` : () => {
|
|
180
|
-
|
|
181
|
-
|
|
187
|
+
const diffString = diff(expected, received, { expand: this.expand });
|
|
188
|
+
return this.utils.matcherHint(".toReceiveMessage", "WS", "expected") + `
|
|
182
189
|
|
|
183
190
|
Expected the next received message to equal:
|
|
184
191
|
${this.utils.printExpected(expected)}
|
|
@@ -188,54 +195,64 @@ Received:
|
|
|
188
195
|
Difference:
|
|
189
196
|
|
|
190
197
|
${diffString}`;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
};
|
|
199
|
+
return {
|
|
200
|
+
actual: received,
|
|
201
|
+
expected,
|
|
202
|
+
message,
|
|
203
|
+
pass
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
var deriveToHaveReceivedMessage = (name, fn) => {
|
|
207
|
+
return function(ws, expected, options) {
|
|
201
208
|
const isWS = ws instanceof WS;
|
|
202
209
|
if (!isWS) {
|
|
203
210
|
return {
|
|
204
211
|
pass: this.isNot,
|
|
205
212
|
// always fail
|
|
206
|
-
message: makeInvalidWsMessage.bind(this, ws,
|
|
213
|
+
message: makeInvalidWsMessage.bind(this, ws, name)
|
|
207
214
|
};
|
|
208
215
|
}
|
|
209
|
-
const
|
|
210
|
-
|
|
216
|
+
const result = fn.call(this, ws.messages, expected, options);
|
|
217
|
+
return { name, ...result };
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
var toHaveReceivedMessages = deriveToHaveReceivedMessage(
|
|
221
|
+
"toHaveReceivedMessages",
|
|
222
|
+
function(received, expected) {
|
|
223
|
+
const equalities = expected.map(
|
|
224
|
+
(expectedMsg) => (
|
|
211
225
|
// object comparison to handle JSON protocols
|
|
212
|
-
|
|
226
|
+
received.some((receivedMsg) => this.equals(receivedMsg, expectedMsg))
|
|
213
227
|
)
|
|
214
228
|
);
|
|
215
|
-
const pass = this.isNot ?
|
|
229
|
+
const pass = this.isNot ? equalities.some(Boolean) : equalities.every(Boolean);
|
|
216
230
|
const message = pass ? () => this.utils.matcherHint(".not.toHaveReceivedMessages", "WS", "expected") + `
|
|
217
231
|
|
|
218
232
|
Expected the WS server to not have received the following messages:
|
|
219
|
-
${this.utils.printExpected(
|
|
233
|
+
${this.utils.printExpected(expected)}
|
|
220
234
|
But it received:
|
|
221
|
-
${this.utils.printReceived(
|
|
235
|
+
${this.utils.printReceived(received)}` : () => {
|
|
222
236
|
return this.utils.matcherHint(".toHaveReceivedMessages", "WS", "expected") + `
|
|
223
237
|
|
|
224
238
|
Expected the WS server to have received the following messages:
|
|
225
|
-
${this.utils.printExpected(
|
|
239
|
+
${this.utils.printExpected(expected)}
|
|
226
240
|
Received:
|
|
227
|
-
${this.utils.printReceived(
|
|
241
|
+
${this.utils.printReceived(received)}
|
|
228
242
|
|
|
229
243
|
`;
|
|
230
244
|
};
|
|
231
245
|
return {
|
|
232
|
-
actual:
|
|
233
|
-
expected
|
|
246
|
+
actual: received,
|
|
247
|
+
expected,
|
|
234
248
|
message,
|
|
235
|
-
name: "toHaveReceivedMessages",
|
|
236
249
|
pass
|
|
237
250
|
};
|
|
238
251
|
}
|
|
252
|
+
);
|
|
253
|
+
expect.extend({
|
|
254
|
+
toReceiveMessage,
|
|
255
|
+
toHaveReceivedMessages
|
|
239
256
|
});
|
|
240
257
|
export {
|
|
241
258
|
WS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-websocket-mock",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Mock websockets and assert complex websocket interactions with Vitest",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -34,26 +34,28 @@
|
|
|
34
34
|
"author": "Akiomi Kamakura",
|
|
35
35
|
"license": "MIT",
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
38
|
-
"@typescript-eslint/parser": "^5.
|
|
39
|
-
"@vitest/coverage-
|
|
40
|
-
"eslint": "^8.
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
|
38
|
+
"@typescript-eslint/parser": "^5.59.0",
|
|
39
|
+
"@vitest/coverage-v8": "^0.32.2",
|
|
40
|
+
"eslint": "^8.38.0",
|
|
41
41
|
"eslint-config-prettier": "^8.5.0",
|
|
42
42
|
"eslint-config-react-app": "^7.0.1",
|
|
43
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
44
|
+
"eslint-plugin-react-refresh": "^0.3.4",
|
|
43
45
|
"eslint-plugin-simple-import-sort": "^10.0.0",
|
|
44
46
|
"prettier": "^2.0.2",
|
|
45
47
|
"rimraf": "^4.1.2",
|
|
46
48
|
"tsup": "^7.0.0",
|
|
47
49
|
"typescript": "^4.0.2",
|
|
48
50
|
"vite": "^4.3.9",
|
|
49
|
-
"vitest": "^0.
|
|
51
|
+
"vitest": "^0.32.0"
|
|
50
52
|
},
|
|
51
53
|
"peerDependencies": {
|
|
52
|
-
"vitest": "^0.
|
|
54
|
+
"vitest": "^0.32.0"
|
|
53
55
|
},
|
|
54
56
|
"dependencies": {
|
|
55
57
|
"jest-diff": "^29.2.0",
|
|
56
|
-
"mock-socket": "^9.1
|
|
58
|
+
"mock-socket": "^9.2.1"
|
|
57
59
|
},
|
|
58
60
|
"files": [
|
|
59
61
|
"dist",
|