From 5745e9ed0cfeb745046fc8788dccf012334b6b3a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 12 Sep 2018 18:36:02 +0200 Subject: [PATCH 1/8] move Logger and LogBuffer to own module --- src/logbuffer.js | 30 +++++++++++++++++++++++ src/logger.js | 62 ++++++++++++++++++++++++++++++++++++++++++++++ src/session.js | 64 ++---------------------------------------------- 3 files changed, 94 insertions(+), 62 deletions(-) create mode 100644 src/logbuffer.js create mode 100644 src/logger.js diff --git a/src/logbuffer.js b/src/logbuffer.js new file mode 100644 index 0000000000..8bf6285e25 --- /dev/null +++ b/src/logbuffer.js @@ -0,0 +1,30 @@ +/* +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. +*/ + +module.exports = 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; + } + }); + } +} diff --git a/src/logger.js b/src/logger.js new file mode 100644 index 0000000000..be3ebde75b --- /dev/null +++ b/src/logger.js @@ -0,0 +1,62 @@ +/* +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. +*/ + +module.exports = class Logger { + constructor(username) { + this.indent = 0; + this.username = username; + this.muted = false; + } + + startGroup(description) { + if (!this.muted) { + const indent = " ".repeat(this.indent * 2); + console.log(`${indent} * ${this.username} ${description}:`); + } + this.indent += 1; + return this; + } + + endGroup() { + this.indent -= 1; + return this; + } + + step(description) { + if (!this.muted) { + const indent = " ".repeat(this.indent * 2); + process.stdout.write(`${indent} * ${this.username} ${description} ... `); + } + return this; + } + + done(status = "done") { + if (!this.muted) { + process.stdout.write(status + "\n"); + } + return this; + } + + mute() { + this.muted = true; + return this; + } + + unmute() { + this.muted = false; + return this; + } +} diff --git a/src/session.js b/src/session.js index 0bfe781e61..86cdd05e95 100644 --- a/src/session.js +++ b/src/session.js @@ -15,68 +15,8 @@ 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; - } - }); - } -} - -class Logger { - constructor(username) { - this.indent = 0; - this.username = username; - this.muted = false; - } - - startGroup(description) { - if (!this.muted) { - const indent = " ".repeat(this.indent * 2); - console.log(`${indent} * ${this.username} ${description}:`); - } - this.indent += 1; - return this; - } - - endGroup() { - this.indent -= 1; - return this; - } - - step(description) { - if (!this.muted) { - const indent = " ".repeat(this.indent * 2); - process.stdout.write(`${indent} * ${this.username} ${description} ... `); - } - return this; - } - - done(status = "done") { - if (!this.muted) { - process.stdout.write(status + "\n"); - } - return this; - } - - mute() { - this.muted = true; - return this; - } - - unmute() { - this.muted = false; - return this; - } -} +const Logger = require('./logger'); +const LogBuffer = require('./logbuffer'); module.exports = class RiotSession { constructor(browser, page, username, riotserver, hsUrl) { From 923ae90576d3beb3a1070d75880d1c7aaf3a06c0 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 12 Sep 2018 18:38:42 +0200 Subject: [PATCH 2/8] move range and delay over to util module --- src/scenario.js | 13 +++---------- src/session.js | 3 ++- src/util.js | 27 +++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 src/util.js diff --git a/src/scenario.js b/src/scenario.js index 946a4122ef..3538fa2d40 100644 --- a/src/scenario.js +++ b/src/scenario.js @@ -21,6 +21,7 @@ const join = require('./tests/join'); const sendMessage = require('./tests/send-message'); const acceptInvite = require('./tests/accept-invite'); const invite = require('./tests/invite'); +const {delay, range} = require('./util'); const { receiveMessage, checkTimelineContains, @@ -49,14 +50,6 @@ module.exports = async function scenario(createSession, restCreator) { await aLazyLoadingTest(alice, bob, charlies); } -function range(start, amount, step = 1) { - const r = []; - for (let i = 0; i < amount; ++i) { - r.push(start + (i * step)); - } - return r; -} - async function createRestUsers(restCreator) { const usernames = range(1, 10).map((i) => `charly-${i}`); const charlies = await restCreator.createSessionRange(usernames, 'testtest'); @@ -88,12 +81,12 @@ async function createE2ERoomAndTalk(alice, bob) { const bobDevice = await getE2EDeviceFromSettings(bob); // wait some time for the encryption warning dialog // to appear after closing the settings - await bob.delay(1000); + await delay(1000); await acceptDialogMaybe(bob, "encryption"); const aliceDevice = await getE2EDeviceFromSettings(alice); // wait some time for the encryption warning dialog // to appear after closing the settings - await alice.delay(1000); + await delay(1000); await acceptDialogMaybe(alice, "encryption"); await verifyDeviceForUser(bob, "alice", aliceDevice); await verifyDeviceForUser(alice, "bob", bobDevice); diff --git a/src/session.js b/src/session.js index 86cdd05e95..839b4a495b 100644 --- a/src/session.js +++ b/src/session.js @@ -17,6 +17,7 @@ limitations under the License. const puppeteer = require('puppeteer'); const Logger = require('./logger'); const LogBuffer = require('./logbuffer'); +const {delay} = require('./util'); module.exports = class RiotSession { constructor(browser, page, username, riotserver, hsUrl) { @@ -169,7 +170,7 @@ module.exports = class RiotSession { } delay(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + return delay(ms); } close() { diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000000..8080d771be --- /dev/null +++ b/src/util.js @@ -0,0 +1,27 @@ +/* +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. +*/ + +module.exports.range = function(start, amount, step = 1) { + const r = []; + for (let i = 0; i < amount; ++i) { + r.push(start + (i * step)); + } + return r; +} + +module.exports.delay = function(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} From 5ec8f6f9b41cd8d65093ae70f6031372ccb30865 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 12 Sep 2018 18:40:25 +0200 Subject: [PATCH 3/8] rename tests folder to the more accurate usecases --- src/scenario.js | 24 +++++++++---------- src/{tests => usecases}/accept-invite.js | 0 src/{tests => usecases}/consent.js | 0 src/{tests => usecases}/create-room.js | 0 src/{tests => usecases}/dialog.js | 0 src/{tests => usecases}/invite.js | 0 src/{tests => usecases}/join.js | 0 src/{tests => usecases}/room-settings.js | 0 src/{tests => usecases}/send-message.js | 0 .../server-notices-consent.js | 0 src/{tests => usecases}/settings.js | 0 src/{tests => usecases}/signup.js | 0 src/{tests => usecases}/timeline.js | 0 src/{tests => usecases}/verify-device.js | 0 14 files changed, 12 insertions(+), 12 deletions(-) rename src/{tests => usecases}/accept-invite.js (100%) rename src/{tests => usecases}/consent.js (100%) rename src/{tests => usecases}/create-room.js (100%) rename src/{tests => usecases}/dialog.js (100%) rename src/{tests => usecases}/invite.js (100%) rename src/{tests => usecases}/join.js (100%) rename src/{tests => usecases}/room-settings.js (100%) rename src/{tests => usecases}/send-message.js (100%) rename src/{tests => usecases}/server-notices-consent.js (100%) rename src/{tests => usecases}/settings.js (100%) rename src/{tests => usecases}/signup.js (100%) rename src/{tests => usecases}/timeline.js (100%) rename src/{tests => usecases}/verify-device.js (100%) diff --git a/src/scenario.js b/src/scenario.js index 3538fa2d40..77307689ea 100644 --- a/src/scenario.js +++ b/src/scenario.js @@ -15,23 +15,23 @@ limitations under the License. */ -const {acceptDialogMaybe} = require('./tests/dialog'); -const signup = require('./tests/signup'); -const join = require('./tests/join'); -const sendMessage = require('./tests/send-message'); -const acceptInvite = require('./tests/accept-invite'); -const invite = require('./tests/invite'); const {delay, range} = require('./util'); +const {acceptDialogMaybe} = require('./usecases/dialog'); +const signup = require('./usecases/signup'); +const join = require('./usecases/join'); +const sendMessage = require('./usecases/send-message'); +const acceptInvite = require('./usecases/accept-invite'); +const invite = require('./usecases/invite'); const { receiveMessage, checkTimelineContains, scrollToTimelineTop -} = require('./tests/timeline'); -const createRoom = require('./tests/create-room'); -const changeRoomSettings = require('./tests/room-settings'); -const acceptServerNoticesInviteAndConsent = require('./tests/server-notices-consent'); -const {enableLazyLoading, getE2EDeviceFromSettings} = require('./tests/settings'); -const verifyDeviceForUser = require("./tests/verify-device"); +} = require('./usecases/timeline'); +const createRoom = require('./usecases/create-room'); +const changeRoomSettings = require('./usecases/room-settings'); +const acceptServerNoticesInviteAndConsent = require('./usecases/server-notices-consent'); +const {enableLazyLoading, getE2EDeviceFromSettings} = require('./usecases/settings'); +const verifyDeviceForUser = require("./usecases/verify-device"); module.exports = async function scenario(createSession, restCreator) { async function createUser(username) { diff --git a/src/tests/accept-invite.js b/src/usecases/accept-invite.js similarity index 100% rename from src/tests/accept-invite.js rename to src/usecases/accept-invite.js diff --git a/src/tests/consent.js b/src/usecases/consent.js similarity index 100% rename from src/tests/consent.js rename to src/usecases/consent.js diff --git a/src/tests/create-room.js b/src/usecases/create-room.js similarity index 100% rename from src/tests/create-room.js rename to src/usecases/create-room.js diff --git a/src/tests/dialog.js b/src/usecases/dialog.js similarity index 100% rename from src/tests/dialog.js rename to src/usecases/dialog.js diff --git a/src/tests/invite.js b/src/usecases/invite.js similarity index 100% rename from src/tests/invite.js rename to src/usecases/invite.js diff --git a/src/tests/join.js b/src/usecases/join.js similarity index 100% rename from src/tests/join.js rename to src/usecases/join.js diff --git a/src/tests/room-settings.js b/src/usecases/room-settings.js similarity index 100% rename from src/tests/room-settings.js rename to src/usecases/room-settings.js diff --git a/src/tests/send-message.js b/src/usecases/send-message.js similarity index 100% rename from src/tests/send-message.js rename to src/usecases/send-message.js diff --git a/src/tests/server-notices-consent.js b/src/usecases/server-notices-consent.js similarity index 100% rename from src/tests/server-notices-consent.js rename to src/usecases/server-notices-consent.js diff --git a/src/tests/settings.js b/src/usecases/settings.js similarity index 100% rename from src/tests/settings.js rename to src/usecases/settings.js diff --git a/src/tests/signup.js b/src/usecases/signup.js similarity index 100% rename from src/tests/signup.js rename to src/usecases/signup.js diff --git a/src/tests/timeline.js b/src/usecases/timeline.js similarity index 100% rename from src/tests/timeline.js rename to src/usecases/timeline.js diff --git a/src/tests/verify-device.js b/src/usecases/verify-device.js similarity index 100% rename from src/tests/verify-device.js rename to src/usecases/verify-device.js From 1725e7524b6c71482218625703220a36d52bc7fa Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 13 Sep 2018 10:31:15 +0200 Subject: [PATCH 4/8] split up scenarios in multiple files as lazy-loading scenarios grow --- src/scenario.js | 101 +++----------------------------- src/scenarios/README.md | 1 + src/scenarios/directory.js | 36 ++++++++++++ src/scenarios/e2e-encryption.js | 55 +++++++++++++++++ src/scenarios/lazy-loading.js | 62 ++++++++++++++++++++ src/session.js | 2 +- src/usecases/README.md | 2 + 7 files changed, 164 insertions(+), 95 deletions(-) create mode 100644 src/scenarios/README.md create mode 100644 src/scenarios/directory.js create mode 100644 src/scenarios/e2e-encryption.js create mode 100644 src/scenarios/lazy-loading.js create mode 100644 src/usecases/README.md diff --git a/src/scenario.js b/src/scenario.js index 77307689ea..f0b4ad988b 100644 --- a/src/scenario.js +++ b/src/scenario.js @@ -15,23 +15,12 @@ limitations under the License. */ -const {delay, range} = require('./util'); -const {acceptDialogMaybe} = require('./usecases/dialog'); +const {range} = require('./util'); const signup = require('./usecases/signup'); -const join = require('./usecases/join'); -const sendMessage = require('./usecases/send-message'); -const acceptInvite = require('./usecases/accept-invite'); -const invite = require('./usecases/invite'); -const { - receiveMessage, - checkTimelineContains, - scrollToTimelineTop -} = require('./usecases/timeline'); -const createRoom = require('./usecases/create-room'); -const changeRoomSettings = require('./usecases/room-settings'); const acceptServerNoticesInviteAndConsent = require('./usecases/server-notices-consent'); -const {enableLazyLoading, getE2EDeviceFromSettings} = require('./usecases/settings'); -const verifyDeviceForUser = require("./usecases/verify-device"); +const roomDirectoryScenarios = require('./scenarios/directory'); +const lazyLoadingScenarios = require('./scenarios/lazy-loading'); +const e2eEncryptionScenarios = require('./scenarios/e2e-encryption'); module.exports = async function scenario(createSession, restCreator) { async function createUser(username) { @@ -45,9 +34,9 @@ module.exports = async function scenario(createSession, restCreator) { const bob = await createUser("bob"); const charlies = await createRestUsers(restCreator); - await createDirectoryRoomAndTalk(alice, bob); - await createE2ERoomAndTalk(alice, bob); - await aLazyLoadingTest(alice, bob, charlies); + await roomDirectoryScenarios(alice, bob); + await e2eEncryptionScenarios(alice, bob); + await lazyLoadingScenarios(alice, bob, charlies); } async function createRestUsers(restCreator) { @@ -56,79 +45,3 @@ async function createRestUsers(restCreator) { await charlies.setDisplayName((s) => `Charly #${s.userName().split('-')[1]}`); return charlies; } - -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}); -} - -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 delay(1000); - await acceptDialogMaybe(bob, "encryption"); - const aliceDevice = await getE2EDeviceFromSettings(alice); - // wait some time for the encryption warning dialog - // to appear after closing the settings - await delay(1000); - await acceptDialogMaybe(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}); -} - -async function aLazyLoadingTest(alice, bob, charlies) { - console.log(" creating a room for lazy loading member scenarios:"); - await enableLazyLoading(alice); - const room = "Lazy Loading Test"; - const alias = "#lltest:localhost"; - const charlyMsg1 = "hi bob!"; - const charlyMsg2 = "how's it going??"; - await createRoom(bob, room); - await changeRoomSettings(bob, {directory: true, visibility: "public_no_guests", alias}); - // wait for alias to be set by server after clicking "save" - // so the charlies can join it. - await bob.delay(500); - const charlyMembers = await charlies.join(alias); - await charlyMembers.talk(charlyMsg1); - await charlyMembers.talk(charlyMsg2); - bob.log.step("sends 20 messages").mute(); - for(let i = 20; i >= 1; --i) { - await sendMessage(bob, `I will only say this ${i} time(s)!`); - } - bob.log.unmute().done(); - await join(alice, alias); - await scrollToTimelineTop(alice); - //alice should see 2 messages from every charly with - //the correct display name - const expectedMessages = [charlyMsg1, charlyMsg2].reduce((messages, msgText) => { - return charlies.sessions.reduce((messages, charly) => { - return messages.concat({ - sender: charly.displayName(), - body: msgText, - }); - }, messages); - }, []); - await checkTimelineContains(alice, expectedMessages, "Charly #1-10"); -} diff --git a/src/scenarios/README.md b/src/scenarios/README.md new file mode 100644 index 0000000000..4eabc8f9ef --- /dev/null +++ b/src/scenarios/README.md @@ -0,0 +1 @@ +scenarios contains the high-level playbook for the test suite diff --git a/src/scenarios/directory.js b/src/scenarios/directory.js new file mode 100644 index 0000000000..3b87d64d78 --- /dev/null +++ b/src/scenarios/directory.js @@ -0,0 +1,36 @@ +/* +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 join = require('../usecases/join'); +const sendMessage = require('../usecases/send-message'); +const {receiveMessage} = require('../usecases/timeline'); +const createRoom = require('../usecases/create-room'); +const changeRoomSettings = require('../usecases/room-settings'); + +module.exports = async function roomDirectoryScenarios(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); //looks up room in directory + 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}); +} diff --git a/src/scenarios/e2e-encryption.js b/src/scenarios/e2e-encryption.js new file mode 100644 index 0000000000..938dc5e592 --- /dev/null +++ b/src/scenarios/e2e-encryption.js @@ -0,0 +1,55 @@ +/* +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 {delay} = require('../util'); +const {acceptDialogMaybe} = require('../usecases/dialog'); +const join = require('../usecases/join'); +const sendMessage = require('../usecases/send-message'); +const acceptInvite = require('../usecases/accept-invite'); +const invite = require('../usecases/invite'); +const {receiveMessage} = require('../usecases/timeline'); +const createRoom = require('../usecases/create-room'); +const changeRoomSettings = require('../usecases/room-settings'); +const {getE2EDeviceFromSettings} = require('../usecases/settings'); +const verifyDeviceForUser = require('../usecases/verify-device'); + +module.exports = async function e2eEncryptionScenarios(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 delay(1000); + await acceptDialogMaybe(bob, "encryption"); + const aliceDevice = await getE2EDeviceFromSettings(alice); + // wait some time for the encryption warning dialog + // to appear after closing the settings + await delay(1000); + await acceptDialogMaybe(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}); +} diff --git a/src/scenarios/lazy-loading.js b/src/scenarios/lazy-loading.js new file mode 100644 index 0000000000..f7360622a1 --- /dev/null +++ b/src/scenarios/lazy-loading.js @@ -0,0 +1,62 @@ +/* +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 {delay} = require('../util'); +const join = require('../usecases/join'); +const sendMessage = require('../usecases/send-message'); +const { + checkTimelineContains, + scrollToTimelineTop +} = require('../usecases/timeline'); +const createRoom = require('../usecases/create-room'); +const changeRoomSettings = require('../usecases/room-settings'); +const {enableLazyLoading} = require('../usecases/settings'); + +module.exports = async function lazyLoadingScenarios(alice, bob, charlies) { + console.log(" creating a room for lazy loading member scenarios:"); + await enableLazyLoading(alice); + const room = "Lazy Loading Test"; + const alias = "#lltest:localhost"; + const charlyMsg1 = "hi bob!"; + const charlyMsg2 = "how's it going??"; + await createRoom(bob, room); + await changeRoomSettings(bob, {directory: true, visibility: "public_no_guests", alias}); + // wait for alias to be set by server after clicking "save" + // so the charlies can join it. + await bob.delay(500); + const charlyMembers = await charlies.join(alias); + await charlyMembers.talk(charlyMsg1); + await charlyMembers.talk(charlyMsg2); + bob.log.step("sends 20 messages").mute(); + for(let i = 20; i >= 1; --i) { + await sendMessage(bob, `I will only say this ${i} time(s)!`); + } + bob.log.unmute().done(); + await join(alice, alias); + await scrollToTimelineTop(alice); + //alice should see 2 messages from every charly with + //the correct display name + const expectedMessages = [charlyMsg1, charlyMsg2].reduce((messages, msgText) => { + return charlies.sessions.reduce((messages, charly) => { + return messages.concat({ + sender: charly.displayName(), + body: msgText, + }); + }, messages); + }, []); + await checkTimelineContains(alice, expectedMessages, "Charly #1-10"); +} diff --git a/src/session.js b/src/session.js index 839b4a495b..3f233ee8f2 100644 --- a/src/session.js +++ b/src/session.js @@ -124,7 +124,7 @@ module.exports = class RiotSession { return await this.queryAll(selector); } - waitForReload(timeout = 5000) { + waitForReload(timeout = 10000) { return new Promise((resolve, reject) => { const timeoutHandle = setTimeout(() => { this.browser.removeEventListener('domcontentloaded', callback); diff --git a/src/usecases/README.md b/src/usecases/README.md new file mode 100644 index 0000000000..daa990e15c --- /dev/null +++ b/src/usecases/README.md @@ -0,0 +1,2 @@ +use cases contains the detailed DOM interactions to perform a given use case, may also do some assertions. +use cases are often used in multiple scenarios. From 5d06c65ce5d28d64a1efd2dd4e746cbf2c999e29 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 13 Sep 2018 12:02:49 +0200 Subject: [PATCH 5/8] split up ll tests in several functions --- src/scenarios/lazy-loading.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/scenarios/lazy-loading.js b/src/scenarios/lazy-loading.js index f7360622a1..0adb3fc793 100644 --- a/src/scenarios/lazy-loading.js +++ b/src/scenarios/lazy-loading.js @@ -29,10 +29,16 @@ const {enableLazyLoading} = require('../usecases/settings'); module.exports = async function lazyLoadingScenarios(alice, bob, charlies) { console.log(" creating a room for lazy loading member scenarios:"); await enableLazyLoading(alice); - const room = "Lazy Loading Test"; - const alias = "#lltest:localhost"; - const charlyMsg1 = "hi bob!"; - const charlyMsg2 = "how's it going??"; + await setupRoomWithBobAliceAndCharlies(alice, bob, charlies); + await checkPaginatedDisplayNames(alice, charlies); +} + +const room = "Lazy Loading Test"; +const alias = "#lltest:localhost"; +const charlyMsg1 = "hi bob!"; +const charlyMsg2 = "how's it going??"; + +async function setupRoomWithBobAliceAndCharlies(alice, bob, charlies) { await createRoom(bob, room); await changeRoomSettings(bob, {directory: true, visibility: "public_no_guests", alias}); // wait for alias to be set by server after clicking "save" @@ -47,6 +53,9 @@ module.exports = async function lazyLoadingScenarios(alice, bob, charlies) { } bob.log.unmute().done(); await join(alice, alias); +} + +async function checkPaginatedDisplayNames(alice, charlies) { await scrollToTimelineTop(alice); //alice should see 2 messages from every charly with //the correct display name From 239e6a4bcef1a92aa69d48923b8f46e072d8c00c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 13 Sep 2018 12:03:29 +0200 Subject: [PATCH 6/8] add ll tests to check if all expected members are in memberlist also move verify-device use case to timeline to reuse memberlist query for this test. --- src/scenarios/e2e-encryption.js | 2 +- src/scenarios/lazy-loading.js | 16 ++++++++++++ .../{verify-device.js => memberlist.js} | 26 ++++++++++++------- 3 files changed, 33 insertions(+), 11 deletions(-) rename src/usecases/{verify-device.js => memberlist.js} (68%) diff --git a/src/scenarios/e2e-encryption.js b/src/scenarios/e2e-encryption.js index 938dc5e592..51d8a70236 100644 --- a/src/scenarios/e2e-encryption.js +++ b/src/scenarios/e2e-encryption.js @@ -25,7 +25,7 @@ const {receiveMessage} = require('../usecases/timeline'); const createRoom = require('../usecases/create-room'); const changeRoomSettings = require('../usecases/room-settings'); const {getE2EDeviceFromSettings} = require('../usecases/settings'); -const verifyDeviceForUser = require('../usecases/verify-device'); +const {verifyDeviceForUser} = require('../usecases/memberlist'); module.exports = async function e2eEncryptionScenarios(alice, bob) { console.log(" creating an e2e encrypted room and join through invite:"); diff --git a/src/scenarios/lazy-loading.js b/src/scenarios/lazy-loading.js index 0adb3fc793..bd7c1d1507 100644 --- a/src/scenarios/lazy-loading.js +++ b/src/scenarios/lazy-loading.js @@ -23,14 +23,17 @@ const { scrollToTimelineTop } = require('../usecases/timeline'); const createRoom = require('../usecases/create-room'); +const {getMembersInMemberlist} = require('../usecases/memberlist'); const changeRoomSettings = require('../usecases/room-settings'); const {enableLazyLoading} = require('../usecases/settings'); +const assert = require('assert'); module.exports = async function lazyLoadingScenarios(alice, bob, charlies) { console.log(" creating a room for lazy loading member scenarios:"); await enableLazyLoading(alice); await setupRoomWithBobAliceAndCharlies(alice, bob, charlies); await checkPaginatedDisplayNames(alice, charlies); + await checkMemberList(alice, charlies); } const room = "Lazy Loading Test"; @@ -69,3 +72,16 @@ async function checkPaginatedDisplayNames(alice, charlies) { }, []); await checkTimelineContains(alice, expectedMessages, "Charly #1-10"); } + +async function checkMemberList(alice, charlies) { + alice.log.step("checks the memberlist contains herself, bob and all charlies"); + const displayNames = (await getMembersInMemberlist(alice)).map((m) => m.displayName); + assert(displayNames.includes("alice")); + assert(displayNames.includes("bob")); + charlies.sessions.forEach((charly) => { + assert(displayNames.includes(charly.displayName()), + `${charly.displayName()} should be in the member list, ` + + `only have ${displayNames}`); + }); + alice.log.done(); +} diff --git a/src/usecases/verify-device.js b/src/usecases/memberlist.js similarity index 68% rename from src/usecases/verify-device.js rename to src/usecases/memberlist.js index 7b01e7c756..b018ed552c 100644 --- a/src/usecases/verify-device.js +++ b/src/usecases/memberlist.js @@ -16,16 +16,13 @@ limitations under the License. const assert = require('assert'); -module.exports = async function verifyDeviceForUser(session, name, expectedDevice) { +module.exports.verifyDeviceForUser = async function(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 membersAndNames = await getMembersInMemberlist(session); + const matchingLabel = membersAndNames.filter((m) => { + return m.displayName === name; + }).map((m) => m.label)[0]; + await matchingLabel.click(); const firstVerifyButton = await session.waitAndQuery(".mx_MemberDeviceInfo_verify"); await firstVerifyButton.click(); const dialogCodeFields = await session.waitAndQueryAll(".mx_QuestionDialog code"); @@ -39,4 +36,13 @@ module.exports = async function verifyDeviceForUser(session, name, expectedDevic const closeMemberInfo = await session.query(".mx_MemberInfo_cancel"); await closeMemberInfo.click(); session.log.done(); -} \ No newline at end of file +} + +async function getMembersInMemberlist(session) { + const memberNameElements = await session.waitAndQueryAll(".mx_MemberList .mx_EntityTile_name"); + return Promise.all(memberNameElements.map(async (el) => { + return {label: el, displayName: await session.innerText(el)}; + })); +} + +module.exports.getMembersInMemberlist = getMembersInMemberlist; From 9f4cf776c5de89a84714182b532ac75b7fd159b6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 13 Sep 2018 12:04:18 +0200 Subject: [PATCH 7/8] make receiveMessage more robust by checking first if the message is not already in the timeline --- src/usecases/timeline.js | 49 ++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/usecases/timeline.js b/src/usecases/timeline.js index beaaec5e5a..32c468048c 100644 --- a/src/usecases/timeline.js +++ b/src/usecases/timeline.js @@ -46,23 +46,38 @@ module.exports.receiveMessage = async function(session, expectedMessage) { session.log.step(`receives message "${expectedMessage.body}" from ${expectedMessage.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 (expectedMessage.encrypted) { - return body.indexOf(expectedMessage.sender) !== -1 && - body.indexOf("m.room.encrypted") !== -1; - } else { - return body.indexOf(expectedMessage.body) !== -1; - } - }); - // wait a bit for the incoming event to be rendered - await session.delay(1000); - const lastTile = await getLastEventTile(session); - const foundMessage = await getMessageFromEventTile(lastTile); - assertMessage(foundMessage, expectedMessage); + + async function assertLastMessage() { + const lastTile = await getLastEventTile(session); + const lastMessage = await getMessageFromEventTile(lastTile); + await assertMessage(lastMessage, expectedMessage); + } + + // first try to see if the message is already the last message in the timeline + let isExpectedMessage = false; + try { + assertLastMessage(); + isExpectedMessage = true; + } catch(ex) {} + + if (!isExpectedMessage) { + await session.page.waitForResponse(async (response) => { + if (response.request().url().indexOf("/sync") === -1) { + return false; + } + const body = await response.text(); + if (expectedMessage.encrypted) { + return body.indexOf(expectedMessage.sender) !== -1 && + body.indexOf("m.room.encrypted") !== -1; + } else { + return body.indexOf(expectedMessage.body) !== -1; + } + }); + // wait a bit for the incoming event to be rendered + await session.delay(1000); + await assertLastMessage(); + } + session.log.done(); } From af255c63866603271c03e4dc80b032fd9f702aae Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 14 Sep 2018 09:52:34 +0200 Subject: [PATCH 8/8] dont assert the first time in receiveMessage, as it will show an ugly assert error while everything is fine, just need to wait longer --- src/usecases/timeline.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/usecases/timeline.js b/src/usecases/timeline.js index 32c468048c..466d7fb222 100644 --- a/src/usecases/timeline.js +++ b/src/usecases/timeline.js @@ -47,20 +47,23 @@ module.exports.receiveMessage = async function(session, expectedMessage) { // wait for a response to come in that contains the message // crude, but effective - async function assertLastMessage() { + async function getLastMessage() { const lastTile = await getLastEventTile(session); - const lastMessage = await getMessageFromEventTile(lastTile); - await assertMessage(lastMessage, expectedMessage); + return getMessageFromEventTile(lastTile); } - // first try to see if the message is already the last message in the timeline + let lastMessage = null; let isExpectedMessage = false; try { - assertLastMessage(); - isExpectedMessage = true; + lastMessage = await getLastMessage(); + isExpectedMessage = lastMessage && + lastMessage.body === expectedMessage.body && + lastMessage.sender === expectedMessage.sender; } catch(ex) {} - - if (!isExpectedMessage) { + // first try to see if the message is already the last message in the timeline + if (isExpectedMessage) { + assertMessage(lastMessage, expectedMessage); + } else { await session.page.waitForResponse(async (response) => { if (response.request().url().indexOf("/sync") === -1) { return false; @@ -75,7 +78,8 @@ module.exports.receiveMessage = async function(session, expectedMessage) { }); // wait a bit for the incoming event to be rendered await session.delay(1000); - await assertLastMessage(); + lastMessage = await getLastMessage(); + assertMessage(lastMessage, expectedMessage); } session.log.done();