Skip to content

Android 1.28.0, iOS 2.14.1 #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 31 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7a6cf5c
fix: weed out all contact and conversation list item locators
Miki-Session Aug 12, 2025
bd073f9
feat: begin using uiscrollable instead of blind scrolling
Miki-Session Aug 12, 2025
44968cf
fix: only use locator class for accountid
Miki-Session Aug 12, 2025
29f7428
fix: Android locator changes
Miki-Session Aug 13, 2025
c09ee8a
fix: more Android locator changes
Miki-Session Aug 13, 2025
ba4db65
fix: create account util clicks close settings
Miki-Session Aug 13, 2025
acb1bca
fix: editusernamebutton is still username on ios
Miki-Session Aug 14, 2025
a054565
Merge remote-tracking branch 'origin/main' into dev
Miki-Session Aug 14, 2025
f9bdc10
fix: ios doesn't have clear input button
Miki-Session Aug 15, 2025
6499905
fix: don't console.error for not found
Miki-Session Aug 15, 2025
311a9be
feat: set app disguise test on iOS
Miki-Session Aug 15, 2025
eb57ead
fix: remove blind sleeps and scrolls
Miki-Session Aug 15, 2025
cea2cc7
fix: standardise control message logging
Miki-Session Aug 17, 2025
d133b3a
feat: truncate long selector descriptions
Miki-Session Aug 18, 2025
c73f623
feat: add steps to app disguise tests
Miki-Session Aug 18, 2025
fc79d74
fix: use ax id for conversationitem
Miki-Session Aug 21, 2025
3639587
feat: add emoji react send tests
Miki-Session Aug 21, 2025
b0e1f9a
fix: use whatever text is available for logging
Miki-Session Aug 21, 2025
c6f2786
chore: rename allure suite
Miki-Session Aug 21, 2025
cf265af
fix: community input is now an id
Miki-Session Aug 21, 2025
c76563b
fix: path menu item is now an uiscrollable
Miki-Session Aug 21, 2025
a3d8eb0
feat: add community emoji react test
Miki-Session Aug 21, 2025
4a0a2b3
feat: allow links for known bugs in allure reports
Miki-Session Aug 21, 2025
6899783
feat: add in allure links for current known issues
Miki-Session Aug 21, 2025
cc68a54
fix: move allure test info to helper file
Miki-Session Aug 21, 2025
dfab494
chore: fix step description
Miki-Session Aug 21, 2025
b534e46
chore: remove SES link
Miki-Session Aug 21, 2025
f3aa4c3
feat: community poll limit on ios
Miki-Session Aug 22, 2025
1224668
refactor!: scrub all occurrences of message body selector
Miki-Session Aug 22, 2025
bcb1c3d
fix: add visible check to verifyelementnotpresent
Miki-Session Aug 22, 2025
cf201a5
fix more failing tests
Miki-Session Aug 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions run/test/specs/app_disguise_icons.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { TestInfo } from '@playwright/test';
import { test, type TestInfo } from '@playwright/test';

import { TestSteps } from '../../types/allure';
import { bothPlatformsIt } from '../../types/sessionIt';
import { USERNAME } from '../../types/testing';
import { AppearanceMenuItem, SelectAppIcon, UserSettings } from './locators/settings';
import { sleepFor } from './utils';
import { newUser } from './utils/create_account';
import { closeApp, openAppOnPlatformSingleDevice, SupportedPlatformsType } from './utils/open_app';
import { AppDisguisePageScreenshot } from './utils/screenshot_paths';
Expand All @@ -22,16 +22,20 @@ bothPlatformsIt({
});

