diff --git a/ts/test-mock/benchmarks/group_send_bench.ts b/ts/test-mock/benchmarks/group_send_bench.ts index c585ee501210..a0697beff646 100644 --- a/ts/test-mock/benchmarks/group_send_bench.ts +++ b/ts/test-mock/benchmarks/group_send_bench.ts @@ -22,6 +22,7 @@ import { import { stats } from '../../util/benchmark/stats'; import { sleep } from '../../util/sleep'; import { MINUTE } from '../../util/durations'; +import { typeIntoInput } from '../helpers'; const LAST_MESSAGE = 'start sending messages now'; @@ -171,7 +172,7 @@ Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise => { }); debug('entering message text'); - await input.type(`my message ${runId}`); + await typeIntoInput(input, `my message ${runId}`); await input.press('Enter'); debug('waiting for message on server side'); diff --git a/ts/test-mock/benchmarks/send_bench.ts b/ts/test-mock/benchmarks/send_bench.ts index 36349312acb9..b677d0195b6f 100644 --- a/ts/test-mock/benchmarks/send_bench.ts +++ b/ts/test-mock/benchmarks/send_bench.ts @@ -8,6 +8,7 @@ import { ReceiptType } from '@signalapp/mock-server'; import { Bootstrap, debug, RUN_COUNT, DISCARD_COUNT } from './fixtures'; import { stats } from '../../util/benchmark/stats'; +import { typeIntoInput } from '../helpers'; const CONVERSATION_SIZE = 500; // messages @@ -88,7 +89,7 @@ Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise => { const input = await app.waitForEnabledComposer(); debug('entering message text'); - await input.type(`my message ${runId}`); + await typeIntoInput(input, `my message ${runId}`); await input.press('Enter'); debug('waiting for message on server side'); diff --git a/ts/test-mock/helpers.ts b/ts/test-mock/helpers.ts index 395a5ef72f7f..42f322ef374b 100644 --- a/ts/test-mock/helpers.ts +++ b/ts/test-mock/helpers.ts @@ -3,6 +3,7 @@ import { assert } from 'chai'; import type { Locator, Page } from 'playwright'; +import { expect } from 'playwright/test'; export function bufferToUuid(buffer: Buffer): string { const hex = buffer.toString('hex'); @@ -16,22 +17,30 @@ export function bufferToUuid(buffer: Buffer): string { ].join('-'); } -export async function type(input: Locator, text: string): Promise { +export async function typeIntoInput( + input: Locator, + text: string +): Promise { let currentValue = ''; + let isInputElement = true; try { currentValue = await input.inputValue(); } catch (e) { + isInputElement = false; // if input is actually not an input (e.g. contenteditable) currentValue = (await input.textContent()) ?? ''; } - // Type with a reasonably human delay - await input.type(text, { delay: 100 }); + await input.type(text, { delay: 30 }); // Wait to ensure that the input (and react state controlling it) has actually // updated with the right value - await input.locator(`:text("${currentValue}${text}")`).waitFor(); + if (isInputElement) { + await expect(input).toHaveValue(`${currentValue}${text}`); + } else { + await input.locator(`:text("${currentValue}${text}")`).waitFor(); + } } export async function expectItemsWithText( diff --git a/ts/test-mock/messaging/edit_test.ts b/ts/test-mock/messaging/edit_test.ts index 1999ebc37442..10a8897d0c61 100644 --- a/ts/test-mock/messaging/edit_test.ts +++ b/ts/test-mock/messaging/edit_test.ts @@ -16,7 +16,7 @@ import { drop } from '../../util/drop'; import { strictAssert } from '../../util/assert'; import { generateAci } from '../../types/ServiceId'; import { IMAGE_GIF } from '../../types/MIME'; -import { type } from '../helpers'; +import { typeIntoInput } from '../helpers'; import type { MessageAttributesType } from '../../model-types'; import { sleep } from '../../util/sleep'; @@ -95,6 +95,21 @@ describe('editing', function (this: Mocha.Suite) { let bootstrap: Bootstrap; let app: App; + async function sendEditedMessage( + page: Page, + timestamp: number, + additionalText: string + ) { + await page + .getByTestId(`${timestamp}`) + .locator('.module-message__buttons__menu') + .click(); + await page.getByRole('menuitem', { name: 'Edit' }).click(); + const input = await app.waitForEnabledComposer(); + await typeIntoInput(input, additionalText); + await input.press('Enter'); + } + beforeEach(async () => { bootstrap = new Bootstrap(); await bootstrap.init(); @@ -309,7 +324,7 @@ describe('editing', function (this: Mocha.Suite) { const input = await app.waitForEnabledComposer(); debug('entering original message text'); - await input.type('edit message 1'); + await typeIntoInput(input, 'edit message 1'); await input.press('Enter'); } @@ -317,23 +332,11 @@ describe('editing', function (this: Mocha.Suite) { const { dataMessage: originalMessage } = await friend.waitForMessage(); assert.strictEqual(originalMessage.body, 'edit message 1'); - const message = window.locator( - `.module-message[data-testid="${originalMessage.timestamp}"]` + await sendEditedMessage( + window, + originalMessage.timestamp?.toNumber() ?? 0, + '.2' ); - await message.waitFor(); - - debug('opening context menu'); - await message.locator('[aria-label="More actions"]').click(); - - debug('starting message edit'); - await window.locator('.module-message__context__edit-message').click(); - - { - const input = await app.waitForEnabledComposer(); - await input.press('Backspace'); - await input.type('2'); - await input.press('Enter'); - } debug("waiting for friend's edit message"); const { editMessage: firstEdit } = await friend.waitForEditMessage(); @@ -341,30 +344,20 @@ describe('editing', function (this: Mocha.Suite) { firstEdit.targetSentTimestamp?.toNumber(), originalMessage.timestamp?.toNumber() ); - assert.strictEqual(firstEdit.dataMessage?.body, 'edit message 2'); + assert.strictEqual(firstEdit.dataMessage?.body, 'edit message 1.2'); - debug('opening context menu again'); - const firstEditMessage = window.locator( - `.module-message[data-testid="${firstEdit.dataMessage?.timestamp?.toNumber()}"]` + await sendEditedMessage( + window, + firstEdit.dataMessage?.timestamp?.toNumber() ?? 0, + '.3' ); - await firstEditMessage.locator('[aria-label="More actions"]').click(); - - debug('starting second message edit'); - await window.locator('.module-message__context__edit-message').click(); - - { - const input = await app.waitForEnabledComposer(); - await input.press('Backspace'); - await input.type('3'); - await input.press('Enter'); - } const { editMessage: secondEdit } = await friend.waitForEditMessage(); assert.strictEqual( secondEdit.targetSentTimestamp?.toNumber(), firstEdit.dataMessage?.timestamp?.toNumber() ); - assert.strictEqual(secondEdit.dataMessage?.body, 'edit message 3'); + assert.strictEqual(secondEdit.dataMessage?.body, 'edit message 1.2.3'); debug('opening edit history'); const secondEditMessage = window.locator( @@ -380,8 +373,8 @@ describe('editing', function (this: Mocha.Suite) { assert.strictEqual(await history.count(), 3); assert.isTrue(await history.locator('"edit message 1"').isVisible()); - assert.isTrue(await history.locator('"edit message 2"').isVisible()); - assert.isTrue(await history.locator('"edit message 3"').isVisible()); + assert.isTrue(await history.locator('"edit message 1.2"').isVisible()); + assert.isTrue(await history.locator('"edit message 1.2.3"').isVisible()); }); it('is fine with out of order edit processing', async () => { @@ -517,20 +510,6 @@ describe('editing', function (this: Mocha.Suite) { return messages[0]; } - async function editMessage( - page: Page, - timestamp: number, - additionalText: string - ) { - await page - .getByTestId(`${timestamp}`) - .locator('.module-message__buttons__menu') - .click(); - await page.getByRole('menuitem', { name: 'Edit' }).click(); - const input = await app.waitForEnabledComposer(); - await type(input, additionalText); - await input.press('Enter'); - } const { contacts, desktop } = bootstrap; const [friend] = contacts; @@ -566,7 +545,7 @@ describe('editing', function (this: Mocha.Suite) { const input = await app.waitForEnabledComposer(); debug('sending message desktop -> friend'); - await input.type(originalText); + await typeIntoInput(input, originalText); await input.press('Enter'); } @@ -636,7 +615,7 @@ describe('editing', function (this: Mocha.Suite) { debug('finding composition input and clicking it v2'); debug('sending edit message v2 desktop -> friend'); - await editMessage(page, originalMessageTimestamp, '2'); + await sendEditedMessage(page, originalMessageTimestamp, '2'); { const readReceiptTimestamp = bootstrap.getTimestamp(); @@ -688,7 +667,7 @@ describe('editing', function (this: Mocha.Suite) { // v3 will be read after we receive v4 const editMessageV3Text = '123'; debug('sending edit message v3 desktop -> friend'); - await editMessage( + await sendEditedMessage( page, editMessageV2.dataMessage?.timestamp?.toNumber() ?? 0, '3' @@ -709,7 +688,7 @@ describe('editing', function (this: Mocha.Suite) { // testing send state of the full message const editMessageV4Text = '1234'; debug('sending edit message v4 desktop -> friend'); - await editMessage( + await sendEditedMessage( page, editMessageV3.dataMessage?.timestamp?.toNumber() ?? 0, '4' diff --git a/ts/test-mock/pnp/merge_test.ts b/ts/test-mock/pnp/merge_test.ts index a173f84b5baf..6dc24eb7ce6e 100644 --- a/ts/test-mock/pnp/merge_test.ts +++ b/ts/test-mock/pnp/merge_test.ts @@ -14,7 +14,7 @@ import { toUntaggedPni } from '../../types/ServiceId'; import { MY_STORY_ID } from '../../types/Stories'; import { Bootstrap } from '../bootstrap'; import type { App } from '../bootstrap'; -import { expectSystemMessages } from '../helpers'; +import { expectSystemMessages, typeIntoInput } from '../helpers'; export const debug = createDebug('mock:test:merge'); @@ -130,7 +130,7 @@ describe('pnp/merge', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('Hello ACI'); + await typeIntoInput(compositionInput, 'Hello ACI'); await compositionInput.press('Enter'); } @@ -157,7 +157,7 @@ describe('pnp/merge', function (this: Mocha.Suite) { debug('Send message to PNI'); const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('Hello PNI'); + await typeIntoInput(compositionInput, 'Hello PNI'); await compositionInput.press('Enter'); } @@ -270,7 +270,7 @@ describe('pnp/merge', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('Hello merged'); + await typeIntoInput(compositionInput, 'Hello merged'); await compositionInput.press('Enter'); } @@ -380,7 +380,7 @@ describe('pnp/merge', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('Hello merged'); + await typeIntoInput(compositionInput, 'Hello merged'); await compositionInput.press('Enter'); } diff --git a/ts/test-mock/pnp/phone_discovery_test.ts b/ts/test-mock/pnp/phone_discovery_test.ts index b469e510585d..a3fed91b9df6 100644 --- a/ts/test-mock/pnp/phone_discovery_test.ts +++ b/ts/test-mock/pnp/phone_discovery_test.ts @@ -12,7 +12,7 @@ import { MY_STORY_ID } from '../../types/Stories'; import { toUntaggedPni } from '../../types/ServiceId'; import { Bootstrap } from '../bootstrap'; import type { App } from '../bootstrap'; -import { expectSystemMessages } from '../helpers'; +import { expectSystemMessages, typeIntoInput } from '../helpers'; export const debug = createDebug('mock:test:merge'); @@ -99,7 +99,7 @@ describe('pnp/phone discovery', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('Hello PNI'); + await typeIntoInput(compositionInput, 'Hello PNI'); await compositionInput.press('Enter'); } diff --git a/ts/test-mock/pnp/pni_change_test.ts b/ts/test-mock/pnp/pni_change_test.ts index e034178ed7c3..1014b9ee3b2f 100644 --- a/ts/test-mock/pnp/pni_change_test.ts +++ b/ts/test-mock/pnp/pni_change_test.ts @@ -10,7 +10,7 @@ import * as durations from '../../util/durations'; import { generatePni, toUntaggedPni } from '../../types/ServiceId'; import { Bootstrap } from '../bootstrap'; import type { App } from '../bootstrap'; -import { expectSystemMessages } from '../helpers'; +import { expectSystemMessages, typeIntoInput } from '../helpers'; export const debug = createDebug('mock:test:pni-change'); @@ -105,7 +105,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('message to contactA'); + await typeIntoInput(compositionInput, 'message to contactA'); await compositionInput.press('Enter'); } @@ -204,7 +204,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('message to contactA'); + await typeIntoInput(compositionInput, 'message to contactA'); await compositionInput.press('Enter'); } @@ -306,7 +306,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('message to contactA'); + await typeIntoInput(compositionInput, 'message to contactA'); await compositionInput.press('Enter'); } @@ -362,7 +362,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('message to contactB'); + await typeIntoInput(compositionInput, 'message to contactB'); await compositionInput.press('Enter'); // We get a safety number change warning, because we get a different identity key! @@ -437,7 +437,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('message to contactA'); + await typeIntoInput(compositionInput, 'message to contactA'); await compositionInput.press('Enter'); } @@ -523,7 +523,7 @@ describe('pnp/PNI Change', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('second message to contactA'); + await typeIntoInput(compositionInput, 'second message to contactA'); await compositionInput.press('Enter'); } diff --git a/ts/test-mock/pnp/pni_signature_test.ts b/ts/test-mock/pnp/pni_signature_test.ts index 1347861a5a1e..f2f05ef7e968 100644 --- a/ts/test-mock/pnp/pni_signature_test.ts +++ b/ts/test-mock/pnp/pni_signature_test.ts @@ -23,7 +23,7 @@ import { RECEIPT_BATCHER_WAIT_MS, } from '../../types/Receipt'; import { sleep } from '../../util/sleep'; -import { expectSystemMessages } from '../helpers'; +import { expectSystemMessages, typeIntoInput } from '../helpers'; export const debug = createDebug('mock:test:pni-signature'); @@ -149,7 +149,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('first'); + await typeIntoInput(compositionInput, 'first'); await compositionInput.press('Enter'); } debug('Wait for the first message with pni signature'); @@ -177,7 +177,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('second'); + await typeIntoInput(compositionInput, 'second'); await compositionInput.press('Enter'); } debug('Wait for the second message with pni signature'); @@ -211,7 +211,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('third'); + await typeIntoInput(compositionInput, 'third'); await compositionInput.press('Enter'); } debug('Wait for the third message without pni signature'); @@ -376,7 +376,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) { const compositionInput = await app.waitForEnabledComposer(); debug('Enter an ACI message text'); - await compositionInput.type('Hello ACI'); + await typeIntoInput(compositionInput, 'Hello ACI'); await compositionInput.press('Enter'); } diff --git a/ts/test-mock/pnp/username_test.ts b/ts/test-mock/pnp/username_test.ts index d08f0c89fa51..e63ad6efd947 100644 --- a/ts/test-mock/pnp/username_test.ts +++ b/ts/test-mock/pnp/username_test.ts @@ -12,7 +12,7 @@ import { uuidToBytes } from '../../util/uuidToBytes'; import { MY_STORY_ID } from '../../types/Stories'; import { Bootstrap } from '../bootstrap'; import type { App } from '../bootstrap'; -import { bufferToUuid } from '../helpers'; +import { bufferToUuid, typeIntoInput } from '../helpers'; import { contactByEncryptedUsernameRoute } from '../../util/signalRoutes'; export const debug = createDebug('mock:test:username'); @@ -99,7 +99,7 @@ describe('pnp/username', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('Hello username'); + await typeIntoInput(compositionInput, 'Hello username'); await compositionInput.press('Enter'); } @@ -191,7 +191,7 @@ describe('pnp/username', function (this: Mocha.Suite) { debug('entering new username'); const usernameField = profileEditor.locator('.Input__input'); - await usernameField.type(NICKNAME); + await typeIntoInput(usernameField, NICKNAME); debug('waiting for generated discriminator'); const discriminator = profileEditor.locator( @@ -308,16 +308,18 @@ describe('pnp/username', function (this: Mocha.Suite) { await window.getByRole('button', { name: 'New chat' }).click(); const searchInput = window.locator('.module-SearchInput__container input'); - await searchInput.type(CARL_USERNAME); + await typeIntoInput(searchInput, CARL_USERNAME); debug('starting lookup'); - await window.locator(`div.ListTile >> "${CARL_USERNAME}"`).click(); + await window + .locator(`div.ListTile >> "${CARL_USERNAME}"`) + .click({ timeout: 2000 }); debug('sending a message'); { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('Hello Carl'); + await typeIntoInput(compositionInput, 'Hello Carl'); await compositionInput.press('Enter'); const { body, source } = await carl.waitForMessage(); @@ -374,7 +376,7 @@ describe('pnp/username', function (this: Mocha.Suite) { { const compositionInput = await app.waitForEnabledComposer(); - await compositionInput.type('Hello Carl'); + await typeIntoInput(compositionInput, 'Hello Carl'); await compositionInput.press('Enter'); const { body, source } = await carl.waitForMessage(); diff --git a/ts/test-mock/rate-limit/viewed_test.ts b/ts/test-mock/rate-limit/viewed_test.ts index 0575ced139cd..a3e9e1b97459 100644 --- a/ts/test-mock/rate-limit/viewed_test.ts +++ b/ts/test-mock/rate-limit/viewed_test.ts @@ -10,6 +10,7 @@ import { Bootstrap } from '../bootstrap'; import type { App } from '../bootstrap'; import { ReceiptType } from '../../types/Receipt'; import { toUntaggedPni } from '../../types/ServiceId'; +import { typeIntoInput } from '../helpers'; export const debug = createDebug('mock:test:challenge:receipts'); @@ -121,7 +122,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) { debug('Sending a message back to user - will trigger captcha!'); { const input = await app.waitForEnabledComposer(); - await input.type('Hi, good to hear from you!'); + await typeIntoInput(input, 'Hi, good to hear from you!'); await input.press('Enter'); } @@ -201,7 +202,7 @@ describe('challenge/receipts', function (this: Mocha.Suite) { debug('Sending a message back to ContactB - will trigger captcha!'); { const input = await app.waitForEnabledComposer(); - await input.type('Hi, good to hear from you!'); + await typeIntoInput(input, 'Hi, good to hear from you!'); await input.press('Enter'); } diff --git a/ts/test-mock/storage/message_request_test.ts b/ts/test-mock/storage/message_request_test.ts index 0f5dd28572e3..1e348902f8e4 100644 --- a/ts/test-mock/storage/message_request_test.ts +++ b/ts/test-mock/storage/message_request_test.ts @@ -6,6 +6,7 @@ import { assert } from 'chai'; import * as durations from '../../util/durations'; import type { App, Bootstrap } from './fixtures'; import { initStorage, debug } from './fixtures'; +import { typeIntoInput } from '../helpers'; describe('storage service', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); @@ -121,7 +122,7 @@ describe('storage service', function (this: Mocha.Suite) { ); const input = composeArea.locator('[data-testid=CompositionInput]'); - await input.type('hello stranger!'); + await typeIntoInput(input, 'hello stranger!'); await input.press('Enter'); {