// Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable no-await-in-loop */ import { assert } from 'chai'; import type { Group } from '@signalapp/mock-server'; import * as durations from '../../util/durations'; import type { App, Bootstrap } from './fixtures'; import { initStorage, debug } from './fixtures'; describe('storage service', function needsName() { this.timeout(durations.MINUTE); let bootstrap: Bootstrap; let app: App; let group: Group; beforeEach(async () => { ({ bootstrap, app, group } = await initStorage()); }); afterEach(async () => { await app.close(); await bootstrap.teardown(); }); it('should pin/unpin groups', async () => { const { phone, desktop, contacts } = bootstrap; const window = await app.getWindow(); const leftPane = window.locator('.left-pane-wrapper'); const conversationStack = window.locator('.conversation-stack'); debug('Verifying that the group is pinned on startup'); await leftPane .locator( '_react=ConversationListItem' + '[isPinned = true] ' + `[title = ${JSON.stringify(group.title)}]` ) .waitFor(); debug('Unpinning group via storage service'); { const state = await phone.expectStorageState('initial state'); await phone.setStorageState(state.unpinGroup(group)); await phone.sendFetchStorage({ timestamp: bootstrap.getTimestamp(), }); await leftPane .locator( '_react=ConversationListItem' + '[isPinned = false] ' + `[title = ${JSON.stringify(group.title)}]` ) .waitFor(); } debug('Pinning group in the app'); { const state = await phone.expectStorageState('consistency check'); const convo = leftPane.locator( '_react=ConversationListItem' + '[isPinned = false] ' + `[title = ${JSON.stringify(group.title)}]` ); await convo.click(); const moreButton = conversationStack.locator( 'button.module-ConversationHeader__button--more' ); await moreButton.click(); const pinButton = conversationStack.locator( '.react-contextmenu-item >> "Pin Conversation"' ); await pinButton.click(); const newState = await phone.waitForStorageState({ after: state, }); assert.isTrue(await newState.isGroupPinned(group), 'group not pinned'); // AccountRecord const { added, removed } = newState.diff(state); assert.strictEqual(added.length, 1, 'only one record must be added'); assert.strictEqual(removed.length, 1, 'only one record must be removed'); } debug('Pinning > 4 conversations'); { // We already have one group and first contact pinned so we need three // more. const toPin = contacts.slice(1, 4); // To do that we need them to appear in the left pane, though. for (const [i, contact] of toPin.entries()) { const isLast = i === toPin.length - 1; debug('sending a message to contact=%d', i); await contact.sendText(desktop, 'Hello!', { timestamp: bootstrap.getTimestamp(), }); const state = await phone.expectStorageState('consistency check'); debug('pinning contact=%d', i); const convo = leftPane.locator( '_react=ConversationListItem' + `[title = ${JSON.stringify(contact.profileName)}]` ); await convo.click(); const moreButton = conversationStack.locator( 'button.module-ConversationHeader__button--more' ); await moreButton.click(); const pinButton = conversationStack.locator( '.react-contextmenu-item >> "Pin Conversation"' ); await pinButton.click(); if (isLast) { // Storage state shouldn't be updated because we failed to pin await window .locator('.Toast >> "You can only pin up to 4 chats"') .waitFor(); break; } debug('verifying storage state change contact=%d', i); const newState = await phone.waitForStorageState({ after: state, }); assert.isTrue(await newState.isPinned(contact), 'contact not pinned'); // AccountRecord const { added, removed } = newState.diff(state); assert.strictEqual(added.length, 1, 'only one record must be added'); assert.strictEqual( removed.length, 1, 'only one record must be removed' ); } } debug('Verifying the final manifest version'); const finalState = await phone.expectStorageState('consistency check'); assert.strictEqual(finalState.version, 5); }); });