diff --git a/ts/state/selectors/message.ts b/ts/state/selectors/message.ts index cb0ef8589d..edba56a824 100644 --- a/ts/state/selectors/message.ts +++ b/ts/state/selectors/message.ts @@ -734,7 +734,7 @@ export const getPropsForMessage = ( textAttachment, payment, canCopy: canCopy(message), - canEditMessage: canEditMessage(message), + canEditMessage: !conversation.draftText && canEditMessage(message), canDeleteForEveryone: canDeleteForEveryone(message, conversation.isMe), canDownload: canDownload(message, conversationSelector), canReact: canReact(message, ourConversationId, conversationSelector), diff --git a/ts/test-mock/bootstrap.ts b/ts/test-mock/bootstrap.ts index 5a35b1744b..2c908a985d 100644 --- a/ts/test-mock/bootstrap.ts +++ b/ts/test-mock/bootstrap.ts @@ -25,6 +25,7 @@ import { drop } from '../util/drop'; import type { RendererConfigType } from '../types/RendererConfig'; import { App } from './playwright'; import { CONTACT_COUNT } from './benchmarks/fixtures'; +import { SignalDesktopUI } from './signal-desktop-ui'; export { App }; @@ -347,6 +348,14 @@ export class Bootstrap { return app; } + public async signalDesktopUI(): Promise { + assert( + this.lastApp !== undefined, + 'Bootstrap has to be initialized first, see: bootstrap.init()' + ); + return new SignalDesktopUI(this.lastApp); + } + public async linkAndClose(): Promise { const app = await this.link(); diff --git a/ts/test-mock/messaging/defects/6659_editing_a_sent_message_deletes_draft_of_new_message_tests.ts b/ts/test-mock/messaging/defects/6659_editing_a_sent_message_deletes_draft_of_new_message_tests.ts new file mode 100644 index 0000000000..e401d06deb --- /dev/null +++ b/ts/test-mock/messaging/defects/6659_editing_a_sent_message_deletes_draft_of_new_message_tests.ts @@ -0,0 +1,84 @@ +// Copyright 2023 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { Proto } from '@signalapp/mock-server'; +import { assert } from 'chai'; +import Long from 'long'; +import type { App } from '../../playwright'; +import * as durations from '../../../util/durations'; +import { Bootstrap } from '../../bootstrap'; +import type { SignalDesktopUI } from '../../signal-desktop-ui'; +import { sleep } from '../../../util/sleep'; + +const pause = process.env.PAUSE; + +const createMessage = (body: string): Proto.IDataMessage => { + return { + body, + groupV2: undefined, + timestamp: Long.fromNumber(Date.now()), + }; +}; + +// https://github.com/signalapp/Signal-Desktop/issues/6659 +describe('[6659] Editing a sent message does not delete draft of new message', function (this: Mocha.Suite) { + this.timeout(durations.MINUTE); + + let bootstrap: Bootstrap; + let app: App; + let ui: SignalDesktopUI; + let sentMessage: Proto.IDataMessage; + + beforeEach(async () => { + bootstrap = new Bootstrap({}); + await bootstrap.init(); + app = await bootstrap.link(); + ui = await bootstrap.signalDesktopUI(); + + const { phone, desktop } = bootstrap; + + sentMessage = createMessage('A B C'); + + await phone.sendRaw( + desktop, + { + dataMessage: sentMessage, + }, + { + timestamp: Number(sentMessage.timestamp), + } + ); + }); + + afterEach(async function (this: Mocha.Context) { + if (!pause) { + await bootstrap?.maybeSaveLogs(this.currentTest, app); + await app?.close(); + await bootstrap?.teardown(); + } + }); + + /* + + See: `ts/components/conversation/MessageContextMenu.tsx` + + */ + it('disallows editing sent messages when there is a draft present', async () => { + await ui.openFirstConversation(); + await ui.typeMessage('Draft message'); + + // [!] Allow time for the menu to re-render + await sleep(100); + + assert.isFalse( + await ui.isShowingEditMessageMenuItem(sentMessage.timestamp) + ); + + await ui.clearMessage(); + + // [!] Allow time for the menu to re-render + await sleep(100); + + assert.isTrue(await ui.isShowingEditMessageMenuItem(sentMessage.timestamp)); + }); +}); diff --git a/ts/test-mock/signal-desktop-ui.ts b/ts/test-mock/signal-desktop-ui.ts new file mode 100644 index 0000000000..21bc047e86 --- /dev/null +++ b/ts/test-mock/signal-desktop-ui.ts @@ -0,0 +1,71 @@ +import { Locator } from 'playwright'; +import type { App } from './playwright'; + +export class SignalDesktopUI { + constructor(private app: App) {} + + public openFirstConversation = async (): Promise => { + const window = await this.app.getWindow(); + const leftPane = window.locator('#LeftPane'); + + await leftPane + .locator('.module-conversation-list__item--contact-or-conversation') + .first() + .click(); + }; + + public editMessage = async ( + timestamp: Long | null | undefined, + text: string + ): Promise => { + const editButton = await this.editMessageButton(timestamp); + + await editButton.click(); + + await this.typeMessage(text); + + await this.sendMessage(); + }; + + public isShowingEditMessageMenuItem = async ( + timestamp: Long | null | undefined + ): Promise => { + const page = await this.app.getWindow(); + + await page + .getByTestId(`${timestamp}`) + .locator('.module-message__buttons__menu') + .click(); + + const result = await page + .getByRole('menuitem', { name: 'Edit' }) + .isVisible(); + + await page.keyboard.press('Escape'); + + return result; + }; + + public typeMessage = async (text: string): Promise => { + const messageTextInput = await this.getMessageTextInput(); + await messageTextInput.fill(text); + }; + + public clearMessage = async (): Promise => { + const messageTextInput = await this.getMessageTextInput(); + await messageTextInput.clear(); + }; + + public sendMessage = async (): Promise => { + const messageTextInput = await this.getMessageTextInput(); + await messageTextInput.press('Enter'); + }; + + public messageText = async (): Promise => { + const messageTextInput = await this.getMessageTextInput(); + return messageTextInput.textContent(); + }; + + private getMessageTextInput = (): Promise => + this.app.waitForEnabledComposer(); +}