Skip to content

Commit dd87441

Browse files
committed
feat(server): server mode ws string option
1 parent 7f51859 commit dd87441

File tree

3 files changed

+231
-109
lines changed

3 files changed

+231
-109
lines changed

lib/utils/getSocketServerImplementation.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ function getSocketServerImplementation(options) {
99
if (options.serverMode === 'sockjs') {
1010
// eslint-disable-next-line global-require
1111
ServerImplementation = require('../servers/SockJSServer');
12+
} else if (options.serverMode === 'ws') {
13+
// eslint-disable-next-line global-require
14+
ServerImplementation = require('../servers/WebsocketServer');
1215
} else {
1316
try {
1417
// eslint-disable-next-line global-require, import/no-dynamic-require
@@ -29,7 +32,7 @@ function getSocketServerImplementation(options) {
2932

3033
if (!serverImplFound) {
3134
throw new Error(
32-
"serverMode must be a string denoting a default implementation (e.g. 'sockjs'), a full path to " +
35+
"serverMode must be a string denoting a default implementation (e.g. 'sockjs', 'ws'), a full path to " +
3336
'a JS file which exports a class extending BaseServer (webpack-dev-server/lib/servers/BaseServer) ' +
3437
'via require.resolve(...), or the class itself which extends BaseServer'
3538
);

test/server/serverMode-option.test.js

Lines changed: 187 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -8,144 +8,226 @@ const sockjs = require('sockjs');
88
const SockJS = require('sockjs-client/dist/sockjs');
99
const SockJSServer = require('../../lib/servers/SockJSServer');
1010
const config = require('../fixtures/simple-config/webpack.config');
11-
const testServer = require('../helpers/test-server');
1211
const BaseServer = require('../../lib/servers/BaseServer');
1312
const port = require('../ports-map')['serverMode-option'];
1413

1514
describe('serverMode option', () => {
15+
let mockedTestServer;
16+
let testServer;
1617
let server;
1718
let req;
19+
let getSocketServerImplementation;
1820

19-
afterEach((done) => {
20-
testServer.close(done);
21-
req = null;
22-
server = null;
23-
});
21+
const serverModes = [
22+
{
23+
title: 'as a string ("sockjs")',
24+
serverMode: 'sockjs',
25+
},
26+
{
27+
title: 'as a path ("sockjs")',
28+
serverMode: require.resolve('../../lib/servers/SockJSServer'),
29+
},
30+
{
31+
title: 'as a string ("ws")',
32+
serverMode: 'ws',
33+
},
34+
{
35+
title: 'as a path ("ws")',
36+
serverMode: require.resolve('../../lib/servers/WebsocketServer'),
37+
},
38+
{
39+
title: 'as a class (custom implementation)',
40+
serverMode: class CustomServer {},
41+
},
42+
{
43+
title: 'as a nonexistent path',
44+
serverMode: '/bad/path/to/implementation',
45+
},
46+
];
2447

25-
describe('as a string ("sockjs")', () => {
26-
beforeEach((done) => {
27-
server = testServer.start(
28-
config,
29-
{
30-
serverMode: 'sockjs',
31-
port,
32-
},
33-
done
34-
);
35-
req = request(`http://localhost:${port}`);
48+
describe('is passed to getSocketServerImplementation correctly', () => {
49+
beforeEach(() => {
50+
jest.mock('../../lib/utils/getSocketServerImplementation');
51+
// eslint-disable-next-line global-require
52+
getSocketServerImplementation = require('../../lib/utils/getSocketServerImplementation');
53+
getSocketServerImplementation.mockImplementation(() => {
54+
return class MockServer {
55+
// eslint-disable-next-line no-empty-function
56+
onConnection() {}
57+
};
58+
});
3659
});
3760

38-
it('sockjs path responds with a 200', (done) => {
39-
req.get('/sockjs-node').expect(200, done);
40-
});
41-
});
61+
afterEach((done) => {
62+
jest.resetAllMocks();
63+
jest.resetModules();
4264

43-
describe('as a path ("sockjs")', () => {
44-
beforeEach((done) => {
45-
server = testServer.start(
46-
config,
47-
{
48-
serverMode: require.resolve('../../lib/servers/SockJSServer'),
49-
port,
50-
},
51-
done
52-
);
53-
req = request(`http://localhost:${port}`);
65+
mockedTestServer.close(done);
5466
});
5567

56-
it('sockjs path responds with a 200', (done) => {
57-
req.get('/sockjs-node').expect(200, done);
68+
serverModes.forEach((data) => {
69+
it(data.title, (done) => {
70+
// eslint-disable-next-line global-require
71+
mockedTestServer = require('../helpers/test-server');
72+
mockedTestServer.start(
73+
config,
74+
{
75+
serverMode: data.serverMode,
76+
port,
77+
},
78+
() => {
79+
expect(getSocketServerImplementation.mock.calls.length).toEqual(1);
80+
expect(getSocketServerImplementation.mock.calls[0].length).toEqual(
81+
1
82+
);
83+
expect(
84+
getSocketServerImplementation.mock.calls[0][0].serverMode
85+
).toEqual(data.serverMode);
86+
done();
87+
}
88+
);
89+
});
5890
});
5991
});
6092

61-
describe('as a class ("sockjs")', () => {
62-
beforeEach((done) => {
63-
server = testServer.start(
64-
config,
65-
{
66-
serverMode: SockJSServer,
67-
port,
68-
},
69-
done
70-
);
71-
req = request(`http://localhost:${port}`);
93+
describe('passed to server', () => {
94+
beforeAll(() => {
95+
jest.unmock('../../lib/utils/getSocketServerImplementation');
96+
// eslint-disable-next-line global-require
97+
testServer = require('../helpers/test-server');
7298
});
7399

74-
it('sockjs path responds with a 200', (done) => {
75-
req.get('/sockjs-node').expect(200, done);
100+
afterEach((done) => {
101+
testServer.close(done);
102+
req = null;
103+
server = null;
76104
});
77-
});
78-
79-
describe('as a class (custom "sockjs" implementation)', () => {
80-
it('uses supplied server implementation', (done) => {
81-
let sockPath;
82-
server = testServer.start(
83-
config,
84-
{
85-
port,
86-
sockPath: '/foo/test/bar/',
87-
serverMode: class MySockJSServer extends BaseServer {
88-
constructor(serv) {
89-
super(serv);
90-
this.socket = sockjs.createServer({
91-
// Use provided up-to-date sockjs-client
92-
sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js',
93-
// Limit useless logs
94-
log: (severity, line) => {
95-
if (severity === 'error') {
96-
this.server.log.error(line);
97-
} else {
98-
this.server.log.debug(line);
99-
}
100-
},
101-
});
102-
103-
this.socket.installHandlers(this.server.listeningApp, {
104-
prefix: this.server.sockPath,
105-
});
106105

107-
sockPath = server.options.sockPath;
108-
}
106+
describe('as a string ("sockjs")', () => {
107+
beforeEach((done) => {
108+
server = testServer.start(
109+
config,
110+
{
111+
serverMode: 'sockjs',
112+
port,
113+
},
114+
done
115+
);
116+
req = request(`http://localhost:${port}`);
117+
});
109118

110-
send(connection, message) {
111-
connection.write(message);
112-
}
119+
it('sockjs path responds with a 200', (done) => {
120+
req.get('/sockjs-node').expect(200, done);
121+
});
122+
});
113123

114-
close(connection) {
115-
connection.close();
116-
}
124+
describe('as a path ("sockjs")', () => {
125+
beforeEach((done) => {
126+
server = testServer.start(
127+
config,
128+
{
129+
serverMode: require.resolve('../../lib/servers/SockJSServer'),
130+
port,
131+
},
132+
done
133+
);
134+
req = request(`http://localhost:${port}`);
135+
});
117136

118-
onConnection(f) {
119-
this.socket.on('connection', (connection) => {
120-
f(connection, connection.headers);
121-
});
122-
}
137+
it('sockjs path responds with a 200', (done) => {
138+
req.get('/sockjs-node').expect(200, done);
139+
});
140+
});
123141

124-
onConnectionClose(connection, f) {
125-
connection.on('close', f);
126-
}
142+
describe('as a class ("sockjs")', () => {
143+
beforeEach((done) => {
144+
server = testServer.start(
145+
config,
146+
{
147+
serverMode: SockJSServer,
148+
port,
127149
},
128-
},
129-
() => {
130-
expect(sockPath).toEqual('/foo/test/bar/');
131-
done();
132-
}
133-
);
150+
done
151+
);
152+
req = request(`http://localhost:${port}`);
153+
});
154+
155+
it('sockjs path responds with a 200', (done) => {
156+
req.get('/sockjs-node').expect(200, done);
157+
});
134158
});
135-
});
136159

137-
describe('as a path with nonexistent path', () => {
138-
it('should throw an error', () => {
139-
expect(() => {
160+
describe('as a class (custom "sockjs" implementation)', () => {
161+
let sockPath;
162+
it('uses supplied server implementation', (done) => {
140163
server = testServer.start(
141164
config,
142165
{
143-
serverMode: '/bad/path/to/implementation',
144166
port,
167+
sockPath: '/foo/test/bar/',
168+
serverMode: class MySockJSServer extends BaseServer {
169+
constructor(serv) {
170+
super(serv);
171+
this.socket = sockjs.createServer({
172+
// Use provided up-to-date sockjs-client
173+
sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js',
174+
// Limit useless logs
175+
log: (severity, line) => {
176+
if (severity === 'error') {
177+
this.server.log.error(line);
178+
} else {
179+
this.server.log.debug(line);
180+
}
181+
},
182+
});
183+
184+
this.socket.installHandlers(this.server.listeningApp, {
185+
prefix: this.server.sockPath,
186+
});
187+
188+
sockPath = server.options.sockPath;
189+
}
190+
191+
send(connection, message) {
192+
connection.write(message);
193+
}
194+
195+
close(connection) {
196+
connection.close();
197+
}
198+
199+
onConnection(f) {
200+
this.socket.on('connection', (connection) => {
201+
f(connection, connection.headers);
202+
});
203+
}
204+
205+
onConnectionClose(connection, f) {
206+
connection.on('close', f);
207+
}
208+
},
145209
},
146-
() => {}
210+
() => {
211+
expect(sockPath).toEqual('/foo/test/bar/');
212+
done();
213+
}
147214
);
148-
}).toThrow(/serverMode must be a string/);
215+
});
216+
});
217+
218+
describe('as a path with nonexistent path', () => {
219+
it('should throw an error', () => {
220+
expect(() => {
221+
server = testServer.start(
222+
config,
223+
{
224+
serverMode: '/bad/path/to/implementation',
225+
port,
226+
},
227+
() => {}
228+
);
229+
}).toThrow(/serverMode must be a string/);
230+
});
149231
});
150232
});
151233

0 commit comments

Comments
 (0)