diff --git a/.babelrc b/.babelrc index ca2e2cead..41d27bf34 100644 --- a/.babelrc +++ b/.babelrc @@ -1,16 +1,15 @@ { "sourceMaps": true, "presets": [ - "stage-0", "es2015" ], "plugins": [ - ["transform-decorators-legacy"], - ["transform-react-jsx"], - ["transform-object-assign"], - ["transform-class-properties"], - ["transform-async-to-generator"], - ["transform-object-rest-spread"], - ["transform-class-properties"] + "add-module-exports", + "transform-async-to-generator", + "transform-class-properties", + "transform-decorators-legacy", + "transform-object-assign", + "transform-object-rest-spread", + "transform-react-jsx" ] } diff --git a/client/coral-admin/src/actions/comments.js b/client/coral-admin/src/actions/comments.js index d4aee034e..e755f1ed1 100644 --- a/client/coral-admin/src/actions/comments.js +++ b/client/coral-admin/src/actions/comments.js @@ -1,30 +1,69 @@ +import coralApi from '../../../coral-framework/helpers/response'; +import * as commentActions from '../constants/comments'; + +// Get comments to fill each of the three lists on the mod queue +export const fetchModerationQueueComments = () => { + return dispatch => { + dispatch({type: commentActions.COMMENTS_MODERATION_QUEUE_FETCH}); + return Promise.all([ + coralApi('/queue/comments/pending'), + coralApi('/comments?status=rejected'), + coralApi('/comments?action_type=flag') + ]) + .then(([pending, rejected, flagged]) => { + + /* Combine seperate calls into a single object */ + flagged.comments.forEach(comment => comment.flagged = true); + return { + comments: [...pending.comments, ...rejected.comments, ...flagged.comments], + users: [...pending.users, ...rejected.users, ...flagged.users], + actions: [...pending.actions, ...rejected.actions, ...flagged.actions] + }; + }) + .then(({comments, users}) => { + + /* Post comments and users to redux store. Actions will be posted when they are needed. */ + dispatch({type: commentActions.USERS_MODERATION_QUEUE_FETCH_SUCCESS, users}); + dispatch({type: commentActions.COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS, comments}); + + }); + }; +}; + +// Create a new comment +export const createComment = (name, body) => { + return dispatch => { + const comment = {body, name}; + return coralApi('/comments', {method: 'POST', comment}) + .then(res => dispatch({type: commentActions.COMMENT_CREATE_SUCCESS, comment: res})) + .catch(error => dispatch({type: commentActions.COMMENT_CREATE_FAILED, error})); + }; +}; + /** * Action disptacher related to comments */ -export const updateStatus = (status, id) => (dispatch, getState) => { - dispatch({type: 'COMMENT_STATUS_UPDATE', id, status}); - dispatch({type: 'COMMENT_UPDATE', comment: getState().comments.get('byId').get(id)}); +// Update a comment. Now to update a comment we need to send back the whole object +export const updateStatus = (status, comment) => { + return dispatch => { + dispatch({type: commentActions.COMMENT_STATUS_UPDATE, id: comment.id, status}); + return coralApi(`/comments/${comment.id}/status`, {method: 'PUT', body: {status}}) + .then(res => dispatch({type: commentActions.COMMENT_UPDATE_SUCCESS, res})) + .catch(error => dispatch({type: commentActions.COMMENT_UPDATE_FAILED, error})); + }; }; export const flagComment = id => (dispatch, getState) => { - dispatch({type: 'COMMENT_FLAG', id}); + dispatch({type: commentActions.COMMENT_FLAG, id}); dispatch({type: 'COMMENT_UPDATE', comment: getState().comments.get('byId').get(id)}); }; -export const createComment = (name, body) => dispatch => { - dispatch({type: 'COMMENT_CREATE', name, body}); -}; - // Dialog Actions export const showBanUserDialog = (userId, userName, commentId) => { - return dispatch => { - dispatch({type: 'SHOW_BANUSER_DIALOG', userId, userName, commentId}); - }; + return {type: commentActions.SHOW_BANUSER_DIALOG, userId, userName, commentId}; }; export const hideBanUserDialog = (showDialog) => { - return dispatch => { - dispatch({type: 'HIDE_BANUSER_DIALOG', showDialog}); - }; + return {type: commentActions.HIDE_BANUSER_DIALOG, showDialog}; }; diff --git a/client/coral-admin/src/actions/users.js b/client/coral-admin/src/actions/users.js index f2ff37cbd..bc42a7a4c 100644 --- a/client/coral-admin/src/actions/users.js +++ b/client/coral-admin/src/actions/users.js @@ -1,14 +1,15 @@ +import coralApi from '../../../coral-framework/helpers/response'; +import * as actions from '../constants/user'; /** * Action disptacher related to users */ -// -// export const banUser = (status, author_id) => (dispatch) => { -// dispatch({type: 'USER_STATUS_UPDATE', author_id, status}); -// }; -export const banUser = (status, userId, commentId) => { +// change status of a user +export const userStatusUpdate = (status, userId, commentId) => { return dispatch => { - dispatch({type: 'USER_BAN', status, userId, commentId}); - dispatch({type: 'COMMENTS_MODERATION_QUEUE_FETCH'}); + dispatch({type: actions.UPDATE_STATUS_REQUEST}); + return coralApi(`/users/${userId}/status`, {method: 'POST', body: {status: status, comment_id: commentId}}) + .then(res => dispatch({type: actions.UPDATE_STATUS_SUCCESS, res})) + .catch(error => dispatch({type: actions.UPDATE_STATUS_FAILURE, error})); }; }; diff --git a/client/coral-admin/src/components/Comment.js b/client/coral-admin/src/components/Comment.js index 4a21b0846..291825c71 100644 --- a/client/coral-admin/src/components/Comment.js +++ b/client/coral-admin/src/components/Comment.js @@ -30,7 +30,7 @@ export default props => {
{links ? Contains Link : null} -
+
{props.actions.map((action, i) => getActionButton(action, i, props))}
@@ -63,21 +63,23 @@ const getActionButton = (action, i, props) => { if (action === 'ban') { return ( ); } return ( props.onClickAction(props.actionsMap[action].status, comment.id)} + onClick={() => props.onClickAction(props.actionsMap[action].status, comment)} /> ); }; diff --git a/client/coral-admin/src/components/CommentList.js b/client/coral-admin/src/components/CommentList.js index 4f1d75d81..c6682e5de 100644 --- a/client/coral-admin/src/components/CommentList.js +++ b/client/coral-admin/src/components/CommentList.js @@ -147,7 +147,10 @@ export default class CommentList extends React.Component { const {active} = this.state; return ( -
this.onCommentAction(action, commentId)} + onClickAction={(action, comment) => this.onCommentAction(action, comment)} onClickShowBanDialog={(userId, userName, commentId) => this.showBanUserDialog(userId, userName, commentId)} actions={['reject', 'approve', 'ban']} loading={comments.loading} /> @@ -126,7 +124,7 @@ class ModerationQueue extends React.Component { handleClose={() => this.hideBanUserDialog()} onClickBanUser={(userId, commentId) => this.banUser(userId, commentId)} user={comments.banUser}/> -
+
this.onCommentAction(action, id)} + onClickAction={(action, comment) => this.onCommentAction(action, comment)} actions={['approve']} loading={comments.loading} />
this.onCommentAction(action, id)} + onClickAction={(action, comment) => this.onCommentAction(action, comment)} actions={['reject', 'approve']} loading={comments.loading} />
-
- this.setState({modalOpen: false})} /> -
+ this.setState({modalOpen: false})} /> ); - } } diff --git a/client/coral-admin/src/reducers/comments.js b/client/coral-admin/src/reducers/comments.js index dca726b5e..2856b0a34 100644 --- a/client/coral-admin/src/reducers/comments.js +++ b/client/coral-admin/src/reducers/comments.js @@ -1,4 +1,5 @@ import * as actions from '../constants/comments'; +import * as userActions from '../constants/user'; import {Map, List, fromJS} from 'immutable'; /** @@ -23,16 +24,16 @@ const initialState = Map({ // Handle the comment actions export default (state = initialState, action) => { switch (action.type) { - case 'COMMENTS_MODERATION_QUEUE_FETCH': return state.set('loading', true); - case 'COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS': return replaceComments(action, state); - case 'COMMENTS_MODERATION_QUEUE_FAILED': return state.set('loading', false); - case 'COMMENT_STATUS_UPDATE': return updateStatus(state, action); - case 'COMMENT_FLAG': return flag(state, action); - case 'COMMENT_CREATE_SUCCESS': return addComment(state, action); - case 'COMMENT_STREAM_FETCH_SUCCESS': return replaceComments(action, state); + case actions.COMMENTS_MODERATION_QUEUE_FETCH: return state.set('loading', true); + case actions.COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS: return replaceComments(action, state); + case actions.COMMENTS_MODERATION_QUEUE_FAILED: return state.set('loading', false); + case actions.COMMENT_STATUS_UPDATE: return updateStatus(state, action); + case actions.COMMENT_FLAG: return flag(state, action); + case actions.COMMENT_CREATE_SUCCESS: return addComment(state, action); + case actions.COMMENT_STREAM_FETCH_SUCCESS: return replaceComments(action, state); case actions.SHOW_BANUSER_DIALOG: return setBanUser(state, true, action); case actions.HIDE_BANUSER_DIALOG: return setBanUser(state, false, action); - case actions.USER_BAN_SUCESS: return setBanUser(state, false, action); + case userActions.UPDATE_STATUS_SUCCESS: return setBanUser(state, false, action); default: return state; } }; diff --git a/client/coral-admin/src/services/store.js b/client/coral-admin/src/services/store.js index 88b2650e0..0700dc579 100644 --- a/client/coral-admin/src/services/store.js +++ b/client/coral-admin/src/services/store.js @@ -2,7 +2,6 @@ import {createStore, applyMiddleware} from 'redux'; import thunk from 'redux-thunk'; import mainReducer from 'reducers'; -import talkAdapter from 'services/talk-adapter'; /** * Create the store by merging the app reducers with @@ -14,5 +13,5 @@ import talkAdapter from 'services/talk-adapter'; export default createStore( mainReducer, window.devToolsExtension && window.devToolsExtension(), - applyMiddleware(thunk, talkAdapter) + applyMiddleware(thunk) ); diff --git a/client/coral-admin/src/services/talk-adapter.js b/client/coral-admin/src/services/talk-adapter.js deleted file mode 100644 index 7c9bc968f..000000000 --- a/client/coral-admin/src/services/talk-adapter.js +++ /dev/null @@ -1,96 +0,0 @@ -import coralApi from '../../../coral-framework/helpers/response'; - -/** - * The adapter is a redux middleware that interecepts the actions that need - * to interface with the backend, do the job and return the results. - * The idea is that if we expose the required actions to handle to devs, the - * moderation app can be platform agnostic. This same client could work not only - * for the coral but also for wordpress comments, disqus and many more. - */ - -// Intercept redux actions and act over the ones we are interested -export default store => next => action => { - - switch (action.type) { - case 'COMMENTS_MODERATION_QUEUE_FETCH': - fetchModerationQueueComments(store); - break; - case 'COMMENT_UPDATE': - updateComment(store, action.comment); - break; - case 'COMMENT_CREATE': - createComment(store, action.name, action.body); - break; - case 'USER_BAN': - userStatusUpdate(store, action.status, action.userId, action.commentId); - break; - } - - next(action); -}; - -// Get comments to fill each of the three lists on the mod queue -const fetchModerationQueueComments = store => - -Promise.all([ - coralApi('/queue/comments/pending'), - coralApi('/comments?status=rejected'), - coralApi('/comments?action_type=flag') -]) -.then(([pending, rejected, flagged]) => { - - /* Combine seperate calls into a single object */ - let all = {}; - all.comments = pending.comments - .concat(rejected.comments) - .concat(flagged.comments.map(comment => { - comment.flagged = true; - return comment; - })); - all.users = pending.users - .concat(rejected.users) - .concat(flagged.users); - all.actions = pending.actions - .concat(rejected.actions) - .concat(flagged.actions); - return all; -}) -.then(all => { - - /* Post comments and users to redux store. Actions will be posted when they are needed. */ - store.dispatch({type: 'USERS_MODERATION_QUEUE_FETCH_SUCCESS', - users: all.users}); - store.dispatch({type: 'COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS', - comments: all.comments}); - -}); - -// .catch(error => store.dispatch({type: 'COMMENTS_MODERATION_QUEUE_FETCH_FAILED', error})); - -// Update a comment. Now to update a comment we need to send back the whole object - -const updateComment = (store, comment) => { - coralApi(`/comments/${comment.get('id')}/status`, {method: 'PUT', body: {status: comment.get('status')}}) - .then(res => store.dispatch({type: 'COMMENT_UPDATE_SUCCESS', res})) - .catch(error => store.dispatch({type: 'COMMENT_UPDATE_FAILED', error})); -}; - -// Create a new comment -const createComment = (store, name, comment) => { - const body = { - status: 'Untouched', - body: comment, - name: name, - createdAt: Date.now() - }; - return coralApi('/comments', {method: 'POST', body}) - .then(res => store.dispatch({type: 'COMMENT_CREATE_SUCCESS', comment: res})) - .catch(error => store.dispatch({type: 'COMMENT_CREATE_FAILED', error})); -}; - -// Ban a user -const userStatusUpdate = (store, status, userId, commentId) => { - return coralApi(`/users/${userId}/status`, {method: 'POST', body: {status: status, comment_id: commentId}}) - .then(res => store.dispatch({type: 'USER_BAN_SUCESS', res})) - .catch(error => store.dispatch({type: 'USER_BAN_FAILED', error})); -}; diff --git a/client/coral-plugin-likes/LikeButton.js b/client/coral-plugin-likes/LikeButton.js index 9e7d06f33..fae30c8c7 100644 --- a/client/coral-plugin-likes/LikeButton.js +++ b/client/coral-plugin-likes/LikeButton.js @@ -2,7 +2,7 @@ import React from 'react'; import {I18n} from '../coral-framework'; import translations from './translations.json'; -const name = 'coral-plugin-flags'; +const name = 'coral-plugin-likes'; const LikeButton = ({like, id, postAction, deleteAction, addItem, showSignInDialog, updateItem, currentUser, banned}) => { const liked = like && like.current_user; diff --git a/client/coral-sign-in/components/SignDialog.js b/client/coral-sign-in/components/SignDialog.js index 68d96b910..966870293 100644 --- a/client/coral-sign-in/components/SignDialog.js +++ b/client/coral-sign-in/components/SignDialog.js @@ -9,10 +9,11 @@ import ForgotContent from './ForgotContent'; const SignDialog = ({open, view, handleClose, offset, ...props}) => ( × {view === 'SIGNIN' && } diff --git a/client/coral-sign-in/components/UserBox.js b/client/coral-sign-in/components/UserBox.js index 11badfaef..e99c0f88c 100644 --- a/client/coral-sign-in/components/UserBox.js +++ b/client/coral-sign-in/components/UserBox.js @@ -9,7 +9,9 @@ const UserBox = ({className, user, logout, ...props}) => ( className={`${styles.userBox} ${className ? className : ''}`} {...props} > - {lang.t('signIn.loggedInAs')} {user.displayName}. {lang.t('signIn.notYou')} {lang.t('signIn.logout')} + {lang.t('signIn.loggedInAs')} + {user.displayName}. {lang.t('signIn.notYou')} + {lang.t('signIn.logout')} ); diff --git a/client/coral-sign-in/components/styles.css b/client/coral-sign-in/components/styles.css index 2bec52b45..f1a7bace3 100644 --- a/client/coral-sign-in/components/styles.css +++ b/client/coral-sign-in/components/styles.css @@ -113,6 +113,7 @@ input.error{ font-weight: bold; cursor: pointer; margin: 0px; + margin-left: 4px; padding-bottom: 2px; border-bottom: solid 1px black; } diff --git a/nightwatch.conf.js b/nightwatch.conf.js index a39ab128f..fb6c5f9c7 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -19,6 +19,7 @@ module.exports = { }, 'test_settings': { 'default': { + 'launch_url' : 'http://localhost:3000', 'selenium_port': 6666, 'selenium_host': 'localhost', 'silent': true, @@ -26,19 +27,27 @@ module.exports = { 'browserName': 'chrome', 'javascriptEnabled': true, 'acceptSslCerts': true, - 'webStorageEnabled' : true, - 'databaseEnabled' : true, - 'applicationCacheEnabled' : false, - 'nativeEvents' : true + 'webStorageEnabled': true, + 'databaseEnabled': true, + 'applicationCacheEnabled': false, + 'nativeEvents': true }, 'screenshots' : { - 'enabled' : true, - 'on_failure' : true, - 'on_error' : true, - 'path' : './tests/e2e/reports' + 'enabled': true, + 'on_failure': true, + 'on_error': true, + 'path': './tests/e2e/reports' }, 'exclude': [ + './tests/e2e/tests/EmbedStreamTests.js' ] + }, + 'integration': { + 'launch_url': 'http://localhost:3000' } } }; + +// "chromeOptions" : { +// "args" : ["start-fullscreen"] +// } diff --git a/package.json b/package.json index e700f7113..752c4c2da 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "lint-fix": "./node_modules/.bin/eslint bin/* . --fix", "test": "NODE_ENV=test ./node_modules/.bin/mocha --compilers js:babel-core/register tests/helpers/*.js --require ignore-styles --recursive tests", "test-watch": "NODE_ENV=test ./node_modules/.bin/mocha --compilers js:babel-core/register --recursive -w tests", - "pree2e": "NODE_ENV=test ./scripts/pree2e.sh", - "e2e": "NODE_ENV=test ./node_modules/.bin/nightwatch", + "pree2e": "NODE_ENV=test scripts/pree2e.sh", + "e2e": "NODE_ENV=test nightwatch", "embed-start": "NODE_ENV=development npm run build && ./bin/cli serve --jobs" }, "config": { @@ -74,10 +74,11 @@ }, "devDependencies": { "autoprefixer": "^6.5.2", - "babel-core": "^6.18.2", + "babel-core": "^6.21.0", "babel-eslint": "^7.1.0", "babel-jest": "^15.0.0", "babel-loader": "^6.2.7", + "babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-transform-async-to-generator": "^6.16.0", "babel-plugin-transform-class-properties": "^6.18.0", "babel-plugin-transform-decorators-legacy": "^1.3.4", @@ -115,7 +116,7 @@ "material-design-lite": "^1.2.1", "mocha": "^3.1.2", "mocha-junit-reporter": "^1.12.1", - "nightwatch": "^0.9.9", + "nightwatch": "^0.9.11", "node-fetch": "^1.6.3", "postcss-loader": "^1.1.0", "postcss-modules": "^0.5.2", @@ -137,7 +138,7 @@ "redux-mock-store": "^1.2.1", "redux-thunk": "^2.1.0", "regenerator": "^0.8.46", - "selenium-standalone": "^5.8.0", + "selenium-standalone": "latest", "style-loader": "^0.13.1", "supertest": "^2.0.1", "timeago.js": "^2.0.3", diff --git a/scripts/pree2e.sh b/scripts/pree2e.sh index dc3266dd2..e79addc2e 100755 --- a/scripts/pree2e.sh +++ b/scripts/pree2e.sh @@ -1,7 +1,15 @@ #!/bin/bash # install selenium -../node_modules/selenium-standalone/bin/selenium-standalone install +selenium-standalone install -# start the app server -npm start & +# Creating Admin Test User +{ echo admin@test.com; echo test; echo test; echo Admin Test User; echo admin;} | dotenv ./bin/cli-users create + +# Creating Moderator Test User +{ echo moderator@test.com; echo test; echo test; echo Moderator Test User; echo moderator;} | dotenv ./bin/cli-users create + +# Creating Commenter Test User +{ echo commenter@test.com; echo test; echo test; echo Commenter Test User; echo ;} | dotenv ./bin/cli-users create + +npm start diff --git a/tests/e2e/globals.js b/tests/e2e/globals.js index 147af2663..22ad3cb4b 100644 --- a/tests/e2e/globals.js +++ b/tests/e2e/globals.js @@ -1,4 +1,18 @@ module.exports = { - waitForConditionTimeout: 20000, - baseUrl: 'localhost:3011/' + waitForConditionTimeout: 8000, + baseUrl: 'http://localhost:3000', + users: { + admin: { + email: 'admin@test.com', + pass: 'test' + }, + moderator: { + email: 'moderator@test.com', + pass: 'test' + }, + commenter: { + email: 'commenter@test.com', + pass: 'test' + } + }, }; diff --git a/tests/e2e/pages/adminPage.js b/tests/e2e/pages/adminPage.js new file mode 100644 index 000000000..cb5371d53 --- /dev/null +++ b/tests/e2e/pages/adminPage.js @@ -0,0 +1,33 @@ +const embedStreamCommands = { + url: function () { + return `${this.api.launchUrl}/admin`; + }, + ready() { + return this + .waitForElementVisible('body', 2000); + }, + approveComment() { + return this + .waitForElementVisible('@commentList') + .waitForElementVisible('@approveButton') + .click('@approveButton'); + } +}; + +module.exports = { + commands: [embedStreamCommands], + elements: { + commentList: { + selector: '#commentList' + }, + banButton: { + selector: '#commentList .actions:first-child .ban' + }, + rejectButton: { + selector: '#commentList .actions:first-child .reject' + }, + approveButton: { + selector: '#commentList .actions:first-child .approve' + } + } +}; diff --git a/tests/e2e/pages/embedStream.js b/tests/e2e/pages/embedStream.js deleted file mode 100644 index cc584a656..000000000 --- a/tests/e2e/pages/embedStream.js +++ /dev/null @@ -1,45 +0,0 @@ -const fetch = require('node-fetch'); - -const embedCommands = { - ready() { - return this.resizeWindow(1200, 800) - .url(client.globals.baseUrl) - .waitForElementVisible('body', 2000) - .frame('coralStreamIframe'); - }, - setConfig(config, baseUrl) { - return fetch(`${baseUrl}/api/v1/settings`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }, - body: JSON.stringify(config) - }); - }, - enterComment() { - const comment = 'This is a test comment'; - return this - .waitForElementVisible('@commentBox') - .setValue('@commentBox', comment) - .click('@postButton') - .waitForElementVisible('.comment', 1000); - }, - validateComment(comment) { - return this - .assert.equal(comment, '.comment'); - } -}; - -module.exports = { - commands: [embedCommands], - elements: { - - commentBox: { - selector: '#commentBox' - }, - postButton: { - selector: '#commentBox .coral-plugin-commentbox-button' - } - } -}; diff --git a/tests/e2e/pages/embedStreamPage.js b/tests/e2e/pages/embedStreamPage.js new file mode 100644 index 000000000..164449ff0 --- /dev/null +++ b/tests/e2e/pages/embedStreamPage.js @@ -0,0 +1,160 @@ +const embedStreamCommands = { + url: function () { + return this + .api.launchUrl; + }, + ready() { + return this + .waitForElementVisible('body', 4000) + .waitForElementVisible('iframe#coralStreamIframe') + .api.frame('coralStreamIframe'); + }, + signUp(user) { + return this + .waitForElementVisible('@signInButton', 2000) + .click('@signInButton') + .waitForElementVisible('@signInDialog') + .waitForElementVisible('@registerButton') + .click('@registerButton') + .setValue('@signInDialogEmail', user.email) + .setValue('@signInDialogPassword', user.pass) + .setValue('@signUpDialogConfirmPassword', user.pass) + .setValue('@signUpDialogDisplayName', user.displayName) + .waitForElementVisible('@signUpButton') + .click('@signUpButton') + .waitForElementVisible('@logInButton') + .click('@logInButton') + .waitForElementVisible('@logoutButton', 5000); + }, + login(user) { + return this + .waitForElementVisible('@signInButton', 2000) + .click('@signInButton') + .waitForElementVisible('@signInDialog') + .waitForElementVisible('@signInDialogEmail') + .waitForElementVisible('@signInDialogPassword') + .setValue('@signInDialogEmail', user.email) + .setValue('@signInDialogPassword', user.pass) + .waitForElementVisible('@logInButton') + .click('@logInButton') + .waitForElementVisible('@logoutButton', 5000); + }, + logout() { + return this + .waitForElementVisible('@logoutButton') + .click('@logoutButton') + .waitForElementVisible('@signInButton', 2000); + }, + postComment(comment = 'Test Comment') { + return this + .waitForElementVisible('@commentBox', 2000) + .setValue('@commentBox', comment) + .click('@postButton'); + }, + likeComment() { + return this + .waitForElementVisible('@likeButton') + .click('@likeButton'); + }, + flagComment() { + return this + .waitForElementVisible('@flagButton') + .click('@flagButton'); + }, + flagUsername() { + return this + .waitForElementVisible('@flagButton') + .click('@flagButton'); + }, + getPermalink(fn) { + return this + .waitForElementVisible('@permalinkButton') + .click('@permalinkButton') + .waitForElementVisible('@permalinkPopUp') + .getValue('@permalinkInput', result => fn(result.value)); + } +}; + +module.exports = { + commands: [embedStreamCommands], + elements: { + signInButton: { + selector: '#coralSignInButton' + }, + signInDialog:{ + selector: '#signInDialog' + }, + signInDialogEmail: { + selector: '#signInDialog #email' + }, + signInDialogPassword: { + selector: '#signInDialog #password' + }, + signUpDialogConfirmPassword: { + selector: '#signInDialog #confirmPassword' + }, + signUpDialogDisplayName: { + selector: '#signInDialog #displayName' + }, + logInButton: { + selector: '#coralLogInButton' + }, + signUpButton: { + selector: '#coralSignUpButton' + }, + logoutButton: { + selector: '.commentStream #logout' + }, + commentBox: { + selector: '.coral-plugin-commentbox-textarea' + }, + postButton: { + selector: '#commentBox .coral-plugin-commentbox-button' + }, + likeButton: { + selector: '.comment .coral-plugin-likes-container .coral-plugin-likes-button' + }, + likeText: { + selector: '.comment .coral-plugin-likes-container .coral-plugin-likes-button .coral-plugin-likes-button-text' + }, + likesCount: { + selector: '.comment .coral-plugin-likes-container .coral-plugin-likes-button .coral-plugin-likes-like-count' + }, + flagButton: { + selector: '.comment .coral-plugin-flags-container .coral-plugin-flags-button' + }, + flagPopUp: { + selector: '.comment .coral-plugin-flags-popup' + }, + flagCommentOption: { + selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-radio-label[for="comments"]' + }, + flagUsernameOption: { + selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-radio-label[for="user"]' + }, + flagOtherOption: { + selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-radio-label[for="other"]' + }, + flagHeaderMessage: { + selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-header' + }, + flagButtonText: { + selector: '.comment .coral-plugin-flags-button-text' + }, + flagDoneButton: { + selector: '.comment .coral-plugin-flags-popup .coral-plugin-flags-popup-button' + }, + permalinkButton: { + selector: '.comment .coral-plugin-permalinks-button' + }, + permalinkPopUp: { + selector: '.comment .coral-plugin-permalinks-popover.active' + }, + permalinkInput: { + selector: '.comment .coral-plugin-permalinks-popover.active input' + }, + registerButton: { + selector: '#signInDialog #coralRegister' + } + } +}; diff --git a/tests/e2e/tests/AppTest.js b/tests/e2e/tests/00_AppTest.js similarity index 84% rename from tests/e2e/tests/AppTest.js rename to tests/e2e/tests/00_AppTest.js index 36281ed3f..27b6dbe75 100644 --- a/tests/e2e/tests/AppTest.js +++ b/tests/e2e/tests/00_AppTest.js @@ -6,8 +6,5 @@ module.exports = { .url(baseUrl) .assert.title('Coral Talk') .waitForElementPresent('body', 1000); - }, - after: client => { - client.end(); } }; diff --git a/tests/e2e/tests/01_EmbedStreamTest.js b/tests/e2e/tests/01_EmbedStreamTest.js new file mode 100644 index 000000000..35f4171c7 --- /dev/null +++ b/tests/e2e/tests/01_EmbedStreamTest.js @@ -0,0 +1,44 @@ +module.exports = { + '@tags': ['embedStream'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + embedStreamPage + .navigate() + .ready(); + }, + 'Login as commenter': client => { + const embedStreamPage = client.page.embedStreamPage(); + const {users} = client.globals; + embedStreamPage + .login(users.commenter); + }, + 'Add test comment': client => { + const embedStreamPage = client.page.embedStreamPage(); + embedStreamPage + .postComment('Test Comment'); + }, + 'Logout': client => { + const embedStreamPage = client.page.embedStreamPage(); + embedStreamPage + .logout(); + }, + 'Login as admin': client => { + const embedStreamPage = client.page.embedStreamPage(); + const {users} = client.globals; + embedStreamPage + .login(users.admin); + }, + 'Approve test comment': client => { + const adminPage = client.page.adminPage(); + + adminPage + .navigate() + .ready(); + + adminPage + .approveComment(); + }, + after: client => { + client.end(); + } +}; diff --git a/tests/e2e/tests/Admin/LoginTest.js b/tests/e2e/tests/Admin/LoginTest.js new file mode 100644 index 000000000..e75d29903 --- /dev/null +++ b/tests/e2e/tests/Admin/LoginTest.js @@ -0,0 +1,23 @@ +module.exports = { + '@tags': ['login', 'admin'], + before(client) { + const embedStreamPage = client.page.embedStreamPage(); + const {launchUrl} = client; + + client + .url(launchUrl); + + embedStreamPage + .ready(); + }, + 'Admin logs in': client => { + const {users} = client.globals; + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .login(users.admin); + }, + after: client => { + client.end(); + } +}; diff --git a/tests/e2e/tests/Commenter/FlagCommentTest.js b/tests/e2e/tests/Commenter/FlagCommentTest.js new file mode 100644 index 000000000..baff5fdf5 --- /dev/null +++ b/tests/e2e/tests/Commenter/FlagCommentTest.js @@ -0,0 +1,34 @@ +module.exports = { + '@tags': ['flag', 'comments', 'commenter'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + const {users} = client.globals; + + embedStreamPage + .navigate() + .ready(); + + embedStreamPage + .login(users.commenter); + }, + 'Commenter flags a comment': client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .flagComment() + .waitForElementVisible('@flagPopUp') + .waitForElementVisible('@flagCommentOption') + .click('@flagCommentOption') + .waitForElementVisible('@flagDoneButton') + .click('@flagDoneButton') + .waitForElementVisible('@flagOtherOption') + .click('@flagOtherOption') + .waitForElementVisible('@flagDoneButton') + .click('@flagDoneButton') + .click('@flagDoneButton') + .expect.element('@flagButtonText').text.to.equal('Reported'); + }, + after: client => { + client.end(); + } +}; diff --git a/tests/e2e/tests/Commenter/FlagUsernameTest.js b/tests/e2e/tests/Commenter/FlagUsernameTest.js new file mode 100644 index 000000000..7d9ab5a90 --- /dev/null +++ b/tests/e2e/tests/Commenter/FlagUsernameTest.js @@ -0,0 +1,33 @@ +module.exports = { + '@tags': ['flag', 'commenter'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + const {users} = client.globals; + + embedStreamPage + .navigate() + .ready(); + + embedStreamPage + .login(users.commenter); + }, + 'Commenter flags a username': client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .flagUsername() + .waitForElementVisible('@flagPopUp') + .waitForElementVisible('@flagUsernameOption') + .click('@flagUsernameOption') + .waitForElementVisible('@flagDoneButton') + .click('@flagDoneButton') + .waitForElementVisible('@flagOtherOption') + .click('@flagOtherOption') + .waitForElementVisible('@flagDoneButton') + .click('@flagDoneButton') + .click('@flagDoneButton'); + }, + after: client => { + client.end(); + } +}; diff --git a/tests/e2e/tests/Commenter/LikeCommentTest.js b/tests/e2e/tests/Commenter/LikeCommentTest.js new file mode 100644 index 000000000..59e964969 --- /dev/null +++ b/tests/e2e/tests/Commenter/LikeCommentTest.js @@ -0,0 +1,26 @@ +module.exports = { + '@tags': ['like', 'comments', 'commenter'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + const {users} = client.globals; + + embedStreamPage + .navigate() + .ready(); + + embedStreamPage + .login(users.commenter); + }, + 'Commenter likes a comment': client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .likeComment() + .waitForElementVisible('@likesCount', 2000) + .expect.element('@likeText').text.to.equal('Liked'); + + }, + after: client => { + client.end(); + } +}; diff --git a/tests/e2e/tests/Commenter/LoginTest.js b/tests/e2e/tests/Commenter/LoginTest.js new file mode 100644 index 000000000..15c830c74 --- /dev/null +++ b/tests/e2e/tests/Commenter/LoginTest.js @@ -0,0 +1,20 @@ +module.exports = { + '@tags': ['login', 'commenter'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .navigate() + .ready(); + }, + 'Commenter logs in': client => { + const {users} = client.globals; + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .login(users.commenter); + }, + after: client => { + client.end(); + } +}; diff --git a/tests/e2e/tests/Commenter/PermalinkTest.js b/tests/e2e/tests/Commenter/PermalinkTest.js new file mode 100644 index 000000000..deed9ec90 --- /dev/null +++ b/tests/e2e/tests/Commenter/PermalinkTest.js @@ -0,0 +1,34 @@ +let permalink = ''; + +module.exports = { + '@tags': ['permalink', 'commenter'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + const {users} = client.globals; + + embedStreamPage + .navigate() + .ready(); + + embedStreamPage + .login(users.commenter); + }, + 'Commenter gets the permalink of a comment': client => { + const embedStreamPage = client.page.embedStreamPage(); + embedStreamPage + .getPermalink(value => { + permalink = value; + }); + }, + 'Commenter navigates to the permalink': client => { + const embedStreamPage = client.page.embedStreamPage(); + embedStreamPage + .navigate(permalink); + + client.assert.urlContains(permalink); + }, + after: client => { + client.end(); + } +}; + diff --git a/tests/e2e/tests/Commenter/PostComment.js b/tests/e2e/tests/Commenter/PostComment.js new file mode 100644 index 000000000..91e28415d --- /dev/null +++ b/tests/e2e/tests/Commenter/PostComment.js @@ -0,0 +1,38 @@ +module.exports = { + '@tags': ['write', 'commenter'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + const {users} = client.globals; + + embedStreamPage + .navigate() + .ready(); + + embedStreamPage + .login(users.commenter); + }, + 'Commenter posts a comment': client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .postComment('I read the comments'); + }, + after: client => { + const adminPage = client.page.adminPage(); + const embedStreamPage = client.page.embedStreamPage(); + const {users} = client.globals; + + embedStreamPage + .logout() + .login(users.admin); + + adminPage + .navigate() + .ready(); + + adminPage + .approveComment(); + + client.end(); + } +}; diff --git a/tests/e2e/tests/Moderator/LoginTest.js b/tests/e2e/tests/Moderator/LoginTest.js new file mode 100644 index 000000000..3f1754a50 --- /dev/null +++ b/tests/e2e/tests/Moderator/LoginTest.js @@ -0,0 +1,23 @@ +module.exports = { + '@tags': ['login', 'moderator'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + const {launchUrl} = client; + + client + .url(launchUrl); + + embedStreamPage + .ready(); + }, + 'Moderator logs in': client => { + const {users} = client.globals; + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .login(users.moderator); + }, + after: client => { + client.end(); + } +}; diff --git a/tests/e2e/tests/Visitor/FlagCommentTest.js b/tests/e2e/tests/Visitor/FlagCommentTest.js new file mode 100644 index 000000000..8fbab04ee --- /dev/null +++ b/tests/e2e/tests/Visitor/FlagCommentTest.js @@ -0,0 +1,20 @@ +module.exports = { + '@tags': ['flag', 'comments', 'visitor'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .navigate() + .ready(); + }, + 'Visitor tries to flag a comment': client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .flagComment() + .waitForElementVisible('@signInDialog', 2000); + }, + after: client => { + client.end(); + } +}; diff --git a/tests/e2e/tests/Visitor/LikeCommentTest.js b/tests/e2e/tests/Visitor/LikeCommentTest.js new file mode 100644 index 000000000..6aaca5b49 --- /dev/null +++ b/tests/e2e/tests/Visitor/LikeCommentTest.js @@ -0,0 +1,20 @@ +module.exports = { + '@tags': ['like', 'comments', 'visitor'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .navigate() + .ready(); + }, + 'Visitor tries to like a comment': client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .likeComment() + .waitForElementVisible('@signInDialog', 2000); + }, + after: client => { + client.end(); + } +}; diff --git a/tests/e2e/tests/Visitor/SignUpTest.js b/tests/e2e/tests/Visitor/SignUpTest.js new file mode 100644 index 000000000..4c7650630 --- /dev/null +++ b/tests/e2e/tests/Visitor/SignUpTest.js @@ -0,0 +1,24 @@ +module.exports = { + '@tags': ['signup', 'visitor'], + before: client => { + const embedStreamPage = client.page.embedStreamPage(); + + embedStreamPage + .navigate() + .ready(); + }, + 'Visitor signs up': client => { + const embedStreamPage = client.page.embedStreamPage(); + const hash = Math.floor(Math.random() * (999 - 0)); + + embedStreamPage + .signUp({ + email: `visitor_${hash}@test.com`, + displayName: 'Visitor', + pass: 'testtest' + }); + }, + after: client => { + client.end(); + } +}; diff --git a/views/article.ejs b/views/article.ejs index f4d7ef910..302d76572 100644 --- a/views/article.ejs +++ b/views/article.ejs @@ -36,7 +36,7 @@