Skip to content

Commit 04d527d

Browse files
test: add tests for websocket events
leverage jest-websocket-mock
1 parent 7696078 commit 04d527d

File tree

3 files changed

+132
-39
lines changed

3 files changed

+132
-39
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"babel-jest": "latest",
4848
"eslint": "latest",
4949
"jest": "latest",
50+
"jest-websocket-mock": "^2.3.0",
5051
"nuxt-edge": "latest",
5152
"playwright": "latest",
5253
"siroc": "latest",

test/WebSocketManager.test.ts

+84-39
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,114 @@
11
import Vue from 'vue'
2+
import WS from 'jest-websocket-mock'
23
import WebSocketManager from '../src/templates/WebSocketManager'
34

5+
let ws = {} as WS
6+
let client = {} as WebSocketManager
7+
48
describe('WebSocketManager', () => {
5-
const getInstance = () => {
9+
beforeEach(async () => {
610
const emitter = new Vue()
7-
return new WebSocketManager('ws://localhost:8025', emitter, 1000)
8-
}
11+
12+
// create a WS instance, listening on port 8025 on localhost.
13+
ws = new WS('ws://localhost:8025')
14+
15+
// Connect to the mock websocket server.
16+
client = new WebSocketManager('ws://localhost:8025', emitter, 1000)
17+
18+
// Wait for the server to have established the connection.
19+
await ws.connected
20+
})
921

1022
afterEach(() => {
1123
jest.restoreAllMocks()
24+
WS.clean()
1225
})
1326

14-
test('constructor sets the properties and invokes connect method', () => {
15-
// Track connect method calls.
16-
jest.spyOn(WebSocketManager.prototype, 'connect')
27+
test('connect method establishes websocket connection', () => {
28+
// connect method is invoked by the constructor.
1729

18-
const instance = getInstance()
30+
// Assertions
31+
expect(client.url).toBe('ws://localhost:8025')
32+
expect(client.emitter).toBeInstanceOf(Vue)
33+
expect(client.reconnectInterval).toBe(1000)
34+
expect(client.ws).toBeInstanceOf(WebSocket)
35+
})
36+
37+
test('emits an event of the type `message` on receiving the data as a string', () => {
38+
// Mock
39+
Vue.prototype.$emit = jest.fn()
40+
41+
// Send dato the client.
42+
ws.send('Test message')
1943

2044
// Assertions
21-
expect(instance.url).toBe('ws://localhost:8025')
22-
expect(instance.emitter).toBeInstanceOf(Vue)
23-
expect(instance.reconnectInterval).toBe(1000)
24-
expect(instance.ws).toBeInstanceOf(WebSocket)
25-
expect(instance.connect).toBeCalled()
45+
expect(Vue.prototype.$emit.mock.calls[0][0]).toBe('message')
46+
expect(Vue.prototype.$emit.mock.calls[0][1].data).toBe('Test message')
2647
})
2748

28-
test('connect method establishes websocket connection', () => {
29-
// connect method is invoked by the constructor.
30-
const instance = getInstance()
49+
test('emits an event of the type based on value for the event key when the data is an object', () => {
50+
// Mock
51+
Vue.prototype.$emit = jest.fn()
52+
53+
// Send data to the client.
54+
ws.send(JSON.stringify({ event: 'socket', data: 'Hello world' }))
3155

3256
// Assertions
33-
expect(instance.reconnectInterval).toBe(1000)
34-
expect(instance.ws).toBeInstanceOf(WebSocket)
57+
expect(Vue.prototype.$emit.mock.calls[0][0]).toBe('socket')
58+
expect(Vue.prototype.$emit.mock.calls[0][1]).toBe('Hello world')
3559
})
3660

37-
test('ready method ensures the websocket connection is open', () => {
38-
const instance = getInstance()
61+
test('attempts reconnection on a close event which is not normal', async () => {
62+
// Mocks
63+
client.connect = jest.fn()
64+
jest.spyOn(global, 'setTimeout')
3965

66+
// Close the connection with a code that is not normal.
67+
ws.close({ wasClean: false, code: 1003, reason: 'nope' })
68+
69+
// Assertions
70+
expect(global.setTimeout).toBeCalledWith(expect.any(Function), 1000)
71+
72+
// Wait for 1s and assert for the reconnection attempt.
73+
await new Promise(resolve => setTimeout(resolve, 1000))
74+
expect(client.connect).toBeCalled()
75+
})
76+
77+
test('closes the websocket connection on error event', () => {
78+
// Mocks
79+
console.error = jest.fn(); // eslint-disable-line
80+
client.ws.close = jest.fn()
81+
82+
// Simulate an error and close the connection.
83+
ws.error()
84+
85+
// Assertions
86+
expect(console.error).toBeCalled(); // eslint-disable-line
87+
expect(client.ws.close).toBeCalled()
88+
})
89+
90+
test('ready method ensures the websocket connection is open', () => {
4091
// The promise is resolved straightaway if the readyState is not 1.
41-
expect(instance.ready()).resolves.toBe(undefined)
92+
expect(client.ready()).resolves.toBe(undefined)
4293
})
4394

4495
test('send method transmits the data received as a string', async () => {
45-
const instance = getInstance()
46-
4796
// Mock implementations of other function calls.
48-
jest.spyOn(instance, 'ready').mockResolvedValue(Promise.resolve())
49-
jest.spyOn(instance.ws, 'send').mockReturnValue(undefined)
97+
jest.spyOn(client, 'ready').mockResolvedValue(Promise.resolve())
98+
jest.spyOn(client.ws, 'send').mockReturnValue(undefined)
5099

51100
// Invoke send method with the message as a string.
52-
await instance.send('Hello world')
101+
await client.send('Hello world')
53102

54103
// Assertions
55-
expect(instance.ready).toBeCalled()
56-
expect(instance.ws.send).toBeCalledWith('Hello world')
104+
expect(client.ready).toBeCalled()
105+
expect(client.ws.send).toBeCalledWith('Hello world')
57106
})
58107

59108
test('send method transmits the data received as an object', async () => {
60-
const instance = getInstance()
61-
62109
// Mock implementations of other function calls.
63-
jest.spyOn(instance, 'ready').mockResolvedValue(Promise.resolve())
64-
jest.spyOn(instance.ws, 'send').mockReturnValue(undefined)
110+
jest.spyOn(client, 'ready').mockResolvedValue(Promise.resolve())
111+
jest.spyOn(client.ws, 'send').mockReturnValue(undefined)
65112

66113
// Invoke send method with the message as an object.
67114
const msg = {
@@ -74,23 +121,21 @@ describe('WebSocketManager', () => {
74121
.slice(2),
75122
date: Date.now()
76123
}
77-
await instance.send(msg)
124+
await client.send(msg)
78125

79126
// Assertions
80-
expect(instance.ready).toBeCalled()
81-
expect(instance.ws.send).toBeCalledWith(JSON.stringify(msg))
127+
expect(client.ready).toBeCalled()
128+
expect(client.ws.send).toBeCalledWith(JSON.stringify(msg))
82129
})
83130

84131
test('close method closes the websocket connection', () => {
85-
const instance = getInstance()
86-
87132
// Track WebSocket instance close method calls.
88-
instance.ws.close = jest.fn()
133+
client.ws.close = jest.fn()
89134

90135
// Invoke close method.
91-
instance.close()
136+
client.close()
92137

93138
// Assertion
94-
expect(instance.ws.close).toBeCalled()
139+
expect(client.ws.close).toBeCalled()
95140
})
96141
})

yarn.lock

+47
Original file line numberDiff line numberDiff line change
@@ -2631,6 +2631,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
26312631
dependencies:
26322632
color-convert "^2.0.1"
26332633

2634+
ansi-styles@^5.0.0:
2635+
version "5.2.0"
2636+
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
2637+
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
2638+
26342639
anymatch@^2.0.0:
26352640
version "2.0.0"
26362641
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -4689,6 +4694,11 @@ diff-sequences@^26.6.2:
46894694
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1"
46904695
integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==
46914696

4697+
diff-sequences@^27.5.1:
4698+
version "27.5.1"
4699+
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
4700+
integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
4701+
46924702
diffie-hellman@^5.0.0:
46934703
version "5.0.3"
46944704
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@@ -7090,6 +7100,16 @@ jest-diff@^26.0.0, jest-diff@^26.6.2:
70907100
jest-get-type "^26.3.0"
70917101
pretty-format "^26.6.2"
70927102

7103+
jest-diff@^27.0.2:
7104+
version "27.5.1"
7105+
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def"
7106+
integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
7107+
dependencies:
7108+
chalk "^4.0.0"
7109+
diff-sequences "^27.5.1"
7110+
jest-get-type "^27.5.1"
7111+
pretty-format "^27.5.1"
7112+
70937113
jest-docblock@^26.0.0:
70947114
version "26.0.0"
70957115
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5"
@@ -7138,6 +7158,11 @@ jest-get-type@^26.3.0:
71387158
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0"
71397159
integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==
71407160

7161+
jest-get-type@^27.5.1:
7162+
version "27.5.1"
7163+
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1"
7164+
integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
7165+
71417166
jest-haste-map@^26.6.2:
71427167
version "26.6.2"
71437168
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa"
@@ -7383,6 +7408,14 @@ jest-watcher@^26.6.2:
73837408
jest-util "^26.6.2"
73847409
string-length "^4.0.1"
73857410

7411+
jest-websocket-mock@^2.3.0:
7412+
version "2.3.0"
7413+
resolved "https://registry.yarnpkg.com/jest-websocket-mock/-/jest-websocket-mock-2.3.0.tgz#317e7d7f8ba54ba632a7300777b02b7ebb606845"
7414+
integrity sha512-kXhRRApRdT4hLG/4rhsfcR0Ke0OzqIsDj0P5t0dl5aiAftShSgoRqp/0pyjS5bh+b9GrIzmfkrV2cn9LxxvSvA==
7415+
dependencies:
7416+
jest-diff "^27.0.2"
7417+
mock-socket "^9.1.0"
7418+
73867419
jest-worker@^26.5.0, jest-worker@^26.6.2:
73877420
version "26.6.2"
73887421
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
@@ -8312,6 +8345,11 @@ mkdist@^0.1.1:
83128345
upath "^2.0.1"
83138346
vue-template-compiler "^2.6.12"
83148347

8348+
mock-socket@^9.1.0:
8349+
version "9.1.2"
8350+
resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.1.2.tgz#cce6cf2193aada937ba41de3288c5c1922fbd571"
8351+
integrity sha512-XKZkCnQ9ISOlTnaPg4LYYSMj7+6i78HyadYzLA5JM4465ibLdjappZD9Csnqc3Tfzep/eEK/LCJ29BTaLHoB1A==
8352+
83158353
modern-normalize@^1.1.0:
83168354
version "1.1.0"
83178355
resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7"
@@ -10082,6 +10120,15 @@ pretty-format@^26.0.0, pretty-format@^26.6.2:
1008210120
ansi-styles "^4.0.0"
1008310121
react-is "^17.0.1"
1008410122

10123+
pretty-format@^27.5.1:
10124+
version "27.5.1"
10125+
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
10126+
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
10127+
dependencies:
10128+
ansi-regex "^5.0.1"
10129+
ansi-styles "^5.0.0"
10130+
react-is "^17.0.1"
10131+
1008510132
pretty-hrtime@^1.0.3:
1008610133
version "1.0.3"
1008710134
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"

0 commit comments

Comments
 (0)