From 7c91ecab7eaa7993e4d64faf1a35d5ca183987d4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 16:45:34 +0200 Subject: [PATCH 01/13] create session object to scope a user, move helper methods there --- helpers.js | 149 ------------------ src/session.js | 147 +++++++++++++++++ {tests => src/tests}/consent.js | 9 +- {tests => src/tests}/create-room.js | 13 +- {tests => src/tests}/join.js | 15 +- .../tests}/server-notices-consent.js | 15 +- {tests => src/tests}/signup.js | 35 ++-- start.js | 36 ++--- 8 files changed, 203 insertions(+), 216 deletions(-) delete mode 100644 helpers.js create mode 100644 src/session.js rename {tests => src/tests}/consent.js (70%) rename {tests => src/tests}/create-room.js (57%) rename {tests => src/tests}/join.js (53%) rename {tests => src/tests}/server-notices-consent.js (68%) rename {tests => src/tests}/signup.js (60%) diff --git a/helpers.js b/helpers.js deleted file mode 100644 index e830824e7c..0000000000 --- a/helpers.js +++ /dev/null @@ -1,149 +0,0 @@ -/* -Copyright 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// puppeteer helpers - -// TODO: rename to queryAndInnertext? -async function tryGetInnertext(page, selector) { - const field = await page.$(selector); - if (field != null) { - const text_handle = await field.getProperty('innerText'); - return await text_handle.jsonValue(); - } - return null; -} - -async function innerText(page, field) { - const text_handle = await field.getProperty('innerText'); - return await text_handle.jsonValue(); -} - -async function newPage() { - const page = await browser.newPage(); - await page.setViewport({ - width: 1280, - height: 800 - }); - return page; -} - -function logConsole(page) { - let buffer = ""; - page.on('console', msg => { - buffer += msg.text() + '\n'; - }); - return { - logs() { - return buffer; - } - } -} - -function logXHRRequests(page) { - let buffer = ""; - page.on('requestfinished', async (req) => { - const type = req.resourceType(); - const response = await req.response(); - //if (type === 'xhr' || type === 'fetch') { - buffer += `${type} ${response.status()} ${req.method()} ${req.url()} \n`; - // if (req.method() === "POST") { - // buffer += " Post data: " + req.postData(); - // } - //} - }); - return { - logs() { - return buffer; - } - } -} - -async function getOuterHTML(element_handle) { - const html_handle = await element_handle.getProperty('outerHTML'); - return await html_handle.jsonValue(); -} - -async function printElements(label, elements) { - console.log(label, await Promise.all(elements.map(getOuterHTML))); -} - -async function replaceInputText(input, text) { - // click 3 times to select all text - await input.click({clickCount: 3}); - // then remove it with backspace - await input.press('Backspace'); - // and type the new text - await input.type(text); -} - -// TODO: rename to waitAndQuery(Single)? -async function waitAndQuerySelector(page, selector, timeout = 500) { - await page.waitForSelector(selector, {visible: true, timeout}); - return await page.$(selector); -} - -async function waitAndQueryAll(page, selector, timeout = 500) { - await page.waitForSelector(selector, {visible: true, timeout}); - return await page.$$(selector); -} - -function waitForNewPage(timeout = 500) { - return new Promise((resolve, reject) => { - const timeoutHandle = setTimeout(() => { - browser.removeEventListener('targetcreated', callback); - reject(new Error(`timeout of ${timeout}ms for waitForNewPage elapsed`)); - }, timeout); - - const callback = async (target) => { - clearTimeout(timeoutHandle); - const page = await target.page(); - resolve(page); - }; - - browser.once('targetcreated', callback); - }); -} - -// other helpers - -function randomInt(max) { - return Math.ceil(Math.random()*max); -} - -function riotUrl(path) { - return riotserver + path; -} - -function delay(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -module.exports = { - tryGetInnertext, - innerText, - newPage, - logConsole, - logXHRRequests, - getOuterHTML, - printElements, - replaceInputText, - waitAndQuerySelector, - waitAndQueryAll, - waitForNewPage, - randomInt, - riotUrl, - delay, -} \ No newline at end of file diff --git a/src/session.js b/src/session.js new file mode 100644 index 0000000000..51bad4a3c9 --- /dev/null +++ b/src/session.js @@ -0,0 +1,147 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +const puppeteer = require('puppeteer'); + +module.exports = class RiotSession { + constructor(browser, page, username, riotserver) { + this.browser = browser; + this.page = page; + this.riotserver = riotserver; + this.username = username; + } + + static async create(username, puppeteerOptions, riotserver) { + const browser = await puppeteer.launch(puppeteerOptions); + const page = await browser.newPage(); + await page.setViewport({ + width: 1280, + height: 800 + }); + return new RiotSession(browser, page, username, riotserver); + } + + async tryGetInnertext(selector) { + const field = await this.page.$(selector); + if (field != null) { + const text_handle = await field.getProperty('innerText'); + return await text_handle.jsonValue(); + } + return null; + } + + async innerText(field) { + const text_handle = await field.getProperty('innerText'); + return await text_handle.jsonValue(); + } + + logConsole() { + let buffer = ""; + this.page.on('console', msg => { + buffer += msg.text() + '\n'; + }); + return { + logs() { + return buffer; + } + } + } + + logXHRRequests() { + let buffer = ""; + this.page.on('requestfinished', async (req) => { + const type = req.resourceType(); + const response = await req.response(); + //if (type === 'xhr' || type === 'fetch') { + buffer += `${type} ${response.status()} ${req.method()} ${req.url()} \n`; + // if (req.method() === "POST") { + // buffer += " Post data: " + req.postData(); + // } + //} + }); + return { + logs() { + return buffer; + } + } + } + + async getOuterHTML(element_handle) { + const html_handle = await element_handle.getProperty('outerHTML'); + return await html_handle.jsonValue(); + } + + async printElements(label, elements) { + console.log(label, await Promise.all(elements.map(getOuterHTML))); + } + + async replaceInputText(input, text) { + // click 3 times to select all text + await input.click({clickCount: 3}); + // then remove it with backspace + await input.press('Backspace'); + // and type the new text + await input.type(text); + } + + // TODO: rename to waitAndQuery(Single)? + async waitAndQuerySelector(selector, timeout = 500) { + await this.page.waitForSelector(selector, {visible: true, timeout}); + return await this.page.$(selector); + } + + async waitAndQueryAll(selector, timeout = 500) { + await this.page.waitForSelector(selector, {visible: true, timeout}); + return await this.page.$$(selector); + } + + waitForNewPage(timeout = 500) { + return new Promise((resolve, reject) => { + const timeoutHandle = setTimeout(() => { + this.browser.removeEventListener('targetcreated', callback); + reject(new Error(`timeout of ${timeout}ms for waitForNewPage elapsed`)); + }, timeout); + + const callback = async (target) => { + clearTimeout(timeoutHandle); + const page = await target.page(); + resolve(page); + }; + + this.browser.once('targetcreated', callback); + }); + } + + waitForSelector(selector) { + return this.page.waitForSelector(selector); + } + + goto(url) { + return this.page.goto(url); + } + + riotUrl(path) { + return this.riotserver + path; + } + + delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + close() { + return this.browser.close(); + } +} diff --git a/tests/consent.js b/src/tests/consent.js similarity index 70% rename from tests/consent.js rename to src/tests/consent.js index 3c8ada9a5e..09026a3082 100644 --- a/tests/consent.js +++ b/src/tests/consent.js @@ -14,15 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -const helpers = require('../helpers'); const assert = require('assert'); -module.exports = async function acceptTerms(page) { - const reviewTermsButton = await helpers.waitAndQuerySelector(page, '.mx_QuestionDialog button.mx_Dialog_primary', 5000); - const termsPagePromise = helpers.waitForNewPage(); +module.exports = async function acceptTerms(session) { + const reviewTermsButton = await session.waitAndQuerySelector('.mx_QuestionDialog button.mx_Dialog_primary', 5000); + const termsPagePromise = session.waitForNewPage(); await reviewTermsButton.click(); const termsPage = await termsPagePromise; const acceptButton = await termsPage.$('input[type=submit]'); await acceptButton.click(); - await helpers.delay(500); //TODO yuck, timers + await session.delay(500); //TODO yuck, timers } \ No newline at end of file diff --git a/tests/create-room.js b/src/tests/create-room.js similarity index 57% rename from tests/create-room.js rename to src/tests/create-room.js index 4c9004bcaf..948e0b115f 100644 --- a/tests/create-room.js +++ b/src/tests/create-room.js @@ -14,19 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -const helpers = require('../helpers'); const assert = require('assert'); -module.exports = async function createRoom(page, roomName) { +module.exports = async function createRoom(session, roomName) { //TODO: brittle selector - const createRoomButton = await helpers.waitAndQuerySelector(page, '.mx_RoleButton[aria-label="Create new room"]'); + const createRoomButton = await session.waitAndQuerySelector('.mx_RoleButton[aria-label="Create new room"]'); await createRoomButton.click(); - const roomNameInput = await helpers.waitAndQuerySelector(page, '.mx_CreateRoomDialog_input'); - await helpers.replaceInputText(roomNameInput, roomName); + const roomNameInput = await session.waitAndQuerySelector('.mx_CreateRoomDialog_input'); + await session.replaceInputText(roomNameInput, roomName); - const createButton = await helpers.waitAndQuerySelector(page, '.mx_Dialog_primary'); + const createButton = await session.waitAndQuerySelector('.mx_Dialog_primary'); await createButton.click(); - await page.waitForSelector('.mx_MessageComposer'); + await session.waitForSelector('.mx_MessageComposer'); } \ No newline at end of file diff --git a/tests/join.js b/src/tests/join.js similarity index 53% rename from tests/join.js rename to src/tests/join.js index ea16a93936..a359d6ef64 100644 --- a/tests/join.js +++ b/src/tests/join.js @@ -14,22 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ -const helpers = require('../helpers'); const assert = require('assert'); -module.exports = async function join(page, roomName) { +module.exports = async function join(session, roomName) { //TODO: brittle selector - const directoryButton = await helpers.waitAndQuerySelector(page, '.mx_RoleButton[aria-label="Room directory"]'); + const directoryButton = await session.waitAndQuerySelector('.mx_RoleButton[aria-label="Room directory"]'); await directoryButton.click(); - const roomInput = await helpers.waitAndQuerySelector(page, '.mx_DirectorySearchBox_input'); - await helpers.replaceInputText(roomInput, roomName); + const roomInput = await session.waitAndQuerySelector('.mx_DirectorySearchBox_input'); + await session.replaceInputText(roomInput, roomName); - const firstRoomLabel = await helpers.waitAndQuerySelector(page, '.mx_RoomDirectory_table .mx_RoomDirectory_name:first-child'); + const firstRoomLabel = await session.waitAndQuerySelector('.mx_RoomDirectory_table .mx_RoomDirectory_name:first-child'); await firstRoomLabel.click(); - const joinLink = await helpers.waitAndQuerySelector(page, '.mx_RoomPreviewBar_join_text a'); + const joinLink = await session.waitAndQuerySelector('.mx_RoomPreviewBar_join_text a'); await joinLink.click(); - await page.waitForSelector('.mx_MessageComposer'); + await session.waitForSelector('.mx_MessageComposer'); } \ No newline at end of file diff --git a/tests/server-notices-consent.js b/src/tests/server-notices-consent.js similarity index 68% rename from tests/server-notices-consent.js rename to src/tests/server-notices-consent.js index 2689036a96..0eb4cd8722 100644 --- a/tests/server-notices-consent.js +++ b/src/tests/server-notices-consent.js @@ -14,14 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -const helpers = require('../helpers'); const assert = require('assert'); -module.exports = async function acceptServerNoticesInviteAndConsent(page, name) { +module.exports = async function acceptServerNoticesInviteAndConsent(session, name) { //TODO: brittle selector - const invitesHandles = await helpers.waitAndQueryAll(page, '.mx_RoomTile_name.mx_RoomTile_invite'); + const invitesHandles = await session.waitAndQueryAll('.mx_RoomTile_name.mx_RoomTile_invite'); const invitesWithText = await Promise.all(invitesHandles.map(async (inviteHandle) => { - const text = await helpers.innerText(page, inviteHandle); + const text = await session.innerText(inviteHandle); return {inviteHandle, text}; })); const inviteHandle = invitesWithText.find(({inviteHandle, text}) => { @@ -30,15 +29,15 @@ module.exports = async function acceptServerNoticesInviteAndConsent(page, name) await inviteHandle.click(); - const acceptInvitationLink = await helpers.waitAndQuerySelector(page, ".mx_RoomPreviewBar_join_text a:first-child"); + const acceptInvitationLink = await session.waitAndQuerySelector(".mx_RoomPreviewBar_join_text a:first-child"); await acceptInvitationLink.click(); - const consentLink = await helpers.waitAndQuerySelector(page, ".mx_EventTile_body a", 1000); + const consentLink = await session.waitAndQuerySelector(".mx_EventTile_body a", 1000); - const termsPagePromise = helpers.waitForNewPage(); + const termsPagePromise = session.waitForNewPage(); await consentLink.click(); const termsPage = await termsPagePromise; const acceptButton = await termsPage.$('input[type=submit]'); await acceptButton.click(); - await helpers.delay(500); //TODO yuck, timers + await session.delay(500); //TODO yuck, timers } \ No newline at end of file diff --git a/tests/signup.js b/src/tests/signup.js similarity index 60% rename from tests/signup.js rename to src/tests/signup.js index 70c478aed1..db6ad6208a 100644 --- a/tests/signup.js +++ b/src/tests/signup.js @@ -14,55 +14,54 @@ See the License for the specific language governing permissions and limitations under the License. */ -const helpers = require('../helpers'); const acceptTerms = require('./consent'); const assert = require('assert'); -module.exports = async function signup(page, username, password, homeserver) { - await page.goto(helpers.riotUrl('/#/register')); +module.exports = async function signup(session, username, password, homeserver) { + await session.goto(session.riotUrl('/#/register')); //click 'Custom server' radio button if (homeserver) { - const advancedRadioButton = await helpers.waitAndQuerySelector(page, '#advanced'); + const advancedRadioButton = await session.waitAndQuerySelector('#advanced'); await advancedRadioButton.click(); } // wait until register button is visible - await page.waitForSelector('.mx_Login_submit[value=Register]', {visible: true, timeout: 500}); + await session.waitForSelector('.mx_Login_submit[value=Register]', {visible: true, timeout: 500}); //fill out form - const loginFields = await page.$$('.mx_Login_field'); + const loginFields = await session.page.$$('.mx_Login_field'); assert.strictEqual(loginFields.length, 7); const usernameField = loginFields[2]; const passwordField = loginFields[3]; const passwordRepeatField = loginFields[4]; const hsurlField = loginFields[5]; - await helpers.replaceInputText(usernameField, username); - await helpers.replaceInputText(passwordField, password); - await helpers.replaceInputText(passwordRepeatField, password); + await session.replaceInputText(usernameField, username); + await session.replaceInputText(passwordField, password); + await session.replaceInputText(passwordRepeatField, password); if (homeserver) { - await page.waitForSelector('.mx_ServerConfig', {visible: true, timeout: 500}); - await helpers.replaceInputText(hsurlField, homeserver); + await session.waitForSelector('.mx_ServerConfig', {visible: true, timeout: 500}); + await session.replaceInputText(hsurlField, homeserver); } //wait over a second because Registration/ServerConfig have a 1000ms //delay to internally set the homeserver url //see Registration::render and ServerConfig::props::delayTimeMs - await helpers.delay(1200); + await session.delay(1200); /// focus on the button to make sure error validation /// has happened before checking the form is good to go - const registerButton = await page.$('.mx_Login_submit'); + const registerButton = await session.page.$('.mx_Login_submit'); await registerButton.focus(); //check no errors - const error_text = await helpers.tryGetInnertext(page, '.mx_Login_error'); + const error_text = await session.tryGetInnertext('.mx_Login_error'); assert.strictEqual(!!error_text, false); //submit form //await page.screenshot({path: "beforesubmit.png", fullPage: true}); await registerButton.click(); //confirm dialog saying you cant log back in without e-mail - const continueButton = await helpers.waitAndQuerySelector(page, '.mx_QuestionDialog button.mx_Dialog_primary'); + const continueButton = await session.waitAndQuerySelector('.mx_QuestionDialog button.mx_Dialog_primary'); await continueButton.click(); //wait for registration to finish so the hash gets set //onhashchange better? - await helpers.delay(2000); + await session.delay(2000); - const url = page.url(); - assert.strictEqual(url, helpers.riotUrl('/#/home')); + const url = session.page.url(); + assert.strictEqual(url, session.riotUrl('/#/home')); } diff --git a/start.js b/start.js index 4912f901c1..c1aecb65aa 100644 --- a/start.js +++ b/start.js @@ -14,19 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -const puppeteer = require('puppeteer'); -const helpers = require('./helpers'); const assert = require('assert'); +const RiotSession = require('./src/session'); -const signup = require('./tests/signup'); -const join = require('./tests/join'); -const createRoom = require('./tests/create-room'); -const acceptServerNoticesInviteAndConsent = require('./tests/server-notices-consent'); +const signup = require('./src/tests/signup'); +const join = require('./src/tests/join'); +const createRoom = require('./src/tests/create-room'); +const acceptServerNoticesInviteAndConsent = require('./src/tests/server-notices-consent'); const homeserver = 'http://localhost:8008'; - -global.riotserver = 'http://localhost:5000'; -global.browser = null; +const riotserver = 'http://localhost:5000'; let consoleLogs = null; let xhrLogs = null; @@ -40,30 +37,27 @@ async function runTests() { console.log(`(using external chrome/chromium at ${path}, make sure it's compatible with puppeteer)`); options.executablePath = path; } - global.browser = await puppeteer.launch(options); - const page = await helpers.newPage(); - globalPage = page; - consoleLogs = helpers.logConsole(page); - xhrLogs = helpers.logXHRRequests(page); + const alice = await RiotSession.create("alice", options, riotserver); + + consoleLogs = alice.logConsole(); + xhrLogs = alice.logXHRRequests(); - const username = 'user-' + helpers.randomInt(10000); - const password = 'testtest'; - process.stdout.write(`* signing up as ${username} ... `); - await signup(page, username, password); + process.stdout.write(`* signing up as ${alice.username} ... `); + await signup(alice, alice.username, 'testtest'); process.stdout.write('done\n'); const noticesName = "Server Notices"; process.stdout.write(`* accepting "${noticesName}" and accepting terms & conditions ... `); - await acceptServerNoticesInviteAndConsent(page, noticesName); + await acceptServerNoticesInviteAndConsent(alice, noticesName); process.stdout.write('done\n'); const room = 'test'; process.stdout.write(`* creating room ${room} ... `); - await createRoom(page, room); + await createRoom(alice, room); process.stdout.write('done\n'); - await browser.close(); + await alice.close(); } function onSuccess() { From 6b843eacfcf76be987273e63100e11b446a7102b Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 17:09:43 +0200 Subject: [PATCH 02/13] move log buffers into session, start logging implicitely --- src/session.js | 37 +++++++++++++++++++++++++++---------- start.js | 32 ++++++++++++++------------------ 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/session.js b/src/session.js index 51bad4a3c9..f8f41b20b8 100644 --- a/src/session.js +++ b/src/session.js @@ -16,12 +16,33 @@ limitations under the License. const puppeteer = require('puppeteer'); +class LogBuffer { + constructor(page, eventName, eventMapper, reduceAsync=false, initialValue = "") { + this.buffer = initialValue; + page.on(eventName, (arg) => { + const result = eventMapper(arg); + if (reduceAsync) { + result.then((r) => this.buffer += r); + } + else { + this.buffer += result; + } + }); + } +} + module.exports = class RiotSession { constructor(browser, page, username, riotserver) { this.browser = browser; this.page = page; this.riotserver = riotserver; this.username = username; + this.consoleLog = new LogBuffer(page, "console", (msg) => `${msg.text()}\n`); + this.networkLog = new LogBuffer(page, "requestfinished", async (req) => { + const type = req.resourceType(); + const response = await req.response(); + return `${type} ${response.status()} ${req.method()} ${req.url()} \n`; + }, true); } static async create(username, puppeteerOptions, riotserver) { @@ -48,16 +69,12 @@ module.exports = class RiotSession { return await text_handle.jsonValue(); } - logConsole() { - let buffer = ""; - this.page.on('console', msg => { - buffer += msg.text() + '\n'; - }); - return { - logs() { - return buffer; - } - } + consoleLogs() { + return this.consoleLog.buffer; + } + + networkLogs() { + return this.networkLog.buffer; } logXHRRequests() { diff --git a/start.js b/start.js index c1aecb65aa..0aa2cb9364 100644 --- a/start.js +++ b/start.js @@ -25,9 +25,7 @@ const acceptServerNoticesInviteAndConsent = require('./src/tests/server-notices- const homeserver = 'http://localhost:8008'; const riotserver = 'http://localhost:5000'; -let consoleLogs = null; -let xhrLogs = null; -let globalPage = null; +let sessions = []; async function runTests() { console.log("running tests ..."); @@ -39,9 +37,7 @@ async function runTests() { } const alice = await RiotSession.create("alice", options, riotserver); - - consoleLogs = alice.logConsole(); - xhrLogs = alice.logXHRRequests(); + sessions.push(alice); process.stdout.write(`* signing up as ${alice.username} ... `); await signup(alice, alice.username, 'testtest'); @@ -65,19 +61,19 @@ function onSuccess() { } async function onFailure(err) { - - let documentHtml = "no page"; - if (globalPage) { - documentHtml = await globalPage.content(); - } - console.log('failure: ', err); - console.log('console.log output:'); - console.log(consoleLogs.logs()); - console.log('XHR requests:'); - console.log(xhrLogs.logs()); - console.log('document html:'); - console.log(documentHtml); + for(var i = 0; i < sessions.length; ++i) { + const session = sessions[i]; + documentHtml = await session.page.content(); + console.log(`---------------- START OF ${session.username} LOGS ----------------`); + console.log('---------------- console.log output:'); + console.log(session.consoleLogs()); + console.log('---------------- network requests:'); + console.log(session.networkLogs()); + console.log('---------------- document html:'); + console.log(documentHtml); + console.log(`---------------- END OF ${session.username} LOGS ----------------`); + } process.exit(-1); } From 4c0ab117bfab1ce4c4caebe83c5f67c1e7488feb Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 17:16:27 +0200 Subject: [PATCH 03/13] move outputting steps to session to scope it to username --- src/session.js | 15 +++++++++++++++ start.js | 14 +++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/session.js b/src/session.js index f8f41b20b8..5b0f78ccd3 100644 --- a/src/session.js +++ b/src/session.js @@ -31,6 +31,20 @@ class LogBuffer { } } +class Logger { + constructor(username) { + this.username = username; + } + + step(description) { + process.stdout.write(` * ${this.username} ${description} ... `); + } + + done() { + process.stdout.write("done\n"); + } +} + module.exports = class RiotSession { constructor(browser, page, username, riotserver) { this.browser = browser; @@ -43,6 +57,7 @@ module.exports = class RiotSession { const response = await req.response(); return `${type} ${response.status()} ${req.method()} ${req.url()} \n`; }, true); + this.log = new Logger(this.username); } static async create(username, puppeteerOptions, riotserver) { diff --git a/start.js b/start.js index 0aa2cb9364..9b0ed716e0 100644 --- a/start.js +++ b/start.js @@ -39,19 +39,19 @@ async function runTests() { const alice = await RiotSession.create("alice", options, riotserver); sessions.push(alice); - process.stdout.write(`* signing up as ${alice.username} ... `); + alice.log.step("signs up"); await signup(alice, alice.username, 'testtest'); - process.stdout.write('done\n'); - + alice.log.done(); + const noticesName = "Server Notices"; - process.stdout.write(`* accepting "${noticesName}" and accepting terms & conditions ... `); + alice.log.step(`accepts "${noticesName}" invite and accepting terms & conditions`); await acceptServerNoticesInviteAndConsent(alice, noticesName); - process.stdout.write('done\n'); + alice.log.done(); const room = 'test'; - process.stdout.write(`* creating room ${room} ... `); + alice.log.step(`creates room ${room}`); await createRoom(alice, room); - process.stdout.write('done\n'); + alice.log.done(); await alice.close(); } From 5fe386119087db97500eed5c379b80cb39b2a086 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 17:23:01 +0200 Subject: [PATCH 04/13] create second user and join room first user creates --- start.js | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/start.js b/start.js index 9b0ed716e0..28eba781e5 100644 --- a/start.js +++ b/start.js @@ -27,6 +27,21 @@ const riotserver = 'http://localhost:5000'; let sessions = []; +async function createUser(username, options, riotserver) { + const session = await RiotSession.create(username, options, riotserver); + sessions.push(session); + + session.log.step("signs up"); + await signup(session, session.username, 'testtest'); + session.log.done(); + + const noticesName = "Server Notices"; + session.log.step(`accepts "${noticesName}" invite and accepting terms & conditions`); + await acceptServerNoticesInviteAndConsent(session, noticesName); + session.log.done(); + return session; +} + async function runTests() { console.log("running tests ..."); const options = {}; @@ -36,24 +51,21 @@ async function runTests() { options.executablePath = path; } - const alice = await RiotSession.create("alice", options, riotserver); - sessions.push(alice); - - alice.log.step("signs up"); - await signup(alice, alice.username, 'testtest'); - alice.log.done(); - - const noticesName = "Server Notices"; - alice.log.step(`accepts "${noticesName}" invite and accepting terms & conditions`); - await acceptServerNoticesInviteAndConsent(alice, noticesName); - alice.log.done(); + const alice = await createUser("alice", options, riotserver); + const bob = await createUser("bob", options, riotserver); const room = 'test'; alice.log.step(`creates room ${room}`); await createRoom(alice, room); alice.log.done(); + bob.log.step(`joins room ${room}`); + await createRoom(bob, room); + bob.log.done(); + + await alice.close(); + await bob.close(); } function onSuccess() { From 4e7df2126bcd42c6fd9a186d41fb68cc2ec767ff Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 17:58:58 +0200 Subject: [PATCH 05/13] move step logging to tests, DRY; put test scenario in separate file, less globals --- src/scenario.js | 37 ++++++++++ src/tests/create-room.js | 2 + src/tests/join.js | 2 + src/tests/server-notices-consent.js | 8 ++- src/tests/signup.js | 2 + start.js | 102 ++++++++++++---------------- 6 files changed, 90 insertions(+), 63 deletions(-) create mode 100644 src/scenario.js diff --git a/src/scenario.js b/src/scenario.js new file mode 100644 index 0000000000..ee049a14f9 --- /dev/null +++ b/src/scenario.js @@ -0,0 +1,37 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +const signup = require('./tests/signup'); +const join = require('./tests/join'); +const createRoom = require('./tests/create-room'); +const acceptServerNoticesInviteAndConsent = require('./tests/server-notices-consent'); + +module.exports = async function scenario(createSession) { + async function createUser(username) { + const session = await createSession(username); + await signup(session, session.username, 'testtest'); + const noticesName = "Server Notices"; + await acceptServerNoticesInviteAndConsent(session, noticesName); + return session; + } + + const alice = await createUser("alice"); + const bob = await createUser("bob"); + const room = 'test'; + await createRoom(alice, room); + // await join(bob, room); +} diff --git a/src/tests/create-room.js b/src/tests/create-room.js index 948e0b115f..eff92baf83 100644 --- a/src/tests/create-room.js +++ b/src/tests/create-room.js @@ -17,6 +17,7 @@ limitations under the License. const assert = require('assert'); module.exports = async function createRoom(session, roomName) { + session.log.step(`creates room ${roomName}`); //TODO: brittle selector const createRoomButton = await session.waitAndQuerySelector('.mx_RoleButton[aria-label="Create new room"]'); await createRoomButton.click(); @@ -28,4 +29,5 @@ module.exports = async function createRoom(session, roomName) { await createButton.click(); await session.waitForSelector('.mx_MessageComposer'); + session.log.done(); } \ No newline at end of file diff --git a/src/tests/join.js b/src/tests/join.js index a359d6ef64..72d4fe10cf 100644 --- a/src/tests/join.js +++ b/src/tests/join.js @@ -17,6 +17,7 @@ limitations under the License. const assert = require('assert'); module.exports = async function join(session, roomName) { + session.log.step(`joins room ${roomName}`); //TODO: brittle selector const directoryButton = await session.waitAndQuerySelector('.mx_RoleButton[aria-label="Room directory"]'); await directoryButton.click(); @@ -31,4 +32,5 @@ module.exports = async function join(session, roomName) { await joinLink.click(); await session.waitForSelector('.mx_MessageComposer'); + session.log.done(); } \ No newline at end of file diff --git a/src/tests/server-notices-consent.js b/src/tests/server-notices-consent.js index 0eb4cd8722..53a318a169 100644 --- a/src/tests/server-notices-consent.js +++ b/src/tests/server-notices-consent.js @@ -16,7 +16,8 @@ limitations under the License. const assert = require('assert'); -module.exports = async function acceptServerNoticesInviteAndConsent(session, name) { +module.exports = async function acceptServerNoticesInviteAndConsent(session, noticesName) { + session.log.step(`accepts "${noticesName}" invite and accepting terms & conditions`); //TODO: brittle selector const invitesHandles = await session.waitAndQueryAll('.mx_RoomTile_name.mx_RoomTile_invite'); const invitesWithText = await Promise.all(invitesHandles.map(async (inviteHandle) => { @@ -24,7 +25,7 @@ module.exports = async function acceptServerNoticesInviteAndConsent(session, nam return {inviteHandle, text}; })); const inviteHandle = invitesWithText.find(({inviteHandle, text}) => { - return text.trim() === name; + return text.trim() === noticesName; }).inviteHandle; await inviteHandle.click(); @@ -40,4 +41,5 @@ module.exports = async function acceptServerNoticesInviteAndConsent(session, nam const acceptButton = await termsPage.$('input[type=submit]'); await acceptButton.click(); await session.delay(500); //TODO yuck, timers -} \ No newline at end of file + session.log.done(); +} \ No newline at end of file diff --git a/src/tests/signup.js b/src/tests/signup.js index db6ad6208a..6b3f06c12c 100644 --- a/src/tests/signup.js +++ b/src/tests/signup.js @@ -18,6 +18,7 @@ const acceptTerms = require('./consent'); const assert = require('assert'); module.exports = async function signup(session, username, password, homeserver) { + session.log.step("signs up"); await session.goto(session.riotUrl('/#/register')); //click 'Custom server' radio button if (homeserver) { @@ -64,4 +65,5 @@ module.exports = async function signup(session, username, password, homeserver) const url = session.page.url(); assert.strictEqual(url, session.riotUrl('/#/home')); + session.log.done(); } diff --git a/start.js b/start.js index 28eba781e5..5e235dd1ef 100644 --- a/start.js +++ b/start.js @@ -16,33 +16,13 @@ limitations under the License. const assert = require('assert'); const RiotSession = require('./src/session'); +const scenario = require('./src/scenario'); -const signup = require('./src/tests/signup'); -const join = require('./src/tests/join'); -const createRoom = require('./src/tests/create-room'); -const acceptServerNoticesInviteAndConsent = require('./src/tests/server-notices-consent'); - -const homeserver = 'http://localhost:8008'; const riotserver = 'http://localhost:5000'; -let sessions = []; - -async function createUser(username, options, riotserver) { - const session = await RiotSession.create(username, options, riotserver); - sessions.push(session); - - session.log.step("signs up"); - await signup(session, session.username, 'testtest'); - session.log.done(); - - const noticesName = "Server Notices"; - session.log.step(`accepts "${noticesName}" invite and accepting terms & conditions`); - await acceptServerNoticesInviteAndConsent(session, noticesName); - session.log.done(); - return session; -} - async function runTests() { + let sessions = []; + console.log("running tests ..."); const options = {}; if (process.env.CHROME_PATH) { @@ -51,43 +31,45 @@ async function runTests() { options.executablePath = path; } - const alice = await createUser("alice", options, riotserver); - const bob = await createUser("bob", options, riotserver); - - const room = 'test'; - alice.log.step(`creates room ${room}`); - await createRoom(alice, room); - alice.log.done(); - - bob.log.step(`joins room ${room}`); - await createRoom(bob, room); - bob.log.done(); - - - await alice.close(); - await bob.close(); -} - -function onSuccess() { - console.log('all tests finished successfully'); -} - -async function onFailure(err) { - console.log('failure: ', err); - for(var i = 0; i < sessions.length; ++i) { - const session = sessions[i]; - documentHtml = await session.page.content(); - console.log(`---------------- START OF ${session.username} LOGS ----------------`); - console.log('---------------- console.log output:'); - console.log(session.consoleLogs()); - console.log('---------------- network requests:'); - console.log(session.networkLogs()); - console.log('---------------- document html:'); - console.log(documentHtml); - console.log(`---------------- END OF ${session.username} LOGS ----------------`); + async function createSession(username) { + const session = await RiotSession.create(username, options, riotserver); + sessions.push(session); + return session; + } + + let failure = false; + try { + await scenario(createSession); + } catch(err) { + console.log('failure: ', err); + for(let i = 0; i < sessions.length; ++i) { + const session = sessions[i]; + documentHtml = await session.page.content(); + console.log(`---------------- START OF ${session.username} LOGS ----------------`); + console.log('---------------- console.log output:'); + console.log(session.consoleLogs()); + console.log('---------------- network requests:'); + console.log(session.networkLogs()); + console.log('---------------- document html:'); + console.log(documentHtml); + console.log(`---------------- END OF ${session.username} LOGS ----------------`); + } + failure = true; + } + + for(let i = 0; i < sessions.length; ++i) { + const session = sessions[i]; + await session.close(); + } + + if (failure) { + process.exit(-1); + } else { + console.log('all tests finished successfully'); } - - process.exit(-1); } -runTests().then(onSuccess, onFailure); \ No newline at end of file +runTests().catch(function(err) { + console.log(err); + process.exit(-1); +}); \ No newline at end of file From aaa5ee1a25b7d698214a55df594c1aad50ce6294 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 18:21:53 +0200 Subject: [PATCH 06/13] more consistent naming on session methods --- src/session.js | 21 ++++++++++++--------- src/tests/consent.js | 2 +- src/tests/create-room.js | 8 ++++---- src/tests/join.js | 8 ++++---- src/tests/room-settings.js | 21 +++++++++++++++++++++ src/tests/server-notices-consent.js | 4 ++-- src/tests/signup.js | 16 ++++++++-------- 7 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 src/tests/room-settings.js diff --git a/src/session.js b/src/session.js index 5b0f78ccd3..d82c90d4d9 100644 --- a/src/session.js +++ b/src/session.js @@ -129,15 +129,22 @@ module.exports = class RiotSession { await input.type(text); } - // TODO: rename to waitAndQuery(Single)? - async waitAndQuerySelector(selector, timeout = 500) { + query(selector) { + return this.page.$(selector); + } + + async waitAndQuery(selector, timeout = 500) { await this.page.waitForSelector(selector, {visible: true, timeout}); - return await this.page.$(selector); + return await this.query(selector); + } + + queryAll(selector) { + return this.page.$$(selector); } async waitAndQueryAll(selector, timeout = 500) { await this.page.waitForSelector(selector, {visible: true, timeout}); - return await this.page.$$(selector); + return await this.queryAll(selector); } waitForNewPage(timeout = 500) { @@ -157,15 +164,11 @@ module.exports = class RiotSession { }); } - waitForSelector(selector) { - return this.page.waitForSelector(selector); - } - goto(url) { return this.page.goto(url); } - riotUrl(path) { + url(path) { return this.riotserver + path; } diff --git a/src/tests/consent.js b/src/tests/consent.js index 09026a3082..cd3d51c1b6 100644 --- a/src/tests/consent.js +++ b/src/tests/consent.js @@ -17,7 +17,7 @@ limitations under the License. const assert = require('assert'); module.exports = async function acceptTerms(session) { - const reviewTermsButton = await session.waitAndQuerySelector('.mx_QuestionDialog button.mx_Dialog_primary', 5000); + const reviewTermsButton = await session.waitAndQuery('.mx_QuestionDialog button.mx_Dialog_primary', 5000); const termsPagePromise = session.waitForNewPage(); await reviewTermsButton.click(); const termsPage = await termsPagePromise; diff --git a/src/tests/create-room.js b/src/tests/create-room.js index eff92baf83..8f5b5c9e85 100644 --- a/src/tests/create-room.js +++ b/src/tests/create-room.js @@ -19,15 +19,15 @@ const assert = require('assert'); module.exports = async function createRoom(session, roomName) { session.log.step(`creates room ${roomName}`); //TODO: brittle selector - const createRoomButton = await session.waitAndQuerySelector('.mx_RoleButton[aria-label="Create new room"]'); + const createRoomButton = await session.waitAndQuery('.mx_RoleButton[aria-label="Create new room"]'); await createRoomButton.click(); - const roomNameInput = await session.waitAndQuerySelector('.mx_CreateRoomDialog_input'); + const roomNameInput = await session.waitAndQuery('.mx_CreateRoomDialog_input'); await session.replaceInputText(roomNameInput, roomName); - const createButton = await session.waitAndQuerySelector('.mx_Dialog_primary'); + const createButton = await session.waitAndQuery('.mx_Dialog_primary'); await createButton.click(); - await session.waitForSelector('.mx_MessageComposer'); + await session.waitAndQuery('.mx_MessageComposer'); session.log.done(); } \ No newline at end of file diff --git a/src/tests/join.js b/src/tests/join.js index 72d4fe10cf..0577b7a8b6 100644 --- a/src/tests/join.js +++ b/src/tests/join.js @@ -19,16 +19,16 @@ const assert = require('assert'); module.exports = async function join(session, roomName) { session.log.step(`joins room ${roomName}`); //TODO: brittle selector - const directoryButton = await session.waitAndQuerySelector('.mx_RoleButton[aria-label="Room directory"]'); + const directoryButton = await session.waitAndQuery('.mx_RoleButton[aria-label="Room directory"]'); await directoryButton.click(); - const roomInput = await session.waitAndQuerySelector('.mx_DirectorySearchBox_input'); + const roomInput = await session.waitAndQuery('.mx_DirectorySearchBox_input'); await session.replaceInputText(roomInput, roomName); - const firstRoomLabel = await session.waitAndQuerySelector('.mx_RoomDirectory_table .mx_RoomDirectory_name:first-child'); + const firstRoomLabel = await session.waitAndQuery('.mx_RoomDirectory_table .mx_RoomDirectory_name:first-child'); await firstRoomLabel.click(); - const joinLink = await session.waitAndQuerySelector('.mx_RoomPreviewBar_join_text a'); + const joinLink = await session.waitAndQuery('.mx_RoomPreviewBar_join_text a'); await joinLink.click(); await session.waitForSelector('.mx_MessageComposer'); diff --git a/src/tests/room-settings.js b/src/tests/room-settings.js new file mode 100644 index 0000000000..70d84de10f --- /dev/null +++ b/src/tests/room-settings.js @@ -0,0 +1,21 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +const assert = require('assert'); + +module.exports = async function changeRoomSettings(session, settings) { + session.waitFor +} \ No newline at end of file diff --git a/src/tests/server-notices-consent.js b/src/tests/server-notices-consent.js index 53a318a169..d52588f962 100644 --- a/src/tests/server-notices-consent.js +++ b/src/tests/server-notices-consent.js @@ -30,10 +30,10 @@ module.exports = async function acceptServerNoticesInviteAndConsent(session, not await inviteHandle.click(); - const acceptInvitationLink = await session.waitAndQuerySelector(".mx_RoomPreviewBar_join_text a:first-child"); + const acceptInvitationLink = await session.waitAndQuery(".mx_RoomPreviewBar_join_text a:first-child"); await acceptInvitationLink.click(); - const consentLink = await session.waitAndQuerySelector(".mx_EventTile_body a", 1000); + const consentLink = await session.waitAndQuery(".mx_EventTile_body a", 1000); const termsPagePromise = session.waitForNewPage(); await consentLink.click(); diff --git a/src/tests/signup.js b/src/tests/signup.js index 6b3f06c12c..434083cbb6 100644 --- a/src/tests/signup.js +++ b/src/tests/signup.js @@ -19,16 +19,16 @@ const assert = require('assert'); module.exports = async function signup(session, username, password, homeserver) { session.log.step("signs up"); - await session.goto(session.riotUrl('/#/register')); + await session.goto(session.url('/#/register')); //click 'Custom server' radio button if (homeserver) { - const advancedRadioButton = await session.waitAndQuerySelector('#advanced'); + const advancedRadioButton = await session.waitAndQuery('#advanced'); await advancedRadioButton.click(); } // wait until register button is visible - await session.waitForSelector('.mx_Login_submit[value=Register]', {visible: true, timeout: 500}); + await session.waitAndQuery('.mx_Login_submit[value=Register]'); //fill out form - const loginFields = await session.page.$$('.mx_Login_field'); + const loginFields = await session.queryAll('.mx_Login_field'); assert.strictEqual(loginFields.length, 7); const usernameField = loginFields[2]; const passwordField = loginFields[3]; @@ -38,7 +38,7 @@ module.exports = async function signup(session, username, password, homeserver) await session.replaceInputText(passwordField, password); await session.replaceInputText(passwordRepeatField, password); if (homeserver) { - await session.waitForSelector('.mx_ServerConfig', {visible: true, timeout: 500}); + await session.waitAndQuery('.mx_ServerConfig'); await session.replaceInputText(hsurlField, homeserver); } //wait over a second because Registration/ServerConfig have a 1000ms @@ -47,7 +47,7 @@ module.exports = async function signup(session, username, password, homeserver) await session.delay(1200); /// focus on the button to make sure error validation /// has happened before checking the form is good to go - const registerButton = await session.page.$('.mx_Login_submit'); + const registerButton = await session.query('.mx_Login_submit'); await registerButton.focus(); //check no errors const error_text = await session.tryGetInnertext('.mx_Login_error'); @@ -57,13 +57,13 @@ module.exports = async function signup(session, username, password, homeserver) await registerButton.click(); //confirm dialog saying you cant log back in without e-mail - const continueButton = await session.waitAndQuerySelector('.mx_QuestionDialog button.mx_Dialog_primary'); + const continueButton = await session.waitAndQuery('.mx_QuestionDialog button.mx_Dialog_primary'); await continueButton.click(); //wait for registration to finish so the hash gets set //onhashchange better? await session.delay(2000); const url = session.page.url(); - assert.strictEqual(url, session.riotUrl('/#/home')); + assert.strictEqual(url, session.url('/#/home')); session.log.done(); } From 2a7438e9fbdb3ab4afbc2f722b1536dd5ab946c3 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 18:23:02 +0200 Subject: [PATCH 07/13] no need to double select here, might speed things up slightly --- src/session.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/session.js b/src/session.js index d82c90d4d9..e5e16400b8 100644 --- a/src/session.js +++ b/src/session.js @@ -133,9 +133,8 @@ module.exports = class RiotSession { return this.page.$(selector); } - async waitAndQuery(selector, timeout = 500) { - await this.page.waitForSelector(selector, {visible: true, timeout}); - return await this.query(selector); + waitAndQuery(selector, timeout = 500) { + return this.page.waitForSelector(selector, {visible: true, timeout}); } queryAll(selector) { @@ -143,7 +142,7 @@ module.exports = class RiotSession { } async waitAndQueryAll(selector, timeout = 500) { - await this.page.waitForSelector(selector, {visible: true, timeout}); + await this.waitAndQuery(selector, timeout); return await this.queryAll(selector); } From 643af2d344870e107944cb3f82a48a09d6026dd9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 7 Aug 2018 18:28:18 +0200 Subject: [PATCH 08/13] run synapse on custom port so it doesn't interfere with other synapses on dev machines --- riot/config-template/config.json | 6 +++--- synapse/config-templates/consent/homeserver.yaml | 6 +++--- synapse/install.sh | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/riot/config-template/config.json b/riot/config-template/config.json index 39bbd6490a..6277e567fc 100644 --- a/riot/config-template/config.json +++ b/riot/config-template/config.json @@ -1,5 +1,5 @@ { - "default_hs_url": "http://localhost:8008", + "default_hs_url": "http://localhost:5005", "default_is_url": "https://vector.im", "disable_custom_urls": false, "disable_guests": false, @@ -18,12 +18,12 @@ "default_theme": "light", "roomDirectory": { "servers": [ - "localhost:8008" + "localhost:5005" ] }, "piwik": { "url": "https://piwik.riot.im/", - "whitelistedHSUrls": ["http://localhost:8008"], + "whitelistedHSUrls": ["http://localhost:5005"], "whitelistedISUrls": ["https://vector.im", "https://matrix.org"], "siteId": 1 }, diff --git a/synapse/config-templates/consent/homeserver.yaml b/synapse/config-templates/consent/homeserver.yaml index a27fbf6f10..38aa4747b5 100644 --- a/synapse/config-templates/consent/homeserver.yaml +++ b/synapse/config-templates/consent/homeserver.yaml @@ -86,7 +86,7 @@ web_client: True # web_client_location: "/path/to/web/root" # The public-facing base URL for the client API (not including _matrix/...) -public_baseurl: http://localhost:8008/ +public_baseurl: http://localhost:{{SYNAPSE_PORT}}/ # Set the soft limit on the number of file descriptors synapse can use # Zero is used to indicate synapse should set the soft limit to the @@ -166,7 +166,7 @@ listeners: # Unsecure HTTP listener, # For when matrix traffic passes through loadbalancer that unwraps TLS. - - port: 8008 + - port: {{SYNAPSE_PORT}} tls: false bind_addresses: ['::', '0.0.0.0'] type: http @@ -693,5 +693,5 @@ user_consent: server_notices: system_mxid_localpart: notices system_mxid_display_name: "Server Notices" - system_mxid_avatar_url: "mxc://localhost:8008/oumMVlgDnLYFaPVkExemNVVZ" + system_mxid_avatar_url: "mxc://localhost:{{SYNAPSE_PORT}}/oumMVlgDnLYFaPVkExemNVVZ" room_name: "Server Notices" diff --git a/synapse/install.sh b/synapse/install.sh index dc4a08cb41..2e9b668b5e 100755 --- a/synapse/install.sh +++ b/synapse/install.sh @@ -4,7 +4,7 @@ SYNAPSE_BRANCH=master INSTALLATION_NAME=consent SERVER_DIR=installations/$INSTALLATION_NAME CONFIG_TEMPLATE=consent -PORT=8008 +PORT=5005 # set current directory to script directory BASE_DIR=$(readlink -f $(dirname $0)) @@ -33,7 +33,7 @@ python -m synapse.app.homeserver \ # apply configuration cp -r $BASE_DIR/config-templates/$CONFIG_TEMPLATE/. ./ sed -i "s#{{SYNAPSE_ROOT}}#$(pwd)/#g" homeserver.yaml -sed -i "s#{{SYNAPSE_PORT}}#${PORT}/#g" homeserver.yaml +sed -i "s#{{SYNAPSE_PORT}}#${PORT}#g" homeserver.yaml sed -i "s#{{FORM_SECRET}}#$(uuidgen)#g" homeserver.yaml sed -i "s#{{REGISTRATION_SHARED_SECRET}}#$(uuidgen)#g" homeserver.yaml sed -i "s#{{MACAROON_SECRET_KEY}}#$(uuidgen)#g" homeserver.yaml From a78c095cf657978e442113cc6e56cf80e60bbfb7 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 8 Aug 2018 11:39:17 +0200 Subject: [PATCH 09/13] add support for changing the room settings --- src/scenario.js | 4 ++- src/session.js | 39 +++++++++++++++-------- src/tests/join.js | 4 +-- src/tests/room-settings.js | 48 ++++++++++++++++++++++++++++- src/tests/server-notices-consent.js | 1 + start.js | 8 ++--- 6 files changed, 83 insertions(+), 21 deletions(-) diff --git a/src/scenario.js b/src/scenario.js index ee049a14f9..9aa0ed2ec0 100644 --- a/src/scenario.js +++ b/src/scenario.js @@ -18,6 +18,7 @@ limitations under the License. const signup = require('./tests/signup'); const join = require('./tests/join'); const createRoom = require('./tests/create-room'); +const changeRoomSettings = require('./tests/room-settings'); const acceptServerNoticesInviteAndConsent = require('./tests/server-notices-consent'); module.exports = async function scenario(createSession) { @@ -33,5 +34,6 @@ module.exports = async function scenario(createSession) { const bob = await createUser("bob"); const room = 'test'; await createRoom(alice, room); - // await join(bob, room); + await changeRoomSettings(alice, {directory: true, visibility: "public_no_guests"}); + await join(bob, room); } diff --git a/src/session.js b/src/session.js index e5e16400b8..4f6e04584f 100644 --- a/src/session.js +++ b/src/session.js @@ -33,15 +33,27 @@ class LogBuffer { class Logger { constructor(username) { + this.indent = 0; this.username = username; } - step(description) { - process.stdout.write(` * ${this.username} ${description} ... `); + startGroup(description) { + const indent = " ".repeat(this.indent * 2); + console.log(`${indent} * ${this.username} ${description}:`); + this.indent += 1; } - done() { - process.stdout.write("done\n"); + endGroup() { + this.indent -= 1; + } + + step(description) { + const indent = " ".repeat(this.indent * 2); + process.stdout.write(`${indent} * ${this.username} ${description} ... `); + } + + done(status = "done") { + process.stdout.write(status + "\n"); } } @@ -79,9 +91,17 @@ module.exports = class RiotSession { return null; } - async innerText(field) { - const text_handle = await field.getProperty('innerText'); - return await text_handle.jsonValue(); + async getElementProperty(handle, property) { + const propHandle = await handle.getProperty(property); + return await propHandle.jsonValue(); + } + + innerText(field) { + return this.getElementProperty(field, 'innerText'); + } + + getOuterHTML(element_handle) { + return this.getElementProperty(field, 'outerHTML'); } consoleLogs() { @@ -111,11 +131,6 @@ module.exports = class RiotSession { } } - async getOuterHTML(element_handle) { - const html_handle = await element_handle.getProperty('outerHTML'); - return await html_handle.jsonValue(); - } - async printElements(label, elements) { console.log(label, await Promise.all(elements.map(getOuterHTML))); } diff --git a/src/tests/join.js b/src/tests/join.js index 0577b7a8b6..3c76ad2c67 100644 --- a/src/tests/join.js +++ b/src/tests/join.js @@ -25,12 +25,12 @@ module.exports = async function join(session, roomName) { const roomInput = await session.waitAndQuery('.mx_DirectorySearchBox_input'); await session.replaceInputText(roomInput, roomName); - const firstRoomLabel = await session.waitAndQuery('.mx_RoomDirectory_table .mx_RoomDirectory_name:first-child'); + const firstRoomLabel = await session.waitAndQuery('.mx_RoomDirectory_table .mx_RoomDirectory_name:first-child', 1000); await firstRoomLabel.click(); const joinLink = await session.waitAndQuery('.mx_RoomPreviewBar_join_text a'); await joinLink.click(); - await session.waitForSelector('.mx_MessageComposer'); + await session.waitAndQuery('.mx_MessageComposer'); session.log.done(); } \ No newline at end of file diff --git a/src/tests/room-settings.js b/src/tests/room-settings.js index 70d84de10f..d7bbad3451 100644 --- a/src/tests/room-settings.js +++ b/src/tests/room-settings.js @@ -17,5 +17,51 @@ limitations under the License. const assert = require('assert'); module.exports = async function changeRoomSettings(session, settings) { - session.waitFor + session.log.startGroup(`changes the room settings`); + /// XXX delay is needed here, possible because the header is being rerendered + /// click doesn't do anything otherwise + await session.delay(500); + const settingsButton = await session.query(".mx_RoomHeader .mx_AccessibleButton[title=Settings]"); + await settingsButton.click(); + const checks = await session.waitAndQueryAll(".mx_RoomSettings_settings input[type=checkbox]"); + assert.equal(checks.length, 3); + const e2eEncryptionCheck = checks[0]; + const sendToUnverifiedDevices = checks[1]; + const isDirectory = checks[2]; + + if (typeof settings.directory === "boolean") { + session.log.step(`sets directory listing to ${settings.directory}`); + const checked = await session.getElementProperty(isDirectory, "checked"); + assert(typeof checked, "boolean"); + if (checked !== settings.directory) { + await isDirectory.click(); + session.log.done(); + } else { + session.log.done("already set"); + } + } + + if (settings.visibility) { + session.log.step(`sets visibility to ${settings.visibility}`); + const radios = await session.waitAndQueryAll(".mx_RoomSettings_settings input[type=radio]"); + assert.equal(radios.length, 7); + const inviteOnly = radios[0]; + const publicNoGuests = radios[1]; + const publicWithGuests = radios[2]; + + if (settings.visibility === "invite_only") { + await inviteOnly.click(); + } else if (settings.visibility === "public_no_guests") { + await publicNoGuests.click(); + } else if (settings.visibility === "public_with_guests") { + await publicWithGuests.click(); + } else { + throw new Error(`unrecognized room visibility setting: ${settings.visibility}`); + } + session.log.done(); + } + + const saveButton = await session.query(".mx_RoomHeader_wrapper .mx_RoomHeader_textButton"); + await saveButton.click(); + session.log.endGroup(); } \ No newline at end of file diff --git a/src/tests/server-notices-consent.js b/src/tests/server-notices-consent.js index d52588f962..def21d04c3 100644 --- a/src/tests/server-notices-consent.js +++ b/src/tests/server-notices-consent.js @@ -41,5 +41,6 @@ module.exports = async function acceptServerNoticesInviteAndConsent(session, not const acceptButton = await termsPage.$('input[type=submit]'); await acceptButton.click(); await session.delay(500); //TODO yuck, timers + await termsPage.close(); session.log.done(); } \ No newline at end of file diff --git a/start.js b/start.js index 5e235dd1ef..11dbe8d2fa 100644 --- a/start.js +++ b/start.js @@ -25,6 +25,7 @@ async function runTests() { console.log("running tests ..."); const options = {}; + // options.headless = false; if (process.env.CHROME_PATH) { const path = process.env.CHROME_PATH; console.log(`(using external chrome/chromium at ${path}, make sure it's compatible with puppeteer)`); @@ -41,6 +42,7 @@ async function runTests() { try { await scenario(createSession); } catch(err) { + failure = true; console.log('failure: ', err); for(let i = 0; i < sessions.length; ++i) { const session = sessions[i]; @@ -54,13 +56,9 @@ async function runTests() { console.log(documentHtml); console.log(`---------------- END OF ${session.username} LOGS ----------------`); } - failure = true; } - for(let i = 0; i < sessions.length; ++i) { - const session = sessions[i]; - await session.close(); - } + await Promise.all(sessions.map((session) => session.close())); if (failure) { process.exit(-1); From 1fd379b3d25208c8d53a5b6722129401090dc0a0 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 8 Aug 2018 12:17:36 +0200 Subject: [PATCH 10/13] wait to receive message from other user --- src/scenario.js | 4 ++++ src/tests/receive-message.js | 42 ++++++++++++++++++++++++++++++++++++ src/tests/room-settings.js | 2 +- src/tests/send-message.js | 25 +++++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/tests/receive-message.js create mode 100644 src/tests/send-message.js diff --git a/src/scenario.js b/src/scenario.js index 9aa0ed2ec0..14c901ba99 100644 --- a/src/scenario.js +++ b/src/scenario.js @@ -17,6 +17,8 @@ limitations under the License. const signup = require('./tests/signup'); const join = require('./tests/join'); +const sendMessage = require('./tests/send-message'); +const receiveMessage = require('./tests/receive-message'); const createRoom = require('./tests/create-room'); const changeRoomSettings = require('./tests/room-settings'); const acceptServerNoticesInviteAndConsent = require('./tests/server-notices-consent'); @@ -36,4 +38,6 @@ module.exports = async function scenario(createSession) { await createRoom(alice, room); await changeRoomSettings(alice, {directory: true, visibility: "public_no_guests"}); await join(bob, room); + await sendMessage(bob, "hi Alice!"); + await receiveMessage(alice, {sender: "bob", body: "hi Alice!"}); } diff --git a/src/tests/receive-message.js b/src/tests/receive-message.js new file mode 100644 index 0000000000..607bc1625e --- /dev/null +++ b/src/tests/receive-message.js @@ -0,0 +1,42 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +const assert = require('assert'); + + +async function getMessageFromTile(eventTile) { + const senderElement = await eventTile.$(".mx_SenderProfile_name"); + const bodyElement = await eventTile.$(".mx_EventTile_body"); + const sender = await(await senderElement.getProperty("innerText")).jsonValue(); + const body = await(await bodyElement.getProperty("innerText")).jsonValue(); + return {sender, body}; +} + +module.exports = async function receiveMessage(session, message) { + session.log.step(`waits to receive message from ${message.sender} in room`); + // wait for a response to come in that contains the message + // crude, but effective + await session.page.waitForResponse(async (response) => { + const body = await response.text(); + return body.indexOf(message.body) !== -1; + }); + + let lastTile = await session.waitAndQuery(".mx_EventTile_last"); + let lastMessage = await getMessageFromTile(lastTile); + assert.equal(lastMessage.body, message.body); + assert.equal(lastMessage.sender, message.sender); + session.log.done(); +} \ No newline at end of file diff --git a/src/tests/room-settings.js b/src/tests/room-settings.js index d7bbad3451..6001d14d34 100644 --- a/src/tests/room-settings.js +++ b/src/tests/room-settings.js @@ -32,7 +32,7 @@ module.exports = async function changeRoomSettings(session, settings) { if (typeof settings.directory === "boolean") { session.log.step(`sets directory listing to ${settings.directory}`); const checked = await session.getElementProperty(isDirectory, "checked"); - assert(typeof checked, "boolean"); + assert.equal(typeof checked, "boolean"); if (checked !== settings.directory) { await isDirectory.click(); session.log.done(); diff --git a/src/tests/send-message.js b/src/tests/send-message.js new file mode 100644 index 0000000000..8a61a15e94 --- /dev/null +++ b/src/tests/send-message.js @@ -0,0 +1,25 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +const assert = require('assert'); + +module.exports = async function sendMessage(session, message) { + session.log.step(`writes "${message}" in room`); + const composer = await session.waitAndQuery('.mx_MessageComposer'); + await composer.type(message); + await composer.press("Enter"); + session.log.done(); +} \ No newline at end of file From c5f064e389e970eaa7bbfe04acbf2a313f00655b Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 8 Aug 2018 12:35:36 +0200 Subject: [PATCH 11/13] make receiving a bit more robust --- src/tests/receive-message.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tests/receive-message.js b/src/tests/receive-message.js index 607bc1625e..c84aefcbfd 100644 --- a/src/tests/receive-message.js +++ b/src/tests/receive-message.js @@ -18,25 +18,25 @@ const assert = require('assert'); async function getMessageFromTile(eventTile) { - const senderElement = await eventTile.$(".mx_SenderProfile_name"); - const bodyElement = await eventTile.$(".mx_EventTile_body"); - const sender = await(await senderElement.getProperty("innerText")).jsonValue(); - const body = await(await bodyElement.getProperty("innerText")).jsonValue(); - return {sender, body}; } module.exports = async function receiveMessage(session, message) { - session.log.step(`waits to receive message from ${message.sender} in room`); + session.log.step(`receives message "${message.body}" from ${message.sender} in room`); // wait for a response to come in that contains the message // crude, but effective await session.page.waitForResponse(async (response) => { const body = await response.text(); return body.indexOf(message.body) !== -1; }); - - let lastTile = await session.waitAndQuery(".mx_EventTile_last"); - let lastMessage = await getMessageFromTile(lastTile); - assert.equal(lastMessage.body, message.body); - assert.equal(lastMessage.sender, message.sender); + // wait a bit for the incoming event to be rendered + await session.delay(100); + let lastTile = await session.query(".mx_EventTile_last"); + const senderElement = await lastTile.$(".mx_SenderProfile_name"); + const bodyElement = await lastTile.$(".mx_EventTile_body"); + const sender = await(await senderElement.getProperty("innerText")).jsonValue(); + const body = await(await bodyElement.getProperty("innerText")).jsonValue(); + + assert.equal(body, message.body); + assert.equal(sender, message.sender); session.log.done(); } \ No newline at end of file From 73c88fe603219c0c6a3f76d3eaef267e849f9c53 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 8 Aug 2018 12:35:50 +0200 Subject: [PATCH 12/13] prepare for more tests --- src/scenario.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/scenario.js b/src/scenario.js index 14c901ba99..07f9518029 100644 --- a/src/scenario.js +++ b/src/scenario.js @@ -34,10 +34,17 @@ module.exports = async function scenario(createSession) { const alice = await createUser("alice"); const bob = await createUser("bob"); + + await createDirectoryRoomAndTalk(alice, bob); +} + +async function createDirectoryRoomAndTalk(alice, bob) { + console.log(" creating a public room and join through directory:"); const room = 'test'; await createRoom(alice, room); await changeRoomSettings(alice, {directory: true, visibility: "public_no_guests"}); await join(bob, room); await sendMessage(bob, "hi Alice!"); await receiveMessage(alice, {sender: "bob", body: "hi Alice!"}); -} +} + From dc87e2bfe0a0268871f04c1b1946f01b851d4199 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 8 Aug 2018 12:42:34 +0200 Subject: [PATCH 13/13] avoid typos --- src/scenario.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/scenario.js b/src/scenario.js index 07f9518029..f035e94c35 100644 --- a/src/scenario.js +++ b/src/scenario.js @@ -41,10 +41,17 @@ module.exports = async function scenario(createSession) { async function createDirectoryRoomAndTalk(alice, bob) { console.log(" creating a public room and join through directory:"); const room = 'test'; + const message = "hi Alice!"; await createRoom(alice, room); await changeRoomSettings(alice, {directory: true, visibility: "public_no_guests"}); await join(bob, room); - await sendMessage(bob, "hi Alice!"); - await receiveMessage(alice, {sender: "bob", body: "hi Alice!"}); + await sendMessage(bob, message); + await receiveMessage(alice, {sender: "bob", body: message}); } +async function createE2ERoomAndTalk(alice, bob) { + await createRoom(bob, "secrets"); + await changeRoomSettings(bob, {encryption: true}); + await invite(bob, "@alice:localhost"); + await acceptInvite(alice, "secrets"); +} \ No newline at end of file