diff --git a/.eslintrc b/.eslintrc index b851edc92b..9547a7446f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,7 +2,11 @@ "extends": ["webpack", "prettier"], "globals": { "document": true, - "window": true + "window": true, + "self": true, + "WorkerGlobalScope": true, + "__resourceQuery": true, + "__webpack_dev_server_client__": true }, "parserOptions": { "sourceType": "script", diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8977de369e..d0fdf318cd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -47,9 +47,6 @@ jobs: node-8: node_version: ^8.9.0 webpack_version: latest - node-6: - node_version: ^6.9.0 - webpack_version: latest steps: - task: NodeTool@0 inputs: @@ -98,9 +95,6 @@ jobs: node-8: node_version: ^8.9.0 webpack_version: latest - node-6: - node_version: ^6.9.0 - webpack_version: latest steps: - task: NodeTool@0 inputs: @@ -149,9 +143,6 @@ jobs: node-8: node_version: ^8.9.0 webpack_version: latest - node-6: - node_version: ^6.9.0 - webpack_version: latest steps: - script: 'git config --global core.autocrlf input' displayName: 'Config git core.autocrlf' diff --git a/bin/webpack-dev-server.js b/bin/webpack-dev-server.js index 286cad93b9..a575fb17df 100755 --- a/bin/webpack-dev-server.js +++ b/bin/webpack-dev-server.js @@ -4,8 +4,6 @@ /* eslint-disable no-shadow, no-console */ -const fs = require('fs'); -const net = require('net'); const debug = require('debug')('webpack-dev-server'); const importLocal = require('import-local'); const yargs = require('yargs'); @@ -115,51 +113,13 @@ function startDevServer(config, options) { throw err; } - if (options.socket) { - server.listeningApp.on('error', (e) => { - if (e.code === 'EADDRINUSE') { - const clientSocket = new net.Socket(); - - clientSocket.on('error', (err) => { - if (err.code === 'ECONNREFUSED') { - // No other server listening on this socket so it can be safely removed - fs.unlinkSync(options.socket); - - server.listen(options.socket, options.host, (error) => { - if (error) { - throw error; - } - }); - } - }); - - clientSocket.connect({ path: options.socket }, () => { - throw new Error('This socket is already used'); - }); - } - }); - - server.listen(options.socket, options.host, (err) => { - if (err) { - throw err; - } - - // chmod 666 (rw rw rw) - const READ_WRITE = 438; - - fs.chmod(options.socket, READ_WRITE, (err) => { - if (err) { - throw err; - } - }); - }); - } else { - server.listen(options.port, options.host, (err) => { - if (err) { - throw err; - } - }); - } + // options.socket does not have a default value, so it will only be set + // if the user sets it explicitly + server.listen(options.socket || options.port, options.host, (err) => { + if (err) { + throw err; + } + }); } processOptions(config, argv, (config, options) => { diff --git a/client-src/.eslintrc b/client-src/.eslintrc new file mode 100644 index 0000000000..bd14a19ef2 --- /dev/null +++ b/client-src/.eslintrc @@ -0,0 +1,5 @@ +{ + "parserOptions": { + "sourceType": "module" + } +} diff --git a/client-src/clients/BaseClient.js b/client-src/clients/BaseClient.js index 1dfa65911b..7c47b70e2a 100644 --- a/client-src/clients/BaseClient.js +++ b/client-src/clients/BaseClient.js @@ -1,5 +1,3 @@ -'use strict'; - /* eslint-disable no-unused-vars */ diff --git a/client-src/clients/SockJSClient.js b/client-src/clients/SockJSClient.js index 193c571d26..9a1fd729fa 100644 --- a/client-src/clients/SockJSClient.js +++ b/client-src/clients/SockJSClient.js @@ -1,5 +1,3 @@ -'use strict'; - /* eslint-disable no-unused-vars */ diff --git a/client-src/clients/WebsocketClient.js b/client-src/clients/WebsocketClient.js index 0089f6faed..9785bb5517 100644 --- a/client-src/clients/WebsocketClient.js +++ b/client-src/clients/WebsocketClient.js @@ -1,5 +1,3 @@ -'use strict'; - /* global WebSocket */ /* eslint-disable diff --git a/client-src/default/index.js b/client-src/default/index.js index dede6f0763..a7dc28d394 100644 --- a/client-src/default/index.js +++ b/client-src/default/index.js @@ -1,14 +1,17 @@ -'use strict'; - -/* global __resourceQuery WorkerGlobalScope self */ /* eslint prefer-destructuring: off */ -const stripAnsi = require('strip-ansi'); -const socket = require('./socket'); -const overlay = require('./overlay'); -const { log, setLogLevel } = require('./utils/log'); -const sendMessage = require('./utils/sendMessage'); -const reloadApp = require('./utils/reloadApp'); -const createSocketUrl = require('./utils/createSocketUrl'); +import stripAnsi from 'strip-ansi'; +import socket from './socket'; +import { + clear as clearOverlay, + showMessage as showMessageOverlay, +} from './overlay'; +import { log, setLogLevel } from './utils/log'; +import sendMessage from './utils/sendMessage'; +import reloadApp from './utils/reloadApp'; +import createSocketUrl from './utils/createSocketUrl'; +import updatePublicPath from './utils/updatePublicPath'; + +updatePublicPath(__resourceQuery); const status = { isUnloading: false, @@ -47,7 +50,7 @@ const onSocketMessage = { log.info('[WDS] App updated. Recompiling...'); // fixes #1042. overlay doesn't clear if errors are fixed but warnings remain. if (options.useWarningOverlay || options.useErrorOverlay) { - overlay.clear(); + clearOverlay(); } sendMessage('Invalid'); }, @@ -57,7 +60,7 @@ const onSocketMessage = { 'still-ok': function stillOk() { log.info('[WDS] Nothing changed.'); if (options.useWarningOverlay || options.useErrorOverlay) { - overlay.clear(); + clearOverlay(); } sendMessage('StillOk'); }, @@ -93,7 +96,7 @@ const onSocketMessage = { ok() { sendMessage('Ok'); if (options.useWarningOverlay || options.useErrorOverlay) { - overlay.clear(); + clearOverlay(); } if (options.initial) { return (options.initial = false); @@ -112,7 +115,7 @@ const onSocketMessage = { log.warn(strippedWarnings[i]); } if (options.useWarningOverlay) { - overlay.showMessage(warnings); + showMessageOverlay(warnings); } if (options.initial) { @@ -128,7 +131,7 @@ const onSocketMessage = { log.error(strippedErrors[i]); } if (options.useErrorOverlay) { - overlay.showMessage(errors); + showMessageOverlay(errors); } options.initial = false; }, diff --git a/client-src/default/overlay.js b/client-src/default/overlay.js index a1bb6cef2b..f73cb29578 100644 --- a/client-src/default/overlay.js +++ b/client-src/default/overlay.js @@ -1,12 +1,10 @@ -'use strict'; - // The error overlay is inspired (and mostly copied) from Create React App (https://github.com/facebookincubator/create-react-app) // They, in turn, got inspired by webpack-hot-middleware (https://github.com/glenjamin/webpack-hot-middleware). -const ansiHTML = require('ansi-html'); -const { AllHtmlEntities } = require('html-entities'); +import ansiHTML from 'ansi-html'; +import { AllHtmlEntities as Entities } from 'html-entities'; -const entities = new AllHtmlEntities(); +const entities = new Entities(); const colors = { reset: ['transparent', 'transparent'], black: '181818', @@ -95,8 +93,8 @@ function ensureOverlayDivExists(onOverlayDivReady) { document.body.appendChild(overlayIframe); } -// Successful compilation. -function clear() { +// successful compilation. +export function clear() { if (!overlayDiv) { // It is not there in the first place. return; @@ -110,7 +108,7 @@ function clear() { } // Compilation with errors (e.g. syntax error or missing modules). -function showMessage(messages) { +export function showMessage(messages) { ensureOverlayDivExists((div) => { // Make it look similar to our terminal. div.innerHTML = `= 0 ? this.publicHost.substr(0, idxPublic) : this.publicHost; + idxPublic >= 0 + ? this.options.public.substr(0, idxPublic) + : this.options.public; if (hostname === publicHostname) { return true; diff --git a/lib/options.json b/lib/options.json index 217ee5a631..d12f058346 100644 --- a/lib/options.json +++ b/lib/options.json @@ -37,16 +37,28 @@ ] }, "clientLogLevel": { - "enum": [ - "info", - "warn", - "error", - "debug", - "trace", - "silent", - "none", - "warning" - ] + "enum": ["info", "warn", "error", "debug", "trace", "silent"] + }, + "clientSocketOptions": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "path": { + "type": "string" + }, + "port": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + } + } }, "compress": { "type": "boolean" @@ -306,25 +318,6 @@ "setup": { "instanceof": "Function" }, - "sockHost": { - "type": "string" - }, - "sockPath": { - "type": "string" - }, - "sockPort": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "null" - } - ] - }, "socket": { "type": "string" }, @@ -377,6 +370,9 @@ } ] }, + "stdin": { + "type": "boolean" + }, "useLocalIp": { "type": "boolean" }, @@ -408,7 +404,8 @@ "bonjour": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverbonjour)", "ca": "should be {String|Buffer}", "cert": "should be {String|Buffer}", - "clientLogLevel": "should be {String} and equal to one of the allowed values\n\n [ 'none', 'silent', 'info', 'debug', 'trace', 'error', 'warning', 'warn' ]\n\n (https://webpack.js.org/configuration/dev-server/#devserverclientloglevel)", + "clientLogLevel": "should be {String} and equal to one of the allowed values\n\n [ 'silent', 'info', 'debug', 'trace', 'error', 'warn' ]\n\n (https://webpack.js.org/configuration/dev-server/#devserverclientloglevel)", + "clientSocketOptions": "should be {Object} (https://webpack.js.org/configuration/dev-server/#devserverclientsocketoptions)", "compress": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devservercompress)", "contentBase": "should be {Number|String|Array} (https://webpack.js.org/configuration/dev-server/#devservercontentbase)", "disableHostCheck": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverdisablehostcheck)", @@ -452,13 +449,11 @@ "serveIndex": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverserveindex)", "serverSideRender": "should be {Boolean} (https://github.com/webpack/webpack-dev-middleware#serversiderender)", "setup": "should be {Function} (https://webpack.js.org/configuration/dev-server/#devserversetup)", - "sockHost": "should be {String|Null} (https://webpack.js.org/configuration/dev-server/#devserversockhost)", - "sockPath": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserversockpath)", - "sockPort": "should be {Number|String|Null} (https://webpack.js.org/configuration/dev-server/#devserversockport)", "socket": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserversocket)", "staticOptions": "should be {Object} (https://webpack.js.org/configuration/dev-server/#devserverstaticoptions)", "stats": "should be {Object|Boolean} (https://webpack.js.org/configuration/dev-server/#devserverstats-)", "transportMode": "should be {String|Object} (https://webpack.js.org/configuration/dev-server/#devservertransportmode)", + "stdin": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverstdin)", "useLocalIp": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserveruselocalip)", "warn": "should be {Function}", "watchContentBase": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverwatchcontentbase)", diff --git a/lib/utils/addEntries.js b/lib/utils/addEntries.js index 501b12e080..a0f45b01e1 100644 --- a/lib/utils/addEntries.js +++ b/lib/utils/addEntries.js @@ -27,18 +27,37 @@ function addEntries(config, options, server) { }, }; + if (!options.clientSocketOptions) { + options.clientSocketOptions = {}; + } + /** @type {string} */ const domain = createDomain(options, app); /** @type {string} */ - const sockHost = options.sockHost ? `&sockHost=${options.sockHost}` : ''; + const sockHost = options.clientSocketOptions.host + ? `&sockHost=${encodeURIComponent(options.clientSocketOptions.host)}` + : ''; + /** @type {string} */ + const sockPath = options.clientSocketOptions.path + ? `&sockPath=${encodeURIComponent(options.clientSocketOptions.path)}` + : ''; + /** @type {string} */ + const sockPort = options.clientSocketOptions.port + ? `&sockPort=${encodeURIComponent(options.clientSocketOptions.port)}` + : ''; /** @type {string} */ - const sockPath = options.sockPath ? `&sockPath=${options.sockPath}` : ''; + const publicPath = options.publicPath + ? `&publicPath=${encodeURIComponent(options.publicPath)}` + : ''; + + /** @type {string} */ + const queryString = `${sockHost}${sockPath}${sockPort}${publicPath}`; + /** @type {string} */ - const sockPort = options.sockPort ? `&sockPort=${options.sockPort}` : ''; + const query = queryString === '' ? '' : `?${queryString.substr(1)}`; + /** @type {string} */ - const clientEntry = `${require.resolve( - '../../client/' - )}?${domain}${sockHost}${sockPath}${sockPort}`; + const clientEntry = `${require.resolve('../../client/')}?${domain}${query}`; /** @type {(string[] | string)} */ let hotEntry; diff --git a/lib/utils/createConfig.js b/lib/utils/createConfig.js index 0211561235..8e7475035f 100644 --- a/lib/utils/createConfig.js +++ b/lib/utils/createConfig.js @@ -31,16 +31,23 @@ function createConfig(config, argv, { port }) { options.socket = argv.socket; } + if ( + (argv.sockHost || argv.sockPath || argv.sockPort) && + !options.clientSocketOptions + ) { + options.clientSocketOptions = {}; + } + if (argv.sockHost) { - options.sockHost = argv.sockHost; + options.clientSocketOptions.host = argv.sockHost; } if (argv.sockPath) { - options.sockPath = argv.sockPath; + options.clientSocketOptions.path = argv.sockPath; } if (argv.sockPort) { - options.sockPort = argv.sockPort; + options.clientSocketOptions.port = argv.sockPort; } if (argv.liveReload === false) { @@ -81,12 +88,7 @@ function createConfig(config, argv, { port }) { } if (argv.stdin) { - process.stdin.on('end', () => { - // eslint-disable-next-line no-process-exit - process.exit(0); - }); - - process.stdin.resume(); + options.stdin = true; } // TODO https://github.com/webpack/webpack-dev-server/issues/616 (v4) diff --git a/lib/utils/handleStdin.js b/lib/utils/handleStdin.js new file mode 100644 index 0000000000..c26431300c --- /dev/null +++ b/lib/utils/handleStdin.js @@ -0,0 +1,16 @@ +'use strict'; + +function handleStdin(options) { + if (options.stdin) { + // listening for this event only once makes testing easier, + // since it prevents event listeners from hanging open + process.stdin.once('end', () => { + // eslint-disable-next-line no-process-exit + process.exit(0); + }); + + process.stdin.resume(); + } +} + +module.exports = handleStdin; diff --git a/lib/utils/runOpen.js b/lib/utils/runOpen.js index a2757ab919..7f0b814ffe 100644 --- a/lib/utils/runOpen.js +++ b/lib/utils/runOpen.js @@ -1,6 +1,6 @@ 'use strict'; -const open = require('opn'); +const open = require('open'); const isAbsoluteUrl = require('is-absolute-url'); function runOpen(uri, options, log) { diff --git a/lib/utils/startUnixSocket.js b/lib/utils/startUnixSocket.js new file mode 100644 index 0000000000..9bf850c2ec --- /dev/null +++ b/lib/utils/startUnixSocket.js @@ -0,0 +1,62 @@ +'use strict'; + +const fs = require('fs'); +const net = require('net'); +const { promisify } = require('util'); + +const accessAsync = promisify(fs.access); + +async function startUnixSocket(listeningApp, socket, cb) { + const chmodSocket = (done) => { + // chmod 666 (rw rw rw) - octal + const READ_WRITE = 438; + fs.chmod(socket, READ_WRITE, done); + }; + + const startSocket = () => { + listeningApp.on('error', (err) => { + cb(err); + }); + + // 511 is the default value for the server.listen backlog parameter + // https://nodejs.org/api/net.html#net_server_listen + listeningApp.listen(socket, 511, (err) => { + if (err) { + cb(err); + } else { + chmodSocket(cb); + } + }); + }; + + try { + await accessAsync(socket, fs.constants.F_OK); + } catch (e) { + // file does not exist + startSocket(); + return; + } + + // file exists + + const clientSocket = new net.Socket(); + + clientSocket.on('error', (err) => { + if (err.code === 'ECONNREFUSED' || err.code === 'ENOTSOCK') { + // No other server listening on this socket so it can be safely removed + fs.unlinkSync(socket); + + startSocket(); + } + }); + + clientSocket.connect({ path: socket }, () => { + // if a client socket successfully connects to the given socket path, + // it means that the socket is in use + const err = new Error('This socket is already used'); + clientSocket.destroy(); + cb(err); + }); +} + +module.exports = startUnixSocket; diff --git a/package-lock.json b/package-lock.json index b85b554cfe..7f143226c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,58 @@ "output-file-sync": "^2.0.0", "slash": "^2.0.0", "source-map": "^0.5.0" + }, + "dependencies": { + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + } } }, "@babel/code-frame": { @@ -1408,6 +1460,66 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, "jest-get-type": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", @@ -1434,6 +1546,21 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, "pretty-format": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", @@ -1743,7 +1870,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.1.tgz", "integrity": "sha512-NT/skIZjgotDSiXs0WqYhgcuBKhUMgfekCmCGtkUAiLqZdOnrdjmZr9wRl3ll64J9NF79uZ4fk16Dx0yMc/Xbg==", - "dev": true, "requires": { "@nodelib/fs.stat": "2.0.1", "run-parallel": "^1.1.9" @@ -1752,14 +1878,12 @@ "@nodelib/fs.stat": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.1.tgz", - "integrity": "sha512-+RqhBlLn6YRBGOIoVYthsG0J9dfpO79eJyN7BYBkZJtfqrBwf2KK+rD/M/yjZR6WBmIhAgOV7S60eCgaSWtbFw==", - "dev": true + "integrity": "sha512-+RqhBlLn6YRBGOIoVYthsG0J9dfpO79eJyN7BYBkZJtfqrBwf2KK+rD/M/yjZR6WBmIhAgOV7S60eCgaSWtbFw==" }, "@nodelib/fs.walk": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.2.tgz", "integrity": "sha512-J/DR3+W12uCzAJkw7niXDcqcKBg6+5G5Q/ZpThpGNzAUz70eOR6RV4XnnSN01qHZiVl0eavoxJsBypQoKsV2QQ==", - "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.1", "fastq": "^1.6.0" @@ -1871,6 +1995,11 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, "@types/semver": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.0.1.tgz", @@ -2219,6 +2348,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" @@ -2228,6 +2358,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, "requires": { "remove-trailing-separator": "^1.0.1" } @@ -2319,6 +2450,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, "requires": { "array-uniq": "^1.0.1" } @@ -2326,7 +2458,8 @@ "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true }, "array-unique": { "version": "0.3.2", @@ -2424,7 +2557,8 @@ "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true }, "async-limiter": { "version": "1.0.0", @@ -2654,9 +2788,9 @@ "dev": true }, "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" }, "bluebird": { "version": "3.5.5", @@ -3082,22 +3216,72 @@ "dev": true }, "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.0.2.tgz", + "integrity": "sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==", "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", + "anymatch": "^3.0.1", + "braces": "^3.0.2", + "fsevents": "^2.0.6", + "glob-parent": "^5.0.0", + "is-binary-path": "^2.1.0", + "is-glob": "^4.0.1", "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "readdirp": "^3.1.1" + }, + "dependencies": { + "anymatch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.0.tgz", + "integrity": "sha512-Ozz7l4ixzI7Oxj2+cw+p0tVUt27BpaJ+1+q1TCeANWxHpvyn2+Un+YamBdfKu0uh8xLodGhoa1v7595NhKDAuA==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz", + "integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==", + "optional": true + }, + "glob-parent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", + "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + } } }, "chownr": { @@ -3316,6 +3500,47 @@ "dev": true, "requires": { "execa": "^1.0.0" + }, + "dependencies": { + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + } } }, "commondir": { @@ -4388,6 +4613,43 @@ "requires": { "execa": "^1.0.0", "ip-regex": "^2.1.0" + }, + "dependencies": { + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + } } }, "define-properties": { @@ -4437,16 +4699,25 @@ } }, "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-5.0.0.tgz", + "integrity": "sha512-TfU3nUY0WDIhN18eq+pgpbLY9AfL5RfiE9czKaTSolc6aK7qASXfDErvYgjV1UqCR4sNXDoxO0/idPmhDUt2Sg==", "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", + "globby": "^10.0.0", "is-path-cwd": "^2.0.0", "is-path-in-cwd": "^2.0.0", "p-map": "^2.0.0", - "pify": "^4.0.1" + "rimraf": "^2.6.3" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } } }, "delayed-stream": { @@ -5290,17 +5561,43 @@ "dev": true }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-2.0.3.tgz", + "integrity": "sha512-iM124nlyGSrXmuyZF1EMe83ESY2chIYVyDRZKgmcDynid2Q2v/+GuE7gNMl6Sy9Niwf4MC0DDxagOxeMPjuLsw==", + "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "^6.0.5", + "get-stream": "^5.0.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^3.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + } } }, "exit": { @@ -5591,7 +5888,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.0.4.tgz", "integrity": "sha512-wkIbV6qg37xTJwqSsdnIphL1e+LaGz4AIQqr00mIubMaEhv1/HEmJ0uuCGZRNRUkZZmOB5mJKO0ZUTVq+SxMQg==", - "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.1", "@nodelib/fs.walk": "^1.2.1", @@ -5605,7 +5901,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -5614,7 +5909,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -5623,7 +5917,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -5631,14 +5924,12 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "micromatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, "requires": { "braces": "^3.0.1", "picomatch": "^2.0.5" @@ -5648,7 +5939,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } @@ -5676,7 +5966,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==", - "dev": true, "requires": { "reusify": "^1.0.0" } @@ -5732,12 +6021,12 @@ } }, "file-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", - "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.0.0.tgz", + "integrity": "sha512-roAbL6IdSGczwfXxhMi6Zq+jD4IfUpL0jWHD7fvmjdOVb7xBfdRUHe4LpBgO23VtVK5AW1OlWZo0p34Jvx3iWg==", "dev": true, "requires": { - "loader-utils": "^1.0.2", + "loader-utils": "^1.2.2", "schema-utils": "^1.0.0" } }, @@ -5976,6 +6265,7 @@ "version": "1.2.9", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, "optional": true, "requires": { "nan": "^2.12.1", @@ -5985,21 +6275,25 @@ "abbrev": { "version": "1.1.1", "bundled": true, + "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", "bundled": true, + "dev": true, "optional": true }, "aproba": { "version": "1.2.0", "bundled": true, + "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", "bundled": true, + "dev": true, "optional": true, "requires": { "delegates": "^1.0.0", @@ -6009,11 +6303,13 @@ "balanced-match": { "version": "1.0.0", "bundled": true, + "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "dev": true, "optional": true, "requires": { "balanced-match": "^1.0.0", @@ -6023,31 +6319,37 @@ "chownr": { "version": "1.1.1", "bundled": true, + "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", "bundled": true, + "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, + "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, + "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", "bundled": true, + "dev": true, "optional": true }, "debug": { "version": "4.1.1", "bundled": true, + "dev": true, "optional": true, "requires": { "ms": "^2.1.1" @@ -6056,21 +6358,25 @@ "deep-extend": { "version": "0.6.0", "bundled": true, + "dev": true, "optional": true }, "delegates": { "version": "1.0.0", "bundled": true, + "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", "bundled": true, + "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", "bundled": true, + "dev": true, "optional": true, "requires": { "minipass": "^2.2.1" @@ -6079,11 +6385,13 @@ "fs.realpath": { "version": "1.0.0", "bundled": true, + "dev": true, "optional": true }, "gauge": { "version": "2.7.4", "bundled": true, + "dev": true, "optional": true, "requires": { "aproba": "^1.0.3", @@ -6099,6 +6407,7 @@ "glob": { "version": "7.1.3", "bundled": true, + "dev": true, "optional": true, "requires": { "fs.realpath": "^1.0.0", @@ -6112,11 +6421,13 @@ "has-unicode": { "version": "2.0.1", "bundled": true, + "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", "bundled": true, + "dev": true, "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -6125,6 +6436,7 @@ "ignore-walk": { "version": "3.0.1", "bundled": true, + "dev": true, "optional": true, "requires": { "minimatch": "^3.0.4" @@ -6133,6 +6445,7 @@ "inflight": { "version": "1.0.6", "bundled": true, + "dev": true, "optional": true, "requires": { "once": "^1.3.0", @@ -6142,16 +6455,19 @@ "inherits": { "version": "2.0.3", "bundled": true, + "dev": true, "optional": true }, "ini": { "version": "1.3.5", "bundled": true, + "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "dev": true, "optional": true, "requires": { "number-is-nan": "^1.0.0" @@ -6160,11 +6476,13 @@ "isarray": { "version": "1.0.0", "bundled": true, + "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", "bundled": true, + "dev": true, "optional": true, "requires": { "brace-expansion": "^1.1.7" @@ -6173,11 +6491,13 @@ "minimist": { "version": "0.0.8", "bundled": true, + "dev": true, "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, + "dev": true, "optional": true, "requires": { "safe-buffer": "^5.1.2", @@ -6187,6 +6507,7 @@ "minizlib": { "version": "1.2.1", "bundled": true, + "dev": true, "optional": true, "requires": { "minipass": "^2.2.1" @@ -6195,6 +6516,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "dev": true, "optional": true, "requires": { "minimist": "0.0.8" @@ -6203,11 +6525,13 @@ "ms": { "version": "2.1.1", "bundled": true, + "dev": true, "optional": true }, "needle": { "version": "2.3.0", "bundled": true, + "dev": true, "optional": true, "requires": { "debug": "^4.1.0", @@ -6218,6 +6542,7 @@ "node-pre-gyp": { "version": "0.12.0", "bundled": true, + "dev": true, "optional": true, "requires": { "detect-libc": "^1.0.2", @@ -6235,6 +6560,7 @@ "nopt": { "version": "4.0.1", "bundled": true, + "dev": true, "optional": true, "requires": { "abbrev": "1", @@ -6244,11 +6570,13 @@ "npm-bundled": { "version": "1.0.6", "bundled": true, + "dev": true, "optional": true }, "npm-packlist": { "version": "1.4.1", "bundled": true, + "dev": true, "optional": true, "requires": { "ignore-walk": "^3.0.1", @@ -6258,6 +6586,7 @@ "npmlog": { "version": "4.1.2", "bundled": true, + "dev": true, "optional": true, "requires": { "are-we-there-yet": "~1.1.2", @@ -6269,16 +6598,19 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, + "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", "bundled": true, + "dev": true, "optional": true }, "once": { "version": "1.4.0", "bundled": true, + "dev": true, "optional": true, "requires": { "wrappy": "1" @@ -6287,16 +6619,19 @@ "os-homedir": { "version": "1.0.2", "bundled": true, + "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", "bundled": true, + "dev": true, "optional": true }, "osenv": { "version": "0.1.5", "bundled": true, + "dev": true, "optional": true, "requires": { "os-homedir": "^1.0.0", @@ -6306,16 +6641,19 @@ "path-is-absolute": { "version": "1.0.1", "bundled": true, + "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", "bundled": true, + "dev": true, "optional": true }, "rc": { "version": "1.2.8", "bundled": true, + "dev": true, "optional": true, "requires": { "deep-extend": "^0.6.0", @@ -6327,6 +6665,7 @@ "minimist": { "version": "1.2.0", "bundled": true, + "dev": true, "optional": true } } @@ -6334,6 +6673,7 @@ "readable-stream": { "version": "2.3.6", "bundled": true, + "dev": true, "optional": true, "requires": { "core-util-is": "~1.0.0", @@ -6348,6 +6688,7 @@ "rimraf": { "version": "2.6.3", "bundled": true, + "dev": true, "optional": true, "requires": { "glob": "^7.1.3" @@ -6356,36 +6697,43 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, + "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", "bundled": true, + "dev": true, "optional": true }, "sax": { "version": "1.2.4", "bundled": true, + "dev": true, "optional": true }, "semver": { "version": "5.7.0", "bundled": true, + "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", "bundled": true, + "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", "bundled": true, + "dev": true, "optional": true }, "string-width": { "version": "1.0.2", "bundled": true, + "dev": true, "optional": true, "requires": { "code-point-at": "^1.0.0", @@ -6396,6 +6744,7 @@ "string_decoder": { "version": "1.1.1", "bundled": true, + "dev": true, "optional": true, "requires": { "safe-buffer": "~5.1.0" @@ -6404,6 +6753,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "dev": true, "optional": true, "requires": { "ansi-regex": "^2.0.0" @@ -6412,11 +6762,13 @@ "strip-json-comments": { "version": "2.0.1", "bundled": true, + "dev": true, "optional": true }, "tar": { "version": "4.4.8", "bundled": true, + "dev": true, "optional": true, "requires": { "chownr": "^1.1.1", @@ -6431,11 +6783,13 @@ "util-deprecate": { "version": "1.0.2", "bundled": true, + "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", "bundled": true, + "dev": true, "optional": true, "requires": { "string-width": "^1.0.2 || 2" @@ -6444,11 +6798,13 @@ "wrappy": { "version": "1.0.2", "bundled": true, + "dev": true, "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, + "dev": true, "optional": true } } @@ -6672,9 +7028,10 @@ "dev": true }, "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, "requires": { "pump": "^3.0.0" } @@ -6810,6 +7167,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, "requires": { "is-glob": "^3.1.0", "path-dirname": "^1.0.0" @@ -6819,6 +7177,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, "requires": { "is-extglob": "^2.1.0" } @@ -6876,28 +7235,55 @@ "dev": true }, "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" }, "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "ignore": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.2.tgz", + "integrity": "sha512-vdqWBp7MyzdmHkkRWV5nY+PfGRbYbahfuvsBCh277tq+w9zyNi7h5CYJCK0kmzti9kU+O/cB7sE8HvKv6aXAKQ==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" } } }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true }, "growly": { "version": "1.3.0", @@ -6967,7 +7353,8 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "has-symbols": { "version": "1.0.0", @@ -7291,6 +7678,21 @@ "slash": "^3.0.0" }, "dependencies": { + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -7301,6 +7703,15 @@ "path-exists": "^4.0.0" } }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -7310,6 +7721,21 @@ "p-locate": "^4.1.0" } }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", @@ -7433,12 +7859,52 @@ } }, "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + } } }, "imurmurhash": { @@ -7597,11 +8063,11 @@ "dev": true }, "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "^2.0.0" } }, "is-buffer": { @@ -7911,6 +8377,17 @@ "istanbul-lib-coverage": "^2.0.5", "make-dir": "^2.1.0", "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "istanbul-lib-source-maps": { @@ -7962,26 +8439,6 @@ "jest-cli": "^24.9.0" }, "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz", - "integrity": "sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", @@ -8005,6 +8462,16 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, "jest-cli": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", @@ -8026,42 +8493,29 @@ "yargs": "^13.3.0" } }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - }, - "jest-validate": { + "jest-config": { "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", - "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", "dev": true, "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", "@jest/types": "^24.9.0", - "camelcase": "^5.3.1", + "babel-jest": "^24.9.0", "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", "jest-get-type": "^24.9.0", - "leven": "^3.1.0", - "pretty-format": "^24.9.0" - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" } }, "require-main-filename": { @@ -8070,6 +8524,15 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -8103,154 +8566,30 @@ }, "yargs": { "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "jest-changed-files": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", - "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz", - "integrity": "sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-config": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", - "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.9.0", - "@jest/types": "^24.9.0", - "babel-jest": "^24.9.0", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.9.0", - "jest-environment-node": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.9.0", - "realpath-native": "^1.1.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz", - "integrity": "sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - }, - "jest-validate": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", - "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "camelcase": "^5.3.1", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "leven": "^3.1.0", - "pretty-format": "^24.9.0" + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" } }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } @@ -8902,6 +9241,31 @@ "requires": { "@types/yargs-parser": "*" } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } } } }, @@ -8979,6 +9343,31 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, "jest-get-type": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", @@ -9264,6 +9653,17 @@ "requires": { "merge-stream": "^2.0.0", "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "jquery": { @@ -10193,10 +10593,9 @@ "dev": true }, "merge2": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.4.tgz", - "integrity": "sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==", - "dev": true + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", + "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==" }, "methods": { "version": "1.1.2", @@ -10395,6 +10794,7 @@ "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, "optional": true }, "nanomatch": { @@ -10583,11 +10983,20 @@ } }, "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", + "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", + "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "^3.0.0" + }, + "dependencies": { + "path-key": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz", + "integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==", + "dev": true + } } }, "nth-check": { @@ -10625,7 +11034,8 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true }, "object-copy": { "version": "0.1.0", @@ -10754,20 +11164,20 @@ } } }, + "open": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.3.0.tgz", + "integrity": "sha512-6AHdrJxPvAXIowO/aIaeHZ8CeMdDf7qCyRNq8NwJpinmCdXhz+NZR7ie1Too94lpciCDsG+qHGO9Mt0svA4OqA==", + "requires": { + "is-wsl": "^1.1.0" + } + }, "opencollective-postinstall": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", "dev": true }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "requires": { - "is-wsl": "^1.1.0" - } - }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -10822,6 +11232,43 @@ "execa": "^1.0.0", "lcid": "^2.0.0", "mem": "^4.0.0" + }, + "dependencies": { + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + } } }, "os-tmpdir": { @@ -10856,9 +11303,10 @@ } }, "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true }, "p-is-promise": { "version": "2.1.0", @@ -10893,10 +11341,11 @@ "dev": true }, "p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.1.0.tgz", + "integrity": "sha512-oepllyG9gX1qH4Sm20YAKxg1GA7L7puhvGnTfimi31P07zSIj7SDV6YtuAx9nbJF51DES+2CIIRkXs8GKqWJxA==", "requires": { + "@types/retry": "^0.12.0", "retry": "^0.12.0" } }, @@ -11001,7 +11450,8 @@ "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true }, "path-exists": { "version": "3.0.0", @@ -11079,8 +11529,7 @@ "picomatch": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", - "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", - "dev": true + "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==" }, "pidtree": { "version": "0.3.0", @@ -11091,17 +11540,20 @@ "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -11119,6 +11571,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, "requires": { "find-up": "^3.0.0" } @@ -11179,6 +11632,15 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -11437,6 +11899,15 @@ "requires": { "glob": "^7.1.3" } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } } } }, @@ -11602,13 +12073,11 @@ } }, "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.1.2.tgz", + "integrity": "sha512-8rhl0xs2cxfVsqzreYCvs8EwBfn/DhVdqtoLmw19uI3SC5avYX9teCurlErfpPXGmYtMHReGaP2RsLnFvz/lnw==", "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "picomatch": "^2.0.4" } }, "realpath-native": { @@ -11747,7 +12216,8 @@ "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true }, "renderkid": { "version": "2.0.3", @@ -11878,11 +12348,18 @@ } }, "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } } }, "resolve-dir": { @@ -11911,7 +12388,8 @@ "resolve-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true }, "resolve-global": { "version": "1.0.0", @@ -11950,8 +12428,7 @@ "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, "rimraf": { "version": "3.0.0", @@ -11996,8 +12473,7 @@ "run-parallel": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==" }, "run-queue": { "version": "1.0.3", @@ -12052,11 +12528,50 @@ "walker": "~1.0.5" }, "dependencies": { + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true } } }, @@ -13098,11 +13613,18 @@ } }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.0.0.tgz", + "integrity": "sha512-WRt32iTpYEZWYOpcetGm0NPeSvaebccx7hhS/5M6sAiqnhedtFCHFxkjzZlJvFNCPowiKSFGiZk5USQDFy83vQ==", "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + } } }, "symbol-observable": { @@ -13629,9 +14151,10 @@ } }, "upath": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", - "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true }, "upper-case": { "version": "1.1.3", @@ -13669,21 +14192,49 @@ } }, "url-loader": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", - "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.1.0.tgz", + "integrity": "sha512-kVrp/8VfEm5fUt+fl2E0FQyrpmOYgMEkBsv8+UDP1wFhszECq5JyGF33I7cajlVY90zRZ6MyfgKXngLvHYZX8A==", "dev": true, "requires": { - "loader-utils": "^1.1.0", - "mime": "^2.0.3", - "schema-utils": "^1.0.0" + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.0.0" }, "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, "mime": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz", - "integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true + }, + "schema-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.2.0.tgz", + "integrity": "sha512-5EwsCNhfFTZvUreQhx/4vVQpJ/lnCAkgoIHLhSpp4ZirE+4hzFvdJi0FMub6hxbFVBJYSpeVVmon+2e7uEGRrA==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } } } }, @@ -13806,6 +14357,54 @@ "chokidar": "^2.0.2", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0" + }, + "dependencies": { + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + } } }, "wbuf": { @@ -13931,12 +14530,31 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -13957,6 +14575,15 @@ "ansi-regex": "^4.1.0" } }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, "v8-compile-cache": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", @@ -14174,11 +14801,11 @@ } }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz", + "integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==", "requires": { - "async-limiter": "~1.0.0" + "async-limiter": "^1.0.0" } }, "xml": { diff --git a/package.json b/package.json index acc642de21..ea094b14c3 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "client" ], "engines": { - "node": ">= 6.11.5" + "node": ">= 8.9.0" }, "scripts": { "lint:prettier": "prettier \"{**/*,*}.{js,json,md,yml,css}\" --list-different", @@ -38,22 +38,22 @@ "dependencies": { "ansi-html": "0.0.7", "bonjour": "^3.5.0", - "chokidar": "^2.1.8", + "chokidar": "^3.0.1", "compression": "^1.7.4", "connect-history-api-fallback": "^1.6.0", "debug": "^4.1.1", - "del": "^4.1.1", + "del": "^5.0.0", "express": "^4.17.1", "html-entities": "^1.2.1", "http-proxy-middleware": "^0.19.1", - "import-local": "^2.0.0", + "import-local": "^3.0.2", "internal-ip": "^4.3.0", "ip": "^1.1.5", "is-absolute-url": "^3.0.1", "killable": "^1.0.1", "loglevel": "^1.6.4", - "opn": "^5.5.0", - "p-retry": "^3.0.1", + "open": "^6.2.0", + "p-retry": "^4.1.0", "portfinder": "^1.0.24", "schema-utils": "^1.0.0", "selfsigned": "^1.10.6", @@ -63,11 +63,11 @@ "sockjs-client": "1.4.0", "spdy": "^4.0.1", "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", + "supports-color": "^7.0.0", "url": "^0.11.0", "webpack-dev-middleware": "^3.7.1", "webpack-log": "^2.0.0", - "ws": "^6.2.1", + "ws": "^7.1.1", "yargs": "12.0.5" }, "devDependencies": { @@ -87,8 +87,8 @@ "eslint-config-prettier": "^6.1.0", "eslint-config-webpack": "^1.2.5", "eslint-plugin-import": "^2.18.2", - "execa": "^1.0.0", - "file-loader": "^3.0.1", + "execa": "^2.0.3", + "file-loader": "^4.0.0", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "husky": "^3.0.5", @@ -109,7 +109,7 @@ "supertest": "^4.0.2", "tcp-port-used": "^1.0.1", "typescript": "^3.6.2", - "url-loader": "^1.1.2", + "url-loader": "^2.1.0", "webpack": "^4.39.3", "webpack-cli": "^3.3.8" }, diff --git a/test/cli/cli.test.js b/test/cli/cli.test.js index 372a21afef..9dbe1cb993 100644 --- a/test/cli/cli.test.js +++ b/test/cli/cli.test.js @@ -1,9 +1,15 @@ +/** + * @jest-environment node + */ + 'use strict'; -const { unlink } = require('fs'); const { join, resolve } = require('path'); const execa = require('execa'); +const { unlinkAsync } = require('../helpers/fs'); const testBin = require('../helpers/test-bin'); +const timer = require('../helpers/timer'); +const { skipTestOnWindows } = require('../helpers/conditional-test'); const httpsCertificateDirectory = resolve( __dirname, @@ -15,134 +21,132 @@ const keyPath = resolve(httpsCertificateDirectory, 'server.key'); const certPath = resolve(httpsCertificateDirectory, 'server.crt'); describe('CLI', () => { - it('--progress', (done) => { - testBin('--progress') - .then((output) => { - expect(output.code).toEqual(0); - expect(output.stderr.includes('0% compiling')).toBe(true); - // should not profile - expect( - output.stderr.includes('ms after chunk modules optimization') - ).toBe(false); - done(); - }) - .catch(done); + it('--progress', async () => { + const { exitCode, stderr } = await testBin('--progress'); + expect(exitCode).toEqual(0); + expect(stderr.includes('0% compiling')).toBe(true); }); - it('--progress --profile', (done) => { - testBin('--progress --profile') - .then((output) => { - expect(output.code).toEqual(0); - // should profile - expect(output.stderr.includes('after chunk modules optimization')).toBe( - true - ); - done(); - }) - .catch(done); + it('--progress --profile', async () => { + const { exitCode, stderr } = await testBin('--progress --profile'); + expect(exitCode).toEqual(0); + // should profile + expect(stderr.includes('after chunk modules optimization')).toBe(true); }); - it('--bonjour', (done) => { - testBin('--bonjour') - .then((output) => { - expect(output.code).toEqual(0); - expect(output.stdout.includes('Bonjour')).toBe(true); - done(); - }) - .catch(done); + it('--bonjour', async () => { + const { exitCode, stdout } = await testBin('--bonjour'); + expect(exitCode).toEqual(0); + expect(stdout.includes('Bonjour')).toBe(true); }); - it('--https', (done) => { - testBin('--https') - .then((output) => { - expect(output.code).toEqual(0); - expect(output.stdout.includes('Project is running at')).toBe(true); - done(); - }) - .catch(done); + it('--https', async () => { + const { exitCode, stdout } = await testBin('--https'); + expect(exitCode).toEqual(0); + expect(stdout.includes('Project is running at')).toBe(true); }); - it('--https --cacert --pfx --key --cert --pfx-passphrase', (done) => { - testBin( + it('--https --cacert --pfx --key --cert --pfx-passphrase', async () => { + const { exitCode, stdout } = await testBin( `--https --cacert ${caPath} --pfx ${pfxPath} --key ${keyPath} --cert ${certPath} --pfx-passphrase webpack-dev-server` - ) - .then((output) => { - expect(output.code).toEqual(0); - expect(output.stdout.includes('Project is running at')).toBe(true); - done(); - }) - .catch(done); + ); + expect(exitCode).toEqual(0); + expect(stdout.includes('Project is running at')).toBe(true); }); - it('--sockPath', (done) => { - testBin('--sockPath /mysockPath') - .then((output) => { - expect( - /http:\/\/localhost:[0-9]+&sockPath=\/mysockPath/.test(output.stdout) - ).toEqual(true); - done(); - }) - .catch(done); + it('--sockPath', async () => { + const { stdout } = await testBin('--sockPath /mysockPath'); + expect( + // the /mysockPath becomes %2FmysockPath from encodeURIComponent + /http:\/\/localhost:[0-9]+\?sockPath=%2FmysockPath/.test(stdout) + ).toEqual(true); }); - it('unspecified port', (done) => { - testBin('') - .then((output) => { - expect(/http:\/\/localhost:[0-9]+/.test(output.stdout)).toEqual(true); - done(); - }) - .catch(done); + it('unspecified port', async () => { + const { stdout } = await testBin(''); + expect(/http:\/\/localhost:[0-9]+/.test(stdout)).toEqual(true); }); - it('--color', (done) => { - testBin('--color') - .then((output) => { - // https://github.com/webpack/webpack-dev-server/blob/master/lib/utils/colors.js - expect( - output.stdout.includes('\u001b[39m \u001b[90mï½¢wdsï½£\u001b[39m:') - ).toEqual(true); - done(); - }) - .catch(done); + it('--color', async () => { + const { stdout } = await testBin('--color'); + // https://github.com/webpack/webpack-dev-server/blob/master/lib/utils/colors.js + expect(stdout.includes('\u001b[39m \u001b[90mï½¢wdsï½£\u001b[39m:')).toBe(true); }); - // The Unix socket to listen to (instead of a host). - it('--socket', (done) => { - const socketPath = join('.', 'webpack.sock'); - - testBin(`--socket ${socketPath}`) - .then((output) => { - expect(output.code).toEqual(0); - - if (process.platform === 'win32') { - done(); - } else { - expect(output.stdout.includes(socketPath)).toBe(true); - - unlink(socketPath, () => { - done(); - }); - } - }) - .catch(done); + describe('Unix socket', () => { + if (skipTestOnWindows('Unix sockets are not supported on Windows')) { + return; + } + + // The Unix socket to listen to (instead of a host). + it('--socket', async () => { + const socketPath = join('.', 'webpack.sock'); + + const { exitCode, stdout } = await testBin(`--socket ${socketPath}`); + expect(exitCode).toEqual(0); + + if (process.platform !== 'win32') { + expect(stdout.includes(socketPath)).toBe(true); + + await unlinkAsync(socketPath); + } + }); }); - it('should accept the promise function of webpack.config.js', (done) => { - testBin( - false, - resolve(__dirname, '../fixtures/promise-config/webpack.config.js') - ) - .then((output) => { - expect(output.code).toEqual(0); - done(); - }) - .catch((err) => { - // for windows - expect(err.stdout.includes('Compiled successfully.')).toEqual(true); - done(); - }); + it('without --stdin, with stdin "end" event should time out', async (done) => { + const configPath = resolve( + __dirname, + '../fixtures/simple-config/webpack.config.js' + ); + const childProcess = testBin(false, configPath, true); + + childProcess.once('exit', () => { + expect(childProcess.killed).toBeTruthy(); + done(); + }); + + await timer(500); + // this is meant to confirm that it does not have any effect on the running process + // since options.stdin is not enabled + childProcess.stdin.emit('end'); + childProcess.stdin.pause(); + + await timer(500); + + childProcess.kill(); + }); + + it('--stdin, with "end" event should exit without time out', async () => { + const configPath = resolve( + __dirname, + '../fixtures/simple-config/webpack.config.js' + ); + const childProcess = testBin('--stdin', configPath); + + await timer(500); + + childProcess.stdin.emit('end'); + childProcess.stdin.pause(); + + const { exitCode, timedOut, killed } = await childProcess; + expect(exitCode).toEqual(0); + expect(timedOut).toBeFalsy(); + expect(killed).toBeFalsy(); + }); + + it('should accept the promise function of webpack.config.js', async () => { + try { + const { exitCode } = await testBin( + false, + resolve(__dirname, '../fixtures/promise-config/webpack.config.js') + ); + expect(exitCode).toEqual(0); + } catch (err) { + expect(err.stdout.includes('Compiled successfully.')).toBe(true); + } }); + // TODO: hiroppy it('should exit the process when SIGINT is detected', (done) => { const cliPath = resolve(__dirname, '../../bin/webpack-dev-server.js'); const examplePath = resolve(__dirname, '../../examples/cli/public'); diff --git a/test/client/__snapshots__/index.test.js.snap b/test/client/__snapshots__/index.test.js.snap index cc267a2ce7..699ac8411f 100644 --- a/test/client/__snapshots__/index.test.js.snap +++ b/test/client/__snapshots__/index.test.js.snap @@ -74,6 +74,12 @@ exports[`index should run onSocketMessage['still-ok'] 1`] = `"[WDS] Nothing chan exports[`index should run onSocketMessage['still-ok'] 2`] = `"StillOk"`; +exports[`index should run updatePublicPath 1`] = ` +Array [ + "foo", +] +`; + exports[`index should set arguments into socket function 1`] = ` Array [ "mock-url", diff --git a/test/client/clients/SockJSClient.test.js b/test/client/clients/SockJSClient.test.js index d6c1ec4745..02009a4512 100644 --- a/test/client/clients/SockJSClient.test.js +++ b/test/client/clients/SockJSClient.test.js @@ -4,6 +4,7 @@ const http = require('http'); const express = require('express'); const sockjs = require('sockjs'); const SockJSClient = require('../../../client-src/clients/SockJSClient'); +const timer = require('../../helpers/timer'); const port = require('../../ports-map').sockJSClient; describe('SockJSClient', () => { @@ -32,13 +33,12 @@ describe('SockJSClient', () => { }); describe('client', () => { - it('should open, receive message, and close', (done) => { - socketServer.on('connection', (connection) => { + it('should open, receive message, and close', async () => { + socketServer.on('connection', async (connection) => { connection.write('hello world'); - setTimeout(() => { - connection.close(); - }, 1000); + await timer(1000); + connection.close(); }); const client = new SockJSClient(`http://localhost:${port}/sockjs-node`); @@ -54,16 +54,13 @@ describe('SockJSClient', () => { data.push(msg); }); - setTimeout(() => { - expect(data).toMatchSnapshot(); - done(); - }, 3000); + await timer(3000); + + expect(data).toMatchSnapshot(); }); }); afterAll((done) => { - listeningApp.close(() => { - done(); - }); + listeningApp.close(done); }); }); diff --git a/test/client/index.test.js b/test/client/index.test.js index 72eb8a63cd..df39415863 100644 --- a/test/client/index.test.js +++ b/test/client/index.test.js @@ -3,7 +3,6 @@ /* eslint-disable no-undefined */ -/* global self */ describe('index', () => { let log; @@ -12,6 +11,7 @@ describe('index', () => { let reloadApp; let sendMessage; let onSocketMessage; + let updatePublicPath; const locationValue = self.location; const resourceQueryValue = global.__resourceQuery; @@ -54,6 +54,13 @@ describe('index', () => { () => 'mock-url' ); + // updatePublicPath + jest.setMock( + '../../client-src/default/utils/updatePublicPath.js', + jest.fn() + ); + updatePublicPath = require('../../client-src/default/utils/updatePublicPath'); + require('../../client-src/default'); onSocketMessage = socket.mock.calls[0][1]; }); @@ -214,4 +221,9 @@ describe('index', () => { expect(log.log.error.mock.calls[0][0]).toMatchSnapshot(); expect(sendMessage.mock.calls[0][0]).toMatchSnapshot(); }); + + test('should run updatePublicPath', () => { + expect(updatePublicPath).toBeCalled(); + expect(updatePublicPath.mock.calls[0]).toMatchSnapshot(); + }); }); diff --git a/test/client/socket-helper.test.js b/test/client/socket-helper.test.js index a40007aa49..b8074cebd5 100644 --- a/test/client/socket-helper.test.js +++ b/test/client/socket-helper.test.js @@ -8,7 +8,7 @@ describe('socket', () => { it('should default to SockJSClient when no __webpack_dev_server_client__ set', () => { jest.mock('../../client/clients/SockJSClient'); - const socket = require('../../client/socket'); + const { default: socket } = require('../../client/socket'); const SockJSClient = require('../../client/clients/SockJSClient'); const mockHandler = jest.fn(); @@ -36,7 +36,7 @@ describe('socket', () => { it('should use __webpack_dev_server_client__ when set', () => { jest.mock('../../client/clients/SockJSClient'); - const socket = require('../../client/socket'); + const { default: socket } = require('../../client/socket'); global.__webpack_dev_server_client__ = require('../../client/clients/SockJSClient'); const mockHandler = jest.fn(); diff --git a/test/client/utils/__snapshots__/createSocketUrl.test.js.snap b/test/client/utils/__snapshots__/createSocketUrl.test.js.snap index cc455bf5ca..22953f1570 100644 --- a/test/client/utils/__snapshots__/createSocketUrl.test.js.snap +++ b/test/client/utils/__snapshots__/createSocketUrl.test.js.snap @@ -1,33 +1,61 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`createSocketUrl should return the url when __resourceQuery is ?test 1`] = `"/sockjs-node"`; - exports[`createSocketUrl should return the url when __resourceQuery is http://0.0.0.0 1`] = `"http://localhost/sockjs-node"`; -exports[`createSocketUrl should return the url when __resourceQuery is http://user:pass@[::]:8080 1`] = `"ttp:user:pass@localhost:8080/sockjs-node"`; +exports[`createSocketUrl should return the url when __resourceQuery is http://0.0.0.0:9000?publicPath=%2Fdist%2F 1`] = `"http://localhost:9000/sockjs-node"`; + +exports[`createSocketUrl should return the url when __resourceQuery is http://0.0.0.0:9000?sockHost=my.host 1`] = `"http://my.host:9000/sockjs-node"`; + +exports[`createSocketUrl should return the url when __resourceQuery is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node 1`] = `"http://localhost:9000/path/to/sockjs-node"`; + +exports[`createSocketUrl should return the url when __resourceQuery is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = `"http://my.host:8888/path/to/sockjs-node"`; + +exports[`createSocketUrl should return the url when __resourceQuery is http://0.0.0.0:9000?sockPort=8888 1`] = `"http://localhost:8888/sockjs-node"`; + +exports[`createSocketUrl should return the url when __resourceQuery is http://user:pass@[::]:8080 1`] = `"http://user:pass@localhost:8080/sockjs-node"`; + +exports[`createSocketUrl should return the url when __resourceQuery is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = `"http://user:pass@my.host:8888/path/to/sockjs-node"`; + +exports[`createSocketUrl should return the url when __resourceQuery is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockPort=8888 1`] = `"http://user:pass@localhost:8888/path/to/sockjs-node"`; -exports[`createSocketUrl should return the url when __resourceQuery is http://user:password@localhost/ 1`] = `"ttp:user:password@localhost/sockjs-node"`; +exports[`createSocketUrl should return the url when __resourceQuery is http://user:password@localhost/ 1`] = `"http://user:password@localhost/sockjs-node"`; -exports[`createSocketUrl should return the url when __resourceQuery is https://example.com 1`] = `"ttps:example.com/sockjs-node"`; +exports[`createSocketUrl should return the url when __resourceQuery is https://example.com 1`] = `"https://example.com/sockjs-node"`; -exports[`createSocketUrl should return the url when __resourceQuery is https://example.com/path 1`] = `"ttps:example.com/sockjs-node"`; +exports[`createSocketUrl should return the url when __resourceQuery is https://example.com/path 1`] = `"https://example.com/sockjs-node"`; -exports[`createSocketUrl should return the url when __resourceQuery is https://example.com/path/foo.js 1`] = `"ttps:example.com/sockjs-node"`; +exports[`createSocketUrl should return the url when __resourceQuery is https://example.com/path/foo.js 1`] = `"https://example.com/sockjs-node"`; -exports[`createSocketUrl should return the url when __resourceQuery is https://localhost:123 1`] = `"ttps:localhost:123/sockjs-node"`; +exports[`createSocketUrl should return the url when __resourceQuery is https://localhost:123 1`] = `"https://localhost:123/sockjs-node"`; -exports[`createSocketUrl should return the url when the current script source is ?test 1`] = `"/sockjs-node"`; +exports[`createSocketUrl should return the url when __resourceQuery is test 1`] = `"/sockjs-node"`; -exports[`createSocketUrl should return the url when the current script source is http://0.0.0.0 1`] = `"http:/sockjs-node"`; +exports[`createSocketUrl should return the url when the current script source is http://0.0.0.0 1`] = `"http://localhost/sockjs-node"`; -exports[`createSocketUrl should return the url when the current script source is http://user:pass@[::]:8080 1`] = `"http:/sockjs-node"`; +exports[`createSocketUrl should return the url when the current script source is http://0.0.0.0:9000?publicPath=%2Fdist%2F 1`] = `"http://localhost:9000/sockjs-node"`; + +exports[`createSocketUrl should return the url when the current script source is http://0.0.0.0:9000?sockHost=my.host 1`] = `"http://my.host:9000/sockjs-node"`; + +exports[`createSocketUrl should return the url when the current script source is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node 1`] = `"http://localhost:9000/path/to/sockjs-node"`; + +exports[`createSocketUrl should return the url when the current script source is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = `"http://my.host:8888/path/to/sockjs-node"`; + +exports[`createSocketUrl should return the url when the current script source is http://0.0.0.0:9000?sockPort=8888 1`] = `"http://localhost:8888/sockjs-node"`; + +exports[`createSocketUrl should return the url when the current script source is http://user:pass@[::]:8080 1`] = `"http://user:pass@localhost:8080/sockjs-node"`; + +exports[`createSocketUrl should return the url when the current script source is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = `"http://user:pass@my.host:8888/path/to/sockjs-node"`; + +exports[`createSocketUrl should return the url when the current script source is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockPort=8888 1`] = `"http://user:pass@localhost:8888/path/to/sockjs-node"`; exports[`createSocketUrl should return the url when the current script source is http://user:password@localhost/ 1`] = `"http://user:password@localhost/sockjs-node"`; -exports[`createSocketUrl should return the url when the current script source is https://example.com 1`] = `"https:/sockjs-node"`; +exports[`createSocketUrl should return the url when the current script source is https://example.com 1`] = `"https://example.com/sockjs-node"`; exports[`createSocketUrl should return the url when the current script source is https://example.com/path 1`] = `"https://example.com/sockjs-node"`; exports[`createSocketUrl should return the url when the current script source is https://example.com/path/foo.js 1`] = `"https://example.com/sockjs-node"`; -exports[`createSocketUrl should return the url when the current script source is https://localhost:123 1`] = `"https:/sockjs-node"`; +exports[`createSocketUrl should return the url when the current script source is https://localhost:123 1`] = `"https://localhost:123/sockjs-node"`; + +exports[`createSocketUrl should return the url when the current script source is test 1`] = `"/sockjs-node"`; diff --git a/test/client/utils/__snapshots__/getUrlParts.test.js.snap b/test/client/utils/__snapshots__/getUrlParts.test.js.snap new file mode 100644 index 0000000000..f6c569f191 --- /dev/null +++ b/test/client/utils/__snapshots__/getUrlParts.test.js.snap @@ -0,0 +1,469 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getUrlParts should return url parts when __resourceQuery is http://0.0.0.0 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://0.0.0.0:9000?publicPath=%2Fdist%2F 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/dist/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://0.0.0.0:9000?publicPath=%2Flong%2Fdist%2Fpath%2F 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/long/dist/path/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://0.0.0.0:9000?publicPath=http%3A%2F%2Fmy.host%3A8888%2Fdist%2F 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "http://my.host:8888/dist/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://0.0.0.0:9000?sockHost=my.host 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/", + "sockHost": "my.host", + "sockPath": "/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/path/to/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/", + "sockHost": "my.host", + "sockPath": "/path/to/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888&publicPath=http%3A%2F%2Fmy.host%3A8888%2Fdist%2F 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "http://my.host:8888/dist/", + "sockHost": "my.host", + "sockPath": "/path/to/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://0.0.0.0:9000?sockPort=8888 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://user:pass@[::]:8080 1`] = ` +Object { + "auth": "user:pass", + "defaultHost": "localhost", + "defaultPort": "8080", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "8080", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = ` +Object { + "auth": "user:pass", + "defaultHost": "localhost", + "defaultPort": "8080", + "protocol": "http:", + "publicPath": "/", + "sockHost": "my.host", + "sockPath": "/path/to/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockPort=8888 1`] = ` +Object { + "auth": "user:pass", + "defaultHost": "localhost", + "defaultPort": "8080", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/path/to/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is http://user:password@localhost/ 1`] = ` +Object { + "auth": "user:password", + "defaultHost": "localhost", + "defaultPort": "", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is https://example.com 1`] = ` +Object { + "auth": null, + "defaultHost": "example.com", + "defaultPort": "", + "protocol": "https:", + "publicPath": "/", + "sockHost": "example.com", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is https://example.com/path 1`] = ` +Object { + "auth": null, + "defaultHost": "example.com", + "defaultPort": "", + "protocol": "https:", + "publicPath": "/", + "sockHost": "example.com", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is https://example.com/path/foo.js 1`] = ` +Object { + "auth": null, + "defaultHost": "example.com", + "defaultPort": "", + "protocol": "https:", + "publicPath": "/", + "sockHost": "example.com", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is https://localhost:123 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "123", + "protocol": "https:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "123", +} +`; + +exports[`getUrlParts should return url parts when __resourceQuery is test 1`] = ` +Object { + "auth": null, + "defaultHost": null, + "defaultPort": "", + "protocol": null, + "publicPath": "/", + "sockHost": null, + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://0.0.0.0 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://0.0.0.0:9000?publicPath=%2Fdist%2F 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/dist/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://0.0.0.0:9000?publicPath=%2Flong%2Fdist%2Fpath%2F 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/long/dist/path/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://0.0.0.0:9000?publicPath=http%3A%2F%2Fmy.host%3A8888%2Fdist%2F 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "http://my.host:8888/dist/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://0.0.0.0:9000?sockHost=my.host 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/", + "sockHost": "my.host", + "sockPath": "/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/path/to/sockjs-node", + "sockPort": "9000", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/", + "sockHost": "my.host", + "sockPath": "/path/to/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888&publicPath=http%3A%2F%2Fmy.host%3A8888%2Fdist%2F 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "http://my.host:8888/dist/", + "sockHost": "my.host", + "sockPath": "/path/to/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://0.0.0.0:9000?sockPort=8888 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "9000", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://user:pass@[::]:8080 1`] = ` +Object { + "auth": "user:pass", + "defaultHost": "localhost", + "defaultPort": "8080", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "8080", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = ` +Object { + "auth": "user:pass", + "defaultHost": "localhost", + "defaultPort": "8080", + "protocol": "http:", + "publicPath": "/", + "sockHost": "my.host", + "sockPath": "/path/to/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockPort=8888 1`] = ` +Object { + "auth": "user:pass", + "defaultHost": "localhost", + "defaultPort": "8080", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/path/to/sockjs-node", + "sockPort": "8888", +} +`; + +exports[`getUrlParts should return url parts when the current script source is http://user:password@localhost/ 1`] = ` +Object { + "auth": "user:password", + "defaultHost": "localhost", + "defaultPort": "", + "protocol": "http:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when the current script source is https://example.com 1`] = ` +Object { + "auth": null, + "defaultHost": "example.com", + "defaultPort": "", + "protocol": "https:", + "publicPath": "/", + "sockHost": "example.com", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when the current script source is https://example.com/path 1`] = ` +Object { + "auth": null, + "defaultHost": "example.com", + "defaultPort": "", + "protocol": "https:", + "publicPath": "/", + "sockHost": "example.com", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when the current script source is https://example.com/path/foo.js 1`] = ` +Object { + "auth": null, + "defaultHost": "example.com", + "defaultPort": "", + "protocol": "https:", + "publicPath": "/", + "sockHost": "example.com", + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; + +exports[`getUrlParts should return url parts when the current script source is https://localhost:123 1`] = ` +Object { + "auth": null, + "defaultHost": "localhost", + "defaultPort": "123", + "protocol": "https:", + "publicPath": "/", + "sockHost": "localhost", + "sockPath": "/sockjs-node", + "sockPort": "123", +} +`; + +exports[`getUrlParts should return url parts when the current script source is test 1`] = ` +Object { + "auth": null, + "defaultHost": null, + "defaultPort": "", + "protocol": null, + "publicPath": "/", + "sockHost": null, + "sockPath": "/sockjs-node", + "sockPort": "", +} +`; diff --git a/test/client/utils/__snapshots__/log.test.js.snap b/test/client/utils/__snapshots__/log.test.js.snap index a62894e923..8f120617a2 100644 --- a/test/client/utils/__snapshots__/log.test.js.snap +++ b/test/client/utils/__snapshots__/log.test.js.snap @@ -19,8 +19,5 @@ Array [ Array [ "trace", ], - Array [ - "warn", - ], ] `; diff --git a/test/client/utils/__snapshots__/updatePublicPath.test.js.snap b/test/client/utils/__snapshots__/updatePublicPath.test.js.snap new file mode 100644 index 0000000000..c9287b2761 --- /dev/null +++ b/test/client/utils/__snapshots__/updatePublicPath.test.js.snap @@ -0,0 +1,73 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`updatePublicPath should set public path when __resourceQuery is http://0.0.0.0 1`] = `"http://localhost/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://0.0.0.0:9000?publicPath=%2Fdist%2F 1`] = `"http://localhost:9000/dist/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://0.0.0.0:9000?publicPath=%2Flong%2Fdist%2Fpath%2F 1`] = `"http://localhost:9000/long/dist/path/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://0.0.0.0:9000?publicPath=http%3A%2F%2Fmy.host%3A8888%2Fdist%2F 1`] = `"http://my.host:8888/dist/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://0.0.0.0:9000?sockHost=my.host 1`] = `"http://localhost:9000/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node 1`] = `"http://localhost:9000/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = `"http://localhost:9000/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888&publicPath=http%3A%2F%2Fmy.host%3A8888%2Fdist%2F 1`] = `"http://my.host:8888/dist/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://0.0.0.0:9000?sockPort=8888 1`] = `"http://localhost:9000/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://user:pass@[::]:8080 1`] = `"http://user:pass@localhost:8080/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = `"http://user:pass@localhost:8080/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockPort=8888 1`] = `"http://user:pass@localhost:8080/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is http://user:password@localhost/ 1`] = `"http://user:password@localhost/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is https://example.com 1`] = `"https://example.com/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is https://example.com/path 1`] = `"https://example.com/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is https://example.com/path/foo.js 1`] = `"https://example.com/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is https://localhost:123 1`] = `"https://localhost:123/"`; + +exports[`updatePublicPath should set public path when __resourceQuery is test 1`] = `"/"`; + +exports[`updatePublicPath should set public path when the current script source is http://0.0.0.0 1`] = `"http://localhost/"`; + +exports[`updatePublicPath should set public path when the current script source is http://0.0.0.0:9000?publicPath=%2Fdist%2F 1`] = `"http://localhost:9000/dist/"`; + +exports[`updatePublicPath should set public path when the current script source is http://0.0.0.0:9000?publicPath=%2Flong%2Fdist%2Fpath%2F 1`] = `"http://localhost:9000/long/dist/path/"`; + +exports[`updatePublicPath should set public path when the current script source is http://0.0.0.0:9000?publicPath=http%3A%2F%2Fmy.host%3A8888%2Fdist%2F 1`] = `"http://my.host:8888/dist/"`; + +exports[`updatePublicPath should set public path when the current script source is http://0.0.0.0:9000?sockHost=my.host 1`] = `"http://localhost:9000/"`; + +exports[`updatePublicPath should set public path when the current script source is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node 1`] = `"http://localhost:9000/"`; + +exports[`updatePublicPath should set public path when the current script source is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = `"http://localhost:9000/"`; + +exports[`updatePublicPath should set public path when the current script source is http://0.0.0.0:9000?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888&publicPath=http%3A%2F%2Fmy.host%3A8888%2Fdist%2F 1`] = `"http://my.host:8888/dist/"`; + +exports[`updatePublicPath should set public path when the current script source is http://0.0.0.0:9000?sockPort=8888 1`] = `"http://localhost:9000/"`; + +exports[`updatePublicPath should set public path when the current script source is http://user:pass@[::]:8080 1`] = `"http://user:pass@localhost:8080/"`; + +exports[`updatePublicPath should set public path when the current script source is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockHost=my.host&sockPort=8888 1`] = `"http://user:pass@localhost:8080/"`; + +exports[`updatePublicPath should set public path when the current script source is http://user:pass@[::]:8080?sockPath=%2Fpath%2Fto%2Fsockjs-node&sockPort=8888 1`] = `"http://user:pass@localhost:8080/"`; + +exports[`updatePublicPath should set public path when the current script source is http://user:password@localhost/ 1`] = `"http://user:password@localhost/"`; + +exports[`updatePublicPath should set public path when the current script source is https://example.com 1`] = `"https://example.com/"`; + +exports[`updatePublicPath should set public path when the current script source is https://example.com/path 1`] = `"https://example.com/"`; + +exports[`updatePublicPath should set public path when the current script source is https://example.com/path/foo.js 1`] = `"https://example.com/"`; + +exports[`updatePublicPath should set public path when the current script source is https://localhost:123 1`] = `"https://localhost:123/"`; + +exports[`updatePublicPath should set public path when the current script source is test 1`] = `"/"`; diff --git a/test/client/utils/createSocketUrl.test.js b/test/client/utils/createSocketUrl.test.js index 09a4cd6adc..9fe77f99b2 100644 --- a/test/client/utils/createSocketUrl.test.js +++ b/test/client/utils/createSocketUrl.test.js @@ -2,7 +2,7 @@ describe('createSocketUrl', () => { const samples = [ - '?test', + 'test', 'https://example.com', 'https://example.com/path', 'https://example.com/path/foo.js', @@ -10,6 +10,21 @@ describe('createSocketUrl', () => { 'http://0.0.0.0', 'https://localhost:123', 'http://user:pass@[::]:8080', + `http://0.0.0.0:9000?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}`, + `http://0.0.0.0:9000?sockHost=${encodeURIComponent('my.host')}`, + 'http://0.0.0.0:9000?sockPort=8888', + `http://0.0.0.0:9000?publicPath=${encodeURIComponent('/dist/')}`, + `http://0.0.0.0:9000?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockHost=${encodeURIComponent('my.host')}&sockPort=8888`, + `http://user:pass@[::]:8080?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockPort=8888`, + `http://user:pass@[::]:8080?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockHost=${encodeURIComponent('my.host')}&sockPort=8888`, // TODO: comment out after the major release // https://github.com/webpack/webpack-dev-server/pull/1954#issuecomment-498043376 // 'file://filename', @@ -21,17 +36,22 @@ describe('createSocketUrl', () => { () => () => url ); - const createSocketUrl = require('../../../client-src/default/utils/createSocketUrl'); + const { + default: createSocketUrl, + // eslint-disable-next-line global-require + } = require('../../../client-src/default/utils/createSocketUrl'); test(`should return the url when __resourceQuery is ${url}`, () => { - expect(createSocketUrl(url)).toMatchSnapshot(); + // include ? at the start of the __resourceQuery to make it like a real + // resource query + expect(createSocketUrl(`?${url}`)).toMatchSnapshot(); }); test(`should return the url when the current script source is ${url}`, () => { expect(createSocketUrl()).toMatchSnapshot(); }); - // put here because resetModules mustn't be reset when L20 is finished + // put here because resetModules mustn't be reset when L27 is finished jest.resetModules(); }); }); diff --git a/test/client/utils/getCurrentScriptSource.test.js b/test/client/utils/getCurrentScriptSource.test.js index 8fb953b1f3..b6c4f4d017 100644 --- a/test/client/utils/getCurrentScriptSource.test.js +++ b/test/client/utils/getCurrentScriptSource.test.js @@ -1,6 +1,8 @@ 'use strict'; -const getCurrentScriptSource = require('../../../client-src/default/utils/getCurrentScriptSource'); +const { + default: getCurrentScriptSource, +} = require('../../../client-src/default/utils/getCurrentScriptSource'); describe('getCurrentScriptSource', () => { afterEach(() => { diff --git a/test/client/utils/getUrlParts.test.js b/test/client/utils/getUrlParts.test.js new file mode 100644 index 0000000000..e4fba55831 --- /dev/null +++ b/test/client/utils/getUrlParts.test.js @@ -0,0 +1,62 @@ +'use strict'; + +describe('getUrlParts', () => { + const samples = [ + 'test', + 'https://example.com', + 'https://example.com/path', + 'https://example.com/path/foo.js', + 'http://user:password@localhost/', + 'http://0.0.0.0', + 'https://localhost:123', + 'http://user:pass@[::]:8080', + `http://0.0.0.0:9000?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}`, + `http://0.0.0.0:9000?sockHost=${encodeURIComponent('my.host')}`, + 'http://0.0.0.0:9000?sockPort=8888', + `http://0.0.0.0:9000?publicPath=${encodeURIComponent('/dist/')}`, + `http://0.0.0.0:9000?publicPath=${encodeURIComponent('/long/dist/path/')}`, + `http://0.0.0.0:9000?publicPath=${encodeURIComponent( + 'http://my.host:8888/dist/' + )}`, + `http://0.0.0.0:9000?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockHost=${encodeURIComponent('my.host')}&sockPort=8888`, + `http://0.0.0.0:9000?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockHost=${encodeURIComponent( + 'my.host' + )}&sockPort=8888&publicPath=${encodeURIComponent( + 'http://my.host:8888/dist/' + )}`, + `http://user:pass@[::]:8080?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockPort=8888`, + `http://user:pass@[::]:8080?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockHost=${encodeURIComponent('my.host')}&sockPort=8888`, + ]; + + samples.forEach((url) => { + jest.doMock( + '../../../client-src/default/utils/getCurrentScriptSource.js', + () => () => url + ); + + const { + default: getUrlParts, + // eslint-disable-next-line global-require + } = require('../../../client-src/default/utils/getUrlParts'); + + test(`should return url parts when __resourceQuery is ${url}`, () => { + expect(getUrlParts(`?${url}`)).toMatchSnapshot(); + }); + + test(`should return url parts when the current script source is ${url}`, () => { + expect(getUrlParts()).toMatchSnapshot(); + }); + + jest.resetModules(); + }); +}); diff --git a/test/client/utils/log.test.js b/test/client/utils/log.test.js index f1f857ae70..3165a5353d 100644 --- a/test/client/utils/log.test.js +++ b/test/client/utils/log.test.js @@ -33,7 +33,7 @@ describe('log', () => { }); test('should set log level via setLogLevel', () => { - ['info', 'warn', 'error', 'debug', 'trace', 'warning'].forEach((level) => { + ['info', 'warn', 'error', 'debug', 'trace'].forEach((level) => { setLogLevel(level); }); @@ -42,14 +42,12 @@ describe('log', () => { ).toMatchSnapshot(); }); - test('should set none and silent via setLogLevel', () => { - ['none', 'silent'].forEach((level) => { - setLogLevel(level); - }); + test('should set silent via setLogLevel', () => { + setLogLevel('silent'); expect( logMock.getLogger.mock.results[0].value.disableAll.mock.results - ).toHaveLength(2); + ).toHaveLength(1); }); test('should output exception log when the level is unknown', () => { diff --git a/test/client/utils/reloadApp.test.js b/test/client/utils/reloadApp.test.js index f2b39859db..84b346769b 100644 --- a/test/client/utils/reloadApp.test.js +++ b/test/client/utils/reloadApp.test.js @@ -1,7 +1,5 @@ 'use strict'; -/* global self */ - describe('reloadApp', () => { let reloadApp; let log; @@ -23,7 +21,8 @@ describe('reloadApp', () => { }; }); - reloadApp = require('../../../client-src/default/utils/reloadApp'); + // eslint-disable-next-line global-require + reloadApp = require('../../../client-src/default/utils/reloadApp').default; }); afterEach(() => { diff --git a/test/client/utils/sendMessage.test.js b/test/client/utils/sendMessage.test.js index a60f2548bc..e13b7b49c8 100644 --- a/test/client/utils/sendMessage.test.js +++ b/test/client/utils/sendMessage.test.js @@ -1,8 +1,8 @@ 'use strict'; -/* global self */ - -const sendMessage = require('../../../client-src/default/utils/sendMessage'); +const { + default: sendMessage, +} = require('../../../client-src/default/utils/sendMessage'); describe('sendMessage', () => { afterEach(() => { diff --git a/test/client/utils/updatePublicPath.test.js b/test/client/utils/updatePublicPath.test.js new file mode 100644 index 0000000000..f2d630f614 --- /dev/null +++ b/test/client/utils/updatePublicPath.test.js @@ -0,0 +1,70 @@ +'use strict'; + +/* eslint-disable camelcase, no-undef */ + +describe('updatePublicPath', () => { + const samples = [ + 'test', + 'https://example.com', + 'https://example.com/path', + 'https://example.com/path/foo.js', + 'http://user:password@localhost/', + 'http://0.0.0.0', + 'https://localhost:123', + 'http://user:pass@[::]:8080', + `http://0.0.0.0:9000?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}`, + `http://0.0.0.0:9000?sockHost=${encodeURIComponent('my.host')}`, + 'http://0.0.0.0:9000?sockPort=8888', + `http://0.0.0.0:9000?publicPath=${encodeURIComponent('/dist/')}`, + `http://0.0.0.0:9000?publicPath=${encodeURIComponent('/long/dist/path/')}`, + `http://0.0.0.0:9000?publicPath=${encodeURIComponent( + 'http://my.host:8888/dist/' + )}`, + `http://0.0.0.0:9000?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockHost=${encodeURIComponent('my.host')}&sockPort=8888`, + `http://0.0.0.0:9000?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockHost=${encodeURIComponent( + 'my.host' + )}&sockPort=8888&publicPath=${encodeURIComponent( + 'http://my.host:8888/dist/' + )}`, + `http://user:pass@[::]:8080?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockPort=8888`, + `http://user:pass@[::]:8080?sockPath=${encodeURIComponent( + '/path/to/sockjs-node' + )}&sockHost=${encodeURIComponent('my.host')}&sockPort=8888`, + ]; + + global.__webpack_public_path__ = ''; + + samples.forEach((url) => { + jest.doMock( + '../../../client-src/default/utils/getCurrentScriptSource.js', + () => () => url + ); + + const { + default: updatePublicPath, + // eslint-disable-next-line global-require + } = require('../../../client-src/default/utils/updatePublicPath'); + + test(`should set public path when __resourceQuery is ${url}`, () => { + __webpack_public_path__ = ''; + updatePublicPath(`?${url}`); + expect(__webpack_public_path__).toMatchSnapshot(); + }); + + test(`should set public path when the current script source is ${url}`, () => { + __webpack_public_path__ = ''; + updatePublicPath(); + expect(__webpack_public_path__).toMatchSnapshot(); + }); + + jest.resetModules(); + }); +}); diff --git a/test/e2e/ClientOptions.test.js b/test/e2e/ClientOptions.test.js index c1cd2fffe9..6a84f6b259 100644 --- a/test/e2e/ClientOptions.test.js +++ b/test/e2e/ClientOptions.test.js @@ -1,28 +1,14 @@ 'use strict'; -const express = require('express'); -const httpProxy = require('http-proxy-middleware'); const request = require('supertest'); const testServer = require('../helpers/test-server'); const config = require('../fixtures/client-config/webpack.config'); const runBrowser = require('../helpers/run-browser'); +const startProxy = require('../helpers/start-proxy'); const [port1, port2, port3] = require('../ports-map').ClientOptions; const { beforeBrowserCloseDelay } = require('../helpers/puppeteer-constants'); describe('Client code', () => { - function startProxy(port, cb) { - const proxy = express(); - proxy.use( - '/', - httpProxy({ - target: `http://localhost:${port1}`, - ws: true, - changeOrigin: true, - }) - ); - return proxy.listen(port, cb); - } - beforeAll((done) => { const options = { compress: true, @@ -46,23 +32,22 @@ describe('Client code', () => { let proxy; beforeAll((done) => { - proxy = startProxy(port2, done); + proxy = startProxy(port2, port1, done); }); afterAll((done) => { - proxy.close(() => { - done(); - }); + proxy.close(done); }); - it('responds with a 200 on proxy port', (done) => { - const req = request(`http://localhost:${port2}`); - req.get('/sockjs-node').expect(200, 'Welcome to SockJS!\n', done); - }); - - it('responds with a 200 on non-proxy port', (done) => { - const req = request(`http://localhost:${port1}`); - req.get('/sockjs-node').expect(200, 'Welcome to SockJS!\n', done); + it('responds with a 200', async () => { + { + const req = request(`http://localhost:${port2}`); + await req.get('/sockjs-node').expect(200, 'Welcome to SockJS!\n'); + } + { + const req = request(`http://localhost:${port1}`); + await req.get('/sockjs-node').expect(200, 'Welcome to SockJS!\n'); + } }); it('requests websocket through the proxy with proper port number', (done) => { @@ -97,7 +82,9 @@ describe('Client complex inline script path', () => { poll: true, }, public: 'myhost.test', - sockPath: '/foo/test/bar/', + clientSocketOptions: { + path: '/foo/test/bar/', + }, quiet: true, }; testServer.startAwaitingCompilation(config, options, done); @@ -139,8 +126,10 @@ describe('Client complex inline script path with sockPort', () => { watchOptions: { poll: true, }, - sockPath: '/foo/test/bar/', - sockPort: port3, + clientSocketOptions: { + path: '/foo/test/bar/', + port: port3, + }, quiet: true, }; testServer.startAwaitingCompilation(config, options, done); @@ -186,7 +175,9 @@ describe('Client complex inline script path with sockPort, no sockPath', () => { watchOptions: { poll: true, }, - sockPort: port3, + clientSocketOptions: { + port: port3, + }, quiet: true, }; testServer.startAwaitingCompilation(config, options, done); @@ -226,7 +217,9 @@ describe('Client complex inline script path with sockHost', () => { watchOptions: { poll: true, }, - sockHost: 'myhost.test', + clientSocketOptions: { + host: 'myhost.test', + }, quiet: true, }; testServer.startAwaitingCompilation(config, options, done); @@ -296,47 +289,34 @@ describe('Client console.log', () => { }, ]; - cases.forEach(({ title, options }) => { - it(title, (done) => { + for (const { title, options } of cases) { + it(title, async () => { const res = []; const testOptions = Object.assign({}, baseOptions, options); - // TODO: use async/await when Node.js v6 support is dropped - Promise.resolve() - .then(() => { - return new Promise((resolve) => { - testServer.startAwaitingCompilation(config, testOptions, resolve); - }); - }) - .then(() => { - // make sure the previous Promise is not passing along strange arguments to runBrowser - return runBrowser(); - }) - .then(({ page, browser }) => { - return new Promise((resolve) => { - page.goto(`http://localhost:${port2}/main`); - page.on('console', ({ _text }) => { - res.push(_text); - }); - // wait for load before closing the browser - page.waitForNavigation({ waitUntil: 'load' }).then(() => { - page.waitFor(beforeBrowserCloseDelay).then(() => { - browser.close().then(() => { - resolve(); - }); - }); - }); - }); - }) - .then(() => { - return new Promise((resolve) => { - testServer.close(resolve); - }); - }) - .then(() => { - expect(res).toMatchSnapshot(); - done(); - }); + // TODO: refactor(hiroppy) + await new Promise((resolve) => { + testServer.startAwaitingCompilation(config, testOptions, resolve); + }); + + const { page, browser } = await runBrowser(); + + page.goto(`http://localhost:${port2}/main`); + page.on('console', ({ _text }) => { + res.push(_text); + }); + + // wait for load before closing the browser + await page.waitForNavigation({ waitUntil: 'load' }); + await page.waitFor(beforeBrowserCloseDelay); + await browser.close(); + + expect(res).toMatchSnapshot(); + + // TODO: refactor(hiroppy) + await new Promise((resolve) => { + testServer.close(resolve); + }); }); - }); + } }); diff --git a/test/e2e/PublicPath.test.js b/test/e2e/PublicPath.test.js new file mode 100644 index 0000000000..31e88d8732 --- /dev/null +++ b/test/e2e/PublicPath.test.js @@ -0,0 +1,109 @@ +'use strict'; + +/* eslint-disable + no-undef +*/ +const fs = require('fs'); +const { resolve } = require('path'); +const testServer = require('../helpers/test-server'); +const reloadConfig = require('../fixtures/reload-config/webpack.config'); +const runBrowser = require('../helpers/run-browser'); +const startProxy = require('../helpers/start-proxy'); +const [port1, port2] = require('../ports-map').PublicPath; + +const cssFilePath = resolve(__dirname, '../fixtures/reload-config/main.css'); + +describe('publicPath', () => { + describe('hot', () => { + let proxy; + beforeAll((done) => { + fs.writeFileSync( + cssFilePath, + 'body { background-color: rgb(0, 0, 255); }' + ); + const options = { + port: port1, + host: '0.0.0.0', + inline: true, + hot: true, + watchOptions: { + poll: 500, + }, + publicPath: '/long/dist/path/', + }; + testServer.startAwaitingCompilation(reloadConfig, options, () => { + proxy = startProxy(port2, port1, done); + }); + }); + + afterAll((done) => { + fs.unlinkSync(cssFilePath); + testServer.close(() => { + proxy.close(done); + }); + }); + + describe('on browser client', () => { + it('should hot reload and request correct hot-update.json path', (done) => { + runBrowser().then(({ page, browser }) => { + let refreshed = false; + let requestedHotUpdate = false; + const hotUpdateRegExp = new RegExp( + // eslint-disable-next-line no-useless-escape + `^http\:\/\/localhost\:${port1}\/long\/dist\/path\/.*\.hot\-update\.json$`, + 'g' + ); + page.waitForNavigation({ waitUntil: 'load' }).then(() => { + page + .evaluate(() => { + const body = document.body; + const bgColor = getComputedStyle(body)['background-color']; + return bgColor; + }) + .then((color) => { + page.setRequestInterception(true).then(() => { + page.on('request', (req) => { + if ( + req.isNavigationRequest() && + req.frame() === page.mainFrame() && + req.url() === `http://localhost:${port2}/main` + ) { + refreshed = true; + } else if (hotUpdateRegExp.test(req.url())) { + requestedHotUpdate = true; + } + req.continue(); + }); + fs.writeFileSync( + cssFilePath, + 'body { background-color: rgb(255, 0, 0); }' + ); + page.waitFor(10000).then(() => { + page + .evaluate(() => { + const body = document.body; + const bgColor = getComputedStyle(body)[ + 'background-color' + ]; + return bgColor; + }) + .then((color2) => { + browser.close().then(() => { + expect(requestedHotUpdate).toBeTruthy(); + expect(color).toEqual('rgb(0, 0, 255)'); + expect(color2).toEqual('rgb(255, 0, 0)'); + expect(refreshed).toBeFalsy(); + done(); + }); + }); + }); + }); + }); + }); + + page.goto(`http://localhost:${port1}/long/dist/path/main`); + }); + }); + }); + }); +}); diff --git a/test/helpers/fs.js b/test/helpers/fs.js new file mode 100644 index 0000000000..d715846748 --- /dev/null +++ b/test/helpers/fs.js @@ -0,0 +1,15 @@ +// we'll delete this file when Node8 is not supported. +// because we can use fs.promise + +'use strict'; + +const { promisify } = require('util'); +const { writeFile, unlink } = require('fs'); + +const unlinkAsync = promisify(unlink); +const writeAsync = promisify(writeFile); + +module.exports = { + unlinkAsync, + writeAsync, +}; diff --git a/test/helpers/run-browser.js b/test/helpers/run-browser.js index 74f63652c3..688b847db3 100644 --- a/test/helpers/run-browser.js +++ b/test/helpers/run-browser.js @@ -3,7 +3,7 @@ const puppeteer = require('puppeteer'); const { puppeteerArgs } = require('./puppeteer-constants'); -function runBrowser(config) { +async function runBrowser(config) { const options = { viewport: { width: 500, @@ -13,27 +13,16 @@ function runBrowser(config) { ...config, }; - return new Promise((resolve, reject) => { - let page; - let browser; - - puppeteer - .launch({ - headless: true, - // args come from: https://github.com/alixaxel/chrome-aws-lambda/blob/master/source/index.js - args: puppeteerArgs, - }) - .then((launchedBrowser) => { - browser = launchedBrowser; - return browser.newPage(); - }) - .then((newPage) => { - page = newPage; - page.emulate(options); - resolve({ page, browser }); - }) - .catch(reject); + const launchedBrowser = await puppeteer.launch({ + headless: true, + // args come from: https://github.com/alixaxel/chrome-aws-lambda/blob/master/source/index.js + args: puppeteerArgs, }); + const browser = launchedBrowser; + const page = await browser.newPage(); + page.emulate(options); + + return { page, browser }; } module.exports = runBrowser; diff --git a/test/helpers/start-proxy.js b/test/helpers/start-proxy.js new file mode 100644 index 0000000000..a62f686a25 --- /dev/null +++ b/test/helpers/start-proxy.js @@ -0,0 +1,19 @@ +'use strict'; + +const express = require('express'); +const httpProxy = require('http-proxy-middleware'); + +function startProxy(proxyPort, targetPort, cb) { + const proxy = express(); + proxy.use( + '/', + httpProxy({ + target: `http://localhost:${targetPort}`, + ws: true, + changeOrigin: true, + }) + ); + return proxy.listen(proxyPort, cb); +} + +module.exports = startProxy; diff --git a/test/helpers/test-bin.js b/test/helpers/test-bin.js index 25e3af020a..39f38f0b16 100644 --- a/test/helpers/test-bin.js +++ b/test/helpers/test-bin.js @@ -1,6 +1,7 @@ 'use strict'; const path = require('path'); +const { spawn } = require('child_process'); const execa = require('execa'); const webpackDevServerPath = path.resolve( @@ -12,9 +13,12 @@ const basicConfigPath = path.resolve( '../fixtures/cli/webpack.config.js' ); -function testBin(testArgs, configPath) { +function testBin(testArgs, configPath, useSpawn) { const cwd = process.cwd(); - const env = process.env.NODE_ENV; + const env = { + NODE_ENV: process.env.NODE_ENV, + PATH: process.env.PATH, + }; if (!configPath) { configPath = basicConfigPath; @@ -28,7 +32,16 @@ function testBin(testArgs, configPath) { const args = [webpackDevServerPath, '--config', configPath].concat(testArgs); - return execa('node', args, { cwd, env, timeout: 10000 }); + const opts = { cwd, env, timeout: 10000 }; + let execLib = execa; + // use Node's spawn as a workaround for execa issues + // https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options + if (useSpawn) { + execLib = spawn; + delete opts.timeout; + } + + return execLib('node', args, opts); } module.exports = testBin; diff --git a/test/helpers/test-server.js b/test/helpers/test-server.js index f9284c4fca..85de3777f7 100644 --- a/test/helpers/test-server.js +++ b/test/helpers/test-server.js @@ -40,9 +40,19 @@ function startFullSetup(config, options, done) { server = new Server(compiler, options); - const port = Object.prototype.hasOwnProperty.call(options, 'port') - ? options.port - : 8080; + // originally the fallback default was 8080, but it should be + // undefined so that the server.listen method can choose it for us, + // and thus prevent port mapping collision between tests + let port; + if (Object.prototype.hasOwnProperty.call(options, 'port')) { + port = options.port; + } else if (Object.prototype.hasOwnProperty.call(options, 'socket')) { + port = options.socket; + } else { + // TODO: remove this when findPort is implemented in the server.listen method + port = 8080; + } + const host = Object.prototype.hasOwnProperty.call(options, 'host') ? options.host : 'localhost'; @@ -65,10 +75,12 @@ function startFullSetup(config, options, done) { function startAwaitingCompilationFullSetup(config, options, done) { let readyCount = 0; - const ready = () => { + let err; + const ready = (e) => { + err = e instanceof Error || (typeof e === 'object' && e.code) ? e : err; readyCount += 1; if (readyCount === 2) { - done(); + done(err); } }; diff --git a/test/helpers/test-unix-socket.js b/test/helpers/test-unix-socket.js new file mode 100644 index 0000000000..d2c504b62f --- /dev/null +++ b/test/helpers/test-unix-socket.js @@ -0,0 +1,30 @@ +'use strict'; + +const http = require('http'); + +const TestUnixSocket = class TestUnixSocket { + constructor() { + this.server = http.createServer(); + this.sockets = new Set(); + this.server.on('connection', (socket) => { + this.sockets.add(socket); + socket.on('close', () => { + this.sockets.delete(socket); + }); + }); + } + + close(done) { + if (this.server.listening) { + // get rid of connected sockets + for (const socket of this.sockets.values()) { + socket.destroy(); + } + this.server.close(done); + } else { + done(); + } + } +}; + +module.exports = TestUnixSocket; diff --git a/test/helpers/timer.js b/test/helpers/timer.js new file mode 100644 index 0000000000..0608301ec8 --- /dev/null +++ b/test/helpers/timer.js @@ -0,0 +1,11 @@ +'use strict'; + +function timer(t) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, t); + }); +} + +module.exports = timer; diff --git a/test/integration/MultiCompiler.test.js b/test/integration/MultiCompiler.test.js index 5d9cc9c15b..d35fe9ba78 100644 --- a/test/integration/MultiCompiler.test.js +++ b/test/integration/MultiCompiler.test.js @@ -17,10 +17,10 @@ describe('multi compiler', () => { afterAll(testServer.close); // TODO: this is a very basic test, optimally it should test multiple configs etc. - it('should handle GET request to bundle', (done) => { - req + it('should handle GET request to bundle', async () => { + await req .get('/main.js') .expect('Content-Type', 'application/javascript; charset=UTF-8') - .expect(200, done); + .expect(200); }); }); diff --git a/test/integration/UniversalCompiler.test.js b/test/integration/UniversalCompiler.test.js index 81225c3ad1..3ae2c3bd7a 100644 --- a/test/integration/UniversalCompiler.test.js +++ b/test/integration/UniversalCompiler.test.js @@ -16,35 +16,33 @@ describe('universal compiler', () => { afterAll(testServer.close); - it('client bundle should have the inlined the client runtime', (done) => { - req + it('client bundle should have the inlined the client runtime', async () => { + const { res, err } = await req .get('/client.js') .expect('Content-Type', 'application/javascript; charset=UTF-8') - .expect(200) - .end((err, res) => { - if (err) { - return done(err); - } - expect(res.text).toContain('Hello from the client'); - expect(res.text).toContain('sockjs-client'); - done(); - }); + .expect(200); + + if (err) { + throw err; + } + + expect(res.text).toContain('Hello from the client'); + expect(res.text).toContain('sockjs-client'); }); - it('server bundle should NOT have the inlined the client runtime', (done) => { + 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 - req + const { res, err } = await req .get('/server.js') .expect('Content-Type', 'application/javascript; charset=UTF-8') - .expect(200) - .end((err, res) => { - if (err) { - return done(err); - } - expect(res.text).toContain('Hello from the server'); - expect(res.text).not.toContain('sockjs-client'); - done(); - }); + .expect(200); + + if (err) { + throw err; + } + + expect(res.text).toContain('Hello from the server'); + expect(res.text).not.toContain('sockjs-client'); }); }); diff --git a/test/options.test.js b/test/options.test.js index 409dc07bec..1c28806cf7 100644 --- a/test/options.test.js +++ b/test/options.test.js @@ -26,82 +26,14 @@ describe('options', () => { const properties = Object.keys(options.properties); const messages = Object.keys(options.errorMessage.properties); - expect(properties.length).toEqual(messages.length); + expect(properties).toEqual(messages); const res = properties.every((name) => messages.includes(name)); expect(res).toEqual(true); }); - describe('validation', () => { - let server; - - afterAll((done) => { - if (server) { - server.close(() => { - done(); - }); - } - }); - - function validateOption(propertyName, cases) { - const successCount = cases.success.length; - const testCases = []; - - for (const key of Object.keys(cases)) { - testCases.push(...cases[key]); - } - - let current = 0; - - return testCases.reduce((p, value) => { - let compiler = webpack(config); - - return p - .then(() => { - const opts = - Object.prototype.toString.call(value) === '[object Object]' && - Object.keys(value).length !== 0 - ? value - : { - [propertyName]: value, - }; - - server = new Server(compiler, opts); - }) - .then(() => { - if (current < successCount) { - expect(true).toBeTruthy(); - } else { - expect(false).toBeTruthy(); - } - }) - .catch((err) => { - if (current >= successCount) { - expect(err).toBeInstanceOf(ValidationError); - } else { - expect(false).toBeTruthy(); - } - }) - .then(() => { - return new Promise((resolve) => { - if (server) { - server.close(() => { - compiler = null; - server = null; - resolve(); - }); - } else { - resolve(); - } - }); - }) - .then(() => { - current += 1; - }); - }, Promise.resolve()); - } - + { const memfs = createFsFromVolume(new Volume()); // We need to patch memfs // https://github.com/webpack/webpack-dev-middleware#fs @@ -133,19 +65,8 @@ describe('options', () => { failure: [false], }, clientLogLevel: { - success: [ - 'silent', - 'info', - 'error', - 'warn', - 'trace', - 'debug', - // deprecated - 'none', - // deprecated - 'warning', - ], - failure: ['whoops!'], + success: ['silent', 'info', 'error', 'warn', 'trace', 'debug'], + failure: ['whoops!', 'none', 'warning'], }, compress: { success: [true], @@ -369,16 +290,8 @@ describe('options', () => { success: [''], failure: [false], }, - sockHost: { - success: [''], - failure: [false], - }, - sockPath: { - success: [''], - failure: [false], - }, - sockPort: { - success: ['', 0, null], + clientSocketOptions: { + success: [{}], failure: [false], }, staticOptions: { @@ -454,6 +367,10 @@ describe('options', () => { }, ], }, + stdin: { + success: [false], + failure: [''], + }, useLocalIp: { success: [false], failure: [''], @@ -476,10 +393,58 @@ describe('options', () => { }, }; - Object.keys(cases).forEach((key) => { - it(key, () => { - return validateOption(key, cases[key]); + for (const [key, values] of Object.entries(cases)) { + it(`should validate "${key}" option`, async () => { + const compiler = webpack(config); + const { success, failure } = values; + + for (const sample of success) { + let server; + + try { + server = new Server(compiler, createOptions(key, sample)); + expect(true).toBeTruthy(); + } catch (e) { + expect(false).toBeTruthy(); + } + + // eslint-disable-next-line no-await-in-loop + await closeServer(server); + } + + for (const sample of failure) { + let server; + + try { + server = new Server(compiler, createOptions(key, sample)); + expect(false).toBeTruthy(); + } catch (e) { + expect(e).toBeInstanceOf(ValidationError); + } + + // eslint-disable-next-line no-await-in-loop + await closeServer(server); + } }); + } + } + + function createOptions(key, value) { + return Object.prototype.toString.call(value) === '[object Object]' && + Object.keys(value).length !== 0 + ? value + : { + [key]: value, + }; + } + + async function closeServer(server) { + await new Promise((resolve) => { + if (server) { + server.close(resolve); + } else { + resolve(); + } }); - }); + } }); diff --git a/test/ports-map.js b/test/ports-map.js index ac73aaa142..13f3e1f6e6 100644 --- a/test/ports-map.js +++ b/test/ports-map.js @@ -43,14 +43,14 @@ const portsList = { 'progress-option': 1, 'profile-option': 1, Iframe: 1, + 'stdin-option': 1, + PublicPath: 2, }; let startPort = 8089; const ports = {}; -Object.keys(portsList).forEach((key) => { - const value = portsList[key]; - +Object.entries(portsList).forEach(([key, value]) => { ports[key] = value === 1 ? (startPort += 1) diff --git a/test/server/Server.test.js b/test/server/Server.test.js index d54712a087..7fa99b84ce 100644 --- a/test/server/Server.test.js +++ b/test/server/Server.test.js @@ -7,6 +7,7 @@ const { noop } = require('webpack-dev-middleware/lib/util'); const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map').Server; +const timer = require('../helpers/timer'); jest.mock('sockjs/lib/transport'); @@ -178,4 +179,28 @@ describe('Server', () => { }); }); }); + + describe('server.listen', () => { + it('should complete async callback before calling onListening', (done) => { + const callOrder = []; + const compiler = webpack(config); + const server = new Server(compiler, { + onListening: () => { + callOrder.push('onListening'); + }, + ...baseDevConfig, + }); + server.listen(port, '0.0.0.0', async () => { + await timer(1000); + callOrder.push('user callback'); + }); + + // we need a timeout because even if the server is done listening, + // the compiler might still be compiling + setTimeout(() => { + expect(callOrder).toMatchSnapshot(); + server.close(done); + }, 5000); + }); + }); }); diff --git a/test/server/__snapshots__/Server.test.js.snap b/test/server/__snapshots__/Server.test.js.snap index b39da65eb6..59ec7f1b8f 100644 --- a/test/server/__snapshots__/Server.test.js.snap +++ b/test/server/__snapshots__/Server.test.js.snap @@ -59,3 +59,10 @@ Array [ }, ] `; + +exports[`Server server.listen should complete async callback before calling onListening 1`] = ` +Array [ + "user callback", + "onListening", +] +`; diff --git a/test/server/after-option.test.js b/test/server/after-option.test.js index b9d57616d0..07e7fd5545 100644 --- a/test/server/after-option.test.js +++ b/test/server/after-option.test.js @@ -39,13 +39,12 @@ describe('after option', () => { afterAll(testServer.close); - it('should handle after route', () => { - return req + it('should handle after route', async () => { + const res = await req .get('/after/some/path') .expect('Content-Type', 'text/html; charset=utf-8') - .expect(200) - .then((response) => { - expect(response.text).toBe('after'); - }); + .expect(200); + + expect(res.text).toBe('after'); }); }); diff --git a/test/server/before-option.test.js b/test/server/before-option.test.js index 3da0ecfd54..4e831133c2 100644 --- a/test/server/before-option.test.js +++ b/test/server/before-option.test.js @@ -39,13 +39,12 @@ describe('before option', () => { afterAll(testServer.close); - it('should handle before route', () => { - return req + it('should handle before route', async () => { + const res = await req .get('/before/some/path') .expect('Content-Type', 'text/html; charset=utf-8') - .expect(200) - .then((response) => { - expect(response.text).toBe('before'); - }); + .expect(200); + + expect(res.text).toBe('before'); }); }); diff --git a/test/server/compress-option.test.js b/test/server/compress-option.test.js index 0a04d1466f..644d40356f 100644 --- a/test/server/compress-option.test.js +++ b/test/server/compress-option.test.js @@ -22,15 +22,15 @@ describe('compress option', () => { afterAll(testServer.close); - it('request to bundle file', (done) => { - req + it('request to bundle file', async () => { + await req .get('/main.js') .expect((res) => { if (res.header['content-encoding']) { throw new Error('Expected `content-encoding` header is undefined.'); } }) - .expect(200, done); + .expect(200); }); }); @@ -49,11 +49,11 @@ describe('compress option', () => { afterAll(testServer.close); - it('request to bundle file', (done) => { - req + it('request to bundle file', async () => { + await req .get('/main.js') .expect('Content-Encoding', 'gzip') - .expect(200, done); + .expect(200); }); }); @@ -72,15 +72,15 @@ describe('compress option', () => { afterAll(testServer.close); - it('request to bundle file', (done) => { - req + it('request to bundle file', async () => { + await req .get('/main.js') .expect((res) => { if (res.header['content-encoding']) { throw new Error('Expected `content-encoding` header is undefined.'); } }) - .expect(200, done); + .expect(200); }); }); }); diff --git a/test/server/contentBase-option.test.js b/test/server/contentBase-option.test.js index 13acc53165..acaa46da02 100644 --- a/test/server/contentBase-option.test.js +++ b/test/server/contentBase-option.test.js @@ -37,19 +37,17 @@ describe('contentBase option', () => { }); afterAll((done) => { - testServer.close(() => { - done(); - }); + testServer.close(done); fs.truncateSync(nestedFile); }); - it('Request to index', (done) => { - req.get('/').expect(200, /Heyo/, done); + it('Request to index', async () => { + await req.get('/').expect(200, /Heyo/); }); - it('Request to other file', (done) => { - req.get('/other.html').expect(200, /Other html/, done); + it('Request to other file', async () => { + await req.get('/other.html').expect(200, /Other html/); }); it('Watches folder recursively', (done) => { @@ -108,12 +106,12 @@ describe('contentBase option', () => { }); }); - it("shouldn't list the files inside the assets folder (404)", (done) => { - req.get('/assets/').expect(404, done); + it("shouldn't list the files inside the assets folder (404)", async () => { + await req.get('/assets/').expect(404); }); - it('should show Heyo. because bar has index.html inside it (200)', (done) => { - req.get('/bar/').expect(200, /Heyo/, done); + it('should show Heyo. because bar has index.html inside it (200)', async () => { + await req.get('/bar/').expect(200, /Heyo/); }); }); @@ -133,17 +131,15 @@ describe('contentBase option', () => { }); afterAll((done) => { - testServer.close(() => { - done(); - }); + testServer.close(done); }); - it('should list the files inside the assets folder (200)', (done) => { - req.get('/assets/').expect(200, done); + it('should list the files inside the assets folder (200)', async () => { + await req.get('/assets/').expect(200); }); - it('should show Heyo. because bar has index.html inside it (200)', (done) => { - req.get('/bar/').expect(200, /Heyo/, done); + it('should show Heyo. because bar has index.html inside it (200)', async () => { + await req.get('/bar/').expect(200, /Heyo/); }); }); @@ -162,17 +158,15 @@ describe('contentBase option', () => { }); afterAll((done) => { - testServer.close(() => { - done(); - }); + testServer.close(done); }); - it('should list the files inside the assets folder (200)', (done) => { - req.get('/assets/').expect(200, done); + it('should list the files inside the assets folder (200)', async () => { + await req.get('/assets/').expect(200); }); - it('should show Heyo. because bar has index.html inside it (200)', (done) => { - req.get('/bar/').expect(200, /Heyo/, done); + it('should show Heyo. because bar has index.html inside it (200)', async () => { + await req.get('/bar/').expect(200, /Heyo/); }); }); @@ -190,17 +184,15 @@ describe('contentBase option', () => { }); afterAll((done) => { - testServer.close(() => { - done(); - }); + testServer.close(done); }); - it('Request to first directory', (done) => { - req.get('/').expect(200, /Heyo/, done); + it('Request to first directory', async () => { + await req.get('/').expect(200, /Heyo/); }); - it('Request to second directory', (done) => { - req.get('/foo.html').expect(200, /Foo!/, done); + it('Request to second directory', async () => { + await req.get('/foo.html').expect(200, /Foo!/); }); }); @@ -218,16 +210,14 @@ describe('contentBase option', () => { }); afterAll((done) => { - testServer.close(() => { - done(); - }); + testServer.close(done); }); - it('Request to page', (done) => { - req + it('Request to page', async () => { + await req .get('/other.html') .expect('Location', '//localhost:9099999/other.html') - .expect(302, done); + .expect(302); }); }); @@ -245,25 +235,23 @@ describe('contentBase option', () => { }); afterAll((done) => { - testServer.close(() => { - done(); - }); + testServer.close(done); }); - it('Request to page', (done) => { - req + it('Request to page', async () => { + await req .get('/foo.html') // TODO: hmm, two slashes seems to be a bug? .expect('Location', 'http://example.com//foo.html') - .expect(302, done); + .expect(302); }); - it('Request to page with search params', (done) => { - req + it('Request to page with search params', async () => { + await req .get('/foo.html?space=ship') // TODO: hmm, two slashes seems to be a bug? .expect('Location', 'http://example.com//foo.html?space=ship') - .expect(302, done); + .expect(302); }); }); @@ -357,18 +345,16 @@ describe('contentBase option', () => { beforeAll((done) => { jest.spyOn(process, 'cwd').mockImplementation(() => contentBasePublic); - server = testServer.start(config, {}, done); + server = testServer.start(config, { port }, done); req = request(server.app); }); afterAll((done) => { - testServer.close(() => { - done(); - }); + testServer.close(done); }); - it('Request to page', (done) => { - req.get('/other.html').expect(200, done); + it('Request to page', async () => { + await req.get('/other.html').expect(200); }); }); @@ -390,13 +376,11 @@ describe('contentBase option', () => { }); afterAll((done) => { - testServer.close(() => { - done(); - }); + testServer.close(done); }); - it('Request to page', (done) => { - req.get('/other.html').expect(404, done); + it('Request to page', async () => { + await req.get('/other.html').expect(404); }); }); @@ -414,13 +398,11 @@ describe('contentBase option', () => { }); afterAll((done) => { - testServer.close(() => { - done(); - }); + testServer.close(done); }); - it('Request foo.wasm', (done) => { - req.get('/foo.wasm').expect('Content-Type', 'application/wasm', done); + it('Request foo.wasm', async () => { + await req.get('/foo.wasm').expect('Content-Type', 'application/wasm'); }); }); }); diff --git a/test/server/headers-option.test.js b/test/server/headers-option.test.js index 1f3f7ad6fa..1891fe01a2 100644 --- a/test/server/headers-option.test.js +++ b/test/server/headers-option.test.js @@ -24,11 +24,11 @@ describe('headers option', () => { afterAll(testServer.close); - it('GET request with headers', (done) => { - req + it('GET request with headers', async () => { + await req .get('/main') .expect('X-Foo', '1') - .expect(200, done); + .expect(200); }); }); @@ -47,17 +47,18 @@ describe('headers option', () => { afterAll(testServer.close); - it('GET request with headers as an array', (done) => { + it('GET request with headers as an array', async () => { // https://github.com/webpack/webpack-dev-server/pull/1650#discussion_r254217027 const expected = ['v7', 'v8', 'v9'].includes( process.version.split('.')[0] ) ? 'key1=value1,key2=value2' : 'key1=value1, key2=value2'; - req + + await req .get('/main') .expect('X-Bar', expected) - .expect(200, done); + .expect(200); }); }); }); diff --git a/test/server/historyApiFallback-option.test.js b/test/server/historyApiFallback-option.test.js index cc600e7a4a..460e9441fd 100644 --- a/test/server/historyApiFallback-option.test.js +++ b/test/server/historyApiFallback-option.test.js @@ -50,11 +50,11 @@ describe('historyApiFallback option', () => { req = request(server.app); }); - it('request to directory', (done) => { - req + it('request to directory', async () => { + await req .get('/foo') .accept('html') - .expect(200, /Foobar/, done); + .expect(200, /Foobar/); }); }); @@ -77,18 +77,18 @@ describe('historyApiFallback option', () => { req = request(server.app); }); - it('historyApiFallback should take preference above directory index', (done) => { - req + it('historyApiFallback should take preference above directory index', async () => { + await req .get('/') .accept('html') - .expect(200, /Foobar/, done); + .expect(200, /Foobar/); }); - it('request to directory', (done) => { - req + it('request to directory', async () => { + await req .get('/foo') .accept('html') - .expect(200, /Foobar/, done); + .expect(200, /Foobar/); }); it('contentBase file should take preference above historyApiFallback', (done) => { @@ -124,11 +124,11 @@ describe('historyApiFallback option', () => { req = request(server.app); }); - it('historyApiFallback should work and ignore static content', (done) => { - req + it('historyApiFallback should work and ignore static content', async () => { + await req .get('/index.html') .accept('html') - .expect(200, /In-memory file/, done); + .expect(200, /In-memory file/); }); }); @@ -160,25 +160,25 @@ describe('historyApiFallback option', () => { req = request(server.app); }); - it('historyApiFallback respect rewrites for index', (done) => { - req + it('historyApiFallback respect rewrites for index', async () => { + await req .get('/') .accept('html') - .expect(200, /Foobar/, done); + .expect(200, /Foobar/); }); - it('historyApiFallback respect rewrites and shows index for unknown urls', (done) => { - req + it('historyApiFallback respect rewrites and shows index for unknown urls', async () => { + await req .get('/acme') .accept('html') - .expect(200, /Foobar/, done); + .expect(200, /Foobar/); }); - it('historyApiFallback respect any other specified rewrites', (done) => { - req + it('historyApiFallback respect any other specified rewrites', async () => { + await req .get('/other') .accept('html') - .expect(200, /Other file/, done); + .expect(200, /Other file/); }); }); @@ -199,11 +199,11 @@ describe('historyApiFallback option', () => { req = request(server.app); }); - it('should take precedence over contentBase files', (done) => { - req + it('should take precedence over contentBase files', async () => { + await req .get('/foo') .accept('html') - .expect(200, /In-memory file/, done); + .expect(200, /In-memory file/); }); }); }); diff --git a/test/server/host-option.test.js b/test/server/host-option.test.js index 009379062a..667caabd9f 100644 --- a/test/server/host-option.test.js +++ b/test/server/host-option.test.js @@ -50,8 +50,8 @@ describe('host option', () => { expect(address.port).toBe(port); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); @@ -77,8 +77,8 @@ describe('host option', () => { expect(address.port).toBe(port); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); @@ -104,8 +104,8 @@ describe('host option', () => { expect(address.port).toBe(port); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); @@ -131,8 +131,8 @@ describe('host option', () => { expect(address.port).toBe(port); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); @@ -158,8 +158,8 @@ describe('host option', () => { expect(address.port).toBe(port); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); diff --git a/test/server/hot-option.test.js b/test/server/hot-option.test.js index 979bbad12c..e79679ff4b 100644 --- a/test/server/hot-option.test.js +++ b/test/server/hot-option.test.js @@ -26,8 +26,8 @@ describe('hot option', () => { afterAll(testServer.close); - it('should include hot script in the bundle', (done) => { - req.get('/main.js').expect(200, /webpack\/hot\/dev-server\.js/, done); + it('should include hot script in the bundle', async () => { + await req.get('/main.js').expect(200, /webpack\/hot\/dev-server\.js/); }); }); @@ -51,8 +51,8 @@ describe('hot option', () => { afterAll(testServer.close); - it('should include hot script in the bundle', (done) => { - req.get('/main.js').expect(200, /webpack\/hot\/dev-server\.js/, done); + it('should include hot script in the bundle', async () => { + await req.get('/main.js').expect(200, /webpack\/hot\/dev-server\.js/); }); }); @@ -72,14 +72,10 @@ describe('hot option', () => { afterAll(testServer.close); - it('should NOT include hot script in the bundle', (done) => { - req - .get('/main.js') - .expect(200) - .then(({ text }) => { - expect(text).not.toMatch(/webpack\/hot\/dev-server\.js/); - done(); - }); + it('should NOT include hot script in the bundle', async () => { + const { text } = await req.get('/main.js').expect(200); + + expect(text).not.toMatch(/webpack\/hot\/dev-server\.js/); }); }); diff --git a/test/server/hotOnly-option.test.js b/test/server/hotOnly-option.test.js index e601325fd3..61cba1eb88 100644 --- a/test/server/hotOnly-option.test.js +++ b/test/server/hotOnly-option.test.js @@ -25,10 +25,10 @@ describe('hotOnly options', () => { afterAll(testServer.close); - it('should include hotOnly script in the bundle', (done) => { - req + it('should include hotOnly script in the bundle', async () => { + await req .get('/main.js') - .expect(200, /webpack\/hot\/only-dev-server\.js/, done); + .expect(200, /webpack\/hot\/only-dev-server\.js/); }); }); diff --git a/test/server/http2-option.test.js b/test/server/http2-option.test.js index 4cf4cd9934..aff171d6fa 100644 --- a/test/server/http2-option.test.js +++ b/test/server/http2-option.test.js @@ -107,14 +107,10 @@ describe('http2 option', () => { req = request(server.app); }); - it('Request to index', (done) => { - req - .get('/') - .expect(200, /Heyo/) - .then(({ res }) => { - expect(res.httpVersion).not.toEqual('2.0'); - done(); - }); + it('Request to index', async () => { + const { res } = await req.get('/').expect(200, /Heyo/); + + expect(res.httpVersion).not.toEqual('2.0'); }); afterAll(testServer.close); diff --git a/test/server/https-option.test.js b/test/server/https-option.test.js index 719875a000..2c569ea5df 100644 --- a/test/server/https-option.test.js +++ b/test/server/https-option.test.js @@ -35,8 +35,8 @@ describe('https option', () => { req = request(server.app); }); - it('Request to index', (done) => { - req.get('/').expect(200, /Heyo/, done); + it('Request to index', async () => { + await req.get('/').expect(200, /Heyo/); }); afterAll(() => { @@ -70,8 +70,8 @@ describe('https option', () => { req = request(server.app); }); - it('Request to index', (done) => { - req.get('/').expect(200, /Heyo/, done); + it('Request to index', async () => { + await req.get('/').expect(200, /Heyo/); }); }); @@ -124,8 +124,8 @@ describe('https option', () => { req = request(server.app); }); - it('Request to index', (done) => { - req.get('/').expect(200, /Heyo/, done); + it('Request to index', async () => { + await req.get('/').expect(200, /Heyo/); }); afterAll(testServer.close); @@ -160,8 +160,8 @@ describe('https option', () => { req = request(server.app); }); - it('Request to index', (done) => { - req.get('/').expect(200, /Heyo/, done); + it('Request to index', async () => { + await req.get('/').expect(200, /Heyo/); }); }); diff --git a/test/server/inline-option.test.js b/test/server/inline-option.test.js index 78344621e6..7fc37ad994 100644 --- a/test/server/inline-option.test.js +++ b/test/server/inline-option.test.js @@ -26,10 +26,10 @@ describe('inline option', () => { afterAll(testServer.close); - it('should include inline client script in the bundle', (done) => { + it('should include inline client script in the bundle', async () => { const url = new RegExp(`client/index.js\\?http://0.0.0.0:${port}`); - req.get('/main.js').expect(200, url, done); + await req.get('/main.js').expect(200, url); }); }); @@ -76,14 +76,10 @@ describe('inline option', () => { afterAll(testServer.close); - it('should NOT include inline client script in the bundle', (done) => { - req - .get('/main.js') - .expect(200) - .then(({ text }) => { - expect(text.includes(`client/index.js?http://0.0.0.0:${port}`)); - done(); - }); + it('should NOT include inline client script in the bundle', async () => { + const { text } = await req.get('/main.js').expect(200); + + expect(text.includes(`client/index.js?http://0.0.0.0:${port}`)); }); }); }); diff --git a/test/server/mimeTypes-option.test.js b/test/server/mimeTypes-option.test.js index 662007dadd..5f2fef5e7a 100644 --- a/test/server/mimeTypes-option.test.js +++ b/test/server/mimeTypes-option.test.js @@ -54,11 +54,11 @@ describe('mimeTypes option', () => { afterAll(testServer.close); - it('request to bundle file with modified mime type', (done) => { - req + it('request to bundle file with modified mime type', async () => { + await req .get('/main.js') .expect('Content-Type', /application\/octet-stream/) - .expect(200, done); + .expect(200); }); }); }); diff --git a/test/server/onListening-option.test.js b/test/server/onListening-option.test.js index d6d66b1565..2d8b86dc3e 100644 --- a/test/server/onListening-option.test.js +++ b/test/server/onListening-option.test.js @@ -1,32 +1,71 @@ 'use strict'; +const { unlink } = require('fs'); +const { join } = require('path'); const testServer = require('../helpers/test-server'); const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map')['onListening-option']; +const { skipTestOnWindows } = require('../helpers/conditional-test'); describe('onListening option', () => { - let onListeningIsRunning = false; - - beforeAll((done) => { - testServer.start( - config, - { - onListening: (devServer) => { - if (!devServer) { - throw new Error('webpack-dev-server is not defined'); - } - - onListeningIsRunning = true; + describe('with host and port', () => { + let onListeningIsRunning = false; + + beforeAll((done) => { + testServer.start( + config, + { + onListening: (devServer) => { + if (!devServer) { + throw new Error('webpack-dev-server is not defined'); + } + + onListeningIsRunning = true; + }, + port, }, - port, - }, - done - ); + done + ); + }); + + afterAll(testServer.close); + + it('should run onListening callback', () => { + expect(onListeningIsRunning).toBe(true); + }); }); - afterAll(testServer.close); + describe('with Unix socket', () => { + if (skipTestOnWindows('Unix sockets are not supported on Windows')) { + return; + } + + const socketPath = join('.', 'onListening.webpack.sock'); + let onListeningIsRunning = false; + + beforeAll((done) => { + testServer.start( + config, + { + onListening: (devServer) => { + if (!devServer) { + throw new Error('webpack-dev-server is not defined'); + } + + onListeningIsRunning = true; + }, + socket: socketPath, + }, + done + ); + }); + + afterAll(testServer.close); + + it('should run onListening callback', (done) => { + expect(onListeningIsRunning).toBe(true); - it('should runs onListening callback', () => { - expect(onListeningIsRunning).toBe(true); + unlink(socketPath, done); + }); }); }); diff --git a/test/server/open-option.test.js b/test/server/open-option.test.js index 9eeb8d3cd8..01f7d88b8a 100644 --- a/test/server/open-option.test.js +++ b/test/server/open-option.test.js @@ -1,9 +1,9 @@ 'use strict'; -jest.mock('opn'); +jest.mock('open'); const webpack = require('webpack'); -const open = require('opn'); +const open = require('open'); const Server = require('../../lib/Server'); const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map')['open-option']; diff --git a/test/server/port-option.test.js b/test/server/port-option.test.js index ff7488de80..d9fd6c2391 100644 --- a/test/server/port-option.test.js +++ b/test/server/port-option.test.js @@ -23,8 +23,8 @@ describe('port', () => { expect(address.port).toBeDefined(); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); @@ -51,8 +51,8 @@ describe('port', () => { expect(address.port).toBeDefined(); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); @@ -78,8 +78,8 @@ describe('port', () => { expect(address.port).toBeDefined(); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); @@ -104,8 +104,8 @@ describe('port', () => { expect(address.port).toBe(33333); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); @@ -130,8 +130,8 @@ describe('port', () => { expect(address.port).toBe(33333); }); - it('Request to index', (done) => { - req.get('/').expect(200, done); + it('Request to index', async () => { + await req.get('/').expect(200); }); afterAll(testServer.close); diff --git a/test/server/proxy-option.test.js b/test/server/proxy-option.test.js index 6f21967248..21917d9ab8 100644 --- a/test/server/proxy-option.test.js +++ b/test/server/proxy-option.test.js @@ -108,32 +108,32 @@ describe('proxy option', () => { }); describe('target', () => { - it('respects a proxy option when a request path is matched', (done) => { - req.get('/proxy1').expect(200, 'from proxy1', done); + it('respects a proxy option when a request path is matched', async () => { + await req.get('/proxy1').expect(200, 'from proxy1'); }); }); describe('pathRewrite', () => { - it('respects a pathRewrite option', (done) => { - req.get('/api/proxy2').expect(200, 'from proxy2', done); + it('respects a pathRewrite option', async () => { + await req.get('/api/proxy2').expect(200, 'from proxy2'); }); }); describe('bypass', () => { - it('can rewrite a request path', (done) => { - req.get('/foo/bar.html').expect(200, /Hello/, done); + it('can rewrite a request path', async () => { + await req.get('/foo/bar.html').expect(200, /Hello/); }); - it('can rewrite a request path regardless of the target defined a bypass option', (done) => { - req.get('/baz/hoge.html').expect(200, /Hello/, done); + it('can rewrite a request path regardless of the target defined a bypass option', async () => { + await req.get('/baz/hoge.html').expect(200, /Hello/); }); - it('should pass through a proxy when a bypass function returns null', (done) => { - req.get('/foo.js').expect(200, /Hey/, done); + it('should pass through a proxy when a bypass function returns null', async () => { + await req.get('/foo.js').expect(200, /Hey/); }); - it('should not pass through a proxy when a bypass function returns false', (done) => { - req.get('/proxyfalse').expect(404, done); + it('should not pass through a proxy when a bypass function returns false', async () => { + await req.get('/proxyfalse').expect(404); }); }); }); @@ -164,8 +164,8 @@ describe('proxy option', () => { }); }); - it('respects a proxy option', (done) => { - req.get('/proxy1').expect(200, 'from proxy1', done); + it('respects a proxy option', async () => { + await req.get('/proxy1').expect(200, 'from proxy1'); }); }); @@ -195,12 +195,12 @@ describe('proxy option', () => { }); }); - it('respects a proxy option', (done) => { - req.get('/proxy1').expect(200, 'from proxy1', done); + it('respects a proxy option', async () => { + await req.get('/proxy1').expect(200, 'from proxy1'); }); - it('respects a proxy option of function', (done) => { - req.get('/api/proxy2').expect(200, 'from proxy2', done); + it('respects a proxy option of function', async () => { + await req.get('/api/proxy2').expect(200, 'from proxy2'); }); }); @@ -243,12 +243,12 @@ describe('proxy option', () => { }); }); - it('respects proxy1 option', (done) => { - req.get('/proxy1').expect(200, 'from proxy', done); + it('respects proxy1 option', async () => { + await req.get('/proxy1').expect(200, 'from proxy'); }); - it('respects proxy2 option', (done) => { - req.get('/proxy2').expect(200, 'from proxy', done); + it('respects proxy2 option', async () => { + await req.get('/proxy2').expect(200, 'from proxy'); }); }); @@ -366,36 +366,35 @@ describe('proxy option', () => { }); }); - it('GET method', (done) => { - req.get('/get').expect(200, 'GET method from proxy', done); + it('GET method', async () => { + await req.get('/get').expect(200, 'GET method from proxy'); }); it('HEAD method', (done) => { req.head('/head').expect(200, done); }); - it('POST method (application/x-www-form-urlencoded)', (done) => { - req + it('POST method (application/x-www-form-urlencoded)', async () => { + await req .post('/post-x-www-form-urlencoded') .send('id=1') - .expect(200, 'POST method from proxy (id: 1)', done); + .expect(200, 'POST method from proxy (id: 1)'); }); - it('POST method (application/json)', (done) => { - req + it('POST method (application/json)', async () => { + await req .post('/post-application-json') .send({ id: '1' }) .set('Accept', 'application/json') .expect('Content-Type', /json/) .expect( 200, - JSON.stringify({ answer: 'POST method from proxy (id: 1)' }), - done + JSON.stringify({ answer: 'POST method from proxy (id: 1)' }) ); }); - it('DELETE method', (done) => { - req.delete('/delete').expect(200, 'DELETE method from proxy', done); + it('DELETE method', async () => { + await req.delete('/delete').expect(200, 'DELETE method from proxy'); }); }); }); diff --git a/test/server/servers/SockJSServer.test.js b/test/server/servers/SockJSServer.test.js index 48c535e98e..14aaf04d73 100644 --- a/test/server/servers/SockJSServer.test.js +++ b/test/server/servers/SockJSServer.test.js @@ -4,6 +4,7 @@ const http = require('http'); const express = require('express'); const SockJS = require('sockjs-client/dist/sockjs'); const SockJSServer = require('../../../lib/servers/SockJSServer'); +const timer = require('../../helpers/timer'); const port = require('../../ports-map').SockJSServer; describe('SockJSServer', () => { @@ -32,18 +33,19 @@ describe('SockJSServer', () => { }); describe('server', () => { - it('should recieve connection, send message, and close client', (done) => { + it('should recieve connection, send message, and close client', async () => { const data = []; - let headers; - socketServer.onConnection((connection, h) => { + + socketServer.onConnection(async (connection, h) => { headers = h; data.push('open'); socketServer.send(connection, 'hello world'); - setTimeout(() => { - // the server closes the connection with the client - socketServer.close(connection); - }, 1000); + + await timer(1000); + + // the server closes the connection with the client + socketServer.close(connection); }); const client = new SockJS(`http://localhost:${port}/sockjs-node`); @@ -56,14 +58,13 @@ describe('SockJSServer', () => { data.push('close'); }; - setTimeout(() => { - expect(headers.host).toMatchSnapshot(); - expect(data).toMatchSnapshot(); - done(); - }, 3000); + await timer(3000); + + expect(headers.host).toMatchSnapshot(); + expect(data).toMatchSnapshot(); }); - it('should receive client close event', (done) => { + it('should receive client close event', async () => { let receivedClientClose = false; socketServer.onConnection((connection) => { socketServer.onConnectionClose(connection, () => { @@ -74,15 +75,14 @@ describe('SockJSServer', () => { // eslint-disable-next-line new-cap const client = new SockJS(`http://localhost:${port}/sockjs-node`); - setTimeout(() => { - // the client closes itself, the server does not close it - client.close(); - }, 1000); + await timer(1000); + + // the client closes itself, the server does not close it + client.close(); + + await timer(3000); - setTimeout(() => { - expect(receivedClientClose).toBeTruthy(); - done(); - }, 3000); + expect(receivedClientClose).toBeTruthy(); }); it('should not throw an exception when connection is null', () => { diff --git a/test/server/sockPath-option.test.js b/test/server/sockPath-option.test.js index b6840228b5..484cbfaf76 100644 --- a/test/server/sockPath-option.test.js +++ b/test/server/sockPath-option.test.js @@ -25,8 +25,8 @@ describe('sockPath options', () => { expect(!!server.sockPath.match(/\/[a-z0-9\-/]+[^/]$/)).toBeTruthy(); }); - it('responds with a 200', (done) => { - req.get('/sockjs-node').expect(200, done); + it('responds with a 200', async () => { + await req.get('/sockjs-node').expect(200); }); }); @@ -37,7 +37,9 @@ describe('sockPath options', () => { server = testServer.start( config, { - sockPath: '/foo/test/bar/', + clientSocketOptions: { + path: '/foo/test/bar/', + }, port, }, done @@ -49,8 +51,8 @@ describe('sockPath options', () => { expect(server.sockPath).toEqual(path); }); - it('responds with a 200 second', (done) => { - req.get(path).expect(200, done); + it('responds with a 200 second', async () => { + await req.get(path).expect(200); }); }); }); diff --git a/test/server/socket-option.test.js b/test/server/socket-option.test.js new file mode 100644 index 0000000000..a933097bb1 --- /dev/null +++ b/test/server/socket-option.test.js @@ -0,0 +1,149 @@ +'use strict'; + +const net = require('net'); +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const testServer = require('../helpers/test-server'); +const TestUnixSocket = require('../helpers/test-unix-socket'); +const { skipTestOnWindows } = require('../helpers/conditional-test'); +const config = require('../fixtures/simple-config/webpack.config'); +const Server = require('../../lib/Server'); + +describe('socket', () => { + const socketPath = path.join('.', 'socket-option.webpack.sock'); + let server = null; + + describe('path to a non-existent file', () => { + let err; + beforeAll((done) => { + server = testServer.start( + config, + { + socket: socketPath, + }, + (e) => { + err = e; + done(); + } + ); + }); + + it('should work as Unix socket or error on windows', (done) => { + if (process.platform === 'win32') { + expect(err.code).toEqual('EACCES'); + done(); + } else { + const clientSocket = new net.Socket(); + clientSocket.connect({ path: socketPath }, () => { + // this means the connection was made successfully + expect(true).toBeTruthy(); + done(); + }); + } + }); + + afterAll((done) => { + testServer.close(() => { + fs.unlink(socketPath, done); + }); + }); + }); + + describe('path to existent, unused file', () => { + if (skipTestOnWindows('Unix sockets are not supported on Windows')) { + return; + } + + beforeAll((done) => { + fs.writeFileSync(socketPath, ''); + server = testServer.start( + config, + { + socket: socketPath, + }, + done + ); + }); + + it('should work as Unix socket', (done) => { + const clientSocket = new net.Socket(); + clientSocket.connect({ path: socketPath }, () => { + // this means the connection was made successfully + expect(true).toBeTruthy(); + done(); + }); + }); + + afterAll((done) => { + testServer.close(() => { + fs.unlink(socketPath, done); + }); + }); + }); + + describe('path to existent file with listening server', () => { + if (skipTestOnWindows('Unix sockets are not supported on Windows')) { + return; + } + + let testUnixSocket; + beforeAll((done) => { + testUnixSocket = new TestUnixSocket(); + testUnixSocket.server.listen(socketPath, 511, () => { + done(); + }); + }); + + it('should throw already used error', (done) => { + server = testServer.start( + config, + { + socket: socketPath, + }, + (err) => { + expect(err).not.toBeNull(); + expect(err).not.toBeUndefined(); + expect(err.message).toEqual('This socket is already used'); + server.close(done); + } + ); + }); + + afterAll((done) => { + testUnixSocket.close(() => { + fs.unlink(socketPath, done); + }); + }); + }); + + describe('path to existent, unused file', () => { + let devServer; + const options = { + socket: socketPath, + }; + beforeAll(() => { + fs.writeFileSync(socketPath, ''); + }); + + // this test is significant because previously the callback was called + // twice if a file at the given socket path already existed, but + // could be removed + it('should only call server.listen callback once', (done) => { + const compiler = webpack(config); + + devServer = new Server(compiler, options); + const onListen = jest.fn(); + // eslint-disable-next-line no-undefined + devServer.listen(options.socket, undefined, onListen); + setTimeout(() => { + expect(onListen).toBeCalledTimes(1); + done(); + }, 10000); + }); + + afterAll((done) => { + devServer.close(done); + }); + }); +}); diff --git a/test/server/stats-option.test.js b/test/server/stats-option.test.js index dc89702cc8..f6db78dcc3 100644 --- a/test/server/stats-option.test.js +++ b/test/server/stats-option.test.js @@ -6,7 +6,7 @@ const config = require('../fixtures/simple-config/webpack.config'); const port = require('../ports-map')['stats-option']; describe('stats option', () => { - it(`should works with difference stats values (contains 'hash', 'assets', 'warnings' and 'errors')`, () => { + it(`should works with difference stats values (contains 'hash', 'assets', 'warnings' and 'errors')`, async () => { const allStats = [ {}, // eslint-disable-next-line no-undefined @@ -18,23 +18,22 @@ describe('stats option', () => { }, ]; - return allStats.reduce((p, stats) => { - return p.then(() => { - return new Promise((resolve) => { - const compiler = webpack(config); - const server = new Server(compiler, { stats, port, quiet: true }); + for (const stats of allStats) { + const compiler = webpack(config); + const server = new Server(compiler, { stats, port, quiet: true }); - compiler.hooks.done.tap('webpack-dev-server', (s) => { - expect(Object.keys(server.getStats(s))).toMatchSnapshot(); + // eslint-disable-next-line no-await-in-loop + await new Promise((resolve) => { + compiler.hooks.done.tap('webpack-dev-server', (s) => { + expect(Object.keys(server.getStats(s))).toMatchSnapshot(); - server.close(resolve); - }); - - compiler.run(() => {}); - server.listen(port, 'localhost'); + server.close(resolve); }); + + compiler.run(() => {}); + server.listen(port, 'localhost'); }); - }, Promise.resolve()); + } }); it('should respect warningsFilter', (done) => { diff --git a/test/server/stdin-option.test.js b/test/server/stdin-option.test.js new file mode 100644 index 0000000000..da742ffff8 --- /dev/null +++ b/test/server/stdin-option.test.js @@ -0,0 +1,62 @@ +'use strict'; + +const config = require('../fixtures/simple-config/webpack.config'); +const testServer = require('../helpers/test-server'); +const timer = require('../helpers/timer'); +const port = require('../ports-map')['stdin-option']; + +describe('stdin', () => { + // eslint-disable-next-line no-unused-vars + let server; + let exitSpy; + + beforeAll(() => { + exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {}); + }); + + afterEach((done) => { + server = null; + exitSpy.mockReset(); + process.stdin.removeAllListeners('end'); + testServer.close(done); + }); + + describe('enabled', () => { + beforeAll((done) => { + server = testServer.start( + config, + { + port, + stdin: true, + }, + done + ); + }); + + it('should exit process', async () => { + process.stdin.emit('end'); + await timer(1000); + process.stdin.pause(); + expect(exitSpy.mock.calls[0]).toEqual([0]); + }); + }); + + describe('disabled (default)', () => { + beforeAll((done) => { + server = testServer.start( + config, + { + port, + }, + done + ); + }); + + it('should not exit process', async () => { + process.stdin.emit('end'); + await timer(1000); + process.stdin.pause(); + expect(exitSpy.mock.calls.length).toEqual(0); + }); + }); +}); diff --git a/test/server/transportMode-option.test.js b/test/server/transportMode-option.test.js index d4a54ffc12..55d819fa4e 100644 --- a/test/server/transportMode-option.test.js +++ b/test/server/transportMode-option.test.js @@ -195,7 +195,9 @@ describe('transportMode', () => { config, { port, - sockPath: '/foo/test/bar/', + clientSocketOptions: { + path: '/foo/test/bar/', + }, transportMode: { server: class MySockJSServer extends BaseServer { constructor(serv) { @@ -217,7 +219,7 @@ describe('transportMode', () => { prefix: this.server.sockPath, }); - sockPath = server.options.sockPath; + sockPath = server.options.clientSocketOptions.path; } send(connection, message) { diff --git a/test/server/utils/__snapshots__/createConfig.test.js.snap b/test/server/utils/__snapshots__/createConfig.test.js.snap index 585d74105c..9e9b6ea851 100644 --- a/test/server/utils/__snapshots__/createConfig.test.js.snap +++ b/test/server/utils/__snapshots__/createConfig.test.js.snap @@ -1202,12 +1202,14 @@ Object { exports[`createConfig sockHost option 1`] = ` Object { + "clientSocketOptions": Object { + "host": true, + }, "hot": true, "hotOnly": false, "noInfo": true, "port": 8080, "publicPath": "/", - "sockHost": true, "stats": Object { "cached": false, "cachedAssets": false, @@ -1217,12 +1219,14 @@ Object { exports[`createConfig sockPath option 1`] = ` Object { + "clientSocketOptions": Object { + "path": "path", + }, "hot": true, "hotOnly": false, "noInfo": true, "port": 8080, "publicPath": "/", - "sockPath": "path", "stats": Object { "cached": false, "cachedAssets": false, @@ -1232,12 +1236,14 @@ Object { exports[`createConfig sockPort option 1`] = ` Object { + "clientSocketOptions": Object { + "port": "port", + }, "hot": true, "hotOnly": false, "noInfo": true, "port": 8080, "publicPath": "/", - "sockPort": "port", "stats": Object { "cached": false, "cachedAssets": false, diff --git a/test/server/utils/addEntries.test.js b/test/server/utils/addEntries.test.js index 1bc6a6cef6..c124c7c36c 100644 --- a/test/server/utils/addEntries.test.js +++ b/test/server/utils/addEntries.test.js @@ -17,7 +17,7 @@ describe('addEntries util', () => { expect(webpackOptions.entry.length).toEqual(2); expect( - normalize(webpackOptions.entry[0]).indexOf('client/index.js?') !== -1 + normalize(webpackOptions.entry[0]).includes('client/index.js?') ).toBeTruthy(); expect(normalize(webpackOptions.entry[1])).toEqual('./foo.js'); }); @@ -33,7 +33,7 @@ describe('addEntries util', () => { expect(webpackOptions.entry.length).toEqual(3); expect( - normalize(webpackOptions.entry[0]).indexOf('client/index.js?') !== -1 + normalize(webpackOptions.entry[0]).includes('client/index.js?') ).toBeTruthy(); expect(webpackOptions.entry[1]).toEqual('./foo.js'); expect(webpackOptions.entry[2]).toEqual('./bar.js'); @@ -54,7 +54,7 @@ describe('addEntries util', () => { expect(webpackOptions.entry.foo.length).toEqual(2); expect( - normalize(webpackOptions.entry.foo[0]).indexOf('client/index.js?') !== -1 + normalize(webpackOptions.entry.foo[0]).includes('client/index.js?') ).toBeTruthy(); expect(webpackOptions.entry.foo[1]).toEqual('./foo.js'); expect(webpackOptions.entry.bar[1]).toEqual('./bar.js'); @@ -70,7 +70,7 @@ describe('addEntries util', () => { expect(webpackOptions.entry[1]).toEqual('./src'); }); - it('should preserves dynamic entry points', (done) => { + it('should preserves dynamic entry points', async () => { let i = 0; const webpackOptions = { // simulate dynamic entry @@ -85,22 +85,21 @@ describe('addEntries util', () => { expect(typeof webpackOptions.entry).toEqual('function'); - webpackOptions - .entry() - .then((entryFirstRun) => - webpackOptions.entry().then((entrySecondRun) => { - expect(entryFirstRun.length).toEqual(2); - expect(entryFirstRun[1]).toEqual('./src-1.js'); - - expect(entrySecondRun.length).toEqual(2); - expect(entrySecondRun[1]).toEqual('./src-2.js'); - done(); - }) - ) - .catch(done); + { + const entry = await webpackOptions.entry(); + + expect(entry.length).toEqual(2); + expect(entry[1]).toEqual('./src-1.js'); + } + { + const entry = await webpackOptions.entry(); + + expect(entry.length).toEqual(2); + expect(entry[1]).toEqual('./src-2.js'); + } }); - it('should preserves asynchronous dynamic entry points', (done) => { + it('should preserves asynchronous dynamic entry points', async () => { let i = 0; const webpackOptions = { // simulate async dynamic entry @@ -117,19 +116,18 @@ describe('addEntries util', () => { expect(typeof webpackOptions.entry).toEqual('function'); - webpackOptions - .entry() - .then((entryFirstRun) => - webpackOptions.entry().then((entrySecondRun) => { - expect(entryFirstRun.length).toEqual(2); - expect(entryFirstRun[1]).toEqual('./src-1.js'); - - expect(entrySecondRun.length).toEqual(2); - expect(entrySecondRun[1]).toEqual('./src-2.js'); - done(); - }) - ) - .catch(done); + { + const entry = await webpackOptions.entry(); + + expect(entry.length).toEqual(2); + expect(entry[1]).toEqual('./src-1.js'); + } + { + const entry = await webpackOptions.entry(); + + expect(entry.length).toEqual(2); + expect(entry[1]).toEqual('./src-2.js'); + } }); it("should prepends webpack's hot reload client script", () => { @@ -312,7 +310,7 @@ describe('addEntries util', () => { if (expectInline) { expect( - normalize(webpackOptions.entry[0]).indexOf('client/index.js?') !== -1 + normalize(webpackOptions.entry[0]).includes('client/index.js?') ).toBeTruthy(); } @@ -344,7 +342,7 @@ describe('addEntries util', () => { if (expectInline) { expect( - normalize(webpackOptions.entry[0]).indexOf('client/index.js?') !== -1 + normalize(webpackOptions.entry[0]).includes('client/index.js?') ).toBeTruthy(); } @@ -400,7 +398,7 @@ describe('addEntries util', () => { expect(webWebpackOptions.entry.length).toEqual(2); expect( - normalize(webWebpackOptions.entry[0]).indexOf('client/index.js?') !== -1 + normalize(webWebpackOptions.entry[0]).includes('client/index.js?') ).toBeTruthy(); expect(normalize(webWebpackOptions.entry[1])).toEqual('./foo.js'); diff --git a/test/server/utils/findPort.test.js b/test/server/utils/findPort.test.js index 8914807a6e..cabfc832e0 100644 --- a/test/server/utils/findPort.test.js +++ b/test/server/utils/findPort.test.js @@ -7,106 +7,109 @@ const findPort = require('../../../lib/utils/findPort'); describe('findPort', () => { let dummyServers = []; - afterEach(() => { + afterEach(async () => { delete process.env.DEFAULT_PORT_RETRY; - return dummyServers - .reduce((p, server) => { - return p.then(() => { - return new Promise((resolve) => { - server.close(resolve); - }); - }); - }, Promise.resolve()) - .then(() => { - dummyServers = []; + async function close(server) { + return new Promise((resolve) => { + server.close(resolve); }); + } + + for (const server of dummyServers) { + // eslint-disable-next-line no-await-in-loop + await close(server); + } + + dummyServers = []; }); - function createDummyServers(n) { - return (Array.isArray(n) ? n : [...new Array(n)]).reduce((p, _, i) => { - return p.then(() => { - return new Promise((resolve) => { - const server = http.createServer(); - dummyServers.push(server); - server.listen(8080 + i, resolve); - }); + async function createDummyServers(n) { + async function create(i) { + return new Promise((resolve) => { + const server = http.createServer(); + dummyServers.push(server); + server.listen(8080 + i, resolve); }); - }, Promise.resolve()); + } + + const samples = [...new Array(n)].map((_, i) => i); + + for (const i of samples) { + // eslint-disable-next-line no-await-in-loop + await create(i); + } } - it('should returns the port when the port is specified', () => { + it('should returns the port when the port is specified', async () => { process.env.DEFAULT_PORT_RETRY = 5; - return findPort(8082).then((port) => { - expect(port).toEqual(8082); - }); + const port = await findPort(8082); + expect(port).toEqual(8082); }); - it.only('should returns the port when the port is null', () => { + it.only('should returns the port when the port is null', async () => { const retryCount = 2; process.env.DEFAULT_PORT_RETRY = 2; - return createDummyServers(retryCount) - .then(() => findPort(null)) - .then((port) => { - expect(port).toEqual(8080 + retryCount); - }); + await createDummyServers(retryCount); + + const port = await findPort(null); + + expect(port).toEqual(8080 + retryCount); }); - it('should returns the port when the port is undefined', () => { + it('should returns the port when the port is undefined', async () => { const retryCount = 2; process.env.DEFAULT_PORT_RETRY = 2; - return ( - createDummyServers(retryCount) - // eslint-disable-next-line no-undefined - .then(() => findPort(undefined)) - .then((port) => { - expect(port).toEqual(8080 + retryCount); - }) - ); + await createDummyServers(retryCount); + + // eslint-disable-next-line no-undefined + const port = findPort(undefined); + + expect(port).toEqual(8080 + retryCount); }); - it('should retry finding the port for up to defaultPortRetry times (number)', () => { + it('should retry finding the port for up to defaultPortRetry times (number)', async () => { const retryCount = 3; process.env.DEFAULT_PORT_RETRY = retryCount; - return createDummyServers(retryCount) - .then(() => findPort()) - .then((port) => { - expect(port).toEqual(8080 + retryCount); - }); + await createDummyServers(retryCount); + + const port = await findPort(); + + expect(port).toEqual(8080 + retryCount); }); - it('should retry finding the port for up to defaultPortRetry times (string)', () => { + it('should retry finding the port for up to defaultPortRetry times (string)', async () => { const retryCount = 3; process.env.DEFAULT_PORT_RETRY = `${retryCount}`; - return createDummyServers(retryCount) - .then(() => findPort()) - .then((port) => { - expect(port).toEqual(8080 + retryCount); - }); + await createDummyServers(retryCount); + + const port = await findPort(); + + expect(port).toEqual(8080 + retryCount); }); - it('should retry finding the port when serial ports are busy', () => { + it('should retry finding the port when serial ports are busy', async () => { const busyPorts = [8080, 8081, 8082, 8083]; process.env.DEFAULT_PORT_RETRY = 3; - return createDummyServers(busyPorts) - .then(() => findPort()) - .then((port) => { - expect(port).toEqual(8080 + busyPorts.length); - }); + await createDummyServers(busyPorts); + + const port = await findPort(); + + expect(port).toEqual(8080 + busyPorts.length); }); - it("should throws the error when the port isn't found", () => { + it("should throws the error when the port isn't found", async () => { expect.assertions(1); const spy = jest @@ -119,12 +122,14 @@ describe('findPort', () => { process.env.DEFAULT_PORT_RETRY = 0; - return createDummyServers(retryCount) - .then(() => findPort()) - .catch((err) => { - expect(err.message).toMatchSnapshot(); + await createDummyServers(retryCount); - spy.mockRestore(); - }); + try { + await findPort(); + } catch (err) { + expect(err.message).toMatchSnapshot(); + } + + spy.mockRestore(); }); }); diff --git a/test/server/utils/handleStdin.test.js b/test/server/utils/handleStdin.test.js new file mode 100644 index 0000000000..5d44e0dc06 --- /dev/null +++ b/test/server/utils/handleStdin.test.js @@ -0,0 +1,41 @@ +'use strict'; + +const timer = require('../../helpers/timer'); +const handleStdin = require('../../../lib/utils/handleStdin'); + +describe('handleStdin', () => { + let exitSpy; + + beforeAll(() => { + exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {}); + }); + + afterEach(() => { + process.stdin.removeAllListeners('end'); + exitSpy.mockReset(); + }); + + describe('enabled', () => { + it('should exit process', async () => { + handleStdin({ + stdin: true, + }); + process.stdin.emit('end'); + + await timer(1000); + process.stdin.pause(); + expect(exitSpy.mock.calls[0]).toEqual([0]); + }); + }); + + describe('disabled (default)', () => { + it('should not exit process', async () => { + handleStdin({}); + process.stdin.emit('end'); + + await timer(1000); + process.stdin.pause(); + expect(exitSpy.mock.calls.length).toEqual(0); + }); + }); +}); diff --git a/test/server/utils/routes.test.js b/test/server/utils/routes.test.js index acc94eb8eb..f4b5030e3d 100644 --- a/test/server/utils/routes.test.js +++ b/test/server/utils/routes.test.js @@ -16,112 +16,106 @@ describe('routes util', () => { afterAll(testServer.close); - it('should handles GET request to live bundle', (done) => { - req.get('/__webpack_dev_server__/live.bundle.js').then(({ res }) => { - expect(res.headers['content-type']).toEqual('application/javascript'); - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles GET request to live bundle', async () => { + const { headers, statusCode } = await req.get( + '/__webpack_dev_server__/live.bundle.js' + ); + + expect(headers['content-type']).toEqual('application/javascript'); + expect(statusCode).toEqual(200); }); - it('should handles HEAD request to live bundle', (done) => { - req.head('/__webpack_dev_server__/live.bundle.js').then(({ res }) => { - expect(res.headers['content-type']).toEqual('application/javascript'); - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles HEAD request to live bundle', async () => { + const { headers, statusCode } = await req.head( + '/__webpack_dev_server__/live.bundle.js' + ); + + expect(headers['content-type']).toEqual('application/javascript'); + expect(statusCode).toEqual(200); }); - it('should handles GET request to sockjs bundle', (done) => { - req.get('/__webpack_dev_server__/sockjs.bundle.js').then(({ res }) => { - expect(res.headers['content-type']).toEqual('application/javascript'); - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles GET request to sockjs bundle', async () => { + const { headers, statusCode } = await req.get( + '/__webpack_dev_server__/sockjs.bundle.js' + ); + + expect(headers['content-type']).toEqual('application/javascript'); + expect(statusCode).toEqual(200); }); - it('should handles HEAD request to sockjs bundle', (done) => { - req.head('/__webpack_dev_server__/sockjs.bundle.js').then(({ res }) => { - expect(res.headers['content-type']).toEqual('application/javascript'); - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles HEAD request to sockjs bundle', async () => { + const { headers, statusCode } = await req.head( + '/__webpack_dev_server__/sockjs.bundle.js' + ); + + expect(headers['content-type']).toEqual('application/javascript'); + expect(statusCode).toEqual(200); }); - it('should handles GET request to inline bundle', (done) => { - req.get('/webpack-dev-server.js').then(({ res }) => { - expect(res.headers['content-type']).toEqual('application/javascript'); - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles GET request to inline bundle', async () => { + const { headers, statusCode } = await req.get('/webpack-dev-server.js'); + + expect(headers['content-type']).toEqual('application/javascript'); + expect(statusCode).toEqual(200); }); - it('should handles HEAD request to inline bundle', (done) => { - req.head('/webpack-dev-server.js').then(({ res }) => { - expect(res.headers['content-type']).toEqual('application/javascript'); - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles HEAD request to inline bundle', async () => { + const { headers, statusCode } = await req.head('/webpack-dev-server.js'); + + expect(headers['content-type']).toEqual('application/javascript'); + expect(statusCode).toEqual(200); }); - it('should handles GET request to live html', (done) => { - req.get('/webpack-dev-server/').then(({ res }) => { - expect(res.headers['content-type']).toEqual('text/html'); - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles GET request to live html', async () => { + const { headers, statusCode } = await req.get('/webpack-dev-server/'); + + expect(headers['content-type']).toEqual('text/html'); + expect(statusCode).toEqual(200); }); - it('should handles HEAD request to live html', (done) => { - req.head('/webpack-dev-server/').then(({ res }) => { - expect(res.headers['content-type']).toEqual('text/html'); - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles HEAD request to live html', async () => { + const { headers, statusCode } = await req.head('/webpack-dev-server/'); + + expect(headers['content-type']).toEqual('text/html'); + expect(statusCode).toEqual(200); }); - it('should handles GET request to directory index', (done) => { - req.get('/webpack-dev-server').then(({ res }) => { - expect(res.headers['content-type']).toEqual('text/html'); - expect(res.statusCode).toEqual(200); - expect(res.text).toMatchSnapshot(); - done(); - }); + it('should handles GET request to directory index', async () => { + const { headers, statusCode, text } = await req.get('/webpack-dev-server'); + + expect(headers['content-type']).toEqual('text/html'); + expect(statusCode).toEqual(200); + expect(text).toMatchSnapshot(); }); - it('should handles HEAD request to directory index', (done) => { - req.head('/webpack-dev-server').then(({ res }) => { - expect(res.headers['content-type']).toEqual('text/html'); - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles HEAD request to directory index', async () => { + const { headers, statusCode } = await req.head('/webpack-dev-server'); + + expect(headers['content-type']).toEqual('text/html'); + expect(statusCode).toEqual(200); }); - it('should handles GET request to magic html', (done) => { - req.get('/main').then(({ res }) => { - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles GET request to magic html', async () => { + const { statusCode } = await req.get('/main'); + + expect(statusCode).toEqual(200); }); - it('should handles HEAD request to magic html', (done) => { - req.head('/main').then(({ res }) => { - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles HEAD request to magic html', async () => { + const { statusCode } = await req.head('/main'); + + expect(statusCode).toEqual(200); }); - it('should handles GET request to main chunk', (done) => { - req.get('/main.js').then(({ res }) => { - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles GET request to main chunk', async () => { + const { statusCode } = await req.get('/main.js'); + + expect(statusCode).toEqual(200); }); - it('should handles HEAD request to main chunk', (done) => { - req.head('/main.js').then(({ res }) => { - expect(res.statusCode).toEqual(200); - done(); - }); + it('should handles HEAD request to main chunk', async () => { + const { statusCode } = await req.head('/main.js'); + + expect(statusCode).toEqual(200); }); }); diff --git a/test/server/utils/runOpen.test.js b/test/server/utils/runOpen.test.js index e6fa3b155a..1ae6b092d1 100644 --- a/test/server/utils/runOpen.test.js +++ b/test/server/utils/runOpen.test.js @@ -1,23 +1,24 @@ 'use strict'; -const opn = require('opn'); +const open = require('open'); const runOpen = require('../../../lib/utils/runOpen'); -jest.mock('opn'); +jest.mock('open'); describe('runOpen util', () => { afterEach(() => { - opn.mockClear(); + open.mockClear(); }); describe('should open browser', () => { beforeEach(() => { - opn.mockImplementation(() => Promise.resolve()); + open.mockImplementation(() => Promise.resolve()); }); - it('on specify URL', () => { - return runOpen('https://example.com', {}, console).then(() => { - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + it('on specify URL', async () => { + await runOpen('https://example.com', {}, console); + + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "https://example.com", Object { @@ -25,16 +26,16 @@ describe('runOpen util', () => { }, ] `); - }); }); - it('on specify URL with page', () => { - return runOpen( + it('on specify URL with page', async () => { + await runOpen( 'https://example.com', { openPage: '/index.html' }, console - ).then(() => { - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + ); + + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "https://example.com/index.html", Object { @@ -42,16 +43,12 @@ describe('runOpen util', () => { }, ] `); - }); }); - it('on specify URL in Google Chrome', () => { - return runOpen( - 'https://example.com', - { open: 'Google Chrome' }, - console - ).then(() => { - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + it('on specify URL in Google Chrome', async () => { + await runOpen('https://example.com', { open: 'Google Chrome' }, console); + + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "https://example.com", Object { @@ -60,16 +57,16 @@ describe('runOpen util', () => { }, ] `); - }); }); - it('on specify URL with page in Google Chrome ', () => { - return runOpen( + it('on specify URL with page in Google Chrome ', async () => { + await runOpen( 'https://example.com', { open: 'Google Chrome', openPage: '/index.html' }, console - ).then(() => { - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + ); + + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "https://example.com/index.html", Object { @@ -78,16 +75,17 @@ describe('runOpen util', () => { }, ] `); - }); }); + }); - it('on specify absolute https URL with page in Google Chrome ', () => { - return runOpen( - 'https://example.com', - { open: 'Google Chrome', openPage: 'https://example2.com' }, - console - ).then(() => { - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + it('on specify absolute https URL with page in Google Chrome ', async () => { + await runOpen( + 'https://example.com', + { open: 'Google Chrome', openPage: 'https://example2.com' }, + console + ); + + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "https://example2.com", Object { @@ -96,16 +94,16 @@ describe('runOpen util', () => { }, ] `); - }); - }); + }); - it('on specify absolute http URL with page in Google Chrome ', () => { - return runOpen( - 'https://example.com', - { open: 'Google Chrome', openPage: 'http://example2.com' }, - console - ).then(() => { - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + it('on specify absolute http URL with page in Google Chrome ', async () => { + runOpen( + 'https://example.com', + { open: 'Google Chrome', openPage: 'http://example2.com' }, + console + ); + + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "http://example2.com", Object { @@ -114,27 +112,26 @@ describe('runOpen util', () => { }, ] `); - }); - }); }); describe('should not open browser', () => { const logMock = { warn: jest.fn() }; beforeEach(() => { - opn.mockImplementation(() => Promise.reject()); + open.mockImplementation(() => Promise.reject()); }); afterEach(() => { logMock.warn.mockClear(); }); - it('on specify URL and log error', () => { - return runOpen('https://example.com', {}, logMock).then(() => { - expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot( - `"Unable to open browser. If you are running in a headless environment, please do not use the --open flag"` - ); - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + it('on specify URL and log error', async () => { + await runOpen('https://example.com', {}, logMock); + + expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Unable to open browser. If you are running in a headless environment, please do not use the --open flag"` + ); + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "https://example.com", Object { @@ -142,19 +139,19 @@ describe('runOpen util', () => { }, ] `); - }); }); - it('on specify URL with page and log error', () => { - return runOpen( + it('on specify URL with page and log error', async () => { + await runOpen( 'https://example.com', { openPage: '/index.html' }, logMock - ).then(() => { - expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot( - `"Unable to open browser. If you are running in a headless environment, please do not use the --open flag"` - ); - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + ); + + expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Unable to open browser. If you are running in a headless environment, please do not use the --open flag"` + ); + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "https://example.com/index.html", Object { @@ -162,19 +159,15 @@ describe('runOpen util', () => { }, ] `); - }); }); - it('on specify URL in Google Chrome and log error', () => { - return runOpen( - 'https://example.com', - { open: 'Google Chrome' }, - logMock - ).then(() => { - expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot( - `"Unable to open browser: Google Chrome. If you are running in a headless environment, please do not use the --open flag"` - ); - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + it('on specify URL in Google Chrome and log error', async () => { + await runOpen('https://example.com', { open: 'Google Chrome' }, logMock); + + expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Unable to open browser: Google Chrome. If you are running in a headless environment, please do not use the --open flag"` + ); + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "https://example.com", Object { @@ -183,19 +176,19 @@ describe('runOpen util', () => { }, ] `); - }); }); - it('on specify URL with page in Google Chrome and log error ', () => { - return runOpen( + it('on specify URL with page in Google Chrome and log error ', async () => { + await runOpen( 'https://example.com', { open: 'Google Chrome', openPage: '/index.html' }, logMock - ).then(() => { - expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot( - `"Unable to open browser: Google Chrome. If you are running in a headless environment, please do not use the --open flag"` - ); - expect(opn.mock.calls[0]).toMatchInlineSnapshot(` + ); + + expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Unable to open browser: Google Chrome. If you are running in a headless environment, please do not use the --open flag"` + ); + expect(open.mock.calls[0]).toMatchInlineSnapshot(` Array [ "https://example.com/index.html", Object { @@ -204,7 +197,6 @@ describe('runOpen util', () => { }, ] `); - }); }); }); }); diff --git a/test/server/utils/startUnixSocket.test.js b/test/server/utils/startUnixSocket.test.js new file mode 100644 index 0000000000..e0ab7b899a --- /dev/null +++ b/test/server/utils/startUnixSocket.test.js @@ -0,0 +1,124 @@ +'use strict'; + +const net = require('net'); +const fs = require('fs'); +const path = require('path'); +const TestUnixSocket = require('../../helpers/test-unix-socket'); +const { skipTestOnWindows } = require('../../helpers/conditional-test'); +const startUnixSocket = require('../../../lib/utils/startUnixSocket'); + +describe('startUnixSocket', () => { + const socketPath = path.join('.', 'startUnixSocket.webpack.sock'); + let testUnixSocket = null; + + describe('path to a non-existent file', () => { + let err; + beforeAll((done) => { + testUnixSocket = new TestUnixSocket(); + startUnixSocket(testUnixSocket.server, socketPath, (e) => { + err = e; + done(); + }); + }); + + it('should work as Unix socket or error on windows', (done) => { + if (process.platform === 'win32') { + expect(err.code).toEqual('EACCES'); + done(); + } else { + const clientSocket = new net.Socket(); + clientSocket.connect({ path: socketPath }, () => { + // this means the connection was made successfully + expect(true).toBeTruthy(); + done(); + }); + } + }); + + afterAll((done) => { + testUnixSocket.close(() => { + fs.unlink(socketPath, done); + }); + }); + }); + + describe('path to existent, unused file', () => { + if (skipTestOnWindows('Unix sockets are not supported on Windows')) { + return; + } + + beforeAll((done) => { + fs.writeFileSync(socketPath, ''); + testUnixSocket = new TestUnixSocket(); + startUnixSocket(testUnixSocket.server, socketPath, done); + }); + + it('should work as Unix socket', (done) => { + const clientSocket = new net.Socket(); + clientSocket.connect({ path: socketPath }, () => { + // this means the connection was made successfully + expect(true).toBeTruthy(); + done(); + }); + }); + + afterAll((done) => { + testUnixSocket.close(() => { + fs.unlink(socketPath, done); + }); + }); + }); + + describe('path to existent file with listening server', () => { + if (skipTestOnWindows('Unix sockets are not supported on Windows')) { + return; + } + + let dummyUnixSocket; + beforeAll((done) => { + dummyUnixSocket = new TestUnixSocket(); + dummyUnixSocket.server.listen(socketPath, 511, () => { + done(); + }); + }); + + it('should throw already used error', (done) => { + testUnixSocket = new TestUnixSocket(); + startUnixSocket(testUnixSocket.server, socketPath, (err) => { + expect(err).not.toBeNull(); + expect(err).not.toBeUndefined(); + expect(err.message).toEqual('This socket is already used'); + testUnixSocket.close(done); + }); + }); + + afterAll((done) => { + dummyUnixSocket.close(() => { + fs.unlink(socketPath, done); + }); + }); + }); + + describe('path to existent, unused file', () => { + beforeAll(() => { + fs.writeFileSync(socketPath, ''); + testUnixSocket = new TestUnixSocket(); + }); + + // this test is significant because previously the callback was called + // twice if a file at the given socket path already existed, but + // could be removed + it('should only call server.listen callback once', (done) => { + const userCallback = jest.fn(); + startUnixSocket(testUnixSocket.server, socketPath, userCallback); + setTimeout(() => { + expect(userCallback).toBeCalledTimes(1); + done(); + }, 3000); + }); + + afterAll((done) => { + testUnixSocket.close(done); + }); + }); +});