async function appDisguiseIcons(platform: SupportedPlatformsType, testInfo: TestInfo) {
const { device } = await openAppOnPlatformSingleDevice(platform, testInfo);
await newUser(device, USERNAME.ALICE, { saveUserData: false });
await device.clickOnElementAll(new UserSettings(device));
// Must scroll down to reveal the Appearance menu item
await device.scrollDown();
await device.clickOnElementAll(new AppearanceMenuItem(device));
await sleepFor(2000);
// Must scroll down to reveal the app disguise option
await device.scrollDown();
await device.clickOnElementAll(new SelectAppIcon(device));
await verifyElementScreenshot(device, new AppDisguisePageScreenshot(device), testInfo);
await closeApp(device);
const { device } = await test.step(TestSteps.SETUP.NEW_USER, async () => {
const { device } = await openAppOnPlatformSingleDevice(platform, testInfo);
await newUser(device, USERNAME.ALICE, { saveUserData: false });
return { device };
});
await test.step(TestSteps.OPEN.APPEARANCE, async () => {
await device.clickOnElementAll(new UserSettings(device));
await device.clickOnElementAll(new AppearanceMenuItem(device));
});
await test.step(TestSteps.VERIFY.ELEMENT_SCREENSHOT('app disguise icons'), async () => {
await device.clickOnElementAll(new SelectAppIcon(device));
await verifyElementScreenshot(device, new AppDisguisePageScreenshot(device), testInfo);
});
await test.step(TestSteps.SETUP.CLOSE_APP, async () => {
await closeApp(device);
});
}
121 changes: 86 additions & 35 deletions run/test/specs/app_disguise_set.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { TestInfo } from '@playwright/test';
import { test, type TestInfo } from '@playwright/test';

