Faster CI runs

This commit is contained in:
Fedor Indutny 2025-08-07 10:33:21 -07:00 committed by GitHub
commit ec8d6a7359
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 242 additions and 207 deletions

View file

@ -14,6 +14,52 @@ on:
jobs:
linux:
strategy:
matrix:
metric:
- startup
- send
- groupSend
- largeGroupSendWithBlocks
- largeGroupSend
- convoOpen
- callHistorySearch
- backup
include:
- metric: startup
script: ts/test-mock/benchmarks/startup_bench.js
runCount: 10
- metric: send
script: ts/test-mock/benchmarks/send_bench.js
runCount: 100
- metric: groupSend
script: ts/test-mock/benchmarks/group_send_bench.js
runCount: 100
conversationSize: 500
- metric: largeGroupSendWithBlocks
script: ts/test-mock/benchmarks/group_send_bench.js
runCount: 50
conversationSize: 500
groupSize: 500
contactCount: 500
blockedCount: 10
discardCount: 2
- metric: largeGroupSend
script: ts/test-mock/benchmarks/group_send_bench.js
runCount: 20
conversationSize: 50
groupSize: 500
contactCount: 500
discardCount: 2
- metric: convoOpen
script: ts/test-mock/benchmarks/group_send_bench.js
runCount: 100
- metric: callHistorySearch
script: ts/test-mock/benchmarks/call_history_search_bench.js
runCount: 100
- metric: backup
script: ts/test-mock/benchmarks/backup_bench.js
runs-on: ubuntu-22.04-8-cores
if: ${{ github.repository == 'signalapp/Signal-Desktop-Private' && (!github.event.schedule || github.ref == 'refs/heads/main') }}
timeout-minutes: 30
@ -72,132 +118,22 @@ jobs:
run: |
echo "MAX_CYCLES=2" >> "$GITHUB_ENV"
- name: Run startup benchmarks
- name: Run ${{ matrix.metric }}
run: |
set -o pipefail
xvfb-run --auto-servernum node ts/test-mock/benchmarks/startup_bench.js |
tee benchmark-startup.log
xvfb-run --auto-servernum node ${{ matrix.script }} | tee benchmark.log
timeout-minutes: 10
env:
NODE_ENV: production
RUN_COUNT: 10
ELECTRON_ENABLE_STACK_DUMPING: on
DEBUG: 'mock:benchmarks'
ARTIFACTS_DIR: artifacts/startup
- name: Run send benchmarks
run: |
set -o pipefail
rm -rf /tmp/mock
xvfb-run --auto-servernum node ts/test-mock/benchmarks/send_bench.js |
tee benchmark-send.log
timeout-minutes: 10
env:
NODE_ENV: production
RUN_COUNT: 100
ELECTRON_ENABLE_STACK_DUMPING: on
# DEBUG: 'mock:benchmarks'
ARTIFACTS_DIR: artifacts/send
- name: Run group send benchmarks
run: |
set -o pipefail
rm -rf /tmp/mock
xvfb-run --auto-servernum node \
ts/test-mock/benchmarks/group_send_bench.js | \
tee benchmark-group-send.log
timeout-minutes: 10
env:
NODE_ENV: production
RUN_COUNT: 100
CONVERSATION_SIZE: 500
ELECTRON_ENABLE_STACK_DUMPING: on
# DEBUG: 'mock:benchmarks'
ARTIFACTS_DIR: artifacts/group-send
- name: Run large group send benchmarks with blocks
run: |
set -o pipefail
rm -rf /tmp/mock
xvfb-run --auto-servernum node \
ts/test-mock/benchmarks/group_send_bench.js | \
tee benchmark-large-group-send-with-blocks.log
timeout-minutes: 10
env:
NODE_ENV: production
GROUP_SIZE: 500
CONTACT_COUNT: 500
BLOCKED_COUNT: 10
DISCARD_COUNT: 2
RUN_COUNT: 50
CONVERSATION_SIZE: 500
ELECTRON_ENABLE_STACK_DUMPING: on
# DEBUG: 'mock:benchmarks'
ARTIFACTS_DIR: artifacts/group-send
- name: Run large group send benchmarks with delivery receipts
run: |
set -o pipefail
rm -rf /tmp/mock
xvfb-run --auto-servernum node \
ts/test-mock/benchmarks/group_send_bench.js | \
tee benchmark-large-group-send.log
timeout-minutes: 10
env:
NODE_ENV: production
GROUP_SIZE: 500
CONTACT_COUNT: 500
GROUP_DELIVERY_RECEIPTS: 500
DISCARD_COUNT: 2
RUN_COUNT: 20
CONVERSATION_SIZE: 50
ELECTRON_ENABLE_STACK_DUMPING: on
# DEBUG: 'mock:benchmarks'
ARTIFACTS_DIR: artifacts/large-group-send
- name: Run conversation open benchmarks
run: |
set -o pipefail
rm -rf /tmp/mock
xvfb-run --auto-servernum node \
ts/test-mock/benchmarks/convo_open_bench.js | \
tee benchmark-convo-open.log
timeout-minutes: 10
env:
NODE_ENV: production
RUN_COUNT: 100
ELECTRON_ENABLE_STACK_DUMPING: on
# DEBUG: 'mock:benchmarks'
ARTIFACTS_DIR: artifacts/convo-open
- name: Run call history search benchmarks
run: |
set -o pipefail
rm -rf /tmp/mock
xvfb-run --auto-servernum node \
ts/test-mock/benchmarks/call_history_search_bench.js | \
tee benchmark-call-history-search.log
timeout-minutes: 10
env:
NODE_ENV: production
RUN_COUNT: 100
ELECTRON_ENABLE_STACK_DUMPING: on
# DEBUG: 'mock:benchmarks'
ARTIFACTS_DIR: artifacts/call-history-search
- name: Run backup benchmarks
run: |
set -o pipefail
rm -rf /tmp/mock
xvfb-run --auto-servernum node \
ts/test-mock/benchmarks/backup_bench.js | \
tee benchmark-backup.log
timeout-minutes: 10
env:
NODE_ENV: production
ELECTRON_ENABLE_STACK_DUMPING: on
# DEBUG: 'mock:benchmarks'
ARTIFACTS_DIR: artifacts/backup-bench
ARTIFACTS_DIR: artifacts/${{ matrix.metric }}
GROUP_SIZE: ${{ matrix.groupSize }}
CONTACT_COUNT: ${{ matrix.contactCount }}
BLOCKED_COUNT: ${{ matrix.blockedCount }}
DISCARD_COUNT: ${{ matrix.discardCount }}
RUN_COUNT: ${{ matrix.runCount }}
CONVERSATION_SIZE: ${{ matrix.conversationSize }}
- name: Upload benchmark logs on failure
if: failure()
@ -222,13 +158,6 @@ jobs:
- name: Publish to DataDog
working-directory: benchmark-results
run: |
node ./bin/publish.js ../benchmark-startup.log desktop.ci.performance.startup
node ./bin/publish.js ../benchmark-send.log desktop.ci.performance.send
node ./bin/publish.js ../benchmark-group-send.log desktop.ci.performance.groupSend
node ./bin/publish.js ../benchmark-large-group-send-with-blocks.log desktop.ci.performance.largeGroupSendWithBlocks
node ./bin/publish.js ../benchmark-large-group-send.log desktop.ci.performance.largeGroupSend
node ./bin/publish.js ../benchmark-convo-open.log desktop.ci.performance.convoOpen
node ./bin/publish.js ../benchmark-call-history-search.log desktop.ci.performance.callHistorySearch
node ./bin/publish.js ../benchmark-backup.log desktop.ci.performance.backup
node ./bin/publish.js ../benchmark.log destop.ci.performance.${{ matrix.metric }}
env:
DD_API_KEY: ${{ secrets.DATADOG_API_KEY }}

