From 251411ac3c7e52ee224e359a1c2c6d8be4f5f5a1 Mon Sep 17 00:00:00 2001 From: Bugra Date: Tue, 3 May 2022 14:40:41 +0300 Subject: [PATCH 1/5] fix: can't start parse-dashboard with options like --port, --config. --- Parse-Dashboard/index.js | 45 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/Parse-Dashboard/index.js b/Parse-Dashboard/index.js index 2363f685b..a12389cad 100644 --- a/Parse-Dashboard/index.js +++ b/Parse-Dashboard/index.js @@ -33,8 +33,9 @@ program.option('--createUser', 'helper tool to allow you to generate secure user program.option('--createMFA', 'helper tool to allow you to generate multi-factor authentication secrets.'); program.parse(process.argv); +const options = program.opts(); -for (const key in program) { +for (const key in options) { const func = CLIHelper[key]; if (func && typeof func === 'function') { func(); @@ -42,31 +43,31 @@ for (const key in program) { } } -const host = program.host || process.env.HOST || '0.0.0.0'; -const port = program.port || process.env.PORT || 4040; -const mountPath = program.mountPath || process.env.MOUNT_PATH || '/'; -const allowInsecureHTTP = program.allowInsecureHTTP || process.env.PARSE_DASHBOARD_ALLOW_INSECURE_HTTP; -const cookieSessionSecret = program.cookieSessionSecret || process.env.PARSE_DASHBOARD_COOKIE_SESSION_SECRET; -const trustProxy = program.trustProxy || process.env.PARSE_DASHBOARD_TRUST_PROXY; -const dev = program.dev; +const host = options.host || process.env.HOST || '0.0.0.0'; +const port = options.port || process.env.PORT || 4040; +const mountPath = options.mountPath || process.env.MOUNT_PATH || '/'; +const allowInsecureHTTP = options.allowInsecureHTTP || process.env.PARSE_DASHBOARD_ALLOW_INSECURE_HTTP; +const cookieSessionSecret = options.cookieSessionSecret || process.env.PARSE_DASHBOARD_COOKIE_SESSION_SECRET; +const trustProxy = options.trustProxy || process.env.PARSE_DASHBOARD_TRUST_PROXY; +const dev = options.dev; if (trustProxy && allowInsecureHTTP) { console.log('Set only trustProxy *or* allowInsecureHTTP, not both. Only one is needed to handle being behind a proxy.'); process.exit(-1); } -let explicitConfigFileProvided = !!program.config; +let explicitConfigFileProvided = !!options.config; let configFile = null; let configFromCLI = null; -let configServerURL = program.serverURL || process.env.PARSE_DASHBOARD_SERVER_URL; -let configGraphQLServerURL = program.graphQLServerURL || process.env.PARSE_DASHBOARD_GRAPHQL_SERVER_URL; -let configMasterKey = program.masterKey || process.env.PARSE_DASHBOARD_MASTER_KEY; -let configAppId = program.appId || process.env.PARSE_DASHBOARD_APP_ID; -let configAppName = program.appName || process.env.PARSE_DASHBOARD_APP_NAME; -let configUserId = program.userId || process.env.PARSE_DASHBOARD_USER_ID; -let configUserPassword = program.userPassword || process.env.PARSE_DASHBOARD_USER_PASSWORD; -let configSSLKey = program.sslKey || process.env.PARSE_DASHBOARD_SSL_KEY; -let configSSLCert = program.sslCert || process.env.PARSE_DASHBOARD_SSL_CERT; +let configServerURL = options.serverURL || process.env.PARSE_DASHBOARD_SERVER_URL; +let configGraphQLServerURL = options.graphQLServerURL || process.env.PARSE_DASHBOARD_GRAPHQL_SERVER_URL; +let configMasterKey = options.masterKey || process.env.PARSE_DASHBOARD_MASTER_KEY; +let configAppId = options.appId || process.env.PARSE_DASHBOARD_APP_ID; +let configAppName = options.appName || process.env.PARSE_DASHBOARD_APP_NAME; +let configUserId = options.userId || process.env.PARSE_DASHBOARD_USER_ID; +let configUserPassword = options.userPassword || process.env.PARSE_DASHBOARD_USER_PASSWORD; +let configSSLKey = options.sslKey || process.env.PARSE_DASHBOARD_SSL_KEY; +let configSSLCert = options.sslCert || process.env.PARSE_DASHBOARD_SSL_CERT; function handleSIGs(server) { const signals = { @@ -86,7 +87,7 @@ function handleSIGs(server) { }); } -if (!program.config && !process.env.PARSE_DASHBOARD_CONFIG) { +if (!options.config && !process.env.PARSE_DASHBOARD_CONFIG) { if (configServerURL && configMasterKey && configAppId) { configFromCLI = { data: { @@ -114,13 +115,13 @@ if (!program.config && !process.env.PARSE_DASHBOARD_CONFIG) { } else if (!configServerURL && !configMasterKey && !configAppName) { configFile = path.join(__dirname, 'parse-dashboard-config.json'); } -} else if (!program.config && process.env.PARSE_DASHBOARD_CONFIG) { +} else if (!options.config && process.env.PARSE_DASHBOARD_CONFIG) { configFromCLI = { data: JSON.parse(process.env.PARSE_DASHBOARD_CONFIG) }; } else { - configFile = program.config; - if (program.appId || program.serverURL || program.masterKey || program.appName || program.graphQLServerURL) { + configFile = options.config; + if (options.appId || options.serverURL || options.masterKey || options.appName || options.graphQLServerURL) { console.log('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); process.exit(3); } From 5f369e401806218196c58c34ede03e4107ee43ed Mon Sep 17 00:00:00 2001 From: Bugra <2475497+bugra9@users.noreply.github.com> Date: Sat, 7 May 2022 10:46:09 +0300 Subject: [PATCH 2/5] Add test to ensure port, config, appId, serverURL, masterKey, appName, graphQLServerURL options are not ignored. --- Parse-Dashboard/tests/.eslintrc.json | 8 ++++ Parse-Dashboard/tests/index.test.js | 65 ++++++++++++++++++++++++++++ package.json | 3 +- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 Parse-Dashboard/tests/.eslintrc.json create mode 100644 Parse-Dashboard/tests/index.test.js diff --git a/Parse-Dashboard/tests/.eslintrc.json b/Parse-Dashboard/tests/.eslintrc.json new file mode 100644 index 000000000..6fcba0c31 --- /dev/null +++ b/Parse-Dashboard/tests/.eslintrc.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + "jest" + ], + "env": { + "jest/globals": true + } +} \ No newline at end of file diff --git a/Parse-Dashboard/tests/index.test.js b/Parse-Dashboard/tests/index.test.js new file mode 100644 index 000000000..3b41700b1 --- /dev/null +++ b/Parse-Dashboard/tests/index.test.js @@ -0,0 +1,65 @@ +const path = require('path'); +const spawn = require('child_process').spawn; + +const TIMEOUT = 1000; // ms + +describe('port, config, appId, serverURL, masterKey, appName, graphQLServerURL options should not be ignored.', () => { + it('Should start with port 4041.', async () => { + const result = await startParseDashboardAndGetOutput(['--port', '4041']); + + expect(result).toContain('The dashboard is now available at http://0.0.0.0:4041/'); + }); + + it('Should return an error message if config and appId options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--appId', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); + + it('Should return an error message if config and serverURL options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--serverURL', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); + + it('Should return an error message if config and masterKey options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--masterKey', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); + + it('Should return an error message if config and appName options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--appName', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); + + it('Should return an error message if config and graphQLServerURL options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--graphQLServerURL', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); +}); + +function startParseDashboardAndGetOutput(args) { + return new Promise((resolve) => { + const indexFilePath = path.resolve('./Parse-Dashboard/index.js'); + const child = spawn('node', [indexFilePath, ...args], { cwd: '.', timeout: TIMEOUT, killSignal: 'SIGINT' }); + + let output = ''; + child.on('error', () => { resolve(output); }); + child.on('close', () => { resolve(output); }); + + if (child.stdout) { + child.stdout.on('data', data => { + output += `STDOUT: ${data}\n`; + }); + } + + if (child.stderr) { + child.stderr.on('data', data => { + output += `STDERROR: ${data}\n`; + }); + } + }); +} diff --git a/package.json b/package.json index 26adff684..2ea3ba735 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,8 @@ "main": "Parse-Dashboard/app.js", "jest": { "roots": [ - "src/lib" + "src/lib", + "Parse-Dashboard" ], "transform": { ".*": "/testing/preprocessor.js" From 7a0ad514b0646da76caa16671935253509989f1d Mon Sep 17 00:00:00 2001 From: Bugra <2475497+bugra9@users.noreply.github.com> Date: Sat, 7 May 2022 13:17:31 +0300 Subject: [PATCH 3/5] fix: Failed Parse-Dashboard/tests/index.test.js tests for Node.js 12 --- Parse-Dashboard/tests/index.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Parse-Dashboard/tests/index.test.js b/Parse-Dashboard/tests/index.test.js index 3b41700b1..bdf340f7f 100644 --- a/Parse-Dashboard/tests/index.test.js +++ b/Parse-Dashboard/tests/index.test.js @@ -44,7 +44,8 @@ describe('port, config, appId, serverURL, masterKey, appName, graphQLServerURL o function startParseDashboardAndGetOutput(args) { return new Promise((resolve) => { const indexFilePath = path.resolve('./Parse-Dashboard/index.js'); - const child = spawn('node', [indexFilePath, ...args], { cwd: '.', timeout: TIMEOUT, killSignal: 'SIGINT' }); + const child = spawn('node', [indexFilePath, ...args], { cwd: '.', timeout: TIMEOUT }); + setTimeout(() => { child.kill(); }, TIMEOUT); // node.js 12 hack (spawn timeout option is not supported.) let output = ''; child.on('error', () => { resolve(output); }); From 0d56ece1cfea3b04665a11d972d091002e5acca5 Mon Sep 17 00:00:00 2001 From: Bugra <2475497+bugra9@users.noreply.github.com> Date: Sat, 7 May 2022 18:53:30 +0300 Subject: [PATCH 4/5] Move CLI tests to dashboard.e2e.test.js --- Parse-Dashboard/tests/.eslintrc.json | 8 --- Parse-Dashboard/tests/index.test.js | 66 ------------------------- package.json | 3 +- src/lib/tests/e2e/dashboard.e2e.test.js | 66 +++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 76 deletions(-) delete mode 100644 Parse-Dashboard/tests/.eslintrc.json delete mode 100644 Parse-Dashboard/tests/index.test.js diff --git a/Parse-Dashboard/tests/.eslintrc.json b/Parse-Dashboard/tests/.eslintrc.json deleted file mode 100644 index 6fcba0c31..000000000 --- a/Parse-Dashboard/tests/.eslintrc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "plugins": [ - "jest" - ], - "env": { - "jest/globals": true - } -} \ No newline at end of file diff --git a/Parse-Dashboard/tests/index.test.js b/Parse-Dashboard/tests/index.test.js deleted file mode 100644 index bdf340f7f..000000000 --- a/Parse-Dashboard/tests/index.test.js +++ /dev/null @@ -1,66 +0,0 @@ -const path = require('path'); -const spawn = require('child_process').spawn; - -const TIMEOUT = 1000; // ms - -describe('port, config, appId, serverURL, masterKey, appName, graphQLServerURL options should not be ignored.', () => { - it('Should start with port 4041.', async () => { - const result = await startParseDashboardAndGetOutput(['--port', '4041']); - - expect(result).toContain('The dashboard is now available at http://0.0.0.0:4041/'); - }); - - it('Should return an error message if config and appId options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--appId', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); - }); - - it('Should return an error message if config and serverURL options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--serverURL', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); - }); - - it('Should return an error message if config and masterKey options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--masterKey', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); - }); - - it('Should return an error message if config and appName options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--appName', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); - }); - - it('Should return an error message if config and graphQLServerURL options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--graphQLServerURL', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); - }); -}); - -function startParseDashboardAndGetOutput(args) { - return new Promise((resolve) => { - const indexFilePath = path.resolve('./Parse-Dashboard/index.js'); - const child = spawn('node', [indexFilePath, ...args], { cwd: '.', timeout: TIMEOUT }); - setTimeout(() => { child.kill(); }, TIMEOUT); // node.js 12 hack (spawn timeout option is not supported.) - - let output = ''; - child.on('error', () => { resolve(output); }); - child.on('close', () => { resolve(output); }); - - if (child.stdout) { - child.stdout.on('data', data => { - output += `STDOUT: ${data}\n`; - }); - } - - if (child.stderr) { - child.stderr.on('data', data => { - output += `STDERROR: ${data}\n`; - }); - } - }); -} diff --git a/package.json b/package.json index 8d4bb4828..3cda5ccfd 100644 --- a/package.json +++ b/package.json @@ -143,8 +143,7 @@ "main": "Parse-Dashboard/app.js", "jest": { "roots": [ - "src/lib", - "Parse-Dashboard" + "src/lib" ], "transform": { ".*": "/testing/preprocessor.js" diff --git a/src/lib/tests/e2e/dashboard.e2e.test.js b/src/lib/tests/e2e/dashboard.e2e.test.js index b7580f13a..d1b6688f7 100644 --- a/src/lib/tests/e2e/dashboard.e2e.test.js +++ b/src/lib/tests/e2e/dashboard.e2e.test.js @@ -9,6 +9,8 @@ jest.disableAutomock(); const express = require('express'); +const path = require('path'); +const spawn = require('child_process').spawn; const ParseDashboard = require('../../../../Parse-Dashboard/app'); const puppeteer = require('puppeteer'); @@ -46,3 +48,67 @@ describe('dashboard e2e', () => { server.close(); }); }); + + +describe('port, config, appId, serverURL, masterKey, appName, graphQLServerURL options should not be ignored.', () => { + it('Should start with port 4041.', async () => { + const result = await startParseDashboardAndGetOutput(['--port', '4041']); + + expect(result).toContain('The dashboard is now available at http://0.0.0.0:4041/'); + }); + + it('Should return an error message if config and appId options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--appId', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); + + it('Should return an error message if config and serverURL options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--serverURL', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); + + it('Should return an error message if config and masterKey options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--masterKey', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); + + it('Should return an error message if config and appName options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--appName', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); + + it('Should return an error message if config and graphQLServerURL options are provided together.', async () => { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--graphQLServerURL', 'helloworld']); + + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + }); +}); + +function startParseDashboardAndGetOutput(args) { + const TIMEOUT = 1000; // ms + return new Promise((resolve) => { + const indexFilePath = path.resolve('./Parse-Dashboard/index.js'); + const child = spawn('node', [indexFilePath, ...args], { cwd: '.', timeout: TIMEOUT }); + setTimeout(() => { child.kill(); }, TIMEOUT); // node.js 12 hack (spawn timeout option is not supported.) + + let output = ''; + child.on('error', () => { resolve(output); }); + child.on('close', () => { resolve(output); }); + + if (child.stdout) { + child.stdout.on('data', data => { + output += `STDOUT: ${data}\n`; + }); + } + + if (child.stderr) { + child.stderr.on('data', data => { + output += `STDERROR: ${data}\n`; + }); + } + }); +} From aba50cafd2c93ffce950957449ab738cab559fb3 Mon Sep 17 00:00:00 2001 From: Bugra <2475497+bugra9@users.noreply.github.com> Date: Sat, 7 May 2022 22:22:00 +0300 Subject: [PATCH 5/5] refactor dashboard.e2e.test.js --- src/lib/tests/e2e/dashboard.e2e.test.js | 52 +++++++++---------------- 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/src/lib/tests/e2e/dashboard.e2e.test.js b/src/lib/tests/e2e/dashboard.e2e.test.js index d1b6688f7..e6eac80c7 100644 --- a/src/lib/tests/e2e/dashboard.e2e.test.js +++ b/src/lib/tests/e2e/dashboard.e2e.test.js @@ -50,50 +50,34 @@ describe('dashboard e2e', () => { }); -describe('port, config, appId, serverURL, masterKey, appName, graphQLServerURL options should not be ignored.', () => { - it('Should start with port 4041.', async () => { +describe('Config options', () => { + it('should start with port option', async () => { const result = await startParseDashboardAndGetOutput(['--port', '4041']); - expect(result).toContain('The dashboard is now available at http://0.0.0.0:4041/'); }); - it('Should return an error message if config and appId options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--appId', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); - }); - - it('Should return an error message if config and serverURL options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--serverURL', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); - }); - - it('Should return an error message if config and masterKey options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--masterKey', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); - }); - - it('Should return an error message if config and appName options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--appName', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); - }); - - it('Should return an error message if config and graphQLServerURL options are provided together.', async () => { - const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', '--graphQLServerURL', 'helloworld']); - - expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + it('should reject to start if config and other options are combined', async () => { + const args = [ + '--appId', + '--serverURL', + '--masterKey', + '--appName', + '--graphQLServerURL' + ]; + + for (const arg of args) { + const result = await startParseDashboardAndGetOutput(['--config', 'helloworld', arg, 'helloworld']); + expect(result).toContain('You must provide either a config file or other CLI options (appName, appId, masterKey, serverURL, and graphQLServerURL); not both.'); + } }); }); function startParseDashboardAndGetOutput(args) { - const TIMEOUT = 1000; // ms + const timeoutInMs = 1000; return new Promise((resolve) => { const indexFilePath = path.resolve('./Parse-Dashboard/index.js'); - const child = spawn('node', [indexFilePath, ...args], { cwd: '.', timeout: TIMEOUT }); - setTimeout(() => { child.kill(); }, TIMEOUT); // node.js 12 hack (spawn timeout option is not supported.) + const child = spawn('node', [indexFilePath, ...args], { cwd: '.', timeout: timeoutInMs }); + setTimeout(() => { child.kill(); }, timeoutInMs); // node.js 12 hack (spawn timeout option is not supported.) let output = ''; child.on('error', () => { resolve(output); });