More integration tests for medium groups

pull/1134/head
Maxim Shishmarev 5 years ago
parent 2a0130ff04
commit fcadcd780e

@ -21,8 +21,8 @@ chai.use(chaiAsPromised);
chai.config.includeStack = true; chai.config.includeStack = true;
// From https://github.com/chaijs/chai/issues/200 // From https://github.com/chaijs/chai/issues/200
chai.use(function (_chai, _) { chai.use((_chai, _) => {
_chai.Assertion.addMethod('withMessage', function (msg) { _chai.Assertion.addMethod('withMessage', msg => {
_.flag(this, 'message', msg); _.flag(this, 'message', msg);
}); });
}); });
@ -250,7 +250,6 @@ module.exports = {
}, },
async makeFriends(app1, client2) { async makeFriends(app1, client2) {
const [app2, pubkey2] = client2; const [app2, pubkey2] = client2;
/** add each other as friends */ /** add each other as friends */
@ -259,11 +258,7 @@ module.exports = {
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 this.setValueWrapper( await this.setValueWrapper(app1, ConversationPage.sessionIDInput, pubkey2);
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,
@ -310,10 +305,6 @@ module.exports = {
ConversationPage.acceptedFriendRequestMessage, ConversationPage.acceptedFriendRequestMessage,
5000 5000
); );
// click away to close the current pane and make further FR possible
await app1.client.element(ConversationPage.globeButtonSection).click();
}, },
async startAppsAsFriends() { async startAppsAsFriends() {
@ -340,7 +331,6 @@ module.exports = {
}, },
async addFriendToNewClosedGroup(members, useSenderKeys) { async addFriendToNewClosedGroup(members, useSenderKeys) {
const [app, ...others] = members; const [app, ...others] = members;
await this.setValueWrapper( await this.setValueWrapper(
@ -348,28 +338,35 @@ module.exports = {
ConversationPage.closedGroupNameTextarea, ConversationPage.closedGroupNameTextarea,
this.VALID_CLOSED_GROUP_NAME1 this.VALID_CLOSED_GROUP_NAME1
); );
await app.client await app.client
.element(ConversationPage.closedGroupNameTextarea) .element(ConversationPage.closedGroupNameTextarea)
.getValue() .getValue()
.should.eventually.equal(this.VALID_CLOSED_GROUP_NAME1); .should.eventually.equal(this.VALID_CLOSED_GROUP_NAME1);
await app.client // This assumes that app does not have any other friends
.element(ConversationPage.createClosedGroupMemberItem)
.isVisible().should.eventually.be.true; 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();
}
// select the first friend as a member of the groups being created
await app.client
.element(ConversationPage.createClosedGroupMemberItem)
.click();
await app.client await app.client
.element(ConversationPage.createClosedGroupMemberItemSelected) .element(ConversationPage.createClosedGroupMemberItemSelected)
.isVisible().should.eventually.be.true; .isVisible().should.eventually.be.true;
if (useSenderKeys) { if (useSenderKeys) {
// Select Sender Keys // Select Sender Keys
await app.client await app.client
.element(ConversationPage.createClosedGroupSealedSenderToggle) .element(ConversationPage.createClosedGroupSealedSenderToggle)
.click(); .click();
} }
// trigger the creation of the group // trigger the creation of the group
@ -384,8 +381,9 @@ module.exports = {
await app.client.isExisting( await app.client.isExisting(
ConversationPage.headerTitleGroupName(this.VALID_CLOSED_GROUP_NAME1) ConversationPage.headerTitleGroupName(this.VALID_CLOSED_GROUP_NAME1)
).should.eventually.be.true; ).should.eventually.be.true;
await app.client.element(ConversationPage.headerTitleMembers(2)).isVisible() await app.client
.should.eventually.be.true; .element(ConversationPage.headerTitleMembers(members.length))
.isVisible().should.eventually.be.true;
// validate overlay is closed // validate overlay is closed
await app.client await app.client
@ -575,6 +573,10 @@ module.exports = {
app1.webContents.executeJavaScript( app1.webContents.executeJavaScript(
'window.LokiMessageAPI = window.StubMessageAPI;' 'window.LokiMessageAPI = window.StubMessageAPI;'
); );
app1.webContents.executeJavaScript(
'window.LokiSnodeAPI = window.StubLokiSnodeAPI;'
);
}, },
logsContainsString: async (app1, str) => { logsContainsString: async (app1, str) => {
@ -589,35 +591,45 @@ module.exports = {
const { query } = url.parse(request.url, true); const { query } = url.parse(request.url, true);
const { pubkey, data, timestamp } = query; const { pubkey, data, timestamp } = query;
if (pubkey) { if (!pubkey) {
if (request.method === 'POST') { console.warn('NO PUBKEY');
if (ENABLE_LOG) { response.writeHead(400, { 'Content-Type': 'text/html' });
console.warn('POST', [data, timestamp]); response.end();
} }
let ori = this.messages[pubkey]; if (request.method === 'POST') {
if (!this.messages[pubkey]) { if (ENABLE_LOG) {
ori = []; console.warn(
} 'POST',
pubkey.substr(2, 3),
data.substr(4, 10),
timestamp
);
}
this.messages[pubkey] = [...ori, { data, timestamp }]; let ori = this.messages[pubkey];
response.writeHead(200, { 'Content-Type': 'text/html' }); if (!this.messages[pubkey]) {
response.end(); ori = [];
} else { }
const retrievedMessages = { messages: this.messages[pubkey] };
if (ENABLE_LOG) { this.messages[pubkey] = [...ori, { data, timestamp }];
console.warn('GET', pubkey, retrievedMessages);
} response.writeHead(200, { 'Content-Type': 'text/html' });
if (this.messages[pubkey]) { response.end();
response.writeHead(200, { 'Content-Type': 'application/json' }); } else {
response.write(JSON.stringify(retrievedMessages)); const retrievedMessages = { messages: this.messages[pubkey] || [] };
this.messages[pubkey] = [];
} if (ENABLE_LOG) {
response.end(); const messages = retrievedMessages.messages.map(m =>
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();
} }
response.end();
}); });
this.stubSnode.listen(STUB_SNODE_SERVER_PORT); this.stubSnode.listen(STUB_SNODE_SERVER_PORT);
} else { } else {

@ -63,7 +63,8 @@ module.exports = {
closedGroupNameTextarea: commonPage.textAreaWithPlaceholder( closedGroupNameTextarea: commonPage.textAreaWithPlaceholder(
'Enter a group name' 'Enter a group name'
), ),
createClosedGroupMemberItem: commonPage.divWithClass('session-member-item'), createClosedGroupMemberItem: idx =>
commonPage.divWithClass(`session-member-item-${idx}`),
createClosedGroupSealedSenderToggle: commonPage.divWithClass( createClosedGroupSealedSenderToggle: commonPage.divWithClass(
'session-toggle' 'session-toggle'
), ),

@ -6,132 +6,154 @@ const common = require('./common');
const ConversationPage = require('./page-objects/conversation.page'); const ConversationPage = require('./page-objects/conversation.page');
async function generateAndSendMessage(app) { async function generateAndSendMessage(app) {
// send a message from app and validate it is received on app2
// send a message from app and validate it is received on app2 const textMessage = common.generateSendMessageText();
const textMessage = common.generateSendMessageText(); await app.client
await app.client
.element(ConversationPage.sendMessageTextarea) .element(ConversationPage.sendMessageTextarea)
.setValue(textMessage); .setValue(textMessage);
await app.client await app.client
.element(ConversationPage.sendMessageTextarea) .element(ConversationPage.sendMessageTextarea)
.getValue() .getValue()
.should.eventually.equal(textMessage); .should.eventually.equal(textMessage);
// send the message // send the message
await app.client.keys('Enter'); 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;
}
// validate that the message has been added to the message list view async function makeFriendsPlusMessage(app, [app2, pubkey]) {
await app.client.waitForExist( await common.makeFriends(app, [app2, pubkey]);
ConversationPage.existingSendMessageText(textMessage),
2000
);
return textMessage; // Send something back so that `app` can see our name
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.globeButtonSection).click();
} }
describe('senderkeys', function() { async function testTwoMembers() {
let app; const [app, app2] = await common.startAppsAsFriends();
let app2;
this.timeout(60000); await app.client.element(ConversationPage.globeButtonSection).click();
this.slow(30000); await app.client.element(ConversationPage.createClosedGroupButton).click();
beforeEach(async () => { const useSenderKeys = true;
await common.killallElectron();
await common.stopStubSnodeServer();
}); // create group and add new friend
await common.addFriendToNewClosedGroup([app, app2], useSenderKeys);
afterEach(async () => { const text1 = await generateAndSendMessage(app);
await common.stopApp(app);
await common.killallElectron();
await common.stopStubSnodeServer();
});
it('Two member group', async function() { // validate that the message has been added to the message list view
await app2.client.waitForExist(
ConversationPage.existingReceivedMessageText(text1),
5000
);
[app, app2] = await common.startAppsAsFriends(); // Send a message back:
const text2 = await generateAndSendMessage(app2);
await app.client.element(ConversationPage.globeButtonSection).click(); // TODO: fix this. We can send messages back manually, not sure
await app.client.element(ConversationPage.createClosedGroupButton).click(); // why this test fails
await app.client.waitForExist(
ConversationPage.existingReceivedMessageText(text2),
10000
);
}
const useSenderKeys = true; async function testThreeMembers() {
// 1. Make three clients A, B, C
// create group and add new friend const app1Props = {
await common.addFriendToNewClosedGroup([app, app2], useSenderKeys); mnemonic: common.TEST_MNEMONIC1,
displayName: common.TEST_DISPLAY_NAME1,
stubSnode: true,
};
const text1 = await generateAndSendMessage(app); const app2Props = {
mnemonic: common.TEST_MNEMONIC2,
displayName: common.TEST_DISPLAY_NAME2,
stubSnode: true,
};
// validate that the message has been added to the message list view const app3Props = {
await app2.client.waitForExist( mnemonic: common.TEST_MNEMONIC3,
ConversationPage.existingReceivedMessageText(text1), displayName: common.TEST_DISPLAY_NAME3,
5000 stubSnode: true,
); };
// Send a message back: const [app1, app2, app3] = await Promise.all([
const text2 = await generateAndSendMessage(app2); common.startAndStub(app1Props),
common.startAndStubN(app2Props, 2),
common.startAndStubN(app3Props, 3),
]);
// TODO: fix this. We can send messages back manually, not sure // 2. Make A friends with B and C (B and C are not friends)
// why this test fails
// await app.client.waitForExist(
// ConversationPage.existingReceivedMessageText(text2),
// 10000
// );
}); await makeFriendsPlusMessage(app1, [app2, common.TEST_PUBKEY2]);
await makeFriendsPlusMessage(app1, [app3, common.TEST_PUBKEY3]);
it('Three member group: test session requests', async function() { const useSenderKeys = true;
// 1. Make three clients A, B, C await app1.client.element(ConversationPage.globeButtonSection).click();
await app1.client.element(ConversationPage.createClosedGroupButton).click();
const app1Props = { // 3. Add all three to the group
mnemonic: common.TEST_MNEMONIC1,
displayName: common.TEST_DISPLAY_NAME1,
stubSnode: true,
};
const app2Props = {
mnemonic: common.TEST_MNEMONIC2,
displayName: common.TEST_DISPLAY_NAME2,
stubSnode: true,
};
const app3Props = { await common.addFriendToNewClosedGroup([app1, app2, app3], useSenderKeys);
mnemonic: common.TEST_MNEMONIC3,
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) // 4. Test that all members can see the message from app1
const text1 = await generateAndSendMessage(app1);
await common.makeFriends(app1, [app2, common.TEST_PUBKEY2]); await app2.client.waitForExist(
ConversationPage.existingReceivedMessageText(text1),
5000
);
await common.makeFriends(app1, [app3, common.TEST_PUBKEY3]); await app3.client.waitForExist(
ConversationPage.existingReceivedMessageText(text1),
5000
);
// const text1 = await generateAndSendMessage(app1); // TODO: test that B and C can send messages to the group
// // validate that the message has been added to the message list view // const text2 = await generateAndSendMessage(app3);
// await app2.client.waitForExist(
// ConversationPage.existingReceivedMessageText(text1),
// 5000
// );
// // validate that the message has been added to the message list view // await app2.client.waitForExist(
// await app3.client.waitForExist( // ConversationPage.existingReceivedMessageText(text2),
// ConversationPage.existingReceivedMessageText(text1), // 5000
// 5000 // );
// ); }
// TODO: test that B and C can send messages to the group describe('senderkeys', function() {
let app;
this.timeout(600000);
this.slow(40000);
beforeEach(async () => {
await common.killallElectron();
await common.stopStubSnodeServer();
}); });
afterEach(async () => {
await common.stopApp(app);
await common.killallElectron();
await common.stopStubSnodeServer();
});
it('Two member group', testTwoMembers);
it('Three member group: test session requests', testThreeMembers);
}); });

