fix link device test and lint

pull/1287/head
Audric Ackermann 5 years ago
parent e2a42d1b61
commit fec0ead1de
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -1,4 +1,4 @@
/* global textsecure, WebAPI, libsignal, window, libloki, _, libsession */ /* global textsecure, WebAPI, window, libloki, _, libsession */
/* eslint-disable more/no-then, no-bitwise */ /* eslint-disable more/no-then, no-bitwise */
@ -192,174 +192,6 @@ function MessageSender() {
MessageSender.prototype = { MessageSender.prototype = {
constructor: MessageSender, constructor: MessageSender,
// makeAttachmentPointer :: Attachment -> Promise AttachmentPointerProto
async makeAttachmentPointer(attachment, publicServer = null, options = {}) {
const { isRaw = false, isAvatar = false } = options;
if (typeof attachment !== 'object' || attachment == null) {
return Promise.resolve(undefined);
}
if (
!(attachment.data instanceof ArrayBuffer) &&
!ArrayBuffer.isView(attachment.data)
) {
return Promise.reject(
new TypeError(
`\`attachment.data\` must be an \`ArrayBuffer\` or \`ArrayBufferView\`; got: ${typeof attachment.data}`
)
);
}
const proto = new textsecure.protobuf.AttachmentPointer();
let attachmentData;
const server = publicServer || this.server;
if (publicServer || isRaw) {
attachmentData = attachment.data;
} else {
proto.key = libsignal.crypto.getRandomBytes(64);
const iv = libsignal.crypto.getRandomBytes(16);
const result = await textsecure.crypto.encryptAttachment(
attachment.data,
proto.key,
iv
);
proto.digest = result.digest;
attachmentData = result.ciphertext;
}
const result = isAvatar
? await server.putAvatar(attachmentData)
: await server.putAttachment(attachmentData);
if (!result) {
return Promise.reject(
new Error('Failed to upload data to attachment fileserver')
);
}
const { url, id } = result;
proto.id = id;
proto.url = url;
proto.contentType = attachment.contentType;
if (attachment.size) {
proto.size = attachment.size;
}
if (attachment.fileName) {
proto.fileName = attachment.fileName;
}
if (attachment.flags) {
proto.flags = attachment.flags;
}
if (attachment.width) {
proto.width = attachment.width;
}
if (attachment.height) {
proto.height = attachment.height;
}
if (attachment.caption) {
proto.caption = attachment.caption;
}
return proto;
},
queueJobForNumber(number, runJob) {
const taskWithTimeout = textsecure.createTaskWithTimeout(
runJob,
`queueJobForNumber ${number}`
);
const runPrevious = this.pendingMessages[number] || Promise.resolve();
this.pendingMessages[number] = runPrevious.then(
taskWithTimeout,
taskWithTimeout
);
const runCurrent = this.pendingMessages[number];
runCurrent.then(() => {
if (this.pendingMessages[number] === runCurrent) {
delete this.pendingMessages[number];
}
});
},
uploadAttachments(message, publicServer) {
return Promise.all(
message.attachments.map(attachment =>
this.makeAttachmentPointer(attachment, publicServer)
)
)
.then(attachmentPointers => {
// eslint-disable-next-line no-param-reassign
message.attachmentPointers = attachmentPointers;
})
.catch(error => {
if (error instanceof Error && error.name === 'HTTPError') {
throw new textsecure.MessageError(message, error);
} else {
throw error;
}
});
},
async uploadLinkPreviews(message, publicServer) {
try {
const preview = await Promise.all(
(message.preview || []).map(async item => ({
...item,
image: await this.makeAttachmentPointer(item.image, publicServer),
}))
);
// eslint-disable-next-line no-param-reassign
message.preview = preview;
} catch (error) {
if (error instanceof Error && error.name === 'HTTPError') {
throw new textsecure.MessageError(message, error);
} else {
throw error;
}
}
},
uploadThumbnails(message, publicServer) {
const makePointer = this.makeAttachmentPointer.bind(this);
const { quote } = message;
if (!quote || !quote.attachments || quote.attachments.length === 0) {
return Promise.resolve();
}
return Promise.all(
quote.attachments.map(attachment => {
const { thumbnail } = attachment;
if (!thumbnail) {
return null;
}
return makePointer(thumbnail, publicServer).then(pointer => {
// eslint-disable-next-line no-param-reassign
attachment.attachmentPointer = pointer;
});
})
).catch(error => {
if (error instanceof Error && error.name === 'HTTPError') {
throw new textsecure.MessageError(message, error);
} else {
throw error;
}
});
},
uploadAvatar(attachment) {
// isRaw is true since the data is already encrypted
// and doesn't need to be encrypted again
return this.makeAttachmentPointer(attachment, null, {
isRaw: true,
isAvatar: true,
});
},
async sendContactSyncMessage(convos) { async sendContactSyncMessage(convos) {
let convosToSync; let convosToSync;
if (!convos) { if (!convos) {
@ -516,7 +348,6 @@ textsecure.MessageSender = function MessageSenderWrapper(username, password) {
this.sendOpenGroupsSyncMessage = sender.sendOpenGroupsSyncMessage.bind( this.sendOpenGroupsSyncMessage = sender.sendOpenGroupsSyncMessage.bind(
sender sender
); );
this.uploadAvatar = sender.uploadAvatar.bind(sender);
this.syncReadMessages = sender.syncReadMessages.bind(sender); this.syncReadMessages = sender.syncReadMessages.bind(sender);
this.syncVerification = sender.syncVerification.bind(sender); this.syncVerification = sender.syncVerification.bind(sender);
this.makeProxiedRequest = sender.makeProxiedRequest.bind(sender); this.makeProxiedRequest = sender.makeProxiedRequest.bind(sender);

@ -264,7 +264,9 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
} }
public setOptionsSetting(settingID: string) { public setOptionsSetting(settingID: string) {
const selectedValue = ($(`#${settingID} .session-radio input:checked`) as any).val(); const selectedValue = ($(
`#${settingID} .session-radio input:checked`
) as any).val();
window.setSettingValue(settingID, selectedValue); window.setSettingValue(settingID, selectedValue);
} }
@ -668,7 +670,9 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
} }
private async onKeyUp(event: any) { private async onKeyUp(event: any) {
const lockPasswordFocussed = ($('#password-lock-input') as any).is(':focus'); const lockPasswordFocussed = ($('#password-lock-input') as any).is(
':focus'
);
if (event.key === 'Enter' && lockPasswordFocussed) { if (event.key === 'Enter' && lockPasswordFocussed) {
await this.validatePasswordLock(); await this.validatePasswordLock();

@ -30,7 +30,9 @@ export class SettingsHeader extends React.Component<Props, any> {
} }
public focusSearch() { public focusSearch() {
($('.left-pane-setting-section .session-search-input input') as any).focus(); ($(
'.left-pane-setting-section .session-search-input input'
) as any).focus();
} }
public showAddLinkedDeviceModal() { public showAddLinkedDeviceModal() {

@ -29,7 +29,6 @@ chai.config.includeStack = true;
const STUB_SNODE_SERVER_PORT = 3000; const STUB_SNODE_SERVER_PORT = 3000;
const ENABLE_LOG = false; const ENABLE_LOG = false;
// tslint:disable-next-line: no-unnecessary-class // tslint:disable-next-line: no-unnecessary-class
export class Common { export class Common {
/* ************** USERS ****************** */ /* ************** USERS ****************** */
@ -78,7 +77,11 @@ export class Common {
} }
// a wrapper to work around electron/spectron bug // a wrapper to work around electron/spectron bug
public static async setValueWrapper(app: Application, selector: any, value: string) { public static async setValueWrapper(
app: Application,
selector: any,
value: string
) {
// keys, setValue and addValue hang on certain platforms // keys, setValue and addValue hang on certain platforms
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
@ -117,7 +120,16 @@ export class Common {
const instance = environment.replace('test-integration-', ''); const instance = environment.replace('test-integration-', '');
const app1 = new Application({ const app1 = new Application({
path: path.join(__dirname, '..', '..', '..', '..', 'node_modules', '.bin', 'electron'), path: path.join(
__dirname,
'..',
'..',
'..',
'..',
'node_modules',
'.bin',
'electron'
),
args: ['.'], args: ['.'],
env: { env: {
NODE_ENV: env, NODE_ENV: env,
@ -181,7 +193,9 @@ export class Common {
return app2; return app2;
} }
public static async startAndAssureCleanedApp(env = 'test-integration-session') { public static async startAndAssureCleanedApp(
env = 'test-integration-session'
) {
const userData = path.join(Common.USER_DATA_ROOT_FOLDER, `Session-${env}`); const userData = path.join(Common.USER_DATA_ROOT_FOLDER, `Session-${env}`);
await Common.rmFolder(userData); await Common.rmFolder(userData);
@ -226,7 +240,11 @@ export class Common {
return appN; return appN;
} }
public static async restoreFromMnemonic(app: Application, mnemonic: string, displayName: string) { public static async restoreFromMnemonic(
app: Application,
mnemonic: string,
displayName: string
) {
await app.client.element(RegistrationPage.registrationTabSignIn).click(); await app.client.element(RegistrationPage.registrationTabSignIn).click();
await app.client.element(RegistrationPage.restoreFromSeedMode).click(); await app.client.element(RegistrationPage.restoreFromSeedMode).click();
await Common.setValueWrapper( await Common.setValueWrapper(
@ -250,7 +268,10 @@ export class Common {
); );
} }
public static async makeFriends(app1: Application, client2: [Application, string]) { public static async makeFriends(
app1: Application,
client2: [Application, string]
) {
const [app2, pubkey2] = client2; const [app2, pubkey2] = client2;
/** add each other as friends */ /** add each other as friends */
@ -259,7 +280,11 @@ export class Common {
await app1.client.element(ConversationPage.contactsButtonSection).click(); await app1.client.element(ConversationPage.contactsButtonSection).click();
await app1.client.element(ConversationPage.addContactButton).click(); await app1.client.element(ConversationPage.addContactButton).click();
await Common.setValueWrapper(app1, ConversationPage.sessionIDInput, pubkey2); await Common.setValueWrapper(
app1,
ConversationPage.sessionIDInput,
pubkey2
);
await app1.client.element(ConversationPage.nextButton).click(); await app1.client.element(ConversationPage.nextButton).click();
await app1.client.waitForExist( await app1.client.waitForExist(
ConversationPage.sendFriendRequestTextarea, ConversationPage.sendFriendRequestTextarea,
@ -331,7 +356,10 @@ export class Common {
return [app1, app2]; return [app1, app2];
} }
public static async addFriendToNewClosedGroup(members: Array<Application>, useSenderKeys?: boolean) { public static async addFriendToNewClosedGroup(
members: Array<Application>,
useSenderKeys?: boolean
) {
const [app, ...others] = members; const [app, ...others] = members;
await app.client await app.client
@ -433,7 +461,11 @@ export class Common {
); );
} }
public static async linkApp2ToApp(app1: Application, app2: Application, app1Pubkey: string) { 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 // app needs to be logged in as user1 and app2 needs to be logged out
// start the pairing dialog for the first app // start the pairing dialog for the first app
await app1.client.element(SettingsPage.settingsButtonSection).click(); await app1.client.element(SettingsPage.settingsButtonSection).click();
@ -497,7 +529,10 @@ export class Common {
.should.eventually.be.equal(app1Pubkey); .should.eventually.be.equal(app1Pubkey);
} }
public static async triggerUnlinkApp2FromApp(app1: Application, app2: Application) { public static async triggerUnlinkApp2FromApp(
app1: Application,
app2: Application
) {
// check app2 is loggedin // check app2 is loggedin
await app2.client.isExisting(RegistrationPage.conversationListContainer) await app2.client.isExisting(RegistrationPage.conversationListContainer)
.should.eventually.be.true; .should.eventually.be.true;
@ -541,7 +576,11 @@ export class Common {
} }
} }
public static async sendMessage(app: Application, messageText: string, fileLocation?: string) { public static async sendMessage(
app: Application,
messageText: string,
fileLocation?: string
) {
await Common.setValueWrapper( await Common.setValueWrapper(
app, app,
ConversationPage.sendMessageTextarea, ConversationPage.sendMessageTextarea,
@ -647,7 +686,11 @@ export class Common {
} }
} }
public static async joinOpenGroup(app: Application, openGroupUrl: string, name: string) { public static async joinOpenGroup(
app: Application,
openGroupUrl: string,
name: string
) {
await app.client await app.client
.element(ConversationPage.conversationButtonSection) .element(ConversationPage.conversationButtonSection)
.click(); .click();
@ -699,7 +742,11 @@ export class Common {
* @param str the string to search (not regex) * @param str the string to search (not regex)
* Note: getRenderProcessLogs() clears the app logs each calls. * Note: getRenderProcessLogs() clears the app logs each calls.
*/ */
public static async logsContains(renderLogs: Array<{message: string}>, str: string, count?: number) { public static async logsContains(
renderLogs: Array<{ message: string }>,
str: string,
count?: number
) {
const foundLines = renderLogs.filter(log => log.message.includes(str)); const foundLines = renderLogs.filter(log => log.message.includes(str));
// tslint:disable-next-line: no-unused-expression // tslint:disable-next-line: no-unused-expression

@ -1,16 +1,7 @@
// import { expect } from 'chai';
// import * as sinon from 'sinon';
// import { TestUtils } from '../../test-utils';
// import { PairingAuthorisation } from '../../../../js/modules/data';
// import { MultiDeviceProtocol } from '../../../session/protocols';
// import { PubKey } from '../../../session/types';
// import { UserUtil } from '../../../util';
// import { StringUtils } from '../../../session/utils';
import { Common } from './common'; import { Common } from './common';
// tslint:disable: no-import-side-effect // tslint:disable: no-import-side-effect
// tslint:disable: await-promise
import './registration_test'; import './registration_test';
import './open_group_test'; import './open_group_test';
@ -26,9 +17,7 @@ before(async () => {
// start the app once before all tests to get the platform-dependent // 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 // path of user data and store it to common.USER_DATA_ROOT_FOLDER
const app1 = await Common.startApp(); const app1 = await Common.startApp();
const ret = await app1.electron.remote.app.getPath( const ret = (await app1.electron.remote.app.getPath('appData')) as any;
'appData'
) as any;
Common.USER_DATA_ROOT_FOLDER = ret; Common.USER_DATA_ROOT_FOLDER = ret;
await Common.stopApp(app1); await Common.stopApp(app1);

@ -3,9 +3,13 @@
/* eslint-disable func-names */ /* eslint-disable func-names */
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
// tslint:disable: no-implicit-dependencies // tslint:disable: no-implicit-dependencies
// tslint:disable: await-promise
import { afterEach, beforeEach, describe, it } from 'mocha'; import { afterEach, beforeEach, describe, it } from 'mocha';
import { Common } from './common'; import { Common } from './common';
import { Application } from 'spectron'; import { Application } from 'spectron';
import * as TestUtils from '../../test-utils/utils/stubbing';
import { expect } from 'chai';
describe('Link Device', function() { describe('Link Device', function() {
let app: Application; let app: Application;
@ -44,27 +48,34 @@ describe('Link Device', function() {
}); });
it('linkDevice:sync no groups, closed group, nor open groups', async () => { it('linkDevice:sync no groups, closed group, nor open groups', async () => {
await TestUtils.spyMessageQueueSend(app);
await TestUtils.spyMessageQueueSend(app2);
await Common.linkApp2ToApp(app, app2, Common.TEST_PUBKEY1); await Common.linkApp2ToApp(app, app2, Common.TEST_PUBKEY1);
await Common.timeout(10000); await Common.timeout(10000);
const allMessagesSentApp = await TestUtils.getAllMessagesSent(app);
const allMessagesSentApp2 = await TestUtils.getAllMessagesSent(app2);
// get logs at this stage (getRenderProcessLogs() clears the app logs) expect(allMessagesSentApp2[0][1]).to.have.property('requestSignature');
const secondaryRenderLogs = await app2.client.getRenderProcessLogs(); expect(allMessagesSentApp2[0][1]).to.have.property(
// pairing request message sent from secondary to primary pubkey 'primaryDevicePubKey',
await Common.logsContains( Common.TEST_PUBKEY1
secondaryRenderLogs,
`Sending pairing-request:pairing-request message to ${Common.TEST_PUBKEY1}`
); );
expect(allMessagesSentApp2[0][1]).to.have.property('secondaryDevicePubKey');
const primaryRenderLogs = await app.client.getRenderProcessLogs(); expect(allMessagesSentApp[1][1]).to.have.property('requestSignature');
// primary grant pairing request expect(allMessagesSentApp[1][1]).to.have.property('grantSignature');
await Common.logsContains( expect(allMessagesSentApp[1][1]).to.have.property(
primaryRenderLogs, 'displayName',
'Sending pairing-request:pairing-request message to OUR SECONDARY PUBKEY' Common.TEST_DISPLAY_NAME1
);
expect(allMessagesSentApp[1][1]).to.have.property(
'primaryDevicePubKey',
Common.TEST_PUBKEY1
); );
expect(allMessagesSentApp[1][1]).to.have.property('secondaryDevicePubKey');
// no friends, no closed groups, no open groups. we should see those message sync in the log // one message for session establishment, one for grant authorization. No group or anything to sync
await Common.logsContains(primaryRenderLogs, 'No closed group to sync.', 1); expect(allMessagesSentApp.length).to.be.equal(2);
await Common.logsContains(primaryRenderLogs, 'No open groups to sync', 1);
await Common.logsContains(primaryRenderLogs, 'No contacts to sync.', 1);
}); });
}); });

@ -17,7 +17,8 @@ export = {
textAreaWithClass: (classname: string) => textAreaWithClass: (classname: string) =>
`//textarea[contains(@class, "${classname}")]`, `//textarea[contains(@class, "${classname}")]`,
byId: (id: string) => `//*[@id="${id}"]`, byId: (id: string) => `//*[@id="${id}"]`,
divWithClass: (classname: string) => `//div[contains(@class, "${classname}")]`, divWithClass: (classname: string) =>
`//div[contains(@class, "${classname}")]`,
divWithClassAndText: (classname: string, text: string) => divWithClassAndText: (classname: string, text: string) =>
module.exports.objWithClassAndText('div', classname, text), module.exports.objWithClassAndText('div', classname, text),
spanWithClassAndText: (classname: string, text: string) => spanWithClassAndText: (classname: string, text: string) =>

@ -1,6 +1,5 @@
import commonPage from './common.page'; import commonPage from './common.page';
export = { export = {
registrationTabSignIn: registrationTabSignIn:
'//div[contains(string(), "Sign In")][contains(@class, "session-registration__tab")][contains(@role, "tab")]', '//div[contains(string(), "Sign In")][contains(@class, "session-registration__tab")][contains(@role, "tab")]',
@ -36,6 +35,11 @@ export = {
), ),
linkDeviceTriggerButton: commonPage.divRoleButtonWithText('Link Device'), linkDeviceTriggerButton: commonPage.divRoleButtonWithText('Link Device'),
toastWrapper: '//*[contains(@class,"session-toast-wrapper")]', toastWrapper: '//*[contains(@class,"session-toast-wrapper")]',
secretWordsText: '//div[contains(@class,"session-registration__content__secret-words")]/div[contains(@class,"subtle")]', secretWordsText:
linkWithThisDevice: commonPage.objWithClassAndText('h4', 'device-pairing-dialog__desc', 'Allow linking with this device?'), '//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?'
),
}; };

@ -5,7 +5,8 @@ export = {
settingsRowWithText: (text: string) => settingsRowWithText: (text: string) =>
`//*[contains(@class, "left-pane-setting-category-list-item")][contains(string(), '${text}')]`, `//*[contains(@class, "left-pane-setting-category-list-item")][contains(string(), '${text}')]`,
leftPaneSettingsButton: '//*[contains(@class,"session-icon-button") and .//*[contains(@class, "gear")]]', leftPaneSettingsButton:
'//*[contains(@class,"session-icon-button") and .//*[contains(@class, "gear")]]',
settingToggleWithText: (text: string) => settingToggleWithText: (text: string) =>
`//div[contains(@class, 'session-settings-item') and contains(string(), '${text}')]//*[contains(@class, 'session-toggle')]`, `//div[contains(@class, 'session-settings-item') and contains(string(), '${text}')]//*[contains(@class, 'session-toggle')]`,
@ -15,7 +16,7 @@ export = {
`//div[contains(@class, 'left-pane-setting-category-list-item') and contains(string(), '${text}')]`, `//div[contains(@class, 'left-pane-setting-category-list-item') and contains(string(), '${text}')]`,
// Confirm is a boolean. Selects confirmation input // Confirm is a boolean. Selects confirmation input
passwordSetModalInput: (_confirm: boolean|undefined) => passwordSetModalInput: (_confirm: boolean | undefined) =>
`//input[@id = 'password-modal-input${_confirm ? '-confirm' : ''}']`, `//input[@id = 'password-modal-input${_confirm ? '-confirm' : ''}']`,
secretWordsTextInDialog: secretWordsTextInDialog:

@ -12,7 +12,6 @@ import SettingsPage from './page-objects/settings.page';
import RegistrationPage from './page-objects/registration.page'; import RegistrationPage from './page-objects/registration.page';
import ConversationPage from './page-objects/conversation.page'; import ConversationPage from './page-objects/conversation.page';
describe('Window Test and Login', function() { describe('Window Test and Login', function() {
let app: Application; let app: Application;
this.timeout(20000); this.timeout(20000);

@ -1,11 +1,7 @@
import chai from 'chai'; import chai from 'chai';
import * as sinon from 'sinon'; import * as sinon from 'sinon';
import _ from 'lodash'; import _ from 'lodash';
import { import { GroupUtils, PromiseUtils } from '../../../session/utils';
GroupUtils,
PromiseUtils,
SyncMessageUtils,
} from '../../../session/utils';
import { Stubs, TestUtils } from '../../../test/test-utils'; import { Stubs, TestUtils } from '../../../test/test-utils';
import { MessageQueue } from '../../../session/sending/MessageQueue'; import { MessageQueue } from '../../../session/sending/MessageQueue';
import { import {

Loading…
Cancel
Save