Files
Desktop/ts/test-mock/storage/sticker_test.node.ts

312 lines
9.4 KiB
TypeScript

// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { Proto } from '@signalapp/mock-server';
import * as durations from '../../util/durations/index.std.js';
import type { App, Bootstrap } from './fixtures.node.js';
import {
initStorage,
debug,
STICKER_PACKS,
EMPTY,
storeStickerPacks,
getStickerPackRecordPredicate,
getStickerPackLink,
} from './fixtures.node.js';
import {
getMessageInTimelineByTimestamp,
sendTextMessage,
} from '../helpers.node.js';
import { strictAssert } from '../../util/assert.std.js';
const { StickerPackOperation } = Proto.SyncMessage;
describe('stickers', function (this: Mocha.Suite) {
this.timeout(durations.MINUTE);
let bootstrap: Bootstrap;
let app: App;
beforeEach(async () => {
({ bootstrap, app } = await initStorage());
const { server } = bootstrap;
await storeStickerPacks(server, STICKER_PACKS);
});
afterEach(async function (this: Mocha.Context) {
if (!bootstrap) {
return;
}
await bootstrap.maybeSaveLogs(this.currentTest, app);
await app.close();
await bootstrap.teardown();
});
it('should install/uninstall stickers', async () => {
const { phone, desktop, contacts } = bootstrap;
const [firstContact] = contacts;
const window = await app.getWindow();
const leftPane = window.locator('#LeftPane');
const conversationView = window.locator(
'.Inbox__conversation > .ConversationView'
);
debug('sending two sticker pack links');
await firstContact.sendText(
desktop,
`First sticker pack ${getStickerPackLink(STICKER_PACKS[0])}`
);
await firstContact.sendText(
desktop,
`Second sticker pack ${getStickerPackLink(STICKER_PACKS[1])}`
);
await leftPane
.locator(`[data-testid="${firstContact.device.aci}"]`)
.click();
{
debug('installing first sticker pack via UI');
const state = await phone.expectStorageState('initial state');
await conversationView
.locator(`a:has-text("${STICKER_PACKS[0].id.toString('hex')}")`)
.click();
await window
.getByTestId('StickerPreviewModal')
.getByRole('button', { name: 'Install' })
.click();
debug('waiting for sync message');
const { syncMessage } = await phone.waitForSyncMessage(entry =>
Boolean(entry.syncMessage.stickerPackOperation?.length)
);
const [syncOp] = syncMessage.stickerPackOperation ?? [];
assert.isTrue(STICKER_PACKS[0].id.equals(syncOp?.packId ?? EMPTY));
assert.isTrue(STICKER_PACKS[0].key.equals(syncOp?.packKey ?? EMPTY));
assert.strictEqual(syncOp?.type, StickerPackOperation.Type.INSTALL);
debug('waiting for storage service update');
const stateAfter = await phone.waitForStorageState({ after: state });
const stickerPack = stateAfter.findRecord(
getStickerPackRecordPredicate(STICKER_PACKS[0])
);
assert.ok(
stickerPack,
'New storage state should have sticker pack record'
);
assert.isTrue(
STICKER_PACKS[0].key.equals(
stickerPack?.record.stickerPack?.packKey ?? EMPTY
),
'Wrong sticker pack key'
);
assert.strictEqual(
stickerPack?.record.stickerPack?.position,
11,
'Wrong sticker pack position'
);
}
{
debug('uninstalling first sticker pack via UI');
const state = await phone.expectStorageState('initial state');
await conversationView
.locator(`a:has-text("${STICKER_PACKS[0].id.toString('hex')}")`)
.click();
await window
.getByTestId('StickerPreviewModal')
.getByRole('button', { name: 'Uninstall' })
.click();
// Confirm
await window
.locator('.module-Button--destructive >> "Uninstall"')
.click();
debug('waiting for sync message');
const { syncMessage } = await phone.waitForSyncMessage(entry =>
Boolean(entry.syncMessage.stickerPackOperation?.length)
);
const [syncOp] = syncMessage.stickerPackOperation ?? [];
assert.isTrue(STICKER_PACKS[0].id.equals(syncOp?.packId ?? EMPTY));
assert.strictEqual(syncOp?.type, StickerPackOperation.Type.REMOVE);
debug('waiting for storage service update');
const stateAfter = await phone.waitForStorageState({ after: state });
const stickerPack = stateAfter.findRecord(
getStickerPackRecordPredicate(STICKER_PACKS[0])
);
assert.ok(
stickerPack,
'New storage state should have sticker pack record'
);
assert.deepStrictEqual(
stickerPack?.record.stickerPack?.packKey,
EMPTY,
'Sticker pack key should be removed'
);
const deletedAt =
stickerPack?.record.stickerPack?.deletedAtTimestamp?.toNumber() ?? 0;
assert.isAbove(
deletedAt,
Date.now() - durations.HOUR,
'Sticker pack should have deleted at timestamp'
);
}
debug('opening sticker manager');
const FunButton = window.getByRole('button', {
name: 'Add an Emoji, Sticker, or GIF',
});
const FunDialog = window.getByRole('dialog', {
name: 'Add an Emoji, Sticker, or GIF',
});
const FunPickerStickersTab = FunDialog.getByRole('tab', {
name: 'Stickers',
});
const FunPickerAddSticker = FunDialog.getByRole('button', {
name: 'Add a sticker pack',
});
await FunButton.click();
await FunPickerStickersTab.click();
await FunPickerAddSticker.click();
const stickerManager = conversationView.locator(
'[data-testid=StickerManager]'
);
debug('switching to Installed tab');
await stickerManager.locator('.Tabs__tab >> "Installed"').click();
{
debug('installing first sticker pack via storage service');
const state = await phone.expectStorageState('initial state');
await phone.setStorageState(
state.updateRecord(
getStickerPackRecordPredicate(STICKER_PACKS[0]),
record => ({
...record,
stickerPack: {
...record?.stickerPack,
packKey: STICKER_PACKS[0].key,
position: 7,
deletedAtTimestamp: undefined,
},
})
)
);
await phone.sendFetchStorage({
timestamp: bootstrap.getTimestamp(),
});
debug('waiting for sticker pack to become visible');
await stickerManager
.locator(`[data-testid="${STICKER_PACKS[0].id.toString('hex')}"]`)
.waitFor();
}
{
debug('installing second sticker pack via sync message');
const state = await phone.expectStorageState('initial state');
await phone.sendStickerPackSync({
type: 'install',
packId: STICKER_PACKS[1].id,
packKey: STICKER_PACKS[1].key,
timestamp: bootstrap.getTimestamp(),
});
debug('waiting for sticker pack to become visible');
await stickerManager
.locator(`[data-testid="${STICKER_PACKS[1].id.toString('hex')}"]`)
.waitFor();
debug('waiting for storage service update');
const stateAfter = await phone.waitForStorageState({ after: state });
const stickerPack = stateAfter.findRecord(
getStickerPackRecordPredicate(STICKER_PACKS[1])
);
assert.ok(
stickerPack,
'New storage state should have sticker pack record'
);
assert.isTrue(
STICKER_PACKS[1].key.equals(
stickerPack?.record.stickerPack?.packKey ?? EMPTY
),
'Wrong sticker pack key'
);
assert.strictEqual(
stickerPack?.record.stickerPack?.position,
12,
'Wrong sticker pack position'
);
}
debug('Verifying the final manifest version');
const finalState = await phone.expectStorageState('consistency check');
assert.strictEqual(finalState.version, 5);
debug(
'verifying that stickers from packs can be received and paths are deduplicated'
);
const firstTimestamp = bootstrap.getTimestamp();
const secondTimestamp = bootstrap.getTimestamp();
await sendTextMessage({
from: firstContact,
to: desktop,
desktop,
text: undefined,
sticker: {
packId: STICKER_PACKS[0].id,
packKey: STICKER_PACKS[0].key,
stickerId: 0,
},
timestamp: firstTimestamp,
});
await sendTextMessage({
from: firstContact,
to: desktop,
desktop,
text: undefined,
sticker: {
packId: STICKER_PACKS[0].id,
packKey: STICKER_PACKS[0].key,
stickerId: 0,
},
timestamp: secondTimestamp,
});
await window.getByRole('button', { name: 'Go back' }).click();
await getMessageInTimelineByTimestamp(window, firstTimestamp)
.locator('.module-image--loaded')
.waitFor();
await getMessageInTimelineByTimestamp(window, secondTimestamp)
.locator('.module-image--loaded')
.waitFor();
const firstStickerData = (await app.getMessagesBySentAt(firstTimestamp))[0]
.sticker?.data;
const secondStickerData = (
await app.getMessagesBySentAt(secondTimestamp)
)[0].sticker?.data;
strictAssert(firstStickerData?.path, 'path exists');
strictAssert(firstStickerData?.localKey, 'localKey exists');
assert.strictEqual(firstStickerData.path, secondStickerData?.path);
assert.strictEqual(firstStickerData.localKey, secondStickerData?.localKey);
});
});