import { englishStrippedStr } from '../../localizer/englishStrippedStr';
import { androidIt } from '../../types/sessionIt';
import { TestSteps } from '../../types/allure';
import { bothPlatformsItSeparate } from '../../types/sessionIt';
import { USERNAME } from '../../types/testing';
import { DisguisedApp } from './locators/external';
import {
Expand All @@ -18,47 +19,97 @@ import { openAppOnPlatformSingleDevice, SupportedPlatformsType } from './utils/o
import { closeApp } from './utils/open_app';
import { runScriptAndLog } from './utils/utilities';

// iOS implementation blocked by SES-3809
androidIt({
bothPlatformsItSeparate({
title: 'App disguise set icon',
risk: 'medium',
countOfDevicesNeeded: 1,
testCb: appDisguiseSetIcon,
android: {
testCb: appDisguiseSetIconAndroid,
},
ios: {
testCb: appDisguiseSetIconIOS,
},
allureSuites: {
parent: 'Settings',
suite: 'App Disguise',
},
allureDescription: 'Verifies the alternate icon set on the App Disguise page is applied',
});

async function appDisguiseSetIcon(platform: SupportedPlatformsType, testInfo: TestInfo) {
const { device } = await openAppOnPlatformSingleDevice(platform, testInfo);
await newUser(device, USERNAME.ALICE, { saveUserData: false });
await device.clickOnElementAll(new UserSettings(device));
// Must scroll down to reveal the Appearance menu item
await device.scrollDown();
await device.clickOnElementAll(new AppearanceMenuItem(device));
await sleepFor(2000);
// Must scroll down to reveal the app disguise option
await device.scrollDown();
await device.clickOnElementAll(new SelectAppIcon(device));
try {
await device.clickOnElementAll(new AppDisguiseMeetingIcon(device));
await device.checkModalStrings(
englishStrippedStr('appIconAndNameChange').toString(),
englishStrippedStr('appIconAndNameChangeConfirmation').toString()
);
await device.clickOnElementAll(new CloseAppButton(device));
await sleepFor(2000);
// Open app library and check for disguised app
await device.swipeFromBottom();
await device.waitForTextElementToBePresent(new DisguisedApp(device));
} finally {
// The disguised app must be uninstalled otherwise every following test will fail
await closeApp(device);
await runScriptAndLog(
`${getAdbFullPath()} -s ${device.udid} uninstall network.loki.messenger.qa`,
true
);
}
async function appDisguiseSetIconIOS(platform: SupportedPlatformsType, testInfo: TestInfo) {
const { device } = await test.step(TestSteps.SETUP.NEW_USER, async () => {
const { device } = await openAppOnPlatformSingleDevice(platform, testInfo);
await newUser(device, USERNAME.ALICE, { saveUserData: false });
return { device };
});
await test.step(TestSteps.OPEN.APPEARANCE, async () => {
await device.clickOnElementAll(new UserSettings(device));
await device.clickOnElementAll(new AppearanceMenuItem(device));
});
await test.step(TestSteps.USER_ACTIONS.APP_DISGUISE, async () => {
await device.clickOnElementAll(new SelectAppIcon(device));
try {
await device.clickOnElementAll(new AppDisguiseMeetingIcon(device));
await test.step(TestSteps.VERIFY.SPECIFIC_MODAL('app disguise'), async () => {
await device.waitForTextElementToBePresent({
strategy: 'accessibility id',
selector: 'You have changed the icon for “Session”.',
});
await device.clickOnElementAll({
strategy: 'accessibility id',
selector: 'OK',
});
});
// TODO maybe grab a screenshot of the disguised app and see what you can do with it
} finally {
// The disguised app must be uninstalled otherwise every following test will fail
await test.step(TestSteps.SETUP.CLOSE_APP, async () => {
await closeApp(device);
await runScriptAndLog(
`xcrun simctl uninstall ${device.udid} com.loki-project.loki-messenger`,
true
);
});
}
});
}

async function appDisguiseSetIconAndroid(platform: SupportedPlatformsType, testInfo: TestInfo) {
const { device } = await test.step(TestSteps.SETUP.NEW_USER, async () => {
const { device } = await openAppOnPlatformSingleDevice(platform, testInfo);
await newUser(device, USERNAME.ALICE, { saveUserData: false });
return { device };
});
await test.step(TestSteps.OPEN.APPEARANCE, async () => {
await device.clickOnElementAll(new UserSettings(device));
await device.clickOnElementAll(new AppearanceMenuItem(device));
});
await test.step(TestSteps.USER_ACTIONS.APP_DISGUISE, async () => {
await device.clickOnElementAll(new SelectAppIcon(device));
try {
await device.clickOnElementAll(new AppDisguiseMeetingIcon(device));
await test.step(TestSteps.VERIFY.SPECIFIC_MODAL('app disgusie'), async () => {
await device.checkModalStrings(
englishStrippedStr('appIconAndNameChange').toString(),
englishStrippedStr('appIconAndNameChangeConfirmation').toString()
);
});
await test.step('Verify app icon changed', async () => {
await device.clickOnElementAll(new CloseAppButton(device));
await sleepFor(2000);
// Open app library and check for disguised app
await device.swipeFromBottom();
await device.waitForTextElementToBePresent(new DisguisedApp(device));
});
} finally {
// The disguised app must be uninstalled otherwise every following test will fail
await test.step(TestSteps.SETUP.CLOSE_APP, async () => {
await closeApp(device);
await runScriptAndLog(
`${getAdbFullPath()} -s ${device.udid} uninstall network.loki.messenger.qa`,
true
);
});
}
});
}
4 changes: 2 additions & 2 deletions run/test/specs/check_avatar_color.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TestSteps } from '../../types/allure';
import { bothPlatformsIt } from '../../types/sessionIt';
import { ConversationSettings } from './locators/conversation';
import { ConversationItem } from './locators/home';
import { UserSettings } from './locators/settings';
import { UserAvatar, UserSettings } from './locators/settings';
import { open_Alice1_Bob1_friends } from './state_builder';
import { isSameColor } from './utils/check_colour';
import { closeApp, SupportedPlatformsType } from './utils/open_app';
Expand Down Expand Up @@ -35,7 +35,7 @@ async function avatarColor(platform: SupportedPlatformsType, testInfo: TestInfo)
});
await test.step(`Get Alice's avatar color on their device from the Settings screen avatar`, async () => {
await alice1.clickOnElementAll(new UserSettings(alice1));
alice1PixelColor = await alice1.getElementPixelColor(new UserSettings(alice1));
alice1PixelColor = await alice1.getElementPixelColor(new UserAvatar(alice1));
});
await test.step(`Get Alice's avatar color on bob's device from the Conversation Settings avatar`, async () => {
await bob1.clickOnElementAll(new ConversationItem(bob1, alice.userName));
Expand Down
61 changes: 61 additions & 0 deletions run/test/specs/community_emoji_react.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { test, type TestInfo } from '@playwright/test';

import { testCommunityLink, testCommunityName } from '../../constants/community';
import { TestSteps } from '../../types/allure';
import { bothPlatformsIt } from '../../types/sessionIt';
import { EmojiReactsPill, FirstEmojiReact } from './locators/conversation';
import { open_Alice1_Bob1_friends } from './state_builder';
import { joinCommunity } from './utils/join_community';
import { closeApp, SupportedPlatformsType } from './utils/open_app';

bothPlatformsIt({
title: 'Send emoji react community',
risk: 'medium',
countOfDevicesNeeded: 2,
testCb: sendEmojiReactionCommunity,
allureSuites: {
parent: 'Sending Messages',
suite: 'Emoji reacts',
},
allureDescription: 'Verifies that an emoji reaction can be sent and is received in a community',
});

async function sendEmojiReactionCommunity(platform: SupportedPlatformsType, testInfo: TestInfo) {
const message = `Testing emoji reacts - ${new Date().getTime()} - ${platform}`;
const {
devices: { alice1, bob1 },
prebuilt: { alice },
} = await test.step(TestSteps.SETUP.QA_SEEDER, async () => {
return open_Alice1_Bob1_friends({
platform,
focusFriendsConvo: false,
testInfo,
});
});
await Promise.all(
[alice1, bob1].map(device => joinCommunity(device, testCommunityLink, testCommunityName))
);
await test.step(TestSteps.SEND.MESSAGE(alice.userName, testCommunityName), async () => {
await alice1.sendMessage(message);
});
await test.step(TestSteps.SEND.EMOJI_REACT, async () => {
await bob1.scrollToBottom();
await bob1.longPressMessage(message);
await bob1.clickOnElementAll(new FirstEmojiReact(bob1));
// Verify long press menu disappeared (so next found emoji is in convo and not in react bar)
await bob1.verifyElementNotPresent({
strategy: 'accessibility id',
selector: 'Reply to message',
});
});
await test.step(TestSteps.VERIFY.EMOJI_REACT, async () => {
await Promise.all(
[alice1, bob1].map(device =>
device.waitForTextElementToBePresent(new EmojiReactsPill(device))
)
);
});
await test.step(TestSteps.SETUP.CLOSE_APP, async () => {
await closeApp(alice1, bob1);
});
}
66 changes: 12 additions & 54 deletions run/test/specs/community_tests_image.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,23 @@ import { test, type TestInfo } from '@playwright/test';

import { testCommunityLink, testCommunityName } from '../../constants/community';
import { TestSteps } from '../../types/allure';
import { androidIt, iosIt } from '../../types/sessionIt';
import { USERNAME } from '../../types/testing';
import { bothPlatformsIt } from '../../types/sessionIt';
import { MessageBody } from './locators/conversation';
import { open_Alice1_Bob1_friends } from './state_builder';
import { sleepFor } from './utils';
import { newUser } from './utils/create_account';
import { joinCommunity } from './utils/join_community';
import { closeApp, openAppOnPlatformSingleDevice, SupportedPlatformsType } from './utils/open_app';
import { closeApp, SupportedPlatformsType } from './utils/open_app';

// NOTE For some reason Appium takes FOREVER to load the iOS page source of the community on the recipients device
// and as such I haven't found a quick and easy way to verify that they see the new image message
// If this becomes a problem in the future then we can extract the unread count from page source and see it increment after the image gets sent
// But for now we have to trust that the sender seeing 'Sent' also delivers it to others on iOS
// This is also why it's a 1-device test and has its own iosIt definition (and not bothPlatformsItSeparate)

androidIt({
bothPlatformsIt({
title: 'Send image to community',
risk: 'medium',
countOfDevicesNeeded: 2,
testCb: sendImageCommunityAndroid,
allureSuites: { parent: 'Sending Messages', suite: 'Sending Attachments' },
testCb: sendImageCommunity,
allureSuites: { parent: 'Sending Messages', suite: 'Attachments' },
allureDescription: 'Verifies that an image can be sent and received in a community',
});

iosIt({
title: 'Send image to community',
risk: 'medium',
countOfDevicesNeeded: 1,
testCb: sendImageCommunityIOS,
allureSuites: { parent: 'Sending Messages', suite: 'Sending Attachments' },
allureDescription: `Verifies that an image can be sent to a community.
Note that due to Appium's limitations, this test does not verify another device receiving the image.`,
});

async function sendImageCommunityAndroid(platform: SupportedPlatformsType, testInfo: TestInfo) {
async function sendImageCommunity(platform: SupportedPlatformsType, testInfo: TestInfo) {
const {
devices: { alice1, bob1 },
} = await test.step(TestSteps.SETUP.QA_SEEDER, async () => {
Expand All @@ -48,45 +31,20 @@ async function sendImageCommunityAndroid(platform: SupportedPlatformsType, testI
const testImageMessage = `Image message + ${new Date().getTime()} - ${platform}`;
await test.step(TestSteps.NEW_CONVERSATION.JOIN_COMMUNITY, async () => {
await Promise.all(
[alice1, bob1].map(async device => {
await joinCommunity(device, testCommunityLink, testCommunityName);
})
[alice1, bob1].map(device => joinCommunity(device, testCommunityLink, testCommunityName))
);
});
await test.step(TestSteps.SEND.IMAGE, async () => {
await alice1.sendImage(testImageMessage, true);
});
await test.step(TestSteps.VERIFY.MESSAGE_RECEIVED, async () => {
await sleepFor(5000); // Give bob some time to receive the message so the test doesn't scroll down too early
await sleepFor(2000); // Give bob some time to receive the image
await bob1.scrollToBottom();
await bob1.trustAttachments(testCommunityName);
await bob1.scrollToBottom(); // Gotta keep scrolling down to make sure we're at the very bottom
await bob1.waitForTextElementToBePresent({
strategy: 'accessibility id',
selector: 'Message body',
text: testImageMessage,
});
await bob1.onAndroid().trustAttachments(testCommunityName);
await bob1.onAndroid().scrollToBottom(); // Trusting attachments scrolls the viewport up a bit so gotta scroll to bottom again
await bob1.waitForTextElementToBePresent(new MessageBody(bob1, testImageMessage));
});

await test.step(TestSteps.SETUP.CLOSE_APP, async () => {
await closeApp(alice1, bob1);
});
}
async function sendImageCommunityIOS(platform: SupportedPlatformsType, testInfo: TestInfo) {
const { device } = await test.step(TestSteps.SETUP.NEW_USER, async () => {
const { device } = await openAppOnPlatformSingleDevice(platform, testInfo);
await newUser(device, USERNAME.ALICE, { saveUserData: false });
return { device };
});
const testImageMessage = `Image message + ${new Date().getTime()} - ${platform}`;
await test.step(TestSteps.NEW_CONVERSATION.JOIN_COMMUNITY, async () => {
await joinCommunity(device, testCommunityLink, testCommunityName);
});
await test.step(TestSteps.SEND.IMAGE, async () => {
await device.sendImage(testImageMessage, true);
});

await test.step(TestSteps.SETUP.CLOSE_APP, async () => {
await closeApp(device);
});
}
Loading
Loading