View file

@ -370,6 +370,11 @@ jobs:
mock-tests:
needs: lint
strategy:
matrix:
workerIndex: [0, 1, 2, 3]
runs-on: ubuntu-22.04-8-cores
if: ${{ github.repository == 'signalapp/Signal-Desktop-Private' }}
timeout-minutes: 30
@ -429,12 +434,24 @@ jobs:
run: |
set -o pipefail
xvfb-run --auto-servernum pnpm run test-mock
timeout-minutes: 15
env:
NODE_ENV: production
DEBUG: mock:test:*
ARTIFACTS_DIR: artifacts/mock
WORKER_INDEX: ${{ matrix.workerIndex }}
WORKER_COUNT: 4
- name: Run docker mock server tests
if: ${{ matrix.workerIndex == 0 }}
run: |
set -o pipefail
xvfb-run --auto-servernum pnpm run test-mock-docker
timeout-minutes: 15
env:
NODE_ENV: production
DEBUG: mock:test:*
ARTIFACTS_DIR: artifacts/startup
ARTIFACTS_DIR: artifacts/mock-docker
- name: Upload mock server test logs on failure
if: failure()

View file

@ -49,7 +49,7 @@
"test-electron": "node ts/scripts/test-electron.js",
"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": "node ts/scripts/mocha-separator.js --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",
@ -336,7 +336,7 @@
"npm-run-all": "4.1.5",
"p-limit": "3.1.0",
"pixelmatch": "5.3.0",
"playwright": "1.45.0",
"playwright": "1.54.2",
"pngjs": "7.0.0",
"postcss": "8.5.3",
"postcss-loader": "8.1.1",

