diff --git a/.github/workflows/ui-automated-tests.yml b/.github/workflows/ui-automated-tests.yml index e2115f2ba8..1a5e8a78c7 100644 --- a/.github/workflows/ui-automated-tests.yml +++ b/.github/workflows/ui-automated-tests.yml @@ -380,12 +380,16 @@ jobs: run: | unzip Uplink-Mac-Universal.zip cp -r ./Uplink.app ./Uplink2.app + cp -r ./Uplink.app ./Uplink3.app perl -i -pe 's/im.satellite.uplink/im.satellite.uplinkChatUserA/g' ./Uplink.app/Contents/Info.plist perl -i -pe 's/im.satellite.uplink/im.satellite.uplinkChatUserB/g' ./Uplink2.app/Contents/Info.plist + perl -i -pe 's/im.satellite.uplink/im.satellite.uplinkChatUserC/g' ./Uplink3.app/Contents/Info.plist cp -r ./Uplink.app /Applications/ cp -r ./Uplink2.app /Applications/ + cp -r ./Uplink3.app /Applications/ sudo xattr -r -d com.apple.quarantine /Applications/Uplink.app sudo xattr -r -d com.apple.quarantine /Applications/Uplink2.app + sudo xattr -r -d com.apple.quarantine /Applications/Uplink3.app - name: Setup Node.js 🔨 uses: actions/setup-node@v3 @@ -467,6 +471,7 @@ jobs: path: | ~/.uplink/.user/debug.log ~/.uplinkUserB/.user/debug.log + ~/.uplinkUserC/.user/debug.log - name: Add label if any of test jobs failed if: failure() diff --git a/config/wdio.mac.multiremote.conf.ts b/config/wdio.mac.multiremote.conf.ts index 1d3d9262b0..2c4bef9eaf 100644 --- a/config/wdio.mac.multiremote.conf.ts +++ b/config/wdio.mac.multiremote.conf.ts @@ -7,6 +7,8 @@ const MACOS_USER_A_BUNDLE_ID = require("@helpers/constants").MACOS_USER_A_BUNDLE_ID; const MACOS_USER_B_BUNDLE_ID = require("@helpers/constants").MACOS_USER_B_BUNDLE_ID; +const MACOS_USER_C_BUNDLE_ID = + require("@helpers/constants").MACOS_USER_C_BUNDLE_ID; const MACOS_DRIVER = require("@helpers/constants").MACOS_DRIVER; const fsp = require("fs").promises; const { readFileSync, rmSync } = require("fs"); @@ -57,7 +59,7 @@ export const config: WebdriverIO.Config = { "appium:systemPort": 4725, "appium:prerun": { command: - 'do shell script "rm -rf ~/.uplink && rm -rf ~/.uplinkUserB"', + 'do shell script "rm -rf ~/.uplink && rm -rf ~/.uplinkUserB && rm -rf ~/.uplinkUserC"', }, }, ], @@ -100,6 +102,7 @@ export const config: WebdriverIO.Config = { onPrepare: async function () { const cacheFolderUserA = homedir() + "/.uplink/.user"; const cacheFolderUserB = homedir() + "/.uplinkUserB/.user"; + const cacheFolderUserC = homedir() + "/.uplinkUserC/.user"; const allureResultsFolder = join(process.cwd(), "./allure-results"); const testReportFolder = join(process.cwd(), "./test-report"); const testResultsFolder = join(process.cwd(), "./test-results"); @@ -116,6 +119,7 @@ export const config: WebdriverIO.Config = { try { await rmSync(cacheFolderUserA, { recursive: true, force: true }); await rmSync(cacheFolderUserB, { recursive: true, force: true }); + await rmSync(cacheFolderUserC, { recursive: true, force: true }); console.log("Deleted Cache Folder Successfully!"); } catch (error) { console.error( @@ -157,16 +161,26 @@ export const config: WebdriverIO.Config = { bundleId: MACOS_USER_B_BUNDLE_ID, }, ]); + await driver.executeScript("macos: terminateApp", [ + { + bundleId: MACOS_USER_C_BUNDLE_ID, + }, + ]); } }, }, afterSuite: async function (suite) { - // Close second application if open + // Close second and third applications if open await driver.executeScript("macos: terminateApp", [ { bundleId: MACOS_USER_B_BUNDLE_ID, }, ]); + await driver.executeScript("macos: terminateApp", [ + { + bundleId: MACOS_USER_C_BUNDLE_ID, + }, + ]); }, }; diff --git a/tests/helpers/commands.ts b/tests/helpers/commands.ts index 831307856f..b56c6cbc9e 100644 --- a/tests/helpers/commands.ts +++ b/tests/helpers/commands.ts @@ -12,6 +12,7 @@ import { MACOS_DRIVER, MACOS_USER_A_BUNDLE_ID, MACOS_USER_B_BUNDLE_ID, + MACOS_USER_C_BUNDLE_ID, WINDOWS_APP, WINDOWS_DRIVER, } from "./constants"; @@ -209,7 +210,23 @@ export async function launchFirstApplication() { } export async function launchSecondApplication(existingUser: boolean = true) { - await launchAppMacOS(MACOS_USER_B_BUNDLE_ID, "/.uplinkUserB"); + await launchAppMacOS( + MACOS_USER_B_BUNDLE_ID, + "/.uplinkUserB", + "/Applications/Uplink2.app", + ); + await browser.pause(5000); + if (existingUser === true) { + await loginWithTestUser(); + } +} + +export async function launchThirdApplication(existingUser: boolean = true) { + await launchAppMacOS( + MACOS_USER_C_BUNDLE_ID, + "/.uplinkUserC", + "/Applications/Uplink3.app", + ); await browser.pause(5000); if (existingUser === true) { await loginWithTestUser(); @@ -242,6 +259,19 @@ export async function activateSecondApplication() { } } +export async function activateThirdApplication() { + if (process.env.DRIVER === WINDOWS_DRIVER) { + await activateAppWindows(WINDOWS_APP); + } else if (process.env.DRIVER === MACOS_DRIVER) { + const appState = await queryAppStateMacOS(MACOS_USER_C_BUNDLE_ID); + if (appState === 1) { + await launchThirdApplication(); + } else { + await activateAppMacOS(MACOS_USER_C_BUNDLE_ID); + } + } +} + export async function closeApplication() { if (process.env.DRIVER === WINDOWS_DRIVER) { await closeAppWindows(WINDOWS_APP); @@ -266,6 +296,14 @@ export async function closeSecondApplication() { ]); } +export async function closeThirdApplication() { + await driver.executeScript("macos: terminateApp", [ + { + bundleId: MACOS_USER_C_BUNDLE_ID, + }, + ]); +} + export async function maximizeWindow() { if (process.env.DRIVER === WINDOWS_DRIVER) { await $('[name="square-button"]').click(); @@ -308,12 +346,14 @@ export async function closeAppMacOS(bundle: string) { export async function launchAppMacOS( bundle: string, - relativePath: string = "/.uplink", + relativePathUserData: string = "/.uplink", + appPath: string = "/Applications/Uplink.app", ) { await driver.executeScript("macos: launchApp", [ { bundleId: bundle, - arguments: ["--path", homedir() + relativePath], + path: appPath, + arguments: ["--path", homedir() + relativePathUserData], }, ]); } diff --git a/tests/helpers/constants.ts b/tests/helpers/constants.ts index 2a43adabda..14f07856ad 100644 --- a/tests/helpers/constants.ts +++ b/tests/helpers/constants.ts @@ -7,6 +7,7 @@ export const CHAT_USER_J_ID = export const MACOS_BUNDLE_ID = "im.satellite.uplink"; export const MACOS_USER_A_BUNDLE_ID = "im.satellite.uplinkChatUserA"; export const MACOS_USER_B_BUNDLE_ID = "im.satellite.uplinkChatUserB"; +export const MACOS_USER_C_BUNDLE_ID = "im.satellite.uplinkChatUserC"; export const MACOS_DRIVER = "mac2"; export const WINDOWS_APP = "C:\\Program Files\\uplink\\uplink.exe"; export const WINDOWS_DRIVER = "windows"; diff --git a/tests/helpers/debugging.ts b/tests/helpers/debugging.ts index 3712a27302..26ad92794b 100644 --- a/tests/helpers/debugging.ts +++ b/tests/helpers/debugging.ts @@ -39,19 +39,20 @@ export async function setupBeforeCreateGroupTests() { // Paste copied DID Key into Status Input await settingsProfile.pasteUserKeyInStatus(); - const didkeyA = await settingsProfile.getCopiedDidFromStatusInput(); + + // Wait for toast notification of Profile Updated to not exist + await settingsProfile.waitUntilNotificationIsClosed(); // Grab cache folder and restart + const didkeyA = await settingsProfile.getCopiedDidFromStatusInput(); await saveTestKeys(usernameA, didkeyA); + await settingsProfile.deleteStatus(); // Go to General Settings and reduce Font Size by 0.5 await settingsProfile.goToGeneralSettings(); - - // Wait for toast notification of Profile Updated to not exist - await settingsGeneral.waitUntilNotificationIsClosed(); + await settingsGeneral.waitForIsShown(true); // Click on font scaling minus button - await settingsGeneral.settingsGeneral.waitForExist(); await settingsGeneral.clickOnFontScalingMinus(); // Go to Notifications Settings and disable all notifications @@ -83,19 +84,20 @@ export async function setupBeforeCreateGroupTests() { // Paste copied DID Key into Status Input await settingsProfile.pasteUserKeyInStatus(); - const didkeyB = await settingsProfile.getCopiedDidFromStatusInput(); + + // Wait for toast notification of Profile Updated to not exist + await settingsGeneral.waitUntilNotificationIsClosed(); // Grab cache folder and restart + const didkeyB = await settingsProfile.getCopiedDidFromStatusInput(); await saveTestKeys(usernameB, didkeyB); + await settingsProfile.deleteStatus(); // Go to General Settings and reduce Font Size by 0.5 await settingsProfile.goToGeneralSettings(); - - // Wait for toast notification of Profile Updated to not exist - await settingsGeneral.waitUntilNotificationIsClosed(); + await settingsGeneral.waitForIsShown(true); // Click on font scaling minus - await settingsGeneral.waitForIsShown(true); await settingsGeneral.clickOnFontScalingMinus(); // Go to Notifications Settings and disable all notifications @@ -132,7 +134,7 @@ export async function setupBeforeCreateGroupTests() { await friendsScreen.validateAllFriendsListIsNotEmpty(); // Go to Chat with User B - await friendsScreen.chatWithFriendButton.click(); + await friendsScreen.goToChatWithFriend(); // Switch control to User B await activateSecondApplication(); diff --git a/tests/specs/reusable-accounts/01-create-accounts-and-friends.spec.ts b/tests/specs/reusable-accounts/01-create-accounts-and-friends.spec.ts index 430f9c628c..edc1792973 100644 --- a/tests/specs/reusable-accounts/01-create-accounts-and-friends.spec.ts +++ b/tests/specs/reusable-accounts/01-create-accounts-and-friends.spec.ts @@ -4,10 +4,11 @@ import { activateSecondApplication, closeFirstApplication, closeSecondApplication, + closeThirdApplication, createNewUser, getUserKey, launchSecondApplication, - maximizeWindow, + launchThirdApplication, saveTestKeys, scrollDown, } from "@helpers/commands"; @@ -395,10 +396,84 @@ export default async function createChatAccountsTests() { // Return to chat and validate Local User Status is Idle await settingsProfile.goToMainScreen(); await chatsInput.waitForIsShown(true); + await closeFirstApplication(); + await closeSecondApplication(); + }); + + it("Chat User C - Create Account", async () => { + // Launch third application + await launchThirdApplication(false); + + // Create a new account and go to Settings Profile + await createPin.waitForIsShown(true); + const username = "ChatUserC"; + await createNewUser(username); + await welcomeScreen.goToSettings(); + await settingsProfile.validateSettingsProfileIsShown(); + + // Click on Copy ID button and assert Toast Notification is displayed + await settingsProfile.openCopyIDContextMenu(); + await settingsProfile.clickOnContextMenuCopyDidKey(); + + // Wait for toast notification of Copied To Clipboard to not exist + await settingsProfile.waitUntilNotificationIsClosed(); + + // Paste copied DID Key into Status Input + await settingsProfile.pasteUserKeyInStatus(); + + // Wait for toast notification of Profile Updated to not exist + await settingsGeneral.waitUntilNotificationIsClosed(); + + // Grab cache folder and restart + const didkey = await settingsProfile.getCopiedDidFromStatusInput(); + await saveTestKeys(username, didkey); + await settingsProfile.deleteStatus(); + }); + + it("Chat User C - Settings General - Reduce font size", async () => { + // Go to General Settings and reduce Font Size by 0.5 + await settingsProfile.goToGeneralSettings(); + await settingsGeneral.waitForIsShown(true); + + // Click on font scaling minus + await settingsGeneral.clickOnFontScalingMinus(); + }); + + it("Chat User C - Settings Developer - Enable Save Logs In A File", async () => { + // Go to Settings About and click 10 times on Version Number to Unlock Developer Settings + await settingsGeneral.goToAboutSettings(); + await settingsAbout.waitForIsShown(true); + await settingsAbout.unlockDeveloperSettings(); + + // Validate Developer Settings button is unlocked + const developerSettingsButton = await settingsAbout.developerButton; + await developerSettingsButton.waitForDisplayed(); + + // Go to Menu from the left and Scroll Down + const settingsAboutButton = await settingsAbout.aboutButton; + await settingsAbout.hoverOnElement(settingsAboutButton); + await scrollDown(1000); + + // Go to Settings Developer and Enable Save Logs in a File + await settingsAbout.goToDeveloperSettings(); + await settingsDeveloper.waitForIsShown(true); + await settingsDeveloper.clickOnSaveLogs(); + await settingsDeveloper.validateSaveLogsIsEnabled(); + }); + + it("Chat User C - Settings Notifications - Disable notifications", async () => { + // Go to Notifications Settings and disable all notifications + await settingsDeveloper.goToNotificationsSettings(); + await settingsNotifications.validateSettingsNotificationsIsShown(); + await settingsNotifications.clickOnFriendsNotifications(); + await settingsNotifications.clickOnMessagesNotifications(); + + // Go to Friends Screen + await settingsNotifications.goToFriends(); + await friendsScreen.validateFriendsScreenIsShown(); }); after(async () => { - await closeFirstApplication(); - await closeSecondApplication(); + await closeThirdApplication(); }); } diff --git a/tests/specs/reusable-accounts/02-chat-replies.spec.ts b/tests/specs/reusable-accounts/02-chat-replies.spec.ts index b7817febe9..1c62b1b9dc 100644 --- a/tests/specs/reusable-accounts/02-chat-replies.spec.ts +++ b/tests/specs/reusable-accounts/02-chat-replies.spec.ts @@ -10,8 +10,10 @@ import { activateFirstApplication, closeFirstApplication, closeSecondApplication, + closeThirdApplication, launchFirstApplication, launchSecondApplication, + launchThirdApplication, } from "@helpers/commands"; const chatsContextMenu = new ContextMenu(); const chatsInput = new InputBar(); @@ -27,13 +29,6 @@ export default async function repliesTests() { await launchSecondApplication(); }); - it("Chat User B - Validate User Status changes are seen in remote side", async () => { - // Validate Chat User B is now Idle - const firstRemoteStatus = - await messageGroupRemote.getLastGroupWrapReceivedCurrentStatus(); - await expect(firstRemoteStatus).toEqual("indicator-idle"); - }); - it("Chat User B - Reply popup - Validate contents and close it", async () => { // Open Context Menu on Last Message Received and select Reply await messageRemote.openContextMenuOnReceivedMessage("Testing...😀"); @@ -84,6 +79,13 @@ export default async function repliesTests() { await userImage.waitForExist(); }); + it("Chat User B - Validate User Status changes are seen in remote side", async () => { + // Validate Chat User B is now Idle + const firstRemoteStatus = + await messageGroupRemote.getLastGroupWrapReceivedCurrentStatus(); + await expect(firstRemoteStatus).toEqual("indicator-idle"); + }); + it("Chat User A - Validate reply message contents", async () => { // Switch control to User A await activateFirstApplication(); diff --git a/tests/specs/reusable-accounts/06-chat-topbar.spec.ts b/tests/specs/reusable-accounts/06-chat-topbar.spec.ts index dd0a57a05c..18783c75ca 100644 --- a/tests/specs/reusable-accounts/06-chat-topbar.spec.ts +++ b/tests/specs/reusable-accounts/06-chat-topbar.spec.ts @@ -8,7 +8,9 @@ import Topbar from "@screenobjects/chats/Topbar"; import { activateFirstApplication, closeFirstApplication, + closeSecondApplication, launchFirstApplication, + launchSecondApplication, } from "@helpers/commands"; const chatsContextMenu = new ContextMenu(); const chatsInput = new InputBar(); @@ -19,6 +21,7 @@ const pinnedMessages = new PinnedMessages(); export default async function chatTopbarTests() { before(async () => { + await launchSecondApplication(); await launchFirstApplication(); }); @@ -124,5 +127,6 @@ export default async function chatTopbarTests() { after(async () => { await closeFirstApplication(); + await closeSecondApplication(); }); } diff --git a/tests/specs/reusable-accounts/12-group-chats-multiple-users.spec.ts b/tests/specs/reusable-accounts/12-group-chats-multiple-users.spec.ts new file mode 100644 index 0000000000..5c99262844 --- /dev/null +++ b/tests/specs/reusable-accounts/12-group-chats-multiple-users.spec.ts @@ -0,0 +1,78 @@ +require("module-alias/register"); +import ChatsSidebar from "@screenobjects/chats/ChatsSidebar"; +import FriendsScreen from "@screenobjects/friends/FriendsScreen"; +import WelcomeScreen from "@screenobjects/welcome-screen/WelcomeScreen"; +import { + activateFirstApplication, + activateThirdApplication, + closeFirstApplication, + closeThirdApplication, + getUserKey, + launchFirstApplication, + launchThirdApplication, +} from "@helpers/commands"; +const chatsSidebar = new ChatsSidebar(); +const friendsScreen = new FriendsScreen(); +const welcomeScreen = new WelcomeScreen(); + +export default async function groupChatMultipleUsersTests() { + before(async () => { + await launchFirstApplication(); + await launchThirdApplication(); + }); + + it("Chat User C - Send friend request to User A", async () => { + await welcomeScreen.goToFriends(); + await friendsScreen.waitForIsShown(true); + + // Obtain did key from Chat User A + const friendDidKey = await getUserKey("ChatUserA"); + await friendsScreen.sendFriendRequest(friendDidKey, "ChatUserA"); + + // Go to All Friends List + await friendsScreen.goToAllFriendsList(); + await friendsScreen.validateAllFriendsListIsShown(); + }); + + it("Chat User A - Accept friend request from User C and go to chat button", async () => { + // Switch control to User A + await activateFirstApplication(); + + // Go to Friends List + await chatsSidebar.goToFriends(); + await friendsScreen.validateFriendsScreenIsShown(); + + // With User A - Go to pending requests list, wait for receiving the friend request and accept it + await friendsScreen.hoverOnPendingListButton(); + await friendsScreen.goToPendingFriendsList(); + await friendsScreen.validateIncomingListIsShown(); + await friendsScreen.waitUntilFriendRequestIsReceived(); + await friendsScreen.acceptIncomingRequest("ChatUserC"); + + // Validate friend is now on all friends list + await friendsScreen.goToAllFriendsList(); + await friendsScreen.validateAllFriendsListIsShown(); + await friendsScreen.validateAllFriendsListIsNotEmpty(); + + // Go to Chat with User C + await friendsScreen.goToChatWithFriend(); + }); + + it("Chat User C - Validate friend request was accepted", async () => { + // Switch control to User C + await activateThirdApplication(); + + // With User C - Go to pending requests list, wait for receiving the friend request and accept it + await friendsScreen.waitUntilUserAcceptedFriendRequest(); + + // Validate friend is now on all friends list + await friendsScreen.goToAllFriendsList(); + await friendsScreen.validateAllFriendsListIsShown(); + await friendsScreen.validateAllFriendsListIsNotEmpty(); + }); + + after(async () => { + await closeFirstApplication(); + await closeThirdApplication(); + }); +} diff --git a/tests/suites/Chats/01-Chats.suite.ts b/tests/suites/Chats/01-Chats.suite.ts index 489df132dd..405d665b9c 100644 --- a/tests/suites/Chats/01-Chats.suite.ts +++ b/tests/suites/Chats/01-Chats.suite.ts @@ -10,6 +10,7 @@ import messageInputTests from "@specs/reusable-accounts/04-message-input.spec"; import repliesTests from "@specs/reusable-accounts/02-chat-replies.spec"; import quickProfileTests from "@specs/reusable-accounts/07-quick-profile.spec"; import sidebarChatsTests from "@specs/reusable-accounts/08-sidebar-chats.spec"; +import groupChatMultipleUsersTests from "@specs/reusable-accounts/12-group-chats-multiple-users.spec"; describe("Create Accounts and Chat Tests", createChatAccountsTests.bind(this)); describe("Chat Replies Tests", repliesTests.bind(this)); @@ -25,3 +26,7 @@ describe( "Group Chats Favorites and Sidebar Tests", groupChatSidebarTests.bind(this), ); +describe( + "Group Chats Multiple Users Tests", + groupChatMultipleUsersTests.bind(this), +);