From 7192e8cafcc83a66e233cab2f56111ea5986e816 Mon Sep 17 00:00:00 2001 From: Ramon Lamana Date: Tue, 26 Feb 2019 19:36:52 -0800 Subject: [PATCH] Add puppeteer for e2e browser code tests --- package-lock.json | 171 ++++++++++++++---- package.json | 1 + test/Client.test.js | 73 ++++++++ test/cli.test.js | 6 +- test/fixtures/client-config/foo.js | 3 + test/fixtures/client-config/index.html | 1 + test/fixtures/client-config/webpack.config.js | 11 ++ test/helpers/run-browser.js | 36 ++++ test/helpers/run-webpack-dev-server.js | 12 +- 9 files changed, 277 insertions(+), 37 deletions(-) create mode 100644 test/Client.test.js create mode 100644 test/fixtures/client-config/foo.js create mode 100644 test/fixtures/client-config/index.html create mode 100644 test/fixtures/client-config/webpack.config.js create mode 100644 test/helpers/run-browser.js diff --git a/package-lock.json b/package-lock.json index 15ac1c895f..048d8fb584 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1069,6 +1069,15 @@ "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", "dev": true }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, "ajv": { "version": "6.9.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz", @@ -3363,6 +3372,21 @@ "is-symbol": "^1.0.2" } }, + "es6-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", + "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, "es6-templates": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", @@ -3988,6 +4012,29 @@ } } }, + "extract-zip": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "dev": true, + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -4039,6 +4086,15 @@ "bser": "^2.0.0" } }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", @@ -4337,8 +4393,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -4356,13 +4411,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4375,18 +4428,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -4489,8 +4539,7 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -4500,7 +4549,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4513,20 +4561,17 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.3.5", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4543,7 +4588,6 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -4616,8 +4660,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -4627,7 +4670,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -4703,8 +4745,7 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -4734,7 +4775,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4752,7 +4792,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4791,13 +4830,11 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.0.3", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -5539,6 +5576,33 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "husky": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/husky/-/husky-1.3.1.tgz", @@ -7547,7 +7611,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -9229,6 +9293,12 @@ "sha.js": "^2.4.8" } }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -9488,6 +9558,12 @@ "ipaddr.js": "1.8.0" } }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "dev": true + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -9550,6 +9626,30 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "puppeteer": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.12.2.tgz", + "integrity": "sha512-xWSyCeD6EazGlfnQweMpM+Hs6X6PhUYhNTHKFj/axNZDq4OmrVERf70isBf7HsnFgB3zOC1+23/8+wCAZYg+Pg==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "extract-zip": "^1.6.6", + "https-proxy-agent": "^2.2.1", + "mime": "^2.0.3", + "progress": "^2.0.1", + "proxy-from-env": "^1.0.0", + "rimraf": "^2.6.1", + "ws": "^6.1.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + } + } + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -12024,6 +12124,15 @@ "camelcase": "^4.1.0" } }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "~1.0.1" + } + }, "yup": { "version": "0.26.10", "resolved": "https://registry.npmjs.org/yup/-/yup-0.26.10.tgz", diff --git a/package.json b/package.json index e903fe821d..3eabe24887 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "marked": "^0.6.1", "nyc": "^13.3.0", "prettier": "^1.16.3", + "puppeteer": "^1.12.2", "rimraf": "^2.6.2", "standard-version": "^5.0.0", "style-loader": "^0.23.1", diff --git a/test/Client.test.js b/test/Client.test.js new file mode 100644 index 0000000000..ed463698d5 --- /dev/null +++ b/test/Client.test.js @@ -0,0 +1,73 @@ +'use strict'; + +const path = require('path'); +const express = require('express'); +const httpProxy = require('http-proxy-middleware'); +const request = require('supertest'); +const runDevServer = require('./helpers/run-webpack-dev-server'); +const runBrowser = require('./helpers/run-browser'); + +const configPath = path.resolve( + __dirname, + './fixtures/client-config/webpack.config.js' +); + +function startProxy(port) { + const proxy = express(); + proxy.use( + '/', + httpProxy({ + target: 'http://0.0.0.0:8080', + ws: true, + changeOrigin: true, + }) + ); + return proxy.listen(port); +} + +describe('Client code', () => { + let server; + + beforeAll((done) => { + server = runDevServer('--host 0.0.0.0 --port 8080', configPath); + setTimeout(done, 5000); + }); + + afterAll(() => { + server.close(); + }); + + describe('behind a proxy', () => { + let proxy; + + jest.setTimeout(20000); + + beforeAll(() => { + proxy = startProxy(9090); + }); + + afterAll(() => { + proxy.close(); + }); + + it('responds with a 200', (done) => { + const req = request('http://localhost:9090'); + req.get('/sockjs-node').expect(200, 'Welcome to SockJS!\n', done); + }); + + it('requests websocket through the proxy with proper port number', (done) => { + runBrowser().then(({ page, browser }) => { + page + .waitForRequest((requestObj) => requestObj.url().match(/sockjs-node/)) + .then((requestObj) => { + expect(requestObj.url()).toMatch( + /^http:\/\/localhost:9090\/sockjs-node/ + ); + browser.close(); + done(); + }); + page.goto('http://localhost:9090/main'); + }); + }); + }); +}); diff --git a/test/cli.test.js b/test/cli.test.js index 1d71f3e041..7179b74f9b 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -19,7 +19,7 @@ const certPath = path.join(httpsCertificateDirectory, 'server.crt'); describe('CLI', () => { it('--progress', (done) => { runDevServer('--progress') - .then((output) => { + .onClose.then((output) => { expect(output.code).toEqual(0); expect(output.stderr.indexOf('0% compiling') >= 0).toBe(true); done(); @@ -39,7 +39,7 @@ describe('CLI', () => { it('--https', (done) => { runDevServer('--https') - .then((output) => { + .onClose.then((output) => { expect(output.code).toEqual(0); expect(output.stdout.indexOf('Project is running at') >= 0).toBe(true); done(); @@ -51,7 +51,7 @@ describe('CLI', () => { runDevServer( `--https --cacert ${caPath} --pfx ${pfxPath} --key ${keyPath} --cert ${certPath} --pfx-passphrase webpack-dev-server` ) - .then((output) => { + .onClose.then((output) => { expect(output.code).toEqual(0); expect(output.stdout.indexOf('Project is running at') >= 0).toBe(true); done(); diff --git a/test/fixtures/client-config/foo.js b/test/fixtures/client-config/foo.js new file mode 100644 index 0000000000..eab26534f3 --- /dev/null +++ b/test/fixtures/client-config/foo.js @@ -0,0 +1,3 @@ +'use strict'; + +console.log('Hey.'); diff --git a/test/fixtures/client-config/index.html b/test/fixtures/client-config/index.html new file mode 100644 index 0000000000..fdfcaae91f --- /dev/null +++ b/test/fixtures/client-config/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/client-config/webpack.config.js b/test/fixtures/client-config/webpack.config.js new file mode 100644 index 0000000000..50d700c09f --- /dev/null +++ b/test/fixtures/client-config/webpack.config.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = { + mode: 'development', + context: __dirname, + entry: './foo.js', + output: { + path: '/', + }, + node: false, +}; diff --git a/test/helpers/run-browser.js b/test/helpers/run-browser.js new file mode 100644 index 0000000000..5e825b4def --- /dev/null +++ b/test/helpers/run-browser.js @@ -0,0 +1,36 @@ +'use strict'; + +const puppeteer = require('puppeteer'); + +function runBrowser(config) { + const options = { + viewport: { + width: 500, + height: 500, + }, + userAgent: '', + ...config, + }; + + return new Promise((resolve, reject) => { + let page; + let browser; + + puppeteer + .launch({ + headless: true, + }) + .then((launchedBrowser) => { + browser = launchedBrowser; + return browser.newPage(); + }) + .then((newPage) => { + page = newPage; + page.emulate(options); + resolve({ page, browser }); + }) + .catch(reject); + }); +} + +module.exports = runBrowser; diff --git a/test/helpers/run-webpack-dev-server.js b/test/helpers/run-webpack-dev-server.js index 2c7b5e98ae..2e6a0d7a70 100644 --- a/test/helpers/run-webpack-dev-server.js +++ b/test/helpers/run-webpack-dev-server.js @@ -29,10 +29,9 @@ function runWebackDevServer(testArgs, configPath) { } const args = [webpackDevServerPath, '--config', configPath].concat(testArgs); + const child = execa('node', args, { cwd, env }); - return new Promise((resolve, reject) => { - const child = execa('node', args, { cwd, env }); - + const onClose = new Promise((resolve, reject) => { child.on('error', (error) => reject(error)); child.stdout.on('data', (data) => { @@ -50,6 +49,13 @@ function runWebackDevServer(testArgs, configPath) { resolve({ stdout, stderr, code }); }); }); + + return { + onClose, + close: () => { + child.kill('SIGINT'); + }, + }; } module.exports = runWebackDevServer;