diff --git a/run/localizer/constants.ts b/run/localizer/constants.ts
index 8ade63e5..7ff7fec1 100644
--- a/run/localizer/constants.ts
+++ b/run/localizer/constants.ts
@@ -3,6 +3,13 @@ export enum LOCALE_DEFAULTS {
session_download_url = 'https://getsession.org/download',
gif = 'GIF',
oxen_foundation = 'Oxen Foundation',
+ network_name = 'Session Network',
+ token_name_long = 'Session Token',
+ staking_reward_pool = 'Staking Reward Pool',
+ token_name_short = 'SESH',
+ usd_name_short = 'USD',
+ session_network_data_price = 'Price data powered by CoinGecko
Accurate at {date_time}',
+ app_pro = 'Session Pro',
}
export const rtlLocales = ['ar', 'fa', 'he', 'ps', 'ur'];
diff --git a/run/localizer/locales.ts b/run/localizer/locales.ts
index 871ebc60..2be47598 100644
--- a/run/localizer/locales.ts
+++ b/run/localizer/locales.ts
@@ -14,6 +14,10 @@ export const simpleDictionary = {
en: 'Copy Account ID',
args: undefined,
},
+ accountId: {
+ en: 'Account ID',
+ args: undefined,
+ },
accountIdCopied: {
en: 'Account ID Copied',
args: undefined,
@@ -58,6 +62,14 @@ export const simpleDictionary = {
en: 'Add',
args: undefined,
},
+ addAdmins: {
+ en: 'Add Admins',
+ args: undefined,
+ },
+ addAdminsDescription: {
+ en: 'Enter the Account ID of the user you are promoting to admin.
To add multiple users, enter each Account ID separated by a comma. Up to 20 Account IDs can be specified at a time.',
+ args: undefined,
+ },
adminCannotBeRemoved: {
en: 'Admins cannot be removed.',
args: undefined,
@@ -174,10 +186,22 @@ export const simpleDictionary = {
en: 'App Icon',
args: undefined,
},
+ appIconAndNameChange: {
+ en: 'Change App Icon and Name',
+ args: undefined,
+ },
+ appIconAndNameChangeConfirmation: {
+ en: 'Changing the app icon and name requires Session to be closed. Notifications will continue to use the default Session icon and name.',
+ args: undefined,
+ },
appIconAndNameDescription: {
en: 'Alternate app icon and name is displayed on home screen and app drawer.',
args: undefined,
},
+ appIconAndNameSelectionDescription: {
+ en: 'The selected app icon and name is displayed on the home screen and app drawer.',
+ args: undefined,
+ },
appIconAndNameSelectionTitle: {
en: 'Icon and name',
args: undefined,
@@ -542,6 +566,10 @@ export const simpleDictionary = {
en: 'Unban User',
args: undefined,
},
+ banUnbanUserDescription: {
+ en: 'Enter the Account ID of the user you are unbanning',
+ args: undefined,
+ },
banUnbanUserUnbanned: {
en: 'User unbanned',
args: undefined,
@@ -554,12 +582,16 @@ export const simpleDictionary = {
en: 'User banned',
args: undefined,
},
+ banUserDescription: {
+ en: 'Enter the Account ID of the user you are banning',
+ args: undefined,
+ },
block: {
en: 'Block',
args: undefined,
},
blockBlockedDescription: {
- en: 'Unblock this contact to send a message.',
+ en: 'Unblock this contact to send a message',
args: undefined,
},
blockBlockedNone: {
@@ -782,6 +814,14 @@ export const simpleDictionary = {
en: 'Clear device only',
args: undefined,
},
+ clearDeviceRestart: {
+ en: 'Clear Device and Restart',
+ args: undefined,
+ },
+ clearDeviceRestore: {
+ en: 'Clear Device and Restore',
+ args: undefined,
+ },
clearMessages: {
en: 'Clear All Messages',
args: undefined,
@@ -790,10 +830,18 @@ export const simpleDictionary = {
en: 'Are you sure you want to clear all messages from your conversation with {name} from your device?',
args: { name: 'string' },
},
+ clearMessagesChatDescriptionUpdated: {
+ en: 'Are you sure you want to clear all messages from your conversation with {name} on this device?',
+ args: { name: 'string' },
+ },
clearMessagesCommunity: {
en: 'Are you sure you want to clear all {community_name} messages from your device?',
args: { community_name: 'string' },
},
+ clearMessagesCommunityUpdated: {
+ en: 'Are you sure you want to clear all messages from {community_name} on this device?',
+ args: { community_name: 'string' },
+ },
clearMessagesForEveryone: {
en: 'Clear for everyone',
args: undefined,
@@ -806,18 +854,38 @@ export const simpleDictionary = {
en: 'Are you sure you want to clear all {group_name} messages?',
args: { group_name: 'string' },
},
+ clearMessagesGroupAdminDescriptionUpdated: {
+ en: 'Are you sure you want to clear all messages from {group_name}?',
+ args: { group_name: 'string' },
+ },
clearMessagesGroupDescription: {
en: 'Are you sure you want to clear all {group_name} messages from your device?',
args: { group_name: 'string' },
},
+ clearMessagesGroupDescriptionUpdated: {
+ en: 'Are you sure you want to clear all messages from {group_name} on this device?',
+ args: { group_name: 'string' },
+ },
clearMessagesNoteToSelfDescription: {
en: 'Are you sure you want to clear all Note to Self messages from your device?',
args: undefined,
},
+ clearMessagesNoteToSelfDescriptionUpdated: {
+ en: 'Are you sure you want to clear all Note to Self messages on this device?',
+ args: undefined,
+ },
+ clearOnThisDevice: {
+ en: 'Clear on this device',
+ args: undefined,
+ },
close: {
en: 'Close',
args: undefined,
},
+ closeApp: {
+ en: 'Close App',
+ args: undefined,
+ },
closeWindow: {
en: 'Close Window',
args: undefined,
@@ -1082,8 +1150,16 @@ export const simpleDictionary = {
en: 'Cut',
args: undefined,
},
+ databaseErrorClearDataWarning: {
+ en: 'Are you sure you want to delete all messages, attachments, and account data from this device and create a new account?',
+ args: undefined,
+ },
databaseErrorGeneric: {
- en: 'A database error occurred.
Export your application logs to share for troubleshooting. If this is unsuccessful, reinstall Session and restore your account.
Warning: This will result in loss of all messages, attachments, and account data older than two weeks.',
+ en: 'A database error occurred.
Export your application logs to share for troubleshooting. If this is unsuccessful, reinstall Session and restore your account.',
+ args: undefined,
+ },
+ databaseErrorRestoreDataWarning: {
+ en: 'Are you sure you want to delete all messages, attachments, and account data from this device and restore your account from the network?',
args: undefined,
},
databaseErrorTimeout: {
@@ -1170,6 +1246,14 @@ export const simpleDictionary = {
en: 'You don’t have permission to delete others’ messages',
args: undefined,
},
+ deleteContactDescription: {
+ en: 'Are you sure you want to delete {name} from your contacts?
This will delete your conversation, including all messages and attachments. Future messages from {name} will appear as a message request.',
+ args: { name: 'string' },
+ },
+ deleteConversationDescription: {
+ en: 'Are you sure you want to delete your conversation with {name}?
This will permanently delete all messages and attachments.',
+ args: { name: 'string' },
+ },
deleteMessageDeletedGlobally: {
en: 'This message was deleted',
args: undefined,
@@ -1386,6 +1470,10 @@ export const simpleDictionary = {
en: 'Document',
args: undefined,
},
+ donate: {
+ en: 'Donate',
+ args: undefined,
+ },
done: {
en: 'Done',
args: undefined,
@@ -1498,6 +1586,10 @@ export const simpleDictionary = {
en: 'Database Error',
args: undefined,
},
+ errorGeneric: {
+ en: 'Something went wrong. Please try again later.',
+ args: undefined,
+ },
errorUnknown: {
en: 'An unknown error occurred.',
args: undefined,
@@ -1522,6 +1614,10 @@ export const simpleDictionary = {
en: 'Follow system settings',
args: undefined,
},
+ forever: {
+ en: 'Forever',
+ args: undefined,
+ },
from: {
en: 'From:',
args: undefined,
@@ -1559,7 +1655,7 @@ export const simpleDictionary = {
args: undefined,
},
groupDeleteDescription: {
- en: 'Are you sure you want to delete {group_name}? This will remove all members and delete all group content.',
+ en: 'Are you sure you want to delete {group_name}?
This will remove all members and delete all group content.',
args: { group_name: 'string' },
},
groupDeleteDescriptionMember: {
@@ -1767,7 +1863,7 @@ export const simpleDictionary = {
args: { group_name: 'string' },
},
groupNotUpdatedWarning: {
- en: 'Group has not been updated in over 30 days. You may experience issues sending messages or viewing Group information.',
+ en: 'This group has not been updated in over 30 days. You may experience issues sending messages or viewing group information.',
args: undefined,
},
groupOnlyAdmin: {
@@ -1894,6 +1990,10 @@ export const simpleDictionary = {
en: 'Toggle system menu bar visibility',
args: undefined,
},
+ hideNoteToSelfDescription: {
+ en: 'Are you sure you want to hide Note to Self from your conversation list?',
+ args: undefined,
+ },
hideOthers: {
en: 'Hide Others',
args: undefined,
@@ -2166,6 +2266,14 @@ export const simpleDictionary = {
en: 'Replying to',
args: undefined,
},
+ messageRequestDisabledToastAttachments: {
+ en: 'You cannot send attachments until your Message Request is accepted',
+ args: undefined,
+ },
+ messageRequestDisabledToastVoiceMessages: {
+ en: 'You cannot send voice messages until your Message Request is accepted',
+ args: undefined,
+ },
messageRequestGroupInvite: {
en: '{name} invited you to join {group_name}.',
args: { name: 'string', group_name: 'string' },
@@ -2206,6 +2314,10 @@ export const simpleDictionary = {
en: 'Allow message requests from Community conversations.',
args: undefined,
},
+ messageRequestsContactDelete: {
+ en: 'Are you sure you want to delete this message request and the associated contact?',
+ args: undefined,
+ },
messageRequestsDelete: {
en: 'Are you sure you want to delete this message request?',
args: undefined,
@@ -2274,6 +2386,14 @@ export const simpleDictionary = {
en: 'Minimize',
args: undefined,
},
+ modalMessageTooLongDescription: {
+ en: 'Please shorten your message to {count} characters or less.',
+ args: { count: 'number' },
+ },
+ modalMessageTooLongTitle: {
+ en: 'Your message is too long',
+ args: undefined,
+ },
next: {
en: 'Next',
args: undefined,
@@ -2426,6 +2546,14 @@ export const simpleDictionary = {
en: 'Muted',
args: undefined,
},
+ notificationsMutedFor: {
+ en: 'Muted for {time_large}',
+ args: { time_large: 'string' },
+ },
+ notificationsMutedForTime: {
+ en: 'Muted until {date_time}',
+ args: { date_time: 'string' },
+ },
notificationsSlowMode: {
en: 'Slow Mode',
args: undefined,
@@ -2790,6 +2918,10 @@ export const simpleDictionary = {
en: 'Session needs storage access to send photos and videos.',
args: undefined,
},
+ permissionsWriteCommunity: {
+ en: "You don't have write permissions in this community",
+ args: undefined,
+ },
pin: {
en: 'Pin',
args: undefined,
@@ -2982,6 +3114,14 @@ export const simpleDictionary = {
en: 'Redo',
args: undefined,
},
+ remainingCharactersOverTooltip: {
+ en: 'Message is too long',
+ args: undefined,
+ },
+ remainingCharactersTooltip: {
+ en: '{count} characters remaining',
+ args: { count: 'number' },
+ },
remove: {
en: 'Remove',
args: undefined,
@@ -3090,6 +3230,10 @@ export const simpleDictionary = {
en: 'Select All',
args: undefined,
},
+ selectAppIcon: {
+ en: 'Select app icon',
+ args: undefined,
+ },
send: {
en: 'Send',
args: undefined,
@@ -3134,6 +3278,46 @@ export const simpleDictionary = {
en: 'Message Requests',
args: undefined,
},
+ sessionNetworkCurrentPrice: {
+ en: 'Current SESH price',
+ args: undefined,
+ },
+ sessionNetworkDescription: {
+ en: 'Messages are sent using the Session Network. The network is comprised of nodes incentivized with Session Token, which keeps Session decentralized and secure. Learn More {icon}',
+ args: { icon: 'string' },
+ },
+ sessionNetworkLearnAboutStaking: {
+ en: 'Learn About Staking',
+ args: undefined,
+ },
+ sessionNetworkMarketCap: {
+ en: 'Market Cap',
+ args: undefined,
+ },
+ sessionNetworkNodesSecuring: {
+ en: 'Session Nodes securing your messages',
+ args: undefined,
+ },
+ sessionNetworkNodesSwarm: {
+ en: 'Session Nodes in your swarm',
+ args: undefined,
+ },
+ sessionNetworkNotificationLive: {
+ en: 'Session Token is live! Explore the new Session Network section in Settings to learn how Session Token powers Session.',
+ args: undefined,
+ },
+ sessionNetworkSecuredBy: {
+ en: 'Network secured by',
+ args: undefined,
+ },
+ sessionNetworkTokenDescription: {
+ en: 'When you stake Session Token to secure the network, you earn rewards in SESH from the Staking Reward Pool.',
+ args: undefined,
+ },
+ sessionNew: {
+ en: '• New',
+ args: undefined,
+ },
sessionNotifications: {
en: 'Notifications',
args: undefined,
@@ -3158,6 +3342,10 @@ export const simpleDictionary = {
en: 'Set',
args: undefined,
},
+ setCommunityDisplayPicture: {
+ en: 'Set Community Display Picture',
+ args: undefined,
+ },
settingsRestartDescription: {
en: 'You must restart Session to apply your new settings.',
args: undefined,
@@ -3198,6 +3386,14 @@ export const simpleDictionary = {
en: 'Show Less',
args: undefined,
},
+ showNoteToSelf: {
+ en: 'Show Note to Self',
+ args: undefined,
+ },
+ showNoteToSelfDescription: {
+ en: 'Are you sure you want to show Note to Self in your conversation list?',
+ args: undefined,
+ },
stickers: {
en: 'Stickers',
args: undefined,
@@ -3238,6 +3434,10 @@ export const simpleDictionary = {
en: 'See and share typing indicators.',
args: undefined,
},
+ unavailable: {
+ en: 'Unavailable',
+ args: undefined,
+ },
undo: {
en: 'Undo',
args: undefined,
@@ -3266,6 +3466,18 @@ export const simpleDictionary = {
en: 'Session failed to update. Please go to https://getsession.org/download and install the new version manually, then contact our Help Center to let us know about this problem.',
args: undefined,
},
+ updateGroupInformation: {
+ en: 'Update Group Information',
+ args: undefined,
+ },
+ updateGroupInformationDescription: {
+ en: 'Group name and description are visible to all group members.',
+ args: undefined,
+ },
+ updateGroupInformationEnterShorterDescription: {
+ en: 'Please enter a shorter group description',
+ args: undefined,
+ },
updateNewVersion: {
en: 'A new version of Session is available, tap to update',
args: undefined,
@@ -3286,6 +3498,10 @@ export const simpleDictionary = {
en: 'Version {version}',
args: { version: 'string' },
},
+ updated: {
+ en: 'Last updated {relative_time} ago',
+ args: { relative_time: 'string' },
+ },
uploading: {
en: 'Uploading',
args: undefined,
diff --git a/run/screenshots/android/app_disguise.png b/run/screenshots/android/app_disguise.png
new file mode 100644
index 00000000..95cf431e
--- /dev/null
+++ b/run/screenshots/android/app_disguise.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:18ed347a459550771454d6038a4a4eb2d52792f0392ece8e8dace5c919251007
+size 127457
diff --git a/run/screenshots/ios/app_disguise.png b/run/screenshots/ios/app_disguise.png
new file mode 100644
index 00000000..a17bbfea
--- /dev/null
+++ b/run/screenshots/ios/app_disguise.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6987b366a695c5b9b39855e95230b0965928f4c7a6f7e9d090f2cf686191d8e0
+size 454344
diff --git a/run/test/specs/app_disguise_icons.spec.ts b/run/test/specs/app_disguise_icons.spec.ts
new file mode 100644
index 00000000..538fc98c
--- /dev/null
+++ b/run/test/specs/app_disguise_icons.spec.ts
@@ -0,0 +1,30 @@
+import { bothPlatformsIt } from '../../types/sessionIt';
+import { SupportedPlatformsType, closeApp, openAppOnPlatformSingleDevice } from './utils/open_app';
+import { newUser } from './utils/create_account';
+import { USERNAME } from '../../types/testing';
+import { AppearanceMenuItem, SelectAppIcon, UserSettings } from './locators/settings';
+import { verifyElementScreenshot } from './utils/verify_screenshots';
+import { AppDisguisePageScreenshot } from './utils/screenshot_paths';
+import { sleepFor } from './utils';
+
+bothPlatformsIt({
+ title: 'App disguise icons',
+ risk: 'medium',
+ countOfDevicesNeeded: 1,
+ testCb: appDisguiseIcons,
+});
+
+async function appDisguiseIcons(platform: SupportedPlatformsType) {
+ const { device } = await openAppOnPlatformSingleDevice(platform);
+ await newUser(device, USERNAME.ALICE);
+ 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));
+ await closeApp(device);
+}
diff --git a/run/test/specs/app_disguise_set.spec.ts b/run/test/specs/app_disguise_set.spec.ts
new file mode 100644
index 00000000..cdb37059
--- /dev/null
+++ b/run/test/specs/app_disguise_set.spec.ts
@@ -0,0 +1,57 @@
+import { androidIt } from '../../types/sessionIt';
+import { SupportedPlatformsType, openAppOnPlatformSingleDevice } from './utils/open_app';
+import { newUser } from './utils/create_account';
+import { USERNAME } from '../../types/testing';
+import {
+ AppDisguiseMeetingIcon,
+ AppearanceMenuItem,
+ CloseAppButton,
+ SelectAppIcon,
+ UserSettings,
+} from './locators/settings';
+import { DisguisedApp } from './locators/external';
+import { sleepFor } from './utils';
+import { runScriptAndLog } from './utils/utilities';
+import { getAdbFullPath } from './utils/binaries';
+import { closeApp } from './utils/open_app';
+import { englishStrippedStr } from '../../localizer/englishStrippedStr';
+
+// iOS implementation blocked by SES-3809
+androidIt({
+ title: 'App disguise set icon',
+ risk: 'medium',
+ countOfDevicesNeeded: 1,
+ testCb: appDisguiseSetIcon,
+});
+
+async function appDisguiseSetIcon(platform: SupportedPlatformsType) {
+ const { device } = await openAppOnPlatformSingleDevice(platform);
+ await newUser(device, USERNAME.ALICE);
+ 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`,
+ true
+ );
+ }
+}
diff --git a/run/test/specs/locators/external.ts b/run/test/specs/locators/external.ts
index 1145b2f7..eeb9080c 100644
--- a/run/test/specs/locators/external.ts
+++ b/run/test/specs/locators/external.ts
@@ -8,3 +8,13 @@ export class PhotoLibrary extends LocatorsInterface {
} as const;
}
}
+
+export class DisguisedApp extends LocatorsInterface {
+ public build() {
+ return {
+ strategy: 'accessibility id',
+ selector: 'MeetingSE',
+ maxWait: 5000,
+ } as const;
+ }
+}
diff --git a/run/test/specs/locators/settings.ts b/run/test/specs/locators/settings.ts
index 2a80fb96..0a05e42c 100644
--- a/run/test/specs/locators/settings.ts
+++ b/run/test/specs/locators/settings.ts
@@ -104,3 +104,78 @@ export class BlockedContacts extends LocatorsInterface {
}
}
}
+
+export class AppearanceMenuItem extends LocatorsInterface {
+ public build() {
+ switch (this.platform) {
+ case 'android':
+ case 'ios':
+ return {
+ strategy: 'accessibility id',
+ selector: 'Appearance',
+ } as const;
+ }
+ }
+}
+
+export class SelectAppIcon extends LocatorsInterface {
+ public build() {
+ switch (this.platform) {
+ case 'android':
+ return {
+ strategy: 'id',
+ selector: 'network.loki.messenger:id/system_settings_app_icon',
+ } as const;
+ case 'ios':
+ return {
+ strategy: 'accessibility id',
+ selector: 'Select alternate app icon',
+ } as const;
+ }
+ }
+}
+export class AppDisguisePage extends LocatorsInterface {
+ public build() {
+ switch (this.platform) {
+ case 'android':
+ return {
+ strategy: 'class name',
+ selector: 'android.widget.ScrollView',
+ } as const;
+ case 'ios':
+ return {
+ strategy: 'class name',
+ selector: 'XCUIElementTypeTable',
+ } as const;
+ }
+ }
+}
+export class AppDisguiseMeetingIcon extends LocatorsInterface {
+ public build() {
+ switch (this.platform) {
+ case 'android':
+ return {
+ strategy: 'id',
+ selector: 'MeetingSE option',
+ } as const;
+ case 'ios':
+ // NOTE see SES-3809
+ throw new Error('No locators implemented for iOS');
+ }
+ }
+}
+
+export class CloseAppButton extends LocatorsInterface {
+ public build() {
+ switch (this.platform) {
+ case 'android':
+ return {
+ strategy: 'class name',
+ selector: 'android.widget.TextView',
+ text: 'Close App',
+ } as const;
+ case 'ios':
+ throw new Error('Modal not implemented for iOS');
+ }
+ }
+}
diff --git a/run/test/specs/utils/screenshot_paths.ts b/run/test/specs/utils/screenshot_paths.ts
index 5d750bb8..8ea2c27e 100644
--- a/run/test/specs/utils/screenshot_paths.ts
+++ b/run/test/specs/utils/screenshot_paths.ts
@@ -2,6 +2,7 @@ import path from 'path';
import { EmptyLandingPage } from '../locators/home';
import { SupportedPlatformsType } from './open_app';
import { PageName } from '../../../types/testing';
+import { AppDisguisePage } from '../locators/settings';
// Extends locator classes with baseline screenshot paths for visual regression testing
// If a locator appears in multiple states, a state argument must be provided to screenshotFileName()
@@ -18,3 +19,9 @@ export class BrowserPageScreenshot {
return path.join('run', 'screenshots', platform, `browser_${pageName}.png`);
}
}
+
+export class AppDisguisePageScreenshot extends AppDisguisePage {
+ public screenshotFileName(): string {
+ return path.join('run', 'screenshots', this.platform, 'app_disguise.png');
+ }
+}
diff --git a/run/types/DeviceWrapper.ts b/run/types/DeviceWrapper.ts
index 41dfb15b..34db3153 100644
--- a/run/types/DeviceWrapper.ts
+++ b/run/types/DeviceWrapper.ts
@@ -1587,6 +1587,11 @@ export class DeviceWrapper {
await this.scroll({ x: 760, y: 710 }, { x: 760, y: 1500 }, 100);
}
+ public async swipeFromBottom(): Promise {
+ const { width, height } = await this.getWindowRect();
+
+ await this.scroll({ x: width / 2, y: height * 0.95 }, { x: width / 2, y: height * 0.35 }, 100);
+ }
public async scrollToBottom() {
if (this.isAndroid()) {
const scrollButton = await this.doesElementExist({
diff --git a/run/types/testing.ts b/run/types/testing.ts
index deaab229..cacb68c1 100644
--- a/run/types/testing.ts
+++ b/run/types/testing.ts
@@ -361,7 +361,10 @@ export type AccessibilityId =
| 'Learn about staking link'
| 'Last updated timestamp'
| 'Albums'
- | 'Select';
+ | 'Select'
+ | 'Appearance'
+ | 'Select alternate app icon'
+ | 'MeetingSE';
export type Id =
| 'Modal heading'
@@ -433,7 +436,8 @@ export type Id =
| 'session-network-menu-item'
| 'Last updated timestamp'
| 'Image button'
- | 'android.widget.TextView';
+ | 'network.loki.messenger:id/system_settings_app_icon'
+ | 'MeetingSE option';
export type TestRisk = 'high' | 'medium' | 'low';