Merge pull request #249 from coralproject/e2e-env-fix

E2E environment fix
This commit is contained in:
David Erwin
2017-01-30 12:14:50 -05:00
committed by GitHub
20 changed files with 169 additions and 74 deletions
+18
View File
@@ -7,6 +7,7 @@
const program = require('commander'); const program = require('commander');
const pkg = require('../package.json'); const pkg = require('../package.json');
const dotenv = require('dotenv'); const dotenv = require('dotenv');
const util = require('../util');
//============================================================================== //==============================================================================
// Setting up the program command line arguments. // Setting up the program command line arguments.
@@ -15,6 +16,7 @@ const dotenv = require('dotenv');
program program
.version(pkg.version) .version(pkg.version)
.option('-c, --config [path]', 'Specify the configuration file to load') .option('-c, --config [path]', 'Specify the configuration file to load')
.option('--pid [path]', 'Specify a path to output the current PID to')
.parse(process.argv); .parse(process.argv);
if (program.config) { if (program.config) {
@@ -27,6 +29,11 @@ if (program.config) {
} }
} }
// If the `--pid` flag is used, put the current pid there.
if (program.pid) {
util.pid(program.pid);
}
// Perform rewrites to the runtime environment variables based on the contents // Perform rewrites to the runtime environment variables based on the contents
// of the process.env.REWRITE_ENV if it exists. This is done here as it is the // of the process.env.REWRITE_ENV if it exists. This is done here as it is the
// entrypoint for the entire application. // entrypoint for the entire application.
@@ -43,4 +50,15 @@ program
// If there is no command listed, output help. // If there is no command listed, output help.
if (!process.argv.slice(2).length) { if (!process.argv.slice(2).length) {
program.outputHelp(); program.outputHelp();
return;
} }
// The ensures that the child process that is created here is always cleaned up
// properly when the parent process dies.
util.onshutdown([
(signal) => {
if ((program.runningCommand.killed === false) && (program.runningCommand.exitCode === null)) {
program.runningCommand.kill(signal);
}
}
]);
+2
View File
@@ -21,6 +21,8 @@ test:
override: override:
# Run the tests using the junit reporter. # Run the tests using the junit reporter.
- MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml NPM_PACKAGE_CONFIG_MOCHA_REPORTER=mocha-junit-reporter npm run test - MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml NPM_PACKAGE_CONFIG_MOCHA_REPORTER=mocha-junit-reporter npm run test
# Run the e2e test suite
- E2E_REPORT_PATH=$CIRCLE_TEST_REPORTS/e2e npm run e2e
deployment: deployment:
release: release:
@@ -8,10 +8,10 @@ import SuspendUserModal from './SuspendUserModal';
// Each action has different meaning and configuration // Each action has different meaning and configuration
const menuOptionsMap = { const menuOptionsMap = {
'reject': {status: 'rejected', icon: 'close', key: 'r'}, 'reject': {status: 'REJECTED', icon: 'close', key: 'r'},
'approve': {status: 'accepted', icon: 'done', key: 't'}, 'approve': {status: 'ACCEPTED', icon: 'done', key: 't'},
'flag': {status: 'flagged', icon: 'flag', filter: 'Untouched'}, 'flag': {status: 'FLAGGED', icon: 'flag', filter: 'Untouched'},
'ban': {status: 'banned', icon: 'not interested'} 'ban': {status: 'BANNED', icon: 'not interested'}
}; };
// Renders a comment list and allow performing actions // Renders a comment list and allow performing actions
@@ -135,9 +135,9 @@ export default class ModerationList extends React.Component {
} else if (action.item_type === 'USERS') { } else if (action.item_type === 'USERS') {
// If a user bio or name is rejected, bring up a dialog before suspending them. // If a user bio or name is rejected, bring up a dialog before suspending them.
if (menuOption === 'rejected') { if (menuOption === 'REJECTED') {
this.setState({suspendUserModal: action}); this.setState({suspendUserModal: action});
} else if (menuOption === 'accepted') { } else if (menuOption === 'ACCEPTED') {
this.props.userStatusUpdate('ACTIVE', action.item_id); this.props.userStatusUpdate('ACTIVE', action.item_id);
} }
} }
+1 -1
View File
@@ -87,7 +87,7 @@ class Comment extends React.Component {
return ( return (
<div <div
className="comment" className={parentId ? 'reply' : 'comment'}
id={`c_${comment.id}`} id={`c_${comment.id}`}
style={{marginLeft: depth * 30}}> style={{marginLeft: depth * 30}}>
<hr aria-hidden={true} /> <hr aria-hidden={true} />
@@ -2,6 +2,7 @@ fragment commentView on Comment {
id id
body body
created_at created_at
status
user { user {
id id
name: displayName name: displayName
@@ -75,8 +75,6 @@ class CommentBox extends Component {
} else if (postedComment.status === 'PREMOD') { } else if (postedComment.status === 'PREMOD') {
addNotification('success', lang.t('comment-post-notif-premod')); addNotification('success', lang.t('comment-post-notif-premod'));
} else { } else {
// appendItemArray(parent_id || id, related, commentId, !parent_id, parent_type);
addNotification('success', 'Your comment has been posted.'); addNotification('success', 'Your comment has been posted.');
} }
@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
const name = 'coral-plugin-replies'; const name = 'coral-plugin-content';
const Content = ({body, styles}) => { const Content = ({body, styles}) => {
const textbreaks = body.split('\n'); const textbreaks = body.split('\n');
+11 -10
View File
@@ -1,16 +1,21 @@
require('babel-core/register'); require('babel-core/register');
let E2E_REPORT_PATH = './test/e2e/reports';
if (process.env.E2E_REPORT_PATH && process.env.E2E_REPORT_PATH.length > 0) {
E2E_REPORT_PATH = process.env.E2E_REPORT_PATH;
}
module.exports = { module.exports = {
'src_folders': './tests/e2e/tests', 'src_folders': './test/e2e/tests',
'output_folder': './tests/e2e/reports', 'output_folder': E2E_REPORT_PATH,
'page_objects_path': './tests/e2e/pages', 'page_objects_path': './test/e2e/pages',
'globals_path': './tests/e2e/globals', 'globals_path': './test/e2e/globals',
'custom_commands_path' : '', 'custom_commands_path' : '',
'custom_assertions_path' : '', 'custom_assertions_path' : '',
'selenium': { 'selenium': {
'start_process': true, 'start_process': true,
'server_path': 'node_modules/selenium-standalone/.selenium/selenium-server/2.53.1-server.jar', 'server_path': 'node_modules/selenium-standalone/.selenium/selenium-server/2.53.1-server.jar',
'log_path': './tests/e2e/reports', 'log_path': E2E_REPORT_PATH,
'host': '127.0.0.1', 'host': '127.0.0.1',
'port': 6666, 'port': 6666,
'cli_args': { 'cli_args': {
@@ -36,7 +41,7 @@ module.exports = {
'enabled': true, 'enabled': true,
'on_failure': true, 'on_failure': true,
'on_error': true, 'on_error': true,
'path': './tests/e2e/reports' 'path': E2E_REPORT_PATH
}, },
'exclude': [ 'exclude': [
'./tests/e2e/tests/EmbedStreamTests.js' './tests/e2e/tests/EmbedStreamTests.js'
@@ -47,7 +52,3 @@ module.exports = {
} }
} }
}; };
// "chromeOptions" : {
// "args" : ["start-fullscreen"]
// }
+2 -1
View File
@@ -14,6 +14,7 @@
"test-cover": "TEST_MODE=unit NODE_ENV=test istanbul cover _mocha --report text --check-coverage -- -R spec", "test-cover": "TEST_MODE=unit NODE_ENV=test istanbul cover _mocha --report text --check-coverage -- -R spec",
"pree2e": "NODE_ENV=test scripts/pree2e.sh", "pree2e": "NODE_ENV=test scripts/pree2e.sh",
"e2e": "NODE_ENV=test nightwatch", "e2e": "NODE_ENV=test nightwatch",
"poste2e": "NODE_ENV=test scripts/poste2e.sh",
"embed-start": "NODE_ENV=development npm run build && ./bin/cli serve --jobs", "embed-start": "NODE_ENV=development npm run build && ./bin/cli serve --jobs",
"postinstall": "npm run build" "postinstall": "npm run build"
}, },
@@ -153,7 +154,7 @@
"redux-mock-store": "^1.2.1", "redux-mock-store": "^1.2.1",
"redux-thunk": "^2.1.0", "redux-thunk": "^2.1.0",
"regenerator": "^0.8.46", "regenerator": "^0.8.46",
"selenium-standalone": "latest", "selenium-standalone": "^5.11.2",
"style-loader": "^0.13.1", "style-loader": "^0.13.1",
"supertest": "^2.0.1", "supertest": "^2.0.1",
"timeago.js": "^2.0.3", "timeago.js": "^2.0.3",
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# If there is a PID file from the e2e tests...
if [ -e /tmp/talk-e2e.pid ]
then
# Then kill the running talk server.
kill $(cat /tmp/talk-e2e.pid)
fi
+9 -5
View File
@@ -1,15 +1,19 @@
#!/bin/bash #!/bin/bash
# install selenium # install selenium
selenium-standalone install selenium-standalone install --config=./selenium.config.js
#Init settings
./bin/cli settings init
# Creating Admin Test User # Creating Admin Test User
./bin/cli-users create --flag_mode --email "admin@test.com" --password "test" --name "Admin Test User" --role "admin" ./bin/cli users create --flag_mode --email "admin@test.com" --password "testtest" --name "AdminTestUser" --role "ADMIN"
# Creating Moderator Test User # Creating Moderator Test User
./bin/cli-users create --flag_mode --email "moderator@test.com" --password "test" --name "Moderator Test User" --role "moderator" ./bin/cli users create --flag_mode --email "moderator@test.com" --password "testtest" --name "ModeratorTestUser" --role "MODERATOR"
# Creating Commenter Test User # Creating Commenter Test User
./bin/cli-users create --flag_mode --email "commenter@test.com" --password "test" --name "commenter@test.com" ./bin/cli users create --flag_mode --email "commenter@test.com" --password "testtest" --name "CommenterTestUser"
npm start & # Start the server and write the PID to a file to be killed later.
./bin/cli --pid /tmp/talk-e2e.pid serve --jobs &
+9
View File
@@ -0,0 +1,9 @@
module.exports = {
drivers: {
chrome: {
version: '2.25',
arch: process.arch,
baseURL: 'https://chromedriver.storage.googleapis.com'
},
},
};
+3 -3
View File
@@ -4,15 +4,15 @@ module.exports = {
users: { users: {
admin: { admin: {
email: 'admin@test.com', email: 'admin@test.com',
pass: 'test' pass: 'testtest'
}, },
moderator: { moderator: {
email: 'moderator@test.com', email: 'moderator@test.com',
pass: 'test' pass: 'testtest'
}, },
commenter: { commenter: {
email: 'commenter@test.com', email: 'commenter@test.com',
pass: 'test' pass: 'testtest'
} }
}, },
}; };
+7 -6
View File
@@ -1,8 +1,8 @@
const Comments = require('../../models/comment'); const Comments = require('../../services/comments');
const Users = require('../../models/user'); const Users = require('../../services/users');
const Actions = require('../../models/action'); const Actions = require('../../services/actions');
const Assets = require('../../models/asset'); const Assets = require('../../services/assets');
const Settings = require('../../models/setting'); const Settings = require('../../services/settings');
const globals = require('./globals'); const globals = require('./globals');
/* Create an array of comments */ /* Create an array of comments */
@@ -22,4 +22,5 @@ module.exports.users = (users) => Users.createLocalUsers(users);
module.exports.actions = (actions) => Actions.create(actions); module.exports.actions = (actions) => Actions.create(actions);
/* Update a setting */ /* Update a setting */
module.exports.settings = (setting) => Settings.init().then(() => Settings.updateSettings(setting)); module.exports.settings = (setting) => Settings.init().then(() =>
Settings.update(setting));
+2 -1
View File
@@ -10,7 +10,8 @@ const embedStreamCommands = {
return this return this
.waitForElementVisible('@moderationList') .waitForElementVisible('@moderationList')
.waitForElementVisible('@approveButton') .waitForElementVisible('@approveButton')
.click('@approveButton'); .click('@approveButton')
.waitForElementNotPresent('@approveButton');
} }
}; };
+2 -2
View File
@@ -127,10 +127,10 @@ module.exports = {
selector: '.comment .coral-plugin-flags-popup' selector: '.comment .coral-plugin-flags-popup'
}, },
flagCommentOption: { flagCommentOption: {
selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-radio-label[for="comments"]' selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-radio-label[for="COMMENTS"]'
}, },
flagUsernameOption: { flagUsernameOption: {
selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-radio-label[for="user"]' selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-radio-label[for="USERS"]'
}, },
flagOtherOption: { flagOtherOption: {
selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-radio-label[for="other"]' selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-radio-label[for="other"]'
+12 -4
View File
@@ -1,10 +1,18 @@
const mocks = require('../mocks');
module.exports = { module.exports = {
'@tags': ['embedStream'], '@tags': ['embedStream'],
before: client => { before: client => {
const embedStreamPage = client.page.embedStreamPage(); client.perform((client, done) => {
embedStreamPage mocks.settings({moderation: 'PRE'})
.navigate() .then(() => {
.ready(); const embedStreamPage = client.page.embedStreamPage();
embedStreamPage
.navigate()
.ready();
done();
});
});
}, },
'Login as commenter': client => { 'Login as commenter': client => {
const embedStreamPage = client.page.embedStreamPage(); const embedStreamPage = client.page.embedStreamPage();
+18 -23
View File
@@ -1,12 +1,12 @@
const mongoose = require('../../helpers/mongoose'); const mongoose = require('../../helpers/mongoose');
const mocks = require('../mocks'); const mocks = require('../mocks');
const mockComment = 'This is a test comment.'; const mockComment = 'I read the comments';
const mockReply = 'This is a test reply'; const mockReply = 'This is a test reply';
const mockUser = { const mockUser = {
email: `${new Date().getTime()}@test.com`, email: `${new Date().getTime()}@test.com`,
name: 'Test User', name: 'testuser',
pw: 'testtesttest' pw: 'testtest'
}; };
module.exports = { module.exports = {
@@ -29,7 +29,6 @@ module.exports = {
.frame('coralStreamIframe') .frame('coralStreamIframe')
// Register and Log In // Register and Log In
.waitForElementVisible('#commentBox', 1000)
.waitForElementVisible('#coralSignInButton', 2000) .waitForElementVisible('#coralSignInButton', 2000)
.click('#coralSignInButton') .click('#coralSignInButton')
.waitForElementVisible('#coralRegister', 1000) .waitForElementVisible('#coralRegister', 1000)
@@ -47,10 +46,10 @@ module.exports = {
// Post a comment // Post a comment
.setValue('.coral-plugin-commentbox-textarea', mockComment) .setValue('.coral-plugin-commentbox-textarea', mockComment)
.click('.coral-plugin-commentbox-button') .click('.coral-plugin-commentbox-button')
.waitForElementVisible('.comment', 1000) .waitForElementVisible('.coral-plugin-content-text', 1000)
// Verify that it appears // Verify that it appears
.assert.containsText('.comment', mockComment); .assert.containsText('.coral-plugin-content-text', mockComment);
done(); done();
}) })
.catch((err) => { .catch((err) => {
@@ -104,8 +103,8 @@ module.exports = {
.click('.coral-plugin-replies-reply-button') .click('.coral-plugin-replies-reply-button')
.waitForElementVisible('#replyText') .waitForElementVisible('#replyText')
.setValue('#replyText', mockReply) .setValue('#replyText', mockReply)
.click('.coral-plugin-replies-textarea button') .click('.coral-plugin-replies-textarea .coral-plugin-commentbox-button')
.waitForElementVisible('.reply', 2000) .waitForElementVisible('.reply', 20000)
// Verify that it appears // Verify that it appears
.assert.containsText('.reply', mockReply); .assert.containsText('.reply', mockReply);
@@ -122,22 +121,18 @@ module.exports = {
mocks.settings({moderation: 'PRE'}) mocks.settings({moderation: 'PRE'})
// Add a mock user // Add a mock user
.then(() => { .then(() => mocks.users([{
return mocks.users([{ displayName: 'Baby Blue',
displayName: 'Baby Blue', email: 'whale@tale.sea',
email: 'whale@tale.sea', password: 'krill'
password: 'krill' }]))
}]);
})
// Add a mock preapproved comment by that user // Add a mock preapproved comment by that user
.then((user) => { .then((user) => mocks.comments([{
return mocks.comments([{ body: 'Whales are not fish.',
body: 'Whales are not fish.', status: 'accepted',
status: 'accepted', author_id: user.id
author_id: user.id }]))
}]);
})
.then(() => { .then(() => {
// Load Page // Load Page
@@ -170,7 +165,7 @@ module.exports = {
// Verify that comment count is correct // Verify that comment count is correct
client.waitForElementVisible('.coral-plugin-comment-count-text', 2000) client.waitForElementVisible('.coral-plugin-comment-count-text', 2000)
.assert.containsText('.coral-plugin-comment-count-text', '1 Comment'); .assert.containsText('.coral-plugin-comment-count-text', '4 Comments');
done(); done();
}); });
}, },
+2 -4
View File
@@ -1,5 +1,3 @@
const uuid = require('uuid');
module.exports = { module.exports = {
'@tags': ['signup', 'visitor'], '@tags': ['signup', 'visitor'],
before: client => { before: client => {
@@ -14,8 +12,8 @@ module.exports = {
embedStreamPage embedStreamPage
.signUp({ .signUp({
email: `visitor_${uuid.v4()}@test.com`, email: `visitor_${Date.now()}@test.com`,
displayName: 'Visitor', displayName: `visitor${Date.now()}`,
pass: 'testtest' pass: 'testtest'
}); });
}, },
+53 -5
View File
@@ -1,3 +1,6 @@
const debug = require('debug')('talk:util');
const fs = require('fs');
const util = module.exports = {}; const util = module.exports = {};
/** /**
@@ -11,10 +14,16 @@ util.toshutdown = [];
* Calls all the shutdown functions and then ends the process. * Calls all the shutdown functions and then ends the process.
* @param {Number} [defaultCode=0] default return code upon sucesfull shutdown. * @param {Number} [defaultCode=0] default return code upon sucesfull shutdown.
*/ */
util.shutdown = (defaultCode = 0) => { util.shutdown = (defaultCode = 0, signal = null) => {
if (signal) {
debug(`Reached ${signal} signal`);
}
Promise Promise
.all(util.toshutdown.map((func) => func ? func() : null).filter((func) => func)) .all(util.toshutdown.map((func) => func ? func(signal) : null).filter((func) => func))
.then(() => { .then(() => {
debug('Shutdown complete, now exiting');
process.exit(defaultCode); process.exit(defaultCode);
}) })
.catch((err) => { .catch((err) => {
@@ -32,13 +41,52 @@ util.shutdown = (defaultCode = 0) => {
*/ */
util.onshutdown = (jobs) => { util.onshutdown = (jobs) => {
debug(`${jobs.length} jobs registered`);
// Add the new jobs to shutdown to the object reference. // Add the new jobs to shutdown to the object reference.
util.toshutdown = util.toshutdown.concat(jobs); util.toshutdown = util.toshutdown.concat(jobs);
}; };
/**
* Register a PID file to be maintained for the lifespan of the process.
* @param {String} path path to the PID file to create
*/
util.pid = (path) => {
if (!/\//.test(path)) {
if (!/\.pid/.test(path)) {
path += '.pid';
}
path = `/tmp/${path}`;
}
const pid = `${process.pid.toString()}\n`;
fs.writeFile(path, pid, (err) => {
if (err) {
console.error(`Can't write PID file: ${err}`);
throw err;
}
// Add the cleanup for the fs onto the shutdown.
util.onshutdown([
() => new Promise((resolve, reject) => {
// Remove the pid file.
fs.unlink(path, (err) => {
if (err) {
return reject(err);
}
return resolve();
});
})
]);
});
};
// Attach to the SIGTERM + SIGINT handles to ensure a clean shutdown in the // Attach to the SIGTERM + SIGINT handles to ensure a clean shutdown in the
// event that we have an external event. SIGUSR2 is called when the app is asked // event that we have an external event. SIGUSR2 is called when the app is asked
// to be 'killed', same procedure here. // to be 'killed', same procedure here.
process.on('SIGTERM', () => util.shutdown()); process.on('SIGTERM', () => util.shutdown(0, 'SIGTERM'));
process.on('SIGINT', () => util.shutdown()); process.on('SIGINT', () => util.shutdown(0, 'SIGINT'));
process.once('SIGUSR2', () => util.shutdown()); process.once('SIGUSR2', () => util.shutdown(0, 'SIGUSR2'));