@ -0,0 +1,10 @@
/* global log */
class StubLokiSnodeAPI {
// eslint-disable-next-line class-methods-use-this
async refreshSwarmNodesForPubKey(pubKey) {
log.info('refreshSwarmNodesForPubkey: ', pubKey);
}
}
module.exports = StubLokiSnodeAPI;

@ -1,4 +1,4 @@
/* global clearTimeout, dcodeIO, Buffer, TextDecoder, process */ /* global clearTimeout, dcodeIO, Buffer, TextDecoder, process, log */
const nodeFetch = require('node-fetch'); const nodeFetch = require('node-fetch');
class StubMessageAPI { class StubMessageAPI {
@ -26,6 +26,35 @@ class StubMessageAPI {
); );
} }
async pollForGroupId(groupId, onMessages) {
const get = {
method: 'GET',
};
const res = await nodeFetch(
`${this.baseUrl}/messages?pubkey=${groupId}`,
get
);
try {
const json = await res.json();
const modifiedMessages = json.messages.map(m => {
// eslint-disable-next-line no-param-reassign
m.conversationId = groupId;
return m;
});
onMessages(modifiedMessages || []);
} catch (e) {
log.error('invalid json for GROUP', e);
onMessages([]);
}
setTimeout(() => {
this.pollForGroupId(groupId, onMessages);
}, 1000);
}
async startLongPolling(numConnections, stopPolling, callback) { async startLongPolling(numConnections, stopPolling, callback) {
const ourPubkey = this.ourKey; const ourPubkey = this.ourKey;
@ -36,10 +65,15 @@ class StubMessageAPI {
`${this.baseUrl}/messages?pubkey=${ourPubkey}`, `${this.baseUrl}/messages?pubkey=${ourPubkey}`,
get get
); );
const json = await res.json();
// console.warn('STUBBED polling messages ', json.messages);
callback(json.messages || []); try {
const json = await res.json();
callback(json.messages || []);
} catch (e) {
log.error('invalid json: ', e);
callback([]);
}
// console.warn('STUBBED polling messages ', json.messages);
} }
} }

