Revert ES6ification of end-to-end tests and add instructions for Windows

Because the tests are run directly by node, we have to use the CommonJS module syntax. We could run the thing through babel, but then we just have another babel.

Windows instructions are from experience and may not be optimized.
pull/21833/head
Travis Ralston 2020-01-10 10:13:41 -07:00
parent 9f04f94c65
commit e66f2a6c3f
24 changed files with 103 additions and 52 deletions

View File

@ -0,0 +1,39 @@
# Running the end-to-end tests on Windows
Windows is not the best platform to run the tests on, but if you have to, enable Windows Subsystem for Linux (WSL)
and start following these steps to get going:
1. Navigate to your working directory (`cd /mnt/c/users/travisr/whatever/matrix-react-sdk` for example).
2. Run `sudo apt-get install unzip python3 virtualenv dos2unix`
3. Run `dos2unix ./test/end-to-end-tests/*.sh ./test/end-to-end-tests/synapse/*.sh ./test/end-to-end-tests/riot/*.sh`
4. Install NodeJS for ubuntu:
```bash
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get update
sudo apt-get install nodejs
```
5. Start Riot on Windows through `yarn start`
6. While that builds... Run:
```bash
sudo apt-get install x11-apps
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome-stable_current_amd64.deb
sudo apt -f install
```
7. Run:
```bash
cd ./test/end-to-end-tests
./synapse/install.sh
./run.sh --riot-url http://localhost:8080 --no-sandbox
```
Note that using `yarn test:e2e` probably won't work for you. You might also have to use the config.json from the
`riot/config-template` directory in order to actually succeed at the tests.
Also note that you'll have to use `--no-sandbox` otherwise Chrome will complain that there's no sandbox available. You
could probably fix this with enough effort, or you could run a headless Chrome in the WSL container without a sandbox.
Reference material that isn't fully represented in the steps above (but snippets have been borrowed):
* https://virtualizationreview.com/articles/2017/02/08/graphical-programs-on-windows-subsystem-on-linux.aspx
* https://gist.github.com/drexler/d70ab957f964dbef1153d46bd853c775

