Initial GV2 mock tests
This commit is contained in:
parent
34a27accfc
commit
e5ba00b798
7 changed files with 192 additions and 11 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -157,7 +157,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
SIGNAL_ENV: production
|
SIGNAL_ENV: production
|
||||||
|
|
||||||
storage-service:
|
mock-tests:
|
||||||
needs: lint
|
needs: lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository == 'signalapp/Signal-Desktop-Private' }}
|
if: ${{ github.repository == 'signalapp/Signal-Desktop-Private' }}
|
||||||
|
@ -198,11 +198,11 @@ jobs:
|
||||||
- name: Bundle
|
- name: Bundle
|
||||||
run: yarn build:webpack
|
run: yarn build:webpack
|
||||||
|
|
||||||
- name: Run storage service tests
|
- name: Run mock server tests
|
||||||
run: |
|
run: |
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
xvfb-run --auto-servernum yarn test-mock
|
xvfb-run --auto-servernum yarn test-mock
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
env:
|
env:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
DEBUG: mock:test-storage
|
DEBUG: mock:test:*
|
||||||
|
|
|
@ -189,7 +189,7 @@
|
||||||
"@chanzuckerberg/axe-storybook-testing": "3.0.2",
|
"@chanzuckerberg/axe-storybook-testing": "3.0.2",
|
||||||
"@electron/fuses": "1.5.0",
|
"@electron/fuses": "1.5.0",
|
||||||
"@mixer/parallel-prettier": "2.0.1",
|
"@mixer/parallel-prettier": "2.0.1",
|
||||||
"@signalapp/mock-server": "1.3.0",
|
"@signalapp/mock-server": "1.5.0-rc.3",
|
||||||
"@storybook/addon-actions": "5.1.11",
|
"@storybook/addon-actions": "5.1.11",
|
||||||
"@storybook/addon-knobs": "5.1.11",
|
"@storybook/addon-knobs": "5.1.11",
|
||||||
"@storybook/addons": "5.1.11",
|
"@storybook/addons": "5.1.11",
|
||||||
|
|
|
@ -45,7 +45,7 @@ import type {
|
||||||
} from './textsecure/Types.d';
|
} from './textsecure/Types.d';
|
||||||
import type { RemoveAllConfiguration } from './types/RemoveAllConfiguration';
|
import type { RemoveAllConfiguration } from './types/RemoveAllConfiguration';
|
||||||
import type { UUIDStringType } from './types/UUID';
|
import type { UUIDStringType } from './types/UUID';
|
||||||
import { UUID } from './types/UUID';
|
import { UUID, UUIDKind } from './types/UUID';
|
||||||
import type { Address } from './types/Address';
|
import type { Address } from './types/Address';
|
||||||
import type { QualifiedAddressStringType } from './types/QualifiedAddress';
|
import type { QualifiedAddressStringType } from './types/QualifiedAddress';
|
||||||
import { QualifiedAddress } from './types/QualifiedAddress';
|
import { QualifiedAddress } from './types/QualifiedAddress';
|
||||||
|
@ -1682,7 +1682,11 @@ export class SignalProtocolStore extends EventsMixin {
|
||||||
const identityRecord = await this.getOrMigrateIdentityRecord(uuid);
|
const identityRecord = await this.getOrMigrateIdentityRecord(uuid);
|
||||||
const id = uuid.toString();
|
const id = uuid.toString();
|
||||||
|
|
||||||
window.ConversationController.getOrCreate(id, 'private');
|
// When saving a PNI identity - don't create a separate conversation
|
||||||
|
const uuidKind = window.textsecure.storage.user.getOurUuidKind(uuid);
|
||||||
|
if (uuidKind !== UUIDKind.PNI) {
|
||||||
|
window.ConversationController.getOrCreate(id, 'private');
|
||||||
|
}
|
||||||
|
|
||||||
const updates: Partial<IdentityKeyType> = {
|
const updates: Partial<IdentityKeyType> = {
|
||||||
...identityRecord,
|
...identityRecord,
|
||||||
|
|
|
@ -13,6 +13,8 @@ import { MAX_READ_KEYS as MAX_STORAGE_READ_KEYS } from '../services/storageConst
|
||||||
import * as durations from '../util/durations';
|
import * as durations from '../util/durations';
|
||||||
import { App } from './playwright';
|
import { App } from './playwright';
|
||||||
|
|
||||||
|
export { App };
|
||||||
|
|
||||||
const debug = createDebug('mock:bootstrap');
|
const debug = createDebug('mock:bootstrap');
|
||||||
|
|
||||||
const ELECTRON = path.join(
|
const ELECTRON = path.join(
|
||||||
|
|
175
ts/test-mock/gv2/create_test.ts
Normal file
175
ts/test-mock/gv2/create_test.ts
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
// Copyright 2022 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
import type { PrimaryDevice, Group } from '@signalapp/mock-server';
|
||||||
|
import { StorageState, Proto } from '@signalapp/mock-server';
|
||||||
|
import createDebug from 'debug';
|
||||||
|
|
||||||
|
import * as durations from '../../util/durations';
|
||||||
|
import { Bootstrap } from '../bootstrap';
|
||||||
|
import type { App } from '../bootstrap';
|
||||||
|
|
||||||
|
export const debug = createDebug('mock:test:gv2');
|
||||||
|
|
||||||
|
describe('gv2', function needsName() {
|
||||||
|
this.timeout(durations.MINUTE);
|
||||||
|
|
||||||
|
let bootstrap: Bootstrap;
|
||||||
|
let app: App;
|
||||||
|
let pniContact: PrimaryDevice;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
bootstrap = new Bootstrap();
|
||||||
|
await bootstrap.init();
|
||||||
|
|
||||||
|
const { phone, contacts, server } = bootstrap;
|
||||||
|
|
||||||
|
let state = StorageState.getEmpty();
|
||||||
|
|
||||||
|
state = state.updateAccount({
|
||||||
|
profileKey: phone.profileKey.serialize(),
|
||||||
|
e164: phone.device.number,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [first] = contacts;
|
||||||
|
state = state.addContact(first, {
|
||||||
|
identityState: Proto.ContactRecord.IdentityState.VERIFIED,
|
||||||
|
whitelisted: true,
|
||||||
|
|
||||||
|
identityKey: first.publicKey.serialize(),
|
||||||
|
profileKey: first.profileKey.serialize(),
|
||||||
|
});
|
||||||
|
|
||||||
|
pniContact = await server.createPrimaryDevice({
|
||||||
|
profileName: 'My name is PNI',
|
||||||
|
});
|
||||||
|
state = state.addContact(pniContact, {
|
||||||
|
identityState: Proto.ContactRecord.IdentityState.VERIFIED,
|
||||||
|
whitelisted: true,
|
||||||
|
|
||||||
|
identityKey: pniContact.pniPublicKey.serialize(),
|
||||||
|
|
||||||
|
// Give PNI as the uuid!
|
||||||
|
serviceUuid: pniContact.device.pni,
|
||||||
|
givenName: 'PNI Contact',
|
||||||
|
});
|
||||||
|
|
||||||
|
await phone.setStorageState(state);
|
||||||
|
|
||||||
|
app = await bootstrap.link();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await app.close();
|
||||||
|
await bootstrap.teardown();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create group and modify it', async () => {
|
||||||
|
const { phone, contacts } = bootstrap;
|
||||||
|
const [first] = contacts;
|
||||||
|
|
||||||
|
let state = await phone.expectStorageState('initial state');
|
||||||
|
|
||||||
|
const window = await app.getWindow();
|
||||||
|
|
||||||
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
const conversationStack = window.locator('.conversation-stack');
|
||||||
|
|
||||||
|
debug('clicking compose and "New group" buttons');
|
||||||
|
|
||||||
|
await leftPane.locator('.module-main-header__compose-icon').click();
|
||||||
|
|
||||||
|
await leftPane
|
||||||
|
.locator('_react=BaseConversationListItem[title = "New group"]')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
debug('inviting ACI member');
|
||||||
|
|
||||||
|
await leftPane
|
||||||
|
.locator(
|
||||||
|
'_react=BaseConversationListItem' +
|
||||||
|
`[title = ${JSON.stringify(first.profileName)}]`
|
||||||
|
)
|
||||||
|
.click();
|
||||||
|
|
||||||
|
debug('inviting PNI member');
|
||||||
|
|
||||||
|
await leftPane
|
||||||
|
.locator('.module-left-pane__compose-search-form__input')
|
||||||
|
.type('PNI');
|
||||||
|
|
||||||
|
await leftPane
|
||||||
|
.locator('_react=BaseConversationListItem[title = "PNI Contact"]')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await leftPane
|
||||||
|
.locator('.module-left-pane__footer button >> "Next"')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
debug('entering group title');
|
||||||
|
|
||||||
|
await leftPane.type('My group');
|
||||||
|
|
||||||
|
await leftPane
|
||||||
|
.locator('.module-left-pane__footer button >> "Create"')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
debug('waiting for invitation modal');
|
||||||
|
|
||||||
|
{
|
||||||
|
const modal = window.locator(
|
||||||
|
'.module-GroupDialog:has-text("Invitation sent")'
|
||||||
|
);
|
||||||
|
|
||||||
|
await modal.locator('button >> "Okay"').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('waiting for group data from storage service');
|
||||||
|
|
||||||
|
let group: Group;
|
||||||
|
{
|
||||||
|
state = await phone.waitForStorageState({ after: state });
|
||||||
|
|
||||||
|
const groups = await phone.getAllGroups(state);
|
||||||
|
assert.strictEqual(groups.length, 1);
|
||||||
|
|
||||||
|
[group] = groups;
|
||||||
|
assert.strictEqual(group.title, 'My group');
|
||||||
|
assert.strictEqual(group.revision, 0);
|
||||||
|
assert.strictEqual(group.state.members?.length, 2);
|
||||||
|
assert.strictEqual(group.state.membersPendingProfileKey?.length, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('opening group settings');
|
||||||
|
|
||||||
|
await conversationStack
|
||||||
|
.locator('button.module-ConversationHeader__button--more')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await conversationStack
|
||||||
|
.locator('.react-contextmenu-item >> "Group settings"')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
debug('editing group title');
|
||||||
|
{
|
||||||
|
const detailsHeader = conversationStack.locator(
|
||||||
|
'_react=ConversationDetailsHeader'
|
||||||
|
);
|
||||||
|
detailsHeader.locator('button >> "My group"').click();
|
||||||
|
|
||||||
|
const modal = window.locator('.module-Modal:has-text("Edit group")');
|
||||||
|
|
||||||
|
// Group title should be immediately focused.
|
||||||
|
await modal.type(' (v2)');
|
||||||
|
|
||||||
|
await modal.locator('button >> "Save"').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('waiting for the second group update');
|
||||||
|
group = await phone.waitForGroupUpdate(group);
|
||||||
|
|
||||||
|
assert.strictEqual(group.title, 'My group (v2)');
|
||||||
|
assert.strictEqual(group.revision, 1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -8,7 +8,7 @@ import { App } from '../playwright';
|
||||||
import { Bootstrap } from '../bootstrap';
|
import { Bootstrap } from '../bootstrap';
|
||||||
import type { BootstrapOptions } from '../bootstrap';
|
import type { BootstrapOptions } from '../bootstrap';
|
||||||
|
|
||||||
export const debug = createDebug('mock:test-storage');
|
export const debug = createDebug('mock:test:storage');
|
||||||
|
|
||||||
export { App, Bootstrap };
|
export { App, Bootstrap };
|
||||||
|
|
||||||
|
|
|
@ -1372,10 +1372,10 @@
|
||||||
node-gyp-build "^4.2.3"
|
node-gyp-build "^4.2.3"
|
||||||
uuid "^8.3.0"
|
uuid "^8.3.0"
|
||||||
|
|
||||||
"@signalapp/mock-server@1.3.0":
|
"@signalapp/mock-server@1.5.0-rc.3":
|
||||||
version "1.3.0"
|
version "1.5.0-rc.3"
|
||||||
resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-1.3.0.tgz#288a994c4f5c26c4c2680289af471e08746ac353"
|
resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-1.5.0-rc.3.tgz#2bb0d80555e84740bf7c110386da6ec9200785d5"
|
||||||
integrity sha512-ix3GO0lytE02nWLj1fKY3UhKM3lCynhvF2LVNHEiMen9wurVyb8mVcmBDb9zRBi63tZmFLAq/IQEYrc1OK3ZJQ==
|
integrity sha512-ShoMNL4XHtvBXDrzgQD2xXpqTDjzzVegQjlA15c9H285Z1qQJhQfnu3A13YDmAYqZ0LbXM8Re5AnWtZJYdGSCA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@signalapp/libsignal-client" "0.15.0"
|
"@signalapp/libsignal-client" "0.15.0"
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
|
|
Loading…
Reference in a new issue