diff --git a/tests/automation/enforce_localized_str.spec.ts b/tests/automation/enforce_localized_str.spec.ts index ec9dc63..98f7a3b 100644 --- a/tests/automation/enforce_localized_str.spec.ts +++ b/tests/automation/enforce_localized_str.spec.ts @@ -259,7 +259,10 @@ function getExpectedStringFromKey( return "You don't have any conversations yet"; case 'onboardingHitThePlusButton': return 'Hit the plus button to start a chat, create a group, or join an official community!'; - + case 'modalMessageTooLongTitle': + return 'Message Too Long'; + case 'modalMessageTooLongDescription': + return 'Please shorten your message to {limit} characters or less.'; default: // returning null means we don't have an expected string yet for this key. // This will make the test fail diff --git a/tests/automation/locators/index.ts b/tests/automation/locators/index.ts index 7db611a..5279174 100644 --- a/tests/automation/locators/index.ts +++ b/tests/automation/locators/index.ts @@ -159,6 +159,9 @@ export class Settings extends Locator { ); static readonly confirmPasswordInput = this.testId('password-input-confirm'); static readonly enableCalls = this.testId('enable-calls-settings-row'); + static readonly enableCommunityMessageRequests = this.testId( + 'enable-communities-message-requests-settings-row', + ); static readonly enableMicrophone = this.testId( 'enable-microphone-settings-row', ); diff --git a/tests/automation/message_checks.spec.ts b/tests/automation/message_checks.spec.ts index bb31894..af402d1 100644 --- a/tests/automation/message_checks.spec.ts +++ b/tests/automation/message_checks.spec.ts @@ -24,6 +24,7 @@ import { trustUser, } from './utilities/send_media'; import { + checkModalStrings, clickOn, clickOnElement, clickOnMatchingText, @@ -255,3 +256,106 @@ sessionTestTwoWindows( console.log(timesArray); }, ); + +// Message length limit tests (pre-pro) +const maxChars = 2000; +const countdownThreshold = 1800; + +const messageLengthTestCases = [ + { + length: 1799, + char: 'a', + shouldSend: true, + }, + { + length: 1800, + char: 'b', + shouldSend: true, + }, + { + length: 2000, + char: 'c', + shouldSend: true, + }, + { + length: 2001, + char: 'd', + shouldSend: false, + }, +]; + +messageLengthTestCases.forEach((testCase) => { + test_Alice_1W_Bob_1W( + `Message length limit (${testCase.length} chars)`, + async ({ alice, aliceWindow1, bob, bobWindow1 }) => { + await createContact(aliceWindow1, bobWindow1, alice, bob); + const expectedCount = + testCase.length < countdownThreshold + ? null + : (maxChars - testCase.length).toString(); + const message = testCase.char.repeat(testCase.length); + // Type the message + await typeIntoInput( + aliceWindow1, + 'message-input-text-area', + message, + true, // Paste because otherwise Playwright times out + ); + + // Check countdown behavior + if (expectedCount) { + await waitForTestIdWithText( + aliceWindow1, + 'tooltip-character-count', + expectedCount, + ); + } else { + // Verify countdown tooltip is not present + try { + await waitForElement( + aliceWindow1, + 'data-testid', + 'tooltip-character-count', + 1000, + ); + throw new Error( + `Countdown should not be visible for messages under ${countdownThreshold} chars`, + ); + } catch (e) { + // Expected - countdown should not exist + console.log('Countdown not present as expected'); + } + } + + // Try to send + await clickOn(aliceWindow1, Conversation.sendMessageButton); + + if (testCase.shouldSend) { + // Message should appear in Alice's window + await Promise.all( + [aliceWindow1, bobWindow1].map(async (w) => { + await waitForTextMessage(w, message); + }), + ); + } else { + // Message Too Long modal + await checkModalStrings( + aliceWindow1, + englishStrippedStr('modalMessageTooLongTitle').toString(), + englishStrippedStr('modalMessageTooLongDescription') + .withArgs({ limit: maxChars.toLocaleString('en-AU') }) // Force "2,000" instead of "2000" + .toString(), + ); + await clickOn(aliceWindow1, Global.confirmButton); + + // Verify message didn't send + try { + await waitForTextMessage(aliceWindow1, message, 2000); + throw new Error('Message should not have been sent'); + } catch (e) { + console.log(`Message didn't send as expected`); + } + } + }, + ); +}); diff --git a/tests/automation/message_requests.spec.ts b/tests/automation/message_requests.spec.ts index 21d22f2..16bb2b4 100644 --- a/tests/automation/message_requests.spec.ts +++ b/tests/automation/message_requests.spec.ts @@ -1,3 +1,5 @@ +import { expect } from '@playwright/test'; + import { englishStrippedStr } from '../localization/englishStrippedStr'; import { Conversation, @@ -7,6 +9,7 @@ import { Settings, } from './locators'; import { test_Alice_1W_Bob_1W } from './setup/sessionTest'; +import { joinCommunity } from './utilities/join_community'; import { sendMessage } from './utilities/message'; import { sendNewMessage } from './utilities/send_message'; import { @@ -14,6 +17,7 @@ import { clickOn, clickOnMatchingText, clickOnWithText, + grabTextFromElement, waitForMatchingText, waitForTestIdWithText, } from './utilities/utils'; @@ -164,3 +168,81 @@ test_Alice_1W_Bob_1W( ); }, ); + +test_Alice_1W_Bob_1W( + 'Community message requests on', + async ({ alice, aliceWindow1, bob, bobWindow1 }) => { + await clickOn(bobWindow1, LeftPane.settingsButton); + await clickOn(bobWindow1, Settings.privacyMenuItem); + await clickOn(bobWindow1, Settings.enableCommunityMessageRequests); + await clickOn(bobWindow1, Global.modalCloseButton); + await Promise.all([joinCommunity(aliceWindow1), joinCommunity(bobWindow1)]); + const communityMsg = `I accept message requests + ${Date.now()}`; + await sendMessage(bobWindow1, communityMsg); + await clickOn(aliceWindow1, Conversation.scrollToBottomButton); + // Using native methods to locate the author corresponding to the sent message + await aliceWindow1 + .locator('.module-message__container', { hasText: communityMsg }) + .locator('..') // Go up to parent + .locator('svg') + .click(); + const elText = await grabTextFromElement( + aliceWindow1, + 'data-testid', + 'account-id', + ); + expect(elText).toMatch(/^15/); + await clickOn(aliceWindow1, HomeScreen.newMessageAccountIDInput); // yes this is the actual locator for the 'Message' button + await waitForTestIdWithText( + aliceWindow1, + 'header-conversation-name', + bob.userName, + ); + const messageRequestMsg = `${alice.userName} to ${bob.userName}`; + const messageRequestResponse = `${bob.userName} accepts message request`; + await sendMessage(aliceWindow1, messageRequestMsg); + await clickOn(bobWindow1, HomeScreen.messageRequestBanner); + // Select message request from User A + await clickOnWithText( + bobWindow1, + HomeScreen.conversationItemName, + alice.userName, + ); + await sendMessage(bobWindow1, messageRequestResponse); + // Check config message of message request acceptance + await waitForTestIdWithText( + bobWindow1, + 'message-request-response-message', + englishStrippedStr('messageRequestYouHaveAccepted') + .withArgs({ + name: alice.userName, + }) + .toString(), + ); + }, +); +test_Alice_1W_Bob_1W( + 'Community message requests off', + async ({ aliceWindow1, bobWindow1 }) => { + await Promise.all([joinCommunity(aliceWindow1), joinCommunity(bobWindow1)]); + const communityMsg = `I do not accept message requests + ${Date.now()}`; + await sendMessage(bobWindow1, communityMsg); + await clickOn(aliceWindow1, Conversation.scrollToBottomButton); + // Using native methods to locate the author corresponding to the sent message + await aliceWindow1 + .locator('.module-message__container', { hasText: communityMsg }) + .locator('..') // Go up to parent + .locator('svg') + .click(); + const elText = await grabTextFromElement( + aliceWindow1, + 'data-testid', + 'account-id', + ); + expect(elText).toMatch(/^15/); + const messageButton = aliceWindow1.getByTestId( + HomeScreen.newMessageAccountIDInput.selector, + ); + await expect(messageButton).toHaveClass(/disabled/); + }, +); diff --git a/tests/automation/types/testing.ts b/tests/automation/types/testing.ts index c7c2d33..a2cad1d 100644 --- a/tests/automation/types/testing.ts +++ b/tests/automation/types/testing.ts @@ -120,6 +120,7 @@ export type DataTestId = | 'edit-profile-icon' | 'empty-conversation-notification' | 'enable-calls-settings-row' + | 'enable-communities-message-requests-settings-row' | 'enable-microphone-settings-row' | 'enable-read-receipts-settings-row' | 'end-call' @@ -186,6 +187,7 @@ export type DataTestId = | 'set-password-settings-button' | 'settings-section' | 'theme-section' + | 'tooltip-character-count' | 'unblock-button-settings-screen' | 'update-group-info-name-input' | 'update-profile-info-name-input' diff --git a/tests/automation/utilities/utils.ts b/tests/automation/utilities/utils.ts index ed7428f..78e53ad 100644 --- a/tests/automation/utilities/utils.ts +++ b/tests/automation/utilities/utils.ts @@ -373,6 +373,7 @@ export async function typeIntoInput( window: Page, dataTestId: DataTestId, text: string, + paste?: boolean, // typing long messages hits the runner timeout ) { console.info(`typeIntoInput testId: ${dataTestId} : "${text}"`); const builtSelector = `css=[data-testid=${dataTestId}]`; @@ -382,7 +383,11 @@ export async function typeIntoInput( await clickOn(window, locator); // reset the content to be empty before typing into the input await window.fill(builtSelector, ''); - return window.type(builtSelector, text); + if (paste) { + await window.fill(builtSelector, text); + } else { + await window.type(builtSelector, text); + } } export async function doesTextIncludeString(