Faster CI runs
This commit is contained in:
parent
109c3036c9
commit
ec8d6a7359
26 changed files with 242 additions and 207 deletions
183
.github/workflows/benchmark.yml
vendored
183
.github/workflows/benchmark.yml
vendored
|
@ -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 }}
|
||||
|
|
19
.github/workflows/ci.yml
vendored
19
.github/workflows/ci.yml
vendored
|
@ -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()
|
||||
|
|
|
@ -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
28
pnpm-lock.yaml
generated
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -1457,6 +1457,7 @@ export async function startApp(): Promise<void> {
|
|||
await StorageService.runStorageServiceSyncJob({
|
||||
reason: andSync,
|
||||
});
|
||||
StorageService.runStorageServiceSyncJob.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
27
ts/scripts/mocha-separator.ts
Normal file
27
ts/scripts/mocha-separator.ts
Normal 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',
|
||||
});
|
|
@ -1034,6 +1034,7 @@ export class BackupsService {
|
|||
window.Whisper.events.once('storageService:syncComplete', resolve);
|
||||
|
||||
runStorageServiceSyncJob({ reason });
|
||||
runStorageServiceSyncJob.flush();
|
||||
await storageService;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -83,6 +83,7 @@ export class App extends EventEmitter {
|
|||
snapshots: true,
|
||||
});
|
||||
}
|
||||
await page?.emulateMedia({ reducedMotion: 'reduce' });
|
||||
await page?.waitForLoadState('load');
|
||||
})(),
|
||||
20 * SECOND
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue