diff --git a/lib/Server.js b/lib/Server.js index babaf9d40c..4a8910bf9c 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -45,7 +45,9 @@ class Server { this.logger, Server.findCacheDir() ); + } + initialize() { this.applyDevServerPlugin(); this.webSocketServerImplementation = getSocketServerImplementation( @@ -924,6 +926,8 @@ class Server { .then((foundPort) => { this.options.port = foundPort; + this.initialize(); + return this.server.listen( this.options.port, this.options.host, @@ -966,12 +970,16 @@ class Server { ); this.staticWatchers = []; - this.server.kill(() => { - // watchers must be closed before closing middleware - prom.then(() => { - this.middleware.close(callback); + if (this.server) { + this.server.kill(() => { + // watchers must be closed before closing middleware + prom.then(() => { + this.middleware.close(callback); + }); }); - }); + } else if (callback) { + callback(); + } } getStats(statsObj) { diff --git a/test/__snapshots__/validate-options.test.js.snap.webpack4 b/test/__snapshots__/validate-options.test.js.snap.webpack4 index c952ed9fa8..d056fd6a2b 100644 --- a/test/__snapshots__/validate-options.test.js.snap.webpack4 +++ b/test/__snapshots__/validate-options.test.js.snap.webpack4 @@ -635,8 +635,6 @@ exports[`options validate should throw an error on the "webSocketServer" option object { type?, options? }" `; -exports[`options validate should throw an error on the "webSocketServer" option with 'nonexistent-implementation' value 1`] = `"Error: When you use custom web socket implementation you must explicitly specify client.transport. client.transport must be a string denoting a default implementation (e.g. 'sockjs', 'ws') or a full path to a JS file via require.resolve(...) which exports a class "`; - exports[`options validate should throw an error on the "webSocketServer" option with 'null' value 1`] = ` "ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema. - options.webSocketServer should be one of these: diff --git a/test/__snapshots__/validate-options.test.js.snap.webpack5 b/test/__snapshots__/validate-options.test.js.snap.webpack5 index c952ed9fa8..d056fd6a2b 100644 --- a/test/__snapshots__/validate-options.test.js.snap.webpack5 +++ b/test/__snapshots__/validate-options.test.js.snap.webpack5 @@ -635,8 +635,6 @@ exports[`options validate should throw an error on the "webSocketServer" option object { type?, options? }" `; -exports[`options validate should throw an error on the "webSocketServer" option with 'nonexistent-implementation' value 1`] = `"Error: When you use custom web socket implementation you must explicitly specify client.transport. client.transport must be a string denoting a default implementation (e.g. 'sockjs', 'ws') or a full path to a JS file via require.resolve(...) which exports a class "`; - exports[`options validate should throw an error on the "webSocketServer" option with 'null' value 1`] = ` "ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema. - options.webSocketServer should be one of these: diff --git a/test/cli/client-option.test.js b/test/cli/client-option.test.js index 0b5222c3e4..f70dc58c24 100644 --- a/test/cli/client-option.test.js +++ b/test/cli/client-option.test.js @@ -172,24 +172,4 @@ describe('"client" CLI option', () => { expect(exitCode).toEqual(0); }); - - it('should add "/ws" web socket path by default', async () => { - const { exitCode, stdout } = await testBin( - null, - './test/fixtures/dev-server/client-default-path-config.js' - ); - - expect(exitCode).toEqual(0); - expect(stdout).toContain('ws%3A%2F%2F0.0.0.0%2Fws'); - }); - - it('should use "client.webSocketURL.pathname" from configuration', async () => { - const { exitCode, stdout } = await testBin( - null, - './test/fixtures/dev-server/client-custom-path-config.js' - ); - - expect(exitCode).toEqual(0); - expect(stdout).toContain('ws%3A%2F%2F0.0.0.0%2Fcustom%2Fpath'); - }); }); diff --git a/test/client/bundle.test.js b/test/client/bundle.test.js index 2e2173b716..928a5502a5 100644 --- a/test/client/bundle.test.js +++ b/test/client/bundle.test.js @@ -1,8 +1,9 @@ 'use strict'; +const webpack = require('webpack'); const acorn = require('acorn'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map').bundle; const isWebpack5 = require('../helpers/isWebpack5'); @@ -12,16 +13,36 @@ describe('bundle', () => { let server; let req; - beforeAll((done) => { - server = testServer.start( - { ...config, target: isWebpack5 ? ['es5', 'web'] : 'web' }, - { port }, - done - ); + beforeAll(async () => { + const compiler = webpack({ + ...config, + target: isWebpack5 ? ['es5', 'web'] : 'web', + }); + + server = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should get full user bundle and parse with ES5', async () => { const { text } = await req diff --git a/test/e2e/__snapshots__/stats.test.js.snap.webpack4 b/test/e2e/__snapshots__/stats.test.js.snap.webpack4 new file mode 100644 index 0000000000..5bb489c90e --- /dev/null +++ b/test/e2e/__snapshots__/stats.test.js.snap.webpack4 @@ -0,0 +1,55 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`stats should work using "{ assets: false }" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "{ warningsFilter: 'test' }" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "{}" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "errors-only" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "false" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "undefined" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; diff --git a/test/e2e/__snapshots__/stats.test.js.snap.webpack5 b/test/e2e/__snapshots__/stats.test.js.snap.webpack5 new file mode 100644 index 0000000000..d4d83d7fb6 --- /dev/null +++ b/test/e2e/__snapshots__/stats.test.js.snap.webpack5 @@ -0,0 +1,64 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`stats should work and respect the "ignoreWarnings" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "{ assets: false }" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "{ warningsFilter: 'test' }" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "{}" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "errors-only" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "false" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`stats should work using "undefined" value for the "stats" option 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; diff --git a/test/e2e/__snapshots__/web-socket-server-url.test.js.snap.webpack4 b/test/e2e/__snapshots__/web-socket-server-url.test.js.snap.webpack4 index 6ce65f3498..4a006deca8 100644 --- a/test/e2e/__snapshots__/web-socket-server-url.test.js.snap.webpack4 +++ b/test/e2e/__snapshots__/web-socket-server-url.test.js.snap.webpack4 @@ -107,6 +107,72 @@ Array [ exports[`web socket server URL should work behind proxy, when hostnames are same and ports are different ("ws"): page errors 1`] = `Array []`; +exports[`web socket server URL should work behind proxy, when the "host" option is "local-ip" and the "port" option is "auto" ("sockjs"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work behind proxy, when the "host" option is "local-ip" and the "port" option is "auto" ("sockjs"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work behind proxy, when the "host" option is "local-ip" and the "port" option is "auto" ("ws"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work behind proxy, when the "host" option is "local-ip" and the "port" option is "auto" ("ws"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work when "host" option is "local-ip" ("sockjs"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work when "host" option is "local-ip" ("sockjs"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work when "host" option is "local-ip" ("ws"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work when "host" option is "local-ip" ("ws"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work when "host" option is "local-ipv4" ("sockjs"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work when "host" option is "local-ipv4" ("sockjs"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work when "host" option is "local-ipv4" ("ws"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work when "host" option is "local-ipv4" ("ws"): page errors 1`] = `Array []`; + exports[`web socket server URL should work when "host" option is IPv4 ("sockjs"): console messages 1`] = ` Array [ "[HMR] Waiting for update signal from WDS...", diff --git a/test/e2e/__snapshots__/web-socket-server-url.test.js.snap.webpack5 b/test/e2e/__snapshots__/web-socket-server-url.test.js.snap.webpack5 index 6ce65f3498..4a006deca8 100644 --- a/test/e2e/__snapshots__/web-socket-server-url.test.js.snap.webpack5 +++ b/test/e2e/__snapshots__/web-socket-server-url.test.js.snap.webpack5 @@ -107,6 +107,72 @@ Array [ exports[`web socket server URL should work behind proxy, when hostnames are same and ports are different ("ws"): page errors 1`] = `Array []`; +exports[`web socket server URL should work behind proxy, when the "host" option is "local-ip" and the "port" option is "auto" ("sockjs"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work behind proxy, when the "host" option is "local-ip" and the "port" option is "auto" ("sockjs"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work behind proxy, when the "host" option is "local-ip" and the "port" option is "auto" ("ws"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work behind proxy, when the "host" option is "local-ip" and the "port" option is "auto" ("ws"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work when "host" option is "local-ip" ("sockjs"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work when "host" option is "local-ip" ("sockjs"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work when "host" option is "local-ip" ("ws"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work when "host" option is "local-ip" ("ws"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work when "host" option is "local-ipv4" ("sockjs"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work when "host" option is "local-ipv4" ("sockjs"): page errors 1`] = `Array []`; + +exports[`web socket server URL should work when "host" option is "local-ipv4" ("ws"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Hot Module Replacement enabled.", + "[webpack-dev-server] Live Reloading enabled.", +] +`; + +exports[`web socket server URL should work when "host" option is "local-ipv4" ("ws"): page errors 1`] = `Array []`; + exports[`web socket server URL should work when "host" option is IPv4 ("sockjs"): console messages 1`] = ` Array [ "[HMR] Waiting for update signal from WDS...", diff --git a/test/e2e/stats.test.js b/test/e2e/stats.test.js new file mode 100644 index 0000000000..9ed57a3f37 --- /dev/null +++ b/test/e2e/stats.test.js @@ -0,0 +1,141 @@ +'use strict'; + +const webpack = require('webpack'); +const Server = require('../../lib/Server'); +const config = require('../fixtures/client-config/webpack.config'); +const runBrowser = require('../helpers/run-browser'); +const port = require('../ports-map').logging; + +global.console.log = jest.fn(); + +describe('stats', () => { + const cases = [ + { + title: 'should work using "{}" value for the "stats" option', + webpackOptions: { + stats: {}, + }, + }, + { + title: 'should work using "undefined" value for the "stats" option', + webpackOptions: { + // eslint-disable-next-line no-undefined + stats: undefined, + }, + }, + { + title: 'should work using "false" value for the "stats" option', + webpackOptions: { + stats: false, + }, + }, + { + title: 'should work using "errors-only" value for the "stats" option', + webpackOptions: { + stats: 'errors-only', + }, + }, + { + title: + 'should work using "{ assets: false }" value for the "stats" option', + webpackOptions: { + stats: { + assets: false, + }, + }, + }, + { + title: + 'should work using "{ warningsFilter: \'test\' }" value for the "stats" option', + webpackOptions: { + plugins: [ + { + apply(compiler) { + compiler.hooks.thisCompilation.tap( + 'warnings-webpack-plugin', + (compilation) => { + compilation.warnings.push( + new Error('Warning from compilation') + ); + } + ); + }, + }, + ], + stats: { warningsFilter: /Warning from compilation/ }, + }, + }, + ]; + + if (webpack.version.startsWith('5')) { + cases.push({ + title: 'should work and respect the "ignoreWarnings" option', + webpackOptions: { + plugins: [ + { + apply(compiler) { + compiler.hooks.thisCompilation.tap( + 'warnings-webpack-plugin', + (compilation) => { + compilation.warnings.push( + new Error('Warning from compilation') + ); + } + ); + }, + }, + ], + ignoreWarnings: [/Warning from compilation/], + }, + }); + } + + cases.forEach((testCase) => { + it(testCase.title, async () => { + const compiler = webpack({ ...config, ...testCase.webpackOptions }); + const devServerOptions = Object.assign( + {}, + { + host: '127.0.0.1', + port, + } + ); + const server = new Server(devServerOptions, compiler); + + await new Promise((resolve, reject) => { + server.listen(devServerOptions.port, devServerOptions.host, (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + const { page, browser } = await runBrowser(); + + const consoleMessages = []; + + page.on('console', (message) => { + consoleMessages.push(message); + }); + + await page.goto(`http://localhost:${port}/main`, { + waitUntil: 'networkidle0', + }); + + expect( + consoleMessages.map((message) => message.text()) + ).toMatchSnapshot(); + + await browser.close(); + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + }); +}); diff --git a/test/e2e/web-socket-server-url.test.js b/test/e2e/web-socket-server-url.test.js index d5e865d205..d65edd53d6 100644 --- a/test/e2e/web-socket-server-url.test.js +++ b/test/e2e/web-socket-server-url.test.js @@ -21,27 +21,6 @@ describe('web socket server URL', () => { const proxyHost = devServerHost; const proxyPort = port2; - function startProxy(callback) { - const app = express(); - app.use( - '/', - createProxyMiddleware({ - target: `http://${devServerHost}:${devServerPort}`, - ws: true, - changeOrigin: true, - logLevel: 'warn', - }) - ); - - return app.listen(proxyPort, proxyHost, callback); - } - - const proxy = await new Promise((resolve) => { - const proxyCreated = startProxy(() => { - resolve(proxyCreated); - }); - }); - const compiler = webpack(config); const devServerOptions = { webSocketServer, @@ -63,6 +42,27 @@ describe('web socket server URL', () => { }); }); + function startProxy(callback) { + const app = express(); + app.use( + '/', + createProxyMiddleware({ + target: `http://${devServerHost}:${devServerPort}`, + ws: true, + changeOrigin: true, + logLevel: 'warn', + }) + ); + + return app.listen(proxyPort, proxyHost, callback); + } + + const proxy = await new Promise((resolve) => { + const proxyCreated = startProxy(() => { + resolve(proxyCreated); + }); + }); + const { page, browser } = await runBrowser(); const pageErrors = []; @@ -127,27 +127,6 @@ describe('web socket server URL', () => { const proxyHost = internalIp.v4.sync(); const proxyPort = port1; - function startProxy(callback) { - const app = express(); - app.use( - '/', - createProxyMiddleware({ - target: `http://${devServerHost}:${devServerPort}`, - ws: true, - changeOrigin: true, - logLevel: 'warn', - }) - ); - - return app.listen(proxyPort, proxyHost, callback); - } - - const proxy = await new Promise((resolve) => { - const proxyCreated = startProxy(() => { - resolve(proxyCreated); - }); - }); - const compiler = webpack(config); const devServerOptions = { webSocketServer, @@ -169,6 +148,27 @@ describe('web socket server URL', () => { }); }); + function startProxy(callback) { + const app = express(); + app.use( + '/', + createProxyMiddleware({ + target: `http://${devServerHost}:${devServerPort}`, + ws: true, + changeOrigin: true, + logLevel: 'warn', + }) + ); + + return app.listen(proxyPort, proxyHost, callback); + } + + const proxy = await new Promise((resolve) => { + const proxyCreated = startProxy(() => { + resolve(proxyCreated); + }); + }); + const { page, browser } = await runBrowser(); const pageErrors = []; @@ -233,6 +233,32 @@ describe('web socket server URL', () => { const proxyHost = internalIp.v4.sync(); const proxyPort = port2; + const compiler = webpack(config); + const devServerOptions = { + client: { + webSocketURL: { + hostname: devServerHost, + }, + }, + webSocketServer, + port: devServerPort, + host: devServerHost, + allowedHosts: 'all', + }; + const server = new Server(devServerOptions, compiler); + + await new Promise((resolve, reject) => { + server.listen(devServerOptions.port, devServerOptions.host, (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + function startProxy(callback) { const app = express(); app.use( @@ -254,16 +280,76 @@ describe('web socket server URL', () => { }); }); + const { page, browser } = await runBrowser(); + + const pageErrors = []; + const consoleMessages = []; + + page + .on('console', (message) => { + consoleMessages.push(message); + }) + .on('pageerror', (error) => { + pageErrors.push(error); + }); + + const webSocketRequests = []; + + if (webSocketServer === 'ws') { + const client = page._client; + + client.on('Network.webSocketCreated', (test) => { + webSocketRequests.push(test); + }); + } else { + page.on('request', (request) => { + if (/\/ws\//.test(request.url())) { + webSocketRequests.push({ url: request.url() }); + } + }); + } + + await page.goto(`http://${proxyHost}:${proxyPort}/main`, { + waitUntil: 'networkidle0', + }); + + const webSocketRequest = webSocketRequests[0]; + + expect(webSocketRequest.url).toContain( + `${websocketURLProtocol}://${devServerHost}:${devServerPort}/ws` + ); + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + 'console messages' + ); + expect(pageErrors).toMatchSnapshot('page errors'); + + proxy.close(); + + await browser.close(); + await new Promise((resolve, reject) => { + server.close((error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + }); + + it(`should work behind proxy, when the "host" option is "local-ip" and the "port" option is "auto" ("${webSocketServer}")`, async () => { + process.env.WEBPACK_DEV_SERVER_BASE_PORT = 40000; + + const proxyHost = internalIp.v4.sync(); + const proxyPort = port2; + const compiler = webpack(config); const devServerOptions = { - client: { - webSocketURL: { - hostname: devServerHost, - }, - }, webSocketServer, - port: devServerPort, - host: devServerHost, + port: 'auto', + host: 'local-ip', allowedHosts: 'all', }; const server = new Server(devServerOptions, compiler); @@ -280,6 +366,31 @@ describe('web socket server URL', () => { }); }); + const resolvedHost = server.options.host; + const resolvedPort = server.options.port; + + function startProxy(callback) { + const app = express(); + + app.use( + '/', + createProxyMiddleware({ + target: `http://${resolvedHost}:${resolvedPort}`, + ws: true, + changeOrigin: true, + logLevel: 'warn', + }) + ); + + return app.listen(proxyPort, proxyHost, callback); + } + + const proxy = await new Promise((resolve) => { + const proxyCreated = startProxy(() => { + resolve(proxyCreated); + }); + }); + const { page, browser } = await runBrowser(); const pageErrors = []; @@ -316,7 +427,7 @@ describe('web socket server URL', () => { const webSocketRequest = webSocketRequests[0]; expect(webSocketRequest.url).toContain( - `${websocketURLProtocol}://${devServerHost}:${devServerPort}/ws` + `${websocketURLProtocol}://${resolvedHost}:${resolvedPort}/ws` ); expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 'console messages' @@ -324,6 +435,7 @@ describe('web socket server URL', () => { expect(pageErrors).toMatchSnapshot('page errors'); proxy.close(); + await browser.close(); await new Promise((resolve, reject) => { server.close((error) => { @@ -336,6 +448,8 @@ describe('web socket server URL', () => { resolve(); }); }); + + delete process.env.WEBPACK_DEV_SERVER_BASE_PORT; }); it(`should work with the "client.webSocketURL.protocol" option ("${webSocketServer}")`, async () => { @@ -1532,12 +1646,13 @@ describe('web socket server URL', () => { }); }); - it(`should work when "port" option is "auto" ("${webSocketServer}")`, async () => { + it(`should work when "host" option is IPv4 ("${webSocketServer}")`, async () => { + const hostname = internalIp.v4.sync(); const compiler = webpack(config); const devServerOptions = { webSocketServer, - port: 'auto', - host: '0.0.0.0', + port: port1, + host: hostname, }; const server = new Server(devServerOptions, compiler); @@ -1582,14 +1697,14 @@ describe('web socket server URL', () => { }); } - await page.goto(`http://127.0.0.1:8080/main`, { + await page.goto(`http://${hostname}:${port1}/main`, { waitUntil: 'networkidle0', }); const webSocketRequest = webSocketRequests[0]; expect(webSocketRequest.url).toContain( - `${websocketURLProtocol}://127.0.0.1:8080/ws` + `${websocketURLProtocol}://${hostname}:${port1}/ws` ); expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 'console messages' @@ -1610,12 +1725,13 @@ describe('web socket server URL', () => { }); }); - it(`should work when "host" option is IPv4 ("${webSocketServer}")`, async () => { + it(`should work when "host" option is "local-ip" ("${webSocketServer}")`, async () => { + const hostname = internalIp.v4.sync(); const compiler = webpack(config); const devServerOptions = { webSocketServer, port: port1, - host: internalIp.v4.sync(), + host: 'local-ip', }; const server = new Server(devServerOptions, compiler); @@ -1660,14 +1776,14 @@ describe('web socket server URL', () => { }); } - await page.goto(`http://${internalIp.v4.sync()}:${port1}/main`, { + await page.goto(`http://${hostname}:${port1}/main`, { waitUntil: 'networkidle0', }); const webSocketRequest = webSocketRequests[0]; expect(webSocketRequest.url).toContain( - `${websocketURLProtocol}://${internalIp.v4.sync()}:${port1}/ws` + `${websocketURLProtocol}://${hostname}:${port1}/ws` ); expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( 'console messages' @@ -1688,6 +1804,169 @@ describe('web socket server URL', () => { }); }); + it(`should work when "host" option is "local-ipv4" ("${webSocketServer}")`, async () => { + const hostname = internalIp.v4.sync(); + const compiler = webpack(config); + const devServerOptions = { + webSocketServer, + port: port1, + host: 'local-ipv4', + }; + const server = new Server(devServerOptions, compiler); + + await new Promise((resolve, reject) => { + server.listen(devServerOptions.port, devServerOptions.host, (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + const { page, browser } = await runBrowser(); + + const pageErrors = []; + const consoleMessages = []; + + page + .on('console', (message) => { + consoleMessages.push(message); + }) + .on('pageerror', (error) => { + pageErrors.push(error); + }); + + const webSocketRequests = []; + + if (webSocketServer === 'ws') { + const client = page._client; + + client.on('Network.webSocketCreated', (test) => { + webSocketRequests.push(test); + }); + } else { + page.on('request', (request) => { + if (/\/ws\//.test(request.url())) { + webSocketRequests.push({ url: request.url() }); + } + }); + } + + await page.goto(`http://${hostname}:${port1}/main`, { + waitUntil: 'networkidle0', + }); + + const webSocketRequest = webSocketRequests[0]; + + expect(webSocketRequest.url).toContain( + `${websocketURLProtocol}://${hostname}:${port1}/ws` + ); + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + 'console messages' + ); + expect(pageErrors).toMatchSnapshot('page errors'); + + await browser.close(); + await new Promise((resolve, reject) => { + server.close((error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + }); + + it(`should work when "port" option is "auto" ("${webSocketServer}")`, async () => { + process.env.WEBPACK_DEV_SERVER_BASE_PORT = 50000; + + const compiler = webpack(config); + const devServerOptions = { + webSocketServer, + port: 'auto', + host: '0.0.0.0', + }; + const server = new Server(devServerOptions, compiler); + + await new Promise((resolve, reject) => { + server.listen(devServerOptions.port, devServerOptions.host, (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + const resolvedFreePort = server.options.port; + + const { page, browser } = await runBrowser(); + + const pageErrors = []; + const consoleMessages = []; + + page + .on('console', (message) => { + consoleMessages.push(message); + }) + .on('pageerror', (error) => { + pageErrors.push(error); + }); + + const webSocketRequests = []; + + if (webSocketServer === 'ws') { + const client = page._client; + + client.on('Network.webSocketCreated', (request) => { + webSocketRequests.push(request); + }); + } else { + page.on('request', (request) => { + if (/\/ws\//.test(request.url())) { + webSocketRequests.push({ url: request.url() }); + } + }); + } + + await page.goto(`http://127.0.0.1:${resolvedFreePort}/main`, { + waitUntil: 'networkidle0', + }); + + const webSocketRequest = webSocketRequests[0]; + + expect(webSocketRequest.url).toContain( + `${websocketURLProtocol}://127.0.0.1:${resolvedFreePort}/ws` + ); + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + 'console messages' + ); + expect(pageErrors).toMatchSnapshot('page errors'); + + await browser.close(); + await new Promise((resolve, reject) => { + server.close((error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + delete process.env.WEBPACK_DEV_SERVER_BASE_PORT; + }); + it(`should work with "client.webSocketURL.*" options ("${webSocketServer}")`, async () => { const compiler = webpack(config); const devServerOptions = { diff --git a/test/integration/ModuleFederation.test.js b/test/integration/ModuleFederation.test.js index afca1fe535..27625c6fa5 100644 --- a/test/integration/ModuleFederation.test.js +++ b/test/integration/ModuleFederation.test.js @@ -1,8 +1,9 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); const requireFromString = require('require-from-string'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const simpleConfig = require('../fixtures/module-federation-config/webpack.config'); const objectEntryConfig = require('../fixtures/module-federation-config/webpack.object-entry.config'); const multiConfig = require('../fixtures/module-federation-config/webpack.multi.config'); @@ -24,33 +25,57 @@ describe('module federation', () => { let server; let req; - beforeAll((done) => { - server = testServer.start(config, { port }, done); + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should use the last entry export', async () => { const { text, statusCode } = await req.get('/main.js'); + expect(statusCode).toEqual(200); expect(text).toContain('entry1'); let exports; + expect(() => { exports = requireFromString(text); }).not.toThrow(); - expect(exports).toEqual('entry2'); }); if (title === 'object multi-entry config') { it('should support the named entry export', async () => { const { text, statusCode } = await req.get('/foo.js'); + expect(statusCode).toEqual(200); expect(text).not.toContain('entry2'); let exports; + expect(() => { exports = requireFromString(text); }).not.toThrow(); @@ -64,20 +89,44 @@ describe('module federation', () => { let server; let req; - beforeAll((done) => { - server = testServer.start(pluginConfig, { port }, done); + beforeAll(async () => { + const compiler = webpack(pluginConfig); + + server = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should contain hot script', async () => { - const { statusCode } = await req.get('/remoteEntry.js'); - expect(statusCode).toEqual(200); - await req.get('/main.js').expect(200, /webpack\/hot\/dev-server\.js/); - await req - .get('/remoteEntry.js') - .expect(200, /webpack\/hot\/dev-server\.js/); + const responseRemoteEntry = await req.get('/remoteEntry.js'); + + expect(responseRemoteEntry.statusCode).toEqual(200); + expect(responseRemoteEntry.text).toMatch(/webpack\/hot\/dev-server\.js/); + + const responseMain = await req.get('/main.js'); + + expect(responseMain.statusCode).toEqual(200); + expect(responseMain.text).toMatch(/webpack\/hot\/dev-server\.js/); }); }); }); diff --git a/test/integration/MultiCompiler.test.js b/test/integration/MultiCompiler.test.js index 0a2d2798d1..23c99ea184 100644 --- a/test/integration/MultiCompiler.test.js +++ b/test/integration/MultiCompiler.test.js @@ -1,7 +1,8 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/multi-compiler-config/webpack.config'); const port = require('../ports-map')['multi-compiler']; @@ -9,18 +10,40 @@ describe('multi compiler', () => { let server; let req; - beforeAll((done) => { - server = testServer.start(config, { port }, done); + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should handle GET request to bundle', async () => { - const res = await req.get('/main.js'); - expect(res.headers['content-type']).toEqual( + const response = await req.get('/main.js'); + + expect(response.headers['content-type']).toEqual( 'application/javascript; charset=utf-8' ); - expect(res.status).toEqual(200); + expect(response.status).toEqual(200); }); }); diff --git a/test/integration/UniversalCompiler.test.js b/test/integration/UniversalCompiler.test.js index 9b0c5d6f64..9689dd6fc3 100644 --- a/test/integration/UniversalCompiler.test.js +++ b/test/integration/UniversalCompiler.test.js @@ -1,7 +1,8 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/universal-compiler-config/webpack.config'); const port = require('../ports-map')['universal-compiler']; @@ -9,32 +10,55 @@ describe('universal compiler', () => { let server; let req; - beforeAll((done) => { - server = testServer.start(config, { port }, done); + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('client bundle should have the inlined the client runtime', async () => { - const res = await req.get('/client.js'); - expect(res.headers['content-type']).toEqual( + const response = await req.get('/client.js'); + + expect(response.headers['content-type']).toEqual( 'application/javascript; charset=utf-8' ); - expect(res.status).toEqual(200); - expect(res.text).toContain('Hello from the client'); - expect(res.text).toContain('WebsocketClient'); + expect(response.status).toEqual(200); + expect(response.text).toContain('Hello from the client'); + expect(response.text).toContain('WebsocketClient'); }); it('server bundle should NOT have the inlined the client runtime', async () => { // we wouldn't normally request a server bundle // but we'll do it here to check the contents - const res = await req.get('/server.js'); - expect(res.headers['content-type']).toEqual( + const response = await req.get('/server.js'); + + expect(response.headers['content-type']).toEqual( 'application/javascript; charset=utf-8' ); - expect(res.status).toEqual(200); - expect(res.text).toContain('Hello from the server'); - expect(res.text).not.toContain('WebsocketClient'); + expect(response.status).toEqual(200); + expect(response.text).toContain('Hello from the server'); + expect(response.text).not.toContain('WebsocketClient'); }); }); diff --git a/test/server/Server.test.js b/test/server/Server.test.js index 693f6807e9..50cf56339d 100644 --- a/test/server/Server.test.js +++ b/test/server/Server.test.js @@ -19,8 +19,6 @@ const baseDevConfig = { static: false, }; -const createServer = (compiler, options) => new Server(options, compiler); - describe('Server', () => { describe('sockjs', () => { it('add decorateConnection', () => { @@ -40,6 +38,7 @@ describe('Server', () => { (compilation) => { const mainDeps = compilation.entries.get('main').dependencies; const globalDeps = compilation.globalEntry.dependencies; + entries = globalDeps .concat(mainDeps) .map((dep) => relative('.', dep.request).split(sep)); @@ -54,73 +53,68 @@ describe('Server', () => { it('add hot option', (done) => { const compiler = webpack(config); - const server = createServer( - compiler, - Object.assign({}, baseDevConfig, { - hot: true, - }) - ); - - getEntries(server); + const server = new Server({ ...baseDevConfig, hot: true }, compiler); compiler.hooks.done.tap('webpack-dev-server', () => { expect(entries).toMatchSnapshot(); - server.close(done); + + server.close(() => { + done(); + }); }); - compiler.run(() => {}); + server.listen(port, 'localhost', (error) => { + if (error) { + throw error; + } + + getEntries(server); + }); }); - // TODO: remove this after plugin support is published - it('should create and run server with old parameters order', (done) => { + it('add hot-only option', (done) => { const compiler = webpack(config); - const server = new Server(compiler, baseDevConfig); - - getEntries(server); + const server = new Server({ ...baseDevConfig, hot: 'only' }, compiler); compiler.hooks.done.tap('webpack-dev-server', () => { - expect(entries).toMatchSnapshot('oldparam'); + expect(entries).toMatchSnapshot(); + server.close(done); }); - compiler.run(() => {}); + server.listen(port, 'localhost', (error) => { + if (error) { + throw error; + } + + getEntries(server); + }); }); // TODO: remove this after plugin support is published - it('should create and run server with MultiCompiler with old parameters order', (done) => { - const compiler = webpack([config, config]); + it('should create and run server with old parameters order', (done) => { + const compiler = webpack(config); const server = new Server(compiler, baseDevConfig); compiler.hooks.done.tap('webpack-dev-server', () => { + expect(entries).toMatchSnapshot('oldparam'); + server.close(done); }); - compiler.run(() => {}); - }); - - it('add hot-only option', (done) => { - const compiler = webpack(config); - const server = createServer( - compiler, - Object.assign({}, baseDevConfig, { - hot: 'only', - }) - ); - - getEntries(server); + server.listen(port, 'localhost', (error) => { + if (error) { + throw error; + } - compiler.hooks.done.tap('webpack-dev-server', () => { - expect(entries).toMatchSnapshot(); - server.close(done); + getEntries(server); }); - - compiler.run(() => {}); }); }); it('test server error reporting', () => { const compiler = webpack(config); - const server = createServer(compiler, baseDevConfig); + const server = new Server(baseDevConfig, compiler); const emitError = () => server.server.emit('error', new Error('Error !!!')); @@ -149,7 +143,7 @@ describe('Server', () => { }); const compiler = webpack(config); - const server = createServer(compiler, baseDevConfig); + const server = new Server(baseDevConfig, compiler); compiler.hooks.done.tap('webpack-dev-server', (s) => { const output = server.getStats(s); @@ -158,7 +152,6 @@ describe('Server', () => { server.close(done); }); - compiler.run(() => {}); server.listen(port, 'localhost'); }); }); @@ -310,7 +303,9 @@ describe('Server', () => { const headers = { host: 'localhost', }; - server = createServer(compiler, options); + + server = new Server(options, compiler); + if (!server.checkHostHeader(headers)) { throw new Error("Validation didn't fail"); } @@ -327,7 +322,7 @@ describe('Server', () => { host: '127.0.0.1', }; - server = createServer(compiler, options); + server = new Server(options, compiler); if (!server.checkHostHeader(headers)) { throw new Error("Validation didn't fail"); @@ -346,7 +341,7 @@ describe('Server', () => { '[ad42::1de2:54c2:c2fa:1234]:8080', ]; - server = createServer(compiler, options); + server = new Server(options, compiler); tests.forEach((test) => { const headers = { host: test }; @@ -368,7 +363,7 @@ describe('Server', () => { host: 'test.hostname:80', }; - server = createServer(compiler, options); + server = new Server(options, compiler); if (server.checkHostHeader(headers)) { throw new Error("Validation didn't fail"); @@ -385,7 +380,7 @@ describe('Server', () => { origin: 'https://test.host', }; - server = createServer(compiler, options); + server = new Server(options, compiler); if (!server.checkOriginHeader(headers)) { throw new Error("Validation didn't fail"); @@ -404,7 +399,7 @@ describe('Server', () => { origin: 'https://test.host', }; - server = createServer(compiler, options); + server = new Server(options, compiler); if (!server.checkOriginHeader(headers)) { throw new Error("Validation didn't fail"); @@ -416,16 +411,21 @@ describe('Server', () => { describe('Testing callback functions on calling invalidate without callback', () => { it('should use default `noop` callback', (done) => { const compiler = webpack(config); - const server = createServer(compiler, baseDevConfig); - - server.invalidate(); - expect(server.middleware.context.callbacks.length).toEqual(1); + const server = new Server(baseDevConfig, compiler); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(done); }); - compiler.run(() => {}); + server.listen(port, '127.0.0.1', (error) => { + if (error) { + throw error; + } + + server.invalidate(); + + expect(server.middleware.context.callbacks.length).toEqual(1); + }); }); }); @@ -433,17 +433,21 @@ describe('Server', () => { it('should use `callback` function', (done) => { const compiler = webpack(config); const callback = jest.fn(); - const server = createServer(compiler, baseDevConfig); - - server.invalidate(callback); - - expect(server.middleware.context.callbacks[0]).toBe(callback); + const server = new Server(baseDevConfig, compiler); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(done); }); - compiler.run(() => {}); + server.listen(port, '127.0.0.1', (error) => { + if (error) { + throw error; + } + + server.invalidate(callback); + + expect(server.middleware.context.callbacks[0]).toBe(callback); + }); }); }); }); diff --git a/test/server/__snapshots__/webSocketServer-option.test.js.snap.webpack4 b/test/server/__snapshots__/webSocketServer-option.test.js.snap.webpack4 index 1430cc503f..0bd27aee0c 100644 --- a/test/server/__snapshots__/webSocketServer-option.test.js.snap.webpack4 +++ b/test/server/__snapshots__/webSocketServer-option.test.js.snap.webpack4 @@ -1,13 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`webSocketServer server is passed to getSocketServerImplementation correctly as a path ("sockjs") 1`] = `"lib/servers/SockJSServer.js"`; - -exports[`webSocketServer server is passed to getSocketServerImplementation correctly as a path ("ws") 1`] = `"lib/servers/WebsocketServer.js"`; - -exports[`webSocketServer server is passed to getSocketServerImplementation correctly as a string ("sockjs") 1`] = `"sockjs"`; - -exports[`webSocketServer server is passed to getSocketServerImplementation correctly as a string ("ws") 1`] = `"ws"`; - exports[`webSocketServer server passed to server with a bad host header results in an error 1`] = ` Array [ "open", diff --git a/test/server/__snapshots__/webSocketServer-option.test.js.snap.webpack5 b/test/server/__snapshots__/webSocketServer-option.test.js.snap.webpack5 index 1430cc503f..0bd27aee0c 100644 --- a/test/server/__snapshots__/webSocketServer-option.test.js.snap.webpack5 +++ b/test/server/__snapshots__/webSocketServer-option.test.js.snap.webpack5 @@ -1,13 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`webSocketServer server is passed to getSocketServerImplementation correctly as a path ("sockjs") 1`] = `"lib/servers/SockJSServer.js"`; - -exports[`webSocketServer server is passed to getSocketServerImplementation correctly as a path ("ws") 1`] = `"lib/servers/WebsocketServer.js"`; - -exports[`webSocketServer server is passed to getSocketServerImplementation correctly as a string ("sockjs") 1`] = `"sockjs"`; - -exports[`webSocketServer server is passed to getSocketServerImplementation correctly as a string ("ws") 1`] = `"ws"`; - exports[`webSocketServer server passed to server with a bad host header results in an error 1`] = ` Array [ "open", diff --git a/test/server/allowedHosts-option.test.js b/test/server/allowedHosts-option.test.js index 3719aa7ae4..f6f1cf37df 100644 --- a/test/server/allowedHosts-option.test.js +++ b/test/server/allowedHosts-option.test.js @@ -15,9 +15,11 @@ describe('allowedHosts', () => { compiler = webpack(config); }); - afterEach((done) => { - server.close(() => { - done(); + afterEach(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); diff --git a/test/server/client-option.test.js b/test/server/client-option.test.js index cd5ae79122..a8c1255780 100644 --- a/test/server/client-option.test.js +++ b/test/server/client-option.test.js @@ -1,24 +1,20 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); -const testServer = require('../helpers/test-server'); const port = require('../ports-map')['client-option']; describe('client option', () => { let server; let req; - afterEach((done) => { - testServer.close(done); - req = null; - server = null; - }); - describe('default behavior', () => { - beforeEach((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { client: { transport: 'sockjs', @@ -26,27 +22,50 @@ describe('client option', () => { webSocketServer: 'sockjs', port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(`http://localhost:${port}`); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('overlay true by default', () => { expect(server.options.client.overlay).toBe(true); }); it('responds with a 200', async () => { - const res = await req.get('/ws'); - expect(res.status).toEqual(200); + const response = await req.get('/ws'); + + expect(response.statusCode).toEqual(200); }); }); describe('path option', () => { const path = '/foo/test/bar'; - beforeEach((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { client: { transport: 'sockjs', @@ -61,50 +80,110 @@ describe('client option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(`http://localhost:${port}`); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('responds with a 200 second', async () => { - const res = await req.get(path); - expect(res.status).toEqual(200); + const response = await req.get(path); + + expect(response.statusCode).toEqual(200); }); }); describe('configure client entry', () => { - it('disables client entry', (done) => { - server = testServer.start( - config, + it('disables client entry', async () => { + const compiler = webpack(config); + + server = new Server( { client: { needClientEntry: false, }, port, }, - async () => { - const res = await request(server.app).get('/main.js'); - expect(res.text).not.toMatch(/client\/index\.js/); - done(); - } + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + const res = await request(server.app).get('/main.js'); + + expect(res.text).not.toMatch(/client\/index\.js/); + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); - it('disables hot entry', (done) => { - server = testServer.start( - config, + it('disables hot entry', async () => { + const compiler = webpack(config); + + server = new Server( { client: { hotEntry: false, }, port, }, - async () => { - const res = await request(server.app).get('/main.js'); - expect(res.text).not.toMatch(/webpack\/hot\/dev-server\.js/); - done(); - } + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + const res = await request(server.app).get('/main.js'); + + expect(res.text).not.toMatch(/webpack\/hot\/dev-server\.js/); + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); }); @@ -141,30 +220,49 @@ describe('client option', () => { jest.unmock('../../lib/utils/getSocketClientPath'); }); - afterEach((done) => { - testServer.close(done); - }); - clientModes.forEach((data) => { it(`${data.title} ${ data.shouldThrow ? 'should throw' : 'should not throw' - }`, (done) => { - const res = () => { - testServer.start( - config, - { - client: data.client, - port, - }, - done - ); - }; + }`, async () => { + const compiler = webpack(config); + + server = new Server( + { + client: data.client, + port, + }, + compiler + ); + + let thrownError; + + try { + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + } catch (error) { + thrownError = error; + } + if (data.shouldThrow) { - expect(res).toThrow(/client\.transport must be a string/); - done(); - } else { - expect(res).not.toThrow(); + expect(thrownError.message).toMatch( + /client\.transport must be a string/ + ); } + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); }); }); diff --git a/test/server/compress-option.test.js b/test/server/compress-option.test.js index a97e1d1e97..477fb3dfae 100644 --- a/test/server/compress-option.test.js +++ b/test/server/compress-option.test.js @@ -1,12 +1,8 @@ -// For whatever reason, this test is now causing hangs. It's not really needed, -// as the middleware it uses for the feature already has tests, so we're -// throwing it into a fire. -// - 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config-other/webpack.config'); const port = require('../ports-map')['compress-option']; @@ -15,62 +11,126 @@ describe('compress option', () => { let req; describe('enabled by default when not specified', () => { - beforeAll((done) => { - server = testServer.start(config, { port }, done); + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('request to bundle file', async () => { - const res = await req.get('/main.js'); - expect(res.headers['content-encoding']).toEqual('gzip'); - expect(res.status).toEqual(200); + const response = await req.get('/main.js'); + + expect(response.headers['content-encoding']).toEqual('gzip'); + expect(response.status).toEqual(200); }); }); describe('as a true', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { compress: true, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('request to bundle file', async () => { - const res = await req.get('/main.js'); - expect(res.headers['content-encoding']).toEqual('gzip'); - expect(res.status).toEqual(200); + const response = await req.get('/main.js'); + + expect(response.headers['content-encoding']).toEqual('gzip'); + expect(response.status).toEqual(200); }); }); describe('as a false', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { compress: false, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('request to bundle file', async () => { - const res = await req.get('/main.js'); + const response = await req.get('/main.js'); + // eslint-disable-next-line no-undefined - expect(res.headers['content-encoding']).toEqual(undefined); - expect(res.status).toEqual(200); + expect(response.headers['content-encoding']).toEqual(undefined); + expect(response.status).toEqual(200); }); }); }); diff --git a/test/server/headers-option.test.js b/test/server/headers-option.test.js index 649c84eb8a..5f122b4d99 100644 --- a/test/server/headers-option.test.js +++ b/test/server/headers-option.test.js @@ -1,7 +1,8 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map')['headers-option']; @@ -10,81 +11,145 @@ describe('headers option', () => { let req; describe('as a string', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { headers: { 'X-Foo': '1' }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('GET request with headers', async () => { - const res = await req.get('/main.js'); - expect(res.headers['x-foo']).toEqual('1'); - expect(res.status).toEqual(200); + const response = await req.get('/main.js'); + + expect(response.headers['x-foo']).toEqual('1'); + expect(response.status).toEqual(200); }); }); describe('as an array', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { headers: { 'X-Bar': ['key1=value1', 'key2=value2'] }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('GET request with headers as an array', async () => { // https://github.com/webpack/webpack-dev-server/pull/1650#discussion_r254217027 const expected = 'key1=value1, key2=value2'; - const res = await req.get('/main.js'); - expect(res.headers['x-bar']).toEqual(expected); - expect(res.status).toEqual(200); + const response = await req.get('/main.js'); + + expect(response.headers['x-bar']).toEqual(expected); + expect(response.status).toEqual(200); }); }); describe('as a function', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { headers: () => { return { 'X-Bar': ['key1=value1', 'key2=value2'] }; }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('GET request with headers as a function', async () => { // https://github.com/webpack/webpack-dev-server/pull/1650#discussion_r254217027 const expected = 'key1=value1, key2=value2'; - const res = await req.get('/main.js'); - expect(res.headers['x-bar']).toEqual(expected); - expect(res.status).toEqual(200); + const response = await req.get('/main.js'); + + expect(response.headers['x-bar']).toEqual(expected); + expect(response.status).toEqual(200); }); }); describe('dev middleware headers take precedence for dev middleware output files', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { headers: { 'X-Foo': '1' }, devMiddleware: { @@ -92,17 +157,37 @@ describe('headers option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('GET request with headers', async () => { - const res = await req.get('/main.js'); - expect(res.headers['x-foo']).toEqual('2'); - expect(res.status).toEqual(200); + const response = await req.get('/main.js'); + + expect(response.headers['x-foo']).toEqual('2'); + expect(response.status).toEqual(200); }); }); }); diff --git a/test/server/historyApiFallback-option.test.js b/test/server/historyApiFallback-option.test.js index 2102b0d955..fd6d2563b2 100644 --- a/test/server/historyApiFallback-option.test.js +++ b/test/server/historyApiFallback-option.test.js @@ -1,8 +1,9 @@ 'use strict'; const path = require('path'); +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/historyapifallback-config/webpack.config'); const config2 = require('../fixtures/historyapifallback-2-config/webpack.config'); const config3 = require('../fixtures/historyapifallback-3-config/webpack.config'); @@ -12,55 +13,102 @@ describe('historyApiFallback option', () => { let server; let req; - afterEach(testServer.close); - describe('as boolean', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { historyApiFallback: true, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('request to directory', async () => { - const res = await req.get('/foo').accept('html'); - expect(res.headers['content-type']).toEqual('text/html; charset=utf-8'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyyy'); + const response = await req.get('/foo').accept('html'); + + expect(response.headers['content-type']).toEqual( + 'text/html; charset=utf-8' + ); + expect(response.status).toEqual(200); + expect(response.text).toContain('Heyyy'); }); }); describe('as object', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { historyApiFallback: { index: '/bar.html', }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('request to directory', async () => { - const res = await req.get('/foo').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Foobar'); + const response = await req.get('/foo').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Foobar'); }); }); describe('as object with static', () => { - beforeAll((done) => { - server = testServer.start( - config2, + beforeAll(async () => { + const compiler = webpack(config2); + + server = new Server( { static: path.resolve( __dirname, @@ -71,34 +119,59 @@ describe('historyApiFallback option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('historyApiFallback should take preference above directory index', async () => { - const res = await req.get('/foo').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Foobar'); + const response = await req.get('/foo').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Foobar'); }); it('request to directory', async () => { - const res = await req.get('/foo').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Foobar'); + const response = await req.get('/foo').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Foobar'); }); it('static file should take preference above historyApiFallback', async () => { - const res = await req.get('/random-file').accept('html'); - expect(res.status).toEqual(200); - expect(res.body.toString().trim()).toEqual('Random file'); + const response = await req.get('/random-file').accept('html'); + + expect(response.status).toEqual(200); + expect(response.body.toString().trim()).toEqual('Random file'); }); }); describe('as object with static set to false', () => { - beforeAll((done) => { - server = testServer.start( - config3, + beforeAll(async () => { + const compiler = webpack(config3); + + server = new Server( { static: false, historyApiFallback: { @@ -106,22 +179,45 @@ describe('historyApiFallback option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('historyApiFallback should work and ignore static content', async () => { - const res = await req.get('/index.html').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('In-memory file'); + const response = await req.get('/index.html').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('In-memory file'); }); }); describe('as object with static and rewrites', () => { - beforeAll((done) => { - server = testServer.start( - config2, + beforeAll(async () => { + const compiler = webpack(config2); + + server = new Server( { port, static: path.resolve( @@ -141,38 +237,63 @@ describe('historyApiFallback option', () => { ], }, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('historyApiFallback respect rewrites for index', async () => { - const res = await req.get('/').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Foobar'); + const response = await req.get('/').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Foobar'); }); it('historyApiFallback respect rewrites and shows index for unknown urls', async () => { - const res = await req.get('/acme').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Foobar'); + const response = await req.get('/acme').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Foobar'); }); it('historyApiFallback respect any other specified rewrites', async () => { - const res = await req.get('/other').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Other file'); + const response = await req.get('/other').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Other file'); }); }); describe('as object with the "verbose" option', () => { let consoleSpy; - beforeAll((done) => { + beforeAll(async () => { consoleSpy = jest.spyOn(global.console, 'log'); - server = testServer.start( - config, + const compiler = webpack(config); + + server = new Server( { historyApiFallback: { index: '/bar.html', @@ -180,19 +301,39 @@ describe('historyApiFallback option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(() => { + afterAll(async () => { consoleSpy.mockRestore(); + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); it('request to directory and log', async () => { - const res = await req.get('/foo').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Foobar'); + const response = await req.get('/foo').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Foobar'); expect(consoleSpy).toHaveBeenCalledWith( 'Rewriting', 'GET', @@ -206,11 +347,12 @@ describe('historyApiFallback option', () => { describe('as object with the "logger" option', () => { let consoleSpy; - beforeAll((done) => { + beforeAll(async () => { consoleSpy = jest.spyOn(global.console, 'log'); - server = testServer.start( - config, + const compiler = webpack(config); + + server = new Server( { historyApiFallback: { index: '/bar.html', @@ -218,19 +360,39 @@ describe('historyApiFallback option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(() => { + afterAll(async () => { consoleSpy.mockRestore(); + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); it('request to directory and log', async () => { - const res = await req.get('/foo').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Foobar'); + const response = await req.get('/foo').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Foobar'); expect(consoleSpy).toHaveBeenCalledWith( 'Rewriting', 'GET', @@ -242,9 +404,10 @@ describe('historyApiFallback option', () => { }); describe('in-memory files', () => { - beforeAll((done) => { - server = testServer.start( - config3, + beforeAll(async () => { + const compiler = webpack(config3); + + server = new Server( { static: path.resolve( __dirname, @@ -253,15 +416,37 @@ describe('historyApiFallback option', () => { historyApiFallback: true, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('should take precedence over static files', async () => { - const res = await req.get('/foo').accept('html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('In-memory file'); + const response = await req.get('/foo').accept('html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('In-memory file'); }); }); }); diff --git a/test/server/host-option.test.js b/test/server/host-option.test.js index 39cd04afd6..fb3255a46b 100644 --- a/test/server/host-option.test.js +++ b/test/server/host-option.test.js @@ -1,25 +1,28 @@ 'use strict'; const path = require('path'); +const webpack = require('webpack'); const request = require('supertest'); const internalIp = require('internal-ip'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); -const testServer = require('../helpers/test-server'); const port = require('../ports-map')['host-option']; const staticDirectory = path.resolve( __dirname, '../fixtures/contentbase-config' ); +const internalIPv4 = internalIp.v4.sync(); describe('host option', () => { let server = null; let req = null; describe('is not be specified', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: staticDirectory, @@ -27,11 +30,32 @@ describe('host option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -40,17 +64,17 @@ describe('host option', () => { }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.status).toEqual(200); + }); }); describe('is undefined', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: staticDirectory, @@ -60,11 +84,32 @@ describe('host option', () => { host: undefined, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '::', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -73,17 +118,17 @@ describe('host option', () => { }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.status).toEqual(200); + }); }); describe('is 127.0.0.1 (IPv4)', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: staticDirectory, @@ -92,11 +137,32 @@ describe('host option', () => { host: '127.0.0.1', port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -105,17 +171,17 @@ describe('host option', () => { }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.status).toEqual(200); + }); }); describe('is localhost', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: staticDirectory, @@ -124,11 +190,32 @@ describe('host option', () => { host: 'localhost', port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -137,17 +224,17 @@ describe('host option', () => { }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.status).toEqual(200); + }); }); describe('is 0.0.0.0', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: staticDirectory, @@ -156,11 +243,32 @@ describe('host option', () => { host: '0.0.0.0', port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '0.0.0.0', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -169,17 +277,17 @@ describe('host option', () => { }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.status).toEqual(200); + }); }); describe('is local-ip', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: staticDirectory, @@ -188,31 +296,51 @@ describe('host option', () => { host: 'local-ip', port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, internalIPv4, (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); - const networkIP = internalIp.v4.sync(); - expect(address.address).toBe(networkIP); + expect(address.address).toBe(internalIPv4); expect(address.port).toBe(port); }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.status).toEqual(200); + }); }); describe('is local-ipv4', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: staticDirectory, @@ -221,24 +349,43 @@ describe('host option', () => { host: 'local-ipv4', port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, internalIPv4, (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); - const networkIP = internalIp.v4.sync(); - expect(address.address).toBe(networkIP); + expect(address.address).toBe(internalIPv4); expect(address.port).toBe(port); }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.status).toEqual(200); + }); }); }); diff --git a/test/server/hot-option.test.js b/test/server/hot-option.test.js index c3d1ae1a58..eabc24c89a 100644 --- a/test/server/hot-option.test.js +++ b/test/server/hot-option.test.js @@ -1,7 +1,8 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/client-config/webpack.config'); const multiCompilerConfig = require('../fixtures/multi-compiler-config/webpack.config'); const port = require('../ports-map')['hot-option']; @@ -11,80 +12,156 @@ describe('hot option', () => { let req; describe('simple hot config entries', () => { - beforeAll((done) => { - const options = { - port, - }; - server = testServer.startAwaitingCompilation(config, options, done); + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should include hot script in the bundle', async () => { - const res = await req.get('/main.js'); - expect(res.status).toEqual(200); - expect(res.text).toContain('webpack/hot/dev-server.js'); + const response = await req.get('/main.js'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('webpack/hot/dev-server.js'); }); }); describe('simple hot-only config entries', () => { - beforeAll((done) => { - const options = { - port, - hot: 'only', - }; - server = testServer.startAwaitingCompilation(config, options, done); + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( + { + port, + hot: 'only', + }, + compiler + ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should include hot-only script in the bundle', async () => { - const res = await req.get('/main.js'); - expect(res.status).toEqual(200); - expect(res.text).toContain('webpack/hot/only-dev-server.js'); + const response = await req.get('/main.js'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('webpack/hot/only-dev-server.js'); }); }); describe('multi compiler hot config entries', () => { - beforeAll((done) => { - const options = { - port, - }; - server = testServer.startAwaitingCompilation( - multiCompilerConfig, - options, - done - ); + beforeAll(async () => { + const compiler = webpack(multiCompilerConfig); + + server = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should include hot script in the bundle', async () => { - const res = await req.get('/main.js'); - expect(res.status).toEqual(200); - expect(res.text).toContain('webpack/hot/dev-server.js'); + const response = await req.get('/main.js'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('webpack/hot/dev-server.js'); }); }); describe('hot disabled entries', () => { - beforeAll((done) => { - const options = { - port, - hot: false, - }; - server = testServer.startAwaitingCompilation(config, options, done); + beforeAll(async () => { + const compiler = webpack(multiCompilerConfig); + + server = new Server({ port, hot: false }, compiler); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should NOT include hot script in the bundle', async () => { - const res = await req.get('/main.js'); - expect(res.status).toEqual(200); - expect(res.text).not.toMatch(/webpack\/hot\/dev-server\.js/); + const response = await req.get('/main.js'); + + expect(response.status).toEqual(200); + expect(response.text).not.toMatch(/webpack\/hot\/dev-server\.js/); }); }); @@ -92,90 +169,119 @@ describe('hot option', () => { // plugin is actually added describe('simple hot config HMR plugin', () => { - it('should register the HMR plugin before compilation is complete', (done) => { + it('should register the HMR plugin before compilation is complete', async () => { let pluginFound = false; - const options = { - port, - }; - const fullSetup = testServer.startAwaitingCompilationFullSetup( - config, - options, - () => { - expect(pluginFound).toBeTruthy(); - done(); - } - ); + const compiler = webpack(config); - const compiler = fullSetup.compiler; compiler.hooks.compilation.intercept({ register: (tapInfo) => { if (tapInfo.name === 'HotModuleReplacementPlugin') { pluginFound = true; } + return tapInfo; }, }); - }); - afterAll(testServer.close); + const serverInTest = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + serverInTest.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + expect(pluginFound).toBe(true); + + await new Promise((resolve) => { + serverInTest.close(() => { + resolve(); + }); + }); + }); }); describe('multi compiler hot config HMR plugin', () => { - it('should register the HMR plugin before compilation is complete', (done) => { + it('should register the HMR plugin before compilation is complete', async () => { let pluginFound = false; - const options = { - port, - }; - const fullSetup = testServer.startAwaitingCompilationFullSetup( - multiCompilerConfig, - options, - () => { - expect(pluginFound).toBeTruthy(); - done(); - } - ); + const compiler = webpack(multiCompilerConfig); - const compiler = fullSetup.compiler.compilers[0]; - compiler.hooks.compilation.intercept({ + compiler.compilers[0].hooks.compilation.intercept({ register: (tapInfo) => { if (tapInfo.name === 'HotModuleReplacementPlugin') { pluginFound = true; } + return tapInfo; }, }); - }); - afterAll(testServer.close); + const serverInTest = new Server({ port }, compiler); + + await new Promise((resolve, reject) => { + serverInTest.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + expect(pluginFound).toBe(true); + + await new Promise((resolve) => { + serverInTest.close(() => { + resolve(); + }); + }); + }); }); describe('hot disabled HMR plugin', () => { - it('should NOT register the HMR plugin before compilation is complete', (done) => { + it('should NOT register the HMR plugin before compilation is complete', async () => { let pluginFound = false; - const options = { - port, - hot: false, - }; - const fullSetup = testServer.startAwaitingCompilationFullSetup( - config, - options, - () => { - expect(pluginFound).toBeFalsy(); - done(); - } - ); + const compiler = webpack(config); - const compiler = fullSetup.compiler; compiler.hooks.compilation.intercept({ register: (tapInfo) => { if (tapInfo.name === 'HotModuleReplacementPlugin') { pluginFound = true; } + return tapInfo; }, }); - }); - afterAll(testServer.close); + const serverInTest = new Server({ port, hot: false }, compiler); + + await new Promise((resolve, reject) => { + serverInTest.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + expect(pluginFound).toBe(false); + + await new Promise((resolve) => { + serverInTest.close(() => { + resolve(); + }); + }); + }); }); }); diff --git a/test/server/http2-option.test.js b/test/server/http2-option.test.js index 0614222828..1fc25b85e7 100644 --- a/test/server/http2-option.test.js +++ b/test/server/http2-option.test.js @@ -2,38 +2,58 @@ const path = require('path'); const http2 = require('http2'); +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/contentbase-config/webpack.config'); const port = require('../ports-map')['http2-option']; -const contentBasePublic = path.resolve( +const staticDirectory = path.resolve( __dirname, '../fixtures/contentbase-config/public' ); -describe('http2 option', () => { +describe('"http2" option', () => { let server; let req; describe('http2 works with https', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { - static: { - directory: contentBasePublic, - watch: false, - }, + static: staticDirectory, https: true, http2: true, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('confirm http2 client can connect', (done) => { const client = http2.connect(`https://localhost:${port}`, { rejectUnauthorized: false, @@ -60,61 +80,95 @@ describe('http2 option', () => { }); http2Req.end(); }); - - afterAll(testServer.close); }); describe('server works with http2 option, but without https option', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { - static: { - directory: contentBasePublic, - watch: false, - }, + static: staticDirectory, http2: true, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyo'); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); - afterAll(testServer.close); + it('Request to index', async () => { + const response = await req.get('/'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Heyo'); + }); }); describe('https without http2 disables HTTP/2', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { - static: { - directory: contentBasePublic, - watch: false, - }, + static: staticDirectory, https: true, http2: false, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyo'); - expect(res.res.httpVersion).not.toEqual('2.0'); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); - afterAll(testServer.close); + it('Request to index', async () => { + const response = await req.get('/'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Heyo'); + expect(response.res.httpVersion).not.toEqual('2.0'); + }); }); }); diff --git a/test/server/https-option.test.js b/test/server/https-option.test.js index 33c2cdc3c1..eb91d5463a 100644 --- a/test/server/https-option.test.js +++ b/test/server/https-option.test.js @@ -3,7 +3,8 @@ const path = require('path'); const fs = require('graceful-fs'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const webpack = require('webpack'); +const Server = require('../../lib/Server'); const config = require('../fixtures/contentbase-config/webpack.config'); const { skipTestOnWindows } = require('../helpers/conditional-test'); const port = require('../ports-map')['https-option']; @@ -22,9 +23,10 @@ describe('https option', () => { let req; describe('as a boolean', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: contentBasePublic, @@ -33,24 +35,45 @@ describe('https option', () => { https: true, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '::', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyo'); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); - afterAll(testServer.close); + it('Request to index', async () => { + const response = await req.get('/'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Heyo'); + }); }); describe('as an object when cacert, pfx, key and cert are buffer', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: contentBasePublic, @@ -73,22 +96,45 @@ describe('https option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '::', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyo'); + const response = await req.get('/'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); describe('as an object when cacert, pfx, key and cert are paths', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: contentBasePublic, https: { @@ -100,15 +146,37 @@ describe('https option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '::', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyo'); + const response = await req.get('/'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); @@ -117,9 +185,10 @@ describe('https option', () => { return; } - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: contentBasePublic, @@ -134,24 +203,45 @@ describe('https option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '::', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyo'); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); - afterAll(testServer.close); + it('Request to index', async () => { + const response = await req.get('/'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Heyo'); + }); }); describe('as an object when cacert, pfx, key and cert are raw strings', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: contentBasePublic, @@ -175,22 +265,45 @@ describe('https option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '::', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyo'); + const response = await req.get('/'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); describe('should support the "requestCert" option', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: contentBasePublic, @@ -214,17 +327,37 @@ describe('https option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '::', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyo'); + const response = await req.get('/'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); - - afterEach(testServer.close); }); diff --git a/test/server/mimeTypes-option.test.js b/test/server/mimeTypes-option.test.js index f900c91787..ea342f47de 100644 --- a/test/server/mimeTypes-option.test.js +++ b/test/server/mimeTypes-option.test.js @@ -1,18 +1,20 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/mime-types-config/webpack.config'); const port = require('../ports-map')['mine-types-option']; -describe('mimeTypes option', () => { +describe('"mimeTypes" option', () => { describe('as an object with a remapped type', () => { let server; let req; - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { devMiddleware: { mimeTypes: { @@ -21,17 +23,39 @@ describe('mimeTypes option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('requests file with different js mime type', async () => { - const res = await req.get('/main.js'); - expect(res.status).toEqual(200); - expect(res.headers['content-type']).toEqual('application/octet-stream'); + const response = await req.get('/main.js'); + + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toEqual( + 'application/octet-stream' + ); }); }); @@ -39,9 +63,10 @@ describe('mimeTypes option', () => { let server; let req; - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { devMiddleware: { mimeTypes: { @@ -50,17 +75,39 @@ describe('mimeTypes option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('requests file with custom mime type', async () => { - const res = await req.get('/file.custom'); - expect(res.status).toEqual(200); - expect(res.headers['content-type']).toEqual('text/html; charset=utf-8'); + const response = await req.get('/file.custom'); + + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toEqual( + 'text/html; charset=utf-8' + ); }); }); }); diff --git a/test/server/onAfterSetupMiddleware-option.test.js b/test/server/onAfterSetupMiddleware-option.test.js index 7a05d2d17c..b332664875 100644 --- a/test/server/onAfterSetupMiddleware-option.test.js +++ b/test/server/onAfterSetupMiddleware-option.test.js @@ -1,7 +1,8 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map')['on-after-setup-middleware-option']; @@ -9,47 +10,73 @@ describe('onAfterSetupMiddleware option', () => { let server; let req; - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { - onAfterSetupMiddleware: ({ app, compiler }) => { - if (!app) { + onAfterSetupMiddleware: (self) => { + if (!self.app) { throw new Error('app is not defined'); } - if (!compiler) { + if (!self.compiler) { throw new Error('compiler is not defined'); } - app.get('/after/some/path', (_, response) => { + self.app.get('/after/some/path', (_, response) => { response.send('after'); }); - app.post('/after/some/path', (_, response) => { + self.app.post('/after/some/path', (_, response) => { response.send('after POST'); }); }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should handle after route', async () => { - const res = await req.get('/after/some/path'); - expect(res.headers['content-type']).toEqual('text/html; charset=utf-8'); - expect(res.status).toEqual(200); - expect(res.text).toBe('after'); + const response = await req.get('/after/some/path'); + + expect(response.headers['content-type']).toEqual( + 'text/html; charset=utf-8' + ); + expect(response.status).toEqual(200); + expect(response.text).toBe('after'); }); it('should handle POST requests to after route', async () => { - const res = await req.post('/after/some/path'); - expect(res.headers['content-type']).toEqual('text/html; charset=utf-8'); - expect(res.status).toEqual(200); - expect(res.text).toBe('after POST'); + const response = await req.post('/after/some/path'); + + expect(response.headers['content-type']).toEqual( + 'text/html; charset=utf-8' + ); + expect(response.status).toEqual(200); + expect(response.text).toBe('after POST'); }); }); diff --git a/test/server/onBeforeSetupMiddleware-option.test.js b/test/server/onBeforeSetupMiddleware-option.test.js index e87cfb2708..9681d36b63 100644 --- a/test/server/onBeforeSetupMiddleware-option.test.js +++ b/test/server/onBeforeSetupMiddleware-option.test.js @@ -1,7 +1,8 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map')['on-before-setup-middleware-option']; @@ -9,36 +10,58 @@ describe('onBeforeSetupMiddleware option', () => { let server; let req; - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { - onBeforeSetupMiddleware: ({ app, compiler }) => { - if (!app) { + onBeforeSetupMiddleware: (self) => { + if (!self.app) { throw new Error('app is not defined'); } - if (!compiler) { + if (!self.compiler) { throw new Error('compiler is not defined'); } - app.get('/before/some/path', (_, response) => { + self.app.get('/before/some/path', (_, response) => { response.send('before'); }); }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should handle before route', async () => { - const res = await req.get('/before/some/path'); - expect(res.headers['content-type']).toEqual('text/html; charset=utf-8'); - expect(res.status).toEqual(200); - expect(res.text).toBe('before'); + const response = await req.get('/before/some/path'); + + expect(response.headers['content-type']).toEqual( + 'text/html; charset=utf-8' + ); + expect(response.status).toEqual(200); + expect(response.text).toBe('before'); }); }); diff --git a/test/server/onListening-option.test.js b/test/server/onListening-option.test.js index cf72b68164..16f61d608e 100644 --- a/test/server/onListening-option.test.js +++ b/test/server/onListening-option.test.js @@ -1,15 +1,18 @@ 'use strict'; -const testServer = require('../helpers/test-server'); +const webpack = require('webpack'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map')['on-listening-option']; describe('onListening option', () => { + let server; let onListeningIsRunning = false; - beforeAll((done) => { - testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { onListening: (devServer) => { if (!devServer) { @@ -20,11 +23,29 @@ describe('onListening option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should runs onListening callback', () => { expect(onListeningIsRunning).toBe(true); diff --git a/test/server/open-option.test.js b/test/server/open-option.test.js index 0f6f42af44..35d60bb205 100644 --- a/test/server/open-option.test.js +++ b/test/server/open-option.test.js @@ -18,20 +18,32 @@ open.mockImplementation(() => { const internalIPv4 = internalIp.v4.sync(); // const internalIPv6 = internalIp.v6.sync(); -const createServer = (compiler, options) => new Server(options, compiler); - describe('"open" option', () => { - afterEach(() => { + let compiler; + let server; + + beforeEach(() => { + compiler = webpack(config); + }); + + afterEach(async () => { open.mockClear(); + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); it('should work with unspecified host', (done) => { - const compiler = webpack(config); - const server = createServer(compiler, { - open: true, - port, - static: false, - }); + server = new Server( + { + open: true, + port, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -43,18 +55,18 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port); }); it("should work with the 'https' option", (done) => { - const compiler = webpack(config); - const server = createServer(compiler, { - open: true, - port, - https: true, - static: false, - }); + server = new Server( + { + open: true, + port, + https: true, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -66,19 +78,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port); }); it("should work with '0.0.0.0' host", (done) => { - const compiler = webpack(config); const host = '0.0.0.0'; - const server = createServer(compiler, { - host, - port, - open: true, - static: false, - }); + server = new Server( + { + host, + port, + open: true, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -90,19 +102,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with '::' host", (done) => { - const compiler = webpack(config); const host = '::'; - const server = createServer(compiler, { - host, - port, - open: true, - static: false, - }); + server = new Server( + { + host, + port, + open: true, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -114,19 +126,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with 'localhost' host", (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: true, - static: false, - }); + server = new Server( + { + host, + port, + open: true, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -138,19 +150,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with '127.0.0.1' host", (done) => { - const compiler = webpack(config); const host = '127.0.0.1'; - const server = createServer(compiler, { - host, - port, - open: true, - static: false, - }); + server = new Server( + { + host, + port, + open: true, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -162,19 +174,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with '::1' host", (done) => { - const compiler = webpack(config); const host = '::1'; - const server = createServer(compiler, { - host, - port, - open: true, - static: false, - }); + server = new Server( + { + host, + port, + open: true, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -185,19 +197,18 @@ describe('"open" option', () => { done(); }); }); - - compiler.run(() => {}); server.listen(port, host); }); it(`should work with '${internalIPv4}' host`, (done) => { - const compiler = webpack(config); - const server = createServer(compiler, { - host: internalIPv4, - port, - open: true, - static: false, - }); + server = new Server( + { + host: internalIPv4, + port, + open: true, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -209,44 +220,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, internalIPv4); }); - // TODO need improve - // if (internalIPv6) { - // it(`should work with '${internalIPv6}' host`, (done) => { - // const compiler = webpack(config); - // const server = createServer(compiler, { - // open: true, - // port, - // static: false, - // }); - // - // compiler.hooks.done.tap('webpack-dev-server', () => { - // server.close(() => { - // expect(open).toHaveBeenCalledWith(`http://[${internalIPv6}]:8117/`, { - // wait: false, - // }); - // - // done(); - // }); - // }); - // - // compiler.run(() => {}); - // server.listen(port, internalIPv6); - // }); - // } - it('should work with boolean', (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: true, - static: false, - }); + server = new Server( + { + host, + port, + open: true, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -258,19 +244,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with boolean but don't close with 'false' value", (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: false, - static: false, - }); + server = new Server( + { + host, + port, + open: false, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -280,19 +266,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it('should work with relative string', (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: 'index.html', - static: false, - }); + server = new Server( + { + host, + port, + open: 'index.html', + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -304,19 +290,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it('should work with relative string starting with "/"', (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: '/index.html', - static: false, - }); + server = new Server( + { + host, + port, + open: '/index.html', + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -328,19 +314,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it('should work with absolute string', (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - open: `http://${host}:${port}/index.html`, - port, - host: 'localhost', - static: false, - }); + server = new Server( + { + open: `http://${host}:${port}/index.html`, + port, + host: 'localhost', + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -352,19 +338,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it('should work with multiple relative strings', (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host: 'localhost', - port, - open: ['first.html', 'second.html'], - static: false, - }); + server = new Server( + { + host: 'localhost', + port, + open: ['first.html', 'second.html'], + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -387,22 +373,22 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it('should work with multiple absolute strings', (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host: 'localhost', - port, - open: [ - `http://${host}:${port}/first.html`, - `http://${host}:${port}/second.html`, - ], - static: false, - }); + server = new Server( + { + host: 'localhost', + port, + open: [ + `http://${host}:${port}/first.html`, + `http://${host}:${port}/second.html`, + ], + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -425,19 +411,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it('should work with empty object', (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: {}, - static: false, - }); + server = new Server( + { + host, + port, + open: {}, + }, + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -449,21 +435,21 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with object and with the boolean value of 'target' option", (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: { - target: true, + server = new Server( + { + host, + port, + open: { + target: true, + }, }, - static: false, - }); + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -475,21 +461,21 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with object and with the 'target' option", (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: { - target: 'index.html', + server = new Server( + { + host, + port, + open: { + target: 'index.html', + }, }, - static: false, - }); + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -501,21 +487,21 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with object and with multiple values of the 'target' option", (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: { - target: ['first.html', 'second.html'], + server = new Server( + { + host, + port, + open: { + target: ['first.html', 'second.html'], + }, }, - static: false, - }); + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -538,21 +524,21 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with object and with the 'app' option", (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: { - app: 'google-chrome', + server = new Server( + { + host, + port, + open: { + app: 'google-chrome', + }, }, - static: false, - }); + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -565,21 +551,21 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with object and with the 'app' and 'arguments' options", (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: { - app: { name: 'google-chrome', arguments: ['--incognito'] }, + server = new Server( + { + host, + port, + open: { + app: { name: 'google-chrome', arguments: ['--incognito'] }, + }, }, - static: false, - }); + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -592,22 +578,22 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it('should work with object with "target" and "app" options', (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: { - target: 'index.html', - app: 'google-chrome', + server = new Server( + { + host, + port, + open: { + target: 'index.html', + app: 'google-chrome', + }, }, - static: false, - }); + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -620,22 +606,22 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with object, with multiple value of the 'target' option and with the 'app' and 'arguments' options", (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: { - target: ['first.html', 'second.html'], - app: { name: 'google-chrome', arguments: ['--incognito'] }, + server = new Server( + { + host, + port, + open: { + target: ['first.html', 'second.html'], + app: { name: 'google-chrome', arguments: ['--incognito'] }, + }, }, - static: false, - }); + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -660,22 +646,22 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should work with object, with multiple value of the 'target' option (relative and absolute URLs) and with the 'app' option with arguments", (done) => { - const compiler = webpack(config); const host = 'localhost'; - const server = createServer(compiler, { - host, - port, - open: { - target: ['first.html', `http://${host}:${port}/second.html`], - app: { name: 'google-chrome', arguments: ['--incognito'] }, + server = new Server( + { + host, + port, + open: { + target: ['first.html', `http://${host}:${port}/second.html`], + app: { name: 'google-chrome', arguments: ['--incognito'] }, + }, }, - static: false, - }); + compiler + ); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(() => { @@ -700,19 +686,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port, host); }); it("should log warning when can't open", (done) => { open.mockImplementation(() => Promise.reject()); - const compiler = webpack(config); - const server = createServer(compiler, { - port, - open: true, - static: false, - }); + server = new Server( + { + port, + open: true, + }, + compiler + ); const loggerWarnSpy = jest.spyOn(server.logger, 'warn'); compiler.hooks.done.tap('webpack-dev-server', () => { @@ -729,19 +715,19 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port); }); it("should log warning when can't open with string", (done) => { open.mockImplementation(() => Promise.reject()); - const compiler = webpack(config); - const server = createServer(compiler, { - open: 'index.html', - port, - static: false, - }); + server = new Server( + { + open: 'index.html', + port, + }, + compiler + ); const loggerWarnSpy = jest.spyOn(server.logger, 'warn'); compiler.hooks.done.tap('webpack-dev-server', () => { @@ -761,22 +747,22 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port); }); it("should log warning when can't open with object", (done) => { open.mockImplementation(() => Promise.reject()); - const compiler = webpack(config); - const server = createServer(compiler, { - open: { - target: 'index.html', - app: 'google-chrome', + server = new Server( + { + open: { + target: 'index.html', + app: 'google-chrome', + }, + port, }, - port, - static: false, - }); + compiler + ); const loggerWarnSpy = jest.spyOn(server.logger, 'warn'); compiler.hooks.done.tap('webpack-dev-server', () => { @@ -797,25 +783,25 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port); }); it("should log warning when can't open with object with the 'app' option with arguments", (done) => { open.mockImplementation(() => Promise.reject()); - const compiler = webpack(config); - const server = createServer(compiler, { - open: { - target: 'index.html', - app: { - name: 'google-chrome', - arguments: ['--incognito', '--new-window'], + server = new Server( + { + open: { + target: 'index.html', + app: { + name: 'google-chrome', + arguments: ['--incognito', '--new-window'], + }, }, + port, }, - port, - static: false, - }); + compiler + ); const loggerWarnSpy = jest.spyOn(server.logger, 'warn'); compiler.hooks.done.tap('webpack-dev-server', () => { @@ -839,25 +825,25 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port); }); it("should log warning when can't open with object with the 'app' option with arguments", (done) => { open.mockImplementation(() => Promise.reject()); - const compiler = webpack(config); - const server = createServer(compiler, { - open: { - target: ['first.html', `http://localhost:${port}/second.html`], - app: { - name: 'google-chrome', - arguments: ['--incognito', '--new-window'], + server = new Server( + { + open: { + target: ['first.html', `http://localhost:${port}/second.html`], + app: { + name: 'google-chrome', + arguments: ['--incognito', '--new-window'], + }, }, + port, }, - port, - static: false, - }); + compiler + ); const loggerWarnSpy = jest.spyOn(server.logger, 'warn'); compiler.hooks.done.tap('webpack-dev-server', () => { @@ -898,7 +884,6 @@ describe('"open" option', () => { }); }); - compiler.run(() => {}); server.listen(port); }); }); diff --git a/test/server/port-option.test.js b/test/server/port-option.test.js index c7291b05b8..8115178944 100644 --- a/test/server/port-option.test.js +++ b/test/server/port-option.test.js @@ -1,8 +1,9 @@ 'use strict'; const path = require('path'); +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map')['port-option']; @@ -11,26 +12,48 @@ const staticDirectory = path.resolve( '../fixtures/contentbase-config' ); -describe('port', () => { +describe('"port" option', () => { let server = null; let req = null; describe('is not be specified', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { + port, static: { directory: staticDirectory, watch: false, }, - port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -39,31 +62,52 @@ describe('port', () => { expect(address.port).toBeDefined(); }); - it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + it('then Request to index', async () => { + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.statusCode).toEqual(200); + }); }); describe('is undefined', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { + // eslint-disable-next-line no-undefined + port: undefined, static: { directory: staticDirectory, watch: false, }, - // eslint-disable-next-line no-undefined - port: undefined, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -73,29 +117,50 @@ describe('port', () => { }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.statusCode).toEqual(200); + }); }); describe('is auto', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { + port: 'auto', static: { directory: staticDirectory, watch: false, }, - port: 'auto', }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -105,29 +170,50 @@ describe('port', () => { }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.statusCode).toEqual(200); + }); }); describe('is "33333"', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { + port: '33333', static: { directory: staticDirectory, watch: false, }, - port: '33333', }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen('33333', '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -136,29 +222,50 @@ describe('port', () => { }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.statusCode).toEqual(200); + }); }); describe('is 33333', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { + port: 33333, static: { directory: staticDirectory, watch: false, }, - port: 33333, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(33333, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); + it('server address', () => { const address = server.server.address(); @@ -167,10 +274,9 @@ describe('port', () => { }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - }); + const response = await req.get('/'); - afterAll(testServer.close); + expect(response.statusCode).toEqual(200); + }); }); }); diff --git a/test/server/proxy-option.test.js b/test/server/proxy-option.test.js index 79013d3257..38f6eb4f10 100644 --- a/test/server/proxy-option.test.js +++ b/test/server/proxy-option.test.js @@ -5,12 +5,13 @@ const request = require('supertest'); const express = require('express'); const bodyParser = require('body-parser'); const WebSocket = require('ws'); -const testServer = require('../helpers/test-server'); +const webpack = require('webpack'); +const Server = require('../../lib/Server'); const config = require('../fixtures/proxy-config/webpack.config'); const [port1, port2, port3, port4] = require('../ports-map')['proxy-option']; const WebSocketServer = WebSocket.Server; -const contentBase = path.resolve(__dirname, '../fixtures/proxy-config'); +const staticDirectory = path.resolve(__dirname, '../fixtures/proxy-config'); const proxyOptionPathsAsProperties = { '/proxy1': { @@ -72,115 +73,148 @@ const proxyOptionOfArray = [ }, ]; -function startProxyServers() { - const listeners = []; - const proxy1 = express(); - const proxy2 = express(); +describe('proxy option', () => { + let proxyServer1; + let proxyServer2; - proxy1.get('/proxy1', (req, res) => { - res.send('from proxy1'); - }); - proxy1.get('/api', (req, res) => { - res.send('api response from proxy1'); - }); - proxy2.get('/proxy2', (req, res) => { - res.send('from proxy2'); - }); + async function listenProxyServers() { + const proxyApp1 = express(); + const proxyApp2 = express(); - listeners.push(proxy1.listen(port1)); - listeners.push(proxy2.listen(port2)); - - // return a function to shutdown proxy servers - return function proxy(done) { - Promise.all( - listeners.map( - (listener) => - new Promise((resolve) => { - listener.close(() => { - // ignore errors - resolve(); - }); - }) - ) - ).then(() => { - done(); + proxyApp1.get('/proxy1', (req, res) => { + res.send('from proxy1'); + }); + proxyApp1.get('/api', (req, res) => { + res.send('api response from proxy1'); + }); + proxyApp2.get('/proxy2', (req, res) => { + res.send('from proxy2'); }); - }; -} -describe('proxy option', () => { + await new Promise((resolve) => { + proxyServer1 = proxyApp1.listen(port1, () => { + resolve(); + }); + }); + + await new Promise((resolve) => { + proxyServer2 = proxyApp2.listen(port2, () => { + resolve(); + }); + }); + } + + async function closeProxyServers() { + await new Promise((resolve) => { + proxyServer1.close(() => { + resolve(); + }); + }); + + await new Promise((resolve) => { + proxyServer2.close(() => { + resolve(); + }); + }); + } + describe('as an object of paths with properties', () => { let server; let req; - let closeProxyServers; - beforeAll((done) => { - closeProxyServers = startProxyServers(); - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, proxy: proxyOptionPathsAsProperties, port: port3, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + await listenProxyServers(); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - closeProxyServers(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); + + await closeProxyServers(); }); describe('target', () => { it('respects a proxy option when a request path is matched', async () => { - const res = await req.get('/proxy1'); - expect(res.status).toEqual(200); - expect(res.text).toContain('from proxy1'); + const response = await req.get('/proxy1'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('from proxy1'); }); }); describe('pathRewrite', () => { it('respects a pathRewrite option', async () => { - const res = await req.get('/api/proxy2'); - expect(res.status).toEqual(200); - expect(res.text).toContain('from proxy2'); + const response = await req.get('/api/proxy2'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('from proxy2'); }); }); describe('bypass', () => { it('can rewrite a request path', async () => { - const res = await req.get('/foo/bar.html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Hello'); + const response = await req.get('/foo/bar.html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Hello'); }); it('can rewrite a request path regardless of the target defined a bypass option', async () => { - const res = await req.get('/baz/hoge.html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Hello'); + const response = await req.get('/baz/hoge.html'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Hello'); }); it('should pass through a proxy when a bypass function returns null', async () => { - const res = await req.get('/foo.js'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Hey'); + const response = await req.get('/foo.js'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('Hey'); }); it('should not pass through a proxy when a bypass function returns false', async () => { - const res = await req.get('/proxyfalse'); - expect(res.status).toEqual(404); + const response = await req.get('/proxyfalse'); + + expect(response.status).toEqual(404); }); it('should wait if bypass returns promise', async () => { - const res = await req.get('/proxy/async'); - expect(res.status).toEqual(200); - expect(res.text).toContain('proxy async response'); + const response = await req.get('/proxy/async'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('proxy async response'); }); }); }); @@ -188,83 +222,122 @@ describe('proxy option', () => { describe('as an option is an object', () => { let server; let req; - let closeProxyServers; - beforeAll((done) => { - closeProxyServers = startProxyServers(); - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, proxy: proxyOption, port: port3, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + await listenProxyServers(); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - closeProxyServers(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); + + await closeProxyServers(); }); it('respects a proxy option', async () => { - const res = await req.get('/proxy1'); - expect(res.status).toEqual(200); - expect(res.text).toContain('from proxy1'); + const response = await req.get('/proxy1'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('from proxy1'); }); }); describe('as an array', () => { let server; let req; - let closeProxyServers; - beforeAll((done) => { - closeProxyServers = startProxyServers(); - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, proxy: proxyOptionOfArray, port: port3, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + await listenProxyServers(); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - closeProxyServers(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); + + await closeProxyServers(); }); it('respects a proxy option', async () => { - const res = await req.get('/proxy1'); - expect(res.status).toEqual(200); - expect(res.text).toContain('from proxy1'); + const response = await req.get('/proxy1'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('from proxy1'); }); it('respects a proxy option of function', async () => { - const res = await req.get('/api/proxy2'); - expect(res.status).toEqual(200); - expect(res.text).toContain('from proxy2'); + const response = await req.get('/api/proxy2'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('from proxy2'); }); it('should allow req, res, and next', async () => { - const { text, statusCode } = await req.get('/api/proxy2?foo=true'); + const response = await req.get('/api/proxy2?foo=true'); - expect(statusCode).toEqual(200); - expect(text).toEqual('foo+next+function'); + expect(response.statusCode).toEqual(200); + expect(response.text).toEqual('foo+next+function'); }); }); @@ -272,24 +345,18 @@ describe('proxy option', () => { let server; let req; let listener; + const proxyTarget = { target: `http://localhost:${port1}`, }; - beforeAll((done) => { - const proxy = express(); + beforeAll(async () => { + const compiler = webpack(config); - proxy.get('*', (proxyReq, res) => { - res.send('from proxy'); - }); - - listener = proxy.listen(port1); - - server = testServer.start( - config, + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, proxy: { @@ -298,32 +365,64 @@ describe('proxy option', () => { }, port: port3, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + const proxy = express(); + + proxy.get('*', (proxyReq, res) => { + res.send('from proxy'); + }); + + listener = proxy.listen(port1); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - listener.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + + await new Promise((resolve) => { + listener.close(() => { + resolve(); + }); }); }); it('respects proxy1 option', async () => { - const res = await req.get('/proxy1'); - expect(res.status).toEqual(200); - expect(res.text).toContain('from proxy'); + const response = await req.get('/proxy1'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('from proxy'); }); it('respects proxy2 option', async () => { - const res = await req.get('/proxy2'); - expect(res.status).toEqual(200); - expect(res.text).toContain('from proxy'); + const response = await req.get('/proxy2'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('from proxy'); }); }); describe('should handles external websocket upgrade', () => { let ws; + let server; let webSocketServer; let responseMessage; @@ -331,12 +430,13 @@ describe('proxy option', () => { webSocketServerTypes.forEach((webSocketServerType) => { describe(`with webSocketServerType: ${webSocketServerType}`, () => { - beforeAll((done) => { - testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, webSocketServer: webSocketServerType, @@ -349,23 +449,37 @@ describe('proxy option', () => { ], port: port3, }, - done + compiler ); + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + webSocketServer = new WebSocketServer({ port: port4 }); - webSocketServer.on('connection', (server) => { - server.on('message', (message) => { - server.send(message); + webSocketServer.on('connection', (connection) => { + connection.on('message', (message) => { + connection.send(message); }); }); }); beforeEach((done) => { ws = new WebSocket(`ws://localhost:${port3}/proxy3/socket`); + ws.on('message', (message) => { responseMessage = message; done(); }); + ws.on('open', () => { ws.send('foo'); }); @@ -375,9 +489,14 @@ describe('proxy option', () => { expect(responseMessage).toEqual('foo'); }); - afterAll((done) => { + afterAll(async () => { webSocketServer.close(); - testServer.close(done); + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); }); }); @@ -391,7 +510,35 @@ describe('proxy option', () => { target: `http://localhost:${port1}`, }; - beforeAll((done) => { + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( + { + static: { + directory: staticDirectory, + watch: false, + }, + proxy: { + '**': proxyTarget, + }, + port: port3, + }, + compiler + ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + const proxy = express(); // Parse application/x-www-form-urlencoded @@ -438,84 +585,84 @@ describe('proxy option', () => { }); listener = proxy.listen(port1); - - server = testServer.start( - config, - { - static: { - directory: contentBase, - watch: false, - }, - proxy: { - '**': proxyTarget, - }, - port: port3, - }, - done - ); req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - listener.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + + await new Promise((resolve) => { + listener.close(() => { + resolve(); + }); }); }); it('errors', async () => { - const res = await req.get('/%'); - expect(res.status).toEqual(500); - expect(res.text).toContain('error from proxy'); + const response = await req.get('/%'); + + expect(response.status).toEqual(500); + expect(response.text).toContain('error from proxy'); }); it('GET method', async () => { - const res = await req.get('/get'); - expect(res.status).toEqual(200); - expect(res.text).toContain('GET method from proxy'); + const response = await req.get('/get'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('GET method from proxy'); }); it('HEAD method', async () => { - const res = await req.head('/head'); - expect(res.status).toEqual(200); + const response = await req.head('/head'); + + expect(response.status).toEqual(200); }); it('POST method (application/x-www-form-urlencoded)', async () => { - const res = await req.post('/post-x-www-form-urlencoded').send('id=1'); - expect(res.status).toEqual(200); - expect(res.text).toContain('POST method from proxy (id: 1)'); + const response = await req + .post('/post-x-www-form-urlencoded') + .send('id=1'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('POST method from proxy (id: 1)'); }); it('POST method (application/json)', async () => { - const res = await req + const response = await req .post('/post-application-json') .send({ id: '1' }) .set('Accept', 'application/json'); - expect(res.status).toEqual(200); - expect(res.headers['content-type']).toEqual( + + expect(response.status).toEqual(200); + expect(response.headers['content-type']).toEqual( 'application/json; charset=utf-8' ); - expect(res.text).toContain('POST method from proxy (id: 1)'); + expect(response.text).toContain('POST method from proxy (id: 1)'); }); it('DELETE method', async () => { - const res = await req.delete('/delete'); - expect(res.status).toEqual(200); - expect(res.text).toContain('DELETE method from proxy'); + const response = await req.delete('/delete'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('DELETE method from proxy'); }); }); describe('should work in multi compiler mode', () => { let server; let req; - let closeProxyServers; - beforeAll((done) => { - closeProxyServers = startProxyServers(); - server = testServer.start( - [config, config], + beforeAll(async () => { + const compiler = webpack([config, config]); + + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, proxy: { @@ -526,31 +673,50 @@ describe('proxy option', () => { }, port: port3, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + await listenProxyServers(); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - closeProxyServers(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); + + await closeProxyServers(); }); it('respects a proxy option', async () => { - const res = await req.get('/proxy1'); - expect(res.status).toEqual(200); - expect(res.text).toContain('from proxy1'); + const response = await req.get('/proxy1'); + + expect(response.status).toEqual(200); + expect(response.text).toContain('from proxy1'); }); }); describe('should work and respect `logProvider` and `logLevel` options', () => { let server; let req; - let closeProxyServers; let customLogProvider; - beforeAll((done) => { + beforeAll(async () => { customLogProvider = { log: jest.fn(), debug: jest.fn(), @@ -559,12 +725,12 @@ describe('proxy option', () => { error: jest.fn(), }; - closeProxyServers = startProxyServers(); - server = testServer.start( - config, + const compiler = webpack([config, config]); + + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, proxy: { @@ -576,24 +742,41 @@ describe('proxy option', () => { }, port: port3, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + await listenProxyServers(); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - closeProxyServers(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); + + await closeProxyServers(); }); describe('target', () => { - it('respects a proxy option when a request path is matched', (done) => { - req.get('/my-path').expect(504, () => { - expect(customLogProvider.error).toHaveBeenCalledTimes(1); + it('respects a proxy option when a request path is matched', async () => { + await req.get('/my-path'); - done(); - }); + expect(customLogProvider.error).toHaveBeenCalledTimes(1); }); }); }); @@ -601,10 +784,9 @@ describe('proxy option', () => { describe('should work and respect the `logLevel` option with `silent` value', () => { let server; let req; - let closeProxyServers; let customLogProvider; - beforeAll((done) => { + beforeAll(async () => { customLogProvider = { log: jest.fn(), debug: jest.fn(), @@ -613,12 +795,12 @@ describe('proxy option', () => { error: jest.fn(), }; - closeProxyServers = startProxyServers(); - server = testServer.start( - config, + const compiler = webpack([config, config]); + + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, proxy: { @@ -630,24 +812,41 @@ describe('proxy option', () => { }, port: port3, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + await listenProxyServers(); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - closeProxyServers(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); + + await closeProxyServers(); }); describe('target', () => { - it('respects a proxy option when a request path is matched', (done) => { - req.get('/my-path').expect(504, () => { - expect(customLogProvider.error).toHaveBeenCalledTimes(0); + it('respects a proxy option when a request path is matched', async () => { + await req.get('/my-path'); - done(); - }); + expect(customLogProvider.error).toHaveBeenCalledTimes(0); }); }); }); @@ -655,10 +854,9 @@ describe('proxy option', () => { describe('should work and respect the `infrastructureLogging.level` option', () => { let server; let req; - let closeProxyServers; let customLogProvider; - beforeAll((done) => { + beforeAll(async () => { customLogProvider = { log: jest.fn(), debug: jest.fn(), @@ -667,12 +865,15 @@ describe('proxy option', () => { error: jest.fn(), }; - closeProxyServers = startProxyServers(); - server = testServer.start( - { ...config, infrastructureLogging: { level: 'error' } }, + const compiler = webpack({ + ...config, + infrastructureLogging: { level: 'error' }, + }); + + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, proxy: { @@ -683,24 +884,41 @@ describe('proxy option', () => { }, port: port3, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + + await listenProxyServers(); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - closeProxyServers(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); + + await closeProxyServers(); }); describe('target', () => { - it('respects a proxy option when a request path is matched', (done) => { - req.get('/my-path').expect(504, () => { - expect(customLogProvider.error).toHaveBeenCalledTimes(1); + it('respects a proxy option when a request path is matched', async () => { + await req.get('/my-path'); - done(); - }); + expect(customLogProvider.error).toHaveBeenCalledTimes(1); }); }); }); @@ -708,10 +926,9 @@ describe('proxy option', () => { describe('should work and respect the `infrastructureLogging.level` option with `none` value', () => { let server; let req; - let closeProxyServers; let customLogProvider; - beforeAll((done) => { + beforeAll(async () => { customLogProvider = { log: jest.fn(), debug: jest.fn(), @@ -720,12 +937,15 @@ describe('proxy option', () => { error: jest.fn(), }; - closeProxyServers = startProxyServers(); - server = testServer.start( - { ...config, infrastructureLogging: { level: 'none' } }, + const compiler = webpack({ + ...config, + infrastructureLogging: { level: 'none' }, + }); + + server = new Server( { static: { - directory: contentBase, + directory: staticDirectory, watch: false, }, proxy: { @@ -736,24 +956,37 @@ describe('proxy option', () => { }, port: port3, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port3, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - closeProxyServers(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); describe('target', () => { - it('respects a proxy option when a request path is matched', (done) => { - req.get('/my-path').expect(504, () => { - expect(customLogProvider.error).toHaveBeenCalledTimes(0); + it('respects a proxy option when a request path is matched', async () => { + await req.get('/my-path'); - done(); - }); + expect(customLogProvider.error).toHaveBeenCalledTimes(0); }); }); }); diff --git a/test/server/setupExitSignals-option.test.js b/test/server/setupExitSignals-option.test.js index 0ca253000e..a8334dcf6a 100644 --- a/test/server/setupExitSignals-option.test.js +++ b/test/server/setupExitSignals-option.test.js @@ -1,7 +1,8 @@ 'use strict'; +const webpack = require('webpack'); +const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); -const testServer = require('../helpers/test-server'); const port = require('../ports-map')['setup-exit-signals-option']; describe('setupExitSignals option', () => { @@ -11,15 +12,29 @@ describe('setupExitSignals option', () => { let stdinResumeSpy; const signals = ['SIGINT', 'SIGTERM']; - beforeEach((done) => { - server = testServer.start( - config, + beforeEach(async () => { + const compiler = webpack(config); + + server = new Server( { setupExitSignals: true, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {}); stdinResumeSpy = jest .spyOn(process.stdin, 'resume') @@ -27,23 +42,27 @@ describe('setupExitSignals option', () => { killSpy = jest.spyOn(server.server, 'kill'); }); - afterEach((done) => { + afterEach(async () => { exitSpy.mockReset(); stdinResumeSpy.mockReset(); signals.forEach((signal) => { process.removeAllListeners(signal); }); process.stdin.removeAllListeners('end'); - testServer.close(done); + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); it.each(signals)('should close and exit on %s', (signal, done) => { process.emit(signal); - - setTimeout(() => { + process.nextTick(() => { expect(killSpy.mock.calls.length).toEqual(1); - expect(exitSpy.mock.calls.length).toEqual(1); + done(); - }, 1000); + }); }); }); diff --git a/test/server/static-directory-option.test.js b/test/server/static-directory-option.test.js index a55badf76b..c377fccb20 100644 --- a/test/server/static-directory-option.test.js +++ b/test/server/static-directory-option.test.js @@ -2,7 +2,9 @@ const path = require('path'); const fs = require('graceful-fs'); +const webpack = require('webpack'); const request = require('supertest'); +const Server = require('../../lib/Server'); const testServer = require('../helpers/test-server'); const config = require('../fixtures/contentbase-config/webpack.config'); const port = require('../ports-map')['static-directory-option']; @@ -21,9 +23,10 @@ describe('static.directory option', () => { describe('to directory', () => { const nestedFile = path.resolve(publicDirectory, 'assets/example.txt'); - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -31,29 +34,46 @@ describe('static.directory option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); fs.truncateSync(nestedFile); }); it('Request to index', async () => { - const res = await req.get('/'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Heyo'); + const response = await req.get('/'); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); it('Request to other file', async () => { - const res = await req.get('/other.html'); - expect(res.status).toEqual(200); - expect(res.text).toContain('Other html'); + const response = await req.get('/other.html'); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Other html'); }); it('Watches folder recursively', (done) => { @@ -71,12 +91,14 @@ describe('static.directory option', () => { it('watch node_modules', (done) => { const filePath = path.join(publicDirectory, 'node_modules', 'index.html'); + fs.writeFileSync(filePath, 'foo', 'utf8'); // chokidar emitted a change, // meaning it watched the file correctly server.staticWatchers[0].on('change', () => { fs.unlinkSync(filePath); + done(); }); @@ -88,9 +110,10 @@ describe('static.directory option', () => { }); describe('test listing files in folders without index.html using the option static.serveIndex:false', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -99,33 +122,51 @@ describe('static.directory option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it("shouldn't list the files inside the assets folder (404)", async () => { - const { status } = await req.get('/assets/'); - expect(status).toEqual(404); + const response = await req.get('/assets/'); + + expect(response.statusCode).toEqual(404); }); it('should show Heyo. because bar has index.html inside it (200)', async () => { - const { status, text } = await req.get('/bar/'); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get('/bar/'); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); describe('test listing files in folders without index.html using the option static.serveIndex:true', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -134,33 +175,51 @@ describe('static.directory option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('should list the files inside the assets folder (200)', async () => { - const { status } = await req.get('/assets/'); - expect(status).toEqual(200); + const response = await req.get('/assets/'); + + expect(response.statusCode).toEqual(200); }); it('should show Heyo. because bar has index.html inside it (200)', async () => { - const { status, text } = await req.get('/bar/'); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get('/bar/'); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); describe('test listing files in folders without index.html using the option static.serveIndex default (true)', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -168,58 +227,93 @@ describe('static.directory option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('should list the files inside the assets folder (200)', async () => { - const { status } = await req.get('/assets/'); - expect(status).toEqual(200); + const response = await req.get('/assets/'); + + expect(response.statusCode).toEqual(200); }); it('should show Heyo. because bar has index.html inside it (200)', async () => { - const { status, text } = await req.get('/bar/'); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get('/bar/'); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); describe('to directories', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: [publicDirectory, otherPublicDirectory], port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('Request to first directory', async () => { - const { status, text } = await req.get('/'); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get('/'); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); it('Request to second directory', async () => { - const { status, text } = await req.get('/foo.html'); - expect(status).toEqual(200); - expect(text).toContain('Foo!'); + const response = await req.get('/foo.html'); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Foo!'); }); }); @@ -229,6 +323,7 @@ describe('static.directory option', () => { done(); }); }); + it('Should throw exception (external url)', (done) => { try { // eslint-disable-next-line no-unused-vars @@ -244,6 +339,7 @@ describe('static.directory option', () => { done(); } }); + it('Should not throw exception (local path with lower case first character)', (done) => { testServer.start( config, @@ -259,6 +355,7 @@ describe('static.directory option', () => { done ); }); + it("Should not throw exception (local path with lower case first character & has '-')", (done) => { testServer.start( config, @@ -272,6 +369,7 @@ describe('static.directory option', () => { done ); }); + it("Should not throw exception (local path with upper case first character & has '-')", (done) => { testServer.start( config, @@ -304,83 +402,95 @@ describe('static.directory option', () => { }); describe('default to PWD', () => { - beforeAll((done) => { + beforeAll(async () => { jest .spyOn(process, 'cwd') .mockImplementation(() => path.resolve(staticDirectory)); - server = testServer.start( - config, + const compiler = webpack(config); + + server = new Server( { - static: null, + // eslint-disable-next-line no-undefined + static: undefined, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('Request to page', async () => { - const { status } = await req.get('/index.html'); - expect(status).toEqual(200); + const response = await req.get('/index.html'); + + expect(response.statusCode).toEqual(200); }); }); describe('disable', () => { - beforeAll((done) => { + beforeAll(async () => { // This is a somewhat weird test, but it is important that we mock // the PWD here, and test if /other.html in our "fake" PWD really is not requested. jest.spyOn(process, 'cwd').mockImplementation(() => publicDirectory); - server = testServer.start( - config, + const compiler = webpack(config); + + server = new Server( { static: false, port, }, - done + compiler ); - req = request(server.app); - }); - afterAll((done) => { - testServer.close(() => { - done(); - }); - }); + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); - it('Request to page', (done) => { - req.get('/other.html').expect(404, done); - }); - }); + return; + } + + resolve(); + }); + }); - describe('Content type', () => { - beforeAll((done) => { - server = testServer.start( - config, - { - static: [publicDirectory], - port, - }, - done - ); req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); - it('Request foo.wasm', (done) => { - req.get('/foo.wasm').expect('Content-Type', 'application/wasm', done); + it('Request to page', async () => { + const response = await req.get('/other.html'); + + expect(response.statusCode).toBe(404); }); }); }); diff --git a/test/server/static-publicPath-option.test.js b/test/server/static-publicPath-option.test.js index 6001570b95..7bd47463d8 100644 --- a/test/server/static-publicPath-option.test.js +++ b/test/server/static-publicPath-option.test.js @@ -2,7 +2,8 @@ const path = require('path'); const request = require('supertest'); -const testServer = require('../helpers/test-server'); +const webpack = require('webpack'); +const Server = require('../../lib/Server'); const config = require('../fixtures/contentbase-config/webpack.config'); const port = require('../ports-map')['static-public-path-option']; @@ -20,9 +21,10 @@ describe('static.publicPath option', () => { let req; describe('to directory', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -31,34 +33,52 @@ describe('static.publicPath option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('Request to index', async () => { - const { status, text } = await req.get(`${staticPublicPath}/`); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); it('Request to other file', async () => { - const { status, text } = await req.get(`${staticPublicPath}/other.html`); - expect(status).toEqual(200); - expect(text).toContain('Other html'); + const response = await req.get(`${staticPublicPath}/other.html`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Other html'); }); }); describe('test listing files in folders without index.html using the option static.serveIndex:false', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -68,33 +88,51 @@ describe('static.publicPath option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it("shouldn't list the files inside the assets folder (404)", async () => { - const { status } = await req.get(`${staticPublicPath}/assets/`); - expect(status).toEqual(404); + const response = await req.get(`${staticPublicPath}/assets/`); + + expect(response.statusCode).toEqual(404); }); it('should show Heyo. because bar has index.html inside it (200)', async () => { - const { status, text } = await req.get(`${staticPublicPath}/bar/`); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get(`${staticPublicPath}/bar/`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); describe('test listing files in folders without index.html using the option static.serveIndex:true', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -104,33 +142,51 @@ describe('static.publicPath option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('should list the files inside the assets folder (200)', async () => { - const { status } = await req.get(`${staticPublicPath}/assets/`); - expect(status).toEqual(200); + const response = await req.get(`${staticPublicPath}/assets/`); + + expect(response.statusCode).toEqual(200); }); it('should show Heyo. because bar has index.html inside it (200)', async () => { - const { status, text } = await req.get(`${staticPublicPath}/bar/`); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get(`${staticPublicPath}/bar/`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); describe('test listing files in folders without index.html using the option static.serveIndex default (true)', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -139,33 +195,51 @@ describe('static.publicPath option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('should list the files inside the assets folder (200)', async () => { - const { status } = await req.get(`${staticPublicPath}/assets/`); - expect(status).toEqual(200); + const response = await req.get(`${staticPublicPath}/assets/`); + + expect(response.statusCode).toEqual(200); }); it('should show Heyo. because bar has index.html inside it (200)', async () => { - const { status, text } = await req.get(`${staticPublicPath}/bar/`); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get(`${staticPublicPath}/bar/`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); }); describe('to directories', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: [ { @@ -179,63 +253,98 @@ describe('static.publicPath option', () => { ], port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('Request to first directory', async () => { - const { status, text } = await req.get(`${staticPublicPath}/`); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); it('Request to second directory', async () => { - const { status, text } = await req.get(`${staticPublicPath}/foo.html`); - expect(status).toEqual(200); - expect(text).toContain('Foo!'); + const response = await req.get(`${staticPublicPath}/foo.html`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Foo!'); }); }); describe('default to PWD', () => { - beforeAll((done) => { + beforeAll(async () => { jest.spyOn(process, 'cwd').mockImplementation(() => staticDirectory); - server = testServer.start( - config, + const compiler = webpack(config); + + server = new Server( { static: { publicPath: staticPublicPath, }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('Request to page', async () => { - const { status } = await req.get(`${staticPublicPath}/index.html`); - expect(status).toEqual(200); + const response = await req.get(`${staticPublicPath}/index.html`); + + expect(response.statusCode).toEqual(200); }); }); describe('Content type', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -243,28 +352,44 @@ describe('static.publicPath option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); - it('Request foo.wasm', (done) => { - req - .get(`${staticPublicPath}/foo.wasm`) - .expect('Content-Type', 'application/wasm', done); + it('Request foo.wasm', async () => { + const response = await req.get(`${staticPublicPath}/foo.wasm`); + + expect(response.headers['content-type']).toBe('application/wasm'); }); }); describe('to ignore other methods than GET and HEAD', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: { directory: publicDirectory, @@ -273,50 +398,74 @@ describe('static.publicPath option', () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); it('GET request', async () => { - const { status } = await req.get(`${staticPublicPath}/`); - expect(status).toEqual(200); + const response = await req.get(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(200); }); it('HEAD request', async () => { - const { status } = await req.head(`${staticPublicPath}/`); - expect(status).toEqual(200); + const response = await req.head(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(200); }); it('POST request', async () => { - const { status } = await req.post(`${staticPublicPath}/`); - expect(status).toEqual(404); + const response = await req.post(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(404); }); it('PUT request', async () => { - const { status } = await req.put(`${staticPublicPath}/`); - expect(status).toEqual(404); + const response = await req.put(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(404); }); it('DELETE request', async () => { - const { status } = await req.delete(`${staticPublicPath}/`); - expect(status).toEqual(404); + const response = await req.delete(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(404); }); it('PATCH request', async () => { - const { status } = await req.patch(`${staticPublicPath}/`); - expect(status).toEqual(404); + const response = await req.patch(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(404); }); }); describe('multiple static.publicPath entries', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: [ { @@ -332,42 +481,59 @@ describe('static.publicPath option', () => { ], port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('Request the first path to index', async () => { - const { status, text } = await req.get(`${staticPublicPath}/`); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); it('Request the first path to other file', async () => { - const { status, text } = await req.get(`${staticPublicPath}/other.html`); - expect(status).toEqual(200); - expect(text).toContain('Other html'); + const response = await req.get(`${staticPublicPath}/other.html`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Other html'); }); it('Request the second path to foo', async () => { - const { status, text } = await req.get( - `${otherStaticPublicPath}/foo.html` - ); - expect(status).toEqual(200); - expect(text).toContain('Foo!'); + const response = await req.get(`${otherStaticPublicPath}/foo.html`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Foo!'); }); }); describe('multiple static.publicPath entries with publicPath array', () => { - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { static: [ { @@ -383,39 +549,58 @@ describe('static.publicPath option', () => { ], port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll((done) => { - testServer.close(() => { - done(); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); }); }); it('Request the first path to index', async () => { - const { status, text } = await req.get(`${staticPublicPath}/`); - expect(status).toEqual(200); - expect(text).toContain('Heyo'); + const response = await req.get(`${staticPublicPath}/`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Heyo'); }); it('Request the first path to other file', async () => { - const { status, text } = await req.get(`${staticPublicPath}/other.html`); - expect(status).toEqual(200); - expect(text).toContain('Other html'); + const response = await req.get(`${staticPublicPath}/other.html`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Other html'); }); it('Request the first path to foo', async () => { - const { status, text } = await req.get(`${staticPublicPath}/foo.html`); - expect(status).toEqual(200); - expect(text).toContain('Foo!'); + const response = await req.get(`${staticPublicPath}/foo.html`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Foo!'); }); it('Request the second path to foo', async () => { - const { status, text } = await req.get(`${staticPublicPath}/foo.html`); - expect(status).toEqual(200); - expect(text).toContain('Foo!'); + const response = await req.get(`${staticPublicPath}/foo.html`); + + expect(response.statusCode).toEqual(200); + expect(response.text).toContain('Foo!'); }); }); }); diff --git a/test/server/stats-option.test.js b/test/server/stats-option.test.js deleted file mode 100644 index 2a571a5dde..0000000000 --- a/test/server/stats-option.test.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict'; - -const webpack = require('webpack'); -const Server = require('../../lib/Server'); -const config = require('../fixtures/simple-config/webpack.config'); -const port = require('../ports-map')['stats-option']; - -const createServer = (compiler, options) => new Server(options, compiler); - -describe('stats option', () => { - beforeAll(() => { - jest.spyOn(console, 'log').mockImplementation(() => {}); - }); - it(`should works with difference stats values (contains 'hash', 'assets', 'warnings' and 'errors')`, () => { - const allStats = [ - {}, - // eslint-disable-next-line no-undefined - undefined, - false, - 'errors-only', - { - assets: false, - }, - ]; - - return allStats.reduce( - (p, stats) => - p.then( - () => - new Promise((resolve) => { - const compiler = webpack(Object.assign({}, config, { stats })); - const server = createServer(compiler, { static: false, port }); - - compiler.hooks.done.tap('webpack-dev-server', (s) => { - expect( - Object.keys(server.getStats(s)).sort() - ).toMatchSnapshot(); - - server.close(resolve); - }); - - compiler.run(() => {}); - server.listen(port, 'localhost'); - }) - ), - Promise.resolve() - ); - }); - - it('should respect warningsFilter', (done) => { - const compiler = webpack( - Object.assign({}, config, { - stats: { warningsFilter: 'test' }, - }) - ); - const server = createServer(compiler, { static: false, port }); - - compiler.hooks.done.tap('webpack-dev-server', (s) => { - s.compilation.warnings = ['test', 'another warning']; - - const output = server.getStats(s); - - expect(output.warnings.length).toBe(1); - - // Webpack@4 - if (typeof output.warnings[0] === 'string') { - expect(output.warnings[0]).toBe('another warning'); - } - // Webpack@5 - else { - expect(output.warnings[0]).toEqual({ message: 'another warning' }); - } - - server.close(done); - }); - - compiler.run(() => {}); - server.listen(port, 'localhost'); - }); -}); diff --git a/test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack4 b/test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack4 index 3e33b7de3e..2cb67fd28e 100644 --- a/test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack4 +++ b/test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack4 @@ -4,7 +4,7 @@ exports[`normalizeOptions allowedHosts is set should set correct options 1`] = ` Object { "allowedHosts": "all", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -39,7 +39,7 @@ exports[`normalizeOptions client custom transport path should set correct option Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "/path/to/custom/client/", "webSocketURL": Object {}, @@ -75,7 +75,7 @@ exports[`normalizeOptions client host and port should set correct options 1`] = Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "hostname": "my.host", @@ -113,7 +113,7 @@ exports[`normalizeOptions client host and string port should set correct options Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "hostname": "my.host", @@ -151,7 +151,7 @@ exports[`normalizeOptions client path should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "pathname": "/custom/path/", @@ -188,7 +188,7 @@ exports[`normalizeOptions client path without leading/ending slashes should set Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "pathname": "custom/path", @@ -225,7 +225,7 @@ exports[`normalizeOptions client.transport sockjs string should set correct opti Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "sockjs", "webSocketURL": Object {}, @@ -261,7 +261,7 @@ exports[`normalizeOptions client.transport ws string and webSocketServer object Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "ws", "webSocketURL": Object {}, @@ -299,7 +299,7 @@ exports[`normalizeOptions client.transport ws string and webSocketServer object Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "ws", "webSocketURL": Object {}, @@ -337,7 +337,7 @@ exports[`normalizeOptions client.transport ws string and webSocketServer ws stri Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "ws", "webSocketURL": Object {}, @@ -373,7 +373,7 @@ exports[`normalizeOptions client.transport ws string should set correct options Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "ws", "webSocketURL": Object {}, @@ -409,7 +409,7 @@ exports[`normalizeOptions dev is set should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -551,7 +551,7 @@ exports[`normalizeOptions liveReload is false should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -586,7 +586,7 @@ exports[`normalizeOptions liveReload is true should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -621,7 +621,7 @@ exports[`normalizeOptions multi compiler watchOptions is set should set correct Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -658,7 +658,7 @@ exports[`normalizeOptions no options should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -693,7 +693,7 @@ exports[`normalizeOptions port string should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -729,7 +729,7 @@ exports[`normalizeOptions single compiler watchOptions is object should set corr Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -766,7 +766,7 @@ exports[`normalizeOptions single compiler watchOptions is object with static wat Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -803,7 +803,7 @@ exports[`normalizeOptions single compiler watchOptions is object with static wat Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -840,7 +840,7 @@ exports[`normalizeOptions single compiler watchOptions is object with watch fals Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -875,7 +875,7 @@ exports[`normalizeOptions static is an array of static objects should set correc Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -921,7 +921,7 @@ exports[`normalizeOptions static is an array of strings and static objects shoul Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -967,7 +967,7 @@ exports[`normalizeOptions static is an array of strings should set correct optio Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1013,7 +1013,7 @@ exports[`normalizeOptions static is an object should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1048,7 +1048,7 @@ exports[`normalizeOptions static is false should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1071,7 +1071,7 @@ exports[`normalizeOptions static is string should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1106,7 +1106,7 @@ exports[`normalizeOptions static is true should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1141,7 +1141,7 @@ exports[`normalizeOptions static publicPath is a string should set correct optio Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1176,7 +1176,7 @@ exports[`normalizeOptions static publicPath is an array should set correct optio Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1212,7 +1212,7 @@ exports[`normalizeOptions static serveIndex is an object should set correct opti Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1247,7 +1247,7 @@ exports[`normalizeOptions static serveIndex is false should set correct options Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1280,7 +1280,7 @@ exports[`normalizeOptions static serveIndex is true should set correct options 1 Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1315,7 +1315,7 @@ exports[`normalizeOptions static watch is an object should set correct options 1 Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1352,7 +1352,7 @@ exports[`normalizeOptions static watch is false should set correct options 1`] = Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1387,7 +1387,7 @@ exports[`normalizeOptions static watch is true should set correct options 1`] = Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1422,7 +1422,7 @@ exports[`normalizeOptions username and password should set correct options 1`] = Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "password": "chuntaro", @@ -1460,7 +1460,7 @@ exports[`normalizeOptions webSocketServer custom server class should set correct Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1495,7 +1495,7 @@ exports[`normalizeOptions webSocketServer custom server path should set correct Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, diff --git a/test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack5 b/test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack5 index 3e33b7de3e..2cb67fd28e 100644 --- a/test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack5 +++ b/test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack5 @@ -4,7 +4,7 @@ exports[`normalizeOptions allowedHosts is set should set correct options 1`] = ` Object { "allowedHosts": "all", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -39,7 +39,7 @@ exports[`normalizeOptions client custom transport path should set correct option Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "/path/to/custom/client/", "webSocketURL": Object {}, @@ -75,7 +75,7 @@ exports[`normalizeOptions client host and port should set correct options 1`] = Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "hostname": "my.host", @@ -113,7 +113,7 @@ exports[`normalizeOptions client host and string port should set correct options Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "hostname": "my.host", @@ -151,7 +151,7 @@ exports[`normalizeOptions client path should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "pathname": "/custom/path/", @@ -188,7 +188,7 @@ exports[`normalizeOptions client path without leading/ending slashes should set Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "pathname": "custom/path", @@ -225,7 +225,7 @@ exports[`normalizeOptions client.transport sockjs string should set correct opti Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "sockjs", "webSocketURL": Object {}, @@ -261,7 +261,7 @@ exports[`normalizeOptions client.transport ws string and webSocketServer object Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "ws", "webSocketURL": Object {}, @@ -299,7 +299,7 @@ exports[`normalizeOptions client.transport ws string and webSocketServer object Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "ws", "webSocketURL": Object {}, @@ -337,7 +337,7 @@ exports[`normalizeOptions client.transport ws string and webSocketServer ws stri Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "ws", "webSocketURL": Object {}, @@ -373,7 +373,7 @@ exports[`normalizeOptions client.transport ws string should set correct options Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "transport": "ws", "webSocketURL": Object {}, @@ -409,7 +409,7 @@ exports[`normalizeOptions dev is set should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -551,7 +551,7 @@ exports[`normalizeOptions liveReload is false should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -586,7 +586,7 @@ exports[`normalizeOptions liveReload is true should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -621,7 +621,7 @@ exports[`normalizeOptions multi compiler watchOptions is set should set correct Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -658,7 +658,7 @@ exports[`normalizeOptions no options should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -693,7 +693,7 @@ exports[`normalizeOptions port string should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -729,7 +729,7 @@ exports[`normalizeOptions single compiler watchOptions is object should set corr Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -766,7 +766,7 @@ exports[`normalizeOptions single compiler watchOptions is object with static wat Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -803,7 +803,7 @@ exports[`normalizeOptions single compiler watchOptions is object with static wat Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -840,7 +840,7 @@ exports[`normalizeOptions single compiler watchOptions is object with watch fals Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -875,7 +875,7 @@ exports[`normalizeOptions static is an array of static objects should set correc Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -921,7 +921,7 @@ exports[`normalizeOptions static is an array of strings and static objects shoul Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -967,7 +967,7 @@ exports[`normalizeOptions static is an array of strings should set correct optio Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1013,7 +1013,7 @@ exports[`normalizeOptions static is an object should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1048,7 +1048,7 @@ exports[`normalizeOptions static is false should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1071,7 +1071,7 @@ exports[`normalizeOptions static is string should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1106,7 +1106,7 @@ exports[`normalizeOptions static is true should set correct options 1`] = ` Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1141,7 +1141,7 @@ exports[`normalizeOptions static publicPath is a string should set correct optio Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1176,7 +1176,7 @@ exports[`normalizeOptions static publicPath is an array should set correct optio Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1212,7 +1212,7 @@ exports[`normalizeOptions static serveIndex is an object should set correct opti Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1247,7 +1247,7 @@ exports[`normalizeOptions static serveIndex is false should set correct options Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1280,7 +1280,7 @@ exports[`normalizeOptions static serveIndex is true should set correct options 1 Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1315,7 +1315,7 @@ exports[`normalizeOptions static watch is an object should set correct options 1 Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1352,7 +1352,7 @@ exports[`normalizeOptions static watch is false should set correct options 1`] = Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1387,7 +1387,7 @@ exports[`normalizeOptions static watch is true should set correct options 1`] = Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1422,7 +1422,7 @@ exports[`normalizeOptions username and password should set correct options 1`] = Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object { "password": "chuntaro", @@ -1460,7 +1460,7 @@ exports[`normalizeOptions webSocketServer custom server class should set correct Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, @@ -1495,7 +1495,7 @@ exports[`normalizeOptions webSocketServer custom server path should set correct Object { "allowedHosts": "auto", "client": Object { - "hotEntry": true, + "hotEntry": undefined, "overlay": true, "webSocketURL": Object {}, }, diff --git a/test/server/utils/routes.test.js b/test/server/utils/routes.test.js index 8ab4251470..1ce9913ab7 100644 --- a/test/server/utils/routes.test.js +++ b/test/server/utils/routes.test.js @@ -1,7 +1,8 @@ 'use strict'; +const webpack = require('webpack'); const request = require('supertest'); -const testServer = require('../../helpers/test-server'); +const Server = require('../../../lib/Server'); const config = require('../../fixtures/simple-config/webpack.config'); const multiConfig = require('../../fixtures/multi-public-path-config/webpack.config'); const port = require('../../ports-map').routes; @@ -11,90 +12,162 @@ describe('routes util', () => { let req; describe('simple config', () => { - beforeAll((done) => { - server = testServer.startAwaitingCompilation(config, { port }, done); + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( + { + port, + }, + compiler + ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should handles GET request to sockjs bundle', async () => { - const res = await req.get('/__webpack_dev_server__/sockjs.bundle.js'); - expect(res.headers['content-type']).toEqual('application/javascript'); - expect(res.statusCode).toEqual(200); + const response = await req.get( + '/__webpack_dev_server__/sockjs.bundle.js' + ); + + expect(response.headers['content-type']).toEqual( + 'application/javascript' + ); + expect(response.statusCode).toEqual(200); }); it('should handle HEAD request to sockjs bundle', async () => { - const res = await req.head('/__webpack_dev_server__/sockjs.bundle.js'); - expect(res.headers['content-type']).toEqual('application/javascript'); - expect(res.statusCode).toEqual(200); + const response = await req.head( + '/__webpack_dev_server__/sockjs.bundle.js' + ); + + expect(response.headers['content-type']).toEqual( + 'application/javascript' + ); + expect(response.statusCode).toEqual(200); }); it('should handle GET request to invalidate endpoint', async () => { - const res = await req.get('/webpack-dev-server/invalidate'); - expect(res.headers['content-type']).not.toEqual('text/html'); - expect(res.statusCode).toEqual(200); + const response = await req.get('/webpack-dev-server/invalidate'); + + expect(response.headers['content-type']).not.toEqual('text/html'); + expect(response.statusCode).toEqual(200); }); it('should handle GET request to live html', async () => { - const res = await req.get('/webpack-dev-server/'); - expect(res.headers['content-type']).toEqual('text/html'); - expect(res.statusCode).toEqual(200); + const response = await req.get('/webpack-dev-server/'); + + expect(response.headers['content-type']).toEqual('text/html'); + expect(response.statusCode).toEqual(200); }); it('should handle HEAD request to live html', async () => { - const res = await req.head('/webpack-dev-server/'); - expect(res.headers['content-type']).toEqual('text/html'); - expect(res.statusCode).toEqual(200); + const response = await req.head('/webpack-dev-server/'); + + expect(response.headers['content-type']).toEqual('text/html'); + expect(response.statusCode).toEqual(200); }); it('should handle GET request to directory index', async () => { - const res = await req.get('/webpack-dev-server'); - expect(res.headers['content-type']).toEqual('text/html'); - expect(res.statusCode).toEqual(200); - expect(res.text).toMatchSnapshot(); + const response = await req.get('/webpack-dev-server'); + + expect(response.headers['content-type']).toEqual('text/html'); + expect(response.statusCode).toEqual(200); + expect(response.text).toMatchSnapshot(); }); it('should handle HEAD request to directory index', async () => { - const res = await req.head('/webpack-dev-server'); - expect(res.headers['content-type']).toEqual('text/html'); - expect(res.statusCode).toEqual(200); + const response = await req.head('/webpack-dev-server'); + + expect(response.headers['content-type']).toEqual('text/html'); + expect(response.statusCode).toEqual(200); }); it('should handle GET request to magic async html', async () => { - const res = await req.get('/main'); - expect(res.statusCode).toEqual(200); + const response = await req.get('/main'); + + expect(response.statusCode).toEqual(200); }); it('should handle HEAD request to magic async html', async () => { - const res = await req.head('/main'); - expect(res.statusCode).toEqual(200); + const response = await req.head('/main'); + + expect(response.statusCode).toEqual(200); }); it('should handle GET request to main async chunk', async () => { - const res = await req.get('/main.js'); - expect(res.statusCode).toEqual(200); + const response = await req.get('/main.js'); + + expect(response.statusCode).toEqual(200); }); it('should handle HEAD request to main async chunk', async () => { - const res = await req.head('/main.js'); - expect(res.statusCode).toEqual(200); + const response = await req.head('/main.js'); + + expect(response.statusCode).toEqual(200); }); }); describe('multi config', () => { - beforeAll((done) => { - server = testServer.startAwaitingCompilation(multiConfig, { port }, done); + beforeAll(async () => { + const compiler = webpack(multiConfig); + + server = new Server( + { + port, + }, + compiler + ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + req = request(server.app); }); - afterAll(testServer.close); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + }); it('should handle GET request to directory index and list all middleware directories', async () => { - const res = await req.get('/webpack-dev-server'); - expect(res.headers['content-type']).toEqual('text/html'); - expect(res.statusCode).toEqual(200); - expect(res.text).toMatchSnapshot(); + const response = await req.get('/webpack-dev-server'); + + expect(response.headers['content-type']).toEqual('text/html'); + expect(response.statusCode).toEqual(200); + expect(response.text).toMatchSnapshot(); }); }); }); diff --git a/test/server/watchFiles-option.test.js b/test/server/watchFiles-option.test.js index f2e3c5155b..0f3c71388c 100644 --- a/test/server/watchFiles-option.test.js +++ b/test/server/watchFiles-option.test.js @@ -1,9 +1,10 @@ 'use strict'; const path = require('path'); +const webpack = require('webpack'); const fs = require('graceful-fs'); const chokidar = require('chokidar'); -const testServer = require('../helpers/test-server'); +const Server = require('../../lib/Server'); const config = require('../fixtures/contentbase-config/webpack.config'); const port = require('../ports-map')['watch-files-option']; @@ -18,19 +19,37 @@ describe("'watchFiles' option", () => { describe('should work with string and path to file', () => { const file = path.join(watchDir, 'assets/example.txt'); - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { watchFiles: file, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); }); - afterAll((done) => { - testServer.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + fs.truncateSync(file); }); @@ -51,19 +70,37 @@ describe("'watchFiles' option", () => { describe('should work with string and path to dir', () => { const file = path.join(watchDir, 'assets/example.txt'); - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { watchFiles: watchDir, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); }); - afterAll((done) => { - testServer.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + fs.truncateSync(file); }); @@ -84,19 +121,37 @@ describe("'watchFiles' option", () => { describe('should work with string and glob', () => { const file = path.join(watchDir, 'assets/example.txt'); - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { watchFiles: `${watchDir}/**/*`, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); }); - afterAll((done) => { - testServer.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + fs.truncateSync(file); }); @@ -117,19 +172,37 @@ describe("'watchFiles' option", () => { describe('should work not crash on non exist file', () => { const nonExistFile = path.join(watchDir, 'assets/non-exist.txt'); - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { watchFiles: nonExistFile, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); }); - afterAll((done) => { - testServer.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + fs.truncateSync(nonExistFile); }); @@ -150,19 +223,37 @@ describe("'watchFiles' option", () => { describe('should work with object with single path', () => { const file = path.join(watchDir, 'assets/example.txt'); - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { watchFiles: { paths: file }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); }); - afterAll((done) => { - testServer.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + fs.truncateSync(file); }); @@ -184,19 +275,37 @@ describe("'watchFiles' option", () => { const file = path.join(watchDir, 'assets/example.txt'); const other = path.join(watchDir, 'assets/other.txt'); - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { watchFiles: { paths: [file, other] }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); }); - afterAll((done) => { - testServer.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + fs.truncateSync(file); }); @@ -227,19 +336,37 @@ describe("'watchFiles' option", () => { const file = path.join(watchDir, 'assets/example.txt'); const other = path.join(watchDir, 'assets/other.txt'); - beforeAll((done) => { - server = testServer.start( - config, + beforeAll(async () => { + const compiler = webpack(config); + + server = new Server( { watchFiles: [{ paths: [file] }, other], port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); }); - afterAll((done) => { - testServer.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + fs.truncateSync(file); fs.truncateSync(other); }); @@ -306,10 +433,12 @@ describe("'watchFiles' option", () => { optionCases.forEach((optionCase) => { describe(JSON.stringify(optionCase), () => { - beforeAll((done) => { + beforeAll(async () => { chokidarMock.mockClear(); - server = testServer.start( - config, + + const compiler = webpack(config); + + server = new Server( { watchFiles: { paths: file, @@ -317,12 +446,29 @@ describe("'watchFiles' option", () => { }, port, }, - done + compiler ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); }); - afterAll((done) => { - testServer.close(done); + afterAll(async () => { + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); + fs.truncateSync(file); }); diff --git a/test/server/webSocketServer-option.test.js b/test/server/webSocketServer-option.test.js index 4e7e419f7e..f2103a6332 100644 --- a/test/server/webSocketServer-option.test.js +++ b/test/server/webSocketServer-option.test.js @@ -3,10 +3,11 @@ /* eslint-disable class-methods-use-this */ -const path = require('path'); const request = require('supertest'); +const webpack = require('webpack'); const sockjs = require('sockjs'); const SockJS = require('sockjs-client/dist/sockjs'); +const Server = require('../../lib/Server'); const SockJSServer = require('../../lib/servers/SockJSServer'); const config = require('../fixtures/simple-config/webpack.config'); const BaseServer = require('../../lib/servers/BaseServer'); @@ -18,101 +19,65 @@ describe('webSocketServer', () => { let testServer; let server; let req; - let getSocketServerImplementation; const serverModes = [ { title: 'as a string ("sockjs")', webSocketServer: 'sockjs', + expected: 'SockJSServer', }, { title: 'as a path ("sockjs")', client: { transport: 'sockjs' }, webSocketServer: require.resolve('../../lib/servers/SockJSServer'), + expected: 'SockJSServer', }, { title: 'as a string ("ws")', webSocketServer: 'ws', + expected: 'WebsocketServer', }, { title: 'as a path ("ws")', client: { transport: 'ws' }, webSocketServer: require.resolve('../../lib/servers/WebsocketServer'), - }, - { - title: 'as a class (custom implementation)', - client: { transport: 'ws' }, - webSocketServer: class CustomServer {}, + expected: 'WebsocketServer', }, ]; describe('is passed to getSocketServerImplementation correctly', () => { - beforeEach(() => { - jest.mock('../../lib/utils/getSocketServerImplementation'); - - getSocketServerImplementation = require('../../lib/utils/getSocketServerImplementation'); - getSocketServerImplementation.mockImplementation( - () => - class MockServer { - // eslint-disable-next-line no-empty-function - onConnection() {} - close() {} - } - ); - }); - - afterEach((done) => { - jest.resetAllMocks(); - jest.resetModules(); - - mockedTestServer.close(done); - }); - serverModes.forEach((data) => { - it(data.title, (done) => { - mockedTestServer = require('../helpers/test-server'); - mockedTestServer.start( - config, + it(data.title, async () => { + const compiler = webpack(config); + + server = new Server( { client: data.client, webSocketServer: data.webSocketServer, port, }, - () => { - expect(getSocketServerImplementation.mock.calls.length).toEqual( - 1 - ); - expect( - getSocketServerImplementation.mock.calls[0].length - ).toEqual(1); - - if (typeof data.webSocketServer === 'string') { - const isStandardWebSocketServerImplementation = - data.webSocketServer === 'ws' || - data.webSocketServer === 'sockjs'; - - expect( - isStandardWebSocketServerImplementation - ? getSocketServerImplementation.mock.calls[0][0] - .webSocketServer.type - : path - .relative( - process.cwd(), - getSocketServerImplementation.mock.calls[0][0] - .webSocketServer.type - ) - .replace(/\\/g, '/') - ).toMatchSnapshot(); - } else { - expect( - getSocketServerImplementation.mock.calls[0][0].webSocketServer - .type - ).toEqual(data.webSocketServer); + compiler + ); + + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; } - done(); - } - ); + resolve(); + }); + }); + + expect(server.webSocketServer.constructor.name).toBe(data.expected); + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); }); }); @@ -263,20 +228,45 @@ describe('webSocketServer', () => { }); describe('as a path with nonexistent path', () => { - it('should throw an error', () => { - expect(() => { - server = testServer.start( - config, - { - client: { transport: 'ws' }, - webSocketServer: '/bad/path/to/implementation', - port, - }, - () => {} - ); - }).toThrow( + it('should throw an error', async () => { + const compiler = webpack(config); + + server = new Server( + { + client: { transport: 'ws' }, + webSocketServer: '/bad/path/to/implementation', + port, + }, + compiler + ); + + let thrownError; + + try { + await new Promise((resolve, reject) => { + server.listen(port, '127.0.0.1', (error) => { + if (error) { + reject(error); + + return; + } + + resolve(); + }); + }); + } catch (error) { + thrownError = error; + } + + expect(thrownError.message).toMatch( /webSocketServer \(webSocketServer\.type\) must be a string/ ); + + await new Promise((resolve) => { + server.close(() => { + resolve(); + }); + }); }); }); diff --git a/test/validate-options.test.js b/test/validate-options.test.js index cdac6396e3..fd0ef61c6e 100644 --- a/test/validate-options.test.js +++ b/test/validate-options.test.js @@ -355,7 +355,6 @@ const tests = { }, ], failure: [ - 'nonexistent-implementation', null, false, { @@ -425,13 +424,15 @@ describe('options', () => { function createTestCase(type, key, value) { it(`should ${ type === 'success' ? 'successfully validate' : 'throw an error on' - } the "${key}" option with '${stringifyValue(value)}' value`, (done) => { - let compiler = webpack(config); - let server; + } the "${key}" option with '${stringifyValue( + value + )}' value`, async () => { + const compiler = webpack(config); let thrownError; try { - server = new Server({ [key]: value }, compiler); + // eslint-disable-next-line no-new + new Server({ [key]: value }, compiler); } catch (error) { thrownError = error; } @@ -442,17 +443,6 @@ describe('options', () => { expect(thrownError).not.toBeUndefined(); expect(thrownError.toString()).toMatchSnapshot(); } - - if (server) { - server.close(() => { - compiler = null; - server = null; - - done(); - }); - } else { - done(); - } }); }