28
pnpm-lock.yaml generated
View file

@ -745,8 +745,8 @@ importers:
specifier: 5.3.0
version: 5.3.0
playwright:
specifier: 1.45.0
version: 1.45.0
specifier: 1.54.2
version: 1.54.2
pngjs:
specifier: 7.0.0
version: 7.0.0
@ -8578,18 +8578,18 @@ packages:
resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==}
engines: {node: '>=14.16'}
playwright-core@1.45.0:
resolution: {integrity: sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==}
engines: {node: '>=18'}
hasBin: true
playwright-core@1.50.1:
resolution: {integrity: sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==}
engines: {node: '>=18'}
hasBin: true
playwright@1.45.0:
resolution: {integrity: sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==}
playwright-core@1.54.2:
resolution: {integrity: sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==}
engines: {node: '>=18'}
hasBin: true
playwright@1.54.2:
resolution: {integrity: sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==}
engines: {node: '>=18'}
hasBin: true
@ -14312,7 +14312,7 @@ snapshots:
jest-serializer-html: 7.1.0
jest-watch-typeahead: 2.2.2(jest@29.7.0(@types/node@20.17.6)(ts-node@10.9.2(@swc/core@1.10.16(@swc/helpers@0.5.15))(@types/node@20.17.6)(typescript@5.6.3)))
nyc: 15.1.0
playwright: 1.45.0
playwright: 1.54.2
storybook: 8.4.4(bufferutil@4.0.9)(prettier@3.3.3)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- '@swc/helpers'
@ -20302,13 +20302,13 @@ snapshots:
dependencies:
find-up: 6.3.0
playwright-core@1.45.0: {}
playwright-core@1.50.1: {}
playwright@1.45.0:
playwright-core@1.54.2: {}
playwright@1.54.2:
dependencies:
playwright-core: 1.45.0
playwright-core: 1.54.2
optionalDependencies:
fsevents: 2.3.2

View file