@ -37,6 +37,7 @@
"test-electron": "yarn grunt test", "test-electron": "yarn grunt test",
"test-integration": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js", "test-integration": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js",
"test-integration-parts": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'registration' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'openGroup' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'addFriends' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'linkDevice' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'closedGroup'", "test-integration-parts": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'registration' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'openGroup' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'addFriends' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'linkDevice' && ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'closedGroup'",
"test-medium-groups": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --timeout 10000 integration_test/integration_test.js --grep 'senderkeys'",
"test-node": "mocha --recursive --exit test/app test/modules ts/test libloki/test/node", "test-node": "mocha --recursive --exit test/app test/modules ts/test libloki/test/node",
"eslint": "eslint --cache .", "eslint": "eslint --cache .",
"eslint-fix": "eslint --fix .", "eslint-fix": "eslint --fix .",

@ -342,6 +342,7 @@ window.LokiMessageAPI = require('./js/modules/loki_message_api');
if (process.env.USE_STUBBED_NETWORK) { if (process.env.USE_STUBBED_NETWORK) {
window.StubMessageAPI = require('./integration_test/stubs/stub_message_api'); window.StubMessageAPI = require('./integration_test/stubs/stub_message_api');
window.StubAppDotNetApi = require('./integration_test/stubs/stub_app_dot_net_api'); window.StubAppDotNetApi = require('./integration_test/stubs/stub_app_dot_net_api');
window.StubLokiSnodeAPI = require('./integration_test/stubs/stub_loki_snode_api');
} }
window.LokiPublicChatAPI = require('./js/modules/loki_public_chat_api'); window.LokiPublicChatAPI = require('./js/modules/loki_public_chat_api');
@ -456,7 +457,7 @@ if (
}; };
/* eslint-enable global-require, import/no-extraneous-dependencies */ /* eslint-enable global-require, import/no-extraneous-dependencies */
window.lokiFeatureFlags = {}; window.lokiFeatureFlags = {};
window.lokiSnodeAPI = {}; // no need stub out each function here window.lokiSnodeAPI = new window.StubLokiSnodeAPI(); // no need stub out each function here
} }
if (config.environment.includes('test-integration')) { if (config.environment.includes('test-integration')) {
window.lokiFeatureFlags = { window.lokiFeatureFlags = {

@ -108,9 +108,10 @@ export class InviteFriendsDialog extends React.Component<Props, State> {
private renderMemberList() { private renderMemberList() {
const members = this.state.friendList; const members = this.state.friendList;
return members.map((member: ContactType) => ( return members.map((member: ContactType, index: number) => (
<SessionMemberListItem <SessionMemberListItem
member={member} member={member}
index={index}
isSelected={false} isSelected={false}
onSelect={(selectedMember: ContactType) => { onSelect={(selectedMember: ContactType) => {
this.onMemberClicked(selectedMember); this.onMemberClicked(selectedMember);

@ -147,9 +147,10 @@ export class UpdateGroupMembersDialog extends React.Component<Props, State> {
private renderMemberList() { private renderMemberList() {
const members = this.state.friendList; const members = this.state.friendList;
return members.map((member: ContactType) => ( return members.map((member: ContactType, index: number) => (
<SessionMemberListItem <SessionMemberListItem
member={member} member={member}
index={index}
isSelected={!member.checkmarked} isSelected={!member.checkmarked}
onSelect={this.onMemberClicked} onSelect={this.onMemberClicked}
onUnselect={this.onMemberClicked} onUnselect={this.onMemberClicked}

@ -123,6 +123,7 @@ export class LeftPaneSectionHeader extends React.Component<Props, State> {
count={notificationCount} count={notificationCount}
size={NotificationCountSize.ON_HEADER} size={NotificationCountSize.ON_HEADER}
onClick={this.props.buttonClicked} onClick={this.props.buttonClicked}
key="notificationCount"
/> />
); );
} }

@ -282,9 +282,10 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
} }
private renderMemberList(members: any) { private renderMemberList(members: any) {
return members.map((member: ContactType) => ( return members.map((member: ContactType, index: number) => (
<SessionMemberListItem <SessionMemberListItem
member={member} member={member}
index={index}
isSelected={false} isSelected={false}
key={member.id} key={member.id}
onSelect={(selectedMember: ContactType) => { onSelect={(selectedMember: ContactType) => {

@ -18,6 +18,7 @@ export interface ContactType {
interface Props { interface Props {
member: ContactType; member: ContactType;
index: number; // index in the list
isSelected: boolean; isSelected: boolean;
onSelect?: any; onSelect?: any;
onUnselect?: any; onUnselect?: any;
@ -54,7 +55,11 @@ export class SessionMemberListItem extends React.Component<Props, State> {
return ( return (
<div <div
className={classNames('session-member-item', isSelected && 'selected')} className={classNames(
`session-member-item-${this.props.index}`,
'session-member-item',
isSelected && 'selected'
)}
onClick={this.handleSelectionAction} onClick={this.handleSelectionAction}
role="button" role="button"
> >

Loading…
Cancel
Save