Half way in improving e2e test infrastructure and refactoring tests to the enhanced (un)subscription process
This commit is contained in:
parent
62cc881fd4
commit
328034bae0
16 changed files with 482 additions and 153 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
.idea
|
||||||
node_modules
|
node_modules
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
5
app.js
5
app.js
|
@ -184,8 +184,9 @@ app.use((req, res, next) => {
|
||||||
res.locals.customScripts = config.customscripts || [];
|
res.locals.customScripts = config.customscripts || [];
|
||||||
|
|
||||||
let bodyClasses = [];
|
let bodyClasses = [];
|
||||||
app.get('env') === 'test' && bodyClasses.push('page--' + (req.path.substring(1).replace(/\//g, '--') || 'home'));
|
if (req.user) {
|
||||||
req.user && bodyClasses.push('logged-in user-' + req.user.username);
|
bodyClasses.push('logged-in user-' + req.user.username);
|
||||||
|
}
|
||||||
res.locals.bodyClass = bodyClasses.join(' ');
|
res.locals.bodyClass = bodyClasses.join(' ');
|
||||||
|
|
||||||
settingsModel.list(['ua_code', 'shoutout'], (err, configItems) => {
|
settingsModel.list(['ua_code', 'shoutout'], (err, configItems) => {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
"sqldumptest": "NODE_ENV=test DUMP_NAME_SUFFIX=-test npm run sqldump",
|
"sqldumptest": "NODE_ENV=test DUMP_NAME_SUFFIX=-test npm run sqldump",
|
||||||
"sqlresettest": "NODE_ENV=test npm run sqldrop && NODE_ENV=test npm run sqlinit",
|
"sqlresettest": "NODE_ENV=test npm run sqldrop && NODE_ENV=test npm run sqlinit",
|
||||||
"starttest": "NODE_ENV=test node index.js",
|
"starttest": "NODE_ENV=test node index.js",
|
||||||
"_e2e": "NODE_ENV=test mocha test/e2e/index.js",
|
"_e2e": "NODE_ENV=test node test/e2e/index.js",
|
||||||
"e2e": "npm run sqlresettest && npm run _e2e"
|
"e2e": "npm run sqlresettest && npm run _e2e"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -70,6 +70,7 @@
|
||||||
"faker": "^4.1.0",
|
"faker": "^4.1.0",
|
||||||
"feedparser": "^2.1.0",
|
"feedparser": "^2.1.0",
|
||||||
"file-type": "^4.1.0",
|
"file-type": "^4.1.0",
|
||||||
|
"fs-extra": "^3.0.1",
|
||||||
"geoip-ultralight": "^0.1.5",
|
"geoip-ultralight": "^0.1.5",
|
||||||
"gettext-parser": "^1.2.2",
|
"gettext-parser": "^1.2.2",
|
||||||
"gm": "^1.23.0",
|
"gm": "^1.23.0",
|
||||||
|
|
|
@ -5,6 +5,7 @@ const config = require('config');
|
||||||
module.exports = {
|
module.exports = {
|
||||||
app: config,
|
app: config,
|
||||||
baseUrl: 'http://localhost:' + config.www.port,
|
baseUrl: 'http://localhost:' + config.www.port,
|
||||||
|
mailUrl: 'http://localhost:' + config.testserver.mailboxserverport,
|
||||||
users: {
|
users: {
|
||||||
admin: {
|
admin: {
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
|
|
145
test/e2e/helpers/mocha-e2e.js
Normal file
145
test/e2e/helpers/mocha-e2e.js
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Mocha = require('mocha');
|
||||||
|
const color = Mocha.reporters.Base.color;
|
||||||
|
|
||||||
|
function UseCaseReporter(runner) {
|
||||||
|
Mocha.reporters.Base.call(this, runner);
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
let indents = 0;
|
||||||
|
|
||||||
|
function indent () {
|
||||||
|
return Array(indents).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
runner.on('start', function () {
|
||||||
|
console.log();
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('suite', suite => {
|
||||||
|
++indents;
|
||||||
|
console.log(color('suite', '%s%s'), indent(), suite.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('suite end', () => {
|
||||||
|
--indents;
|
||||||
|
if (indents === 1) {
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('use-case', useCase => {
|
||||||
|
++indents;
|
||||||
|
console.log();
|
||||||
|
console.log(color('suite', '%sUse case: %s'), indent(), useCase.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('use-case end', () => {
|
||||||
|
--indents;
|
||||||
|
if (indents === 1) {
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('step pass', step => {
|
||||||
|
console.log(indent() + color('checkmark', ' ' + Mocha.reporters.Base.symbols.ok) + color('pass', ' %s'), step.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('step fail', step => {
|
||||||
|
console.log(indent() + color('fail', ' %s'), step.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('pending', test => {
|
||||||
|
const fmt = indent() + color('pending', ' - %s');
|
||||||
|
console.log(fmt, test.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('pass', test => {
|
||||||
|
let fmt;
|
||||||
|
if (test.speed === 'fast') {
|
||||||
|
fmt = indent() +
|
||||||
|
color('checkmark', ' ' + Mocha.reporters.Base.symbols.ok) +
|
||||||
|
color('pass', ' %s');
|
||||||
|
console.log(fmt, test.title);
|
||||||
|
} else {
|
||||||
|
fmt = indent() +
|
||||||
|
color('checkmark', ' ' + Mocha.reporters.Base.symbols.ok) +
|
||||||
|
color('pass', ' %s') +
|
||||||
|
color(test.speed, ' (%dms)');
|
||||||
|
console.log(fmt, test.title, test.duration);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('fail', test => {
|
||||||
|
console.log(indent() + color('fail', ' %s'), test.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('end', () => {
|
||||||
|
const stats = self.stats;
|
||||||
|
let fmt;
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
// passes
|
||||||
|
fmt = color('bright pass', ' ') + color('green', ' %d passing');
|
||||||
|
console.log(fmt, stats.passes);
|
||||||
|
|
||||||
|
// pending
|
||||||
|
if (stats.pending) {
|
||||||
|
fmt = color('pending', ' ') + color('pending', ' %d pending');
|
||||||
|
console.log(fmt, stats.pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
// failures
|
||||||
|
if (stats.failures) {
|
||||||
|
fmt = color('fail', ' %d failing');
|
||||||
|
console.log(fmt, stats.failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocha = new Mocha()
|
||||||
|
.timeout(120000)
|
||||||
|
.reporter(UseCaseReporter);
|
||||||
|
|
||||||
|
mocha._originalRun = mocha.run;
|
||||||
|
|
||||||
|
let runner;
|
||||||
|
mocha.run = fn => {
|
||||||
|
runner = mocha._originalRun(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function useCase(name, asyncFn) {
|
||||||
|
it('Use case: ' + name, async () => {
|
||||||
|
runner.emit('use-case', {title: name});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await asyncFn();
|
||||||
|
runner.emit('use-case end');
|
||||||
|
} catch (err) {
|
||||||
|
runner.emit('use-case end');
|
||||||
|
console.err(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function step(name, asyncFn) {
|
||||||
|
try {
|
||||||
|
await asyncFn();
|
||||||
|
runner.emit('step pass', {title: name});
|
||||||
|
} catch (err) {
|
||||||
|
runner.emit('step fail', {title: name});
|
||||||
|
console.err(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mocha,
|
||||||
|
useCase,
|
||||||
|
step
|
||||||
|
};
|
|
@ -1,36 +1,34 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
require('./helpers/exit-unless-test');
|
require('./helpers/exit-unless-test');
|
||||||
|
const mocha = require('./helpers/mocha-e2e').mocha;
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
global.USE_SHARED_DRIVER = true;
|
global.USE_SHARED_DRIVER = true;
|
||||||
|
|
||||||
const driver = require('./helpers/driver');
|
const driver = require('./helpers/driver');
|
||||||
|
|
||||||
const only = 'only';
|
const only = 'only';
|
||||||
const skip = 'skip';
|
const skip = 'skip';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let tests = [
|
let tests = [
|
||||||
['tests/login'],
|
'login',
|
||||||
['tests/subscription']
|
'subscription',
|
||||||
|
['subscription-uc', only]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
tests = tests.map(testSpec => (testSpec.constructor === Array ? testSpec : [testSpec]));
|
||||||
|
tests = tests.filter(testSpec => testSpec[1] !== skip);
|
||||||
tests = tests.filter(t => t[1] !== skip);
|
if (tests.some(testSpec => testSpec[1] === only)) {
|
||||||
|
tests = tests.filter(testSpec => testSpec[1] === only);
|
||||||
if (tests.some(t => t[1] === only)) {
|
|
||||||
tests = tests.filter(t => t[1] === only);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('e2e', function() {
|
for (const testSpec of tests) {
|
||||||
this.timeout(10000);
|
const testPath = path.join(__dirname, 'tests', testSpec[0] + '.js');
|
||||||
|
mocha.addFile(testPath);
|
||||||
|
}
|
||||||
|
|
||||||
tests.forEach(t => {
|
mocha.run(failures => {
|
||||||
describe(t[0], () => {
|
driver.originalQuit();
|
||||||
require('./' + t[0]); // eslint-disable-line global-require
|
process.exit(failures); // exit with non-zero status if there were failures
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
after(() => driver.originalQuit());
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const page = require('./page');
|
|
||||||
|
|
||||||
module.exports = driver => Object.assign(page(driver), {
|
|
||||||
elementToWaitFor: 'alert',
|
|
||||||
elements: {
|
|
||||||
alert: 'div.alert:not(.js-warning)'
|
|
||||||
},
|
|
||||||
getText() {
|
|
||||||
return this.element('alert').getText();
|
|
||||||
},
|
|
||||||
clear() {
|
|
||||||
return this.driver.executeScript(`
|
|
||||||
var elements = document.getElementsByClassName('alert');
|
|
||||||
while(elements.length > 0){
|
|
||||||
elements[0].parentNode.removeChild(elements[0]);
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const page = require('./page');
|
const page = require('./web');
|
||||||
|
|
||||||
module.exports = driver => Object.assign(page(driver), {
|
module.exports = driver => Object.assign(page(driver), {
|
||||||
url: '/',
|
url: '/',
|
||||||
|
|
19
test/e2e/page-objects/mail.js
Normal file
19
test/e2e/page-objects/mail.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const config = require('../helpers/config');
|
||||||
|
|
||||||
|
const page = require('./page');
|
||||||
|
|
||||||
|
module.exports = (driver, ...extras) => page(driver, {
|
||||||
|
|
||||||
|
async fetchMail(address) {
|
||||||
|
await this.driver.sleep(1000);
|
||||||
|
await this.driver.navigate().to(`${config.mailUrl}/${address}`);
|
||||||
|
await this.waitUntilVisible();
|
||||||
|
},
|
||||||
|
|
||||||
|
async ensureUrl(path) {
|
||||||
|
throw new Error('Unsupported method.');
|
||||||
|
},
|
||||||
|
|
||||||
|
}, ...extras);
|
|
@ -4,52 +4,78 @@ const config = require('../helpers/config');
|
||||||
const webdriver = require('selenium-webdriver');
|
const webdriver = require('selenium-webdriver');
|
||||||
const By = webdriver.By;
|
const By = webdriver.By;
|
||||||
const until = webdriver.until;
|
const until = webdriver.until;
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
module.exports = driver => ({
|
module.exports = (driver, ...extras) => Object.assign({
|
||||||
driver,
|
driver,
|
||||||
|
|
||||||
elements: {},
|
elements: {},
|
||||||
|
|
||||||
element(key) {
|
async element(key) {
|
||||||
return this.driver.findElement(By.css(this.elements[key] || key));
|
return await this.driver.findElement(By.css(this.elements[key] || key));
|
||||||
},
|
},
|
||||||
|
|
||||||
navigate(path) {
|
async waitUntilVisible(selector) {
|
||||||
this.driver.navigate().to(config.baseUrl + (path || this.url));
|
// This is left here to ease debugging
|
||||||
return this.waitUntilVisible();
|
// await this.sleep(2000);
|
||||||
},
|
// await this.takeScreenshot('image.png');
|
||||||
|
// console.log(await this.source());
|
||||||
|
|
||||||
waitUntilVisible() {
|
const sel = selector || this.elements[this.elementToWaitFor] || 'body';
|
||||||
let selector = this.elements[this.elementToWaitFor];
|
await this.driver.wait(until.elementLocated(By.css(sel)), 10000);
|
||||||
if (!selector && this.url) {
|
|
||||||
selector = 'body.page--' + (this.url.substring(1).replace(/\//g, '--') || 'home');
|
if (this.url) {
|
||||||
|
await this.ensureUrl();
|
||||||
}
|
}
|
||||||
return selector ? this.driver.wait(until.elementLocated(By.css(selector))) : this.driver.sleep(1000);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
submit() {
|
async link(key) {
|
||||||
return this.element('submitButton').click();
|
const elem = await this.element(key);
|
||||||
|
return await elem.getAttribute('href');
|
||||||
},
|
},
|
||||||
|
|
||||||
click(key) {
|
async submit() {
|
||||||
return this.element(key).click();
|
const submitButton = await this.element('submitButton');
|
||||||
|
await submitButton.click();
|
||||||
},
|
},
|
||||||
|
|
||||||
getText(key) {
|
async click(key) {
|
||||||
return this.element(key).getText();
|
const elem = await this.element(key);
|
||||||
|
await elem.click();
|
||||||
},
|
},
|
||||||
|
|
||||||
getValue(key) {
|
async getText(key) {
|
||||||
return this.element(key).getAttribute('value');
|
const elem = await this.element(key);
|
||||||
|
return await elem.getText();
|
||||||
},
|
},
|
||||||
|
|
||||||
setValue(key, value) {
|
async getValue(key) {
|
||||||
return this.element(key).sendKeys(value);
|
const elem = await this.element(key);
|
||||||
|
return await elem.getAttribute('value');
|
||||||
},
|
},
|
||||||
|
|
||||||
containsText(str) {
|
async setValue(key, value) {
|
||||||
// let text = await driver.findElement({ css: 'body' }).getText();
|
const elem = await this.element(key);
|
||||||
return this.driver.executeScript(`
|
await elem.sendKeys(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
async containsText(str) {
|
||||||
|
return await this.driver.executeScript(`
|
||||||
return (document.documentElement.textContent || document.documentElement.innerText).indexOf('${str}') > -1;
|
return (document.documentElement.textContent || document.documentElement.innerText).indexOf('${str}') > -1;
|
||||||
`);
|
`);
|
||||||
|
},
|
||||||
|
|
||||||
|
async source() {
|
||||||
|
return await this.driver.getPageSource();
|
||||||
|
},
|
||||||
|
|
||||||
|
async takeScreenshot(destPath) {
|
||||||
|
const pngData = await this.driver.takeScreenshot();
|
||||||
|
const buf = new Buffer(pngData, 'base64');
|
||||||
|
await fs.writeFile(destPath, buf);
|
||||||
|
},
|
||||||
|
|
||||||
|
async sleep(ms) {
|
||||||
|
await this.driver.sleep(ms);
|
||||||
}
|
}
|
||||||
});
|
}, ...extras);
|
||||||
|
|
|
@ -1,84 +1,94 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const config = require('../helpers/config');
|
const config = require('../helpers/config');
|
||||||
const page = require('./page');
|
const webBase = require('./web');
|
||||||
|
const mailBase = require('./mail');
|
||||||
|
|
||||||
const web = {
|
module.exports = (driver, list) => {
|
||||||
enterEmail(value) {
|
|
||||||
this.element('emailInput').clear();
|
const web = params => webBase(driver, {
|
||||||
return this.element('emailInput').sendKeys(value);
|
async enterEmail(value) {
|
||||||
}
|
const emailInput = await this.element('emailInput');
|
||||||
|
await emailInput.clear();
|
||||||
|
await emailInput.sendKeys(value);
|
||||||
|
},
|
||||||
|
}, params);
|
||||||
|
|
||||||
|
const mail = params => mailBase(driver, params);
|
||||||
|
|
||||||
|
return {
|
||||||
|
webSubscribe: web({
|
||||||
|
url: `/subscription/${list.cid}`,
|
||||||
|
elementToWaitFor: 'form',
|
||||||
|
elements: {
|
||||||
|
form: `form[action="/subscription/${list.cid}/subscribe"]`,
|
||||||
|
emailInput: '#main-form input[name="email"]',
|
||||||
|
submitButton: 'a[href="#submit"]'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
webConfirmSubscriptionNotice: web({
|
||||||
|
url: `/subscription/${list.cid}/confirm-subscription-notice`,
|
||||||
|
elementToWaitFor: 'homepageButton',
|
||||||
|
elements: {
|
||||||
|
homepageButton: `a[href="${config.settings['default-homepage']}"]`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
mailConfirmSubscription: mail({
|
||||||
|
elementToWaitFor: 'confirmLink',
|
||||||
|
elements: {
|
||||||
|
confirmLink: `a[href^="${config.settings['service-url']}subscription/confirm/subscribe/"]`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
webSubscribedNotice: web({
|
||||||
|
url: `/subscription/${list.cid}/subscribed-notice`,
|
||||||
|
elementToWaitFor: 'homepageButton',
|
||||||
|
elements: {
|
||||||
|
homepageButton: `a[href="${config.settings['default-homepage']}"]`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
mailSubscriptionConfirmed: mail({
|
||||||
|
elementToWaitFor: 'unsubscribeLink',
|
||||||
|
elements: {
|
||||||
|
unsubscribeLink: `a[href^="${config.settings['service-url']}subscription/${list.cid}/unsubscribe/"]`,
|
||||||
|
manageLink: `a[href^="${config.settings['service-url']}subscription/${list.cid}/manage/"]`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
/*
|
||||||
|
webUnsubscribe: web({ // FIXME
|
||||||
|
elementToWaitFor: 'submitButton',
|
||||||
|
elements: {
|
||||||
|
submitButton: 'a[href="#submit"]'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
*/
|
||||||
|
|
||||||
|
webUnsubscribedNotice: web({
|
||||||
|
url: `/subscription/${list.cid}/unsubscribed-notice`,
|
||||||
|
elementToWaitFor: 'homepageButton',
|
||||||
|
elements: {
|
||||||
|
homepageButton: `a[href="${config.settings['default-homepage']}"]`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
mailUnsubscriptionConfirmed: mail({
|
||||||
|
elementToWaitFor: 'resubscribeLink',
|
||||||
|
elements: {
|
||||||
|
resubscribeLink: `a[href^="${config.settings['service-url']}subscription/${list.cid}"]`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
/* TODO
|
||||||
|
webManage: web({
|
||||||
|
url: `/subscription/${list.cid}/manage`,
|
||||||
|
elementToWaitFor: 'homepageButton',
|
||||||
|
elements: {
|
||||||
|
homepageButton: `a[href="${config.settings['default-homepage']}"]`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
*/
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mail = {
|
|
||||||
navigate(address) {
|
|
||||||
this.driver.sleep(100);
|
|
||||||
this.driver.navigate().to(`http://localhost:${config.app.testserver.mailboxserverport}/${address}`);
|
|
||||||
return this.waitUntilVisible();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = (driver, list) => ({
|
|
||||||
|
|
||||||
webSubscribe: Object.assign(page(driver), web, {
|
|
||||||
url: `/subscription/${list.cid}`,
|
|
||||||
elementToWaitFor: 'form',
|
|
||||||
elements: {
|
|
||||||
form: `form[action="/subscription/${list.cid}/subscribe"]`,
|
|
||||||
emailInput: '#main-form input[name="email"]',
|
|
||||||
submitButton: 'a[href="#submit"]'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
webConfirmSubscriptionNotice: Object.assign(page(driver), web, {
|
|
||||||
url: `/subscription/${list.cid}/confirm-notice`,
|
|
||||||
elementToWaitFor: 'homepageButton',
|
|
||||||
elements: {
|
|
||||||
homepageButton: `a[href="${config.settings['default-homepage']}"]`
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
mailConfirmSubscription: Object.assign(page(driver), mail, {
|
|
||||||
elementToWaitFor: 'confirmLink',
|
|
||||||
elements: {
|
|
||||||
confirmLink: `a[href^="${config.settings['service-url']}subscription/subscribe/"]`
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
webSubscribedNotice: Object.assign(page(driver), web, {
|
|
||||||
elementToWaitFor: 'homepageButton',
|
|
||||||
elements: {
|
|
||||||
homepageButton: 'a[href^="https://mailtrain.org"]'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
mailSubscriptionConfirmed: Object.assign(page(driver), mail, {
|
|
||||||
elementToWaitFor: 'unsubscribeLink',
|
|
||||||
elements: {
|
|
||||||
unsubscribeLink: 'a[href*="/unsubscribe/"]',
|
|
||||||
manageLink: 'a[href*="/manage/"]'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
webUnsubscribe: Object.assign(page(driver), web, {
|
|
||||||
elementToWaitFor: 'submitButton',
|
|
||||||
elements: {
|
|
||||||
submitButton: 'a[href="#submit"]'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
webUnsubscribedNotice: Object.assign(page(driver), web, {
|
|
||||||
elementToWaitFor: 'homepageButton',
|
|
||||||
elements: {
|
|
||||||
homepageButton: 'a[href^="https://mailtrain.org"]'
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
mailUnsubscriptionConfirmed: Object.assign(page(driver), mail, {
|
|
||||||
elementToWaitFor: 'resubscribeLink',
|
|
||||||
elements: {
|
|
||||||
resubscribeLink: `a[href^="${config.settings['service-url']}subscription/${list.cid}"]`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const page = require('./page');
|
const page = require('./web');
|
||||||
|
|
||||||
module.exports = driver => ({
|
module.exports = driver => ({
|
||||||
|
|
||||||
|
|
44
test/e2e/page-objects/web.js
Normal file
44
test/e2e/page-objects/web.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const config = require('../helpers/config');
|
||||||
|
const By = require('selenium-webdriver').By;
|
||||||
|
const url = require('url');
|
||||||
|
|
||||||
|
const page = require('./page');
|
||||||
|
|
||||||
|
module.exports = (driver, ...extras) => page(driver, {
|
||||||
|
|
||||||
|
async navigate(path) {
|
||||||
|
await this.driver.navigate().to(config.baseUrl + (path || this.url));
|
||||||
|
await this.waitUntilVisible();
|
||||||
|
},
|
||||||
|
|
||||||
|
async ensureUrl(path) {
|
||||||
|
const desiredUrl = path || this.url;
|
||||||
|
|
||||||
|
if (desiredUrl) {
|
||||||
|
const currentUrl = url.parse(await this.driver.getCurrentUrl());
|
||||||
|
if (this.url !== currentUrl.pathname || config.baseUrl !== `${currentUrl.protocol}//${currentUrl.host}`) {
|
||||||
|
throw new Error(`Unexpected URL. Expecting ${config.baseUrl}${this.url} got ${currentUrl.protocol}//${currentUrl.host}/${currentUrl.pathname}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async waitForFlash() {
|
||||||
|
await this.waitUntilVisible('div.alert:not(.js-warning)');
|
||||||
|
},
|
||||||
|
|
||||||
|
async getFlash() {
|
||||||
|
const elem = await this.driver.findElement(By.css('div.alert:not(.js-warning)'));
|
||||||
|
return await elem.getText();
|
||||||
|
},
|
||||||
|
|
||||||
|
async clearFlash() {
|
||||||
|
await this.driver.executeScript(`
|
||||||
|
var elements = document.getElementsByClassName('alert');
|
||||||
|
while(elements.length > 0){
|
||||||
|
elements[0].parentNode.removeChild(elements[0]);
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}, ...extras);
|
|
@ -11,8 +11,6 @@ const {
|
||||||
} = require('../page-objects/users')(driver);
|
} = require('../page-objects/users')(driver);
|
||||||
|
|
||||||
describe('login', function() {
|
describe('login', function() {
|
||||||
this.timeout(10000);
|
|
||||||
|
|
||||||
before(() => driver.manage().deleteAllCookies());
|
before(() => driver.manage().deleteAllCookies());
|
||||||
|
|
||||||
it('can access home page', async () => {
|
it('can access home page', async () => {
|
||||||
|
|
106
test/e2e/tests/subscription-uc.js
Normal file
106
test/e2e/tests/subscription-uc.js
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const config = require('../helpers/config');
|
||||||
|
const { useCase, step } = require('../helpers/mocha-e2e');
|
||||||
|
const shortid = require('shortid');
|
||||||
|
const expect = require('chai').expect;
|
||||||
|
const driver = require('../helpers/driver');
|
||||||
|
|
||||||
|
const page = require('../page-objects/subscription')(driver, config.lists.one);
|
||||||
|
|
||||||
|
function generateEmail() {
|
||||||
|
return 'keep.' + shortid.generate() + '@mailtrain.org';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function subscribe(testUserEmail) {
|
||||||
|
const subscription = {
|
||||||
|
email: testUserEmail
|
||||||
|
};
|
||||||
|
|
||||||
|
await step('User navigates to list subscription page', async () => {
|
||||||
|
await page.webSubscribe.navigate();
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('User submits a valid email', async () => {
|
||||||
|
await page.webSubscribe.enterEmail(testUserEmail);
|
||||||
|
await page.webSubscribe.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('System shows a notice that further instructions are in the email', async () => {
|
||||||
|
await page.webConfirmSubscriptionNotice.waitUntilVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('System sends an email with a link to confirm the subscription', async () => {
|
||||||
|
await page.mailConfirmSubscription.fetchMail(testUserEmail);
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('User clicks confirm subscription in the email', async () => {
|
||||||
|
await page.mailConfirmSubscription.click('confirmLink');
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('System shows a notice that subscription has been confirmed', async () => {
|
||||||
|
await page.webSubscribedNotice.waitUntilVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('System sends an email with subscription confirmation', async () => {
|
||||||
|
await page.mailSubscriptionConfirmed.fetchMail(testUserEmail);
|
||||||
|
subscription.unsubscribeLink = await page.mailSubscriptionConfirmed.link('unsubscribeLink');
|
||||||
|
subscription.manageLink = await page.mailSubscriptionConfirmed.link('manageLink');
|
||||||
|
});
|
||||||
|
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Subscription use-cases', function() {
|
||||||
|
before(() => driver.manage().deleteAllCookies());
|
||||||
|
|
||||||
|
useCase('Subscription to a public list (main scenario)', async () => {
|
||||||
|
await subscribe(generateEmail());
|
||||||
|
});
|
||||||
|
|
||||||
|
useCase('Subscription to a public list (invalid email)', async () => {
|
||||||
|
await step('User navigates to list subscribe page', async () => {
|
||||||
|
await page.webSubscribe.navigate();
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('User submits an invalid email', async () => {
|
||||||
|
await page.webSubscribe.enterEmail('foo@bar.nope');
|
||||||
|
await page.webSubscribe.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('System shows a flash notice that email is invalid', async () => {
|
||||||
|
await page.webSubscribe.waitForFlash();
|
||||||
|
expect(await page.webSubscribe.getFlash()).to.contain('Invalid email address');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
useCase('Unsubscription from list #1 (one-step, no form)', async () => {
|
||||||
|
const subscription = await subscribe(generateEmail());
|
||||||
|
|
||||||
|
await step('User clicks the unsubscribe button', async () => {
|
||||||
|
await page.mailSubscriptionConfirmed.click('unsubscribeLink');
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('System show a notice that confirms unsubscription', async () => {
|
||||||
|
await page.webUnsubscribedNotice.waitUntilVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('System sends an email that confirms unsubscription', async () => {
|
||||||
|
await page.mailUnsubscriptionConfirmed.fetchMail(subscription.email);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
useCase('Change email in list #1 (one-step, no form)', async () => {
|
||||||
|
const subscription = await subscribe(generateEmail());
|
||||||
|
|
||||||
|
await step('User clicks the manage subscription button', async () => {
|
||||||
|
await page.mailSubscriptionConfirmed.click('manageLink');
|
||||||
|
});
|
||||||
|
|
||||||
|
await step('System show a notice that confirms unsubscription', async () => {
|
||||||
|
await page.webManage.waitUntilVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
after(() => driver.quit());
|
||||||
|
});
|
|
@ -5,7 +5,7 @@ const shortid = require('shortid');
|
||||||
const expect = require('chai').expect;
|
const expect = require('chai').expect;
|
||||||
const driver = require('../helpers/driver');
|
const driver = require('../helpers/driver');
|
||||||
|
|
||||||
const page = require('../page-objects/page')(driver);
|
const page = require('../page-objects/web')(driver);
|
||||||
const flash = require('../page-objects/flash')(driver);
|
const flash = require('../page-objects/flash')(driver);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue