diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8977de369e..b2f609847b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,10 +2,13 @@ trigger: - master - next +variables: + npm_config_cache: $(Pipeline.Workspace)/.npm + jobs: - job: Lint pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-latest steps: - task: NodeTool@0 inputs: @@ -20,10 +23,12 @@ jobs: node -v npm -v displayName: 'Print versions' - - task: Npm@1 + - task: CacheBeta@1 inputs: - command: custom - customCommand: ci + key: npm | $(Agent.OS) | package-lock.json + path: $(npm_config_cache) + displayName: 'Cache npm' + - script: npm ci displayName: 'Install dependencies' - script: npm run lint displayName: 'Run lint' @@ -34,10 +39,13 @@ jobs: - job: Linux pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-latest strategy: - maxParallel: 5 + maxParallel: 6 matrix: + node-13: + node_version: ^13.0.0 + webpack_version: latest node-12: node_version: ^12.0.0 webpack_version: latest @@ -50,6 +58,9 @@ jobs: node-6: node_version: ^6.9.0 webpack_version: latest + node-10-canary: + node_version: ^10.13.0 + webpack_version: next steps: - task: NodeTool@0 inputs: @@ -64,10 +75,12 @@ jobs: node -v npm -v displayName: 'Print versions' - - task: Npm@1 + - task: CacheBeta@1 inputs: - command: custom - customCommand: ci + key: npm | $(Agent.OS) | package-lock.json + path: $(npm_config_cache) + displayName: 'Cache npm' + - script: npm ci displayName: 'Install dependencies' - script: npm i webpack@$(webpack_version) displayName: 'Install "webpack@$(webpack_version)"' @@ -85,10 +98,13 @@ jobs: - job: macOS pool: - vmImage: macOS-10.14 + vmImage: macOS-latest strategy: - maxParallel: 5 + maxParallel: 6 matrix: + node-13: + node_version: ^13.0.0 + webpack_version: latest node-12: node_version: ^12.0.0 webpack_version: latest @@ -101,6 +117,9 @@ jobs: node-6: node_version: ^6.9.0 webpack_version: latest + node-10-canary: + node_version: ^10.13.0 + webpack_version: next steps: - task: NodeTool@0 inputs: @@ -115,10 +134,12 @@ jobs: node -v npm -v displayName: 'Print versions' - - task: Npm@1 + - task: CacheBeta@1 inputs: - command: custom - customCommand: ci + key: npm | $(Agent.OS) | package-lock.json + path: $(npm_config_cache) + displayName: 'Cache npm' + - script: npm ci displayName: 'Install dependencies' - script: npm i webpack@$(webpack_version) displayName: 'Install "webpack@$(webpack_version)"' @@ -136,10 +157,13 @@ jobs: - job: Windows pool: - vmImage: windows-2019 + vmImage: windows-latest strategy: - maxParallel: 5 + maxParallel: 6 matrix: + node-13: + node_version: ^13.0.0 + webpack_version: latest node-12: node_version: ^12.0.0 webpack_version: latest @@ -152,6 +176,9 @@ jobs: node-6: node_version: ^6.9.0 webpack_version: latest + node-10-canary: + node_version: ^10.13.0 + webpack_version: next steps: - script: 'git config --global core.autocrlf input' displayName: 'Config git core.autocrlf' @@ -169,10 +196,12 @@ jobs: node -v npm -v displayName: 'Print versions' - - task: Npm@1 + - task: CacheBeta@1 inputs: - command: custom - customCommand: ci + key: npm | $(Agent.OS) | package-lock.json + path: $(npm_config_cache) + displayName: 'Cache npm' + - script: npm ci displayName: 'Install dependencies' - script: npm i webpack@$(webpack_version) displayName: 'Install "webpack@$(webpack_version)"' diff --git a/package.json b/package.json index 55d0e9b7f1..344425e573 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "webpack-cli": "^3.3.10" }, "peerDependencies": { - "webpack": "^4.0.0" + "webpack": "^4.0.0 || ^5.0.0" }, "author": "Tobias Koppers @sokra", "bugs": "https://github.com/webpack/webpack-dev-server/issues", diff --git a/test/cli/cli.test.js b/test/cli/cli.test.js index 51114d3dd4..29bd740011 100644 --- a/test/cli/cli.test.js +++ b/test/cli/cli.test.js @@ -176,8 +176,9 @@ describe('CLI', () => { it('should exit the process when SIGINT is detected, even before the compilation is done', (done) => { const cliPath = resolve(__dirname, '../../bin/webpack-dev-server.js'); - const examplePath = resolve(__dirname, '../../examples/cli/public'); - const cp = execa('node', [cliPath], { cwd: examplePath }); + const simpleConfig = resolve(__dirname, '../fixtures/simple-config'); + const cp = execa('node', [cliPath], { cwd: simpleConfig }); + let killed = false; cp.stdout.on('data', () => { @@ -197,11 +198,13 @@ describe('CLI', () => { it('should use different random port when multiple instances are started on different processes', (done) => { const cliPath = resolve(__dirname, '../../bin/webpack-dev-server.js'); - const examplePath = resolve(__dirname, '../../examples/cli/public'); + const simpleConfig = resolve(__dirname, '../fixtures/simple-config'); - const cp = execa('node', [cliPath, '--colors=false'], { cwd: examplePath }); + const cp = execa('node', [cliPath, '--colors=false'], { + cwd: simpleConfig, + }); const cp2 = execa('node', [cliPath, '--colors=false'], { - cwd: examplePath, + cwd: simpleConfig, }); const runtime = { @@ -220,22 +223,27 @@ describe('CLI', () => { const portMatch = /Project is running at http:\/\/localhost:(\d*)\//.exec( bits ); + if (portMatch) { runtime.cp.port = portMatch[1]; } + if (/Compiled successfully/.test(bits)) { expect(cp.pid !== 0).toBe(true); cp.kill('SIGINT'); } }); + cp2.stdout.on('data', (data) => { const bits = data.toString(); const portMatch = /Project is running at http:\/\/localhost:(\d*)\//.exec( bits ); + if (portMatch) { runtime.cp2.port = portMatch[1]; } + if (/Compiled successfully/.test(bits)) { expect(cp.pid !== 0).toBe(true); cp2.kill('SIGINT'); @@ -249,8 +257,10 @@ describe('CLI', () => { done(); } }); + cp2.on('exit', () => { runtime.cp2.done = true; + if (runtime.cp.done) { expect(runtime.cp.port !== runtime.cp2.port).toBe(true); done(); diff --git a/test/e2e/ClientOptions.test.js b/test/e2e/ClientOptions.test.js index ca3ea17057..b0a5b50834 100644 --- a/test/e2e/ClientOptions.test.js +++ b/test/e2e/ClientOptions.test.js @@ -324,7 +324,8 @@ describe('Client console.log', () => { }); }) .then(() => { - expect(res).toMatchSnapshot(); + // Order doesn't matter, maybe we should improve that in future + expect(res.sort()).toMatchSnapshot(); done(); }); }); diff --git a/test/e2e/Iframe.test.js b/test/e2e/Iframe.test.js index 2ec8caf628..fa46c88377 100644 --- a/test/e2e/Iframe.test.js +++ b/test/e2e/Iframe.test.js @@ -84,7 +84,8 @@ describe('Client iframe console.log', () => { }); }) .then(() => { - expect(res).toMatchSnapshot(); + // Order doesn't matter, maybe we should improve that in future + expect(res.sort()).toMatchSnapshot(); done(); }); }); diff --git a/test/e2e/__snapshots__/ClientOptions.test.js.snap b/test/e2e/__snapshots__/ClientOptions.test.js.snap index d12069f13d..27b5c3406b 100644 --- a/test/e2e/__snapshots__/ClientOptions.test.js.snap +++ b/test/e2e/__snapshots__/ClientOptions.test.js.snap @@ -15,8 +15,8 @@ Array [ exports[`Client console.log hot enabled 1`] = ` Array [ - "[HMR] Waiting for update signal from WDS...", "Hey.", + "[HMR] Waiting for update signal from WDS...", "[WDS] Hot Module Replacement enabled.", "[WDS] Live Reloading enabled.", ] diff --git a/test/e2e/__snapshots__/Iframe.test.js.snap b/test/e2e/__snapshots__/Iframe.test.js.snap index d66c695eb9..ba2007f1ca 100644 --- a/test/e2e/__snapshots__/Iframe.test.js.snap +++ b/test/e2e/__snapshots__/Iframe.test.js.snap @@ -15,8 +15,8 @@ Array [ exports[`Client iframe console.log hot enabled 1`] = ` Array [ - "[HMR] Waiting for update signal from WDS...", "Hey.", + "[HMR] Waiting for update signal from WDS...", "[WDS] Hot Module Replacement enabled.", "[WDS] Live Reloading enabled.", ] diff --git a/test/server/Server.test.js b/test/server/Server.test.js index d54712a087..ca041b7795 100644 --- a/test/server/Server.test.js +++ b/test/server/Server.test.js @@ -39,9 +39,9 @@ describe('Server', () => { return relative('.', p).split(sep); }) ).toMatchSnapshot(); - expect( - server.middleware.context.compiler.options.plugins - ).toMatchSnapshot(); + expect(server.middleware.context.compiler.options.plugins).toEqual([ + new webpack.HotModuleReplacementPlugin(), + ]); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(done); @@ -64,9 +64,9 @@ describe('Server', () => { return relative('.', p).split(sep); }) ).toMatchSnapshot(); - expect( - server.middleware.context.compiler.options.plugins - ).toMatchSnapshot(); + expect(server.middleware.context.compiler.options.plugins).toEqual([ + new webpack.HotModuleReplacementPlugin(), + ]); compiler.hooks.done.tap('webpack-dev-server', () => { server.close(done); @@ -78,14 +78,6 @@ describe('Server', () => { // issue: https://github.com/webpack/webpack-dev-server/issues/1724 describe('express.static.mine.types', () => { - beforeEach(() => { - jest.resetModules(); - }); - - afterEach(() => { - jest.unmock('express'); - }); - it("should success even if mine.types doesn't exist", (done) => { jest.mock('express', () => { const data = jest.requireActual('express'); @@ -118,31 +110,6 @@ describe('Server', () => { }); }); - describe('WEBPACK_DEV_SERVER environment variable', () => { - const OLD_ENV = process.env; - - beforeEach(() => { - // this is important - it clears the cache - jest.resetModules(); - - process.env = { ...OLD_ENV }; - - delete process.env.WEBPACK_DEV_SERVER; - }); - - afterEach(() => { - process.env = OLD_ENV; - }); - - it('should be present', () => { - expect(process.env.WEBPACK_DEV_SERVER).toBeUndefined(); - - require('../../lib/Server'); - - expect(process.env.WEBPACK_DEV_SERVER).toBe(true); - }); - }); - describe('Invalidate Callback', () => { describe('Testing callback functions on calling invalidate without callback', () => { it('should be `noop` (the default callback function)', (done) => { @@ -178,4 +145,29 @@ describe('Server', () => { }); }); }); + + describe('WEBPACK_DEV_SERVER environment variable', () => { + const OLD_ENV = process.env; + + beforeEach(() => { + // this is important - it clears the cache + jest.resetModules(); + + process.env = { ...OLD_ENV }; + + delete process.env.WEBPACK_DEV_SERVER; + }); + + afterEach(() => { + process.env = OLD_ENV; + }); + + it('should be present', () => { + expect(process.env.WEBPACK_DEV_SERVER).toBeUndefined(); + + require('../../lib/Server'); + + expect(process.env.WEBPACK_DEV_SERVER).toBe(true); + }); + }); }); diff --git a/test/server/__snapshots__/Server.test.js.snap b/test/server/__snapshots__/Server.test.js.snap index b39da65eb6..6b69e950ff 100644 --- a/test/server/__snapshots__/Server.test.js.snap +++ b/test/server/__snapshots__/Server.test.js.snap @@ -19,17 +19,6 @@ Array [ ] `; -exports[`Server addEntries add hot option 2`] = ` -Array [ - HotModuleReplacementPlugin { - "fullBuildTimeout": 200, - "multiStep": undefined, - "options": Object {}, - "requestTimeout": 10000, - }, -] -`; - exports[`Server addEntries add hotOnly option 1`] = ` Array [ Array [ @@ -48,14 +37,3 @@ Array [ ], ] `; - -exports[`Server addEntries add hotOnly option 2`] = ` -Array [ - HotModuleReplacementPlugin { - "fullBuildTimeout": 200, - "multiStep": undefined, - "options": Object {}, - "requestTimeout": 10000, - }, -] -`; diff --git a/test/server/__snapshots__/stats-option.test.js.snap b/test/server/__snapshots__/stats-option.test.js.snap index b5405f897e..5360f60162 100644 --- a/test/server/__snapshots__/stats-option.test.js.snap +++ b/test/server/__snapshots__/stats-option.test.js.snap @@ -2,55 +2,55 @@ exports[`stats option should works with difference stats values (contains 'hash', 'assets', 'warnings' and 'errors') 1`] = ` Array [ - "errors", - "warnings", - "hash", - "assetsByChunkName", "assets", + "assetsByChunkName", + "errors", "filteredAssets", + "hash", + "warnings", ] `; exports[`stats option should works with difference stats values (contains 'hash', 'assets', 'warnings' and 'errors') 2`] = ` Array [ - "errors", - "warnings", - "hash", - "assetsByChunkName", "assets", + "assetsByChunkName", + "errors", "filteredAssets", + "hash", + "warnings", ] `; exports[`stats option should works with difference stats values (contains 'hash', 'assets', 'warnings' and 'errors') 3`] = ` Array [ - "errors", - "warnings", - "hash", - "assetsByChunkName", "assets", + "assetsByChunkName", + "errors", "filteredAssets", + "hash", + "warnings", ] `; exports[`stats option should works with difference stats values (contains 'hash', 'assets', 'warnings' and 'errors') 4`] = ` Array [ - "errors", - "warnings", - "hash", - "assetsByChunkName", "assets", + "assetsByChunkName", + "errors", "filteredAssets", + "hash", + "warnings", ] `; exports[`stats option should works with difference stats values (contains 'hash', 'assets', 'warnings' and 'errors') 5`] = ` Array [ - "errors", - "warnings", - "hash", - "assetsByChunkName", "assets", + "assetsByChunkName", + "errors", "filteredAssets", + "hash", + "warnings", ] `; diff --git a/test/server/inline-option.test.js b/test/server/inline-option.test.js index 78344621e6..0bbf89de63 100644 --- a/test/server/inline-option.test.js +++ b/test/server/inline-option.test.js @@ -27,7 +27,7 @@ describe('inline option', () => { afterAll(testServer.close); it('should include inline client script in the bundle', (done) => { - const url = new RegExp(`client/index.js\\?http://0.0.0.0:${port}`); + const url = new RegExp(`client(/index.js)?\\?http://0.0.0.0:${port}`); req.get('/main.js').expect(200, url, done); }); @@ -54,7 +54,7 @@ describe('inline option', () => { afterAll(testServer.close); it('should include inline client script in the bundle', (done) => { - const url = new RegExp(`client/index.js\\?http://0.0.0.0:${port}`); + const url = new RegExp(`client(/index.js)?\\?http://0.0.0.0:${port}`); req.get('/main.js').expect(200, url, done); }); @@ -77,11 +77,14 @@ describe('inline option', () => { afterAll(testServer.close); it('should NOT include inline client script in the bundle', (done) => { + const url = new RegExp(`client(/index.js)?\\?http://0.0.0.0:${port}`); + req .get('/main.js') .expect(200) .then(({ text }) => { - expect(text.includes(`client/index.js?http://0.0.0.0:${port}`)); + expect(url.test(text)).toBe(false); + done(); }); }); diff --git a/test/server/stats-option.test.js b/test/server/stats-option.test.js index dc89702cc8..bd71095ced 100644 --- a/test/server/stats-option.test.js +++ b/test/server/stats-option.test.js @@ -25,7 +25,7 @@ describe('stats option', () => { const server = new Server(compiler, { stats, port, quiet: true }); compiler.hooks.done.tap('webpack-dev-server', (s) => { - expect(Object.keys(server.getStats(s))).toMatchSnapshot(); + expect(Object.keys(server.getStats(s)).sort()).toMatchSnapshot(); server.close(resolve); }); @@ -51,7 +51,15 @@ describe('stats option', () => { const output = server.getStats(s); expect(output.warnings.length).toBe(1); - expect(output.warnings[0]).toBe('another warning'); + + // 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); }); diff --git a/test/server/utils/__snapshots__/updateCompiler.test.js.snap b/test/server/utils/__snapshots__/updateCompiler.test.js.snap deleted file mode 100644 index d922651e48..0000000000 --- a/test/server/utils/__snapshots__/updateCompiler.test.js.snap +++ /dev/null @@ -1,47 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`updateCompiler multi compiler config, hot and inline should apply plugins 1`] = ` -Array [ - HotModuleReplacementPlugin { - "fullBuildTimeout": 200, - "multiStep": undefined, - "options": Object {}, - "requestTimeout": 10000, - }, -] -`; - -exports[`updateCompiler multi compiler config, hot and inline should apply plugins 2`] = ` -Array [ - HotModuleReplacementPlugin { - "fullBuildTimeout": 200, - "multiStep": undefined, - "options": Object {}, - "requestTimeout": 10000, - }, -] -`; - -exports[`updateCompiler simple config with HMR already, hot and inline should apply plugins 1`] = ` -Array [ - HotModuleReplacementPlugin { - "fullBuildTimeout": 200, - "multiStep": undefined, - "options": Object {}, - "requestTimeout": 10000, - }, -] -`; - -exports[`updateCompiler simple config, hot and inline should apply plugins 1`] = ` -Array [ - HotModuleReplacementPlugin { - "fullBuildTimeout": 200, - "multiStep": undefined, - "options": Object {}, - "requestTimeout": 10000, - }, -] -`; - -exports[`updateCompiler simple config, inline should apply plugins without HMR 1`] = `undefined`; diff --git a/test/server/utils/updateCompiler.test.js b/test/server/utils/updateCompiler.test.js index 796bc543d4..4203a6f4a6 100644 --- a/test/server/utils/updateCompiler.test.js +++ b/test/server/utils/updateCompiler.test.js @@ -6,14 +6,14 @@ const updateCompiler = require('../../../lib/utils/updateCompiler'); describe('updateCompiler', () => { describe('simple config, inline', () => { let compiler; + beforeAll(() => { const webpackConfig = require('../../fixtures/simple-config/webpack.config'); + compiler = webpack(webpackConfig); }); it('should apply plugins without HMR', () => { - const spy = jest.spyOn(compiler.hooks.entryOption, 'call'); - updateCompiler(compiler, { transportMode: { server: 'sockjs', @@ -24,6 +24,7 @@ describe('updateCompiler', () => { let tapsByHMR = 0; let tapsByProvidePlugin = 0; + compiler.hooks.compilation.taps.forEach((tap) => { if (tap.name === 'HotModuleReplacementPlugin') { tapsByHMR += 1; @@ -32,26 +33,23 @@ describe('updateCompiler', () => { } }); - expect(spy).toHaveBeenCalledTimes(1); + expect(compiler.hooks.entryOption.taps.length).toBe(1); expect(tapsByHMR).toEqual(0); expect(tapsByProvidePlugin).toEqual(1); - // should be empty - expect(compiler.options.plugins).toMatchSnapshot(); - - spy.mockRestore(); + expect(compiler.options.plugins).toBeUndefined(); }); }); describe('simple config, hot and inline', () => { let compiler; + beforeAll(() => { const webpackConfig = require('../../fixtures/simple-config/webpack.config'); + compiler = webpack(webpackConfig); }); it('should apply plugins', () => { - const spy = jest.spyOn(compiler.hooks.entryOption, 'call'); - updateCompiler(compiler, { transportMode: { server: 'sockjs', @@ -63,6 +61,7 @@ describe('updateCompiler', () => { let tapsByHMR = 0; let tapsByProvidePlugin = 0; + compiler.hooks.compilation.taps.forEach((tap) => { if (tap.name === 'HotModuleReplacementPlugin') { tapsByHMR += 1; @@ -71,26 +70,27 @@ describe('updateCompiler', () => { } }); - expect(spy).toHaveBeenCalledTimes(1); + expect(compiler.hooks.entryOption.taps.length).toBe(1); expect(tapsByHMR).toEqual(1); expect(tapsByProvidePlugin).toEqual(1); - expect(compiler.options.plugins).toMatchSnapshot(); - - spy.mockRestore(); + expect(compiler.options.plugins).toEqual([ + new webpack.HotModuleReplacementPlugin(), + ]); }); }); describe('simple config with HMR already, hot and inline', () => { let compiler; + beforeAll(() => { const webpackConfig = require('../../fixtures/simple-config/webpack.config'); + webpackConfig.plugins = [new webpack.HotModuleReplacementPlugin()]; + compiler = webpack(webpackConfig); }); it('should apply plugins', () => { - const spy = jest.spyOn(compiler.hooks.entryOption, 'call'); - updateCompiler(compiler, { transportMode: { server: 'sockjs', @@ -102,6 +102,7 @@ describe('updateCompiler', () => { let tapsByHMR = 0; let tapsByProvidePlugin = 0; + compiler.hooks.compilation.taps.forEach((tap) => { if (tap.name === 'HotModuleReplacementPlugin') { tapsByHMR += 1; @@ -110,29 +111,27 @@ describe('updateCompiler', () => { } }); - expect(spy).toHaveBeenCalledTimes(1); + expect(compiler.hooks.entryOption.taps.length).toBe(1); expect(tapsByHMR).toEqual(1); expect(tapsByProvidePlugin).toEqual(1); - expect(compiler.options.plugins).toMatchSnapshot(); - - spy.mockRestore(); + expect(compiler.options.plugins).toEqual([ + new webpack.HotModuleReplacementPlugin(), + ]); }); }); describe('multi compiler config, hot and inline', () => { let multiCompiler; + beforeAll(() => { const webpackConfig = require('../../fixtures/multi-compiler-2-config/webpack.config'); + webpackConfig[1].plugins = [new webpack.HotModuleReplacementPlugin()]; + multiCompiler = webpack(webpackConfig); }); it('should apply plugins', () => { - const spies = []; - multiCompiler.compilers.forEach((compiler) => { - spies.push(jest.spyOn(compiler.hooks.entryOption, 'call')); - }); - updateCompiler(multiCompiler, { transportMode: { server: 'sockjs', @@ -142,14 +141,10 @@ describe('updateCompiler', () => { hot: true, }); - spies.forEach((spy) => { - expect(spy).toHaveBeenCalledTimes(1); - spy.mockRestore(); - }); - multiCompiler.compilers.forEach((compiler) => { let tapsByHMR = 0; let tapsByProvidePlugin = 0; + compiler.hooks.compilation.taps.forEach((tap) => { if (tap.name === 'HotModuleReplacementPlugin') { tapsByHMR += 1; @@ -157,9 +152,13 @@ describe('updateCompiler', () => { tapsByProvidePlugin += 1; } }); + + expect(compiler.hooks.entryOption.taps.length).toBe(1); expect(tapsByHMR).toEqual(1); expect(tapsByProvidePlugin).toEqual(1); - expect(compiler.options.plugins).toMatchSnapshot(); + expect(compiler.options.plugins).toEqual([ + new webpack.HotModuleReplacementPlugin(), + ]); }); }); });