diff --git a/client/coral-admin/src/components/ActionsMenu.js b/client/coral-admin/src/components/ActionsMenu.js index 25aee1fbc..6351afda7 100644 --- a/client/coral-admin/src/components/ActionsMenu.js +++ b/client/coral-admin/src/components/ActionsMenu.js @@ -32,8 +32,9 @@ class ActionsMenu extends React.Component { }; render() { + const {className = ''} = this.props; return ( -
+
- -
+
+

{t('bandialog.ban_user')}

+
+
+

{t('bandialog.are_you_sure', username)}

+ {info} +
+
+ +
); diff --git a/client/coral-admin/src/components/SuspendUserDialog.js b/client/coral-admin/src/components/SuspendUserDialog.js index d56201e2f..cfff3b17e 100644 --- a/client/coral-admin/src/components/SuspendUserDialog.js +++ b/client/coral-admin/src/components/SuspendUserDialog.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import {Dialog} from 'coral-ui'; import {RadioGroup, Radio} from 'react-mdl'; import styles from './SuspendUserDialog.css'; +import cn from 'classnames'; import Button from 'coral-ui/components/Button'; @@ -87,7 +88,7 @@ class SuspendUserDialog extends React.Component { -
@@ -120,7 +121,7 @@ class SuspendUserDialog extends React.Component { {row.profiles.map(({id}) => id)} @@ -52,6 +52,7 @@ const Table = ({users, setRole, onHeaderClickHandler, setCommenterStatus, viewUs setCommenterStatus(row.id, status)}> @@ -61,6 +62,7 @@ const Table = ({users, setRole, onHeaderClickHandler, setCommenterStatus, viewUs setRole(row.id, role)}> diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js index aedc69739..cea49720b 100644 --- a/client/coral-admin/src/routes/Moderation/components/Comment.js +++ b/client/coral-admin/src/routes/Moderation/components/Comment.js @@ -73,7 +73,7 @@ class Comment extends React.Component { return (
  • @@ -95,7 +95,7 @@ class Comment extends React.Component { : null } {currentUserId !== comment.user.id && - + diff --git a/client/coral-ui/components/Dialog.js b/client/coral-ui/components/Dialog.js index 93431db4c..a7d34a512 100644 --- a/client/coral-ui/components/Dialog.js +++ b/client/coral-ui/components/Dialog.js @@ -10,12 +10,12 @@ export default class Dialog extends Component { onCancel: PropTypes.func, onClose: PropTypes.func, open: PropTypes.bool, - style: PropTypes.object + style: PropTypes.object, }; static defaultProps = { onCancel: (e) => e.preventDefault(), - onClose: (e) => e.preventDefault() + onClose: (e) => e.preventDefault(), }; componentDidMount(){ diff --git a/client/coral-ui/components/Dropdown.js b/client/coral-ui/components/Dropdown.js index 2cad6e990..dddffbfa2 100644 --- a/client/coral-ui/components/Dropdown.js +++ b/client/coral-ui/components/Dropdown.js @@ -131,7 +131,7 @@ class Dropdown extends React.Component { const {containerClassName, toggleClassName, toggleOpenClassName} = this.props; return ( -
    +
    -
      +
        {React.Children.toArray(this.props.children) .map((child, i) => React.cloneElement(child, { diff --git a/client/coral-ui/components/Option.js b/client/coral-ui/components/Option.js index 5b752eb48..8fd92a904 100644 --- a/client/coral-ui/components/Option.js +++ b/client/coral-ui/components/Option.js @@ -16,8 +16,9 @@ class Option extends React.Component { render() { const {className, label = '', onClick, onKeyDown} = this.props; + const id = this.props.id ? this.props.id : this.props.value; return ( -
      • +
      • {label}
      • ); @@ -26,6 +27,7 @@ class Option extends React.Component { Option.propTypes = { className: PropTypes.string, + id: PropTypes.string, label: PropTypes.string, onClick: PropTypes.func, onKeyDown: PropTypes.func, diff --git a/docs/_docs/02-02-advanced-configuration.md b/docs/_docs/02-02-advanced-configuration.md index 73e72e2cb..74099258d 100644 --- a/docs/_docs/02-02-advanced-configuration.md +++ b/docs/_docs/02-02-advanced-configuration.md @@ -369,12 +369,12 @@ Then all the routes for the API will be expecting to be hit on `/talk/`, such as can perform the path stripping when serving an upstream proxy, but some CDN's cannot. You would use this option in the latter situation. -## TALK_SMTP_EMAIL +## TALK_SMTP_FROM_ADDRESS The email address to send emails from using the SMTP provider in the format: ```plain -TALK_SMTP_EMAIL="The Coral Project" +TALK_SMTP_FROM_ADDRESS="The Coral Project" ``` Including the name and email address. diff --git a/nightwatch-browserstack.conf.js b/nightwatch-browserstack.conf.js index d79f6917a..f31580988 100644 --- a/nightwatch-browserstack.conf.js +++ b/nightwatch-browserstack.conf.js @@ -22,11 +22,12 @@ const nightwatch_config = { 'browserstack.user': process.env.BROWSERSTACK_USER || 'coralproject2', 'browserstack.key': process.env.BROWSERSTACK_KEY, 'browserstack.local': true, + 'browserstack.localIdentifier': process.env.BROWSERSTACK_LOCAL_IDENTIFIER ? process.env.BROWSERSTACK_LOCAL_IDENTIFIER : undefined, 'browserstack.debug': true, // Disable this, as it makes bs slow and brittle. 'browserstack.networkLogs': false, - 'browserstack.resolution': '1600x1200', + 'resolution': '1600x1200', }, screenshots : { enabled: true, diff --git a/nightwatch.conf.js b/nightwatch.conf.js index 97eee0e81..e04cc04a8 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -40,6 +40,8 @@ module.exports = { path: process.env.REPORTS_FOLDER || './test/e2e/reports', }, }, + 'chrome': { + }, 'chrome-headless': { desiredCapabilities: { chromeOptions : { diff --git a/package.json b/package.json index 1b57bfb09..8dc9ac46b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "watch:client": "NODE_ENV=development webpack --progress --watch", "watch:server": "nodemon --config .nodemon.json", "start:development": "NODE_ENV=development ./bin/cli -c .env serve -j -w", - "start:production": "NODE_ENV=production ./bin/cli serve -j -w", + "start": "NODE_ENV=production ./bin/cli serve -j -w", "prebuild": "npm-run-all clean generate-introspection", "build": "NODE_ENV=production webpack -p --bail", "lint:yaml": "yamllint locales/*.yml", @@ -23,12 +23,8 @@ "test:server": "TEST_MODE=unit NODE_ENV=test mocha -R ${MOCHA_REPORTER:-spec}", "test:client": "TEST_MODE=unit NODE_ENV=test jest", "test:client:watch": "TEST_MODE=unit NODE_ENV=test jest --watch", - "pree2e": "selenium-standalone install", - "pree2e:ci": "selenium-standalone install", - "pree2e:browserstack": "selenium-standalone install", - "e2e": "NODE_ENV=test nightwatch", + "e2e": "./scripts/e2e.js", "e2e:ci": "./scripts/e2e-ci.sh", - "e2e:browserstack": "NODE_ENV=test ./scripts/e2e-browserstack.js --config nightwatch-browserstack.conf.js", "heroku-postbuild": "npm-run-all plugins:reconcile build" }, "talk": { diff --git a/plugins/talk-plugin-moderation-actions/client/components/ApproveCommentAction.js b/plugins/talk-plugin-moderation-actions/client/components/ApproveCommentAction.js index f683acae6..125a3589e 100644 --- a/plugins/talk-plugin-moderation-actions/client/components/ApproveCommentAction.js +++ b/plugins/talk-plugin-moderation-actions/client/components/ApproveCommentAction.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import styles from './styles.css'; import {t} from 'plugin-api/beta/client/services'; import {Icon} from 'plugin-api/beta/client/components/ui'; @@ -6,16 +7,23 @@ import cn from 'classnames'; const isApproved = (status) => (status === 'ACCEPTED'); -export default ({approveComment, comment: {status}}) => ( +const ApproveCommentAction = ({approveComment, comment: {status}}) => ( isApproved(status) ? ( {t('talk-plugin-moderation-actions.approved_comment')} ) : ( - ) ); + +ApproveCommentAction.propTypes = { + approveComment: PropTypes.func, + comment: PropTypes.object, +}; + +export default ApproveCommentAction; \ No newline at end of file diff --git a/plugins/talk-plugin-moderation-actions/client/components/BanUserAction.js b/plugins/talk-plugin-moderation-actions/client/components/BanUserAction.js index 36f9b1489..75956a0b6 100644 --- a/plugins/talk-plugin-moderation-actions/client/components/BanUserAction.js +++ b/plugins/talk-plugin-moderation-actions/client/components/BanUserAction.js @@ -1,14 +1,21 @@ import React from 'react'; +import PropTypes from 'prop-types'; import styles from './styles.css'; import {t} from 'plugin-api/beta/client/services'; import {Icon} from 'plugin-api/beta/client/components/ui'; import cn from 'classnames'; -export default ({onBanUser}) => ( +const BanUserAction = ({onBanUser}) => ( ); + +BanUserAction.propTypes = { + onBanUser: PropTypes.func, +}; + +export default BanUserAction; diff --git a/plugins/talk-plugin-moderation-actions/client/components/BanUserDialog.js b/plugins/talk-plugin-moderation-actions/client/components/BanUserDialog.js index 702ee4b10..c98e3e7c2 100644 --- a/plugins/talk-plugin-moderation-actions/client/components/BanUserDialog.js +++ b/plugins/talk-plugin-moderation-actions/client/components/BanUserDialog.js @@ -1,10 +1,12 @@ import React from 'react'; +import PropTypes from 'prop-types'; +import cn from 'classnames'; import styles from './BanUserDialog.css'; import {t} from 'plugin-api/beta/client/services'; import {Dialog, Button} from 'plugin-api/beta/client/components/ui'; -export default ({showBanDialog, closeBanDialog, banUser}) => ( - +const BanUserDialog = ({showBanDialog, closeBanDialog, banUser}) => ( + ×

        {t('talk-plugin-moderation-actions.ban_user_dialog_headline')}

        {t('talk-plugin-moderation-actions.ban_user_dialog_sub')}

        @@ -12,12 +14,28 @@ export default ({showBanDialog, closeBanDialog, banUser}) => ( {t('talk-plugin-moderation-actions.ban_user_dialog_copy')}

        - -
        ); + +BanUserDialog.propTypes = { + showBanDialog: PropTypes.func.isRequired, + closeBanDialog: PropTypes.func.isRequired, + banUser: PropTypes.func.isRequired, +}; + +export default BanUserDialog; \ No newline at end of file diff --git a/plugins/talk-plugin-moderation-actions/client/components/Menu.js b/plugins/talk-plugin-moderation-actions/client/components/Menu.js index 470349b69..29120423e 100644 --- a/plugins/talk-plugin-moderation-actions/client/components/Menu.js +++ b/plugins/talk-plugin-moderation-actions/client/components/Menu.js @@ -1,9 +1,10 @@ import React from 'react'; +import PropTypes from 'prop-types'; import cn from 'classnames'; import styles from './Menu.css'; import {t} from 'plugin-api/beta/client/services'; -export default ({className = '', children}) => ( +const Menu = ({className = '', children}) => (

        {t('talk-plugin-moderation-actions.moderation_actions')} @@ -11,3 +12,10 @@ export default ({className = '', children}) => ( {children}

        ); + +Menu.propTypes = { + className: PropTypes.string, + children: PropTypes.node, +}; + +export default Menu; diff --git a/plugins/talk-plugin-moderation-actions/client/components/ModerationActions.js b/plugins/talk-plugin-moderation-actions/client/components/ModerationActions.js index fa12500de..e27626298 100644 --- a/plugins/talk-plugin-moderation-actions/client/components/ModerationActions.js +++ b/plugins/talk-plugin-moderation-actions/client/components/ModerationActions.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import cn from 'classnames'; import Menu from './Menu'; import styles from './ModerationActions.css'; @@ -21,7 +22,7 @@ export default class ModerationActions extends React.Component { } {menuVisible && ( - + ( +const RejectCommentAction = ({rejectComment}) => ( ); + +RejectCommentAction.propTypes = { + rejectComment: PropTypes.func, +}; + +export default RejectCommentAction; diff --git a/scripts/e2e-browserstack.js b/scripts/e2e-browserstack.js deleted file mode 100755 index 103cd08fe..000000000 --- a/scripts/e2e-browserstack.js +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env node - -const Nightwatch = require('nightwatch'); -const browserstack = require('browserstack-local'); -const {onshutdown} = require('../bin/util'); - -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; - } - - console.log('Connected. Now testing...'); - Nightwatch.cli(function(argv) { - Nightwatch.CliRunner(argv) - .setup(null, 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(){}); - }); - }); - }); - - 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/scripts/e2e-ci.sh b/scripts/e2e-ci.sh index 4da3b615e..3e9726340 100755 --- a/scripts/e2e-ci.sh +++ b/scripts/e2e-ci.sh @@ -1,92 +1,20 @@ #!/bin/bash -CIRCLE_TEST_REPORTS=${CIRCLE_TEST_REPORTS:-./test/e2e/reports} +REPORTS_FOLDER=${CIRCLE_TEST_REPORTS:-./test/e2e/reports} CIRCLE_BRANCH=${CIRCLE_BRANCH:-master} # Amount of retries before failure. E2E_MAX_RETRIES=${E2E_MAX_RETRIES:-1} -# Amount of seconds between tests. -E2E_SLEEP_BETWEEN_TESTS=${E2E_SLEEP_BETWEEN_TESTS:-1} - # Safari >= 8 has issues connecting to browserstack-local. Safari < 8 is too old. -BROWSERS="chrome firefox ie edge" #safari +# IE 64bit has issues with receiving keyboard input. Let's wait for them to fix it. +BROWSERS="chrome,firefox,edge" #ie safari -if [[ "${CIRCLE_BRANCH}" == "master" ]]; then - - # List of failed browsers. - failedBrowsers= - - # List of succeeded browsers. - succeededBrowsers= - - exitCode=0 - - browserstack() { - - # Current number of tries. - try=${2:-0} - - echo "-- Start e2e for $1 #$try --" - - REPORTS_FOLDER="$CIRCLE_TEST_REPORTS/$1" yarn e2e:browserstack --env "$1" - - # Determine exit code. - result=$? - if [ "$result" -ne "0" ]; then - echo "-- Failed e2e for $1 #$try --" - - # Try again until E2E_MAX_RETRIES is reached. - if [ "$try" -lt "$E2E_MAX_RETRIES" ]; then - let try=try+1 - - # Sleep a bit to let browserstack-local close properly. - sleep "$E2E_SLEEP_BETWEEN_TESTS" - - browserstack "$1" "$try" - return - fi - - # Failed, add to list of failed browsers. - failedBrowsers="$failedBrowsers $1" - - # Remember exit code. - exitCode=$result - else - echo "-- Success e2e for $1 #$try --" - - # Succeeded, add to list of succeeded browsers. - succeededBrowsers="$succeededBrowsers $1" - eval "browser_${1}_succeeded_at=$try" - fi - - # Sleep a bit to let browserstack-local close properly. - sleep "$E2E_SLEEP_BETWEEN_TESTS" - } - - # Test using browserstack. - for browser in $BROWSERS - do - browserstack "$browser" - done - - - # Print information about succeeded browsers. - for x in $succeededBrowsers - do - echo "Succeeded $x at try #$(eval "echo \$browser_${x}_succeeded_at")" - done - - # Print information about failed browsers. - for x in $failedBrowsers - do - echo "Failed $x" - done - exit $exitCode +if [[ "${CIRCLE_BRANCH}" == "master" && -n "$BROWSERSTACK_KEY" ]]; then + echo Testing on browserstack + yarn e2e --reports-folder "$REPORTS_FOLDER" --bs-key "$BROWSERSTACK_KEY" --retries "$E2E_MAX_RETRIES" --browsers $BROWSERS else # When browserstack is not available test locally using chrome headless. - REPORTS_FOLDER="$CIRCLE_TEST_REPORTS/chrome" yarn e2e --env chrome-headless - - # Will exit with status of last command. - exit $? + echo Testing locally + yarn e2e --reports-folder "$REPORTS_FOLDER" --retries "$E2E_MAX_RETRIES" --headless fi diff --git a/scripts/e2e.js b/scripts/e2e.js new file mode 100755 index 000000000..97e321310 --- /dev/null +++ b/scripts/e2e.js @@ -0,0 +1,207 @@ +#!/usr/bin/env node + +process.env['NODE_ENV'] = 'test'; + +const browserstack = require('browserstack-local'); +const {onshutdown, shutdown} = require('../bin/util'); +const program = require('commander'); +const Table = require('cli-table'); +const serve = require('../serve'); +const childProcess = require('child_process'); +const uuid = require('uuid').v4; +const mongoose = require('../services/mongoose'); + +// Make things colorful! +require('colors'); + +function startTunnel(key, localIdentifier) { + const bs_local = new browserstack.Local(); + + // Code to start browserstack local before start of test + console.log('Connecting local'); + + return new Promise((resolve, reject) => { + bs_local.start({ + key, + logFile: './test/e2e/bslocal.log', + verbose: 'true', + force: 'true', + onlyAutomate: 'true', + localIdentifier, + }, (error) => { + if (error) { + reject(error); + } + resolve(); + }); + onshutdown([ + () => bs_local.stop(function(){}), + ]); + }); +} + +function seleniumInstall() { + return new Promise((resolve, reject) => { + try { + const nw = childProcess.spawn( + './node_modules/.bin/selenium-standalone', + ['install'], + { + stdio: 'inherit', + }); + nw.on('close', (code) => { + code === 0 ? resolve() : reject(); + }); + } + catch (ex) { + reject(ex); + } + }); +} + +function nightwatch(env, config, reportsFolder, browserstack) { + return new Promise((resolve, reject) => { + try { + const nw = childProcess.spawn( + './node_modules/.bin/nightwatch', + ['--config', config, '--env', env], + { + env: Object.assign({}, process.env, { + 'BROWSERSTACK_LOCAL_IDENTIFIER': browserstack.localIdentifier, + 'BROWSERSTACK_KEY': browserstack.key, + 'BROWSERSTACK_USER': browserstack.user, + 'REPORTS_FOLDER': `${reportsFolder}/${env}`, + }), + stdio: 'inherit', + }); + nw.on('close', (code) => { + code === 0 ? resolve() : reject(); + }); + } + catch (ex) { + reject(ex); + } + }); +} + +function printResults(browsers, succeeded, retries) { + let table = new Table({ + head: [ + 'Browser'.cyan, + 'Status'.cyan, + 'Retries'.cyan, + ] + }); + + for (let browser of browsers) { + const wasSuccessful = browser in succeeded; + table.push([ + browser, + wasSuccessful ? 'success'.green : 'failed'.red, + wasSuccessful ? succeeded[browser] : retries, + ]); + } + + console.log(table.toString()); +} + +function printSection(txt) { + console.log('*****************************'.magenta); + console.log(`${'*'.magenta} ${txt.cyan}`); + console.log('*****************************'.magenta); +} + +async function runBrowserTests(browsers, config, retries = 1, reportsFolder, browserstack) { + const succeeded = {}; + for (let browser of browsers) { + for (let t = 0; t < retries + 1; t++) { + try { + printSection(`e2e test for ${browser} #${t}`); + await nightwatch(browser, config, reportsFolder, browserstack); + succeeded[browser] = t; + console.log(`\n==> Succeeded e2e for ${browser} #${t}\n`.green); + break; + } + catch (ex) { + if (ex) { + console.log('There was an error while starting the test runner:\n\n'); + process.stderr.write(`${ex.stack}\n`); + } + console.log(`\n==> Failed e2e for ${browser} #${t}\n`.red); + } + } + } + printResults(browsers, succeeded, retries); + return Object.keys(succeeded).length === browsers.length; +} + +async function start(program) { + const localIdentifier = uuid(); + let browsers = program.browsers.split(','); + const retries = Number.parseInt(program.retries); + const browserstack = {}; + const date = new Date().toISOString() + .replace(/[T.]/g, '-') + .replace(/:/g, ''); + const reportsFolder = `${program.reportsFolder}/${date}`; + let exitCode = 0; + let config = 'nightwatch.conf.js'; + try { + if (program.tunnel && program.bsKey) { + await startTunnel(program.bsKey, localIdentifier); + } + + console.log('Dropping test database'); + await mongoose.connection.dropDatabase(); + await serve(); + + if (program.bsKey) { + config = 'nightwatch-browserstack.conf.js'; + browserstack.localIdentifier = localIdentifier; + browserstack.key = program.bsKey; + browserstack.user = program.bsUser; + } else { + + // Install selenium standalone. + await seleniumInstall(); + if (program.headless) { + browsers = browsers.map((b) => `${b}-headless`); + } + } + + const succeeded = await runBrowserTests( + browsers, + config, + retries, + reportsFolder, + browserstack, + ); + if (!succeeded) { + exitCode = 1; + } + } + catch (ex) { + console.log('There was an error:\n\n'); + process.stderr.write(`${ex.stack}\n`); + process.exit(2); + } + finally { + console.log('Shutting down'); + shutdown(); + } + process.exit(exitCode); +} + +program + .version('0.1.0') + .description('Perform e2e testing locally or if browserstack credentials are provided on browserstack.') + .option('-u, --bs-user [user]', 'Browserstack user', 'coralproject2') + .option('-k, --bs-key [key]', 'Browserstack api key') + .option('--no-tunnel', 'Dont start browserstack-local') + .option('-b, --browsers [list of browsers]', 'Browsers to test', 'chrome') + .option('-r, --retries [number]', 'Number of retries before failing', '1') + .option('--headless', 'Start in headless mode for local e2e') + .option('--reports-folder [folder]', 'Reports folder', './test/e2e/reports') + .parse(process.argv); + +start(program); diff --git a/test/e2e/globals.js b/test/e2e/globals.js index a828804b4..4d2740404 100644 --- a/test/e2e/globals.js +++ b/test/e2e/globals.js @@ -1,11 +1,10 @@ -const serve = require('../../serve'); const mongoose = require('../../services/mongoose'); const {shutdown} = require('../../bin/util'); module.exports = { before: async (done) => { + console.log('Dropping test database'); await mongoose.connection.dropDatabase(); - await serve(); done(); }, after: (done) => { diff --git a/test/e2e/page_objects/admin.js b/test/e2e/page_objects/admin.js index 94ce82826..aa3b58ab5 100644 --- a/test/e2e/page_objects/admin.js +++ b/test/e2e/page_objects/admin.js @@ -7,23 +7,165 @@ module.exports = { return this .waitForElementVisible('body'); }, + openDrawer() { + this + .waitForElementVisible('@drawerButton') + .click('@drawerButton'); + this.expect.section('@drawer').to.be.visible; + return this.section.drawer; + }, + goToModerate() { + this + .click('@moderateNav') + .expect.section('@moderate').to.be.visible; + return this.section.moderate; + }, + goToStories() { + this + .click('@storiesNav') + .expect.section('@stories').to.be.visible; + return this.section.stories; + }, + goToCommunity() { + this + .click('@communityNav') + .expect.section('@community').to.be.visible; + return this.section.community; + }, + logout() { + this + .waitForElementVisible('@settingsButton') + .click('@settingsButton') + .waitForElementVisible('@signOutButton') + .click('@signOutButton'); + }, + navigateAndLogin(user) { + this + .navigate() + .expect.section('@login').to.be.visible; + return this.section.login.login(user); + }, }], elements: { - 'loginLayout': '.talk-admin-login', - 'signInForm': '.talk-admin-login-sign-in', - 'emailInput': '.talk-admin-login-sign-in #email', - 'passwordInput': '.talk-admin-login-sign-in #password', - 'signInButton': '.talk-admin-login-sign-in-button', - 'storiesNav': '.talk-admin-nav-stories', - 'storiesDrawerNav': '.talk-admin-drawer-nav .talk-admin-nav-stories', - 'storiesSection': '.talk-admin-stories', - 'communityNav': '.talk-admin-nav-community', - 'communityDrawerNav': '.talk-admin-drawer-nav .talk-admin-nav-community', - 'communitySection': '.talk-admin-community', - 'moderationContainer': '.talk-admin-moderation-container', - 'drawerButton': '.mdl-layout__drawer-button', - 'drawerOverlay': 'div.mdl-layout__obfuscator.is-visible', - 'settingsButton': '.talk-admin-header-settings-button', - 'signOutButton': '.talk-admin-header-sign-out', - } + drawerButton: '.mdl-layout__drawer-button', + drawerOverlay: 'div.mdl-layout__obfuscator.is-visible', + storiesNav: '.talk-admin-nav-stories', + communityNav: '.talk-admin-nav-community', + moderateNav: '.talk-admin-nav-moderate', + settingsButton: '.talk-admin-header-settings-button', + signOutButton: '.talk-admin-header-sign-out', + suspendUserDialog: '.talk-admin-suspend-user-dialog', + suspendUserConfirmButton: '.talk-admin-suspend-user-dialog-confirm', + supendUserSendButton: '.talk-admin-suspend-user-dialog-send', + toast: '.toastify', + toastClose: '.toastify__close', + }, + sections: { + moderate: { + selector: '.talk-admin-moderation-container', + elements: { + comment: '.talk-admin-moderate-comment', + commentActionMenu: '.talk-admin-moderate-comment-actions-menu', + actionItemSuspendUser: '.action-menu-item#suspendUser', + actionMenuButton: '.talk-admin-moderate-comment-actions-menu #actions-dropdown-0' + } + }, + stories: { + selector: '.talk-admin-stories', + }, + community: { + selector: '.talk-admin-community', + commands: [{ + url: function() { + return `${this.api.launchUrl}/admin/community`; + }, + ready() { + return this + .waitForElementVisible('body'); + }, + goToPeople() { + this + .click('@peopleNav') + .expect.section('@people').to.be.visible; + return this.section.people; + }, + }], + elements: { + peopleNav: '.talk-admin-nav-people', + flaggedAccountsNav: '.talk-admin-nav-flagged-accounts', + flaggedAccountsContainer: '.talk-adnin-community-flagged-accounts', + flaggedUser:'.talk-admin-community-flagged-user', + flaggedUserApproveButton: '.talk-admin-flagged-user-approve-button', + flaggedUserRejectButton: '.talk-admin-flagged-user-reject-button', + usernameDialog: '.talk-reject-username-dialog', + usernameDialogButtons: '.talk-reject-username-dialog-buttons', + usernameDialogSuspend: '.talk-reject-username-dialog-button-k', + usernameDialogSuspensionMessage: '.talk-reject-username-dialog-suspension-message' + }, + sections: { + people: { + selector: '.talk-admin-community-people-container', + elements: { + firstRow: '.talk-admin-community-people-row:first-child', + dropdownStatus: '.talk-admin-community-people-dd-status', + dropdownRole: '.talk-admin-community-people-dd-role', + dropdownStatusActive: '.talk-admin-community-people-dd-status .dd-list-active', + optionActive: '.dd-option#ACTIVE', + optionBanned: '.dd-option#BANNED', + } + } + } + }, + drawer: { + selector: '.talk-admin-drawer-nav', + commands: [{ + goToStories() { + this + .click('@storiesButton'); + this.parent.expect.section('@stories').to.be.visible; + this.close(); + return this.parent.section.stories; + }, + goToCommunity() { + this + .click('@communityButton'); + this.parent.expect.section('@community').to.be.visible; + this.close(); + return this.parent.section.stories; + }, + close() { + this.parent + .click('@drawerOverlay') + .waitForElementNotPresent('@drawerOverlay'); + return this.parent; + }, + }], + elements: { + 'storiesButton': '.talk-admin-drawer-nav .talk-admin-nav-stories', + 'communityButton': '.talk-admin-drawer-nav .talk-admin-nav-community', + }, + }, + login: { + commands: [{ + login(user) { + this + .waitForElementVisible('@signInForm') + .setValue('@emailInput', user.email) + .setValue('@passwordInput', user.password) + .waitForElementVisible('@signInButton') + .click('@signInButton'); + const adminPage = this.api.page.admin(); + adminPage.expect.section('@moderate').to.be.visible; + return adminPage.section.moderate; + }, + }], + selector: '.talk-admin-login', + elements: { + 'signInForm': '.talk-admin-login-sign-in', + 'emailInput': '.talk-admin-login-sign-in #email', + 'passwordInput': '.talk-admin-login-sign-in #password', + 'signInButton': '.talk-admin-login-sign-in-button', + } + }, + }, }; diff --git a/test/e2e/page_objects/adminCommunity.js b/test/e2e/page_objects/adminCommunity.js deleted file mode 100644 index ef3ccbb4c..000000000 --- a/test/e2e/page_objects/adminCommunity.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = { - commands: [{ - url: function() { - return `${this.api.launchUrl}/admin/community`; - }, - ready() { - return this - .waitForElementVisible('body'); - }, - }], - elements: { - container: '.talk-admin-community', - flaggedAccountsContainer: '.talk-adnin-community-flagged-accounts', - flaggedUser:'.talk-admin-community-flagged-user', - flaggedUserApproveButton: '.talk-admin-flagged-user-approve-button', - flaggedUserRejectButton: '.talk-admin-flagged-user-reject-button', - usernameDialog: '.talk-reject-username-dialog', - usernameDialogButtons: '.talk-reject-username-dialog-buttons', - usernameDialogSuspend: '.talk-reject-username-dialog-button-k', - usernameDialogSuspensionMessage: '.talk-reject-username-dialog-suspension-message' - } -}; diff --git a/test/e2e/page_objects/embedStream.js b/test/e2e/page_objects/embedStream.js index ff02be476..1cecad2a3 100644 --- a/test/e2e/page_objects/embedStream.js +++ b/test/e2e/page_objects/embedStream.js @@ -1,21 +1,38 @@ const iframeId = 'coralStreamEmbed_iframe'; +const SortedWindowHandler = require('../utils/SortedWindowHandler'); module.exports = { commands: [{ + ready: function() { + this.switchToIframe(); + this.expect.section('@comments').to.be.visible; + return this.section.comments; + }, + goToProfileSection: function() { + this.waitForElementVisible('@profileTabButton'); + this.click('@profileTabButton'); + this.expect.section('@profile').to.be.visible; + return this.section.profile; + }, + goToCommentsSection: function() { + this.waitForElementVisible('@commentsTabButton'); + this.click('@commentsTabButton'); + this.expect.section('@comments').to.be.visible; + return this.section.comments; + }, navigateToAsset: function(asset) { this.api.url(`${this.api.launchUrl}/assets/title/${asset}`); return this; }, - getEmbedSection: function() { + switchToIframe: function() { this.waitForElementVisible('@iframe'); // Pause a bit to let iframe initialize in the hope that it'll // fix https://www.browserstack.com/automate/builds/96419cf46e3d6376a36ae6d3f90934112df1ed91/sessions/224f1a1566c1c8c7859e2e76ece51862200f0173#automate_button - this.api.pause(200); + this.api.pause(1000); this.api.frame(iframeId); - this.expect.section('@embed').to.be.present; - return this.section.embed; + return this; }, }], url: function() { @@ -23,28 +40,66 @@ module.exports = { }, elements: { iframe: `#${iframeId}`, + commentsTabButton: '.talk-embed-stream-comments-tab > button', + profileTabButton: '.talk-embed-stream-profile-tab > button', + banDialog: '.talk-ban-user-dialog', + banDialogConfirmButton: '.talk-ban-user-dialog-button-confirm', }, sections: { - embed: { + comments: { commands: [{ - getProfileSection: function() { - this.waitForElementVisible('@profileTabButton'); - this.click('@profileTabButton'); - this.expect.section('@profile').to.be.present; - return this.section.profile; + openLoginPopup(callback) { + const windowHandler = new SortedWindowHandler(this.api); + + this + .waitForElementVisible('@signInButton') + .click('@signInButton'); + + // Wait for window to be created + // https://www.browserstack.com/automate/builds/1ceccf4efb4683b7feb890f45a32b5922b40ed3f/sessions/17b1a79682bef2498cb0be86eac317a08c976b0a#automate_button + this.api.pause(200); + + // Focusing on the Login PopUp + windowHandler.windowHandles((handles) => { + this.api.switchWindow(handles[1]); + }); + + const popup = this.api.page.popup().ready(); + callback(popup); + + // Give a tiny bit of time to let popup close. + this.api.pause(50); + + if (this.api.capabilities.browserName === 'MicrosoftEdge') { + + // More time for edge. + // https://www.browserstack.com/automate/builds/1ceccf4efb4683b7feb890f45a32b5922b40ed3f/sessions/7393dbfda8387e43b6d5851f359b0c07db414973 + this.api.pause(1000); + } + + // Focusing on the Embed Window + windowHandler.windowHandles((handles) => { + this.api.switchWindow(handles[0]); + + // For some reasons firefox does not automatically load auth after login. + // https://www.browserstack.com/automate/builds/37650cb4e66c6edce0ba0800a1c1b7e7f74bf991/sessions/7a4e9da69b0f9ecdf8b7fa9150639e47b1532cb0#automate_button + if (this.api.capabilities.browserName === 'firefox') { + this.parent.navigate().ready(); + } else { + this.parent.switchToIframe(); + } + }); + return this; }, - getCommentsSection: function() { - this.waitForElementVisible('@commentsTabButton'); - this.click('@commentsTabButton'); - this.expect.section('@comments').to.be.present; - return this.section.comments; + logout() { + this + .waitForElementVisible('@logoutButton') + .click('@logoutButton'); }, }], - selector: '#talk-embed-stream-container', + selector: '.talk-embed-stream-comments-tab-pane', elements: { logoutButton: '.talk-stream-userbox-logout', - commentsTabButton: '.talk-embed-stream-comments-tab > button', - profileTabButton: '.talk-embed-stream-profile-tab > button', signInButton: '#coralSignInButton', commentBoxTextarea: '#commentText', commentBoxPostButton: '.talk-plugin-commentbox-button', @@ -62,24 +117,30 @@ module.exports = { elements: { offensiveUsernameRadio: '.talk-plugin-flags-popup-radio#USERNAME_OFFENSIVE', flagUsernameRadio: '.talk-plugin-flags-popup-radio#USERS', + flagCommentRadio: '.talk-plugin-flags-popup-radio#COMMENTS', continueButton: '.talk-plugin-flags-popup-button', popUpText: '.talk-plugin-flags-popup-text', + spamCommentRadio: '.talk-plugin-flags-popup-radio#COMMENT_SPAM', } }, - profile: { - selector: '.talk-embed-stream-profile-tab-pane', + mod: { + selector: '.talk-plugin-moderation-actions', elements: { - notLoggedIn: '.talk-embed-stream-not-logged-in', - myCommentHistory: '.talk-my-profile-comment-history', - myCommentHistoryReactions: '.talk-my-profile-comment-history .comment-summary .comment-summary-reactions', - myCommentHistoryReactionCount: '.talk-my-profile-comment-history .comment-summary .comment-summary-reactions .comment-summary-reaction-count', - myCommentHistoryComment: '.talk-my-profile-comment-history .my-comment-body', + arrow: '.talk-plugin-moderation-actions-arrow', + menu: '.talk-plugin-modetarion-actions-menu', + banButton: '.talk-plugin-moderation-actions-ban', }, }, - comments: { - selector: '.talk-embed-stream-comments-tab-pane', - elements: {}, - }, + }, + }, + profile: { + selector: '.talk-embed-stream-profile-tab-pane', + elements: { + notLoggedIn: '.talk-embed-stream-not-logged-in', + myCommentHistory: '.talk-my-profile-comment-history', + myCommentHistoryReactions: '.talk-my-profile-comment-history .comment-summary .comment-summary-reactions', + myCommentHistoryReactionCount: '.talk-my-profile-comment-history .comment-summary .comment-summary-reactions .comment-summary-reaction-count', + myCommentHistoryComment: '.talk-my-profile-comment-history .my-comment-body', }, }, }, diff --git a/test/e2e/page_objects/login.js b/test/e2e/page_objects/login.js deleted file mode 100644 index 1abc40aa8..000000000 --- a/test/e2e/page_objects/login.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - elements: { - registerButton: '#coralRegister', - signInButton: '#coralSignInButton', - emailInput: '#email', - usernameInput: '#username', - passwordInput: '#password', - confirmPasswordInput: '#confirmPassword', - signUpButton: '#coralSignUpButton', - signIn: '.coral-sign-in', - loginButton: '#coralLogInButton' - }, -}; diff --git a/test/e2e/page_objects/popup.js b/test/e2e/page_objects/popup.js new file mode 100644 index 000000000..4d2aee7b6 --- /dev/null +++ b/test/e2e/page_objects/popup.js @@ -0,0 +1,41 @@ +module.exports = { + commands: [{ + ready() { + return this + .waitForElementVisible('body'); + }, + login(user) { + return this + .setValue('@emailInput', user.email) + .setValue('@passwordInput', user.password) + .waitForElementVisible('@signIn') + .waitForElementVisible('@loginButton') + .click('@loginButton'); + }, + register(user) { + return this + .waitForElementVisible('@registerButton') + .click('@registerButton') + .setValue('@emailInput', user.email) + .setValue('@usernameInput', user.username) + .setValue('@passwordInput', user.password) + .setValue('@confirmPasswordInput', user.password) + .waitForElementVisible('@signUpButton') + .click('@signUpButton') + .waitForElementVisible('@signIn') + .waitForElementVisible('@loginButton') + .click('@loginButton'); + }, + }], + elements: { + registerButton: '#coralRegister', + signInButton: '#coralSignInButton', + emailInput: '#email', + usernameInput: '#username', + passwordInput: '#password', + confirmPasswordInput: '#confirmPassword', + signUpButton: '#coralSignUpButton', + signIn: '.coral-sign-in', + loginButton: '#coralLogInButton' + }, +}; diff --git a/test/e2e/specs/01_install.js b/test/e2e/specs/01_install.js index 25c902e0f..078a22e50 100644 --- a/test/e2e/specs/01_install.js +++ b/test/e2e/specs/01_install.js @@ -1,12 +1,27 @@ module.exports = { '@tags': ['install'], + before: (client) => { + client.resizeWindow(1600, 1200); + }, + + afterEach: (client, done) => { + if (client.currentTest.results.failed) { + throw new Error('Test Case failed, skipping all the rest'); + } + done(); + }, + + after: (client) => { + client.end(); + }, + 'User goes to install': (client) => { const install = client.page.install(); install .navigate() - .expect.section('@step1').to.be.present; + .expect.section('@step1').to.be.visible; }, 'User clicks get started button': (client) => { @@ -20,7 +35,7 @@ module.exports = { const install = client.page.install(); install - .expect.section('@step2').to.be.present; + .expect.section('@step2').to.be.visible; }, 'User fills step 2': (client) => { const step2 = client.page.install().section.step2; @@ -36,7 +51,7 @@ module.exports = { const install = client.page.install(); install - .expect.section('@step3').to.be.present; + .expect.section('@step3').to.be.visible; }, 'User fills step 3': (client) => { const step3 = client.page.install().section.step3; @@ -54,7 +69,7 @@ module.exports = { const install = client.page.install(); install - .expect.section('@step4').to.be.present; + .expect.section('@step4').to.be.visible; }, 'User fills step 4': (client) => { const step4 = client.page.install().section.step4; @@ -73,9 +88,6 @@ module.exports = { const install = client.page.install(); install - .expect.section('@step5').to.be.present; + .expect.section('@step5').to.be.visible; }, - after: (client) => { - client.end(); - } }; diff --git a/test/e2e/specs/02_admin.js b/test/e2e/specs/02_admin.js index 1f56ef2b8..29957e487 100644 --- a/test/e2e/specs/02_admin.js +++ b/test/e2e/specs/02_admin.js @@ -1,56 +1,41 @@ module.exports = { '@tags': ['admin', 'login'], - beforeEach: (client) => { + before: (client) => { client.resizeWindow(1024, 800); }, + + afterEach: (client, done) => { + if (client.currentTest.results.failed) { + throw new Error('Test Case failed, skipping all the rest'); + } + done(); + }, + + after: (client) => { + client.end(); + }, + 'Admin logs in': (client) => { const adminPage = client.page.admin(); const {testData: {admin}} = client.globals; - adminPage - .navigate() - .waitForElementVisible('@loginLayout') - .waitForElementVisible('@signInForm') - .setValue('@emailInput', admin.email) - .setValue('@passwordInput', admin.password) - .waitForElementVisible('@signInButton') - .click('@signInButton'); - - adminPage - .waitForElementVisible('@moderationContainer'); + adminPage.navigateAndLogin(admin); }, 'Admin goes to Stories': (client) => { const adminPage = client.page.admin(); adminPage - .navigate() - .waitForElementVisible('@drawerButton') - .click('@drawerButton') - .waitForElementVisible('@storiesDrawerNav') - .click('@storiesDrawerNav') - .waitForElementVisible('@drawerOverlay') - .click('@drawerOverlay') - .waitForElementVisible('@storiesSection'); - + .openDrawer() + .goToStories(); }, 'Admin goes to Community': (client) => { const adminPage = client.page.admin(); adminPage - .navigate() - .waitForElementVisible('@drawerButton') - .click('@drawerButton') - .waitForElementVisible('@communityDrawerNav') - .click('@communityDrawerNav') - .waitForElementVisible('@drawerOverlay') - .click('@drawerOverlay') - .waitForElementVisible('@communitySection'); + .openDrawer() + .goToCommunity(); }, - - after: (client) => { - client.end(); - } }; diff --git a/test/e2e/specs/03_embedStream.js b/test/e2e/specs/03_embedStream.js index 53da1e87d..567596dd2 100644 --- a/test/e2e/specs/03_embedStream.js +++ b/test/e2e/specs/03_embedStream.js @@ -1,7 +1,21 @@ -const SortedWindowHandler = require('../utils/SortedWindowHandler'); - module.exports = { '@tags': ['embedStream', 'login'], + + before: (client) => { + client.resizeWindow(1600, 1200); + }, + + afterEach: (client, done) => { + if (client.currentTest.results.failed) { + throw new Error('Test Case failed, skipping all the rest'); + } + done(); + }, + + after: (client) => { + client.end(); + }, + 'creates a new asset': (client) => { const asset = 'newAssetTest'; const embedStream = client.page.embedStream(); @@ -9,92 +23,43 @@ module.exports = { embedStream .navigateToAsset(asset) .assert.title(asset) - .getEmbedSection(); + .ready(); }, 'creates an user and user logs in': (client) => { const {testData: {user}} = client.globals; const embedStream = client.page.embedStream(); - const embed = embedStream + // Go back to default asset. + const comments = + embedStream .navigate() - .getEmbedSection(); + .ready(); - const windowHandler = new SortedWindowHandler(client); - - embed - .waitForElementVisible('@signInButton') - .click('@signInButton'); - - // Wait for window to be created - // https://www.browserstack.com/automate/builds/1ceccf4efb4683b7feb890f45a32b5922b40ed3f/sessions/17b1a79682bef2498cb0be86eac317a08c976b0a#automate_button - client.pause(200); - - // Focusing on the Login PopUp - windowHandler.windowHandles((handles) => { - client.switchWindow(handles[1]); - }); - - const login = client.page.login(); - - login - .waitForElementVisible('@registerButton') - .click('@registerButton') - .setValue('@emailInput', user.email) - .setValue('@usernameInput', user.username) - .setValue('@passwordInput', user.password) - .setValue('@confirmPasswordInput', user.password) - .waitForElementVisible('@signUpButton') - .click('@signUpButton') - .waitForElementVisible('@signIn') - .waitForElementVisible('@loginButton') - .click('@loginButton'); - - // Give a tiny bit of time to let popup close. - client.pause(50); - - if (client.capabilities.browserName === 'MicrosoftEdge') { - - // More time for edge. - // https://www.browserstack.com/automate/builds/1ceccf4efb4683b7feb890f45a32b5922b40ed3f/sessions/7393dbfda8387e43b6d5851f359b0c07db414973 - client.pause(1000); - } - - // Focusing on the Embed Window - windowHandler.windowHandles((handles) => { - client.switchWindow(handles[0]); - }); + comments + .openLoginPopup((popup) => { + popup.register(user); + }); }, 'user posts a comment': (client) => { - const embedStream = client.page.embedStream(); + const comments = client.page.embedStream().section.comments; const {testData: {comment}} = client.globals; - const embed = embedStream - .navigate() - .getEmbedSection(); - - embed + comments .waitForElementVisible('@commentBoxTextarea') .setValue('@commentBoxTextarea', comment.body) .waitForElementVisible('@commentBoxPostButton') .click('@commentBoxPostButton') .waitForElementVisible('@firstCommentContent') .getText('@firstCommentContent', (result) => { - embed.assert.equal(result.value, comment.body); + comments.assert.equal(result.value, comment.body); }); }, 'signed in user sees comment history': (client) => { - const embedStream = client.page.embedStream(); + const profile = client.page.embedStream().goToProfileSection(); const {testData: {comment}} = client.globals; - const embed = embedStream - .navigate() - .getEmbedSection(); - - const profile = embed - .getProfileSection(); - profile .waitForElementVisible('@myCommentHistory') .waitForElementVisible('@myCommentHistoryComment') @@ -103,14 +68,7 @@ module.exports = { }); }, 'user sees replies and reactions to comments': (client) => { - const embedStream = client.page.embedStream(); - - const embed = embedStream - .navigate() - .getEmbedSection(); - - const profile = embed - .getProfileSection(); + const profile = client.page.embedStream().section.profile; profile .waitForElementVisible('@myCommentHistory') @@ -122,17 +80,12 @@ module.exports = { }, 'user goes to the stream and replies and reacts to comment': (client) => { const embedStream = client.page.embedStream(); - - const embed = embedStream - .navigate() - .getEmbedSection(); - - embed + const comments = embedStream.goToCommentsSection(); + comments .waitForElementVisible('@respectButton') .click('@respectButton'); - const profile = embed - .getProfileSection(); + const profile = embedStream.goToProfileSection(); profile .waitForElementVisible('@myCommentHistory') @@ -144,31 +97,16 @@ module.exports = { }, 'user logs out': (client) => { const embedStream = client.page.embedStream(); + const comments = embedStream.goToCommentsSection(); - const embed = embedStream - .navigate() - .getEmbedSection(); - - embed - .waitForElementVisible('@commentsTabButton') - .click('@commentsTabButton') - .waitForElementVisible('@logoutButton') - .click('@logoutButton'); + comments + .logout(); }, 'not logged in user clicks my profile tab': (client) => { const embedStream = client.page.embedStream(); - - const embed = embedStream - .navigate() - .getEmbedSection(); - - const profile = embed - .getProfileSection(); + const profile = embedStream.goToProfileSection(); profile .assert.visible('@notLoggedIn'); }, - after: (client) => { - client.end(); - } }; diff --git a/test/e2e/specs/04_userStatus.js b/test/e2e/specs/04_userStatus.js index 32bf6b72a..5a7593e53 100644 --- a/test/e2e/specs/04_userStatus.js +++ b/test/e2e/specs/04_userStatus.js @@ -1,36 +1,39 @@ module.exports = { + + before: (client) => { + client.resizeWindow(1600, 1200); + }, + + afterEach: (client, done) => { + if (client.currentTest.results.failed) { + throw new Error('Test Case failed, skipping all the rest'); + } + done(); + }, + + after: (client) => { + client.end(); + }, + 'admin logs in': (client) => { const adminPage = client.page.admin(); const {testData: {admin}} = client.globals; - adminPage - .navigate() - .waitForElementVisible('@loginLayout') - .waitForElementVisible('@signInForm') - .setValue('@emailInput', admin.email) - .setValue('@passwordInput', admin.password) - .waitForElementVisible('@signInButton') - .click('@signInButton'); - - client.pause(3000); - - adminPage - .waitForElementVisible('@moderationContainer'); + adminPage.navigateAndLogin(admin); }, 'admin flags user\'s username as offensive': (client) => { const embedStream = client.page.embedStream(); - const flagSection = client.page.embedStream().section.embed.section.flag; - const embed = embedStream + const comments = embedStream .navigate() - .getEmbedSection(); + .ready(); - embed + comments .waitForElementVisible('@firstComment') .waitForElementVisible('@flagButton') .click('@flagButton'); - flagSection + comments.section.flag .waitForElementVisible('@flagUsernameRadio') .click('@flagUsernameRadio') .waitForElementVisible('@continueButton') @@ -42,26 +45,27 @@ module.exports = { .click('@continueButton'); }, 'admin goes to Reported Usernames': (client) => { - const community = client.page.adminCommunity(); + const adminPage = client.page.admin(); + + const community = adminPage + .navigate() + .ready() + .goToCommunity(); community - .navigate(); - - community - .waitForElementVisible('@container') .waitForElementVisible('@flaggedAccountsContainer') .waitForElementVisible('@flaggedUser'); }, 'admin rejects the user flag': (client) => { - const community = client.page.adminCommunity(); - + const community = client.page.admin().section.community; + community .waitForElementVisible('@flaggedUserRejectButton') .click('@flaggedUserRejectButton'); }, 'admin suspends the user': (client) => { - const community = client.page.adminCommunity(); - + const community = client.page.admin().section.community; + community .waitForElementVisible('@usernameDialog') .waitForElementVisible('@usernameDialogButtons') @@ -72,86 +76,42 @@ module.exports = { .waitForElementNotPresent('@flaggedUser'); }, 'admin logs out': (client) => { - const admin = client.page.admin(); - - admin - .waitForElementVisible('@settingsButton') - .click('@settingsButton') - .waitForElementVisible('@signOutButton') - .click('@signOutButton'); + client.page.admin().logout(); }, 'user logs in': (client) => { const {testData: {user}} = client.globals; const embedStream = client.page.embedStream(); - const embed = embedStream + embedStream .navigate() - .getEmbedSection(); - - embed - .waitForElementVisible('@signInButton') - .click('@signInButton'); - - client.pause(3000); - - // Focusing on the Login PopUp - client.windowHandles((result) => { - const handle = result.value[1]; - client.switchWindow(handle); - }); - - const login = client.page.login(); - - login - .setValue('@emailInput', user.email) - .setValue('@passwordInput', user.password) - .waitForElementVisible('@signIn') - .waitForElementVisible('@loginButton') - .click('@loginButton'); - - // Focusing on the Embed Window - client.windowHandles((result) => { - const handle = result.value[0]; - client.switchWindow(handle); - }); + .ready() + .openLoginPopup((popup) => popup.login(user)); }, 'user account is suspended, should see restricted message box': (client) => { const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; - const embed = embedStream - .navigate() - .getEmbedSection(); - - embed + comments .waitForElementVisible('@restrictedMessageBox'); }, - 'user picks another username': (client) => { - const {testData: {user}} = client.globals; const embedStream = client.page.embedStream(); - - const embed = embedStream - .navigate() - .getEmbedSection(); + const comments = embedStream.section.comments; + const {testData: {user}} = client.globals; - embed + comments .waitForElementVisible('@suspendedAccountInput') - .setValue('@suspendedAccountInput', `${user.username}-alternative`) + .setValue('@suspendedAccountInput', `${user.username}_alternative`) .waitForElementVisible('@suspendedAccountSubmitButton') - .click('@suspendedAccountSubmitButton'); + .click('@suspendedAccountSubmitButton') + .waitForElementNotPresent('@suspendedAccountInput'); }, 'user should not be able to comment': (client) => { const embedStream = client.page.embedStream(); - - const embed = embedStream - .navigate() - .getEmbedSection(); + const comments = embedStream.section.comments; - embed + comments .waitForElementNotPresent('@commentBoxTextarea') .waitForElementNotPresent('@commentBoxPostButton'); }, - after: (client) => { - client.end(); - } }; diff --git a/test/e2e/specs/05_banUser.js b/test/e2e/specs/05_banUser.js new file mode 100644 index 000000000..da0d11e7a --- /dev/null +++ b/test/e2e/specs/05_banUser.js @@ -0,0 +1,125 @@ +module.exports = { + + before: (client) => { + client.resizeWindow(1600, 1200); + }, + + afterEach: (client, done) => { + if (client.currentTest.results.failed) { + throw new Error('Test Case failed, skipping all the rest'); + } + done(); + }, + + after: (client) => { + client.end(); + }, + + 'admin logs in': (client) => { + const adminPage = client.page.admin(); + const {testData: {admin}} = client.globals; + + adminPage.navigateAndLogin(admin); + }, + 'navigate to the embed stream': (client) => { + const embedStream = client.page.embedStream(); + + embedStream + .navigate() + .ready(); + }, + 'admin bans user': (client) => { + const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; + + comments.section.mod + .waitForElementVisible('@arrow') + .click('@arrow') + .waitForElementVisible('@menu') + .waitForElementVisible('@banButton') + .click('@banButton'); + + embedStream + .waitForElementVisible('@banDialog') + .waitForElementVisible('@banDialogConfirmButton') + .click('@banDialogConfirmButton') + .waitForElementNotVisible('@banDialog'); + }, + 'admin logs out': (client) => { + const comments = client.page.embedStream().section.comments; + + comments + .logout(); + }, + 'user logs in': (client) => { + const {testData: {user}} = client.globals; + const comments = client.page.embedStream().section.comments; + + comments + .openLoginPopup((popup) => popup.login(user)); + }, + 'user account is banned, should see restricted message box': (client) => { + const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; + + comments + .waitForElementVisible('@restrictedMessageBox'); + }, + 'user logs out': (client) => { + const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; + + comments + .logout(); + }, + 'admin logs in (2)': (client) => { + const adminPage = client.page.admin(); + const {testData: {admin}} = client.globals; + + adminPage.navigateAndLogin(admin); + }, + 'admin goes to community': (client) => { + const adminPage = client.page.admin(); + + adminPage + .goToCommunity() + .goToPeople(); + }, + 'admin removes ban from user': (client) => { + const people = client.page.admin() + .section.community + .section.people; + + people + .waitForElementVisible('@firstRow') + .waitForElementVisible('@dropdownStatus') + .click('@dropdownStatus') + .waitForElementVisible('@dropdownStatusActive') + .click('@optionActive'); + }, + 'admin logs out 2': (client) => { + client.page.admin().logout(); + }, + 'navigate to the embed stream 2': (client) => { + const embedStream = client.page.embedStream(); + + embedStream + .navigate() + .ready(); + }, + 'user logs in 2': (client) => { + const {testData: {user}} = client.globals; + const comments = client.page.embedStream().section.comments; + + comments + .openLoginPopup((popup) => popup.login(user)); + }, + 'user should be able to comment': (client) => { + const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; + + comments + .waitForElementVisible('@commentBoxTextarea') + .waitForElementVisible('@commentBoxPostButton'); + }, +}; diff --git a/test/e2e/specs/06_suspendUser.js b/test/e2e/specs/06_suspendUser.js new file mode 100644 index 000000000..20f38c2c2 --- /dev/null +++ b/test/e2e/specs/06_suspendUser.js @@ -0,0 +1,138 @@ +module.exports = { + + before: (client) => { + client.resizeWindow(1600, 1200); + }, + + afterEach: (client, done) => { + if (client.currentTest.results.failed) { + throw new Error('Test Case failed, skipping all the rest'); + } + done(); + }, + + after: (client) => { + client.end(); + }, + 'user logs in': (client) => { + const {testData: {user}} = client.globals; + const embedStream = client.page.embedStream(); + const comments = client.page.embedStream().section.comments; + + embedStream + .navigate() + .ready(); + + comments + .openLoginPopup((popup) => popup.login(user)); + }, + 'user posts comment': (client) => { + const comments = client.page.embedStream().section.comments; + const {testData: {comment}} = client.globals; + + comments + .waitForElementVisible('@commentBoxTextarea') + .setValue('@commentBoxTextarea', comment.body) + .waitForElementVisible('@commentBoxPostButton') + .click('@commentBoxPostButton') + .waitForElementVisible('@firstCommentContent') + .getText('@firstCommentContent', (result) => { + comments.assert.equal(result.value, comment.body); + }); + }, + 'user logs out': (client) => { + const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; + + comments + .logout(); + }, + 'admin logs in': (client) => { + const adminPage = client.page.admin(); + const {testData: {admin}} = client.globals; + + adminPage.navigateAndLogin(admin); + }, + 'navigate to the embed stream': (client) => { + const embedStream = client.page.embedStream(); + + embedStream + .navigate() + .ready(); + }, + 'admin reports comment': (client) => { + const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; + + comments + .waitForElementVisible('@firstComment') + .waitForElementVisible('@flagButton') + .click('@flagButton'); + + comments.section.flag + .waitForElementVisible('@flagCommentRadio') + .click('@flagCommentRadio') + .waitForElementVisible('@continueButton') + .click('@continueButton') + .waitForElementVisible('@spamCommentRadio') + .click('@spamCommentRadio') + .click('@continueButton') + .waitForElementVisible('@popUpText') + .click('@continueButton'); + }, + 'admin suspends user': (client) => { + const adminPage = client.page.admin(); + const moderate = adminPage.section.moderate; + + adminPage + .navigate() + .ready() + .goToModerate(); + + moderate + .waitForElementVisible('@comment') + .waitForElementVisible('@commentActionMenu') + .waitForElementVisible('@actionMenuButton') + .click('@actionMenuButton') + .waitForElementVisible('@actionItemSuspendUser') + .click('@actionItemSuspendUser'); + + adminPage + .waitForElementVisible('@suspendUserDialog') + .waitForElementVisible('@suspendUserConfirmButton') + .click('@suspendUserConfirmButton') + .waitForElementVisible('@supendUserSendButton') + .click('@supendUserSendButton'); + + adminPage + .waitForElementVisible('@toast') + .waitForElementVisible('@toastClose') + .click('@toastClose'); + + }, + 'admin logs out': (client) => { + const adminPage = client.page.admin(); + + adminPage + .logout(); + }, + 'user logs in (2)': (client) => { + const {testData: {user}} = client.globals; + const embedStream = client.page.embedStream(); + const comments = client.page.embedStream().section.comments; + + embedStream + .navigate() + .ready(); + + comments + .openLoginPopup((popup) => popup.login(user)); + }, + 'user account is suspended, should see restricted message box': (client) => { + const embedStream = client.page.embedStream(); + const comments = embedStream.section.comments; + + comments + .waitForElementVisible('@restrictedMessageBox'); + }, +};