Add integration test for call decline
This commit is contained in:
parent
cf03754d2f
commit
b95161859e
7 changed files with 158 additions and 10 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
@ -429,7 +429,8 @@ jobs:
|
|||
run: |
|
||||
set -o pipefail
|
||||
xvfb-run --auto-servernum pnpm run test-mock
|
||||
timeout-minutes: 10
|
||||
xvfb-run --auto-servernum pnpm run test-mock-docker
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
NODE_ENV: production
|
||||
DEBUG: mock:test:*
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"test-release": "node ts/scripts/test-release.js",
|
||||
"test-node": "cross-env LANG=en-us electron-mocha --timeout 10000 --main test/fix-linux-gtk.js --file test/setup-test-node.js --recursive ts/test-node",
|
||||
"test-mock": "mocha --require ts/test-mock/setup-ci.js ts/test-mock/**/*_test.js",
|
||||
"test-mock-docker": "mocha --require ts/test-mock/setup-ci.js ts/test-mock/**/*_test.docker.js",
|
||||
"test-eslint": "mocha .eslint/rules/**/*.test.js --ignore-leaks",
|
||||
"test-lint-intl": "ts-node ./build/intl-linter/linter.ts --test",
|
||||
"eslint": "eslint --cache . --cache-strategy content --max-warnings 0",
|
||||
|
@ -222,7 +223,7 @@
|
|||
"@indutny/parallel-prettier": "3.0.0",
|
||||
"@indutny/rezip-electron": "2.0.1",
|
||||
"@napi-rs/canvas": "0.1.61",
|
||||
"@signalapp/mock-server": "13.1.0",
|
||||
"@signalapp/mock-server": "13.2.0",
|
||||
"@storybook/addon-a11y": "8.4.4",
|
||||
"@storybook/addon-actions": "8.4.4",
|
||||
"@storybook/addon-controls": "8.4.4",
|
||||
|
|
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
|
@ -430,8 +430,8 @@ importers:
|
|||
specifier: 0.1.61
|
||||
version: 0.1.61
|
||||
'@signalapp/mock-server':
|
||||
specifier: 13.1.0
|
||||
version: 13.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
specifier: 13.2.0
|
||||
version: 13.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
|
||||
'@storybook/addon-a11y':
|
||||
specifier: 8.4.4
|
||||
version: 8.4.4(storybook@8.4.4(bufferutil@4.0.9)(prettier@3.3.3)(utf-8-validate@5.0.10))
|
||||
|
@ -2770,8 +2770,8 @@ packages:
|
|||
'@signalapp/libsignal-client@0.76.3':
|
||||
resolution: {integrity: sha512-Ht8XtdsSvgiCb8ftUYE9DaLcWy0vltrj9cQ2sfy+DGUayE1k2njicNhB2RKOfQV2Wb/1Cl0WxVZP/NlXRo2+jA==}
|
||||
|
||||
'@signalapp/mock-server@13.1.0':
|
||||
resolution: {integrity: sha512-CuDNLNEBMzwIs5jr7Lx9F4YFoRD62s7WgPGtm3qpaggixSQtabjMC7AKSR0xvaHcZpYZtBU5jcGK8Roguo9nuw==}
|
||||
'@signalapp/mock-server@13.2.0':
|
||||
resolution: {integrity: sha512-f5uxzsIwPmkevX5ycCRWgqy/VCbvj/dUA8yGWlfO2hFc62UZ4FaOY0n5YFMOpQgp5eMO6TMgr1KqIR/uAxfkIg==}
|
||||
|
||||
'@signalapp/parchment-cjs@3.0.1':
|
||||
resolution: {integrity: sha512-hSBMQ1M7wE4GcC8ZeNtvpJF+DAJg3eIRRf1SiHS3I3Algav/sgJJNm6HIYm6muHuK7IJmuEjkL3ILSXgmu0RfQ==}
|
||||
|
@ -12466,7 +12466,7 @@ snapshots:
|
|||
type-fest: 4.26.1
|
||||
uuid: 11.0.2
|
||||
|
||||
'@signalapp/mock-server@13.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
|
||||
'@signalapp/mock-server@13.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
|
||||
dependencies:
|
||||
'@indutny/parallel-prettier': 3.0.0(prettier@3.3.3)
|
||||
'@signalapp/libsignal-client': 0.60.2
|
||||
|
|
110
ts/test-mock/calling/callMessages_test.docker.ts
Normal file
110
ts/test-mock/calling/callMessages_test.docker.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { StorageState } from '@signalapp/mock-server';
|
||||
import { expect } from 'playwright/test';
|
||||
import * as durations from '../../util/durations';
|
||||
import type { App } from '../playwright';
|
||||
import { Bootstrap } from '../bootstrap';
|
||||
import { runTurnInContainer, tearDownTurnContainer } from './helpers';
|
||||
|
||||
describe('callMessages', function callMessages(this: Mocha.Suite) {
|
||||
this.timeout(durations.MINUTE);
|
||||
|
||||
let bootstrap1: Bootstrap;
|
||||
let bootstrap2: Bootstrap;
|
||||
let app1: App;
|
||||
let app2: App;
|
||||
|
||||
beforeEach(async () => {
|
||||
runTurnInContainer();
|
||||
|
||||
bootstrap1 = new Bootstrap();
|
||||
await bootstrap1.init();
|
||||
|
||||
bootstrap2 = new Bootstrap({ server: bootstrap1.server });
|
||||
await bootstrap2.init();
|
||||
|
||||
let state1 = StorageState.getEmpty();
|
||||
state1 = state1.updateAccount({
|
||||
profileKey: bootstrap1.phone.profileKey.serialize(),
|
||||
});
|
||||
|
||||
state1 = state1.addContact(bootstrap2.phone, {
|
||||
whitelisted: true,
|
||||
profileKey: bootstrap2.phone.profileKey.serialize(),
|
||||
givenName: 'Contact2',
|
||||
});
|
||||
|
||||
state1 = state1.pin(bootstrap2.phone);
|
||||
|
||||
await bootstrap1.phone.setStorageState(state1);
|
||||
|
||||
app1 = await bootstrap1.link();
|
||||
|
||||
let state2 = StorageState.getEmpty();
|
||||
state2 = state2.updateAccount({
|
||||
profileKey: bootstrap2.phone.profileKey.serialize(),
|
||||
});
|
||||
|
||||
state2 = state2.addContact(bootstrap1.phone, {
|
||||
whitelisted: true,
|
||||
profileKey: bootstrap1.phone.profileKey.serialize(),
|
||||
givenName: 'Contact1',
|
||||
});
|
||||
|
||||
state2 = state2.pin(bootstrap1.phone);
|
||||
await bootstrap2.phone.setStorageState(state2);
|
||||
|
||||
app2 = await bootstrap2.link();
|
||||
});
|
||||
|
||||
afterEach(async function after(this: Mocha.Context) {
|
||||
tearDownTurnContainer();
|
||||
|
||||
if (!bootstrap1) {
|
||||
return;
|
||||
}
|
||||
await bootstrap1.maybeSaveLogs(this.currentTest, app1);
|
||||
await bootstrap2.maybeSaveLogs(this.currentTest, app2);
|
||||
|
||||
await app2.close();
|
||||
await app1.close();
|
||||
|
||||
await bootstrap2.teardown();
|
||||
await bootstrap1.teardown();
|
||||
});
|
||||
|
||||
it('can call and decline a call', async () => {
|
||||
const window1 = await app1.getWindow();
|
||||
const leftPane1 = window1.locator('#LeftPane');
|
||||
|
||||
await leftPane1
|
||||
.locator(`[data-testid="${bootstrap2.phone.device.aci}"]`)
|
||||
.click();
|
||||
// Try to start a call
|
||||
await window1.locator('.module-ConversationHeader__button--audio').click();
|
||||
const window1Permissions = await app1.waitForWindow();
|
||||
await window1Permissions.getByText('Allow Access').click();
|
||||
await window1
|
||||
.locator('.CallingLobbyJoinButton')
|
||||
.and(window1.locator('button:visible'))
|
||||
.click();
|
||||
|
||||
const window2 = await app2.getWindow();
|
||||
|
||||
// Only wait for 3 seconds to make sure that this succeeded properly rather
|
||||
// than timing out after ~10 seconds and using a direct connection
|
||||
await window2
|
||||
.locator('.IncomingCallBar__button--decline')
|
||||
.click({ timeout: 3000 });
|
||||
|
||||
await expect(
|
||||
window1.locator('.module-calling__modal-container')
|
||||
).toBeEmpty();
|
||||
|
||||
await expect(
|
||||
window2.locator('.module-calling__modal-container')
|
||||
).toBeEmpty();
|
||||
});
|
||||
});
|
32
ts/test-mock/calling/helpers.ts
Normal file
32
ts/test-mock/calling/helpers.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as child from 'child_process';
|
||||
import createDebug from 'debug';
|
||||
|
||||
const debug = createDebug('mock:test:calling:helpers');
|
||||
|
||||
export function runTurnInContainer(): void {
|
||||
tearDownTurnContainer();
|
||||
const result = child.spawnSync('docker', [
|
||||
'run',
|
||||
'--name',
|
||||
'coturn',
|
||||
'-d',
|
||||
'--network=host',
|
||||
'coturn/coturn',
|
||||
]);
|
||||
debug(
|
||||
'create coturn: signal: ',
|
||||
result.signal,
|
||||
' status: ',
|
||||
result.status,
|
||||
'stderr: ',
|
||||
result.stderr?.toString()
|
||||
);
|
||||
}
|
||||
|
||||
export function tearDownTurnContainer(): void {
|
||||
debug('tearDownTurnContainer');
|
||||
child.spawnSync('docker', ['rm', '--force', '--volumes', 'coturn']);
|
||||
}
|
|
@ -7,7 +7,7 @@ import type { App } from '../playwright';
|
|||
import { Bootstrap } from '../bootstrap';
|
||||
import { typeIntoInput, waitForEnabledComposer } from '../helpers';
|
||||
|
||||
describe('callMessages', function (this: Mocha.Suite) {
|
||||
describe('twoClients', function twoClients(this: Mocha.Suite) {
|
||||
this.timeout(durations.MINUTE);
|
||||
|
||||
let bootstrap1: Bootstrap;
|
||||
|
@ -56,12 +56,12 @@ describe('callMessages', function (this: Mocha.Suite) {
|
|||
app2 = await bootstrap2.link();
|
||||
});
|
||||
|
||||
afterEach(async function (this: Mocha.Context) {
|
||||
afterEach(async function after(this: Mocha.Context) {
|
||||
if (!bootstrap1) {
|
||||
return;
|
||||
}
|
||||
await bootstrap2.maybeSaveLogs(this.currentTest, app2);
|
||||
await bootstrap1.maybeSaveLogs(this.currentTest, app1);
|
||||
await bootstrap2.maybeSaveLogs(this.currentTest, app2);
|
||||
|
||||
await app2.close();
|
||||
await app1.close();
|
|
@ -145,6 +145,10 @@ export class App extends EventEmitter {
|
|||
return this.#waitForEvent('storageServiceComplete');
|
||||
}
|
||||
|
||||
public async waitForWindow(): Promise<Page> {
|
||||
return this.#app.waitForEvent('window');
|
||||
}
|
||||
|
||||
public async waitForManifestVersion(version: number): Promise<void> {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue