Add Lightbox integration test
This commit is contained in:
parent
4f9d383180
commit
bdbc63ccf0
16 changed files with 343 additions and 85 deletions
|
@ -558,6 +558,7 @@ export function Lightbox({
|
||||||
<img
|
<img
|
||||||
alt={i18n('icu:lightboxImageAlt')}
|
alt={i18n('icu:lightboxImageAlt')}
|
||||||
className="Lightbox__object"
|
className="Lightbox__object"
|
||||||
|
data-testid={attachment.fileName}
|
||||||
onContextMenu={(ev: React.MouseEvent<HTMLImageElement>) => {
|
onContextMenu={(ev: React.MouseEvent<HTMLImageElement>) => {
|
||||||
// These are the only image types supported by Electron's NativeImage
|
// These are the only image types supported by Electron's NativeImage
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
} from './fixtures';
|
} from './fixtures';
|
||||||
import { stats } from '../../util/benchmark/stats';
|
import { stats } from '../../util/benchmark/stats';
|
||||||
import { sleep } from '../../util/sleep';
|
import { sleep } from '../../util/sleep';
|
||||||
import { typeIntoInput } from '../helpers';
|
import { typeIntoInput, waitForEnabledComposer } from '../helpers';
|
||||||
import { MINUTE } from '../../util/durations';
|
import { MINUTE } from '../../util/durations';
|
||||||
|
|
||||||
const LAST_MESSAGE = 'start sending messages now';
|
const LAST_MESSAGE = 'start sending messages now';
|
||||||
|
@ -169,7 +169,7 @@ Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const deltaList = new Array<number>();
|
const deltaList = new Array<number>();
|
||||||
const input = await app.waitForEnabledComposer();
|
const input = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
function sendReceiptsInBatches({
|
function sendReceiptsInBatches({
|
||||||
receipts,
|
receipts,
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { ReceiptType } from '@signalapp/mock-server';
|
||||||
|
|
||||||
import { Bootstrap, debug, RUN_COUNT, DISCARD_COUNT } from './fixtures';
|
import { Bootstrap, debug, RUN_COUNT, DISCARD_COUNT } from './fixtures';
|
||||||
import { stats } from '../../util/benchmark/stats';
|
import { stats } from '../../util/benchmark/stats';
|
||||||
import { typeIntoInput } from '../helpers';
|
import { typeIntoInput, waitForEnabledComposer } from '../helpers';
|
||||||
|
|
||||||
const CONVERSATION_SIZE = 500; // messages
|
const CONVERSATION_SIZE = 500; // messages
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
||||||
const deltaList = new Array<number>();
|
const deltaList = new Array<number>();
|
||||||
for (let runId = 0; runId < RUN_COUNT + DISCARD_COUNT; runId += 1) {
|
for (let runId = 0; runId < RUN_COUNT + DISCARD_COUNT; runId += 1) {
|
||||||
debug('finding composition input and clicking it');
|
debug('finding composition input and clicking it');
|
||||||
const input = await app.waitForEnabledComposer();
|
const input = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
debug('entering message text');
|
debug('entering message text');
|
||||||
await typeIntoInput(input, `my message ${runId}`);
|
await typeIntoInput(input, `my message ${runId}`);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2023 Signal Messenger, LLC
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import createDebug from 'debug';
|
||||||
import {
|
import {
|
||||||
type Device,
|
type Device,
|
||||||
type Group,
|
type Group,
|
||||||
|
@ -12,6 +13,10 @@ import { assert } from 'chai';
|
||||||
import Long from 'long';
|
import Long from 'long';
|
||||||
import type { Locator, Page } from 'playwright';
|
import type { Locator, Page } from 'playwright';
|
||||||
import { expect } from 'playwright/test';
|
import { expect } from 'playwright/test';
|
||||||
|
import type { SignalService } from '../protobuf';
|
||||||
|
import { strictAssert } from '../util/assert';
|
||||||
|
|
||||||
|
const debug = createDebug('mock:test:helpers');
|
||||||
|
|
||||||
export function bufferToUuid(buffer: Buffer): string {
|
export function bufferToUuid(buffer: Buffer): string {
|
||||||
const hex = buffer.toString('hex');
|
const hex = buffer.toString('hex');
|
||||||
|
@ -280,3 +285,85 @@ export function getMessageInTimelineByTimestamp(
|
||||||
): Locator {
|
): Locator {
|
||||||
return getTimeline(page).getByTestId(`${timestamp}`);
|
return getTimeline(page).getByTestId(`${timestamp}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTimelineMessageWithText(page: Page, text: string): Locator {
|
||||||
|
return getTimeline(page).locator('.module-message').filter({ hasText: text });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function composerAttachImages(
|
||||||
|
page: Page,
|
||||||
|
filePaths: ReadonlyArray<string>
|
||||||
|
): Promise<void> {
|
||||||
|
const AttachmentInput = page.getByTestId('attachfile-input');
|
||||||
|
|
||||||
|
const AttachmentsList = page.locator('.module-attachments');
|
||||||
|
const AttachmentsListImage = AttachmentsList.locator('.module-image');
|
||||||
|
const AttachmentsListImageLoaded = AttachmentsListImage.locator(
|
||||||
|
'.module-image__image'
|
||||||
|
);
|
||||||
|
|
||||||
|
debug('setting input files');
|
||||||
|
await AttachmentInput.setInputFiles(filePaths);
|
||||||
|
|
||||||
|
debug(`waiting for ${filePaths.length} items`);
|
||||||
|
await AttachmentsListImage.nth(filePaths.length - 1).waitFor();
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
filePaths.map(async (_, index) => {
|
||||||
|
debug(`waiting for ${index} image to render in attachments list`);
|
||||||
|
await AttachmentsListImageLoaded.nth(index).waitFor({
|
||||||
|
state: 'visible',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendMessageWithAttachments(
|
||||||
|
page: Page,
|
||||||
|
receiver: PrimaryDevice,
|
||||||
|
text: string,
|
||||||
|
filePaths: Array<string>
|
||||||
|
): Promise<Array<SignalService.IAttachmentPointer>> {
|
||||||
|
await composerAttachImages(page, filePaths);
|
||||||
|
|
||||||
|
debug('sending message');
|
||||||
|
const input = await waitForEnabledComposer(page);
|
||||||
|
await typeIntoInput(input, text);
|
||||||
|
await input.press('Enter');
|
||||||
|
|
||||||
|
const Message = getTimelineMessageWithText(page, text);
|
||||||
|
const MessageImageLoaded = Message.locator('.module-image__image');
|
||||||
|
|
||||||
|
await Message.waitFor();
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
filePaths.map(async (_, index) => {
|
||||||
|
debug(`waiting for ${index} image to render in timeline`);
|
||||||
|
await MessageImageLoaded.nth(index).waitFor({
|
||||||
|
state: 'visible',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
debug('get received message data');
|
||||||
|
const receivedMessage = await receiver.waitForMessage();
|
||||||
|
const attachments = receivedMessage.dataMessage.attachments ?? [];
|
||||||
|
strictAssert(
|
||||||
|
attachments.length === filePaths.length,
|
||||||
|
'attachments must exist'
|
||||||
|
);
|
||||||
|
|
||||||
|
return attachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function waitForEnabledComposer(page: Page): Promise<Locator> {
|
||||||
|
const composeArea = page.locator(
|
||||||
|
'.composition-area-wrapper, .Inbox__conversation .ConversationView'
|
||||||
|
);
|
||||||
|
const composeContainer = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput][data-enabled=true]'
|
||||||
|
);
|
||||||
|
await composeContainer.waitFor();
|
||||||
|
|
||||||
|
return composeContainer.locator('.ql-editor');
|
||||||
|
}
|
||||||
|
|
|
@ -9,16 +9,16 @@ import type { App } from '../playwright';
|
||||||
import { Bootstrap } from '../bootstrap';
|
import { Bootstrap } from '../bootstrap';
|
||||||
import {
|
import {
|
||||||
getMessageInTimelineByTimestamp,
|
getMessageInTimelineByTimestamp,
|
||||||
getTimeline,
|
getTimelineMessageWithText,
|
||||||
|
sendMessageWithAttachments,
|
||||||
sendTextMessage,
|
sendTextMessage,
|
||||||
typeIntoInput,
|
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import * as durations from '../../util/durations';
|
import * as durations from '../../util/durations';
|
||||||
import { strictAssert } from '../../util/assert';
|
import { strictAssert } from '../../util/assert';
|
||||||
|
|
||||||
export const debug = createDebug('mock:test:attachments');
|
export const debug = createDebug('mock:test:attachments');
|
||||||
|
|
||||||
describe('attachments', function (this: Mocha.Suite) {
|
describe.only('attachments', function (this: Mocha.Suite) {
|
||||||
this.timeout(durations.MINUTE);
|
this.timeout(durations.MINUTE);
|
||||||
|
|
||||||
let bootstrap: Bootstrap;
|
let bootstrap: Bootstrap;
|
||||||
|
@ -60,48 +60,33 @@ describe('attachments', function (this: Mocha.Suite) {
|
||||||
const page = await app.getWindow();
|
const page = await app.getWindow();
|
||||||
|
|
||||||
await page.getByTestId(pinned.device.aci).click();
|
await page.getByTestId(pinned.device.aci).click();
|
||||||
await page
|
|
||||||
.getByTestId('attachfile-input')
|
|
||||||
.setInputFiles(
|
|
||||||
path.join(__dirname, '..', '..', '..', 'fixtures', 'cat-screenshot.png')
|
|
||||||
);
|
|
||||||
await page
|
|
||||||
.locator('.module-image.module-staged-attachment .module-image__image')
|
|
||||||
.waitFor();
|
|
||||||
const input = await app.waitForEnabledComposer();
|
|
||||||
await typeIntoInput(input, 'This is my cat');
|
|
||||||
await input.press('Enter');
|
|
||||||
|
|
||||||
const allMessagesLocator = getTimeline(page).getByRole('article');
|
const [attachmentCat] = await sendMessageWithAttachments(
|
||||||
await expect(allMessagesLocator).toHaveCount(1);
|
page,
|
||||||
|
pinned,
|
||||||
|
'This is my cat',
|
||||||
|
[path.join(__dirname, '..', '..', '..', 'fixtures', 'cat-screenshot.png')]
|
||||||
|
);
|
||||||
|
|
||||||
const allMessages = await allMessagesLocator.all();
|
const Message = getTimelineMessageWithText(page, 'This is my cat');
|
||||||
const message = allMessages[0];
|
const MessageSent = Message.locator(
|
||||||
|
'.module-message__metadata__status-icon--sent'
|
||||||
await message.getByText('This is my cat').waitFor();
|
);
|
||||||
await message
|
|
||||||
.locator('.module-message__metadata__status-icon--sent')
|
|
||||||
.waitFor();
|
|
||||||
|
|
||||||
const timestamp = await message
|
|
||||||
.locator('.module-message.module-message--outgoing')
|
|
||||||
.getAttribute('data-testid');
|
|
||||||
|
|
||||||
|
debug('waiting for send');
|
||||||
|
await MessageSent.waitFor();
|
||||||
|
const timestamp = await Message.getAttribute('data-testid');
|
||||||
strictAssert(timestamp, 'timestamp must exist');
|
strictAssert(timestamp, 'timestamp must exist');
|
||||||
|
|
||||||
// For this test, just send back the same attachment that was uploaded to test a
|
// For this test, just send back the same attachment that was uploaded to test a
|
||||||
// round-trip
|
// round-trip
|
||||||
const receivedMessage = await pinned.waitForMessage();
|
|
||||||
const attachment = receivedMessage.dataMessage.attachments?.[0];
|
|
||||||
strictAssert(attachment, 'attachment must exist');
|
|
||||||
|
|
||||||
const incomingTimestamp = Date.now();
|
const incomingTimestamp = Date.now();
|
||||||
await sendTextMessage({
|
await sendTextMessage({
|
||||||
from: pinned,
|
from: pinned,
|
||||||
to: bootstrap.desktop,
|
to: bootstrap.desktop,
|
||||||
desktop: bootstrap.desktop,
|
desktop: bootstrap.desktop,
|
||||||
text: 'Wait, that is MY cat!',
|
text: 'Wait, that is MY cat!',
|
||||||
attachments: [attachment],
|
attachments: [attachmentCat],
|
||||||
timestamp: incomingTimestamp,
|
timestamp: incomingTimestamp,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { drop } from '../../util/drop';
|
||||||
import { strictAssert } from '../../util/assert';
|
import { strictAssert } from '../../util/assert';
|
||||||
import { generateAci } from '../../types/ServiceId';
|
import { generateAci } from '../../types/ServiceId';
|
||||||
import { IMAGE_GIF } from '../../types/MIME';
|
import { IMAGE_GIF } from '../../types/MIME';
|
||||||
import { typeIntoInput } from '../helpers';
|
import { typeIntoInput, waitForEnabledComposer } from '../helpers';
|
||||||
import type { MessageAttributesType } from '../../model-types';
|
import type { MessageAttributesType } from '../../model-types';
|
||||||
import { sleep } from '../../util/sleep';
|
import { sleep } from '../../util/sleep';
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ describe('editing', function (this: Mocha.Suite) {
|
||||||
.locator('.module-message__buttons__menu')
|
.locator('.module-message__buttons__menu')
|
||||||
.click();
|
.click();
|
||||||
await page.getByRole('menuitem', { name: 'Edit' }).click();
|
await page.getByRole('menuitem', { name: 'Edit' }).click();
|
||||||
const input = await app.waitForEnabledComposer();
|
const input = await waitForEnabledComposer(page);
|
||||||
await typeIntoInput(input, additionalText);
|
await typeIntoInput(input, additionalText);
|
||||||
await input.press('Enter');
|
await input.press('Enter');
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ describe('editing', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('finding composition input and clicking it');
|
debug('finding composition input and clicking it');
|
||||||
{
|
{
|
||||||
const input = await app.waitForEnabledComposer();
|
const input = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
debug('entering original message text');
|
debug('entering original message text');
|
||||||
await typeIntoInput(input, 'edit message 1');
|
await typeIntoInput(input, 'edit message 1');
|
||||||
|
@ -542,7 +542,7 @@ describe('editing', function (this: Mocha.Suite) {
|
||||||
const originalText = '1';
|
const originalText = '1';
|
||||||
debug('finding composition input and clicking it');
|
debug('finding composition input and clicking it');
|
||||||
{
|
{
|
||||||
const input = await app.waitForEnabledComposer();
|
const input = await waitForEnabledComposer(page);
|
||||||
|
|
||||||
debug('sending message desktop -> friend');
|
debug('sending message desktop -> friend');
|
||||||
await typeIntoInput(input, originalText);
|
await typeIntoInput(input, originalText);
|
||||||
|
|
|
@ -15,7 +15,11 @@ import { uuidToBytes } from '../../util/uuidToBytes';
|
||||||
import { MY_STORY_ID } from '../../types/Stories';
|
import { MY_STORY_ID } from '../../types/Stories';
|
||||||
import { Bootstrap } from '../bootstrap';
|
import { Bootstrap } from '../bootstrap';
|
||||||
import type { App } from '../bootstrap';
|
import type { App } from '../bootstrap';
|
||||||
import { expectSystemMessages, typeIntoInput } from '../helpers';
|
import {
|
||||||
|
expectSystemMessages,
|
||||||
|
typeIntoInput,
|
||||||
|
waitForEnabledComposer,
|
||||||
|
} from '../helpers';
|
||||||
|
|
||||||
export const debug = createDebug('mock:test:messaging');
|
export const debug = createDebug('mock:test:messaging');
|
||||||
|
|
||||||
|
@ -259,7 +263,7 @@ describe('messaging/expireTimerVersion', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to merged contact');
|
debug('Send message to merged contact');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello');
|
await typeIntoInput(compositionInput, 'Hello');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
|
174
ts/test-mock/messaging/lightbox_test.ts
Normal file
174
ts/test-mock/messaging/lightbox_test.ts
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
// Copyright 2024 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import createDebug from 'debug';
|
||||||
|
import { expect } from 'playwright/test';
|
||||||
|
import { type PrimaryDevice, StorageState } from '@signalapp/mock-server';
|
||||||
|
import * as path from 'path';
|
||||||
|
import type { App } from '../playwright';
|
||||||
|
import { Bootstrap } from '../bootstrap';
|
||||||
|
import {
|
||||||
|
getMessageInTimelineByTimestamp,
|
||||||
|
getTimelineMessageWithText,
|
||||||
|
sendMessageWithAttachments,
|
||||||
|
sendTextMessage,
|
||||||
|
} from '../helpers';
|
||||||
|
import * as durations from '../../util/durations';
|
||||||
|
import { strictAssert } from '../../util/assert';
|
||||||
|
import type { SignalService } from '../../protobuf';
|
||||||
|
|
||||||
|
const debug = createDebug('mock:test:lightbox');
|
||||||
|
|
||||||
|
describe.only('lightbox', function (this: Mocha.Suite) {
|
||||||
|
this.timeout(durations.MINUTE);
|
||||||
|
|
||||||
|
let bootstrap: Bootstrap;
|
||||||
|
let app: App;
|
||||||
|
let pinned: PrimaryDevice;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
bootstrap = new Bootstrap();
|
||||||
|
await bootstrap.init();
|
||||||
|
|
||||||
|
let state = StorageState.getEmpty();
|
||||||
|
|
||||||
|
const { phone, contacts } = bootstrap;
|
||||||
|
[pinned] = contacts;
|
||||||
|
|
||||||
|
state = state.addContact(pinned, {
|
||||||
|
identityKey: pinned.publicKey.serialize(),
|
||||||
|
profileKey: pinned.profileKey.serialize(),
|
||||||
|
whitelisted: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
state = state.pin(pinned);
|
||||||
|
await phone.setStorageState(state);
|
||||||
|
|
||||||
|
app = await bootstrap.link();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async function (this: Mocha.Context) {
|
||||||
|
if (!bootstrap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await bootstrap.maybeSaveLogs(this.currentTest, app);
|
||||||
|
await app.close();
|
||||||
|
await bootstrap.teardown();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can page through different messages in the same conversation', async () => {
|
||||||
|
const page = await app.getWindow();
|
||||||
|
|
||||||
|
await page.getByTestId(pinned.device.aci).click();
|
||||||
|
|
||||||
|
async function sendAttachmentsBack(
|
||||||
|
text: string,
|
||||||
|
attachments: Array<SignalService.IAttachmentPointer>
|
||||||
|
) {
|
||||||
|
debug(`replying with ${attachments.length} attachments`);
|
||||||
|
const timestamp = bootstrap.getTimestamp();
|
||||||
|
await sendTextMessage({
|
||||||
|
from: pinned,
|
||||||
|
to: bootstrap.desktop,
|
||||||
|
desktop: bootstrap.desktop,
|
||||||
|
text,
|
||||||
|
attachments,
|
||||||
|
timestamp,
|
||||||
|
});
|
||||||
|
debug('wait for message to appear in timeline');
|
||||||
|
const Message = getMessageInTimelineByTimestamp(page, timestamp);
|
||||||
|
const MessageImageLoaded = Message.locator('img.module-image__image');
|
||||||
|
|
||||||
|
await Message.waitFor();
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
attachments.map(async (_, index) => {
|
||||||
|
debug(`waiting for ${index} image to render in timeline`);
|
||||||
|
await MessageImageLoaded.nth(index).waitFor({
|
||||||
|
state: 'visible',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixturesDir = path.join(__dirname, '..', '..', '..', 'fixtures');
|
||||||
|
const imageCat = path.join(fixturesDir, 'cat-screenshot.png');
|
||||||
|
const imageSnow = path.join(fixturesDir, 'snow.jpg');
|
||||||
|
const imageWaterfall = path.join(
|
||||||
|
fixturesDir,
|
||||||
|
'koushik-chowdavarapu-105425-unsplash.jpg'
|
||||||
|
);
|
||||||
|
|
||||||
|
const [attachmentCat] = await sendMessageWithAttachments(
|
||||||
|
page,
|
||||||
|
pinned,
|
||||||
|
'Message1',
|
||||||
|
[imageCat]
|
||||||
|
);
|
||||||
|
const [attachmentSnow, attachmentWaterfall] =
|
||||||
|
await sendMessageWithAttachments(page, pinned, 'Message2', [
|
||||||
|
imageSnow,
|
||||||
|
imageWaterfall,
|
||||||
|
]);
|
||||||
|
|
||||||
|
await sendAttachmentsBack('Message3', [attachmentCat]);
|
||||||
|
await sendAttachmentsBack('Message4', [
|
||||||
|
attachmentSnow,
|
||||||
|
attachmentWaterfall,
|
||||||
|
]);
|
||||||
|
|
||||||
|
debug('Clicking first image');
|
||||||
|
const FirstMessage = getTimelineMessageWithText(page, 'Message1');
|
||||||
|
const FirstImage = FirstMessage.locator('.module-image').nth(0);
|
||||||
|
|
||||||
|
await FirstImage.click();
|
||||||
|
|
||||||
|
const Lightbox = page.locator('.Lightbox');
|
||||||
|
const LightboxContent = Lightbox.locator('.Lightbox__zoomable-container');
|
||||||
|
const LighboxPrev = Lightbox.locator('.Lightbox__button--previous');
|
||||||
|
const LighboxNext = Lightbox.locator('.Lightbox__button--next');
|
||||||
|
|
||||||
|
await Lightbox.waitFor();
|
||||||
|
|
||||||
|
async function expectLightboxImage(
|
||||||
|
attachment: SignalService.IAttachmentPointer
|
||||||
|
) {
|
||||||
|
strictAssert(attachment.fileName, 'Must have filename');
|
||||||
|
const Object = LightboxContent.getByTestId(attachment.fileName);
|
||||||
|
debug(`Waiting for ${attachment.fileName}`);
|
||||||
|
await expect(Object).toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
const order = [
|
||||||
|
['sent 1: attachment 1', attachmentCat],
|
||||||
|
['sent 2: attachment 1', attachmentSnow],
|
||||||
|
['sent 2: attachment 2', attachmentWaterfall],
|
||||||
|
['received 1: attachment 1', attachmentCat],
|
||||||
|
['received 2: attachment 1', attachmentSnow],
|
||||||
|
['received 2: attachment 2', attachmentWaterfall],
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const reverseOrder = order.slice().reverse();
|
||||||
|
|
||||||
|
for (const [index, [label, attachment]] of order.entries()) {
|
||||||
|
if (index > 0) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await LighboxNext.click();
|
||||||
|
}
|
||||||
|
debug(label);
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await expectLightboxImage(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [index, [label, attachment]] of reverseOrder.entries()) {
|
||||||
|
if (index > 0) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await LighboxPrev.click();
|
||||||
|
}
|
||||||
|
debug(label);
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await expectLightboxImage(attachment);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2022 Signal Messenger, LLC
|
// Copyright 2022 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import type { ElectronApplication, Locator, Page } from 'playwright';
|
import type { ElectronApplication, Page } from 'playwright';
|
||||||
import { _electron as electron } from 'playwright';
|
import { _electron as electron } from 'playwright';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import pTimeout from 'p-timeout';
|
import pTimeout from 'p-timeout';
|
||||||
|
@ -89,19 +89,6 @@ export class App extends EventEmitter {
|
||||||
this.privApp.on('close', () => this.emit('close'));
|
this.privApp.on('close', () => this.emit('close'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async waitForEnabledComposer(): Promise<Locator> {
|
|
||||||
const window = await this.getWindow();
|
|
||||||
const composeArea = window.locator(
|
|
||||||
'.composition-area-wrapper, .Inbox__conversation .ConversationView'
|
|
||||||
);
|
|
||||||
const composeContainer = composeArea.locator(
|
|
||||||
'[data-testid=CompositionInput][data-enabled=true]'
|
|
||||||
);
|
|
||||||
await composeContainer.waitFor();
|
|
||||||
|
|
||||||
return composeContainer.locator('.ql-editor');
|
|
||||||
}
|
|
||||||
|
|
||||||
public async waitForProvisionURL(): Promise<string> {
|
public async waitForProvisionURL(): Promise<string> {
|
||||||
return this.waitForEvent('provisioning-url');
|
return this.waitForEvent('provisioning-url');
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,11 @@ import { toUntaggedPni } from '../../types/ServiceId';
|
||||||
import { MY_STORY_ID } from '../../types/Stories';
|
import { MY_STORY_ID } from '../../types/Stories';
|
||||||
import { Bootstrap } from '../bootstrap';
|
import { Bootstrap } from '../bootstrap';
|
||||||
import type { App } from '../bootstrap';
|
import type { App } from '../bootstrap';
|
||||||
import { expectSystemMessages, typeIntoInput } from '../helpers';
|
import {
|
||||||
|
expectSystemMessages,
|
||||||
|
typeIntoInput,
|
||||||
|
waitForEnabledComposer,
|
||||||
|
} from '../helpers';
|
||||||
|
|
||||||
export const debug = createDebug('mock:test:merge');
|
export const debug = createDebug('mock:test:merge');
|
||||||
|
|
||||||
|
@ -128,7 +132,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to ACI');
|
debug('Send message to ACI');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello ACI');
|
await typeIntoInput(compositionInput, 'Hello ACI');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -155,7 +159,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
if (withPNIMessage) {
|
if (withPNIMessage) {
|
||||||
debug('Send message to PNI');
|
debug('Send message to PNI');
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello PNI');
|
await typeIntoInput(compositionInput, 'Hello PNI');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -268,7 +272,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to merged contact');
|
debug('Send message to merged contact');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello merged');
|
await typeIntoInput(compositionInput, 'Hello merged');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -378,7 +382,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to merged contact');
|
debug('Send message to merged contact');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello merged');
|
await typeIntoInput(compositionInput, 'Hello merged');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -526,7 +530,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to merged contact');
|
debug('Send message to merged contact');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello merged');
|
await typeIntoInput(compositionInput, 'Hello merged');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
|
|
@ -12,7 +12,11 @@ import { MY_STORY_ID } from '../../types/Stories';
|
||||||
import { toUntaggedPni } from '../../types/ServiceId';
|
import { toUntaggedPni } from '../../types/ServiceId';
|
||||||
import { Bootstrap } from '../bootstrap';
|
import { Bootstrap } from '../bootstrap';
|
||||||
import type { App } from '../bootstrap';
|
import type { App } from '../bootstrap';
|
||||||
import { expectSystemMessages, typeIntoInput } from '../helpers';
|
import {
|
||||||
|
expectSystemMessages,
|
||||||
|
typeIntoInput,
|
||||||
|
waitForEnabledComposer,
|
||||||
|
} from '../helpers';
|
||||||
|
|
||||||
export const debug = createDebug('mock:test:merge');
|
export const debug = createDebug('mock:test:merge');
|
||||||
|
|
||||||
|
@ -97,7 +101,7 @@ describe('pnp/phone discovery', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to PNI and establish a session');
|
debug('Send message to PNI and establish a session');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello PNI');
|
await typeIntoInput(compositionInput, 'Hello PNI');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
|
|
@ -10,7 +10,11 @@ import * as durations from '../../util/durations';
|
||||||
import { generatePni, toUntaggedPni } from '../../types/ServiceId';
|
import { generatePni, toUntaggedPni } from '../../types/ServiceId';
|
||||||
import { Bootstrap } from '../bootstrap';
|
import { Bootstrap } from '../bootstrap';
|
||||||
import type { App } from '../bootstrap';
|
import type { App } from '../bootstrap';
|
||||||
import { expectSystemMessages, typeIntoInput } from '../helpers';
|
import {
|
||||||
|
expectSystemMessages,
|
||||||
|
typeIntoInput,
|
||||||
|
waitForEnabledComposer,
|
||||||
|
} from '../helpers';
|
||||||
|
|
||||||
export const debug = createDebug('mock:test:pni-change');
|
export const debug = createDebug('mock:test:pni-change');
|
||||||
|
|
||||||
|
@ -103,7 +107,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to contactA');
|
debug('Send message to contactA');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'message to contactA');
|
await typeIntoInput(compositionInput, 'message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -202,7 +206,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to contactA');
|
debug('Send message to contactA');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'message to contactA');
|
await typeIntoInput(compositionInput, 'message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -304,7 +308,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to contactA');
|
debug('Send message to contactA');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'message to contactA');
|
await typeIntoInput(compositionInput, 'message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -360,7 +364,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to contactB');
|
debug('Send message to contactB');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'message to contactB');
|
await typeIntoInput(compositionInput, 'message to contactB');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -435,7 +439,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to contactA');
|
debug('Send message to contactA');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'message to contactA');
|
await typeIntoInput(compositionInput, 'message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -521,7 +525,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to contactA');
|
debug('Send message to contactA');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'second message to contactA');
|
await typeIntoInput(compositionInput, 'second message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
|
|
@ -23,7 +23,11 @@ import {
|
||||||
RECEIPT_BATCHER_WAIT_MS,
|
RECEIPT_BATCHER_WAIT_MS,
|
||||||
} from '../../types/Receipt';
|
} from '../../types/Receipt';
|
||||||
import { sleep } from '../../util/sleep';
|
import { sleep } from '../../util/sleep';
|
||||||
import { expectSystemMessages, typeIntoInput } from '../helpers';
|
import {
|
||||||
|
expectSystemMessages,
|
||||||
|
typeIntoInput,
|
||||||
|
waitForEnabledComposer,
|
||||||
|
} from '../helpers';
|
||||||
|
|
||||||
export const debug = createDebug('mock:test:pni-signature');
|
export const debug = createDebug('mock:test:pni-signature');
|
||||||
|
|
||||||
|
@ -147,7 +151,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
||||||
}
|
}
|
||||||
debug('Enter first message text');
|
debug('Enter first message text');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'first');
|
await typeIntoInput(compositionInput, 'first');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -175,7 +179,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
||||||
}
|
}
|
||||||
debug('Enter second message text');
|
debug('Enter second message text');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'second');
|
await typeIntoInput(compositionInput, 'second');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -209,7 +213,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Enter third message text');
|
debug('Enter third message text');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'third');
|
await typeIntoInput(compositionInput, 'third');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -373,7 +377,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
{
|
{
|
||||||
debug('Wait for composition input to clear');
|
debug('Wait for composition input to clear');
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
debug('Enter an ACI message text');
|
debug('Enter an ACI message text');
|
||||||
await typeIntoInput(compositionInput, 'Hello ACI');
|
await typeIntoInput(compositionInput, 'Hello ACI');
|
||||||
|
|
|
@ -12,7 +12,11 @@ import { uuidToBytes } from '../../util/uuidToBytes';
|
||||||
import { MY_STORY_ID } from '../../types/Stories';
|
import { MY_STORY_ID } from '../../types/Stories';
|
||||||
import { Bootstrap } from '../bootstrap';
|
import { Bootstrap } from '../bootstrap';
|
||||||
import type { App } from '../bootstrap';
|
import type { App } from '../bootstrap';
|
||||||
import { bufferToUuid, typeIntoInput } from '../helpers';
|
import {
|
||||||
|
bufferToUuid,
|
||||||
|
typeIntoInput,
|
||||||
|
waitForEnabledComposer,
|
||||||
|
} from '../helpers';
|
||||||
import { contactByEncryptedUsernameRoute } from '../../util/signalRoutes';
|
import { contactByEncryptedUsernameRoute } from '../../util/signalRoutes';
|
||||||
|
|
||||||
export const debug = createDebug('mock:test:username');
|
export const debug = createDebug('mock:test:username');
|
||||||
|
@ -97,7 +101,7 @@ describe('pnp/username', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Send message to username');
|
debug('Send message to username');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello username');
|
await typeIntoInput(compositionInput, 'Hello username');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -317,7 +321,7 @@ describe('pnp/username', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('sending a message');
|
debug('sending a message');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello Carl');
|
await typeIntoInput(compositionInput, 'Hello Carl');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -374,7 +378,7 @@ describe('pnp/username', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('sending a message');
|
debug('sending a message');
|
||||||
{
|
{
|
||||||
const compositionInput = await app.waitForEnabledComposer();
|
const compositionInput = await waitForEnabledComposer(window);
|
||||||
|
|
||||||
await typeIntoInput(compositionInput, 'Hello Carl');
|
await typeIntoInput(compositionInput, 'Hello Carl');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { Bootstrap } from '../bootstrap';
|
||||||
import type { App } from '../bootstrap';
|
import type { App } from '../bootstrap';
|
||||||
import { ReceiptType } from '../../types/Receipt';
|
import { ReceiptType } from '../../types/Receipt';
|
||||||
import { toUntaggedPni } from '../../types/ServiceId';
|
import { toUntaggedPni } from '../../types/ServiceId';
|
||||||
import { typeIntoInput } from '../helpers';
|
import { typeIntoInput, waitForEnabledComposer } from '../helpers';
|
||||||
|
|
||||||
export const debug = createDebug('mock:test:challenge:receipts');
|
export const debug = createDebug('mock:test:challenge:receipts');
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Sending a message back to user - will trigger captcha!');
|
debug('Sending a message back to user - will trigger captcha!');
|
||||||
{
|
{
|
||||||
const input = await app.waitForEnabledComposer();
|
const input = await waitForEnabledComposer(window);
|
||||||
await typeIntoInput(input, 'Hi, good to hear from you!');
|
await typeIntoInput(input, 'Hi, good to hear from you!');
|
||||||
await input.press('Enter');
|
await input.press('Enter');
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) {
|
||||||
|
|
||||||
debug('Sending a message back to ContactB - will trigger captcha!');
|
debug('Sending a message back to ContactB - will trigger captcha!');
|
||||||
{
|
{
|
||||||
const input = await app.waitForEnabledComposer();
|
const input = await waitForEnabledComposer(window);
|
||||||
await typeIntoInput(input, 'Hi, good to hear from you!');
|
await typeIntoInput(input, 'Hi, good to hear from you!');
|
||||||
await input.press('Enter');
|
await input.press('Enter');
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { assert } from 'chai';
|
||||||
import * as durations from '../../util/durations';
|
import * as durations from '../../util/durations';
|
||||||
import type { App, Bootstrap } from './fixtures';
|
import type { App, Bootstrap } from './fixtures';
|
||||||
import { initStorage, debug } from './fixtures';
|
import { initStorage, debug } from './fixtures';
|
||||||
import { typeIntoInput } from '../helpers';
|
import { typeIntoInput, waitForEnabledComposer } from '../helpers';
|
||||||
|
|
||||||
describe('storage service', function (this: Mocha.Suite) {
|
describe('storage service', function (this: Mocha.Suite) {
|
||||||
this.timeout(durations.MINUTE);
|
this.timeout(durations.MINUTE);
|
||||||
|
@ -117,7 +117,7 @@ describe('storage service', function (this: Mocha.Suite) {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('Enter message text');
|
debug('Enter message text');
|
||||||
const input = await app.waitForEnabledComposer();
|
const input = await waitForEnabledComposer(window);
|
||||||
await typeIntoInput(input, 'hello stranger!');
|
await typeIntoInput(input, 'hello stranger!');
|
||||||
await input.press('Enter');
|
await input.press('Enter');
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue