bring indentation in line with other front-end projects

This commit is contained in:
Bruno Windels 2018-08-14 12:53:16 +02:00
parent 2c983f8cee
commit 377a20fffa
22 changed files with 583 additions and 560 deletions

23
.editorconfig Normal file
View file

@ -0,0 +1,23 @@
# Copyright 2017 Aviral Dasgupta
#
# 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.
root = true
[*]
charset=utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

View file

@ -1,14 +1,14 @@
{
"name": "e2e-tests",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"puppeteer": "^1.6.0"
}
"name": "e2e-tests",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"puppeteer": "^1.6.0"
}
}

View file

@ -3,8 +3,8 @@ RIOT_BRANCH=master
BASE_DIR=$(readlink -f $(dirname $0))
if [ -d $BASE_DIR/riot-web ]; then
echo "riot is already installed"
exit
echo "riot is already installed"
exit
fi
cd $BASE_DIR

View file

@ -5,7 +5,7 @@ PIDFILE=$BASE_DIR/riot.pid
CONFIG_BACKUP=config.e2etests_backup.json
if [ -f $PIDFILE ]; then
exit
exit
fi
cd $BASE_DIR/
@ -14,29 +14,29 @@ pushd riot-web/webapp/ > /dev/null
# backup config file before we copy template
if [ -f config.json ]; then
mv config.json $CONFIG_BACKUP
mv config.json $CONFIG_BACKUP
fi
cp $BASE_DIR/config-template/config.json .
LOGFILE=$(mktemp)
# run web server in the background, showing output on error
(
python -m SimpleHTTPServer $PORT > $LOGFILE 2>&1 &
PID=$!
echo $PID > $PIDFILE
# wait so subshell does not exit
# otherwise sleep below would not work
wait $PID; RESULT=$?
python -m SimpleHTTPServer $PORT > $LOGFILE 2>&1 &
PID=$!
echo $PID > $PIDFILE
# wait so subshell does not exit
# otherwise sleep below would not work
wait $PID; RESULT=$?
# NOT expected SIGTERM (128 + 15)
# from stop.sh?
if [ $RESULT -ne 143 ]; then
echo "failed"
cat $LOGFILE
rm $PIDFILE 2> /dev/null
fi
rm $LOGFILE
exit $RESULT
# NOT expected SIGTERM (128 + 15)
# from stop.sh?
if [ $RESULT -ne 143 ]; then
echo "failed"
cat $LOGFILE
rm $PIDFILE 2> /dev/null
fi
rm $LOGFILE
exit $RESULT
)&
# to be able to return the exit code for immediate errors (like address already in use)
# we wait for a short amount of time in the background and exit when the first
@ -46,6 +46,6 @@ sleep 0.5 &
wait -n; RESULT=$?
# return exit code of first child to exit
if [ $RESULT -eq 0 ]; then
echo "running"
echo "running"
fi
exit $RESULT

View file

@ -6,15 +6,15 @@ CONFIG_BACKUP=config.e2etests_backup.json
cd $BASE_DIR
if [ -f $PIDFILE ]; then
echo "stopping riot server ..."
PID=$(cat $PIDFILE)
rm $PIDFILE
kill $PID
echo "stopping riot server ..."
PID=$(cat $PIDFILE)
rm $PIDFILE
kill $PID
# revert config file
cd riot-web/webapp
rm config.json
if [ -f $CONFIG_BACKUP ]; then
mv $CONFIG_BACKUP config.json
fi
# revert config file
cd riot-web/webapp
rm config.json
if [ -f $CONFIG_BACKUP ]; then
mv $CONFIG_BACKUP config.json
fi
fi

View file

