Merge pull request #1677 from Bilb/fix-late-app-fetch
add some logs to opengroup retry and building of onion pathspull/1682/head
commit
27ceafcb55
@ -1,76 +0,0 @@
|
||||
/* eslint-disable func-names */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
// tslint:disable: await-promise
|
||||
// tslint:disable: no-implicit-dependencies
|
||||
// tslint:disable: no-invalid-this
|
||||
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import { Common } from './common';
|
||||
import { Application } from 'spectron';
|
||||
|
||||
import ConversationPage from './page-objects/conversation.page';
|
||||
|
||||
describe('Add contact', function() {
|
||||
this.timeout(60000);
|
||||
this.slow(20000);
|
||||
let app: Application;
|
||||
let app2: Application;
|
||||
|
||||
beforeEach(async () => {
|
||||
await Common.killallElectron();
|
||||
await Common.stopStubSnodeServer();
|
||||
|
||||
const app1Props = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_1,
|
||||
displayName: Common.TEST_DISPLAY_NAME1,
|
||||
};
|
||||
|
||||
const app2Props = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_2,
|
||||
displayName: Common.TEST_DISPLAY_NAME2,
|
||||
};
|
||||
|
||||
[app, app2] = await Promise.all([
|
||||
Common.startAndStub(app1Props),
|
||||
Common.startAndStubN(app2Props, 2),
|
||||
]);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await Common.stopApp(app);
|
||||
await Common.killallElectron();
|
||||
await Common.stopStubSnodeServer();
|
||||
});
|
||||
|
||||
it('addContacts: can add a contact by sessionID', async () => {
|
||||
const textMessage = Common.generateSendMessageText();
|
||||
|
||||
await app.client.element(ConversationPage.contactsButtonSection).click();
|
||||
await app.client.element(ConversationPage.addContactButton).click();
|
||||
await app.client.isExisting(ConversationPage.leftPaneOverlay).should.eventually.be.true;
|
||||
|
||||
await Common.setValueWrapper(app, ConversationPage.sessionIDInput, Common.TEST_PUBKEY2);
|
||||
await app.client
|
||||
.element(ConversationPage.sessionIDInput)
|
||||
.getValue()
|
||||
.should.eventually.equal(Common.TEST_PUBKEY2);
|
||||
|
||||
await app.client.element(ConversationPage.nextButton).click();
|
||||
await app.client.waitForExist(ConversationPage.sendMessageTextarea, 1000);
|
||||
|
||||
// send a text message to that user
|
||||
await app.client.element(ConversationPage.sendMessageTextarea).setValue(textMessage);
|
||||
await app.client.keys('Enter');
|
||||
await app.client.waitForExist(ConversationPage.existingSendMessageText(textMessage), 1000);
|
||||
|
||||
// assure session request message has been sent
|
||||
await Common.timeout(3000);
|
||||
await app.client.isExisting(ConversationPage.retrySendButton).should.eventually.be.false;
|
||||
|
||||
await app2.client.waitForExist(ConversationPage.conversationItem, 5000);
|
||||
|
||||
await app2.client.element(ConversationPage.conversationItem).click();
|
||||
|
||||
await app2.client.waitForExist(ConversationPage.existingReceivedMessageText(textMessage), 1000);
|
||||
});
|
||||
});
|
@ -1,52 +0,0 @@
|
||||
/* eslint-disable func-names */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
// tslint:disable: await-promise
|
||||
// tslint:disable: no-implicit-dependencies
|
||||
// tslint:disable: no-invalid-this
|
||||
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import { Common } from './common';
|
||||
import { Application } from 'spectron';
|
||||
|
||||
import ConversationPage from './page-objects/conversation.page';
|
||||
|
||||
describe('Closed groups', function() {
|
||||
this.timeout(60000);
|
||||
this.slow(20000);
|
||||
let app: Application;
|
||||
let app2: Application;
|
||||
|
||||
beforeEach(async () => {
|
||||
await Common.killallElectron();
|
||||
await Common.stopStubSnodeServer();
|
||||
|
||||
[app, app2] = await Common.startAppsAsFriends();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await Common.stopApp(app);
|
||||
await Common.killallElectron();
|
||||
await Common.stopStubSnodeServer();
|
||||
});
|
||||
|
||||
it('closedGroup: can create a closed group with a friend and send/receive a message', async () => {
|
||||
// create group and add new friend
|
||||
await Common.addFriendToNewClosedGroup([app, app2]);
|
||||
|
||||
// send a message from app and validate it is received on app2
|
||||
const textMessage = Common.generateSendMessageText();
|
||||
await app.client.element(ConversationPage.sendMessageTextarea).setValue(textMessage);
|
||||
await app.client
|
||||
.element(ConversationPage.sendMessageTextarea)
|
||||
.getValue()
|
||||
.should.eventually.equal(textMessage);
|
||||
// send the message
|
||||
await app.client.keys('Enter');
|
||||
|
||||
// validate that the message has been added to the message list view
|
||||
await app.client.waitForExist(ConversationPage.existingSendMessageText(textMessage), 2000);
|
||||
|
||||
// validate that the message has been added to the message list view
|
||||
await app2.client.waitForExist(ConversationPage.existingReceivedMessageText(textMessage), 5000);
|
||||
});
|
||||
});
|
@ -1,570 +0,0 @@
|
||||
// tslint:disable: no-implicit-dependencies
|
||||
|
||||
import { Application } from 'spectron';
|
||||
import path from 'path';
|
||||
import url from 'url';
|
||||
import http from 'http';
|
||||
import fse from 'fs-extra';
|
||||
import { exec } from 'child_process';
|
||||
|
||||
import chai from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
import CommonPage from './page-objects/common.page';
|
||||
import RegistrationPage from './page-objects/registration.page';
|
||||
import ConversationPage from './page-objects/conversation.page';
|
||||
import SettingsPage from './page-objects/settings.page';
|
||||
|
||||
chai.should();
|
||||
chai.use(chaiAsPromised as any);
|
||||
chai.config.includeStack = true;
|
||||
|
||||
// From https://github.com/chaijs/chai/issues/200
|
||||
chai.use((_chai, _) => {
|
||||
_chai.Assertion.addMethod('withMessage', (msg: string) => {
|
||||
_.flag(Common, 'message', msg);
|
||||
});
|
||||
});
|
||||
|
||||
const STUB_SNODE_SERVER_PORT = 3000;
|
||||
const ENABLE_LOG = false;
|
||||
|
||||
// tslint:disable-next-line: no-unnecessary-class
|
||||
export class Common {
|
||||
/* ************** USERS ****************** */
|
||||
public static readonly TEST_RECOVERY_PHRASE_1 =
|
||||
'faxed mechanic mocked agony unrest loincloth pencil eccentric boyfriend oasis speedy ribbon faxed';
|
||||
public static readonly TEST_PUBKEY1 =
|
||||
'0552b85a43fb992f6bdb122a5a379505a0b99a16f0628ab8840249e2a60e12a413';
|
||||
public static readonly TEST_DISPLAY_NAME1 = 'tester_Alice';
|
||||
|
||||
public static readonly TEST_RECOVERY_PHRASE_2 =
|
||||
'guide inbound jerseys bays nouns basin sulking awkward stockpile ostrich ascend pylons ascend';
|
||||
public static readonly TEST_PUBKEY2 =
|
||||
'054e1ca8681082dbd9aad1cf6fc89a32254e15cba50c75b5a73ac10a0b96bcbd2a';
|
||||
public static readonly TEST_DISPLAY_NAME2 = 'tester_Bob';
|
||||
|
||||
public static readonly TEST_RECOVERY_PHRASE_3 =
|
||||
'alpine lukewarm oncoming blender kiwi fuel lobster upkeep vogue simplest gasp fully simplest';
|
||||
public static readonly TEST_PUBKEY3 =
|
||||
'05f8662b6e83da5a31007cc3ded44c601f191e07999acb6db2314a896048d9036c';
|
||||
public static readonly TEST_DISPLAY_NAME3 = 'tester_Charlie';
|
||||
|
||||
/* ************** OPEN GROUPS ****************** */
|
||||
public static readonly VALID_GROUP_URL = 'https://chat.getsession.org';
|
||||
public static readonly VALID_GROUP_URL2 = 'https://chat-dev.lokinet.org';
|
||||
public static readonly VALID_GROUP_NAME = 'Session Public Chat';
|
||||
public static readonly VALID_GROUP_NAME2 = 'Loki Dev Chat';
|
||||
|
||||
/* ************** CLOSED GROUPS ****************** */
|
||||
public static readonly VALID_CLOSED_GROUP_NAME1 = 'Closed Group 1';
|
||||
|
||||
public static USER_DATA_ROOT_FOLDER = '';
|
||||
private static stubSnode: any;
|
||||
private static messages: any;
|
||||
private static fileServer: any;
|
||||
|
||||
// tslint:disable: await-promise
|
||||
// tslint:disable: no-console
|
||||
|
||||
public static async timeout(ms: number) {
|
||||
// tslint:disable-next-line: no-string-based-set-timeout
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
public static async closeToast(app: Application) {
|
||||
await app.client.element(CommonPage.toastCloseButton).click();
|
||||
}
|
||||
|
||||
// a wrapper to work around electron/spectron bug
|
||||
public static async setValueWrapper(app: Application, selector: any, value: string) {
|
||||
// keys, setValue and addValue hang on certain platforms
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
await app.client.execute(
|
||||
(slctr, val) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
const iter = document.evaluate(
|
||||
slctr,
|
||||
document,
|
||||
null,
|
||||
XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
|
||||
null
|
||||
);
|
||||
const elem = iter.iterateNext() as any;
|
||||
if (elem) {
|
||||
elem.value = val;
|
||||
} else {
|
||||
console.error('Cant find', slctr, elem, iter);
|
||||
}
|
||||
},
|
||||
selector,
|
||||
value
|
||||
);
|
||||
// let session js detect the text change
|
||||
await app.client.element(selector).click();
|
||||
} else {
|
||||
// Linux & Windows don't require wrapper
|
||||
await app.client.element(selector).setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static async startApp(environment = 'test-integration-session') {
|
||||
const env = environment.startsWith('test-integration') ? 'test-integration' : environment;
|
||||
const instance = environment.replace('test-integration-', '');
|
||||
|
||||
const app1 = new Application({
|
||||
path: path.join(__dirname, '..', '..', '..', '..', 'node_modules', '.bin', 'electron'),
|
||||
args: ['.'],
|
||||
env: {
|
||||
NODE_ENV: env,
|
||||
NODE_APP_INSTANCE: instance,
|
||||
USE_STUBBED_NETWORK: true,
|
||||
ELECTRON_ENABLE_LOGGING: true,
|
||||
ELECTRON_ENABLE_STACK_DUMPING: true,
|
||||
ELECTRON_DISABLE_SANDBOX: 1,
|
||||
},
|
||||
requireName: 'electronRequire',
|
||||
// chromeDriverLogPath: '../chromedriverlog.txt',
|
||||
chromeDriverArgs: [
|
||||
`remote-debugging-port=${Math.floor(
|
||||
// tslint:disable-next-line: insecure-random
|
||||
Math.random() * (9999 - 9000) + 9000
|
||||
)}`,
|
||||
],
|
||||
});
|
||||
await app1.start();
|
||||
await app1.client.waitUntilWindowLoaded();
|
||||
|
||||
return app1;
|
||||
}
|
||||
|
||||
public static async startApp2() {
|
||||
const app2 = await Common.startApp('test-integration-session-2');
|
||||
return app2;
|
||||
}
|
||||
|
||||
public static async stopApp(app1: Application) {
|
||||
if (app1 && app1.isRunning()) {
|
||||
await app1.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static async killallElectron() {
|
||||
// rtharp - my 2nd client on MacOs needs: pkill -f "node_modules/.bin/electron"
|
||||
// node_modules/electron/dist/electron is node_modules/electron/dist/Electron.app on MacOS
|
||||
const killStr =
|
||||
process.platform === 'win32'
|
||||
? 'taskkill /im electron.exe /t /f'
|
||||
: 'pkill -f "node_modules/electron/dist/electron" | pkill -f "node_modules/.bin/electron"';
|
||||
return new Promise(resolve => {
|
||||
exec(killStr, (_err, stdout, stderr) => {
|
||||
resolve({ stdout, stderr });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static async rmFolder(folder: string) {
|
||||
await fse.remove(folder);
|
||||
}
|
||||
|
||||
public static async startAndAssureCleanedApp2() {
|
||||
const app2 = await Common.startAndAssureCleanedApp('test-integration-session-2');
|
||||
return app2;
|
||||
}
|
||||
|
||||
public static async startAndAssureCleanedApp(env = 'test-integration-session') {
|
||||
const userData = path.join(Common.USER_DATA_ROOT_FOLDER, `Session-${env}`);
|
||||
|
||||
await Common.rmFolder(userData);
|
||||
|
||||
const app1 = await Common.startApp(env);
|
||||
await app1.client.waitForExist(RegistrationPage.registrationTabSignIn, 4000);
|
||||
|
||||
return app1;
|
||||
}
|
||||
|
||||
public static async startAndStub({
|
||||
recoveryPhrase,
|
||||
displayName,
|
||||
env = 'test-integration-session',
|
||||
}: {
|
||||
recoveryPhrase: string;
|
||||
displayName: string;
|
||||
env?: string;
|
||||
}) {
|
||||
const app = await Common.startAndAssureCleanedApp(env);
|
||||
Common.startStubSnodeServer();
|
||||
|
||||
if (recoveryPhrase && displayName) {
|
||||
await Common.restoreFromRecoveryPhrase(app, recoveryPhrase, displayName);
|
||||
// not sure we need Common - rtharp.
|
||||
await Common.timeout(2000);
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
public static async startAndStubN(props: any, n: number) {
|
||||
// Make app with stub as number n
|
||||
const appN = await Common.startAndStub({
|
||||
env: `test-integration-session-${n}`,
|
||||
...props,
|
||||
});
|
||||
|
||||
return appN;
|
||||
}
|
||||
|
||||
public static async restoreFromRecoveryPhrase(
|
||||
app: Application,
|
||||
recoveryPhrase: string,
|
||||
displayName: string
|
||||
) {
|
||||
await app.client.element(RegistrationPage.registrationTabSignIn).click();
|
||||
await app.client.element(RegistrationPage.restoreFromSeedMode).click();
|
||||
await Common.setValueWrapper(app, RegistrationPage.recoveryPhraseInput, recoveryPhrase);
|
||||
|
||||
await Common.setValueWrapper(app, RegistrationPage.displayNameInput, displayName);
|
||||
|
||||
// await app.client.element(RegistrationPage.continueSessionButton).click();
|
||||
await app.client.keys('Enter');
|
||||
|
||||
await app.client.waitForExist(RegistrationPage.conversationListContainer, 4000);
|
||||
}
|
||||
|
||||
public static async makeFriends(app1: Application, client2: [Application, string]) {
|
||||
const [_, pubkey2] = client2;
|
||||
|
||||
/** add each other as friends */
|
||||
const textMessage = Common.generateSendMessageText();
|
||||
await app1.client.element(ConversationPage.contactsButtonSection).click();
|
||||
await app1.client.element(ConversationPage.addContactButton).click();
|
||||
|
||||
await Common.setValueWrapper(app1, ConversationPage.sessionIDInput, pubkey2);
|
||||
await app1.client.element(ConversationPage.nextButton).click();
|
||||
await app1.client.waitForExist(ConversationPage.sendMessageTextareaAndMessage, 1000);
|
||||
|
||||
// send a text message to that user (will be a friend request)
|
||||
await Common.setValueWrapper(app1, ConversationPage.sendMessageTextareaAndMessage, textMessage);
|
||||
await app1.client.keys('Enter');
|
||||
await app1.client.waitForExist(ConversationPage.existingSendMessageText(textMessage), 1000);
|
||||
}
|
||||
|
||||
public static async startAppsAsFriends() {
|
||||
const app1Props = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_1,
|
||||
displayName: Common.TEST_DISPLAY_NAME1,
|
||||
stubSnode: true,
|
||||
};
|
||||
|
||||
const app2Props = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_2,
|
||||
displayName: Common.TEST_DISPLAY_NAME2,
|
||||
stubSnode: true,
|
||||
};
|
||||
|
||||
const [app1, app2] = await Promise.all([
|
||||
Common.startAndStub(app1Props),
|
||||
Common.startAndStubN(app2Props, 2),
|
||||
]);
|
||||
|
||||
await Common.makeFriends(app1, [app2, Common.TEST_PUBKEY2]);
|
||||
|
||||
return [app1, app2];
|
||||
}
|
||||
|
||||
public static async addFriendToNewClosedGroup(members: Array<Application>) {
|
||||
const [app, ...others] = members;
|
||||
|
||||
await app.client.element(ConversationPage.conversationButtonSection).click();
|
||||
await app.client.element(ConversationPage.createClosedGroupButton).click();
|
||||
|
||||
await Common.setValueWrapper(
|
||||
app,
|
||||
ConversationPage.closedGroupNameTextarea,
|
||||
Common.VALID_CLOSED_GROUP_NAME1
|
||||
);
|
||||
|
||||
await app.client
|
||||
.element(ConversationPage.closedGroupNameTextarea)
|
||||
.getValue()
|
||||
.should.eventually.equal(Common.VALID_CLOSED_GROUP_NAME1);
|
||||
|
||||
// Common assumes that app does not have any other friends
|
||||
|
||||
for (let i = 0; i < others.length; i += 1) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await app.client.element(ConversationPage.createClosedGroupMemberItem(i)).isVisible().should
|
||||
.eventually.be.true;
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await app.client.element(ConversationPage.createClosedGroupMemberItem(i)).click();
|
||||
}
|
||||
|
||||
await app.client.element(ConversationPage.createClosedGroupMemberItemSelected).isVisible()
|
||||
.should.eventually.be.true;
|
||||
|
||||
// trigger the creation of the group
|
||||
await app.client.element(ConversationPage.validateCreationClosedGroupButton).click();
|
||||
|
||||
await app.client.waitForExist(ConversationPage.sessionToastGroupCreatedSuccess, 1000);
|
||||
await app.client.isExisting(
|
||||
ConversationPage.headerTitleGroupName(Common.VALID_CLOSED_GROUP_NAME1)
|
||||
).should.eventually.be.true;
|
||||
await app.client.element(ConversationPage.headerTitleMembers(members.length)).isVisible().should
|
||||
.eventually.be.true;
|
||||
|
||||
// validate overlay is closed
|
||||
await app.client.isExisting(ConversationPage.leftPaneOverlay).should.eventually.be.equal(false);
|
||||
|
||||
// move back to the conversation section
|
||||
await app.client.element(ConversationPage.conversationButtonSection).click();
|
||||
|
||||
// validate open chat has been added
|
||||
await app.client.isExisting(
|
||||
ConversationPage.rowOpenGroupConversationName(Common.VALID_CLOSED_GROUP_NAME1)
|
||||
).should.eventually.be.true;
|
||||
|
||||
await Promise.all(
|
||||
others.map(async otherApp => {
|
||||
// next check that other members have been invited and have the group in their conversations
|
||||
await otherApp.client.waitForExist(
|
||||
ConversationPage.rowOpenGroupConversationName(Common.VALID_CLOSED_GROUP_NAME1),
|
||||
6000
|
||||
);
|
||||
// open the closed group conversation on otherApp
|
||||
await otherApp.client.element(ConversationPage.conversationButtonSection).click();
|
||||
await Common.timeout(500);
|
||||
await otherApp.client
|
||||
.element(ConversationPage.rowOpenGroupConversationName(Common.VALID_CLOSED_GROUP_NAME1))
|
||||
.click();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public static async linkApp2ToApp(app1: Application, app2: Application, app1Pubkey: string) {
|
||||
// app needs to be logged in as user1 and app2 needs to be logged out
|
||||
// start the pairing dialog for the first app
|
||||
await app1.client.element(SettingsPage.settingsButtonSection).click();
|
||||
|
||||
await app1.client.isVisible(ConversationPage.noPairedDeviceMessage);
|
||||
// we should not find the linkDeviceButtonDisabled button (as DISABLED)
|
||||
await app1.client.isExisting(ConversationPage.linkDeviceButtonDisabled).should.eventually.be
|
||||
.false;
|
||||
await app1.client.element(ConversationPage.linkDeviceButton).click();
|
||||
|
||||
// validate device pairing dialog is shown and has a qrcode
|
||||
await app1.client.isVisible(ConversationPage.qrImageDiv);
|
||||
|
||||
// next trigger the link request from the app2 with the app1 pubkey
|
||||
await app2.client.element(RegistrationPage.registrationTabSignIn).click();
|
||||
await app2.client.element(RegistrationPage.linkDeviceMode).click();
|
||||
|
||||
await Common.setValueWrapper(app2, RegistrationPage.textareaLinkDevicePubkey, app1Pubkey);
|
||||
await app2.client.element(RegistrationPage.linkDeviceTriggerButton).click();
|
||||
await app1.client.waitForExist(SettingsPage.secretWordsTextInDialog, 7000);
|
||||
const secretWordsapp1 = await app1.client
|
||||
.element(SettingsPage.secretWordsTextInDialog)
|
||||
.getText();
|
||||
await app1.client.waitForExist(RegistrationPage.linkWithThisDevice, 10000);
|
||||
|
||||
await app2.client
|
||||
.element(RegistrationPage.secretWordsText)
|
||||
.getText()
|
||||
.should.eventually.be.equal(secretWordsapp1);
|
||||
|
||||
await app1.client.element(ConversationPage.allowPairingButton).click();
|
||||
await app1.client.element(ConversationPage.okButton).click();
|
||||
// validate device paired in settings list with correct secrets
|
||||
await app1.client.waitForExist(ConversationPage.devicePairedDescription(secretWordsapp1), 2000);
|
||||
|
||||
await app1.client.isExisting(ConversationPage.unpairDeviceButton).should.eventually.be.true;
|
||||
await app1.client.isExisting(ConversationPage.linkDeviceButtonDisabled).should.eventually.be
|
||||
.true;
|
||||
|
||||
// validate app2 (secondary device) is linked successfully
|
||||
await app2.client.waitForExist(RegistrationPage.conversationListContainer, 4000);
|
||||
|
||||
// validate primary pubkey of app2 is the same that in app1
|
||||
await app2.webContents
|
||||
.executeJavaScript("window.storage.get('primaryDevicePubKey')")
|
||||
.should.eventually.be.equal(app1Pubkey);
|
||||
}
|
||||
|
||||
public static async triggerUnlinkApp2FromApp(app1: Application, app2: Application) {
|
||||
// check app2 is loggedin
|
||||
await app2.client.isExisting(RegistrationPage.conversationListContainer).should.eventually.be
|
||||
.true;
|
||||
|
||||
await app1.client.element(SettingsPage.settingsButtonSection).click();
|
||||
await app1.client.isExisting(ConversationPage.linkDeviceButtonDisabled).should.eventually.be
|
||||
.true;
|
||||
// click the unlink button
|
||||
await app1.client.element(ConversationPage.unpairDeviceButton).click();
|
||||
await app1.client.element(ConversationPage.validateUnpairDevice).click();
|
||||
|
||||
await app1.client.waitForExist(ConversationPage.noPairedDeviceMessage, 5000);
|
||||
await app1.client.element(ConversationPage.linkDeviceButton).isEnabled().should.eventually.be
|
||||
.true;
|
||||
|
||||
// let time to app2 to catch the event and restart dropping its data
|
||||
await Common.timeout(5000);
|
||||
|
||||
// check that the app restarted
|
||||
// (did not find a better way than checking the app no longer being accessible)
|
||||
let isApp2Joinable = true;
|
||||
try {
|
||||
await app2.client.isExisting(RegistrationPage.registrationTabSignIn).should.eventually.be
|
||||
.true;
|
||||
} catch (err) {
|
||||
// if we get an error here, it means Spectron is lost.
|
||||
// Common is a good thing because it means app2 restarted
|
||||
isApp2Joinable = false;
|
||||
}
|
||||
|
||||
if (isApp2Joinable) {
|
||||
throw new Error(
|
||||
'app2 is still joinable so it did not restart, so it did not unlink correctly'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static async sendMessage(app: Application, messageText: string, fileLocation?: string) {
|
||||
await Common.setValueWrapper(app, ConversationPage.sendMessageTextarea, messageText);
|
||||
await app.client
|
||||
.element(ConversationPage.sendMessageTextarea)
|
||||
.getValue()
|
||||
.should.eventually.equal(messageText);
|
||||
|
||||
// attach a file
|
||||
if (fileLocation) {
|
||||
await Common.setValueWrapper(app, ConversationPage.attachmentInput, fileLocation);
|
||||
}
|
||||
|
||||
// send message
|
||||
await app.client.element(ConversationPage.sendMessageTextarea).click();
|
||||
await app.client.keys('Enter');
|
||||
}
|
||||
|
||||
public static generateSendMessageText(): string {
|
||||
return `Test message from integration tests ${Date.now()}`;
|
||||
}
|
||||
|
||||
public static startStubSnodeServer() {
|
||||
if (!Common.stubSnode) {
|
||||
Common.messages = {};
|
||||
Common.stubSnode = http.createServer((request: any, response: any) => {
|
||||
const { query } = url.parse(request.url, true);
|
||||
const { pubkey, data, timestamp } = query;
|
||||
|
||||
if (!pubkey) {
|
||||
console.warn('NO PUBKEY');
|
||||
response.writeHead(400, { 'Content-Type': 'text/html' });
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(pubkey)) {
|
||||
console.error('pubkey cannot be an array');
|
||||
response.writeHead(400, { 'Content-Type': 'text/html' });
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
console.error('data cannot be an array');
|
||||
response.writeHead(400, { 'Content-Type': 'text/html' });
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.method === 'POST') {
|
||||
if (ENABLE_LOG) {
|
||||
console.warn('POST', pubkey.substr(2, 3), data.substr(4, 10), timestamp);
|
||||
}
|
||||
|
||||
let ori = Common.messages[pubkey];
|
||||
|
||||
if (!Common.messages[pubkey]) {
|
||||
ori = [];
|
||||
}
|
||||
|
||||
Common.messages[pubkey] = [...ori, { data, timestamp }];
|
||||
|
||||
response.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
response.end();
|
||||
} else {
|
||||
const retrievedMessages = { messages: Common.messages[pubkey] || [] };
|
||||
|
||||
if (ENABLE_LOG) {
|
||||
const messages = retrievedMessages.messages.map((m: any) => m.data.substr(4, 10));
|
||||
console.warn('GET', pubkey.substr(2, 3), messages);
|
||||
}
|
||||
response.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
response.write(JSON.stringify(retrievedMessages));
|
||||
response.end();
|
||||
}
|
||||
});
|
||||
Common.startLocalFileServer();
|
||||
Common.stubSnode.listen(STUB_SNODE_SERVER_PORT);
|
||||
} else {
|
||||
Common.messages = {};
|
||||
}
|
||||
}
|
||||
|
||||
public static startLocalFileServer() {
|
||||
if (!Common.fileServer) {
|
||||
// be sure to run `git submodule update --init && cd session-file-server && yarn install; cd -`
|
||||
// eslint-disable-next-line global-require
|
||||
// tslint:disable-next-line: no-require-imports
|
||||
Common.fileServer = require('../../../../session-file-server/app');
|
||||
}
|
||||
}
|
||||
|
||||
public static async joinOpenGroup(app: Application, openGroupUrl: string, name: string) {
|
||||
await app.client.element(ConversationPage.conversationButtonSection).click();
|
||||
await app.client.element(ConversationPage.joinOpenGroupButton).click();
|
||||
|
||||
await Common.setValueWrapper(app, ConversationPage.openGroupInputUrl, openGroupUrl);
|
||||
await app.client
|
||||
.element(ConversationPage.openGroupInputUrl)
|
||||
.getValue()
|
||||
.should.eventually.equal(openGroupUrl);
|
||||
await app.client.element(ConversationPage.joinOpenGroupButton).click();
|
||||
|
||||
await app.client.waitForExist(ConversationPage.sessionToastJoinOpenGroup, 2 * 1000);
|
||||
|
||||
// account for slow home internet connection delays...
|
||||
await app.client.waitForExist(ConversationPage.sessionToastJoinOpenGroupSuccess, 20 * 1000);
|
||||
|
||||
// validate overlay is closed
|
||||
await app.client.isExisting(ConversationPage.leftPaneOverlay).should.eventually.be.false;
|
||||
|
||||
// validate open chat has been added
|
||||
await app.client.waitForExist(ConversationPage.rowOpenGroupConversationName(name), 20 * 1000);
|
||||
}
|
||||
|
||||
public static async stopStubSnodeServer() {
|
||||
if (Common.stubSnode) {
|
||||
await Common.stubSnode.close();
|
||||
Common.stubSnode = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a string in logs
|
||||
* @param app the render logs to search in
|
||||
* @param str the string to search (not regex)
|
||||
* Note: getRenderProcessLogs() clears the app logs each calls.
|
||||
*/
|
||||
public static logsContains(renderLogs: Array<{ message: string }>, str: string, count?: number) {
|
||||
const foundLines = renderLogs.filter(log => log.message.includes(str));
|
||||
|
||||
// tslint:disable-next-line: no-unused-expression
|
||||
chai.expect(foundLines.length > 0, `'${str}' not found in logs but was expected`).to.be.true;
|
||||
|
||||
if (count) {
|
||||
chai
|
||||
.expect(foundLines.length, `'${str}' found but not the correct number of times`)
|
||||
.to.be.equal(count);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { Common } from './common';
|
||||
|
||||
// tslint:disable: no-import-side-effect no-invalid-this await-promise
|
||||
|
||||
import './registration_itest';
|
||||
import './open_group_itest';
|
||||
import './add_contacts_itest';
|
||||
import './link_device_itest';
|
||||
import './closed_group_itest';
|
||||
import './message_functions_itest';
|
||||
import './settings_itest';
|
||||
import './message_sync_itest';
|
||||
import './sender_keys_itest';
|
||||
|
||||
before(async function() {
|
||||
// start the app once before all tests to get the platform-dependent
|
||||
// path of user data and store it to common.USER_DATA_ROOT_FOLDER
|
||||
this.timeout(60000);
|
||||
this.slow(20000);
|
||||
const app1 = await Common.startApp();
|
||||
const ret = await app1.electron.remote.app.getPath('appData');
|
||||
Common.USER_DATA_ROOT_FOLDER = ret;
|
||||
|
||||
await Common.stopApp(app1);
|
||||
});
|
@ -1,71 +0,0 @@
|
||||
/* eslint-disable func-names */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
// tslint:disable: await-promise
|
||||
// tslint:disable: no-implicit-dependencies
|
||||
// tslint:disable: no-invalid-this
|
||||
|
||||
import path from 'path';
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import { Common } from './common';
|
||||
import { Application } from 'spectron';
|
||||
|
||||
import ConversationPage from './page-objects/conversation.page';
|
||||
|
||||
describe('Message Functions', function() {
|
||||
this.timeout(60000);
|
||||
this.slow(20000);
|
||||
let app: Application;
|
||||
let app2: Application;
|
||||
|
||||
beforeEach(async () => {
|
||||
await Common.killallElectron();
|
||||
await Common.stopStubSnodeServer();
|
||||
|
||||
[app, app2] = await Common.startAppsAsFriends();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await Common.stopApp(app);
|
||||
await Common.killallElectron();
|
||||
await Common.stopStubSnodeServer();
|
||||
});
|
||||
|
||||
it('messageFunction: can send attachment', async () => {
|
||||
// create group and add new friend
|
||||
await Common.addFriendToNewClosedGroup([app, app2]);
|
||||
|
||||
// send attachment from app1 to closed group
|
||||
const fileLocation = path.join(__dirname, 'test_attachment');
|
||||
const messageText = 'test_attachment';
|
||||
await Common.closeToast(app);
|
||||
|
||||
await Common.sendMessage(app, messageText, fileLocation);
|
||||
|
||||
// validate attachment sent
|
||||
await app.client.waitForExist(ConversationPage.existingSendMessageText(messageText), 3000);
|
||||
// validate attachment recieved
|
||||
await app2.client.waitForExist(ConversationPage.existingReceivedMessageText(messageText), 5000);
|
||||
});
|
||||
|
||||
it('messageFunction: can delete message', async () => {
|
||||
// create group and add new friend
|
||||
await Common.addFriendToNewClosedGroup([app, app2]);
|
||||
const messageText = 'delete_me';
|
||||
await Common.sendMessage(app, messageText);
|
||||
|
||||
await app.client.waitForExist(ConversationPage.existingSendMessageText(messageText), 6000);
|
||||
await app2.client.waitForExist(ConversationPage.existingReceivedMessageText(messageText), 7000);
|
||||
|
||||
// delete message in context menu
|
||||
await app.client.element(ConversationPage.messageCtxMenu(messageText)).click();
|
||||
await app.client.element(ConversationPage.deleteMessageCtxButton).click();
|
||||
|
||||
// delete message from modal
|
||||
await app.client.waitForExist(ConversationPage.deleteMessageModalButton, 5000);
|
||||
await app.client.element(ConversationPage.deleteMessageModalButton).click();
|
||||
|
||||
// verify the message is actually deleted
|
||||
await app.client.isExisting(ConversationPage.existingSendMessageText(messageText)).should
|
||||
.eventually.be.false;
|
||||
});
|
||||
});
|
@ -1,97 +0,0 @@
|
||||
/* eslint-disable func-names */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
// tslint:disable: await-promise
|
||||
// tslint:disable: no-implicit-dependencies
|
||||
// tslint:disable: no-invalid-this
|
||||
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import { Common } from './common';
|
||||
import { Application } from 'spectron';
|
||||
|
||||
import ConversationPage from './page-objects/conversation.page';
|
||||
|
||||
describe('Open groups', function() {
|
||||
this.timeout(60000);
|
||||
this.slow(20000);
|
||||
let app: Application;
|
||||
|
||||
beforeEach(async () => {
|
||||
await Common.killallElectron();
|
||||
const login = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_1,
|
||||
displayName: Common.TEST_DISPLAY_NAME1,
|
||||
};
|
||||
app = await Common.startAndStub(login);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await Common.killallElectron();
|
||||
});
|
||||
|
||||
it('openGroup: works with valid open group url', async () => {
|
||||
await Common.joinOpenGroup(app, Common.VALID_GROUP_URL, Common.VALID_GROUP_NAME);
|
||||
});
|
||||
|
||||
it('openGroup: cannot join two times the same open group', async () => {
|
||||
await Common.joinOpenGroup(app, Common.VALID_GROUP_URL2, Common.VALID_GROUP_NAME2);
|
||||
|
||||
// adding a second time the same open group
|
||||
await app.client.element(ConversationPage.conversationButtonSection).click();
|
||||
await app.client.element(ConversationPage.joinOpenGroupButton).click();
|
||||
|
||||
await Common.setValueWrapper(app, ConversationPage.openGroupInputUrl, Common.VALID_GROUP_URL2);
|
||||
await app.client.element(ConversationPage.joinOpenGroupButton).click();
|
||||
// validate session loader is not shown
|
||||
await app.client.isExisting(ConversationPage.sessionLoader).should.eventually.be.false;
|
||||
|
||||
await app.client.waitForExist(ConversationPage.sessionToastJoinOpenGroupAlreadyExist, 1 * 1000);
|
||||
|
||||
// validate overlay is still opened as connection failed
|
||||
await app.client.isExisting(ConversationPage.leftPaneOverlay).should.eventually.be.true;
|
||||
});
|
||||
|
||||
it('openGroup: can send message to open group', async () => {
|
||||
// join dev-chat group
|
||||
await app.client.element(ConversationPage.conversationButtonSection).click();
|
||||
await app.client.element(ConversationPage.joinOpenGroupButton).click();
|
||||
|
||||
await Common.setValueWrapper(app, ConversationPage.openGroupInputUrl, Common.VALID_GROUP_URL2);
|
||||
await app.client.element(ConversationPage.joinOpenGroupButton).click();
|
||||
|
||||
// wait for toast to appear
|
||||
await app.client.waitForExist(ConversationPage.sessionToastJoinOpenGroupSuccess, 30 * 1000);
|
||||
await Common.timeout(5 * 1000); // wait for toast to clear
|
||||
|
||||
await app.client.waitForExist(
|
||||
ConversationPage.rowOpenGroupConversationName(Common.VALID_GROUP_NAME2),
|
||||
10 * 1000
|
||||
);
|
||||
// generate a message containing the current timestamp so we can find it in the list of messages
|
||||
const textMessage = Common.generateSendMessageText();
|
||||
await app.client.element(ConversationPage.conversationButtonSection).click();
|
||||
|
||||
await app.client.isExisting(
|
||||
ConversationPage.rowOpenGroupConversationName(Common.VALID_GROUP_NAME2)
|
||||
);
|
||||
|
||||
await app.client
|
||||
.element(ConversationPage.rowOpenGroupConversationName(Common.VALID_GROUP_NAME2))
|
||||
.click();
|
||||
|
||||
await Common.setValueWrapper(app, ConversationPage.sendMessageTextarea, textMessage);
|
||||
await app.client
|
||||
.element(ConversationPage.sendMessageTextarea)
|
||||
.getValue()
|
||||
.should.eventually.equal(textMessage);
|
||||
// allow some time to fetch some messages
|
||||
await Common.timeout(3000);
|
||||
|
||||
// send the message
|
||||
await app.client.keys('Enter');
|
||||
await Common.timeout(5000);
|
||||
// validate that the message has been added to the message list view
|
||||
await app.client.waitForExist(ConversationPage.existingSendMessageText(textMessage), 3 * 1000);
|
||||
// we should validate that the message has been added effectively sent
|
||||
// (checking the check icon on the metadata part of the message?)
|
||||
});
|
||||
});
|
@ -1,28 +0,0 @@
|
||||
export = {
|
||||
// generics
|
||||
objWithClassAndText: (obj: string, classname: string, text: string) =>
|
||||
`//${obj}[contains(string(), "${text}")][contains(@class, "${classname}")]`,
|
||||
|
||||
divRoleButtonWithText: (text: string) =>
|
||||
`//div[contains(string(), "${text}")][contains(@role, "button")]`,
|
||||
divRoleButtonWithTextDisabled: (text: string) =>
|
||||
`//div[contains(string(), "${text}")][contains(@role, "button")][contains(@class, "disabled")]`,
|
||||
divRoleButtonDangerWithText: (text: string) =>
|
||||
`${module.exports.divRoleButtonWithText(text)}[contains(@class, "danger")]`,
|
||||
inputWithPlaceholder: (placeholder: string) =>
|
||||
`//input[contains(@placeholder, "${placeholder}")]`,
|
||||
inputWithId: (id: string) => `//input[contains(@id, '${id}')]`,
|
||||
textAreaWithPlaceholder: (placeholder: string) =>
|
||||
`//textarea[contains(@placeholder, "${placeholder}")]`,
|
||||
textAreaWithClass: (classname: string) => `//textarea[contains(@class, "${classname}")]`,
|
||||
byId: (id: string) => `//*[@id="${id}"]`,
|
||||
divWithClass: (classname: string) => `//div[contains(@class, "${classname}")]`,
|
||||
divWithClassAndText: (classname: string, text: string) =>
|
||||
module.exports.objWithClassAndText('div', classname, text),
|
||||
spanWithClassAndText: (classname: string, text: string) =>
|
||||
module.exports.objWithClassAndText('span', classname, text),
|
||||
toastWithText: (text: string) =>
|
||||
module.exports.divWithClassAndText('session-toast-wrapper', text),
|
||||
toastCloseButton:
|
||||
'//div[contains(@class, "session-toast-wrapper")]//div[contains(@class, "toast-close")]/div',
|
||||
};
|
@ -1,88 +0,0 @@
|
||||
import commonPage from './common.page';
|
||||
|
||||
export = {
|
||||
// conversation view
|
||||
sessionLoader: commonPage.divWithClass('session-loader'),
|
||||
leftPaneOverlay: commonPage.divWithClass('module-left-pane-overlay'),
|
||||
sendMessageTextarea: commonPage.textAreaWithClass('send-message'),
|
||||
sendMessageTextareaAndMessage: commonPage.textAreaWithPlaceholder('Type your message'),
|
||||
existingSendMessageText: (textMessage: string) =>
|
||||
`//*[contains(@class, "module-message__text--outgoing") and .//span[contains(@class, "text-selectable")][contains(string(), '${textMessage}')]]`,
|
||||
existingReceivedMessageText: (textMessage: string) =>
|
||||
`//*[contains(@class, "module-message__text--incoming") and .//span[contains(@class, "text-selectable")][contains(string(), '${textMessage}')]]`,
|
||||
|
||||
// conversations
|
||||
conversationButtonSection:
|
||||
'//*[contains(@class,"session-icon-button") and .//*[contains(@class, "chatBubble")]]',
|
||||
retrySendButton: commonPage.divWithClassAndText(
|
||||
'module-friend-request__buttonContainer--outgoing',
|
||||
'Retry Send'
|
||||
),
|
||||
headerTitleMembers: (num: number) =>
|
||||
commonPage.spanWithClassAndText('module-conversation-header__title-text', `${num} members`),
|
||||
|
||||
conversationItem: "//*[contains(@class, 'module-conversation-list-item')]",
|
||||
|
||||
attachmentInput: '//*[contains(@class, "choose-file")]/input[@type="file"]',
|
||||
attachmentButton: '//*[contains(@class, "choose-file")]/button',
|
||||
|
||||
messageCtxMenu: (message: string) =>
|
||||
`//div[contains(@class, 'message-wrapper')]//span[contains(string(), '${message}')]/parent::div/parent::div/parent::div/parent::div//div[contains(@class, 'module-message__buttons__menu')]`,
|
||||
|
||||
deleteMessageCtxButton:
|
||||
'//*[contains(@class, "react-contextmenu--visible")]/div[contains(string(), "Delete")]',
|
||||
deleteMessageModalButton:
|
||||
'//*[contains(@class, "session-modal")]//div[contains(string(), "Delete") and contains(@class, "session-button")]',
|
||||
|
||||
// channels
|
||||
joinOpenGroupButton: commonPage.divRoleButtonWithText('Join Open Group'),
|
||||
openGroupInputUrl: commonPage.textAreaWithPlaceholder('chat.getsession.org'),
|
||||
sessionToastJoinOpenGroup: commonPage.toastWithText('Connecting to server...'),
|
||||
sessionToastJoinOpenGroupSuccess: commonPage.toastWithText(
|
||||
'Successfully connected to open group'
|
||||
),
|
||||
sessionToastJoinOpenGroupAlreadyExist: commonPage.toastWithText(
|
||||
'You are already connected to this open group'
|
||||
),
|
||||
rowOpenGroupConversationName: (groupName: string) =>
|
||||
commonPage.spanWithClassAndText('module-conversation__user__profile-number', groupName),
|
||||
|
||||
// closed group
|
||||
createClosedGroupButton: commonPage.divRoleButtonWithText('Create Closed Group'),
|
||||
closedGroupNameTextarea: commonPage.textAreaWithPlaceholder('Enter a group name'),
|
||||
createClosedGroupMemberItem: (idx: number) =>
|
||||
commonPage.divWithClass(`session-member-item-${idx}`),
|
||||
createClosedGroupSealedSenderToggle: commonPage.divWithClass('session-toggle'),
|
||||
createClosedGroupMemberItemSelected: commonPage.divWithClass('session-member-item selected'),
|
||||
validateCreationClosedGroupButton: commonPage.divRoleButtonWithText('Create Closed Group'),
|
||||
sessionToastGroupCreatedSuccess: commonPage.toastWithText('Group created successfully'),
|
||||
headerTitleGroupName: (groupname: string) =>
|
||||
commonPage.spanWithClassAndText('module-contact-name__profile-name', groupname),
|
||||
|
||||
// contacts
|
||||
contactsButtonSection:
|
||||
'//*[contains(@class,"session-icon-button") and .//*[contains(@class, "users")]]',
|
||||
addContactButton: commonPage.divRoleButtonWithText('Add Contact'),
|
||||
sessionIDInput: commonPage.textAreaWithPlaceholder('Enter Session ID'),
|
||||
nextButton: commonPage.divRoleButtonWithText('Next'),
|
||||
|
||||
descriptionDeleteAccount: commonPage.spanWithClassAndText(
|
||||
'session-confirm-main-message',
|
||||
'Are you sure you want to delete your account?'
|
||||
),
|
||||
validateDeleteAccount: commonPage.divRoleButtonDangerWithText('OK'),
|
||||
|
||||
// device linking
|
||||
noPairedDeviceMessage:
|
||||
'//*[contains(@class, "session-settings-item__title")][contains(string(), "No linked devices")]',
|
||||
linkDeviceButton: commonPage.divRoleButtonWithText('Link New Device'),
|
||||
linkDeviceButtonDisabled: commonPage.divRoleButtonWithTextDisabled('Link New Device'),
|
||||
qrImageDiv: commonPage.divWithClass('qr-image'),
|
||||
allowPairingButton: commonPage.divRoleButtonWithText('Allow Linking'),
|
||||
okButton: commonPage.divRoleButtonWithText('OK'),
|
||||
devicePairedDescription: (secretWords: string) =>
|
||||
commonPage.divWithClassAndText('session-settings-item__description', secretWords),
|
||||
unpairDeviceButton: commonPage.divRoleButtonDangerWithText('Unlink Device'),
|
||||
deleteAccountButton: commonPage.divRoleButtonDangerWithText('Delete Account'),
|
||||
validateUnpairDevice: commonPage.divRoleButtonDangerWithText('Unlink'),
|
||||
};
|
@ -1,34 +0,0 @@
|
||||
import commonPage from './common.page';
|
||||
|
||||
export = {
|
||||
registrationTabSignIn:
|
||||
'//div[contains(string(), "Sign In")][contains(@class, "session-registration__tab")][contains(@role, "tab")]',
|
||||
|
||||
// create new account
|
||||
createSessionIDButton: commonPage.divRoleButtonWithText('Create Session ID'),
|
||||
continueButton: commonPage.divRoleButtonWithText('Continue'),
|
||||
textareaGeneratedPubkey: '//textarea[contains(@class, "session-id-editable-textarea")]',
|
||||
getStartedButton: commonPage.divRoleButtonWithText('Get started'),
|
||||
|
||||
// restore from seed
|
||||
restoreFromSeedMode: commonPage.divRoleButtonWithText('Restore From Recovery'),
|
||||
|
||||
recoveryPhraseInput: commonPage.inputWithPlaceholder('Enter Recovery Phrase'),
|
||||
displayNameInput: commonPage.inputWithPlaceholder('Enter a display name'),
|
||||
passwordInput: commonPage.inputWithPlaceholder('Enter password (optional)'),
|
||||
continueSessionButton: commonPage.divRoleButtonWithText('Continue Your Session'),
|
||||
conversationListContainer: commonPage.divWithClass('module-conversations-list-content'),
|
||||
|
||||
// device linking
|
||||
linkDeviceMode: commonPage.divRoleButtonWithText('Link Device to Existing Session ID'),
|
||||
textareaLinkDevicePubkey: commonPage.textAreaWithPlaceholder('Enter your Session ID'),
|
||||
linkDeviceTriggerButton: commonPage.divRoleButtonWithText('Link Device'),
|
||||
toastWrapper: '//*[contains(@class,"session-toast-wrapper")]',
|
||||
secretWordsText:
|
||||
'//div[contains(@class,"session-registration__content__secret-words")]/div[contains(@class,"subtle")]',
|
||||
linkWithThisDevice: commonPage.objWithClassAndText(
|
||||
'h4',
|
||||
'device-pairing-dialog__desc',
|
||||
'Allow linking with this device?'
|
||||
),
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
export = {
|
||||
// settings view
|
||||
settingsButtonSection:
|
||||
'//*[contains(@class,"session-icon-button") and .//*[contains(@class, "gear")]]',
|
||||
settingsRowWithText: (text: string) =>
|
||||
`//*[contains(@class, "left-pane-setting-category-list-item")][contains(string(), '${text}')]`,
|
||||
|
||||
leftPaneSettingsButton:
|
||||
'//*[contains(@class,"session-icon-button") and .//*[contains(@class, "gear")]]',
|
||||
|
||||
settingToggleWithText: (text: string) =>
|
||||
`//div[contains(@class, 'session-settings-item') and contains(string(), '${text}')]//*[contains(@class, 'session-toggle')]`,
|
||||
settingButtonWithText: (text: string) =>
|
||||
`//div[contains(@class, 'session-settings-item')]//*[contains(@class, 'session-button') and contains(string(), '${text}')]`,
|
||||
settingCategoryWithText: (text: string) =>
|
||||
`//div[contains(@class, 'left-pane-setting-category-list-item') and contains(string(), '${text}')]`,
|
||||
|
||||
// Confirm is a boolean. Selects confirmation input
|
||||
passwordSetModalInput: (_confirm: boolean | undefined) =>
|
||||
`//input[@id = 'password-modal-input${_confirm ? '-confirm' : ''}']`,
|
||||
|
||||
secretWordsTextInDialog:
|
||||
'//div[@class="device-pairing-dialog__secret-words"]/div[@class="subtle"]',
|
||||
};
|
@ -1,132 +0,0 @@
|
||||
/* eslint-disable prefer-arrow-callback */
|
||||
/* eslint-disable func-names */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
// tslint:disable: no-implicit-dependencies
|
||||
// tslint:disable: await-promise
|
||||
// tslint:disable: no-invalid-this
|
||||
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import { Common } from './common';
|
||||
import { Application } from 'spectron';
|
||||
|
||||
import SettingsPage from './page-objects/settings.page';
|
||||
import RegistrationPage from './page-objects/registration.page';
|
||||
import ConversationPage from './page-objects/conversation.page';
|
||||
|
||||
describe('Window Test and Login', function() {
|
||||
this.timeout(60000);
|
||||
this.slow(20000);
|
||||
let app: Application;
|
||||
|
||||
beforeEach(async () => {
|
||||
await Common.killallElectron();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await Common.stopApp(app);
|
||||
await Common.killallElectron();
|
||||
});
|
||||
|
||||
it('registration: opens one window', async () => {
|
||||
app = await Common.startAndAssureCleanedApp();
|
||||
app.client.getWindowCount().should.eventually.be.equal(1);
|
||||
});
|
||||
|
||||
it('registration: window title is correct', async () => {
|
||||
app = await Common.startAndAssureCleanedApp();
|
||||
|
||||
app.client.getTitle().should.eventually.be.equal('Session - test-integration-session');
|
||||
});
|
||||
|
||||
it('registration: can restore from seed', async () => {
|
||||
app = await Common.startAndAssureCleanedApp();
|
||||
|
||||
await app.client.element(RegistrationPage.registrationTabSignIn).click();
|
||||
await app.client.element(RegistrationPage.restoreFromSeedMode).click();
|
||||
await app.client
|
||||
.element(RegistrationPage.recoveryPhraseInput)
|
||||
.setValue(Common.TEST_RECOVERY_PHRASE_1);
|
||||
await app.client.element(RegistrationPage.displayNameInput).setValue(Common.TEST_DISPLAY_NAME1);
|
||||
|
||||
// validate fields are filled
|
||||
await app.client
|
||||
.element(RegistrationPage.recoveryPhraseInput)
|
||||
.getValue()
|
||||
.should.eventually.equal(Common.TEST_RECOVERY_PHRASE_1);
|
||||
await app.client
|
||||
.element(RegistrationPage.displayNameInput)
|
||||
.getValue()
|
||||
.should.eventually.equal(Common.TEST_DISPLAY_NAME1);
|
||||
|
||||
// trigger login
|
||||
await app.client.element(RegistrationPage.continueSessionButton).click();
|
||||
await app.client.waitForExist(RegistrationPage.conversationListContainer, 4000);
|
||||
|
||||
await Common.timeout(2000);
|
||||
|
||||
await app.webContents
|
||||
.executeJavaScript("window.storage.get('primaryDevicePubKey')")
|
||||
.should.eventually.be.equal(Common.TEST_PUBKEY1);
|
||||
});
|
||||
|
||||
it('registration: can create new account', async () => {
|
||||
app = await Common.startAndAssureCleanedApp();
|
||||
await app.client.element(RegistrationPage.createSessionIDButton).click();
|
||||
// wait for the animation of generated pubkey to finish
|
||||
await Common.timeout(2000);
|
||||
const pubkeyGenerated = await app.client
|
||||
.element(RegistrationPage.textareaGeneratedPubkey)
|
||||
.getValue();
|
||||
// validate generated pubkey
|
||||
pubkeyGenerated.should.have.lengthOf(66);
|
||||
pubkeyGenerated.substr(0, 2).should.be.equal('05');
|
||||
await app.client.element(RegistrationPage.continueButton).click();
|
||||
await app.client.isExisting(RegistrationPage.displayNameInput).should.eventually.be.true;
|
||||
await app.client.element(RegistrationPage.displayNameInput).setValue(Common.TEST_DISPLAY_NAME1);
|
||||
await app.client.element(RegistrationPage.getStartedButton).click();
|
||||
await app.client.waitForExist(ConversationPage.conversationButtonSection, 5000);
|
||||
|
||||
await app.webContents
|
||||
.executeJavaScript("window.storage.get('primaryDevicePubKey')")
|
||||
.should.eventually.be.equal(pubkeyGenerated);
|
||||
});
|
||||
|
||||
it('registration: can delete account when logged in', async () => {
|
||||
// login as user1
|
||||
const login = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_1,
|
||||
displayName: Common.TEST_DISPLAY_NAME1,
|
||||
};
|
||||
app = await Common.startAndStub(login);
|
||||
|
||||
await app.client.waitForExist(RegistrationPage.conversationListContainer, 4000);
|
||||
|
||||
await app.webContents
|
||||
.executeJavaScript("window.storage.get('primaryDevicePubKey')")
|
||||
.should.eventually.be.equal(Common.TEST_PUBKEY1);
|
||||
// delete account
|
||||
await app.client.element(SettingsPage.settingsButtonSection).click();
|
||||
await app.client.element(ConversationPage.deleteAccountButton).click();
|
||||
await app.client.isExisting(ConversationPage.descriptionDeleteAccount).should.eventually.be
|
||||
.true;
|
||||
// click on the modal OK button to delete the account
|
||||
await app.client.element(ConversationPage.validateDeleteAccount).click();
|
||||
// wait for the app restart
|
||||
await Common.timeout(2000);
|
||||
|
||||
// Spectron will loose the connection with the app during the app restart.
|
||||
// We have to restart the app without altering the logged in user or anything here, just to get a valid new ref to the app.
|
||||
await Common.stopApp(app);
|
||||
app = await Common.startApp();
|
||||
|
||||
// validate that on app start, the registration sign in is shown
|
||||
await app.client.waitForExist(RegistrationPage.registrationTabSignIn, 3000);
|
||||
// validate that no pubkey are set in storage
|
||||
await app.webContents
|
||||
.executeJavaScript("window.storage.get('primaryDevicePubKey')")
|
||||
.should.eventually.be.equal(null);
|
||||
// and that the conversation list is not shown
|
||||
await app.client.isExisting(RegistrationPage.conversationListContainer).should.eventually.be
|
||||
.false;
|
||||
});
|
||||
});
|
@ -1,111 +0,0 @@
|
||||
/* eslint-disable func-names */
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
// tslint:disable: await-promise
|
||||
// tslint:disable: no-implicit-dependencies
|
||||
// tslint:disable: no-invalid-this
|
||||
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import { Common } from './common';
|
||||
import { Application } from 'spectron';
|
||||
import ConversationPage from './page-objects/conversation.page';
|
||||
|
||||
async function generateAndSendMessage(app: Application) {
|
||||
// send a message from app and validate it is received on app2
|
||||
const textMessage = Common.generateSendMessageText();
|
||||
await app.client.element(ConversationPage.sendMessageTextarea).setValue(textMessage);
|
||||
|
||||
await app.client
|
||||
.element(ConversationPage.sendMessageTextarea)
|
||||
.getValue()
|
||||
.should.eventually.equal(textMessage);
|
||||
// send the message
|
||||
await app.client.keys('Enter');
|
||||
|
||||
// validate that the message has been added to the message list view
|
||||
await app.client.waitForExist(ConversationPage.existingSendMessageText(textMessage), 2000);
|
||||
|
||||
return textMessage;
|
||||
}
|
||||
|
||||
async function makeFriendsPlusMessage(app: Application, [app2, pubkey]: [Application, string]) {
|
||||
await Common.makeFriends(app, [app2, pubkey]);
|
||||
|
||||
// Send something back so that `app` can see our name
|
||||
await app2.client.waitForExist(ConversationPage.conversationItem, 5000);
|
||||
await app2.client.element(ConversationPage.conversationItem).click();
|
||||
const text = await generateAndSendMessage(app2);
|
||||
|
||||
await app.client.waitForExist(ConversationPage.existingReceivedMessageText(text), 8000);
|
||||
|
||||
// Click away so we can call this function again
|
||||
await app.client.element(ConversationPage.conversationButtonSection).click();
|
||||
}
|
||||
|
||||
async function testTwoMembers() {
|
||||
const [app, app2] = await Common.startAppsAsFriends();
|
||||
|
||||
// create group and add new friend
|
||||
await Common.addFriendToNewClosedGroup([app, app2]);
|
||||
|
||||
const text1 = await generateAndSendMessage(app);
|
||||
|
||||
// validate that the message has been added to the message list view
|
||||
await app2.client.waitForExist(ConversationPage.existingReceivedMessageText(text1), 5000);
|
||||
|
||||
// Send a message back:
|
||||
const text2 = await generateAndSendMessage(app2);
|
||||
|
||||
// TODO: fix this. We can send messages back manually, not sure
|
||||
// why this test fails
|
||||
await app.client.waitForExist(ConversationPage.existingReceivedMessageText(text2), 10000);
|
||||
}
|
||||
|
||||
async function testThreeMembers() {
|
||||
// 1. Make three clients A, B, C
|
||||
|
||||
const app1Props = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_1,
|
||||
displayName: Common.TEST_DISPLAY_NAME1,
|
||||
stubSnode: true,
|
||||
};
|
||||
|
||||
const app2Props = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_2,
|
||||
displayName: Common.TEST_DISPLAY_NAME2,
|
||||
stubSnode: true,
|
||||
};
|
||||
|
||||
const app3Props = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_3,
|
||||
displayName: Common.TEST_DISPLAY_NAME3,
|
||||
stubSnode: true,
|
||||
};
|
||||
|
||||
const [app1, app2, app3] = await Promise.all([
|
||||
Common.startAndStub(app1Props),
|
||||
Common.startAndStubN(app2Props, 2),
|
||||
Common.startAndStubN(app3Props, 3),
|
||||
]);
|
||||
|
||||
// 2. Make A friends with B and C (B and C are not friends)
|
||||
await makeFriendsPlusMessage(app1, [app2, Common.TEST_PUBKEY2]);
|
||||
|
||||
await makeFriendsPlusMessage(app1, [app3, Common.TEST_PUBKEY3]);
|
||||
|
||||
// 3. Add all three to the group
|
||||
|
||||
await Common.addFriendToNewClosedGroup([app1, app2, app3]);
|
||||
|
||||
// 4. Test that all members can see the message from app1
|
||||
const text1 = await generateAndSendMessage(app1);
|
||||
await app2.client.waitForExist(ConversationPage.existingReceivedMessageText(text1), 5000);
|
||||
await app3.client.waitForExist(ConversationPage.existingReceivedMessageText(text1), 5000);
|
||||
// TODO: test that B and C can send messages to the group
|
||||
|
||||
// const text2 = await generateAndSendMessage(app3);
|
||||
|
||||
// await app2.client.waitForExist(
|
||||
// ConversationPage.existingReceivedMessageText(text2),
|
||||
// 5000
|
||||
// );
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
// tslint:disable: await-promise
|
||||
// tslint:disable: no-implicit-dependencies
|
||||
// tslint:disable: no-invalid-this
|
||||
|
||||
import { after, before, describe, it } from 'mocha';
|
||||
import { Common } from './common';
|
||||
import { Application } from 'spectron';
|
||||
|
||||
import SettingsPage from './page-objects/settings.page';
|
||||
import CommonPage from './page-objects/common.page';
|
||||
|
||||
// Generate random password
|
||||
// tslint:disable-next-line: insecure-random
|
||||
const password = Math.random()
|
||||
.toString(36)
|
||||
.substr(2, 8);
|
||||
const passwordInputID = 'password-modal-input';
|
||||
|
||||
describe('Settings', function() {
|
||||
this.timeout(60000);
|
||||
this.slow(20000);
|
||||
let app: Application;
|
||||
|
||||
before(async () => {
|
||||
await Common.killallElectron();
|
||||
await Common.stopStubSnodeServer();
|
||||
|
||||
const appProps = {
|
||||
recoveryPhrase: Common.TEST_RECOVERY_PHRASE_1,
|
||||
displayName: Common.TEST_DISPLAY_NAME1,
|
||||
};
|
||||
|
||||
app = await Common.startAndStub(appProps);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await Common.stopApp(app);
|
||||
await Common.killallElectron();
|
||||
await Common.stopStubSnodeServer();
|
||||
});
|
||||
|
||||
it('settings: can toggle menubar', async () => {
|
||||
const menuBarVisible = await app.browserWindow.isMenuBarVisible();
|
||||
|
||||
await app.client.element(SettingsPage.settingsButtonSection).click();
|
||||
await app.client.element(SettingsPage.settingToggleWithText('Hide Menu Bar')).click();
|
||||
|
||||
// Confirm that toggling works
|
||||
const menuBarToggled = await app.browserWindow.isMenuBarVisible();
|
||||
menuBarToggled.should.equal(!menuBarVisible);
|
||||
});
|
||||
|
||||
it('settings: can set password', async () => {
|
||||
await app.client.element(SettingsPage.settingsRowWithText('Privacy')).click();
|
||||
|
||||
await app.client.element(SettingsPage.settingButtonWithText('Set Password')).click();
|
||||
|
||||
await Common.setValueWrapper(app, CommonPage.inputWithId(passwordInputID), password);
|
||||
await Common.setValueWrapper(
|
||||
app,
|
||||
CommonPage.inputWithId(`${passwordInputID}-confirm`),
|
||||
password
|
||||
);
|
||||
|
||||
await app.client.keys('Enter');
|
||||
|
||||
// Verify password set
|
||||
await app.client.waitForExist(CommonPage.toastWithText('Set Password'), 2000);
|
||||
|
||||
await Common.closeToast(app);
|
||||
});
|
||||
|
||||
it('settings: can remove password', async () => {
|
||||
// Enter password to unlock settings
|
||||
await Common.setValueWrapper(app, CommonPage.inputWithId('password-lock-input'), password);
|
||||
|
||||
await app.client.keys('Enter');
|
||||
|
||||
// Remove password
|
||||
await app.client.element(SettingsPage.settingButtonWithText('Remove Password')).click();
|
||||
|
||||
await Common.setValueWrapper(app, CommonPage.inputWithId(passwordInputID), password);
|
||||
|
||||
await app.client.keys('Enter');
|
||||
|
||||
// Verify password removed with toast
|
||||
await app.client.waitForExist(CommonPage.toastWithText('Removed Password'), 2000);
|
||||
|
||||
// Verify password actully removed
|
||||
await app.client.isExisting(CommonPage.divWithClass('session-settings__password-lock')).should
|
||||
.eventually.be.false;
|
||||
});
|
||||
});
|
@ -1,37 +0,0 @@
|
||||
import { StringUtils } from '../../../../session/utils';
|
||||
|
||||
import { default as insecureNodeFetch } from 'node-fetch';
|
||||
|
||||
class StubMessageAPI {
|
||||
public ourKey: string;
|
||||
public baseUrl: string;
|
||||
constructor(ourKey: string) {
|
||||
this.ourKey = ourKey;
|
||||
this.baseUrl = 'http://localhost:3000';
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
public async sendMessage(
|
||||
pubKey: string,
|
||||
data: any,
|
||||
messageTimeStamp: number,
|
||||
ttl: number,
|
||||
options = {}
|
||||
) {
|
||||
const post = {
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
const data64 = StringUtils.decode(data, 'base64');
|
||||
// insecureNodeFetch but this is a stub
|
||||
|
||||
await insecureNodeFetch(
|
||||
`${
|
||||
this.baseUrl
|
||||
}/messages?pubkey=${pubKey}×tamp=${messageTimeStamp}&data=${encodeURIComponent(data64)}`,
|
||||
post
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StubMessageAPI;
|
Binary file not shown.
Loading…
Reference in New Issue