2018-01-26 11:07:09 +00:00
|
|
|
import time
|
2022-11-21 04:49:53 +00:00
|
|
|
|
2023-07-18 00:51:04 +00:00
|
|
|
from appium.webdriver.common.mobileby import MobileBy
|
2018-07-03 18:50:18 +00:00
|
|
|
from selenium.common.exceptions import TimeoutException, NoSuchElementException
|
2023-07-31 15:54:31 +00:00
|
|
|
from typing_extensions import Literal
|
2022-11-21 04:49:53 +00:00
|
|
|
|
|
|
|
from tests import test_dapp_url
|
2022-12-29 13:49:00 +00:00
|
|
|
from views.base_element import Button, Text, BaseElement, SilentButton, CheckBox, EditBox
|
2023-07-18 00:51:04 +00:00
|
|
|
from views.base_view import BaseView, UnreadMessagesCountText
|
2018-01-14 17:43:36 +00:00
|
|
|
|
2021-07-20 08:34:10 +00:00
|
|
|
|
2021-01-25 16:35:40 +00:00
|
|
|
class ChatButton(Button):
|
|
|
|
def __init__(self, driver, **kwargs):
|
|
|
|
super().__init__(driver, **kwargs)
|
2019-04-05 13:05:23 +00:00
|
|
|
|
2018-01-14 17:43:36 +00:00
|
|
|
def navigate(self):
|
2021-01-25 16:35:40 +00:00
|
|
|
from views.chat_view import ChatView
|
|
|
|
return ChatView(self.driver)
|
2019-11-20 16:27:29 +00:00
|
|
|
|
2023-05-04 14:26:59 +00:00
|
|
|
|
2023-03-13 13:29:18 +00:00
|
|
|
class ActivityTabButton(Button):
|
|
|
|
def __init__(self, driver, **kwargs):
|
|
|
|
super().__init__(driver, **kwargs)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def counter(self):
|
2023-06-26 09:59:29 +00:00
|
|
|
return BaseElement(self.driver,
|
|
|
|
xpath='//*[@content-desc="%s"]//*[@content-desc="notification-dot"]' % self.accessibility_id)
|
2023-03-13 13:29:18 +00:00
|
|
|
|
2021-11-18 15:16:48 +00:00
|
|
|
|
2021-01-25 16:35:40 +00:00
|
|
|
class ChatElement(SilentButton):
|
2023-02-24 19:05:30 +00:00
|
|
|
def __init__(self, driver, username_part, community=False, community_channel=False):
|
2018-06-28 18:46:51 +00:00
|
|
|
self.username = username_part
|
2021-07-08 08:32:08 +00:00
|
|
|
self.community = community
|
2023-02-24 19:05:30 +00:00
|
|
|
self.community_channel = community_channel
|
2023-08-24 12:30:47 +00:00
|
|
|
if self.community_channel:
|
2023-06-26 09:59:29 +00:00
|
|
|
super().__init__(
|
|
|
|
driver,
|
2023-08-25 09:39:55 +00:00
|
|
|
xpath="//*[@content-desc='chat-name-text']//*[starts-with(@text,'# %s')]/.." % username_part)
|
2023-08-24 12:30:47 +00:00
|
|
|
elif community:
|
2023-06-26 09:59:29 +00:00
|
|
|
super().__init__(
|
|
|
|
driver,
|
|
|
|
xpath="//*[@content-desc='chat-name-text'][starts-with(@text,'%s')]/.." % username_part)
|
2023-08-24 12:30:47 +00:00
|
|
|
else:
|
|
|
|
super().__init__(
|
|
|
|
driver,
|
|
|
|
xpath="//*[@content-desc='author-primary-name'][starts-with(@text,'%s')]/.." % username_part)
|
2018-01-14 17:43:36 +00:00
|
|
|
|
|
|
|
def navigate(self):
|
2021-07-08 08:32:08 +00:00
|
|
|
if self.community:
|
|
|
|
from views.chat_view import CommunityView
|
|
|
|
return CommunityView(self.driver)
|
|
|
|
else:
|
|
|
|
from views.chat_view import ChatView
|
|
|
|
return ChatView(self.driver)
|
2018-01-14 17:43:36 +00:00
|
|
|
|
2018-05-25 17:29:07 +00:00
|
|
|
def click(self):
|
2021-07-08 08:32:08 +00:00
|
|
|
if self.community:
|
|
|
|
from views.chat_view import CommunityView
|
2023-02-24 19:05:30 +00:00
|
|
|
desired_element = CommunityView(self.driver).community_description_text
|
2021-07-08 08:32:08 +00:00
|
|
|
else:
|
|
|
|
from views.chat_view import ChatView
|
|
|
|
desired_element = ChatView(self.driver).chat_message_input
|
2018-05-25 17:29:07 +00:00
|
|
|
self.click_until_presence_of_element(desired_element=desired_element)
|
2021-07-08 08:32:08 +00:00
|
|
|
|
2018-05-25 17:29:07 +00:00
|
|
|
return self.navigate()
|
|
|
|
|
2018-07-03 18:50:18 +00:00
|
|
|
def find_element(self):
|
2018-07-13 10:56:36 +00:00
|
|
|
for i in range(2):
|
2018-07-03 18:50:18 +00:00
|
|
|
try:
|
|
|
|
return super(ChatElement, self).find_element()
|
2018-07-13 10:56:36 +00:00
|
|
|
except NoSuchElementException as e:
|
|
|
|
if i == 0:
|
2021-02-16 14:57:20 +00:00
|
|
|
self.wait_for_visibility_of_element(20)
|
2018-07-13 10:56:36 +00:00
|
|
|
else:
|
2021-02-16 14:57:20 +00:00
|
|
|
e.msg = 'Device %s: Unable to find chat with name %s' % (self.driver.number, self.username)
|
2018-07-13 10:56:36 +00:00
|
|
|
raise e
|
2018-07-03 18:50:18 +00:00
|
|
|
|
2018-07-19 09:57:45 +00:00
|
|
|
@property
|
|
|
|
def new_messages_counter(self):
|
2023-07-18 00:51:04 +00:00
|
|
|
if self.community:
|
|
|
|
return UnreadMessagesCountText(self.driver, self.locator)
|
|
|
|
|
|
|
|
class NewMessageCounterText(Text):
|
|
|
|
def __init__(self, driver, parent_locator: str):
|
|
|
|
super().__init__(
|
|
|
|
driver,
|
|
|
|
xpath="%s//*[@content-desc='new-message-counter']/android.widget.TextView" % parent_locator)
|
|
|
|
|
|
|
|
return NewMessageCounterText(self.driver, self.locator)
|
2018-07-19 09:57:45 +00:00
|
|
|
|
2021-04-30 09:31:39 +00:00
|
|
|
@property
|
|
|
|
def chat_preview(self):
|
|
|
|
class PreveiewMessageText(Text):
|
|
|
|
def __init__(self, driver, parent_locator: str):
|
|
|
|
super().__init__(driver, xpath="%s//*[@content-desc='chat-message-text']" % parent_locator)
|
|
|
|
|
2021-11-26 14:15:26 +00:00
|
|
|
return PreveiewMessageText(self.driver, self.locator)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def no_message_preview(self):
|
|
|
|
class NoMessageText(Text):
|
|
|
|
def __init__(self, driver, parent_locator: str):
|
|
|
|
super().__init__(driver, xpath="%s//*[@content-desc='no-messages-text']" % parent_locator)
|
2023-06-26 09:59:29 +00:00
|
|
|
|
2021-11-26 14:15:26 +00:00
|
|
|
return NoMessageText(self.driver, self.locator)
|
2021-04-30 09:31:39 +00:00
|
|
|
|
2020-03-03 12:06:55 +00:00
|
|
|
@property
|
2023-08-02 12:17:11 +00:00
|
|
|
def new_messages_grey_dot(self):
|
2020-03-03 12:06:55 +00:00
|
|
|
class UnreadMessagesPublicChat(BaseElement):
|
2023-08-02 12:17:11 +00:00
|
|
|
def __init__(self, driver, parent_locator):
|
|
|
|
super().__init__(driver, xpath="%s/*[@content-desc='unviewed-messages-public']" % parent_locator)
|
|
|
|
|
|
|
|
return UnreadMessagesPublicChat(self.driver, self.locator)
|
|
|
|
|
|
|
|
# @property
|
|
|
|
# def new_messages_community(self):
|
|
|
|
# class UnreadMessagesCommunity(BaseElement):
|
|
|
|
# def __init__(self, driver, parent_locator: str):
|
|
|
|
# super().__init__(driver, prefix=parent_locator, xpath="%s/android.view.ViewGroup" % parent_locator)
|
|
|
|
#
|
|
|
|
# return UnreadMessagesCommunity(self.driver, self.locator)
|
2022-12-13 18:07:34 +00:00
|
|
|
|
2021-03-04 09:35:48 +00:00
|
|
|
@property
|
|
|
|
def chat_image(self):
|
|
|
|
class ChatImage(BaseElement):
|
|
|
|
def __init__(self, driver):
|
|
|
|
super().__init__(driver, xpath="//*[@content-desc='chat-icon']")
|
|
|
|
|
|
|
|
return ChatImage(self.driver)
|
|
|
|
|
2021-11-18 15:16:48 +00:00
|
|
|
|
2022-12-09 12:56:49 +00:00
|
|
|
class ActivityCenterElement(SilentButton):
|
|
|
|
def __init__(self, driver, username):
|
|
|
|
self.chat_name = username
|
2021-11-18 15:16:48 +00:00
|
|
|
super().__init__(driver,
|
2022-12-09 12:56:49 +00:00
|
|
|
xpath="//*[contains(@text, '%s')]/ancestor::*[@content-desc='activity']" % username)
|
2021-07-05 19:28:55 +00:00
|
|
|
|
2023-03-13 13:29:18 +00:00
|
|
|
@property
|
|
|
|
def title(self):
|
2023-06-26 09:59:29 +00:00
|
|
|
return Button(self.driver, xpath=self.locator + '//*[@content-desc="activity-title"]')
|
2023-03-13 13:29:18 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def unread_indicator(self):
|
|
|
|
return Button(self.driver, xpath=self.locator + '//*[@content-desc="activity-unread-indicator"]')
|
|
|
|
|
2023-03-20 12:58:09 +00:00
|
|
|
@property
|
|
|
|
def message_body(self):
|
|
|
|
return Button(self.driver, xpath=self.locator + '//*[@content-desc="activity-message-body"]')
|
|
|
|
|
2023-03-01 11:33:58 +00:00
|
|
|
def handle_cr(self, element_accessibility: str):
|
2022-06-28 15:54:14 +00:00
|
|
|
try:
|
2023-03-01 11:33:58 +00:00
|
|
|
accept_element = Button(self.driver,
|
|
|
|
xpath=self.locator + '/*[@content-desc="%s"]' % element_accessibility).find_element()
|
2022-06-28 15:54:14 +00:00
|
|
|
except NoSuchElementException:
|
|
|
|
return ''
|
|
|
|
if accept_element:
|
|
|
|
accept_element.click()
|
|
|
|
|
2023-03-01 11:33:58 +00:00
|
|
|
def accept_contact_request(self):
|
|
|
|
self.handle_cr("accept-contact-request")
|
|
|
|
|
2022-06-28 15:54:14 +00:00
|
|
|
def decline_contact_request(self):
|
2023-03-01 11:33:58 +00:00
|
|
|
self.handle_cr("decline-contact-request")
|
|
|
|
|
|
|
|
def cancel_contact_request(self):
|
|
|
|
self.handle_cr("cancel-contact-request")
|
2022-06-28 15:54:14 +00:00
|
|
|
|
2021-11-18 15:16:48 +00:00
|
|
|
|
2021-10-22 17:23:27 +00:00
|
|
|
class PushNotificationElement(SilentButton):
|
|
|
|
def __init__(self, driver, pn_text):
|
|
|
|
self.pn_text = pn_text
|
|
|
|
super().__init__(driver, xpath="//*[@text='%s']" % pn_text)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def icon(self):
|
|
|
|
class PnIconElement(BaseElement):
|
|
|
|
def __init__(self, driver, parent_locator):
|
|
|
|
super().__init__(driver,
|
|
|
|
xpath="%s/../../../../*/*[@resource-id='android:id/message_icon']" % parent_locator)
|
|
|
|
|
|
|
|
return PnIconElement(self.driver, self.locator)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def username(self):
|
|
|
|
class PnUsername(BaseElement):
|
|
|
|
def __init__(self, driver, parent_locator):
|
|
|
|
super().__init__(driver,
|
|
|
|
xpath="%s/../../*[@resource-id='android:id/message_name']" % parent_locator)
|
|
|
|
|
|
|
|
return PnUsername(self.driver, self.locator).text
|
|
|
|
|
|
|
|
@property
|
|
|
|
def group_chat_icon(self):
|
|
|
|
class GroupChatIconElement(BaseElement):
|
|
|
|
def __init__(self, driver, parent_locator):
|
2021-11-18 15:16:48 +00:00
|
|
|
super().__init__(driver,
|
2021-12-06 16:55:33 +00:00
|
|
|
xpath="%s/../../../../*[@resource-id='android:id/right_icon_container']" % parent_locator)
|
2021-10-22 17:23:27 +00:00
|
|
|
|
|
|
|
return GroupChatIconElement(self.driver, self.locator)
|
|
|
|
|
|
|
|
|
2023-06-26 09:59:29 +00:00
|
|
|
class ContactDetailsRow(BaseElement):
|
|
|
|
def __init__(self, driver, username=None, index=None):
|
|
|
|
main_locator = "//*[@content-desc='user-list']"
|
|
|
|
if username:
|
|
|
|
xpath_locator = "%s[*[contains(@text,'%s')]]" % (main_locator, username)
|
|
|
|
elif index:
|
|
|
|
xpath_locator = "%s[%s]" % (main_locator, index)
|
|
|
|
else:
|
|
|
|
xpath_locator = main_locator
|
|
|
|
super().__init__(driver, xpath=xpath_locator)
|
|
|
|
|
|
|
|
self.options_button = Button(self.driver, xpath="(%s//android.widget.ImageView)[2]" % xpath_locator)
|
|
|
|
self.username_text = Text(self.driver, xpath="(%s//android.widget.TextView)[2]" % xpath_locator)
|
|
|
|
|
|
|
|
|
2023-07-18 00:51:04 +00:00
|
|
|
class MuteButton(Button):
|
|
|
|
def __init__(self, driver, accessibility_id):
|
|
|
|
super().__init__(driver=driver, accessibility_id=accessibility_id)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def text(self):
|
|
|
|
return self.find_element().find_element(by=MobileBy.CLASS_NAME, value="android.widget.TextView").text
|
|
|
|
|
|
|
|
|
2018-01-14 17:43:36 +00:00
|
|
|
class HomeView(BaseView):
|
|
|
|
def __init__(self, driver):
|
2021-01-25 16:35:40 +00:00
|
|
|
super().__init__(driver)
|
2018-01-14 17:43:36 +00:00
|
|
|
|
2021-01-25 16:35:40 +00:00
|
|
|
self.plus_button = Button(self.driver, accessibility_id="new-chat-button")
|
2023-05-19 13:08:52 +00:00
|
|
|
self.plus_community_button = Button(self.driver, accessibility_id="new-communities-button")
|
2021-01-25 16:35:40 +00:00
|
|
|
self.chat_name_text = Text(self.driver, accessibility_id="chat-name-text")
|
|
|
|
self.start_new_chat_button = ChatButton(self.driver, accessibility_id="start-1-1-chat-button")
|
|
|
|
self.new_group_chat_button = ChatButton(self.driver, accessibility_id="start-group-chat-button")
|
|
|
|
self.join_public_chat_button = ChatButton(self.driver, accessibility_id="join-public-chat-button")
|
|
|
|
self.universal_qr_scanner_button = Button(self.driver, accessibility_id="universal-qr-scanner")
|
|
|
|
self.invite_friends_button = Button(self.driver, accessibility_id="invite-friends-button")
|
|
|
|
self.stop_status_service_button = Button(self.driver, accessibility_id="STOP")
|
2021-11-18 15:16:48 +00:00
|
|
|
self.my_profile_on_start_new_chat_button = Button(self.driver,
|
|
|
|
xpath="//*[@content-desc='current-account-photo']")
|
2023-04-28 10:15:32 +00:00
|
|
|
self.communities_button = ChatButton(self.driver, accessibility_id="create-community")
|
2023-07-31 15:54:31 +00:00
|
|
|
self.create_closed_community_button = ChatButton(self.driver, accessibility_id="create-closed-community")
|
|
|
|
self.create_open_community_button = ChatButton(self.driver, accessibility_id="create-open-community")
|
|
|
|
self.create_token_gated_community_button = ChatButton(self.driver,
|
|
|
|
accessibility_id="create-token-gated-community")
|
2022-08-18 15:15:10 +00:00
|
|
|
self.ens_banner_close_button = Button(self.driver, accessibility_id=":ens-banner-close-button")
|
2021-01-25 16:35:40 +00:00
|
|
|
|
2021-04-19 08:37:46 +00:00
|
|
|
# Notification centre
|
|
|
|
self.notifications_button = Button(self.driver, accessibility_id="notifications-button")
|
2023-03-15 15:41:34 +00:00
|
|
|
self.notifications_unread_badge = BaseElement(self.driver, accessibility_id="activity-center-unread-count")
|
2023-07-27 11:14:09 +00:00
|
|
|
self.show_qr_code_button = Button(self.driver, accessibility_id="show-qr-button")
|
2022-11-28 11:17:12 +00:00
|
|
|
self.open_activity_center_button = Button(self.driver, accessibility_id="open-activity-center-button")
|
2022-12-09 12:56:49 +00:00
|
|
|
self.close_activity_centre = Button(self.driver, accessibility_id="close-activity-center")
|
2023-06-26 09:59:29 +00:00
|
|
|
|
2021-05-07 09:19:05 +00:00
|
|
|
self.notifications_select_button = Button(self.driver, translation_id="select")
|
|
|
|
self.notifications_reject_and_delete_button = Button(self.driver, accessibility_id="reject-and-delete"
|
|
|
|
"-activity-center")
|
2021-11-18 15:16:48 +00:00
|
|
|
self.notifications_accept_and_add_button = Button(self.driver,
|
|
|
|
accessibility_id="accept-and-add-activity-center")
|
2021-05-07 09:19:05 +00:00
|
|
|
self.notifications_select_all = Button(self.driver, xpath="(//android.widget.CheckBox["
|
2021-12-21 13:57:13 +00:00
|
|
|
"@content-desc='checkbox-off'])[1]")
|
2023-05-04 14:26:59 +00:00
|
|
|
|
|
|
|
# Tabs and elements on messages home view
|
2022-12-29 13:49:00 +00:00
|
|
|
self.recent_tab = Button(self.driver, accessibility_id="tab-recent")
|
|
|
|
self.groups_tab = Button(self.driver, accessibility_id="tab-groups")
|
|
|
|
self.contacts_tab = Button(self.driver, accessibility_id="tab-contacts")
|
|
|
|
self.contact_new_badge = Button(self.driver, accessibility_id="notification-dot")
|
2023-06-26 09:59:29 +00:00
|
|
|
self.pending_contact_request_button = Button(self.driver,
|
|
|
|
accessibility_id="open-activity-center-contact-requests")
|
|
|
|
self.pending_contact_request_text = Text(
|
|
|
|
self.driver,
|
|
|
|
xpath='//*[@content-desc="pending-contact-requests-count"]/android.widget.TextView')
|
2022-12-29 13:49:00 +00:00
|
|
|
|
2023-05-04 14:26:59 +00:00
|
|
|
# Tabs and elements on community home view
|
|
|
|
self.pending_communities_tab = Button(self.driver, accessibility_id="pending-tab")
|
|
|
|
self.joined_communities_tab = Button(self.driver, accessibility_id="joined-tab")
|
|
|
|
self.opened_communities_tab = Button(self.driver, accessibility_id="opened-tab")
|
2021-08-23 15:22:36 +00:00
|
|
|
|
2021-01-25 16:35:40 +00:00
|
|
|
# Options on long tap
|
|
|
|
self.chats_menu_invite_friends_button = Button(self.driver, accessibility_id="chats-menu-invite-friends-button")
|
2023-01-09 13:46:24 +00:00
|
|
|
self.delete_chat_button = Button(self.driver, translation_id="delete-chat")
|
2023-03-20 12:58:09 +00:00
|
|
|
self.clear_history_button = Button(self.driver, accessibility_id="clear-history")
|
2023-07-18 00:51:04 +00:00
|
|
|
self.mute_chat_button = MuteButton(self.driver, accessibility_id="mute-chat")
|
|
|
|
self.mute_community_button = MuteButton(self.driver, accessibility_id="mute-community")
|
|
|
|
self.mute_channel_button = MuteButton(self.driver, accessibility_id="chat-toggle-muted")
|
2023-04-28 10:15:32 +00:00
|
|
|
self.mark_all_messages_as_read_button = Button(self.driver, accessibility_id="mark-as-read")
|
2019-04-05 13:05:23 +00:00
|
|
|
|
2021-02-16 14:57:20 +00:00
|
|
|
# Connection icons
|
|
|
|
self.mobile_connection_off_icon = Button(self.driver, accessibility_id="conn-button-mobile-sync-off")
|
|
|
|
self.mobile_connection_on_icon = Button(self.driver, accessibility_id="conn-button-mobile-sync")
|
|
|
|
self.connection_offline_icon = Button(self.driver, accessibility_id="conn-button-offline")
|
|
|
|
|
|
|
|
# Sync using mobile data bottom sheet
|
|
|
|
self.continue_syncing_button = Button(self.driver, accessibility_id="mobile-network-continue-syncing")
|
|
|
|
self.stop_syncing_button = Button(self.driver, accessibility_id="mobile-network-stop-syncing")
|
2021-12-16 16:25:42 +00:00
|
|
|
self.remember_my_choice_checkbox = CheckBox(self.driver, accessibility_id=":checkbox-on")
|
2021-02-16 14:57:20 +00:00
|
|
|
|
|
|
|
# Connection status bottom sheet
|
|
|
|
self.connected_to_n_peers_text = Text(self.driver, accessibility_id="connected-to-n-peers")
|
2021-11-18 15:16:48 +00:00
|
|
|
self.connected_to_node_text = Text(self.driver, accessibility_id="connected-to-mailserver")
|
2021-02-16 14:57:20 +00:00
|
|
|
self.waiting_for_wi_fi = Text(self.driver, accessibility_id="waiting-wi-fi")
|
|
|
|
self.use_mobile_data_switch = Button(self.driver, accessibility_id="mobile-network-use-mobile")
|
2021-11-18 15:16:48 +00:00
|
|
|
self.connection_settings_button = Button(self.driver, accessibility_id="settings")
|
2021-02-16 14:57:20 +00:00
|
|
|
self.not_connected_to_node_text = Text(self.driver, accessibility_id="not-connected-nodes")
|
|
|
|
self.not_connected_to_peers_text = Text(self.driver, accessibility_id="not-connected-to-peers")
|
|
|
|
|
2022-11-21 04:49:53 +00:00
|
|
|
# New UI
|
|
|
|
self.new_chat_button = Button(self.driver, accessibility_id="new-chat-button")
|
2023-08-09 16:14:42 +00:00
|
|
|
self.discover_communities_button = Button(self.driver, accessibility_id="communities-home-discover-card")
|
2022-11-21 04:49:53 +00:00
|
|
|
|
2023-02-02 11:16:34 +00:00
|
|
|
# New UI bottom sheet
|
|
|
|
self.start_a_new_chat_bottom_sheet_button = Button(self.driver, accessibility_id="start-a-new-chat")
|
|
|
|
self.add_a_contact_chat_bottom_sheet_button = Button(self.driver, accessibility_id="add-a-contact")
|
2023-03-01 11:33:58 +00:00
|
|
|
self.setup_chat_button = Button(self.driver, accessibility_id="next-button")
|
2023-02-02 11:16:34 +00:00
|
|
|
|
2023-03-13 13:29:18 +00:00
|
|
|
# Activity centre
|
2023-03-20 12:58:09 +00:00
|
|
|
self.all_activity_tab_button = ActivityTabButton(self.driver, translation_id="all")
|
2023-03-13 13:29:18 +00:00
|
|
|
self.mention_activity_tab_button = ActivityTabButton(self.driver, accessibility_id="tab-mention")
|
|
|
|
self.reply_activity_tab_button = ActivityTabButton(self.driver, accessibility_id="tab-reply")
|
Swipe gestures for Activity Center notifications with CTA (#15284)
Implements swipe actions for notifications with call to action (e.g. pending
contact requests, unverified identity verifications, etc).
Fixes https://github.com/status-im/status-mobile/issues/15118
According to the Design team, the goal is to deliver a consistent experience to
users, so whenever the user sees a notification with buttons, the same actions
can be taken via the swipe buttons.
Note: swipe buttons are using placeholder icons while the Design team works out
which ones to use
Additionally, a bunch of fixes:
- Fix: outgoing pending contact requests were not being removed from the UI when
cancelled.
- Fix: Membership tab not showing unread indicator.
- Fix: dismissed membership notification not marked as read.
- Fix: dismissed membership notification was displaying decline/accept buttons.
Regression came from changes in status-go related to soft deletion of
notifications.
- Fix: incorrect check for the pending state of a contact request.
- Fixed lots of bugs for identity verification notifications, as it was
completely broken. Unfortunately, somebody made lots of changes without
actually testing the flows.
- Add basic error handling and log if accepting, declining or canceling contact
requests fail.
The demo shows an identity verification with swipe actions to reply or decline.
[identity-verification-swipe-to-reply.webm](https://user-images.githubusercontent.com/46027/223565755-b2ca3f68-12e2-4e1e-9e52-edd52cfcc971.webm)
Out of scope: The old quo input is still in use in the identity verification
notification. This will eventually be solved by issue
https://github.com/status-im/status-mobile/issues/14364
### Steps to test
Notifications with one or more buttons (actions) are affected by this change,
because now the user can also swipe left/right to act on them.
- Membership notifications: private group chat. The following PR explains how to
generate them https://github.com/status-im/status-mobile/pull/14785
- Contact requests, and community gated requests to join (Admin tab).
- Identity verifications. I believe the only way to test identity verification
flows at the moment is to use the Desktop app, since initiating the challenge
is not implemented in Mobile yet.
- Mentions and replies don't have new swipe buttons because they don't have call
to action buttons throughout their lifecycle.
Steps to test identity verification flows:
#### Identity verification flow 1
- `A` and `B` are mutual contacts.
- `A` sends a verification request to `B`.
- `A` should not see any notification yet.
- `B` should receive an identity verification notification. `B` can either
decline or reply.
- `B` declines and the status `Declined` is shown instead of buttons.
- `B` can now either swipe to toggle read/unread or swipe delete the
notification.
- `A` should not receive any notification after `A` declined.
#### Identity verification flow 2
- `A` and `B` are mutual contacts.
- `A` sends a verification request to `B`.
- `A` should not see any notification yet.
- `B` should receive an identity verification notification. `B` can either
decline or reply.
- `B` press `Reply` and a bottom sheet is displayed with a text input.
- `B` sends the reply/answer message and the status `Replied` is shown instead
of buttons.
- `B` can now either swipe to toggle read/unread or swipe to delete the
notification.
- `A` should receive a notification with the reply from `B`.
- `A` can either mark the answer as untrustworthy or accept it (trust it) via
the normal buttons, as well as via the swipe left/right buttons.
- If `A` accepts the answer, then the status `Confirmed` is shown instead of
buttons. On the other hand, if `A` marks as untrustworthy, then the status
`Untrustworthy` is shown instead of buttons.
- `B` should receive no further notifications due to `A`s actions.
- `A` can now either swipe to toggle read/unread or swipe delete the
notification.
2023-03-14 15:34:13 +00:00
|
|
|
self.activity_notification_swipe_button = Button(self.driver, accessibility_id="notification-swipe")
|
2023-03-13 13:29:18 +00:00
|
|
|
self.activity_unread_filter_button = Button(self.driver, accessibility_id="selector-filter")
|
2023-03-20 12:58:09 +00:00
|
|
|
self.more_options_activity_button = Button(self.driver, accessibility_id="activity-center-open-more")
|
|
|
|
self.mark_all_read_activity_button = Button(self.driver, translation_id="mark-all-notifications-as-read")
|
2023-03-13 13:29:18 +00:00
|
|
|
|
2023-07-27 11:14:09 +00:00
|
|
|
# Share tab
|
|
|
|
self.link_to_profile_text = Text(
|
|
|
|
self.driver,
|
|
|
|
xpath="(//*[@content-desc='link-to-profile']/preceding-sibling::*[1]/android.widget.TextView)[1]")
|
|
|
|
|
2018-01-14 17:43:36 +00:00
|
|
|
def wait_for_syncing_complete(self):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info('Waiting for syncing to complete')
|
2018-01-14 17:43:36 +00:00
|
|
|
while True:
|
|
|
|
try:
|
2021-02-01 16:09:04 +00:00
|
|
|
sync = self.element_by_text_part('Syncing').wait_for_element(10)
|
2018-08-15 12:51:52 +00:00
|
|
|
self.driver.info(sync.text)
|
2018-01-14 17:43:36 +00:00
|
|
|
except TimeoutException:
|
|
|
|
break
|
|
|
|
|
2023-02-24 19:05:30 +00:00
|
|
|
def get_chat(self, username, community=False, community_channel=False, wait_time=10):
|
|
|
|
if community:
|
|
|
|
self.driver.info("Looking for community: '%s'" % username)
|
|
|
|
else:
|
|
|
|
self.driver.info("Looking for chat: '%s'" % username)
|
|
|
|
chat_element = ChatElement(self.driver, username[:25], community=community, community_channel=community_channel)
|
|
|
|
if not chat_element.is_element_displayed(wait_time) and community is False and community_channel is False:
|
2022-11-28 11:17:12 +00:00
|
|
|
if self.notifications_unread_badge.is_element_displayed(30):
|
2022-12-19 02:02:57 +00:00
|
|
|
chat_in_ac = ActivityCenterElement(self.driver, username[:25])
|
|
|
|
self.open_activity_center_button.click_until_presence_of_element(chat_in_ac)
|
|
|
|
chat_in_ac.wait_for_element(20)
|
|
|
|
chat_in_ac.click()
|
2021-04-19 08:37:46 +00:00
|
|
|
return chat_element
|
|
|
|
|
2023-04-28 10:15:32 +00:00
|
|
|
def get_to_community_channel_from_home(self, community_name, channel_name='general'):
|
|
|
|
community_view = self.get_community_view()
|
|
|
|
self.get_chat(community_name, community=True).click()
|
|
|
|
return community_view.get_channel(channel_name).click()
|
|
|
|
|
2021-04-19 08:37:46 +00:00
|
|
|
def get_chat_from_home_view(self, username):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("Looking for chat: '%s'" % username)
|
2021-04-19 08:37:46 +00:00
|
|
|
chat_element = ChatElement(self.driver, username[:25])
|
|
|
|
return chat_element
|
2018-01-26 11:07:09 +00:00
|
|
|
|
2023-03-13 13:29:18 +00:00
|
|
|
def get_element_from_activity_center_view(self, message_body):
|
|
|
|
self.driver.info("Looking for activity center element: '%s'" % message_body)
|
|
|
|
chat_element = ActivityCenterElement(self.driver, message_body)
|
2021-08-23 15:22:36 +00:00
|
|
|
return chat_element
|
|
|
|
|
2023-03-01 11:33:58 +00:00
|
|
|
def handle_contact_request(self, username: str, action='accept'):
|
2023-07-14 15:08:04 +00:00
|
|
|
if self.toast_content_element.is_element_displayed(10):
|
|
|
|
self.toast_content_element.wait_for_invisibility_of_element()
|
2022-11-28 11:17:12 +00:00
|
|
|
if self.notifications_unread_badge.is_element_displayed(30):
|
|
|
|
self.open_activity_center_button.click()
|
2022-12-09 12:56:49 +00:00
|
|
|
chat_element = ActivityCenterElement(self.driver, username[:25])
|
2023-03-01 11:33:58 +00:00
|
|
|
if action == 'accept':
|
|
|
|
self.driver.info("Accepting incoming CR for %s" % username)
|
2022-06-28 15:54:14 +00:00
|
|
|
chat_element.accept_contact_request()
|
2023-03-01 11:33:58 +00:00
|
|
|
elif action == 'decline':
|
|
|
|
self.driver.info("Rejecting incoming CR for %s" % username)
|
2022-06-28 15:54:14 +00:00
|
|
|
chat_element.decline_contact_request()
|
2023-03-01 11:33:58 +00:00
|
|
|
elif action == 'cancel':
|
|
|
|
self.driver.info("Canceling outgoing CR for %s" % username)
|
|
|
|
chat_element.cancel_contact_request()
|
|
|
|
else:
|
|
|
|
self.driver.fail("Illegal option for CR!")
|
2022-12-09 12:56:49 +00:00
|
|
|
self.close_activity_centre.click()
|
|
|
|
self.chats_tab.wait_for_visibility_of_element()
|
2022-06-28 15:54:14 +00:00
|
|
|
|
2020-03-19 16:34:20 +00:00
|
|
|
def get_username_below_start_new_chat_button(self, username_part):
|
2021-11-18 15:16:48 +00:00
|
|
|
return Text(self.driver,
|
|
|
|
xpath="//*[@content-desc='enter-contact-code-input']/../..//*[starts-with(@text,'%s')]" % username_part)
|
2020-03-19 16:34:20 +00:00
|
|
|
|
2023-03-01 11:33:58 +00:00
|
|
|
def add_contact(self, public_key, nickname='', remove_from_contacts=False):
|
|
|
|
self.driver.info("Adding user to Contacts via chats > add new contact")
|
|
|
|
self.new_chat_button.click_until_presence_of_element(self.add_a_contact_chat_bottom_sheet_button)
|
|
|
|
self.add_a_contact_chat_bottom_sheet_button.click()
|
|
|
|
|
|
|
|
chat = self.get_chat_view()
|
2021-02-04 12:51:01 +00:00
|
|
|
chat.public_key_edit_box.click()
|
|
|
|
chat.public_key_edit_box.send_keys(public_key)
|
2023-03-15 15:44:13 +00:00
|
|
|
chat.element_by_translation_id("user-found").wait_for_visibility_of_element()
|
|
|
|
if not chat.view_profile_new_contact_button.is_element_displayed():
|
|
|
|
chat.click_system_back_button()
|
2023-03-08 10:31:42 +00:00
|
|
|
chat.view_profile_new_contact_button.click_until_presence_of_element(chat.profile_block_contact_button)
|
2023-03-01 11:33:58 +00:00
|
|
|
if remove_from_contacts and chat.profile_remove_from_contacts.is_element_displayed():
|
|
|
|
chat.profile_remove_from_contacts.click()
|
2023-03-08 10:31:42 +00:00
|
|
|
chat.profile_add_to_contacts_button.click()
|
2020-11-02 15:21:33 +00:00
|
|
|
if nickname:
|
2023-03-01 11:33:58 +00:00
|
|
|
chat.set_nickname(nickname)
|
2023-08-18 16:20:40 +00:00
|
|
|
self.navigate_back_to_home_view()
|
2018-01-26 11:07:09 +00:00
|
|
|
|
2023-04-28 10:15:32 +00:00
|
|
|
def create_group_chat(self, user_names_to_add: list, group_chat_name: str = 'new_group_chat'):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("## Creating group chat '%s'" % group_chat_name, device=False)
|
2023-02-02 11:16:34 +00:00
|
|
|
self.new_chat_button.click()
|
2023-03-01 11:33:58 +00:00
|
|
|
chat = self.get_chat_view()
|
2023-04-28 10:15:32 +00:00
|
|
|
self.start_a_new_chat_bottom_sheet_button.click()
|
|
|
|
[chat.get_username_checkbox(user_name).click() for user_name in user_names_to_add]
|
|
|
|
self.setup_chat_button.click()
|
2023-03-01 11:33:58 +00:00
|
|
|
chat.chat_name_editbox.send_keys(group_chat_name)
|
|
|
|
chat.create_button.click()
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("## Group chat %s is created successfully!" % group_chat_name, device=False)
|
2023-03-01 11:33:58 +00:00
|
|
|
return chat
|
2018-01-26 11:07:09 +00:00
|
|
|
|
2023-06-26 09:59:29 +00:00
|
|
|
def send_contact_request_via_bottom_sheet(self, key: str):
|
2023-02-02 11:16:34 +00:00
|
|
|
chat = self.get_chat_view()
|
|
|
|
self.new_chat_button.click()
|
|
|
|
self.add_a_contact_chat_bottom_sheet_button.click()
|
|
|
|
chat.public_key_edit_box.click()
|
|
|
|
chat.public_key_edit_box.send_keys(key)
|
2023-03-15 15:44:13 +00:00
|
|
|
chat.element_by_translation_id("user-found").wait_for_visibility_of_element()
|
|
|
|
if not chat.view_profile_new_contact_button.is_element_displayed():
|
|
|
|
chat.click_system_back_button()
|
2023-03-08 10:31:42 +00:00
|
|
|
chat.view_profile_new_contact_button.click_until_presence_of_element(chat.profile_add_to_contacts_button)
|
|
|
|
chat.profile_add_to_contacts_button.click()
|
2023-08-18 16:20:40 +00:00
|
|
|
self.navigate_back_to_home_view()
|
2023-02-02 11:16:34 +00:00
|
|
|
|
2023-08-18 16:20:40 +00:00
|
|
|
def create_community_e2e(self, name: str, description="some_description", set_image=False,
|
|
|
|
file_name='sauce_logo.png',
|
2023-07-31 15:54:31 +00:00
|
|
|
require_approval=True):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("## Creating community '%s', set image is set to '%s'" % (name, str(set_image)), device=False)
|
2023-05-19 13:08:52 +00:00
|
|
|
self.plus_community_button.click()
|
2021-07-08 08:32:08 +00:00
|
|
|
chat_view = self.communities_button.click()
|
|
|
|
chat_view.community_name_edit_box.set_value(name)
|
|
|
|
chat_view.community_description_edit_box.set_value(description)
|
|
|
|
if set_image:
|
|
|
|
from views.profile_view import ProfileView
|
|
|
|
set_picture_view = ProfileView(self.driver)
|
2021-08-09 11:16:02 +00:00
|
|
|
set_picture_view.element_by_translation_id("community-thumbnail-upload").scroll_and_click()
|
|
|
|
set_picture_view.element_by_translation_id("community-image-pick").scroll_and_click()
|
2021-07-08 08:32:08 +00:00
|
|
|
set_picture_view.select_photo_from_gallery(file_name)
|
|
|
|
set_picture_view.crop_photo_button.click()
|
2022-02-16 12:35:33 +00:00
|
|
|
if require_approval:
|
|
|
|
self.element_by_translation_id("membership-title").scroll_and_click()
|
|
|
|
self.element_by_translation_id("membership-approval").click()
|
|
|
|
self.done_button.click()
|
2021-07-08 08:32:08 +00:00
|
|
|
|
2022-02-16 12:35:33 +00:00
|
|
|
chat_view.confirm_create_in_community_button.wait_and_click()
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("## Community is created successfully!", device=False)
|
2023-04-28 10:15:32 +00:00
|
|
|
return self.get_community_view()
|
2021-07-08 08:32:08 +00:00
|
|
|
|
2023-07-31 15:54:31 +00:00
|
|
|
def create_community(self, community_type: Literal["open", "closed", "token-gated"]):
|
|
|
|
self.driver.info("## Creating %s community" % community_type)
|
|
|
|
self.plus_community_button.click()
|
|
|
|
if community_type == "open":
|
|
|
|
self.create_open_community_button.click()
|
|
|
|
elif community_type == "closed":
|
|
|
|
self.create_closed_community_button.click()
|
|
|
|
elif community_type == "token-gated":
|
|
|
|
self.create_token_gated_community_button.click()
|
|
|
|
else:
|
|
|
|
raise ValueError("Incorrect community type is set")
|
|
|
|
|
2022-12-29 13:49:00 +00:00
|
|
|
def import_community(self, key):
|
|
|
|
self.driver.info("## Importing community")
|
|
|
|
import_button = Button(self.driver, translation_id="import")
|
|
|
|
self.plus_button.click()
|
|
|
|
chat_view = self.communities_button.click()
|
|
|
|
chat_view.chat_options.click()
|
|
|
|
chat_view.element_by_translation_id("import-community").wait_and_click()
|
|
|
|
EditBox(self.driver, xpath="//android.widget.EditText").set_value(key)
|
|
|
|
import_button.click_until_absense_of_element(import_button)
|
|
|
|
|
2018-02-14 13:48:18 +00:00
|
|
|
def join_public_chat(self, chat_name: str):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("## Creating public chat %s" % chat_name, device=False)
|
2019-07-23 23:10:55 +00:00
|
|
|
self.plus_button.click_until_presence_of_element(self.join_public_chat_button, attempts=5)
|
2019-07-01 18:10:11 +00:00
|
|
|
self.join_public_chat_button.wait_for_visibility_of_element(5)
|
2021-01-25 16:35:40 +00:00
|
|
|
chat_view = self.join_public_chat_button.click()
|
2021-12-06 16:55:33 +00:00
|
|
|
chat_view.chat_name_editbox.wait_for_visibility_of_element(20)
|
2021-01-25 16:35:40 +00:00
|
|
|
chat_view.chat_name_editbox.click()
|
|
|
|
chat_view.chat_name_editbox.send_keys(chat_name)
|
2018-05-23 13:02:45 +00:00
|
|
|
time.sleep(2)
|
2019-04-05 13:05:23 +00:00
|
|
|
self.confirm_until_presence_of_element(chat_view.chat_message_input)
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("## Public chat '%s' is created successfully!" % chat_name, device=False)
|
2021-08-23 15:22:36 +00:00
|
|
|
return self.get_chat_view()
|
2018-10-07 15:12:21 +00:00
|
|
|
|
2020-10-14 09:34:53 +00:00
|
|
|
def open_status_test_dapp(self, url=test_dapp_url, allow_all=True):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("Opening dapp '%s', allow all:'%s'" % (test_dapp_url, str(allow_all)))
|
2019-03-19 12:35:15 +00:00
|
|
|
dapp_view = self.dapp_tab_button.click()
|
2020-10-14 09:34:53 +00:00
|
|
|
dapp_view.open_url(url)
|
2019-03-19 12:35:15 +00:00
|
|
|
status_test_dapp = dapp_view.get_status_test_dapp_view()
|
2019-11-21 10:26:53 +00:00
|
|
|
if allow_all:
|
2022-05-31 15:38:36 +00:00
|
|
|
if status_test_dapp.allow_button.is_element_displayed(20):
|
|
|
|
status_test_dapp.allow_button.click_until_absense_of_element(status_test_dapp.allow_button)
|
2019-11-21 10:26:53 +00:00
|
|
|
else:
|
|
|
|
status_test_dapp.deny_button.click_until_absense_of_element(status_test_dapp.deny_button)
|
2018-10-07 15:12:21 +00:00
|
|
|
return status_test_dapp
|
2019-05-20 12:16:15 +00:00
|
|
|
|
|
|
|
def delete_chat_long_press(self, username):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("Deleting chat '%s' by long press" % username)
|
2020-03-17 17:27:10 +00:00
|
|
|
self.get_chat(username).long_press_element()
|
2019-05-20 12:16:15 +00:00
|
|
|
self.delete_chat_button.click()
|
2023-01-09 13:46:24 +00:00
|
|
|
self.delete_chat_button.click()
|
2020-03-17 17:27:10 +00:00
|
|
|
|
2020-04-10 10:25:46 +00:00
|
|
|
def leave_chat_long_press(self, username):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("Leaving chat '%s' by long press" % username)
|
2020-04-10 10:25:46 +00:00
|
|
|
self.get_chat(username).long_press_element()
|
2021-01-25 16:35:40 +00:00
|
|
|
from views.chat_view import ChatView
|
|
|
|
ChatView(self.driver).leave_chat_button.click()
|
|
|
|
ChatView(self.driver).leave_button.click()
|
2020-04-10 10:25:46 +00:00
|
|
|
|
2020-03-17 17:27:10 +00:00
|
|
|
def clear_chat_long_press(self, username):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("Clearing history in chat '%s' by long press" % username)
|
2020-03-17 17:27:10 +00:00
|
|
|
self.get_chat(username).long_press_element()
|
|
|
|
self.clear_history_button.click()
|
2021-01-25 16:35:40 +00:00
|
|
|
from views.chat_view import ChatView
|
2021-10-15 10:26:36 +00:00
|
|
|
ChatView(self.driver).clear_button.click()
|
|
|
|
|
2023-07-18 00:51:04 +00:00
|
|
|
def mute_chat_long_press(self, chat_name, mute_period="mute-till-unmute", community=False, community_channel=False):
|
|
|
|
self.driver.info("Muting chat with %s" % chat_name)
|
2023-08-24 12:30:47 +00:00
|
|
|
self.get_chat(username=chat_name, community=community, community_channel=community_channel).long_press_element()
|
2023-07-18 00:51:04 +00:00
|
|
|
if community:
|
|
|
|
self.mute_community_button.click()
|
|
|
|
elif community_channel:
|
|
|
|
self.mute_channel_button.click()
|
|
|
|
else:
|
|
|
|
self.mute_chat_button.click()
|
|
|
|
self.element_by_translation_id(mute_period).click()
|
|
|
|
|
2021-10-22 17:23:27 +00:00
|
|
|
def get_pn(self, pn_text: str):
|
2021-10-25 16:05:22 +00:00
|
|
|
self.driver.info("Getting PN by '%s'" % pn_text)
|
2022-01-27 20:41:57 +00:00
|
|
|
expected_element = PushNotificationElement(self.driver, pn_text)
|
|
|
|
return expected_element if expected_element.is_element_displayed(60) else False
|
2022-12-29 13:49:00 +00:00
|
|
|
|
2023-06-26 09:59:29 +00:00
|
|
|
def contact_details_row(self, username=None, index=None):
|
|
|
|
return ContactDetailsRow(self.driver, username=username, index=index)
|
|
|
|
|
|
|
|
def get_contact_rows_count(self):
|
|
|
|
return len(ContactDetailsRow(self.driver).find_elements())
|
2023-07-27 11:14:09 +00:00
|
|
|
|
|
|
|
def get_public_key_via_share_profile_tab(self):
|
|
|
|
self.driver.info("Getting public key via Share tab")
|
|
|
|
self.show_qr_code_button.click()
|
2023-08-22 01:53:16 +00:00
|
|
|
self.link_to_profile_text.wait_for_visibility_of_element()
|
2023-07-27 11:14:09 +00:00
|
|
|
self.link_to_profile_text.click()
|
|
|
|
c_text = self.driver.get_clipboard_text()
|
|
|
|
self.click_system_back_button()
|
|
|
|
return c_text.split("/")[-1]
|