@ -5,7 +5,7 @@ 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
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,
@ -29,57 +29,57 @@ const getE2EDeviceFromSettings = require('./tests/e2e-device');
const verifyDeviceForUser = require("./tests/verify-device");
module.exports = async function scenario(createSession) {
async function createUser(username) {
const session = await createSession(username);
await signup(session, session.username, 'testtest');
await acceptServerNoticesInviteAndConsent(session);
return session;
}
async function createUser(username) {
const session = await createSession(username);
await signup(session, session.username, 'testtest');
await acceptServerNoticesInviteAndConsent(session);
return session;
}
const alice = await createUser("alice");
const bob = await createUser("bob");
const alice = await createUser("alice");
const bob = await createUser("bob");
await createDirectoryRoomAndTalk(alice, bob);
await createE2ERoomAndTalk(alice, bob);
await createDirectoryRoomAndTalk(alice, bob);
await createE2ERoomAndTalk(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);
const bobMessage = "hi Alice!";
await sendMessage(bob, bobMessage);
await receiveMessage(alice, {sender: "bob", body: bobMessage});
const aliceMessage = "hi Bob, welcome!"
await sendMessage(alice, aliceMessage);
await receiveMessage(bob, {sender: "alice", body: aliceMessage});
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);
const bobMessage = "hi Alice!";
await sendMessage(bob, bobMessage);
await receiveMessage(alice, {sender: "bob", body: bobMessage});
const aliceMessage = "hi Bob, welcome!"
await sendMessage(alice, aliceMessage);
await receiveMessage(bob, {sender: "alice", body: aliceMessage});
}
async function createE2ERoomAndTalk(alice, bob) {
console.log(" creating an e2e encrypted room and join through invite:");
const room = "secrets";
await createRoom(bob, room);
await changeRoomSettings(bob, {encryption: true});
await invite(bob, "@alice:localhost");
await acceptInvite(alice, room);
const bobDevice = await getE2EDeviceFromSettings(bob);
// wait some time for the encryption warning dialog
// to appear after closing the settings
await bob.delay(500);
await acceptDialog(bob, "encryption");
const aliceDevice = await getE2EDeviceFromSettings(alice);
// wait some time for the encryption warning dialog
// to appear after closing the settings
await alice.delay(500);
await acceptDialog(alice, "encryption");
await verifyDeviceForUser(bob, "alice", aliceDevice);
await verifyDeviceForUser(alice, "bob", bobDevice);
const aliceMessage = "Guess what I just heard?!"
await sendMessage(alice, aliceMessage);
await receiveMessage(bob, {sender: "alice", body: aliceMessage, encrypted: true});
const bobMessage = "You've got to tell me!";
await sendMessage(bob, bobMessage);
await receiveMessage(alice, {sender: "bob", body: bobMessage, encrypted: true});
console.log(" creating an e2e encrypted room and join through invite:");
const room = "secrets";
await createRoom(bob, room);
await changeRoomSettings(bob, {encryption: true});
await invite(bob, "@alice:localhost");
await acceptInvite(alice, room);
const bobDevice = await getE2EDeviceFromSettings(bob);
// wait some time for the encryption warning dialog
// to appear after closing the settings
await bob.delay(500);
await acceptDialog(bob, "encryption");
const aliceDevice = await getE2EDeviceFromSettings(alice);
// wait some time for the encryption warning dialog
// to appear after closing the settings
await alice.delay(500);
await acceptDialog(alice, "encryption");
await verifyDeviceForUser(bob, "alice", aliceDevice);
await verifyDeviceForUser(alice, "bob", bobDevice);
const aliceMessage = "Guess what I just heard?!"
await sendMessage(alice, aliceMessage);
await receiveMessage(bob, {sender: "alice", body: aliceMessage, encrypted: true});
const bobMessage = "You've got to tell me!";
await sendMessage(bob, bobMessage);
await receiveMessage(alice, {sender: "bob", body: bobMessage, encrypted: true});
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -17,180 +17,180 @@ 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;
}
});
}
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;
}
});
}
}
class Logger {
constructor(username) {
this.indent = 0;
this.username = username;
}
constructor(username) {
this.indent = 0;
this.username = username;
}
startGroup(description) {
const indent = " ".repeat(this.indent * 2);
console.log(`${indent} * ${this.username} ${description}:`);
this.indent += 1;
}
startGroup(description) {
const indent = " ".repeat(this.indent * 2);
console.log(`${indent} * ${this.username} ${description}:`);
this.indent += 1;
}
endGroup() {
this.indent -= 1;
}
endGroup() {
this.indent -= 1;
}
step(description) {
const indent = " ".repeat(this.indent * 2);
process.stdout.write(`${indent} * ${this.username} ${description} ... `);
}
step(description) {
const indent = " ".repeat(this.indent * 2);
process.stdout.write(`${indent} * ${this.username} ${description} ... `);
}
done(status = "done") {
process.stdout.write(status + "\n");
}
done(status = "done") {
process.stdout.write(status + "\n");
}
}
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);
this.log = new Logger(this.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();
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);
this.log = new Logger(this.username);
}
return null;
}
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() {
return this.consoleLog.buffer;
}
networkLogs() {
return this.networkLog.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;
}
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 printElements(label, elements) {
console.log(label, await Promise.all(elements.map(getOuterHTML)));
}
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 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);
}
async getElementProperty(handle, property) {
const propHandle = await handle.getProperty(property);
return await propHandle.jsonValue();
}
query(selector) {
return this.page.$(selector);
}
innerText(field) {
return this.getElementProperty(field, 'innerText');
}
waitAndQuery(selector, timeout = 500) {
return this.page.waitForSelector(selector, {visible: true, timeout});
}
getOuterHTML(element_handle) {
return this.getElementProperty(field, 'outerHTML');
}
queryAll(selector) {
return this.page.$$(selector);
}
consoleLogs() {
return this.consoleLog.buffer;
}
async waitAndQueryAll(selector, timeout = 500) {
await this.waitAndQuery(selector, timeout);
return await this.queryAll(selector);
}
networkLogs() {
return this.networkLog.buffer;
}
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);
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;
}
}
}
const callback = async (target) => {
clearTimeout(timeoutHandle);
const page = await target.page();
resolve(page);
};
async printElements(label, elements) {
console.log(label, await Promise.all(elements.map(getOuterHTML)));
}
this.browser.once('targetcreated', callback);
});
}
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);
}
goto(url) {
return this.page.goto(url);
}
query(selector) {
return this.page.$(selector);
}
url(path) {
return this.riotserver + path;
}
waitAndQuery(selector, timeout = 500) {
return this.page.waitForSelector(selector, {visible: true, timeout});
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
queryAll(selector) {
return this.page.$$(selector);
}
close() {
return this.browser.close();
}
async waitAndQueryAll(selector, timeout = 500) {
await this.waitAndQuery(selector, timeout);
return await this.queryAll(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);
});
}
goto(url) {
return this.page.goto(url);
}
url(path) {
return this.riotserver + path;
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
close() {
return this.browser.close();
}
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -18,24 +18,24 @@ const assert = require('assert');
const {acceptDialogMaybe} = require('./dialog');
module.exports = async function acceptInvite(session, name) {
session.log.step(`accepts "${name}" invite`);
//TODO: brittle selector
const invitesHandles = await session.waitAndQueryAll('.mx_RoomTile_name.mx_RoomTile_invite', 1000);
const invitesWithText = await Promise.all(invitesHandles.map(async (inviteHandle) => {
const text = await session.innerText(inviteHandle);
return {inviteHandle, text};
}));
const inviteHandle = invitesWithText.find(({inviteHandle, text}) => {
return text.trim() === name;
}).inviteHandle;
session.log.step(`accepts "${name}" invite`);
//TODO: brittle selector
const invitesHandles = await session.waitAndQueryAll('.mx_RoomTile_name.mx_RoomTile_invite', 1000);
const invitesWithText = await Promise.all(invitesHandles.map(async (inviteHandle) => {
const text = await session.innerText(inviteHandle);
return {inviteHandle, text};
}));
const inviteHandle = invitesWithText.find(({inviteHandle, text}) => {
return text.trim() === name;
}).inviteHandle;
await inviteHandle.click();
await inviteHandle.click();
const acceptInvitationLink = await session.waitAndQuery(".mx_RoomPreviewBar_join_text a:first-child");
await acceptInvitationLink.click();
const acceptInvitationLink = await session.waitAndQuery(".mx_RoomPreviewBar_join_text a:first-child");
await acceptInvitationLink.click();
// accept e2e warning dialog
acceptDialogMaybe(session, "encryption");
// accept e2e warning dialog
acceptDialogMaybe(session, "encryption");
session.log.done();
session.log.done();
}

View file

@ -17,11 +17,11 @@ limitations under the License.
const assert = require('assert');
module.exports = async function acceptTerms(session) {
const reviewTermsButton = await session.waitAndQuery('.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 session.delay(500); //TODO yuck, timers
const reviewTermsButton = await session.waitAndQuery('.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 session.delay(500); //TODO yuck, timers
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -17,17 +17,17 @@ 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.waitAndQuery('.mx_RoleButton[aria-label="Create new room"]');
await createRoomButton.click();
session.log.step(`creates room "${roomName}"`);
//TODO: brittle selector
const createRoomButton = await session.waitAndQuery('.mx_RoleButton[aria-label="Create new room"]');
await createRoomButton.click();
const roomNameInput = await session.waitAndQuery('.mx_CreateRoomDialog_input');
await session.replaceInputText(roomNameInput, roomName);
const roomNameInput = await session.waitAndQuery('.mx_CreateRoomDialog_input');
await session.replaceInputText(roomNameInput, roomName);
const createButton = await session.waitAndQuery('.mx_Dialog_primary');
await createButton.click();
const createButton = await session.waitAndQuery('.mx_Dialog_primary');
await createButton.click();
await session.waitAndQuery('.mx_MessageComposer');
session.log.done();
await session.waitAndQuery('.mx_MessageComposer');
session.log.done();
}

View file

@ -18,30 +18,30 @@ const assert = require('assert');
async function acceptDialog(session, expectedContent) {
const foundDialog = await acceptDialogMaybe(session, expectedContent);
if (!foundDialog) {
throw new Error("could not find a dialog");
}
const foundDialog = await acceptDialogMaybe(session, expectedContent);
if (!foundDialog) {
throw new Error("could not find a dialog");
}
}
async function acceptDialogMaybe(session, expectedContent) {
let dialog = null;
try {
dialog = await session.waitAndQuery(".mx_QuestionDialog", 100);
} catch(err) {
return false;
}
if (expectedContent) {
const contentElement = await dialog.$(".mx_Dialog_content");
const content = await (await contentElement.getProperty("innerText")).jsonValue();
assert.ok(content.indexOf(expectedContent) !== -1);
}
const primaryButton = await dialog.$(".mx_Dialog_primary");
await primaryButton.click();
return true;
let dialog = null;
try {
dialog = await session.waitAndQuery(".mx_QuestionDialog", 100);
} catch(err) {
return false;
}
if (expectedContent) {
const contentElement = await dialog.$(".mx_Dialog_content");
const content = await (await contentElement.getProperty("innerText")).jsonValue();
assert.ok(content.indexOf(expectedContent) !== -1);
}
const primaryButton = await dialog.$(".mx_Dialog_primary");
await primaryButton.click();
return true;
}
module.exports = {
acceptDialog,
acceptDialogMaybe,
acceptDialog,
acceptDialogMaybe,
};

View file

@ -5,7 +5,7 @@ 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
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,
@ -17,15 +17,15 @@ limitations under the License.
const assert = require('assert');
module.exports = async function getE2EDeviceFromSettings(session) {
session.log.step(`gets e2e device/key from settings`);
const settingsButton = await session.query('.mx_BottomLeftMenu_settings');
await settingsButton.click();
const deviceAndKey = await session.waitAndQueryAll(".mx_UserSettings_section.mx_UserSettings_cryptoSection code");
assert.equal(deviceAndKey.length, 2);
const id = await (await deviceAndKey[0].getProperty("innerText")).jsonValue();
const key = await (await deviceAndKey[1].getProperty("innerText")).jsonValue();
const closeButton = await session.query(".mx_RoomHeader_cancelButton");
await closeButton.click();
session.log.done();
return {id, key};
session.log.step(`gets e2e device/key from settings`);
const settingsButton = await session.query('.mx_BottomLeftMenu_settings');
await settingsButton.click();
const deviceAndKey = await session.waitAndQueryAll(".mx_UserSettings_section.mx_UserSettings_cryptoSection code");
assert.equal(deviceAndKey.length, 2);
const id = await (await deviceAndKey[0].getProperty("innerText")).jsonValue();
const key = await (await deviceAndKey[1].getProperty("innerText")).jsonValue();
const closeButton = await session.query(".mx_RoomHeader_cancelButton");
await closeButton.click();
session.log.done();
return {id, key};
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -17,14 +17,14 @@ limitations under the License.
const assert = require('assert');
module.exports = async function invite(session, userId) {
session.log.step(`invites "${userId}" to room`);
await session.delay(200);
const inviteButton = await session.waitAndQuery(".mx_RightPanel_invite");
await inviteButton.click();
const inviteTextArea = await session.waitAndQuery(".mx_ChatInviteDialog textarea");
await inviteTextArea.type(userId);
await inviteTextArea.press("Enter");
const confirmButton = await session.query(".mx_Dialog_primary");
await confirmButton.click();
session.log.done();
session.log.step(`invites "${userId}" to room`);
await session.delay(200);
const inviteButton = await session.waitAndQuery(".mx_RightPanel_invite");
await inviteButton.click();
const inviteTextArea = await session.waitAndQuery(".mx_ChatInviteDialog textarea");
await inviteTextArea.type(userId);
await inviteTextArea.press("Enter");
const confirmButton = await session.query(".mx_Dialog_primary");
await confirmButton.click();
session.log.done();
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -17,20 +17,20 @@ 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.waitAndQuery('.mx_RoleButton[aria-label="Room directory"]');
await directoryButton.click();
session.log.step(`joins room "${roomName}"`);
//TODO: brittle selector
const directoryButton = await session.waitAndQuery('.mx_RoleButton[aria-label="Room directory"]');
await directoryButton.click();
const roomInput = await session.waitAndQuery('.mx_DirectorySearchBox_input');
await session.replaceInputText(roomInput, 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', 1000);
await firstRoomLabel.click();
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();
const joinLink = await session.waitAndQuery('.mx_RoomPreviewBar_join_text a');
await joinLink.click();
await session.waitAndQuery('.mx_MessageComposer');
session.log.done();
await session.waitAndQuery('.mx_MessageComposer');
session.log.done();
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -17,33 +17,33 @@ limitations under the License.
const assert = require('assert');
module.exports = async function receiveMessage(session, message) {
session.log.step(`receives message "${message.body}" from ${message.sender}`);
// wait for a response to come in that contains the message
// crude, but effective
await session.page.waitForResponse(async (response) => {
if (response.request().url().indexOf("/sync") === -1) {
return false;
}
const body = await response.text();
session.log.step(`receives message "${message.body}" from ${message.sender}`);
// wait for a response to come in that contains the message
// crude, but effective
await session.page.waitForResponse(async (response) => {
if (response.request().url().indexOf("/sync") === -1) {
return false;
}
const body = await response.text();
if (message.encrypted) {
return body.indexOf(message.sender) !== -1 &&
body.indexOf("m.room.encrypted") !== -1;
} else {
return body.indexOf(message.body) !== -1;
}
});
// wait a bit for the incoming event to be rendered
await session.delay(300);
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();
if (message.encrypted) {
return body.indexOf(message.sender) !== -1 &&
body.indexOf("m.room.encrypted") !== -1;
} else {
return body.indexOf(message.body) !== -1;
const e2eIcon = await lastTile.$(".mx_EventTile_e2eIcon");
assert.ok(e2eIcon);
}
});
// wait a bit for the incoming event to be rendered
await session.delay(300);
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();
if (message.encrypted) {
const e2eIcon = await lastTile.$(".mx_EventTile_e2eIcon");
assert.ok(e2eIcon);
}
assert.equal(body, message.body);
assert.equal(sender, message.sender);
session.log.done();
assert.equal(body, message.body);
assert.equal(sender, message.sender);
session.log.done();
}

View file

@ -18,66 +18,66 @@ const assert = require('assert');
const {acceptDialog} = require('./dialog');
async function setCheckboxSetting(session, checkbox, enabled) {
const checked = await session.getElementProperty(checkbox, "checked");
assert.equal(typeof checked, "boolean");
if (checked !== enabled) {
await checkbox.click();
session.log.done();
return true;
} else {
session.log.done("already set");
}
const checked = await session.getElementProperty(checkbox, "checked");
assert.equal(typeof checked, "boolean");
if (checked !== enabled) {
await checkbox.click();
session.log.done();
return true;
} else {
session.log.done("already set");
}
}
module.exports = async function changeRoomSettings(session, settings) {
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];
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}`);
await setCheckboxSetting(session, isDirectory, settings.directory);
}
if (typeof settings.directory === "boolean") {
session.log.step(`sets directory listing to ${settings.directory}`);
await setCheckboxSetting(session, isDirectory, settings.directory);
}
if (typeof settings.encryption === "boolean") {
session.log.step(`sets room e2e encryption to ${settings.encryption}`);
const clicked = await setCheckboxSetting(session, e2eEncryptionCheck, settings.encryption);
// if enabling, accept beta warning dialog
if (clicked && settings.encryption) {
await acceptDialog(session, "encryption");
}
}
if (typeof settings.encryption === "boolean") {
session.log.step(`sets room e2e encryption to ${settings.encryption}`);
const clicked = await setCheckboxSetting(session, e2eEncryptionCheck, settings.encryption);
// if enabling, accept beta warning dialog
if (clicked && settings.encryption) {
await acceptDialog(session, "encryption");
}
}
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) {
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();
}
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();
const saveButton = await session.query(".mx_RoomHeader_wrapper .mx_RoomHeader_textButton");
await saveButton.click();
session.log.endGroup();
session.log.endGroup();
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -17,13 +17,13 @@ limitations under the License.
const assert = require('assert');
module.exports = async function sendMessage(session, message) {
session.log.step(`writes "${message}" in room`);
// this selector needs to be the element that has contenteditable=true,
// not any if its parents, otherwise it behaves flaky at best.
const composer = await session.waitAndQuery('.mx_MessageComposer_editor');
await composer.type(message);
const text = await session.innerText(composer);
assert.equal(text.trim(), message.trim());
await composer.press("Enter");
session.log.done();
session.log.step(`writes "${message}" in room`);
// this selector needs to be the element that has contenteditable=true,
// not any if its parents, otherwise it behaves flaky at best.
const composer = await session.waitAndQuery('.mx_MessageComposer_editor');
await composer.type(message);
const text = await session.innerText(composer);
assert.equal(text.trim(), message.trim());
await composer.press("Enter");
session.log.done();
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -17,15 +17,15 @@ limitations under the License.
const assert = require('assert');
const acceptInvite = require("./accept-invite")
module.exports = async function acceptServerNoticesInviteAndConsent(session) {
await acceptInvite(session, "Server Notices");
session.log.step(`accepts terms & conditions`);
const consentLink = await session.waitAndQuery(".mx_EventTile_body a", 1000);
const termsPagePromise = session.waitForNewPage();
await consentLink.click();
const termsPage = await termsPagePromise;
const acceptButton = await termsPage.$('input[type=submit]');
await acceptButton.click();
await session.delay(500); //TODO yuck, timers
await termsPage.close();
session.log.done();
await acceptInvite(session, "Server Notices");
session.log.step(`accepts terms & conditions`);
const consentLink = await session.waitAndQuery(".mx_EventTile_body a", 1000);
const termsPagePromise = session.waitForNewPage();
await consentLink.click();
const termsPage = await termsPagePromise;
const acceptButton = await termsPage.$('input[type=submit]');
await acceptButton.click();
await session.delay(500); //TODO yuck, timers
await termsPage.close();
session.log.done();
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -18,52 +18,52 @@ 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.url('/#/register'));
//click 'Custom server' radio button
if (homeserver) {
const advancedRadioButton = await session.waitAndQuery('#advanced');
await advancedRadioButton.click();
}
// wait until register button is visible
await session.waitAndQuery('.mx_Login_submit[value=Register]');
//fill out form
const loginFields = await session.queryAll('.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 session.replaceInputText(usernameField, username);
await session.replaceInputText(passwordField, password);
await session.replaceInputText(passwordRepeatField, password);
if (homeserver) {
await session.waitAndQuery('.mx_ServerConfig');
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 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.query('.mx_Login_submit');
await registerButton.focus();
//check no errors
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();
session.log.step("signs up");
await session.goto(session.url('/#/register'));
//click 'Custom server' radio button
if (homeserver) {
const advancedRadioButton = await session.waitAndQuery('#advanced');
await advancedRadioButton.click();
}
// wait until register button is visible
await session.waitAndQuery('.mx_Login_submit[value=Register]');
//fill out form
const loginFields = await session.queryAll('.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 session.replaceInputText(usernameField, username);
await session.replaceInputText(passwordField, password);
await session.replaceInputText(passwordRepeatField, password);
if (homeserver) {
await session.waitAndQuery('.mx_ServerConfig');
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 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.query('.mx_Login_submit');
await registerButton.focus();
//check no errors
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 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);
//confirm dialog saying you cant log back in without e-mail
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.url('/#/home'));
session.log.done();
const url = session.page.url();
assert.strictEqual(url, session.url('/#/home'));
session.log.done();
}

View file

@ -5,7 +5,7 @@ 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
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,
@ -17,26 +17,26 @@ limitations under the License.
const assert = require('assert');
module.exports = async function verifyDeviceForUser(session, name, expectedDevice) {
session.log.step(`verifies e2e device for ${name}`);
const memberNameElements = await session.queryAll(".mx_MemberList .mx_EntityTile_name");
const membersAndNames = await Promise.all(memberNameElements.map(async (el) => {
return [el, await session.innerText(el)];
}));
const matchingMember = membersAndNames.filter(([el, text]) => {
return text === name;
}).map(([el]) => el)[0];
await matchingMember.click();
const firstVerifyButton = await session.waitAndQuery(".mx_MemberDeviceInfo_verify");
await firstVerifyButton.click();
const dialogCodeFields = await session.waitAndQueryAll(".mx_QuestionDialog code");
assert.equal(dialogCodeFields.length, 2);
const deviceId = await session.innerText(dialogCodeFields[0]);
const deviceKey = await session.innerText(dialogCodeFields[1]);
assert.equal(expectedDevice.id, deviceId);
assert.equal(expectedDevice.key, deviceKey);
const confirmButton = await session.query(".mx_Dialog_primary");
await confirmButton.click();
const closeMemberInfo = await session.query(".mx_MemberInfo_cancel");
await closeMemberInfo.click();
session.log.done();
session.log.step(`verifies e2e device for ${name}`);
const memberNameElements = await session.queryAll(".mx_MemberList .mx_EntityTile_name");
const membersAndNames = await Promise.all(memberNameElements.map(async (el) => {
return [el, await session.innerText(el)];
}));
const matchingMember = membersAndNames.filter(([el, text]) => {
return text === name;
}).map(([el]) => el)[0];
await matchingMember.click();
const firstVerifyButton = await session.waitAndQuery(".mx_MemberDeviceInfo_verify");
await firstVerifyButton.click();
const dialogCodeFields = await session.waitAndQueryAll(".mx_QuestionDialog code");
assert.equal(dialogCodeFields.length, 2);
const deviceId = await session.innerText(dialogCodeFields[0]);
const deviceKey = await session.innerText(dialogCodeFields[1]);
assert.equal(expectedDevice.id, deviceId);
assert.equal(expectedDevice.key, deviceKey);
const confirmButton = await session.query(".mx_Dialog_primary");
await confirmButton.click();
const closeMemberInfo = await session.query(".mx_MemberInfo_cancel");
await closeMemberInfo.click();
session.log.done();
}

106
start.js
View file

@ -5,7 +5,7 @@ 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
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,
@ -24,64 +24,64 @@ const noLogs = process.argv.indexOf("--no-logs") !== -1;
const debug = process.argv.indexOf("--debug") !== -1;
async function runTests() {
let sessions = [];
let sessions = [];
console.log("running tests ...");
const options = {};
if (debug) {
// options.slowMo = 10;
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)`);
options.executablePath = path;
}
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) {
failure = true;
console.log('failure: ', err);
if (!noLogs) {
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 ----------------`);
}
console.log("running tests ...");
const options = {};
if (debug) {
options.slowMo = 20;
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)`);
options.executablePath = path;
}
}
// wait 5 minutes on failure if not running headless
// to inspect what went wrong
if (failure && options.headless === false) {
await new Promise((resolve) => setTimeout(resolve, 5 * 60 * 1000));
}
async function createSession(username) {
const session = await RiotSession.create(username, options, riotserver);
sessions.push(session);
return session;
}
await Promise.all(sessions.map((session) => session.close()));
let failure = false;
try {
await scenario(createSession);
} catch(err) {
failure = true;
console.log('failure: ', err);
if (!noLogs) {
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 ----------------`);
}
}
}
if (failure) {
process.exit(-1);
} else {
console.log('all tests finished successfully');
}
// wait 5 minutes on failure if not running headless
// to inspect what went wrong
if (failure && options.headless === false) {
await new Promise((resolve) => setTimeout(resolve, 5 * 60 * 1000));
}
await Promise.all(sessions.map((session) => session.close()));
if (failure) {
process.exit(-1);
} else {
console.log('all tests finished successfully');
}
}
runTests().catch(function(err) {
console.log(err);
process.exit(-1);
console.log(err);
process.exit(-1);
});

View file

@ -9,8 +9,8 @@ PORT=5005
BASE_DIR=$(readlink -f $(dirname $0))
if [ -d $BASE_DIR/$SERVER_DIR ]; then
echo "synapse is already installed"
exit
echo "synapse is already installed"
exit
fi
cd $BASE_DIR