@ -61,6 +61,11 @@ const fn = script.runInThisContext({
// See `ts/scripts/generate-preload-cache.ts`
if (process.env.GENERATE_PRELOAD_CACHE) {
// Use hottest cache possible in CI
if (process.env.CI) {
fn(require, __dirname);
window.startApp();
}
writeFileSync(cachePath, script.createCachedData());
ipcRenderer.send('shutdown');
} else {

View file

@ -351,6 +351,16 @@ export class ConversationController {
if (isGroupV1(conversation.attributes)) {
maybeDeriveGroupV2Id(conversation);
}
// If conversation does not have pre-existing storageID and is not our
// own (that we create on link), it might need to be uploaded to storage
// service.
if (conversation.attributes.storageID == null) {
StorageService.storageServiceUploadJob({
reason: 'new conversation',
});
}
await saveConversation(conversation.attributes);
} catch (error) {
log.error(
@ -1221,11 +1231,15 @@ export class ConversationController {
log.warn(`${logId}: Update messages table`);
await migrateConversationMessages(obsoleteId, currentId);
log.warn(`${logId}: Emit refreshConversation event to close old/open new`);
window.Whisper.events.trigger('refreshConversation', {
newId: currentId,
oldId: obsoleteId,
});
if (
window.reduxStore.getState().conversations.selectedConversationId ===
obsoleteId
) {
log.warn(`${logId}: opening new conversation`);
window.reduxActions.conversations.showConversation({
conversationId: currentId,
});
}
log.warn(
`${logId}: Eliminate old conversation from ConversationController lookups`
@ -1236,8 +1250,10 @@ export class ConversationController {
current.captureChange('combineConversations');
drop(current.updateLastMessage());
const state = window.reduxStore.getState();
if (state.conversations.selectedConversationId === current.id) {
if (
window.reduxStore.getState().conversations.selectedConversationId ===
current.id
) {
// TODO: DESKTOP-4807
drop(current.loadNewestMessages(undefined, undefined));
}

View file

@ -1457,6 +1457,7 @@ export async function startApp(): Promise<void> {
await StorageService.runStorageServiceSyncJob({
reason: andSync,
});
StorageService.runStorageServiceSyncJob.flush();
}
}

View file

@ -318,14 +318,15 @@ export function ConversationList({
);
const renderRow: ListRowRenderer = useCallback(
({ key, index, style }) => {
({ key: providedKey, index, style }) => {
const row = getRow(index);
if (!row) {
assertDev(false, `Expected a row at index ${index}`);
return <div key={key} style={style} />;
return <div key={providedKey} style={style} />;
}
let result: ReactNode;
let key: string;
switch (row.type) {
case RowType.ArchiveButton:
result = (
@ -344,9 +345,11 @@ export function ConversationList({
</span>
</button>
);
key = 'archive';
break;
case RowType.Blank:
result = undefined;
key = `blank:${providedKey}`;
break;
case RowType.Contact: {
const { isClickable = true, hasContextMenu = false } = row;
@ -368,6 +371,7 @@ export function ConversationList({
onRemove={isClickable ? removeConversation : undefined}
/>
);
key = `contact:${row.contact.id}`;
break;
}
case RowType.ContactCheckbox:
@ -382,6 +386,7 @@ export function ConversationList({
theme={theme}
/>
);
key = `contact-checkbox:${row.contact.id}`;
break;
case RowType.ClearFilterButton:
result = (
@ -400,6 +405,7 @@ export function ConversationList({
</Button>
</div>
);
key = 'clear-filter';
break;
case RowType.PhoneNumberCheckbox:
result = (
@ -419,6 +425,7 @@ export function ConversationList({
theme={theme}
/>
);
key = `phone-number-checkbox:${row.phoneNumber.e164}`;
break;
case RowType.UsernameCheckbox:
result = (
@ -438,6 +445,7 @@ export function ConversationList({
theme={theme}
/>
);
key = `username-checkbox:${row.username}`;
break;
case RowType.GenericCheckbox:
result = (
@ -453,6 +461,7 @@ export function ConversationList({
clickable
/>
);
key = `generic-checkbox:${providedKey}`;
break;
case RowType.Conversation: {
const itemProps = pick(row.conversation, [
@ -486,6 +495,7 @@ export function ConversationList({
'serviceId',
]);
const { badges, title, unreadCount, lastMessage } = itemProps;
key = `conversation:${itemProps.id}`;
result = (
<ConversationListItem
{...itemProps}
@ -514,6 +524,7 @@ export function ConversationList({
onClick={showChooseGroupMembers}
/>
);
key = 'create-new-group';
break;
case RowType.FindByUsername:
result = (
@ -523,6 +534,7 @@ export function ConversationList({
onClick={showFindByUsername}
/>
);
key = 'find-by-username';
break;
case RowType.FindByPhoneNumber:
result = (
@ -532,6 +544,7 @@ export function ConversationList({
onClick={showFindByPhoneNumber}
/>
);
key = 'find-by-phonenumber';
break;
case RowType.Header: {
const headerText = row.getHeaderText(i18n);
@ -543,16 +556,20 @@ export function ConversationList({
{headerText}
</div>
);
key = `header:${providedKey}`;
break;
}
case RowType.MessageSearchResult:
result = <>{renderMessageSearchResult?.(row.messageId)}</>;
key = `message-search-result:${row.messageId}`;
break;
case RowType.SearchResultsLoadingFakeHeader:
result = <SearchResultsLoadingFakeHeaderComponent />;
key = `loading-header:${providedKey}`;
break;
case RowType.SearchResultsLoadingFakeRow:
result = <SearchResultsLoadingFakeRowComponent />;
key = `loading-row:${providedKey}`;
break;
case RowType.SelectSingleGroup:
result = (
@ -562,6 +579,7 @@ export function ConversationList({
onSelectGroup={onSelectConversation}
/>
);
key = 'select-single-group';
break;
case RowType.StartNewConversation:
result = (
@ -577,6 +595,7 @@ export function ConversationList({
showConversation={showConversation}
/>
);
key = 'start-new-conversation';
break;
case RowType.UsernameSearchResult:
result = (
@ -592,6 +611,7 @@ export function ConversationList({
showConversation={showConversation}
/>
);
key = `username-search-result:${row.username}`;
break;
case RowType.EmptyResults:
result = (
@ -599,6 +619,7 @@ export function ConversationList({
{row.message}
</div>
);
key = 'empty-results';
break;
default:
throw missingCaseError(row);

View file

@ -272,7 +272,13 @@ function renderNode({
!isLinkSneaky(node.url)
) {
return (
<a key={key} className={formattingClasses} href={node.url}>
<a
key={key}
className={formattingClasses}
href={node.url}
target="_blank"
rel="noreferrer"
>
{content}
</a>
);

View file

@ -48,7 +48,7 @@ export const parseEnvironment = makeEnumParser(
export const isTestEnvironment = (env: Environment): boolean =>
env === Environment.Test;
const isMockEnvironment = (): boolean => {
export const isMockEnvironment = (): boolean => {
if (isMockTestEnvironment == null) {
log.error('Mock test environment not set');
}

View file

@ -24,7 +24,9 @@ export async function commonShouldJobContinue({
}
try {
await waitForOnline({ timeout: timeRemaining });
if (isDeviceLinked()) {
await waitForOnline({ timeout: timeRemaining });
}
} catch (err: unknown) {
log.info("didn't come online in time, giving up");
return false;

View file

@ -39,6 +39,7 @@ async function main(): Promise<void> {
WAYLAND_DISPLAY: process.env.WAYLAND_DISPLAY,
XAUTHORITY: process.env.XAUTHORITY,
CI: process.env.CI ? 'on' : undefined,
GENERATE_PRELOAD_CACHE: 'on',
SIGNAL_CI_CONFIG: JSON.stringify({
storagePath,

View file

@ -0,0 +1,27 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { spawnSync } from 'node:child_process';
import { join } from 'node:path';
const MOCHA = join(__dirname, '..', '..', 'node_modules', '.bin', 'mocha');
const WORKER_COUNT = parseInt(process.env.WORKER_COUNT || '1', 10);
const WORKER_INDEX = parseInt(process.env.WORKER_INDEX || '0', 10);
const separator = process.argv.indexOf('--');
if (separator === -1) {
throw new Error('Expected `--` separator between options and files');
}
const flags = process.argv.slice(2, separator);
const files = process.argv.slice(separator + 1);
const filteredFiles = files.filter((_file, index) => {
return index % WORKER_COUNT === WORKER_INDEX;
});
console.log(`Running on ${filteredFiles.length}/${files.length} of files`);
spawnSync(MOCHA, [...flags, ...filteredFiles], {
stdio: 'inherit',
});

View file

@ -1034,6 +1034,7 @@ export class BackupsService {
window.Whisper.events.once('storageService:syncComplete', resolve);
runStorageServiceSyncJob({ reason });
runStorageServiceSyncJob.flush();
await storageService;
}

View file

@ -82,7 +82,9 @@ import {
getRoomIdFromRootKeyString,
} from '../util/callLinksRingrtc';
import { fromPniUuidBytesOrUntaggedString } from '../util/ServiceId';
import { isDone as isRegistrationDone } from '../util/registration';
import { callLinkRefreshJobQueue } from '../jobs/callLinkRefreshJobQueue';
import { isMockEnvironment } from '../environment';
const log = createLogger('storage');
@ -2083,7 +2085,6 @@ async function upload({
if (!window.storage.get('storageKey')) {
// requesting new keys runs the sync job which will detect the conflict
// and re-run the upload job once we're merged and up-to-date.
log.info(`${logId}: no storageKey, requesting new keys`);
backOff.reset();
if (window.ConversationController.areWePrimaryDevice()) {
@ -2091,6 +2092,12 @@ async function upload({
return;
}
if (!isRegistrationDone()) {
log.warn(`${logId}: no storageKey, unlinked`);
return;
}
log.info(`${logId}: no storageKey, requesting new keys`);
await singleProtoJobQueue.add(MessageSender.getRequestKeySyncMessage());
return;
@ -2267,7 +2274,7 @@ export const storageServiceUploadJob = debounce(
`upload v${window.storage.get('manifestVersion')}`
);
},
500
isMockEnvironment() ? 0 : 500
);
export const runStorageServiceSyncJob = debounce(
@ -2289,7 +2296,7 @@ export const runStorageServiceSyncJob = debounce(
)
);
},
500
isMockEnvironment() ? 0 : 500
);
export const addPendingDelete = (item: ExtendedStorageID): void => {

View file

@ -4879,14 +4879,17 @@ function onConversationClosed(
): ThunkAction<void, RootStateType, unknown, ConversationUnloadedActionType> {
return async dispatch => {
const conversation = window.ConversationController.get(conversationId);
// Conversation was removed due to the merge
if (!conversation) {
throw new Error('onConversationClosed: Conversation not found');
log.warn(
`onConversationClosed: Conversation ${conversationId} not found`
);
}
const logId = `onConversationClosed/${conversation.idForLogging()}`;
const logId = `onConversationClosed/${conversation?.idForLogging() ?? conversationId}`;
log.info(`${logId}: unloading due to ${reason}`);
if (conversation.get('draftChanged')) {
if (conversation?.get('draftChanged')) {
if (conversation.hasDraft()) {
log.info(`${logId}: new draft info needs update`);
const now = Date.now();

View file

@ -102,18 +102,6 @@ export const SmartChatsTab = memo(function SmartChatsTab() {
}, [prevConversationId, selectedConversationId]);
useEffect(() => {
function refreshConversation({
newId,
oldId,
}: {
newId: string;
oldId: string;
}) {
if (prevConversationId === oldId) {
showConversation({ conversationId: newId });
}
}
// Close current opened conversation to reload the group information once
// linked.
function unload() {
@ -128,12 +116,10 @@ export const SmartChatsTab = memo(function SmartChatsTab() {
}
window.Whisper.events.on('pack-install-failed', packInstallFailed);
window.Whisper.events.on('refreshConversation', refreshConversation);
window.Whisper.events.on('setupAsNewDevice', unload);
return () => {
window.Whisper.events.off('pack-install-failed', packInstallFailed);
window.Whisper.events.off('refreshConversation', refreshConversation);
window.Whisper.events.off('setupAsNewDevice', unload);
};
}, [onConversationClosed, prevConversationId, showConversation, showToast]);

View file

@ -400,17 +400,22 @@ export class Bootstrap {
await app.stageLocalBackupForImport(localBackup);
}
debug('looking for QR code or relink button');
const qrCode = window.locator(
'.module-InstallScreenQrCodeNotScannedStep__qr-code__code'
let gotProvisionURL = false;
drop(
(async () => {
try {
const relinkButton = window.locator('.LeftPaneDialog__icon--relink');
await relinkButton.waitFor();
if (gotProvisionURL) {
return;
}
await relinkButton.click();
} catch {
// Ignore, provision will fail if QR code was never generated
}
})()
);
const relinkButton = window.locator('.LeftPaneDialog__icon--relink');
await qrCode.or(relinkButton).waitFor();
if (await relinkButton.isVisible()) {
debug('unlinked, clicking left pane button');
await relinkButton.click();
await qrCode.waitFor();
}
debug('waiting for provision');
const provision = await this.server.waitForProvision();
@ -418,6 +423,8 @@ export class Bootstrap {
debug('waiting for provision URL');
const provisionURL = await app.waitForProvisionURL();
gotProvisionURL = true;
debug('completing provision');
this.#privDesktop = await provision.complete({
provisionURL,

View file

@ -57,7 +57,9 @@ describe('readSync', function (this: Mocha.Suite) {
const leftPane = page.locator('#LeftPane');
await leftPane
.locator('.module-conversation-list__item--contact-or-conversation')
.locator(
'.module-conversation-list__item--contact-or-conversation >> "<(˶ᵔᵕᵔ˶)>"'
)
.first()
.waitFor();

View file

@ -87,11 +87,13 @@ describe('messaging/relink', function (this: Mocha.Suite) {
)
.waitFor();
debug('unlinkng');
await app.unlink();
await app.waitForUnlink();
await phone.unlink(desktop);
await server.removeDevice(desktop.number, desktop.deviceId);
debug('closing');
await app.close();
debug('change pinned contact, identity key');

View file

@ -88,9 +88,7 @@ describe('unprocessed', function (this: Mocha.Suite) {
const page = await app.getWindow();
debug('opening conversation');
await page
.locator(`[data-testid="${alice.device.aci}"] >> "${alice.profileName}"`)
.click();
await page.getByTestId(alice.device.aci).click();
await page.locator('.module-message__text >> "hello: 4"').waitFor();
await page.locator('.module-message__text >> "hello: 5"').waitFor();

View file

@ -83,6 +83,7 @@ export class App extends EventEmitter {
snapshots: true,
});
}
await page?.emulateMedia({ reducedMotion: 'reduce' });
await page?.waitForLoadState('load');
})(),
20 * SECOND

View file

@ -110,10 +110,12 @@ describe('storage service', function (this: Mocha.Suite) {
}
const updatedState = await phone.setStorageState(
state.addRecord({
type: IdentifierType.ACCOUNT,
record: oldAccount.record,
})
state
.addRecord({
type: IdentifierType.ACCOUNT,
record: oldAccount.record,
})
.updateAccount({})
);
debug('sending fetch storage');

View file

@ -48,20 +48,21 @@ describe('storage service', function (this: Mocha.Suite) {
debug('Unpinning group via storage service');
{
const state = await phone.expectStorageState('initial state');
const newState = state.unpinGroup(group);
await phone.setStorageState(state.unpinGroup(group));
await phone.setStorageState(newState);
await phone.sendFetchStorage({
timestamp: bootstrap.getTimestamp(),
});
await leftPane.locator(`[data-testid="${group.id}"]`).waitFor();
await app.waitForManifestVersion(newState.version);
}
debug('Pinning group in the app');
{
const state = await phone.expectStorageState('consistency check');
const convo = leftPane.locator(`[data-testid="${group.id}"]`);
const convo = leftPane.getByTestId(group.id);
await convo.click();
const moreButton = conversationStack.locator(

View file

@ -70,11 +70,10 @@ describe('storage service', function (this: Mocha.Suite) {
await conversationView
.locator(`a:has-text("${STICKER_PACKS[0].id.toString('hex')}")`)
.click({ noWaitAfter: true });
.click();
await window
.locator(
'.module-sticker-manager__preview-modal__footer--install button >> "Install"'
)
.getByTestId('StickerPreviewModal')
.getByRole('button', { name: 'Install' })
.click();
debug('waiting for sync message');
@ -114,12 +113,10 @@ describe('storage service', function (this: Mocha.Suite) {
await conversationView
.locator(`a:has-text("${STICKER_PACKS[0].id.toString('hex')}")`)
.click({ noWaitAfter: true });
.click();
await window
.locator(
'.module-sticker-manager__preview-modal__footer--install button ' +
'>> "Uninstall"'
)
.getByTestId('StickerPreviewModal')
.getByRole('button', { name: 'Uninstall' })
.click();
// Confirm

View file

@ -28,6 +28,7 @@ import type {
import createTaskWithTimeout from './TaskWithTimeout';
import * as Bytes from '../Bytes';
import * as Errors from '../types/errors';
import { isMockEnvironment } from '../environment';
import { senderCertificateService } from '../services/senderCertificate';
import { backupsService } from '../services/backups';
import {
@ -96,7 +97,9 @@ const LAST_RESORT_KEY_UPDATE_TIME_KEY: StorageKeyByServiceIdKind = {
};
const PRE_KEY_ARCHIVE_AGE = 90 * DAY;
const PRE_KEY_GEN_BATCH_SIZE = 100;
// Use 20 keys for mock tests which is above the minimum, but takes much less
// time to generate and store in the database (especially for PQ keys)
const PRE_KEY_GEN_BATCH_SIZE = isMockEnvironment() ? 20 : 100;
const PRE_KEY_MAX_COUNT = 200;
const PRE_KEY_ID_KEY: StorageKeyByServiceIdKind = {
[ServiceIdKind.ACI]: 'maxPreKeyId',