diff --git a/config.js b/config.js index c42a1e4d2..a67273c61 100644 --- a/config.js +++ b/config.js @@ -119,7 +119,7 @@ const CONFIG = { //------------------------------------------------------------------------------ // Port to bind to. - PORT: process.env.TALK_PORT || process.env.PORT || '3000', + PORT: process.env.TALK_PORT || process.env.PORT || process.env.NODE_ENV === 'test' ? '3001' : '3000', // The URL for this Talk Instance as viewable from the outside. ROOT_URL: process.env.TALK_ROOT_URL || null, @@ -182,12 +182,13 @@ const CONFIG = { // CONFIG VALIDATION //============================================================================== -if (CONFIG.ROOT_URL_MOUNT_PATH && !CONFIG.ROOT_URL) { - throw new Error('TALK_ROOT_URL must be specified if TALK_ROOT_URL_MOUNT_PATH is set to TRUE'); -} - -if (process.env.NODE_ENV === 'test' && !CONFIG.ROOT_URL) { - CONFIG.ROOT_URL = 'http://localhost:3000'; +if (process.env.NODE_ENV === 'test') { + if (!CONFIG.ROOT_URL) { + CONFIG.ROOT_URL = 'http://localhost:3001'; + } + if (!CONFIG.STATIC_URL) { + CONFIG.STATIC_URI = 'http://localhost:3001'; + } } else if (!CONFIG.ROOT_URL) { throw new Error('TALK_ROOT_URL must be provided'); } diff --git a/nightwatch-local.conf.js b/nightwatch-local.conf.js index 3f6711dab..9fa67db35 100644 --- a/nightwatch-local.conf.js +++ b/nightwatch-local.conf.js @@ -1,42 +1,46 @@ +const { + ROOT_URL +} = require('./config'); + module.exports = { - 'src_folders': './test/e2e/specs/', - 'output_folder': './test/e2e/tests_output', - 'page_objects_path': './test/e2e/page_objects', - 'globals_path': './test/e2e/globals', - 'selenium': { - 'start_process': true, - 'server_path': 'node_modules/selenium-standalone/.selenium/selenium-server/3.5.3-server.jar', - 'log_path': './test/e2e/reports', - 'host': '127.0.0.1', - 'port': 6666, - 'cli_args': { + src_folders: './test/e2e/specs/', + output_folder: './test/e2e/tests_output', + page_objects_path: './test/e2e/page_objects', + globals_path: './test/e2e/globals', + selenium: { + start_process: true, + server_path: 'node_modules/selenium-standalone/.selenium/selenium-server/3.5.3-server.jar', + log_path: './test/e2e/reports', + host: '127.0.0.1', + port: 6666, + cli_args: { 'webdriver.chrome.driver': 'node_modules/selenium-standalone/.selenium/chromedriver/2.32-x64-chromedriver' } }, - 'test_settings': { - 'default': { - 'launch_url' : 'http://localhost:3000', - 'selenium_port': 6666, - 'selenium_host': 'localhost', - 'silent': true, - 'desiredCapabilities': { - 'browserName': 'chrome', - 'javascriptEnabled': true, - 'acceptSslCerts': true, - 'webStorageEnabled': true, - 'databaseEnabled': true, - 'applicationCacheEnabled': false, - 'nativeEvents': true + test_settings: { + default: { + launch_url: ROOT_URL, + selenium_port: 6666, + selenium_host: 'localhost', + silent: true, + desiredCapabilities: { + browserName: 'chrome', + javascriptEnabled: true, + acceptSslCerts: true, + webStorageEnabled: true, + databaseEnabled: true, + applicationCacheEnabled: false, + nativeEvents: true }, - 'screenshots' : { - 'enabled': true, - 'on_failure': true, - 'on_error': true, - 'path': './test/e2e/tests_output' + screenshots : { + enabled: true, + on_failure: true, + on_error: true, + path: './test/e2e/tests_output' }, }, - 'integration': { - 'launch_url': 'http://localhost:3000' + integration: { + launch_url: 'http://localhost:3000' } } }; diff --git a/nightwatch.conf.js b/nightwatch.conf.js index a64c0c2e5..f973b6d50 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -1,27 +1,29 @@ +const { + ROOT_URL +} = require('./config'); + const nightwatch_config = { src_folders: './test/e2e/specs/', output_folder: './test/e2e/tests_output', page_objects_path: './test/e2e/page_objects', + globals_path: './test/e2e/globals', selenium : { - 'start_process': false, - 'host': 'hub-cloud.browserstack.com', - 'port': 80 + start_process: false, + host: 'hub-cloud.browserstack.com', + port: 80 }, test_settings: { default: { - globals: { - waitForConditionTimeout: 5000, - }, - launch_url: process.env.TALK_ROOT_URL || 'http://localhost:3000', + launch_url: ROOT_URL, desiredCapabilities: { 'browserstack.user': process.env.BROWSERSTACK_USER || 'coralproject2', 'browserstack.key': process.env.BROWSERSTACK_KEY, 'browserstack.local': true, - 'browser': 'chrome', 'browserstack.debug': true, 'browserstack.networkLogs': true, + 'browser': 'chrome', } } } diff --git a/package.json b/package.json index 1f4a78671..46b6efd60 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "lint": "yamllint locales/*.yml && eslint --ext=.js --ext=.json bin/* .", "lint-fix": "yarn lint --fix", "jest-watch": "TEST_MODE=unit NODE_ENV=test jest --watch", - "e2e": "./scripts/e2e.js", + "e2e": "NODE_ENV=test ./scripts/e2e.js", "pree2e-local": "./scripts/e2e-local.sh", "e2e-local": "nightwatch --config nightwatch-local.conf.js", "test": "TEST_MODE=unit NODE_ENV=test jest && TEST_MODE=unit NODE_ENV=test mocha -R ${MOCHA_REPORTER:-spec}", diff --git a/scripts/e2e-serve.js b/scripts/e2e-serve.js new file mode 100644 index 000000000..2b94da9f7 --- /dev/null +++ b/scripts/e2e-serve.js @@ -0,0 +1,169 @@ +#!/usr/bin/env node + +const debug = require('debug')('talk:e2e:serve'); +const app = require('../app'); +const errors = require('../errors'); +const {createServer} = require('http'); +const scraper = require('../services/scraper'); +const mailer = require('../services/mailer'); +const SetupService = require('../services/setup'); +const kue = require('../services/kue'); +const mongoose = require('../services/mongoose'); +const util = require('../bin/util'); +const cache = require('../services/cache'); +const MigrationService = require('../services/migration'); +const {createSubscriptionManager} = require('../graph/subscriptions'); +const { + PORT +} = require('../config'); + +/** +* Get port from environment and store in Express. +*/ + +const port = normalizePort(PORT); +app.set('port', port); + +/** +* Create HTTP server. +*/ +const server = createServer(app); + +/** + * Event listener for HTTP server "error" event. + */ +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + let bind = typeof port === 'string' + ? `Pipe ${port}` + : `Port ${port}`; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(`${bind} requires elevated privileges`); + break; + case 'EADDRINUSE': + console.error(`${bind} is already in use`); + break; + } + + throw error; +} + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + let port = parseInt(val, 10); + + if (isNaN(port)) { + + // named pipe + return val; + } + + if (port >= 0) { + + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "listening" event. + */ + +async function onListening() { + + // Start the cache instance. + await cache.init(); + + let addr = server.address(); + let bind = typeof addr === 'string' + ? `pipe ${addr}` + : `port ${addr.port}`; + debug(`API Server Listening on ${bind}`); +} + +/** + * Start the app. + */ +async function serve() { + try { + + // Check to see if the application is installed. If the application + // has been installed, then it will throw errors.ErrSettingsNotInit, this + // just means we don't have to check that the migrations have run. + await SetupService.isAvailable(); + + debug('setup is currently available, migrations not being checked'); + + } catch (e) { + + // Check the error. + switch (e) { + case errors.ErrInstallLock, errors.ErrSettingsInit: + + debug('setup is not currently available, migrations now being checked'); + + // The error was expected, just continue. + break; + default: + + // The error was not expected, throw the error! + throw e; + } + + // Now try and check the migration status. + try { + + // Verify that the minimum migration version is met. + await MigrationService.verify(); + + } catch (e) { + console.error(e); + process.exit(1); + } + } + + /** + * Listen on provided port, on all network interfaces. + */ + server.on('error', onError); + server.on('listening', onListening); + server.on('listening', () => { + + }); + server.listen(port, () => { + + // Mount the websocket server if requested. + debug(`Websocket Server Listening on ${port}`); + + // Mount the subscriptions server on the application server. + createSubscriptionManager(server); + }); + + // Start the scraper processor. + scraper.process(); + + // Start the mail processor. + mailer.process(); + + // Define a safe shutdown function to call in the event we need to shutdown + // because the node hooks are below which will interrupt the shutdown process. + // Shutdown the mongoose connection, the app server, and the scraper. + util.onshutdown([ + () => kue.Task.shutdown(), + () => mongoose.disconnect(), + () => server.close() + ]); +} + +module.exports = serve; diff --git a/scripts/e2e.js b/scripts/e2e.js index 3b36da8d6..103cd08fe 100755 --- a/scripts/e2e.js +++ b/scripts/e2e.js @@ -2,40 +2,50 @@ const Nightwatch = require('nightwatch'); const browserstack = require('browserstack-local'); +const {onshutdown} = require('../bin/util'); -try { - const bs_local = new browserstack.Local(); - process.mainModule.filename = './node_modules/.bin/nightwatch'; +async function start() { + try { + const bs_local = new browserstack.Local(); + process.mainModule.filename = './node_modules/.bin/nightwatch'; - // Code to start browserstack local before start of test - console.log('Connecting local'); - Nightwatch.bs_local = bs_local; - bs_local.start({ - 'key': process.env.BROWSERSTACK_KEY, - 'logFile': './test/e2e/bslocal.log' - }, function(error) { - if (error) { - console.error(error); - throw error; - } + // Code to start browserstack local before start of test + console.log('Connecting local'); + Nightwatch.bs_local = bs_local; - console.log('Connected. Now testing...'); - Nightwatch.cli(function(argv) { - Nightwatch.CliRunner(argv) - .setup(null, function(){ + bs_local.start({ + 'key': process.env.BROWSERSTACK_KEY, + 'logFile': './test/e2e/bslocal.log' + }, function(error) { + if (error) { + console.error(error); + throw error; + } - // Code to stop browserstack local after end of parallel test - bs_local.stop(function(){}); - }) - .runTests(function(){ + console.log('Connected. Now testing...'); + Nightwatch.cli(function(argv) { + Nightwatch.CliRunner(argv) + .setup(null, function(){ - // Code to stop browserstack local after end of single test - bs_local.stop(function(){}); - }); + // Code to stop browserstack local after end of parallel test + bs_local.stop(function(){}); + }) + .runTests(function(){ + + // Code to stop browserstack local after end of single test + bs_local.stop(function(){}); + }); + }); }); - }); -} catch (ex) { - console.log('There was an error while starting the test runner:\n\n'); - process.stderr.write(`${ex.stack}\n`); - process.exit(2); + + onshutdown([ + () => bs_local.stop(function(){}), + ]); + } catch (ex) { + console.log('There was an error while starting the test runner:\n\n'); + process.stderr.write(`${ex.stack}\n`); + process.exit(2); + } } + +start(); diff --git a/test/e2e/globals.js b/test/e2e/globals.js index 4e8bbe698..a894ef66a 100644 --- a/test/e2e/globals.js +++ b/test/e2e/globals.js @@ -1,14 +1,24 @@ -const uuid = require('uuid'); -const {murmur3} = require('murmurhash-js'); -const rHash = murmur3(uuid.v4()); +const serve = require('../../scripts/e2e-serve'); +const mongoose = require('../../services/mongoose'); +const {shutdown} = require('../../bin/util'); module.exports = { + before: async (done) => { + await mongoose.connection.dropDatabase(); + await serve(); + done(); + }, + after: (done) => { + shutdown(); + done(); + }, waitForConditionTimeout: 5000, testData: { + admin: { + email: 'admin@test.com', + username: 'admin', + password: 'testtest', + }, organizationName: 'Coral', - email: `test_${rHash}@test.test`, - username: `test${rHash}`, - password: `testpassword${rHash}`, - domain: 'http://localhost:3000' } }; diff --git a/test/e2e/page_objects/adminPage.js b/test/e2e/page_objects/adminPage.js index 775cde026..c4fcb83c1 100644 --- a/test/e2e/page_objects/adminPage.js +++ b/test/e2e/page_objects/adminPage.js @@ -5,7 +5,7 @@ module.exports = { }, ready() { return this - .waitForElementVisible('body', 2000); + .waitForElementVisible('body'); }, }], elements: { diff --git a/test/e2e/page_objects/installPage.js b/test/e2e/page_objects/installPage.js index a81a17347..df28f051d 100644 --- a/test/e2e/page_objects/installPage.js +++ b/test/e2e/page_objects/installPage.js @@ -5,7 +5,7 @@ module.exports = { }, ready() { return this - .waitForElementVisible('body', 2000); + .waitForElementVisible('body'); } }], elements: { @@ -20,7 +20,7 @@ module.exports = { 'step3ConfirmPasswordInput': '.talk-install-step-3 #confirmPassword', 'step3saveButton': '.talk-install-step-3-save-button', 'step4': '.talk-install-step-4', - 'step4DomainInput': '.talk-install-step-4-permited-domains-input', + 'step4DomainInput': '.talk-install-step-4-permited-domains-input input', 'step4saveButton': '.talk-install-step-4-save-button', } }; diff --git a/test/e2e/specs/01_install_test.js b/test/e2e/specs/01_install_test.js index c03b6cc5d..c2e6b19c9 100644 --- a/test/e2e/specs/01_install_test.js +++ b/test/e2e/specs/01_install_test.js @@ -2,7 +2,7 @@ module.exports = { '@tags': ['install'], 'User goes to install': (client) => { const installPage = client.page.installPage(); - + installPage .navigate() .ready(); @@ -10,14 +10,14 @@ module.exports = { }, 'User clicks get started button': (client) => { const installPage = client.page.installPage(); - + installPage .waitForElementVisible('@getStartedButton') .click('@getStartedButton'); }, 'User should see step 2 - Add Organization Name': (client) => { const installPage = client.page.installPage(); - + installPage .waitForElementVisible('@step2'); }, @@ -33,38 +33,37 @@ module.exports = { }, 'User should see step 3 - Create your account': (client) => { const installPage = client.page.installPage(); - + installPage .waitForElementVisible('@step3'); }, 'User fills step 3': (client) => { const installPage = client.page.installPage(); - const {testData} = client.globals; + const {testData: {admin}} = client.globals; installPage - .setValue('@step3EmailInput', testData.email) - .setValue('@step3UsernameInput', testData.username) - .setValue('@step3PasswordInput', testData.password) - .setValue('@step3ConfirmPasswordInput', testData.password) + .setValue('@step3EmailInput', admin.email) + .setValue('@step3UsernameInput', admin.username) + .setValue('@step3PasswordInput', admin.password) + .setValue('@step3ConfirmPasswordInput', admin.password) .waitForElementVisible('@step3saveButton') .click('@step3saveButton'); }, 'User should see step 4 - Domain Whitelist': (client) => { const installPage = client.page.installPage(); - + installPage .waitForElementVisible('@step4'); }, 'User fills step 4': (client) => { const installPage = client.page.installPage(); - const testData = client.page.getTestData(); installPage .waitForElementVisible('@step4DomainInput') - .setValue('@step4DomainInput', testData.domain); + .setValue('@step4DomainInput', client.launchUrl); client.keys(client.Keys.ENTER); - + installPage .waitForElementVisible('@step4saveButton') .click('@step4saveButton'); diff --git a/test/e2e/specs/02_admin_test.js b/test/e2e/specs/02_admin_test.js index faa3a7108..b62f6b9e0 100644 --- a/test/e2e/specs/02_admin_test.js +++ b/test/e2e/specs/02_admin_test.js @@ -2,13 +2,14 @@ module.exports = { '@tags': ['admin', 'login'], 'Admin logs in': (client) => { const adminPage = client.page.adminPage(); - const {testData} = client.globals; - + const {testData: {admin}} = client.globals; + adminPage + .navigate() .waitForElementVisible('@loginLayout') .waitForElementVisible('@signInForm') - .setValue('@emailInput', testData.email) - .setValue('@passwordInput', testData.password) + .setValue('@emailInput', admin.email) + .setValue('@passwordInput', admin.password) .waitForElementVisible('@signInButton') .click('@signInButton'); diff --git a/test/e2e/specs/embedStream.js b/test/e2e/specs/03_embedStream.js similarity index 74% rename from test/e2e/specs/embedStream.js rename to test/e2e/specs/03_embedStream.js index 4486f2d9e..56d27300c 100644 --- a/test/e2e/specs/embedStream.js +++ b/test/e2e/specs/03_embedStream.js @@ -1,18 +1,13 @@ -const uuid = require('uuid'); -const {murmur3} = require('murmurhash-js'); module.exports = { 'Creates a new asset': (client) => { - const asset = `test@${murmur3(uuid.v4())}`; + const asset = 'newAssetTest'; const embedStream = client.page.embedStream(); embedStream .navigateToAsset(asset) .assert.title(asset) .getEmbedSection(); - - client - .end(); }, 'not logged in user clicks my profile tab': (client) => { @@ -27,8 +22,9 @@ module.exports = { profile .assert.visible('@notLoggedIn'); - - client - .end(); }, + + after: (client) => { + client.end(); + } };