View File

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export default class LogBuffer { module.exports = class LogBuffer {
constructor(page, eventName, eventMapper, reduceAsync=false, initialValue = "") { constructor(page, eventName, eventMapper, reduceAsync=false, initialValue = "") {
this.buffer = initialValue; this.buffer = initialValue;
page.on(eventName, (arg) => { page.on(eventName, (arg) => {

View File

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export default class Logger { module.exports = class Logger {
constructor(username) { constructor(username) {
this.indent = 0; this.indent = 0;
this.username = username; this.username = username;

View File

@ -19,7 +19,7 @@ const request = require('request-promise-native');
const cheerio = require('cheerio'); const cheerio = require('cheerio');
const url = require("url"); const url = require("url");
export async function approveConsent(consentUrl) { module.exports.approveConsent = async function(consentUrl) {
const body = await request.get(consentUrl); const body = await request.get(consentUrl);
const doc = cheerio.load(body); const doc = cheerio.load(body);
const v = doc("input[name=v]").val(); const v = doc("input[name=v]").val();
@ -28,4 +28,4 @@ export async function approveConsent(consentUrl) {
const formAction = doc("form").attr("action"); const formAction = doc("form").attr("action");
const absAction = url.resolve(consentUrl, formAction); const absAction = url.resolve(consentUrl, formAction);
await request.post(absAction).form({v, u, h}); await request.post(absAction).form({v, u, h});
} };

View File

@ -32,7 +32,7 @@ function execAsync(command, options) {
}); });
} }
export default class RestSessionCreator { module.exports = class RestSessionCreator {
constructor(synapseSubdir, hsUrl, cwd) { constructor(synapseSubdir, hsUrl, cwd) {
this.synapseSubdir = synapseSubdir; this.synapseSubdir = synapseSubdir;
this.hsUrl = hsUrl; this.hsUrl = hsUrl;
@ -72,12 +72,12 @@ export default class RestSessionCreator {
async _authenticate(username, password) { async _authenticate(username, password) {
const requestBody = { const requestBody = {
"type": "m.login.password", "type": "m.login.password",
"identifier": { "identifier": {
"type": "m.id.user", "type": "m.id.user",
"user": username, "user": username,
}, },
"password": password, "password": password,
}; };
const url = `${this.hsUrl}/_matrix/client/r0/login`; const url = `${this.hsUrl}/_matrix/client/r0/login`;
const responseBody = await request.post({url, json: true, body: requestBody}); const responseBody = await request.post({url, json: true, body: requestBody});

View File

@ -17,7 +17,7 @@ limitations under the License.
const Logger = require('../logger'); const Logger = require('../logger');
export default class RestMultiSession { module.exports = class RestMultiSession {
constructor(sessions, groupName) { constructor(sessions, groupName) {
this.log = new Logger(groupName); this.log = new Logger(groupName);
this.sessions = sessions; this.sessions = sessions;

View File

@ -18,7 +18,7 @@ limitations under the License.
const uuidv4 = require('uuid/v4'); const uuidv4 = require('uuid/v4');
/* no pun intented */ /* no pun intented */
export default class RestRoom { module.exports = class RestRoom {
constructor(session, roomId, log) { constructor(session, roomId, log) {
this.session = session; this.session = session;
this._roomId = roomId; this._roomId = roomId;

View File

@ -19,7 +19,7 @@ const Logger = require('../logger');
const RestRoom = require('./room'); const RestRoom = require('./room');
const {approveConsent} = require('./consent'); const {approveConsent} = require('./consent');
export default class RestSession { module.exports = class RestSession {
constructor(credentials) { constructor(credentials) {
this.log = new Logger(credentials.userId); this.log = new Logger(credentials.userId);
this._credentials = credentials; this._credentials = credentials;

View File

@ -22,7 +22,7 @@ const {receiveMessage} = require('../usecases/timeline');
const {createRoom} = require('../usecases/create-room'); const {createRoom} = require('../usecases/create-room');
const changeRoomSettings = require('../usecases/room-settings'); const changeRoomSettings = require('../usecases/room-settings');
export default async function roomDirectoryScenarios(alice, bob) { module.exports = async function roomDirectoryScenarios(alice, bob) {
console.log(" creating a public room and join through directory:"); console.log(" creating a public room and join through directory:");
const room = 'test'; const room = 'test';
await createRoom(alice, room); await createRoom(alice, room);

View File

@ -24,7 +24,7 @@ const changeRoomSettings = require('../usecases/room-settings');
const {startSasVerifcation, acceptSasVerification} = require('../usecases/verify'); const {startSasVerifcation, acceptSasVerification} = require('../usecases/verify');
const assert = require('assert'); const assert = require('assert');
export default async function e2eEncryptionScenarios(alice, bob) { module.exports = async function e2eEncryptionScenarios(alice, bob) {
console.log(" creating an e2e encrypted room and join through invite:"); console.log(" creating an e2e encrypted room and join through invite:");
const room = "secrets"; const room = "secrets";
await createRoom(bob, room); await createRoom(bob, room);

View File

@ -28,7 +28,7 @@ const {getMembersInMemberlist} = require('../usecases/memberlist');
const changeRoomSettings = require('../usecases/room-settings'); const changeRoomSettings = require('../usecases/room-settings');
const assert = require('assert'); const assert = require('assert');
export default async function lazyLoadingScenarios(alice, bob, charlies) { module.exports = async function lazyLoadingScenarios(alice, bob, charlies) {
console.log(" creating a room for lazy loading member scenarios:"); console.log(" creating a room for lazy loading member scenarios:");
const charly1to5 = charlies.slice("charly-1..5", 0, 5); const charly1to5 = charlies.slice("charly-1..5", 0, 5);
const charly6to10 = charlies.slice("charly-6..10", 5); const charly6to10 = charlies.slice("charly-6..10", 5);

View File

@ -22,7 +22,7 @@ const {delay} = require('./util');
const DEFAULT_TIMEOUT = 20000; const DEFAULT_TIMEOUT = 20000;
export default class RiotSession { module.exports = class RiotSession {
constructor(browser, page, username, riotserver, hsUrl) { constructor(browser, page, username, riotserver, hsUrl) {
this.browser = browser; this.browser = browser;
this.page = page; this.page = page;

View File

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export default async function acceptInvite(session, name) { module.exports = async function acceptInvite(session, name) {
session.log.step(`accepts "${name}" invite`); session.log.step(`accepts "${name}" invite`);
//TODO: brittle selector //TODO: brittle selector
const invitesHandles = await session.queryAll('.mx_RoomTile_name.mx_RoomTile_invite'); const invitesHandles = await session.queryAll('.mx_RoomTile_name.mx_RoomTile_invite');
@ -24,7 +24,7 @@ export default async function acceptInvite(session, name) {
return {inviteHandle, text}; return {inviteHandle, text};
})); }));
const inviteHandle = invitesWithText.find(({inviteHandle, text}) => { const inviteHandle = invitesWithText.find(({inviteHandle, text}) => {
return text.trim() === name; return text.trim() === name;
}).inviteHandle; }).inviteHandle;
await inviteHandle.click(); await inviteHandle.click();

View File

@ -15,12 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export async function openRoomDirectory(session) { async function openRoomDirectory(session) {
const roomDirectoryButton = await session.query('.mx_LeftPanel_explore .mx_AccessibleButton'); const roomDirectoryButton = await session.query('.mx_LeftPanel_explore .mx_AccessibleButton');
await roomDirectoryButton.click(); await roomDirectoryButton.click();
} }
export async function createRoom(session, roomName) { async function createRoom(session, roomName) {
session.log.step(`creates room "${roomName}"`); session.log.step(`creates room "${roomName}"`);
const roomListHeaders = await session.queryAll('.mx_RoomSubList_labelContainer'); const roomListHeaders = await session.queryAll('.mx_RoomSubList_labelContainer');
@ -43,3 +43,5 @@ export async function createRoom(session, roomName) {
await session.query('.mx_MessageComposer'); await session.query('.mx_MessageComposer');
session.log.done(); session.log.done();
} }
module.exports = {openRoomDirectory, createRoom};

View File

@ -17,20 +17,20 @@ limitations under the License.
const assert = require('assert'); const assert = require('assert');
export async function assertDialog(session, expectedTitle) { async function assertDialog(session, expectedTitle) {
const titleElement = await session.query(".mx_Dialog .mx_Dialog_title"); const titleElement = await session.query(".mx_Dialog .mx_Dialog_title");
const dialogHeader = await session.innerText(titleElement); const dialogHeader = await session.innerText(titleElement);
assert(dialogHeader, expectedTitle); assert(dialogHeader, expectedTitle);
} }
export async function acceptDialog(session, expectedTitle) { async function acceptDialog(session, expectedTitle) {
const foundDialog = await acceptDialogMaybe(session, expectedTitle); const foundDialog = await acceptDialogMaybe(session, expectedTitle);
if (!foundDialog) { if (!foundDialog) {
throw new Error("could not find a dialog"); throw new Error("could not find a dialog");
} }
} }
export async function acceptDialogMaybe(session, expectedTitle) { async function acceptDialogMaybe(session, expectedTitle) {
let primaryButton = null; let primaryButton = null;
try { try {
primaryButton = await session.query(".mx_Dialog .mx_Dialog_primary"); primaryButton = await session.query(".mx_Dialog .mx_Dialog_primary");
@ -43,3 +43,9 @@ export async function acceptDialogMaybe(session, expectedTitle) {
await primaryButton.click(); await primaryButton.click();
return true; return true;
} }
module.exports = {
assertDialog,
acceptDialog,
acceptDialogMaybe,
};

View File

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export default async function invite(session, userId) { module.exports = async function invite(session, userId) {
session.log.step(`invites "${userId}" to room`); session.log.step(`invites "${userId}" to room`);
await session.delay(1000); await session.delay(1000);
const memberPanelButton = await session.query(".mx_RightPanel_membersButton"); const memberPanelButton = await session.query(".mx_RightPanel_membersButton");

View File

@ -17,7 +17,7 @@ limitations under the License.
const {openRoomDirectory} = require('./create-room'); const {openRoomDirectory} = require('./create-room');
export default async function join(session, roomName) { module.exports = async function join(session, roomName) {
session.log.step(`joins room "${roomName}"`); session.log.step(`joins room "${roomName}"`);
await openRoomDirectory(session); await openRoomDirectory(session);
const roomInput = await session.query('.mx_DirectorySearchBox input'); const roomInput = await session.query('.mx_DirectorySearchBox input');

View File

@ -17,7 +17,7 @@ limitations under the License.
const assert = require('assert'); const assert = require('assert');
export async function openMemberInfo(session, name) { async function openMemberInfo(session, name) {
const membersAndNames = await getMembersInMemberlist(session); const membersAndNames = await getMembersInMemberlist(session);
const matchingLabel = membersAndNames.filter((m) => { const matchingLabel = membersAndNames.filter((m) => {
return m.displayName === name; return m.displayName === name;
@ -25,7 +25,9 @@ export async function openMemberInfo(session, name) {
await matchingLabel.click(); await matchingLabel.click();
} }
export async function verifyDeviceForUser(session, name, expectedDevice) { module.exports.openMemberInfo = openMemberInfo;
module.exports.verifyDeviceForUser = async function(session, name, expectedDevice) {
session.log.step(`verifies e2e device for ${name}`); session.log.step(`verifies e2e device for ${name}`);
const membersAndNames = await getMembersInMemberlist(session); const membersAndNames = await getMembersInMemberlist(session);
const matchingLabel = membersAndNames.filter((m) => { const matchingLabel = membersAndNames.filter((m) => {
@ -58,9 +60,9 @@ export async function verifyDeviceForUser(session, name, expectedDevice) {
const closeMemberInfo = await session.query(".mx_MemberInfo_cancel"); const closeMemberInfo = await session.query(".mx_MemberInfo_cancel");
await closeMemberInfo.click(); await closeMemberInfo.click();
session.log.done(); session.log.done();
} };
export async function getMembersInMemberlist(session) { async function getMembersInMemberlist(session) {
const memberPanelButton = await session.query(".mx_RightPanel_membersButton"); const memberPanelButton = await session.query(".mx_RightPanel_membersButton");
try { try {
await session.query(".mx_RightPanel_headerButton_highlight", 500); await session.query(".mx_RightPanel_headerButton_highlight", 500);
@ -77,3 +79,5 @@ export async function getMembersInMemberlist(session) {
return {label: el, displayName: await session.innerText(el)}; return {label: el, displayName: await session.innerText(el)};
})); }));
} }
module.exports.getMembersInMemberlist = getMembersInMemberlist;

View File

@ -30,7 +30,7 @@ async function setSettingsToggle(session, toggle, enabled) {
} }
} }
export default async function changeRoomSettings(session, settings) { module.exports = async function changeRoomSettings(session, settings) {
session.log.startGroup(`changes the room settings`); session.log.startGroup(`changes the room settings`);
/// XXX delay is needed here, possibly because the header is being rerendered /// XXX delay is needed here, possibly because the header is being rerendered
/// click doesn't do anything otherwise /// click doesn't do anything otherwise

View File

@ -29,7 +29,7 @@ async function openSettings(session, section) {
} }
} }
export async function enableLazyLoading(session) { module.exports.enableLazyLoading = async function(session) {
session.log.step(`enables lazy loading of members in the lab settings`); session.log.step(`enables lazy loading of members in the lab settings`);
const settingsButton = await session.query('.mx_BottomLeftMenu_settings'); const settingsButton = await session.query('.mx_BottomLeftMenu_settings');
await settingsButton.click(); await settingsButton.click();
@ -39,9 +39,9 @@ export async function enableLazyLoading(session) {
const closeButton = await session.query(".mx_RoomHeader_cancelButton"); const closeButton = await session.query(".mx_RoomHeader_cancelButton");
await closeButton.click(); await closeButton.click();
session.log.done(); session.log.done();
} };
export async function getE2EDeviceFromSettings(session) { module.exports.getE2EDeviceFromSettings = async function(session) {
session.log.step(`gets e2e device/key from settings`); session.log.step(`gets e2e device/key from settings`);
await openSettings(session, "security"); await openSettings(session, "security");
const deviceAndKey = await session.queryAll(".mx_SettingsTab_section .mx_SecurityUserSettingsTab_deviceInfo code"); const deviceAndKey = await session.queryAll(".mx_SettingsTab_section .mx_SecurityUserSettingsTab_deviceInfo code");
@ -52,4 +52,4 @@ export async function getE2EDeviceFromSettings(session) {
await closeButton.click(); await closeButton.click();
session.log.done(); session.log.done();
return {id, key}; return {id, key};
} };

View File

@ -17,7 +17,7 @@ limitations under the License.
const assert = require('assert'); const assert = require('assert');
export default async function signup(session, username, password, homeserver) { module.exports = async function signup(session, username, password, homeserver) {
session.log.step("signs up"); session.log.step("signs up");
await session.goto(session.url('/#/register')); await session.goto(session.url('/#/register'));
// change the homeserver by clicking the advanced section // change the homeserver by clicking the advanced section

View File

@ -17,7 +17,7 @@ limitations under the License.
const assert = require('assert'); const assert = require('assert');
export async function scrollToTimelineTop(session) { module.exports.scrollToTimelineTop = async function(session) {
session.log.step(`scrolls to the top of the timeline`); session.log.step(`scrolls to the top of the timeline`);
await session.page.evaluate(() => { await session.page.evaluate(() => {
return Promise.resolve().then(async () => { return Promise.resolve().then(async () => {
@ -41,9 +41,9 @@ export async function scrollToTimelineTop(session) {
}); });
}); });
session.log.done(); session.log.done();
} };
export async function receiveMessage(session, expectedMessage) { module.exports.receiveMessage = async function(session, expectedMessage) {
session.log.step(`receives message "${expectedMessage.body}" from ${expectedMessage.sender}`); session.log.step(`receives message "${expectedMessage.body}" from ${expectedMessage.sender}`);
// wait for a response to come in that contains the message // wait for a response to come in that contains the message
// crude, but effective // crude, but effective
@ -67,10 +67,10 @@ export async function receiveMessage(session, expectedMessage) {
}); });
assertMessage(lastMessage, expectedMessage); assertMessage(lastMessage, expectedMessage);
session.log.done(); session.log.done();
} };
export async function checkTimelineContains(session, expectedMessages, sendersDescription) { module.exports.checkTimelineContains = async function(session, expectedMessages, sendersDescription) {
session.log.step(`checks timeline contains ${expectedMessages.length} ` + session.log.step(`checks timeline contains ${expectedMessages.length} ` +
`given messages${sendersDescription ? ` from ${sendersDescription}`:""}`); `given messages${sendersDescription ? ` from ${sendersDescription}`:""}`);
const eventTiles = await getAllEventTiles(session); const eventTiles = await getAllEventTiles(session);
@ -91,7 +91,7 @@ export async function checkTimelineContains(session, expectedMessages, sendersDe
expectedMessages.forEach((expectedMessage) => { expectedMessages.forEach((expectedMessage) => {
const foundMessage = timelineMessages.find((message) => { const foundMessage = timelineMessages.find((message) => {
return message.sender === expectedMessage.sender && return message.sender === expectedMessage.sender &&
message.body === expectedMessage.body; message.body === expectedMessage.body;
}); });
try { try {
assertMessage(foundMessage, expectedMessage); assertMessage(foundMessage, expectedMessage);
@ -102,7 +102,7 @@ export async function checkTimelineContains(session, expectedMessages, sendersDe
}); });
session.log.done(); session.log.done();
} };
function assertMessage(foundMessage, expectedMessage) { function assertMessage(foundMessage, expectedMessage) {
assert(foundMessage, `message ${JSON.stringify(expectedMessage)} not found in timeline`); assert(foundMessage, `message ${JSON.stringify(expectedMessage)} not found in timeline`);

View File

@ -38,7 +38,7 @@ async function getSasCodes(session) {
return sasLabels; return sasLabels;
} }
export async function startSasVerifcation(session, name) { module.exports.startSasVerifcation = async function(session, name) {
await startVerification(session, name); await startVerification(session, name);
// expect "Verify device" dialog and click "Begin Verification" // expect "Verify device" dialog and click "Begin Verification"
await assertDialog(session, "Verify device"); await assertDialog(session, "Verify device");
@ -51,9 +51,9 @@ export async function startSasVerifcation(session, name) {
// click "Got it" when verification is done // click "Got it" when verification is done
await acceptDialog(session); await acceptDialog(session);
return sasCodes; return sasCodes;
} };
export async function acceptSasVerification(session, name) { module.exports.acceptSasVerification = async function(session, name) {
await assertDialog(session, "Incoming Verification Request"); await assertDialog(session, "Incoming Verification Request");
const opponentLabelElement = await session.query(".mx_IncomingSasDialog_opponentProfile h2"); const opponentLabelElement = await session.query(".mx_IncomingSasDialog_opponentProfile h2");
const opponentLabel = await session.innerText(opponentLabelElement); const opponentLabel = await session.innerText(opponentLabelElement);
@ -67,4 +67,4 @@ export async function acceptSasVerification(session, name) {
// click "Got it" when verification is done // click "Got it" when verification is done
await acceptDialog(session); await acceptDialog(session);
return sasCodes; return sasCodes;
} };

View File

@ -15,14 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export function range(start, amount, step = 1) { module.exports.range = function(start, amount, step = 1) {
const r = []; const r = [];
for (let i = 0; i < amount; ++i) { for (let i = 0; i < amount; ++i) {
r.push(start + (i * step)); r.push(start + (i * step));
} }
return r; return r;
} };
export function delay(ms) { module.exports.delay = function(ms) {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
} };