Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba74308139 | ||
|
|
a8fd6fdd60 | ||
|
|
fe9dd7c549 | ||
|
|
be79a8e54c | ||
|
|
36d96c6264 | ||
|
|
340213ece5 | ||
|
|
032a8786ff | ||
|
|
15d671275f | ||
|
|
f3533c5f99 | ||
|
|
b18d7b6a2a | ||
|
|
2c23ab4c5d | ||
|
|
646add59b8 | ||
|
|
71651601ad | ||
|
|
cd111aeeec | ||
|
|
81b60ce86c | ||
|
|
356dd89147 | ||
|
|
a6102c1133 | ||
|
|
7dfa93f2a2 | ||
|
|
c6271557b1 | ||
|
|
d1f3a33194 | ||
|
|
c6371bf3b3 |
+35
@@ -1,3 +1,38 @@
|
||||
## 1.18.0 Release notes :package:
|
||||
|
||||
### Features
|
||||
* 🚦Online status indicator <https://github.com/status-im/status-react/issues/12547>
|
||||
* 👛 Add possibility to select wallet for ENS names <https://github.com/status-im/status-react/pull/12761>
|
||||
* 🐞 Bug reporting and logs sharing improvements <https://github.com/status-im/status-react/pull/12773>
|
||||
* 🍳 Simplify Keycard onboarding by using defaults for pairing password <https://github.com/status-im/status-react/issues/12685>
|
||||
* 💾 Backup contacts via Waku <https://github.com/status-im/status-react/issues/12550>
|
||||
* 🪙 Added ENS token <https://github.com/status-im/status-react/issues/12807>
|
||||
* 🌠 Using react-native-fast-image for stickers <https://github.com/status-im/status-react/pull/12769>
|
||||
#### Message reliability improvements
|
||||
* 𝌗 Show loading until all messages are processed <https://github.com/status-im/status-react/pull/12834>
|
||||
* 💪🏼 More robust message confirmations handling <https://github.com/status-im/status-react/pull/12824>
|
||||
* 📶 Allow sending messages when offline <https://github.com/status-im/status-react/issues/11889>
|
||||
#### Improvements on device pairing
|
||||
* 🔔💻 Notification in activity centre now sync across devices <https://github.com/status-im/status-react/issues/12571>
|
||||
* ✅💻 Sync read messages across devices <https://github.com/status-im/status-react/pull/12698>
|
||||
#### Network settings improvements
|
||||
* ✨ Add Binance Smart chain <https://github.com/status-im/status-react/pull/12809>
|
||||
* ♏︎ Added custom network symbol <https://github.com/status-im/status-react/commit/c16b0860ec921a61c119857de27c6f29e301e4f6>
|
||||
* 💰 Network fee on xDai chain <https://github.com/status-im/status-react/pull/12845>
|
||||
* 📇 Support tx history (in a way) when on BSC chains <https://github.com/status-im/status-react/pull/12843>
|
||||
* 🙅🏻 Remove POA network from the list <https://github.com/status-im/status-react/pull/12887>
|
||||
|
||||
### Bug Fixes
|
||||
* Fix activity center community invite preview <https://github.com/status-im/status-react/pull/12803>
|
||||
* Missing notifications for some contacts <https://github.com/status-im/status-react/issues/12776>
|
||||
* Fixes scrolling issues on the 'Max priority fee' bottom sheet <https://github.com/status-im/status-react/issues/12530>
|
||||
* Handle Keycard timeout on iOS 15 <https://github.com/status-im/status-react/pull/12826>
|
||||
* Allow user to be reinvited to the same group after leaving <https://github.com/status-im/status-react/pull/12814>
|
||||
* Fix for "TypeError: Network request failed" when switching to Goerli network <https://github.com/status-im/status-react/issues/11964>
|
||||
* Fix for Spin of death on adding custom NFT token contract <https://github.com/status-im/status-react/issues/12365>
|
||||
* Changing screen orientation results in breaking images / layout <https://github.com/status-im/status-react/issues/12858>
|
||||
|
||||
|
||||
## 1.17
|
||||
|
||||
### For iOS and Android
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
library 'status-jenkins-lib@v1.3.3'
|
||||
|
||||
pipeline {
|
||||
agent { label 'linux && x86_64 && nix-2.3' }
|
||||
agent { label 'linux' }
|
||||
|
||||
options {
|
||||
timestamps()
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
library 'status-jenkins-lib@v1.3.3'
|
||||
|
||||
pipeline {
|
||||
agent { label 'macos && x86_64 && nix-2.3 && xcode-12.5' }
|
||||
agent { label 'macos && x86_64 && xcode-12.5' }
|
||||
|
||||
parameters {
|
||||
string(
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
(def key->string str)
|
||||
|
||||
(defn- set-item! [key value]
|
||||
(defn set-item! [key value]
|
||||
(-> ^js async-storage
|
||||
(.setItem (key->string key)
|
||||
(clj->transit value))
|
||||
|
||||
@@ -169,7 +169,6 @@
|
||||
(let [{:keys [custom-domain? username address]}
|
||||
(:ens/registration db)
|
||||
{:keys [public-key]} (:multiaccount db)
|
||||
address (or address (ethereum/default-address db))
|
||||
chain (ethereum/chain-keyword db)
|
||||
chain-id (ethereum/chain-id db)
|
||||
amount (registration-cost chain-id)
|
||||
|
||||
+68
-53
@@ -1,65 +1,64 @@
|
||||
(ns status-im.events
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.chat.models :as chat]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.fx :as fx]
|
||||
status-im.utils.logging.core
|
||||
status-im.backup.core
|
||||
[status-im.wallet.core :as wallet]
|
||||
[status-im.keycard.core :as keycard]
|
||||
[status-im.utils.dimensions :as dimensions]
|
||||
[status-im.multiaccounts.biometric.core :as biometric]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.ui.components.permissions :as permissions]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.anon-metrics.core :as anon-metrics]
|
||||
[status-im.utils.universal-links.core :as universal-links]
|
||||
clojure.set
|
||||
status-im.currency.core
|
||||
status-im.navigation
|
||||
status-im.utils.universal-links.core
|
||||
status-im.wallet.custom-tokens.core
|
||||
status-im.waku.core
|
||||
status-im.wallet.choose-recipient.core
|
||||
status-im.wallet.accounts.core
|
||||
status-im.popover.core
|
||||
status-im.visibility-status-popover.core
|
||||
status-im.visibility-status-updates.core
|
||||
status-im.bottom-sheet.core
|
||||
(:require clojure.set
|
||||
[re-frame.core :as re-frame]
|
||||
status-im.add-new.core
|
||||
status-im.search.core
|
||||
status-im.http.core
|
||||
status-im.profile.core
|
||||
[status-im.anon-metrics.core :as anon-metrics]
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
status-im.backup.core
|
||||
status-im.bootnodes.core
|
||||
status-im.bottom-sheet.core
|
||||
status-im.browser.core
|
||||
status-im.browser.permissions
|
||||
[status-im.chat.models :as chat]
|
||||
status-im.chat.models.images
|
||||
status-im.ui.screens.privacy-and-security-settings.events
|
||||
status-im.chat.models.input
|
||||
status-im.chat.models.loading
|
||||
status-im.chat.models.transport
|
||||
[status-im.constants :as constants]
|
||||
status-im.contact.block
|
||||
status-im.contact.chat
|
||||
status-im.contact.core
|
||||
status-im.currency.core
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
status-im.ethereum.subscriptions
|
||||
status-im.fleet.core
|
||||
status-im.http.core
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
status-im.init.core
|
||||
[status-im.keycard.core :as keycard]
|
||||
status-im.log-level.core
|
||||
status-im.mailserver.constants
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.biometric.core :as biometric]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
status-im.multiaccounts.login.core
|
||||
status-im.multiaccounts.logout.core
|
||||
status-im.multiaccounts.update.core
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.navigation :as navigation]
|
||||
status-im.notifications-center.core
|
||||
status-im.pairing.core
|
||||
status-im.popover.core
|
||||
status-im.profile.core
|
||||
status-im.search.core
|
||||
status-im.signals.core
|
||||
status-im.stickers.core
|
||||
status-im.transport.core
|
||||
status-im.init.core
|
||||
status-im.log-level.core
|
||||
status-im.mailserver.constants
|
||||
status-im.ethereum.subscriptions
|
||||
status-im.fleet.core
|
||||
status-im.contact.block
|
||||
status-im.contact.core
|
||||
status-im.contact.chat
|
||||
status-im.chat.models.input
|
||||
status-im.chat.models.loading
|
||||
status-im.bootnodes.core
|
||||
status-im.browser.core
|
||||
status-im.browser.permissions
|
||||
status-im.chat.models.transport
|
||||
status-im.notifications-center.core
|
||||
[status-im.navigation :as navigation]))
|
||||
[status-im.ui.components.permissions :as permissions]
|
||||
[status-im.ui.components.react :as react]
|
||||
status-im.ui.screens.privacy-and-security-settings.events
|
||||
[status-im.utils.dimensions :as dimensions]
|
||||
[status-im.utils.fx :as fx]
|
||||
status-im.utils.logging.core
|
||||
[status-im.utils.universal-links.core :as universal-links]
|
||||
[status-im.utils.utils :as utils]
|
||||
status-im.visibility-status-popover.core
|
||||
status-im.visibility-status-updates.core
|
||||
status-im.waku.core
|
||||
status-im.wallet.accounts.core
|
||||
status-im.wallet.choose-recipient.core
|
||||
[status-im.wallet.core :as wallet]
|
||||
status-im.wallet.custom-tokens.core))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:dismiss-keyboard
|
||||
@@ -193,8 +192,24 @@
|
||||
(fx/defn on-will-focus
|
||||
{:events [:screens/on-will-focus]
|
||||
:interceptors [anon-metrics/interceptor]}
|
||||
[cofx view-id]
|
||||
[{:keys [db] :as cofx} view-id]
|
||||
(fx/merge cofx
|
||||
(cond
|
||||
(= :chat view-id)
|
||||
{::async-storage/set! {:chat-id (get-in cofx [:db :current-chat-id])
|
||||
:key-uid (get-in cofx [:db :multiaccount :key-uid])}
|
||||
:db (assoc db :screens/was-focused-once? true)}
|
||||
|
||||
(= :login view-id)
|
||||
{}
|
||||
|
||||
(not (get db :screens/was-focused-once?))
|
||||
{:db (assoc db :screens/was-focused-once? true)}
|
||||
|
||||
:else
|
||||
{::async-storage/set! {:chat-id nil
|
||||
:key-uid nil}
|
||||
:db (assoc db :screens/was-focused-once? true)})
|
||||
#(case view-id
|
||||
:keycard-settings (keycard/settings-screen-did-load %)
|
||||
:reset-card (keycard/reset-card-screen-did-load %)
|
||||
|
||||
@@ -325,6 +325,19 @@
|
||||
(logging/set-log-level (:log-level multiaccount))
|
||||
(notifications-center/get-activity-center-notifications-count))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::open-last-chat
|
||||
(fn [key-uid]
|
||||
(async-storage/get-item
|
||||
:chat-id
|
||||
(fn [chat-id]
|
||||
(when chat-id
|
||||
(async-storage/get-item
|
||||
:key-uid
|
||||
(fn [stored-key-uid]
|
||||
(when (= stored-key-uid key-uid)
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])))))))))
|
||||
|
||||
(fx/defn get-chats-callback
|
||||
{:events [::get-chats-callback]}
|
||||
[{:keys [db] :as cofx}]
|
||||
@@ -339,7 +352,8 @@
|
||||
::initialize-wallet
|
||||
(fn [accounts custom-tokens favourites]
|
||||
(re-frame/dispatch [::initialize-wallet
|
||||
accounts custom-tokens favourites]))}
|
||||
accounts custom-tokens favourites]))
|
||||
::open-last-chat (get-in db [:multiaccount :key-uid])}
|
||||
notifications-enabled?
|
||||
(assoc ::notifications/enable nil))
|
||||
(transport/start-messenger)
|
||||
@@ -381,7 +395,7 @@
|
||||
config/metrics-enabled?)
|
||||
(navigation/navigate-to :anon-metrics-opt-in {})
|
||||
|
||||
:else (re-frame/dispatch [:init-root :chat-stack]))))
|
||||
:else (re-frame/dispatch [:init-root :chat-stack]))))
|
||||
|
||||
(fx/defn login-only-events
|
||||
[{:keys [db] :as cofx} key-uid password save-password?]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns status-im.navigation
|
||||
(:require [status-im.utils.fx :as fx]
|
||||
[status-im.anon-metrics.core :as anon-metrics]))
|
||||
(:require [status-im.anon-metrics.core :as anon-metrics]
|
||||
[status-im.utils.fx :as fx]))
|
||||
|
||||
(defn- all-screens-params [db view screen-params]
|
||||
(cond-> db
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
(ns status-im.navigation.core
|
||||
(:require
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ui.screens.views :as views]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.navigation.roots :as roots]
|
||||
[status-im.ui.components.react :as react]
|
||||
[quo.components.text-input :as quo.text-input]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[status-im.utils.fx :as fx]
|
||||
["react-native-navigation" :refer (Navigation)]
|
||||
["react-native" :as rn]
|
||||
["react-native-gesture-handler" :refer (gestureHandlerRootHOC)]
|
||||
["react-native" :as rn]))
|
||||
["react-native-navigation" :refer (Navigation)]
|
||||
[quo.components.text-input :as quo.text-input]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.navigation.roots :as roots]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.views :as views]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.platform :as platform]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(def debug? ^boolean js/goog.DEBUG)
|
||||
|
||||
@@ -26,6 +27,7 @@
|
||||
|
||||
;; REGISTER COMPONENT (LAZY)
|
||||
(defn reg-comp [key]
|
||||
(log/debug "reg-comp" key)
|
||||
(if-let [comp (get views/components (keyword key))]
|
||||
(.registerComponent Navigation key (fn [] (views/component comp)))
|
||||
(let [screen (views/screen key)]
|
||||
@@ -35,6 +37,7 @@
|
||||
(.setLazyComponentRegistrator Navigation reg-comp))
|
||||
|
||||
(defn dismiss-all-modals []
|
||||
(log/debug "dissmiss-all-modals")
|
||||
(when @curr-modal
|
||||
(reset! curr-modal false)
|
||||
(reset! dissmissing true)
|
||||
@@ -44,6 +47,7 @@
|
||||
|
||||
;; PUSH SCREEN
|
||||
(defn navigate [comp]
|
||||
(log/debug "NAVIGATE" comp)
|
||||
(let [{:keys [options]} (get views/screens comp)]
|
||||
(.push Navigation
|
||||
(name @root-comp-id)
|
||||
@@ -57,6 +61,7 @@
|
||||
|
||||
;; OPEN MODAL
|
||||
(defn update-modal-topbar-options [options]
|
||||
(log/debug "update-modal-topbar-options" options)
|
||||
(merge options
|
||||
(roots/merge-top-bar {:elevation 0
|
||||
:noBorder true
|
||||
@@ -68,6 +73,7 @@
|
||||
options)))
|
||||
|
||||
(defn open-modal [comp]
|
||||
(log/debug "open-modal" comp)
|
||||
(let [{:keys [options]} (get views/screens comp)]
|
||||
(if @dissmissing
|
||||
(reset! dissmissing comp)
|
||||
@@ -88,6 +94,7 @@
|
||||
|
||||
;; DISSMISS MODAL
|
||||
(defn dissmissModal []
|
||||
(log/debug "dissmissModal")
|
||||
(reset! dissmissing true)
|
||||
(.dismissModal Navigation (name (last @modals))))
|
||||
|
||||
@@ -105,6 +112,7 @@
|
||||
(handler)))))))
|
||||
|
||||
(defn set-view-id [view-id]
|
||||
(log/debug "set-view-id" view-id)
|
||||
(when-let [{:keys [on-focus]} (get views/screens view-id)]
|
||||
(re-frame/dispatch [:set :view-id view-id])
|
||||
(re-frame/dispatch [:screens/on-will-focus view-id])
|
||||
@@ -135,6 +143,7 @@
|
||||
(.events Navigation)
|
||||
(fn [^js evn]
|
||||
(let [view-id (keyword (.-componentName evn))]
|
||||
(log/debug "screen-appear-reg" view-id)
|
||||
(when (get views/screens view-id)
|
||||
(when (and (not= view-id :bottom-sheet)
|
||||
(not= view-id :popover)
|
||||
@@ -159,6 +168,7 @@
|
||||
(re-frame/reg-fx
|
||||
:init-root-fx
|
||||
(fn [new-root-id]
|
||||
(log/debug :init-root-fx new-root-id)
|
||||
(reset! root-comp-id new-root-id)
|
||||
(reset! root-id @root-comp-id)
|
||||
(.setRoot Navigation (clj->js (get (roots/roots) new-root-id)))))
|
||||
@@ -166,6 +176,7 @@
|
||||
(re-frame/reg-fx
|
||||
:init-root-with-component-fx
|
||||
(fn [[new-root-id new-root-comp-id]]
|
||||
(log/debug :init-root-with-component-fx new-root-id new-root-comp-id)
|
||||
(reset! root-comp-id new-root-comp-id)
|
||||
(reset! root-id @root-comp-id)
|
||||
(.setRoot Navigation (clj->js (get (roots/roots) new-root-id)))))
|
||||
@@ -173,6 +184,7 @@
|
||||
(fx/defn set-multiaccount-root
|
||||
{:events [::set-multiaccount-root]}
|
||||
[{:keys [db]}]
|
||||
(log/debug :set-multiaccounts-root)
|
||||
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
|
||||
keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts
|
||||
key-uid
|
||||
@@ -192,6 +204,7 @@
|
||||
(.hide ^js splash-screen))))
|
||||
|
||||
(defn get-screen-component [comp]
|
||||
(log/debug :get-screen-component comp)
|
||||
(let [{:keys [options]} (get views/screens comp)]
|
||||
{:component {:id comp
|
||||
:name comp
|
||||
@@ -203,6 +216,7 @@
|
||||
(re-frame/reg-fx
|
||||
:set-stack-root-fx
|
||||
(fn [[stack comp]]
|
||||
(log/debug :set-stack-root-fx stack comp)
|
||||
(.setStackRoot Navigation
|
||||
(name stack)
|
||||
(clj->js (if (vector? comp)
|
||||
@@ -225,6 +239,7 @@
|
||||
(re-frame/reg-fx
|
||||
:change-tab-fx
|
||||
(fn [tab]
|
||||
(log/debug :change-tab-fx)
|
||||
(reset! root-comp-id (get tab-root-ids (get tab-key-idx tab)))
|
||||
(.mergeOptions Navigation "tabs-stack" (clj->js {:bottomTabs {:currentTabIndex (get tab-key-idx tab)}}))
|
||||
;;when we change tab we want to dismiss all modals
|
||||
@@ -234,6 +249,7 @@
|
||||
(re-frame/reg-fx
|
||||
:change-tab-count-fx
|
||||
(fn [[tab cnt]]
|
||||
(log/debug :change-tab-count-fx tab cnt)
|
||||
(.mergeOptions Navigation
|
||||
(name (get tab-root-ids (get tab-key-idx tab)))
|
||||
(clj->js {:bottomTab (cond
|
||||
@@ -253,6 +269,7 @@
|
||||
(re-frame/reg-fx
|
||||
:pop-to-root-tab-fx
|
||||
(fn [comp]
|
||||
(log/debug :pop-to-root-tab-fx comp)
|
||||
(dismiss-all-modals)
|
||||
(.popToRoot Navigation (name comp))))
|
||||
|
||||
@@ -343,11 +360,13 @@
|
||||
(re-frame/reg-fx
|
||||
:navigate-to-fx
|
||||
(fn [key]
|
||||
(log/debug :navigate-to-fx key)
|
||||
(navigate key)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:navigate-back-fx
|
||||
(fn []
|
||||
(log/debug :navigate-back-fx)
|
||||
(if @curr-modal
|
||||
(dissmissModal)
|
||||
(.pop Navigation (name @root-comp-id)))))
|
||||
@@ -355,5 +374,6 @@
|
||||
(re-frame/reg-fx
|
||||
:navigate-replace-fx
|
||||
(fn [view-id]
|
||||
(log/debug :navigate-replace-fx view-id)
|
||||
(.pop Navigation (name @root-comp-id))
|
||||
(navigate view-id)))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns status-im.notifications.local
|
||||
(:require [status-im.utils.fx :as fx]
|
||||
(:require [status-im.async-storage.core :as async-storage]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.ethereum.decode :as decode]
|
||||
["@react-native-community/push-notification-ios" :default pn-ios]
|
||||
[status-im.notifications.android :as pn-android]
|
||||
@@ -40,6 +41,7 @@
|
||||
|
||||
(defn handle-notification-press [{{deep-link :deepLink} :userInfo
|
||||
interaction :userInteraction}]
|
||||
(async-storage/set-item! (str :chat-id) nil)
|
||||
(when (and deep-link
|
||||
(or platform/ios?
|
||||
(and platform/android? interaction)))
|
||||
|
||||
@@ -103,4 +103,5 @@
|
||||
(conj chats lang-topic)
|
||||
chats)]
|
||||
(for [chat chats]
|
||||
(render-topic chat)))]])]]))
|
||||
^{:key chat}
|
||||
[render-topic chat]))]])]]))
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
:message-id @current-player-message-id}))
|
||||
nil)
|
||||
|
||||
(defview message-content [{:keys [audio audio-duration-ms message-id outgoing]} timestamp-view]
|
||||
(defview message-content [{:keys [audio audio-duration-ms message-id outgoing]}]
|
||||
(letsubs [state (reagent/atom nil)
|
||||
progress (reagent/atom 0)
|
||||
progress-anim (anim/create-value 0)
|
||||
@@ -223,5 +223,4 @@
|
||||
(#{:playing :paused :seeking} (:general @state)) @progress
|
||||
:else (:duration @state))
|
||||
s (quot time 1000)]
|
||||
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]
|
||||
timestamp-view]]))))
|
||||
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]]]))))
|
||||
|
||||
@@ -24,42 +24,64 @@
|
||||
[status-im.ui.screens.chat.components.reply :as components.reply]
|
||||
[status-im.ui.screens.chat.message.link-preview :as link-preview]
|
||||
[status-im.ui.screens.communities.icon :as communities.icon]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.chat.models.pin-message :as models.pin-message])
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn message-timestamp-anim
|
||||
[anim-opacity show-timestamp?]
|
||||
(animation/start
|
||||
(animation/anim-sequence
|
||||
[(animation/timing
|
||||
anim-opacity
|
||||
{:toValue 1
|
||||
:duration 100
|
||||
:easing (.-ease ^js animation/easing)
|
||||
:useNativeDriver true})
|
||||
(animation/timing
|
||||
anim-opacity
|
||||
{:toValue 0
|
||||
:delay 2000
|
||||
:duration 100
|
||||
:easing (.-ease ^js animation/easing)
|
||||
:useNativeDriver true})]) #(reset! show-timestamp? false)))
|
||||
|
||||
(defview mention-element [from]
|
||||
(letsubs [contact-name [:contacts/contact-name-by-identity from]]
|
||||
contact-name))
|
||||
|
||||
(def edited-at-text (str " ⌫ " (i18n/label :t/edited)))
|
||||
|
||||
(defn message-status [{:keys [outgoing content outgoing-status pinned edited-at in-popover?]}]
|
||||
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
|
||||
[react/view
|
||||
{:align-self :flex-end
|
||||
:position :absolute
|
||||
:bottom 9 ; 6 Bubble bottom, 3 message baseline
|
||||
(if (:rtl? content) :left :right) 0
|
||||
:flex-direction :row
|
||||
:align-items :flex-end}
|
||||
(when outgoing
|
||||
[icons/icon (case outgoing-status
|
||||
:sending :tiny-icons/tiny-pending
|
||||
:sent :tiny-icons/tiny-sent
|
||||
:not-sent :tiny-icons/tiny-warning
|
||||
:delivered :tiny-icons/tiny-delivered
|
||||
:tiny-icons/tiny-pending)
|
||||
{:width 16
|
||||
:height 12
|
||||
:color (if pinned colors/gray colors/white)
|
||||
:accessibility-label (name outgoing-status)}])
|
||||
(when edited-at [react/text {:style (style/message-status-text (and outgoing (not pinned)))} edited-at-text])]))
|
||||
|
||||
(defn message-timestamp
|
||||
([message]
|
||||
[message-timestamp message false])
|
||||
([{:keys [timestamp-str outgoing content outgoing-status pinned edited-at in-popover?]} justify-timestamp?]
|
||||
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
|
||||
[react/view (when justify-timestamp?
|
||||
{:align-self :flex-end
|
||||
:position :absolute
|
||||
:bottom 9 ; 6 Bubble bottom, 3 message baseline
|
||||
(if (:rtl? content) :left :right) 0
|
||||
:flex-direction :row
|
||||
:align-items :flex-end})
|
||||
(when (and outgoing justify-timestamp?)
|
||||
[icons/icon (case outgoing-status
|
||||
:sending :tiny-icons/tiny-pending
|
||||
:sent :tiny-icons/tiny-sent
|
||||
:not-sent :tiny-icons/tiny-warning
|
||||
:delivered :tiny-icons/tiny-delivered
|
||||
:tiny-icons/tiny-pending)
|
||||
{:width 16
|
||||
:height 12
|
||||
:color (if pinned colors/gray colors/white)
|
||||
:accessibility-label (name outgoing-status)}])
|
||||
[react/text {:style (style/message-timestamp-text (and outgoing (not pinned)))}
|
||||
(str
|
||||
timestamp-str
|
||||
(when edited-at edited-at-text))]])))
|
||||
[{:keys [timestamp-str in-popover?] :as message} show-timestamp?]
|
||||
(when-not in-popover? ;; We keep track if showing this message in a list in pin-limit-popover
|
||||
(let [anim-opacity (animation/create-value 0)]
|
||||
[react/animated-view {:style (style/message-timestamp-wrapper message) :opacity anim-opacity}
|
||||
(when @show-timestamp? (message-timestamp-anim anim-opacity show-timestamp?))
|
||||
[react/text {:style (style/message-timestamp-text)}
|
||||
timestamp-str]])))
|
||||
|
||||
(defview quoted-message
|
||||
[_ {:keys [from parsed-text image]} outgoing current-public-key public? pinned]
|
||||
@@ -168,19 +190,19 @@
|
||||
(defn render-parsed-text [message tree]
|
||||
(reduce (fn [acc e] (render-block message acc e)) [:<>] tree))
|
||||
|
||||
(defn render-parsed-text-with-timestamp [{:keys [timestamp-str outgoing edited-at in-popover?] :as message} tree]
|
||||
(defn render-parsed-text-with-message-status [{:keys [outgoing edited-at in-popover?] :as message} tree]
|
||||
(let [elements (render-parsed-text message tree)
|
||||
timestamp [react/text {:style (style/message-timestamp-placeholder)}
|
||||
(str (if (and outgoing (not in-popover?)) " " " ") (when-not in-popover? (str timestamp-str (when edited-at edited-at-text))))]
|
||||
message-status [react/text {:style (style/message-status-placeholder)}
|
||||
(str (if (and outgoing (not in-popover?)) " " " ") (when (and (not in-popover?) edited-at) edited-at-text))]
|
||||
last-element (peek elements)]
|
||||
;; Using `nth` here as slightly faster than `first`, roughly 30%
|
||||
;; It's worth considering pure js structures for this code path as
|
||||
;; it's perfomance critical
|
||||
(if (= react/text-class (nth last-element 0))
|
||||
;; Append timestamp to last text
|
||||
(conj (pop elements) (conj last-element timestamp))
|
||||
;; Append timestamp to new block
|
||||
(conj elements timestamp))))
|
||||
;; Append message status to last text
|
||||
(conj (pop elements) (conj last-element message-status))
|
||||
;; Append message status to new block
|
||||
(conj elements message-status))))
|
||||
|
||||
(defn unknown-content-type
|
||||
[{:keys [outgoing content-type content] :as message}]
|
||||
@@ -409,7 +431,8 @@
|
||||
|
||||
(defn collapsible-text-message [{:keys [mentioned]} _]
|
||||
(let [collapsed? (reagent/atom false)
|
||||
collapsible? (reagent/atom false)]
|
||||
collapsible? (reagent/atom false)
|
||||
show-timestamp? (reagent/atom false)]
|
||||
(fn [{:keys [content outgoing current-public-key public? pinned in-popover?] :as message} on-long-press modal]
|
||||
(let [max-height (when-not (or outgoing modal)
|
||||
(if @collapsible?
|
||||
@@ -418,7 +441,8 @@
|
||||
[react/touchable-highlight
|
||||
(when-not modal
|
||||
{:on-press (fn [_]
|
||||
(react/dismiss-keyboard!))
|
||||
(react/dismiss-keyboard!)
|
||||
(reset! show-timestamp? true))
|
||||
:delay-long-press 100
|
||||
:on-long-press (fn []
|
||||
(if @collapsed?
|
||||
@@ -426,40 +450,41 @@
|
||||
(js/setTimeout #(on-long-press-fn on-long-press message content) 200))
|
||||
(on-long-press-fn on-long-press message content)))
|
||||
:disabled in-popover?})
|
||||
[react/view {:style (style/message-view message)}
|
||||
[react/view {:style (style/message-view-content)
|
||||
:max-height max-height}
|
||||
(let [response-to (:response-to content)]
|
||||
[react/view {:on-layout
|
||||
#(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px)
|
||||
(not @collapsible?)
|
||||
(not outgoing)
|
||||
(not modal))
|
||||
(reset! collapsed? true)
|
||||
(reset! collapsible? true))}
|
||||
(when (and (seq response-to) (:quoted-message message))
|
||||
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
|
||||
[render-parsed-text-with-timestamp message (:parsed-text content)]])
|
||||
(when-not @collapsed?
|
||||
[message-timestamp message true])
|
||||
(when (and @collapsible? (not modal))
|
||||
(if @collapsed?
|
||||
(let [color (if pinned colors/pin-background (if mentioned colors/mentioned-background colors/blue-light))]
|
||||
[react/touchable-highlight
|
||||
{:on-press #(swap! collapsed? not)
|
||||
:style {:position :absolute :bottom 0 :left 0 :right 0 :height 72}}
|
||||
[react/linear-gradient {:colors [(str color "00") color]
|
||||
:start {:x 0 :y 0} :end {:x 0 :y 0.9}}
|
||||
[react/view {:height 72 :align-self :center :justify-content :flex-end
|
||||
:padding-bottom 10}
|
||||
[react/view (style/collapse-button)
|
||||
[icons/icon :main-icons/dropdown
|
||||
{:color colors/white}]]]]])
|
||||
[react/touchable-highlight {:on-press #(swap! collapsed? not)
|
||||
:style {:align-self :center :margin 5}}
|
||||
[react/view (style/collapse-button)
|
||||
[icons/icon :main-icons/dropdown-up
|
||||
{:color colors/white}]]]))]]]))))
|
||||
[react/view (style/message-view-wrapper outgoing)
|
||||
[message-timestamp message show-timestamp?]
|
||||
[react/view {:style (style/message-view message)}
|
||||
[react/view {:style (style/message-view-content)
|
||||
:max-height max-height}
|
||||
(let [response-to (:response-to content)]
|
||||
[react/view {:on-layout
|
||||
#(when (and (> (.-nativeEvent.layout.height ^js %) max-message-height-px)
|
||||
(not @collapsible?)
|
||||
(not outgoing)
|
||||
(not modal))
|
||||
(reset! collapsed? true)
|
||||
(reset! collapsible? true))}
|
||||
(when (and (seq response-to) (:quoted-message message))
|
||||
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
|
||||
[render-parsed-text-with-message-status message (:parsed-text content)]])
|
||||
(when-not @collapsible? [message-status message])
|
||||
(when (and @collapsible? (not modal))
|
||||
(if @collapsed?
|
||||
(let [color (if pinned colors/pin-background (if mentioned colors/mentioned-background colors/blue-light))]
|
||||
[react/touchable-highlight
|
||||
{:on-press #(swap! collapsed? not)
|
||||
:style {:position :absolute :bottom 0 :left 0 :right 0 :height 72}}
|
||||
[react/linear-gradient {:colors [(str color "00") color]
|
||||
:start {:x 0 :y 0} :end {:x 0 :y 0.9}}
|
||||
[react/view {:height 72 :align-self :center :justify-content :flex-end
|
||||
:padding-bottom 10}
|
||||
[react/view (style/collapse-button)
|
||||
[icons/icon :main-icons/dropdown
|
||||
{:color colors/white}]]]]])
|
||||
[react/touchable-highlight {:on-press #(swap! collapsed? not)
|
||||
:style {:align-self :center :margin 5}}
|
||||
[react/view (style/collapse-button)
|
||||
[icons/icon :main-icons/dropdown-up
|
||||
{:color colors/white}]]]))]]]]))))
|
||||
|
||||
(defmethod ->message constants/content-type-text
|
||||
[message {:keys [on-long-press modal] :as reaction-picker}]
|
||||
@@ -484,33 +509,37 @@
|
||||
(defmethod ->message constants/content-type-emoji
|
||||
[{:keys [content current-public-key outgoing public? pinned in-popover? message-pin-enabled] :as message} {:keys [on-long-press modal]
|
||||
:as reaction-picker}]
|
||||
(let [response-to (:response-to content)]
|
||||
[message-content-wrapper message
|
||||
[react/touchable-highlight (when-not modal
|
||||
{:disabled in-popover?
|
||||
:on-press (fn []
|
||||
(react/dismiss-keyboard!))
|
||||
:delay-long-press 100
|
||||
:on-long-press (fn []
|
||||
(on-long-press
|
||||
(concat
|
||||
[{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
|
||||
:id :reply
|
||||
:label (i18n/label :t/message-reply)}
|
||||
{:on-press #(react/copy-to-clipboard (get content :text))
|
||||
:id :copy
|
||||
:label (i18n/label :t/sharing-copy-to-clipboard)}]
|
||||
(when message-pin-enabled [{:on-press #(pin-message message)
|
||||
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))})
|
||||
[react/view (style/message-view message)
|
||||
[react/view {:style (style/message-view-content)}
|
||||
[react/view {:style (style/style-message-text outgoing)}
|
||||
(when (and (seq response-to) (:quoted-message message))
|
||||
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
|
||||
[react/text {:style (style/emoji-message message)}
|
||||
(:text content)]]
|
||||
[message-timestamp message]]]]
|
||||
reaction-picker]))
|
||||
(let [response-to (:response-to content)
|
||||
show-timestamp? (reagent/atom false)]
|
||||
(fn [] [message-content-wrapper message
|
||||
[react/touchable-highlight (when-not modal
|
||||
{:disabled in-popover?
|
||||
:on-press (fn []
|
||||
(react/dismiss-keyboard!)
|
||||
(reset! show-timestamp? true))
|
||||
:delay-long-press 100
|
||||
:on-long-press (fn []
|
||||
(on-long-press
|
||||
(concat
|
||||
[{:on-press #(re-frame/dispatch [:chat.ui/reply-to-message message])
|
||||
:id :reply
|
||||
:label (i18n/label :t/message-reply)}
|
||||
{:on-press #(react/copy-to-clipboard (get content :text))
|
||||
:id :copy
|
||||
:label (i18n/label :t/sharing-copy-to-clipboard)}]
|
||||
(when message-pin-enabled [{:on-press #(pin-message message)
|
||||
:label (if pinned (i18n/label :t/unpin) (i18n/label :t/pin))}]))))})
|
||||
[react/view (style/message-view-wrapper outgoing)
|
||||
[message-timestamp message show-timestamp?]
|
||||
[react/view (style/message-view message)
|
||||
[react/view {:style (style/message-view-content)}
|
||||
[react/view {:style (style/style-message-text outgoing)}
|
||||
(when (and (seq response-to) (:quoted-message message))
|
||||
[quoted-message response-to (:quoted-message message) outgoing current-public-key public? pinned])
|
||||
[react/text {:style (style/emoji-message message)}
|
||||
(:text content)]]
|
||||
[message-status message]]]]]
|
||||
reaction-picker])))
|
||||
|
||||
(defmethod ->message constants/content-type-sticker
|
||||
[{:keys [content from outgoing in-popover?]
|
||||
@@ -556,14 +585,19 @@
|
||||
|
||||
(defmethod ->message constants/content-type-audio [message {:keys [on-long-press modal]
|
||||
:as reaction-picker}]
|
||||
[message-content-wrapper message
|
||||
[react/touchable-highlight (when-not modal
|
||||
{:on-long-press
|
||||
(fn [] (on-long-press []))})
|
||||
[react/view {:style (style/message-view message) :accessibility-label :audio-message}
|
||||
[react/view {:style (style/message-view-content)}
|
||||
[message.audio/message-content message [message-timestamp message false]]]]]
|
||||
reaction-picker])
|
||||
(let [show-timestamp? (reagent/atom false)]
|
||||
(fn [] [message-content-wrapper message
|
||||
[react/touchable-highlight (when-not modal
|
||||
{:on-long-press
|
||||
(fn [] (on-long-press []))
|
||||
:on-press (fn []
|
||||
(reset! show-timestamp? true))})
|
||||
[react/view (style/message-view-wrapper (:outgoing message))
|
||||
[message-timestamp message show-timestamp?]
|
||||
[react/view {:style (style/message-view message) :accessibility-label :audio-message}
|
||||
[react/view {:style (style/message-view-content)}
|
||||
[message.audio/message-content message] [message-status message]]]]]
|
||||
reaction-picker])))
|
||||
|
||||
(defmethod ->message :default [message]
|
||||
[message-content-wrapper message
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
:justify-content :space-between})
|
||||
|
||||
(defn timestamp [outgoing]
|
||||
(merge (message.style/message-timestamp-text outgoing)
|
||||
(merge (message.style/audio-message-timestamp-text outgoing)
|
||||
{:margin-left 40}))
|
||||
@@ -29,11 +29,33 @@
|
||||
(def message-timestamp
|
||||
{:font-size 10})
|
||||
|
||||
(defn message-timestamp-placeholder
|
||||
(defn message-status-placeholder
|
||||
[]
|
||||
(merge message-timestamp {:opacity 0 :color "rgba(0,0,0,0)"}))
|
||||
|
||||
(defn message-timestamp-text
|
||||
(defn message-timestamp-wrapper [{:keys [last-in-group? outgoing group-chat]}]
|
||||
{:justify-content :center
|
||||
(if outgoing :margin-right :margin-left) 12 ;; horizontal margin is only required at the adjust side of the message.
|
||||
:margin-top (if (and last-in-group?
|
||||
(or outgoing
|
||||
(not group-chat)))
|
||||
16
|
||||
0) ;; Add gap between message groups
|
||||
:opacity 0})
|
||||
|
||||
(defn message-timestamp-text []
|
||||
(merge message-timestamp
|
||||
{:color colors/gray
|
||||
:text-align :center}))
|
||||
|
||||
(defn message-status-text [outgoing]
|
||||
{:font-size 10
|
||||
:line-height 10
|
||||
:color (if outgoing
|
||||
colors/white-transparent-70-persist
|
||||
colors/gray)})
|
||||
|
||||
(defn audio-message-timestamp-text
|
||||
[outgoing]
|
||||
(merge message-timestamp
|
||||
{:line-height 10
|
||||
@@ -44,7 +66,7 @@
|
||||
(defn message-wrapper [{:keys [outgoing in-popover?]}]
|
||||
(if (and outgoing (not in-popover?))
|
||||
{:margin-left 96}
|
||||
{:margin-right 52}))
|
||||
{:margin-right 96}))
|
||||
|
||||
(defn message-author-wrapper
|
||||
[outgoing display-photo? in-popover?]
|
||||
@@ -156,10 +178,10 @@
|
||||
:padding-left 3})
|
||||
|
||||
(defn emoji-message
|
||||
[{:keys [incoming-group]}]
|
||||
[{:keys [incoming-group outgoing]}]
|
||||
{:font-size 28
|
||||
:line-height 34 ;TODO: Smaller crops the icon on the top
|
||||
:margin-right 12
|
||||
:margin-right (if outgoing 18 0) ;; Margin to display outgoing message status
|
||||
:margin-top (if incoming-group 4 0)})
|
||||
|
||||
(defn collapse-button []
|
||||
@@ -171,6 +193,10 @@
|
||||
:shadow-color (:shadow-01 @colors/theme)
|
||||
:shadow-offset {:width 0 :height 4}})
|
||||
|
||||
(defn message-view-wrapper [outgoing]
|
||||
{:align-self :flex-end
|
||||
:flex-direction (if outgoing :row :row-reverse)})
|
||||
|
||||
(defn message-view
|
||||
[{:keys [content-type outgoing group-chat last-in-group? mentioned pinned]}]
|
||||
(merge
|
||||
|
||||
@@ -51,11 +51,14 @@
|
||||
(swap! handlers/handler-nesting-level inc))
|
||||
(let [[first-arg & rest-args] args
|
||||
initial-fxs? (map? first-arg)
|
||||
fx-fns (if initial-fxs? rest-args args)]
|
||||
(clojure.core/reduce (fn [fxs fx-fn]
|
||||
(let [updated-cofx (update-db cofx fxs)]
|
||||
(if fx-fn
|
||||
(safe-merge fxs (fx-fn updated-cofx))
|
||||
fxs)))
|
||||
(if initial-fxs? first-arg {:db db})
|
||||
fx-fns)))
|
||||
fx-fns (if initial-fxs? rest-args args)
|
||||
res
|
||||
(clojure.core/reduce (fn [fxs fx-fn]
|
||||
(let [updated-cofx (update-db cofx fxs)]
|
||||
(if fx-fn
|
||||
(safe-merge fxs (fx-fn updated-cofx))
|
||||
fxs)))
|
||||
(if initial-fxs? first-arg {:db db})
|
||||
fx-fns)]
|
||||
(swap! handlers/handler-nesting-level dec)
|
||||
res))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.92.4+hotfix.2",
|
||||
"commit-sha1": "3df0ea59a83426b3b54049b3d56df459b0c77111",
|
||||
"src-sha256": "1a63qlvvfzp9pkdaq7plhmadn0q9dm20blp1322q1piajphf641v"
|
||||
"version": "v0.92.5",
|
||||
"commit-sha1": "64589fbd1f81022c7f995a7d993f0c026ecc6923",
|
||||
"src-sha256": "0d4zh9kmgi0vwgn1wsbjfnp7hiv2m0l1472wkjg23snskz2g5g14"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[pytest]
|
||||
norecursedirs = .git views
|
||||
addopts = -s -v --tb=line --junitxml=result.xml
|
||||
addopts = -s -v --tb=line --junitxml=result.xml --dist loadgroup -n 5
|
||||
junit_family=legacy
|
||||
markers =
|
||||
critical: TCs by priority
|
||||
|
||||
@@ -17,7 +17,7 @@ future==0.18.2
|
||||
flaky==3.7.0
|
||||
idna==2.10
|
||||
kiwisolver==1.3.1
|
||||
lxml==4.6.2
|
||||
lxml==4.6.5
|
||||
matplotlib==3.3.3
|
||||
multidict==5.0.2
|
||||
namedlist==1.8
|
||||
@@ -30,9 +30,9 @@ pycryptodome==3.9.9
|
||||
pyethash==0.1.27
|
||||
pyparsing==2.4.7
|
||||
pysha3==1.0.2
|
||||
pytest==6.1.2
|
||||
pytest==6.2.0
|
||||
pytest-forked==1.3.0
|
||||
pytest-xdist==2.2.0
|
||||
pytest-xdist==2.5.0
|
||||
python-dateutil==2.8.1
|
||||
pytz==2020.4
|
||||
PyYAML==5.4
|
||||
|
||||
@@ -12,14 +12,14 @@ RERUN_ERRORS = [
|
||||
'failed to start the browser or device',
|
||||
'ERROR The test with session id',
|
||||
"503 Service Unavailable",
|
||||
"object has no attribute",
|
||||
# "object has no attribute",
|
||||
"[Errno 104] Connection reset by peer",
|
||||
"Sauce could not start your job",
|
||||
"HTTP Error 303",
|
||||
"http.client.RemoteDisconnected: Remote end closed connection without response",
|
||||
"[Errno 110] Connection timed out",
|
||||
"replacement transaction underpriced",
|
||||
"StaleElementReferenceException",
|
||||
# "StaleElementReferenceException",
|
||||
"'GetStartedButton' is not found on the screen",
|
||||
"'AccessKeyButton' is not found on the screen",
|
||||
"'SignInPhraseText' is not found on the screen"
|
||||
|
||||
@@ -1036,7 +1036,7 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
|
||||
profile_1.mail_server_button.click()
|
||||
profile_1.use_history_node_button.click()
|
||||
profile_1.home_button.click(desired_view='chat')
|
||||
if not public_chat_1.chat_element_by_text(message).is_element_displayed(30):
|
||||
if not public_chat_1.chat_element_by_text(message).is_element_displayed(60):
|
||||
self.errors.append('History was not fetched after enabling use_history_node')
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@@ -1287,6 +1287,7 @@ class TestProfileMultipleDevice(MultipleDeviceTestCase):
|
||||
if not channel_2.chat_element_by_text(message_after_sync).is_element_displayed(30):
|
||||
self.errors.append("Message sent to community channel after sync is not shown!")
|
||||
[channel.back_button.click() for channel in (channel_1, channel_2)]
|
||||
[home.get_chat(comm_before_sync_name, community=True).click() for home in (home_1, home_2)]
|
||||
comm_before_1.add_channel(channel_after_sync)
|
||||
if not comm_before_2.get_chat(channel_after_sync).is_element_displayed(30):
|
||||
self.errors.append("New added channel after sync is not shown!")
|
||||
|
||||
@@ -616,7 +616,6 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
|
||||
device_1.just_fyi('check that new messages from blocked user are not delivered')
|
||||
self.drivers[0].launch_app()
|
||||
device_1.sign_in()
|
||||
home_1.join_public_chat(chat_name)
|
||||
for message in message_before_block_2, message_after_block_2:
|
||||
if chat_public_1.chat_element_by_text(message).is_element_displayed():
|
||||
self.errors.append(
|
||||
@@ -1019,7 +1018,7 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
|
||||
public_1.chat_message_input.send_keys('@')
|
||||
if (chat_1.search_user_in_mention_suggestion_list(ens_nickname_value).is_element_displayed() or
|
||||
chat_1.search_user_in_mention_suggestion_list(sender['username']).is_element_displayed()):
|
||||
self.errors.append('Blcoked user is available in mention suggestion list')
|
||||
self.errors.append('Blocked user is available in mention suggestion list')
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class TestCommandsMultipleDevices(MultipleDeviceTestCase):
|
||||
@marks.critical
|
||||
@marks.transaction
|
||||
def test_send_eth_in_1_1_chat_transaction_push(self):
|
||||
sender = transaction_senders['A']
|
||||
sender = transaction_senders['B']
|
||||
self.create_drivers(2)
|
||||
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
|
||||
home_1 = device_1.recover_access(passphrase=sender['passphrase'], enable_notifications=True)
|
||||
|
||||
@@ -208,7 +208,7 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
|
||||
message_before_edit, message_after_edit = "Message BEFORE edit 1", "Message AFTER edit 2"
|
||||
public_chat_1.send_message(message_before_edit)
|
||||
public_chat_1.edit_message_in_chat(message_before_edit, message_after_edit)
|
||||
if not public_chat_1.element_by_text_part("⌫ Edited").is_element_displayed():
|
||||
if not public_chat_1.element_by_text_part("⌫ Edited").is_element_displayed(60):
|
||||
self.errors.append('No mark in message bubble about this message was edited')
|
||||
|
||||
device_2.just_fyi(
|
||||
@@ -223,9 +223,9 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
|
||||
if not home_1.element_by_text_part(message_after_edit_1_1).is_element_present():
|
||||
self.errors.append('UNedited message version displayed on preview')
|
||||
chat_private_1 = device_1_one_to_one_chat_element.click()
|
||||
if not home_1.element_by_text_part(message_after_edit_1_1).is_element_present():
|
||||
if not home_1.element_by_text_part(message_after_edit_1_1).is_element_present(30):
|
||||
self.errors.append('No edited message in 1-1 chat displayed')
|
||||
if not home_1.element_by_text_part("⌫ Edited").is_element_present():
|
||||
if not home_1.element_by_text_part("⌫ Edited").is_element_present(30):
|
||||
self.errors.append('No mark in message bubble about this message was edited on receiver side')
|
||||
|
||||
device_2.just_fyi("Verify Device1 can not edit and delete received message from Device2")
|
||||
@@ -238,10 +238,10 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
|
||||
device_2.just_fyi("Delete message and check it is not shown in chat preview on home")
|
||||
chat_private_2.delete_message_in_chat(message_after_edit_1_1)
|
||||
for chat in (chat_private_2, chat_private_1):
|
||||
if chat.chat_element_by_text(message_after_edit_1_1).is_element_displayed():
|
||||
if chat.chat_element_by_text(message_after_edit_1_1).is_element_displayed(30):
|
||||
self.errors.append("Deleted message is shown in chat view for 1-1 chat")
|
||||
chat_private_1.home_button.double_click()
|
||||
if home_1.element_by_text(message_after_edit_1_1).is_element_displayed():
|
||||
if home_1.element_by_text(message_after_edit_1_1).is_element_displayed(30):
|
||||
self.errors.append("Deleted message is shown on chat element on home screen")
|
||||
|
||||
device_2.just_fyi("Send one more message and check that PN will be deleted with message deletion")
|
||||
@@ -258,13 +258,13 @@ class TestMessagesOneToOneChatMultiple(MultipleDeviceTestCase):
|
||||
chat_private_2.just_fyi("Check for that edited message is shown for Device 2 and delete message in public chat")
|
||||
[home.home_button.double_click() for home in (home_1, home_2)]
|
||||
public_chat_1, public_chat_2 = home_1.get_chat('#%s' % chat_name).click(), home_2.join_public_chat(chat_name)
|
||||
if not public_chat_2.element_by_text_part("⌫ Edited").is_element_displayed():
|
||||
if not public_chat_2.element_by_text_part("⌫ Edited").is_element_displayed(60):
|
||||
self.errors.append('No mark in message bubble about this message was edited')
|
||||
if not public_chat_2.element_by_text_part(message_after_edit).is_element_displayed():
|
||||
if not public_chat_2.element_by_text_part(message_after_edit).is_element_displayed(60):
|
||||
self.errors.append('Message is not edited.')
|
||||
public_chat_1.delete_message_in_chat(message_after_edit)
|
||||
for chat in (public_chat_1, public_chat_2):
|
||||
if chat.chat_element_by_text(message_after_edit).is_element_displayed():
|
||||
if chat.chat_element_by_text(message_after_edit).is_element_displayed(30):
|
||||
self.errors.append("Deleted message is shown in chat view for public chat")
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
import emoji
|
||||
import random
|
||||
from dateutil import parser
|
||||
from tests import marks
|
||||
from tests.base_test_case import MultipleDeviceTestCase, SingleDeviceTestCase
|
||||
from views.sign_in_view import SignInView
|
||||
from datetime import timedelta
|
||||
from time import sleep
|
||||
|
||||
import emoji
|
||||
import pytest
|
||||
from dateutil import parser
|
||||
|
||||
class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
|
||||
from tests import marks
|
||||
from tests.base_test_case import MultipleDeviceTestCase, SingleDeviceTestCase, create_shared_drivers, \
|
||||
MultipleSharedDeviceTestCase
|
||||
from views.home_view import HomeView
|
||||
from views.sign_in_view import SignInView
|
||||
|
||||
|
||||
@pytest.mark.xdist_group(name="public_chat")
|
||||
class TestPublicChatMultipleDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.drivers, cls.loop = create_shared_drivers(2)
|
||||
device_1, device_2 = SignInView(cls.drivers[0]), SignInView(cls.drivers[1])
|
||||
home_1, home_2 = device_1.create_user(), device_2.create_user()
|
||||
profile_1 = home_1.profile_button.click()
|
||||
cls.username_1 = profile_1.default_username_text.text
|
||||
profile_1.home_button.click()
|
||||
home_2.home_button.click()
|
||||
|
||||
@marks.testrail_id(5313)
|
||||
@marks.critical
|
||||
def test_public_chat_messaging_emojis_timestamps(self):
|
||||
self.create_drivers(2)
|
||||
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
|
||||
home_1, home_2 = device_1.create_user(), device_2.create_user()
|
||||
profile_1 = home_1.profile_button.click()
|
||||
default_username_1 = profile_1.default_username_text.text
|
||||
profile_1.home_button.click()
|
||||
home_2.home_button.click()
|
||||
|
||||
home_1, home_2 = HomeView(self.drivers[0]), HomeView(self.drivers[1])
|
||||
home_1.just_fyi("Check preselected chats, redirect to status chat")
|
||||
home_1.plus_button.click_until_presence_of_element(home_1.join_public_chat_button)
|
||||
home_1.join_public_chat_button.click()
|
||||
@@ -50,8 +59,8 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
|
||||
if timestamp not in sent_time_variants:
|
||||
self.errors.append(
|
||||
"Timestamp is not shown, expected '%s', in fact '%s'" % (sent_time_variants.join(','), timestamp))
|
||||
if chat_2.chat_element_by_text(message).username.text != default_username_1:
|
||||
self.errors.append("Default username '%s' is not shown next to the received message" % default_username_1)
|
||||
if chat_2.chat_element_by_text(message).username.text != self.username_1:
|
||||
self.errors.append("Default username '%s' is not shown next to the received message" % self.username_1)
|
||||
|
||||
chat_1.send_message(emoji_message)
|
||||
for chat in chat_1, chat_2:
|
||||
@@ -63,13 +72,11 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
|
||||
@marks.testrail_id(5360)
|
||||
@marks.critical
|
||||
def test_unread_messages_counter_public_chat(self):
|
||||
self.create_drivers(2)
|
||||
driver_2 = self.drivers[1]
|
||||
home_1, home_2 = SignInView(self.drivers[0]).create_user(), SignInView(self.drivers[1]).create_user()
|
||||
profile_1 = home_1.profile_button.click()
|
||||
username_1 = profile_1.default_username_text.text
|
||||
profile_1.home_button.click()
|
||||
|
||||
home_1, home_2 = HomeView(self.drivers[0]), HomeView(self.drivers[1])
|
||||
home_1.get_back_to_home_view()
|
||||
home_2.get_back_to_home_view()
|
||||
home_1.home_button.click()
|
||||
home_2.home_button.click()
|
||||
chat_name = home_1.get_random_chat_name()
|
||||
chat_1, chat_2 = home_1.join_public_chat(chat_name), home_2.join_public_chat(chat_name)
|
||||
chat_1.send_message('пиу')
|
||||
@@ -87,7 +94,7 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
|
||||
|
||||
home_1.just_fyi("Check unread message counter when mentioned in public chat")
|
||||
chat_2 = home_2.get_chat_view()
|
||||
chat_2.select_mention_from_suggestion_list(username_1, username_1[:2])
|
||||
chat_2.select_mention_from_suggestion_list(self.username_1, self.username_1[:2])
|
||||
chat_2.send_message_button.click()
|
||||
chat_element.new_messages_counter.wait_for_element(30)
|
||||
chat_element.new_messages_counter.wait_for_element_text("1", 60)
|
||||
@@ -104,6 +111,7 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
|
||||
chat_2.chat_element_by_text(message_2).wait_for_element(20)
|
||||
|
||||
home_2.just_fyi("Check that unread message indicator is not reappeared after relogin")
|
||||
driver_2 = self.drivers[1]
|
||||
driver_2.close_app()
|
||||
driver_2.launch_app()
|
||||
SignInView(driver_2).sign_in()
|
||||
@@ -112,6 +120,9 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
|
||||
self.errors.append('New messages counter is shown after relogin')
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
|
||||
class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
|
||||
|
||||
@marks.testrail_id(6270)
|
||||
@marks.medium
|
||||
def test_mark_all_messages_as_read_public_chat(self):
|
||||
|
||||
@@ -13,6 +13,7 @@ import views.upgrade_dbs.group.data as group
|
||||
class TestUpgradeApplication(SingleDeviceTestCase):
|
||||
|
||||
@marks.testrail_id(6284)
|
||||
@marks.flaky
|
||||
def test_unread_previews_public_chat_version_upgrade(self):
|
||||
sign_in = SignInView(self.driver)
|
||||
unread_one_to_one_name, unread_public_name = 'All Whopping Dassierat', '#before-upgrade'
|
||||
@@ -117,8 +118,8 @@ class TestUpgradeApplication(SingleDeviceTestCase):
|
||||
public_chat = home.get_chat(mention).click()
|
||||
public_chat.scroll_to_start_of_history()
|
||||
pub_chat_data = chats[mention]
|
||||
public_chat.element_starts_with_text(pub_chat_data['reply']).scroll_to_element()
|
||||
public_replied_message = public_chat.chat_element_by_text(pub_chat_data['reply'])
|
||||
public_replied_message.scroll_to_element()
|
||||
if pub_chat_data['long'] not in public_replied_message.replied_message_text:
|
||||
self.errors.append("Reply is not present in message received in public chat %s after upgrade" % mention)
|
||||
public_chat.element_starts_with_text(pub_chat_data['mention']).scroll_to_element()
|
||||
@@ -477,6 +478,7 @@ class TestUpgradeMultipleApplication(MultipleDeviceTestCase):
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(695812)
|
||||
@marks.flaky
|
||||
def test_devices_activity_centre_profile_settings_upgrade(self):
|
||||
self.create_drivers(2)
|
||||
user = ens_user
|
||||
@@ -524,7 +526,8 @@ class TestUpgradeMultipleApplication(MultipleDeviceTestCase):
|
||||
if not profile_1.accept_new_chats_from_contacts_only.is_element_displayed():
|
||||
self.errors.append("Accept contacts from setting is not preserved after upgrade!")
|
||||
profile_1.profile_button.click()
|
||||
profile_1.appearance_button.click()
|
||||
profile_1.privacy_and_security_button.click()
|
||||
profile_1.show_profile_pictures_of.scroll_to_element()
|
||||
if not profile_1.show_profile_pictures_of.is_element_image_similar_to_template('block_dark.png'):
|
||||
self.errors.append('Dark mode is not applied!')
|
||||
if not profile_1.element_by_translation_id("everyone").is_element_displayed():
|
||||
|
||||
@@ -19,47 +19,89 @@ from tests import test_suite_data, start_threads, appium_container, pytest_confi
|
||||
import base64
|
||||
from re import findall
|
||||
|
||||
sauce_username = environ.get('SAUCE_USERNAME')
|
||||
|
||||
|
||||
sauce_access_key = environ.get('SAUCE_ACCESS_KEY')
|
||||
|
||||
executor_sauce_lab = 'http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (sauce_username, sauce_access_key)
|
||||
|
||||
executor_local = 'http://localhost:4723/wd/hub'
|
||||
|
||||
implicit_wait = 5
|
||||
|
||||
|
||||
def get_capabilities_local():
|
||||
desired_caps = dict()
|
||||
if pytest_config_global['docker']:
|
||||
# apk is in shared volume directory
|
||||
apk = '/root/shared_volume/%s' % pytest_config_global['apk']
|
||||
else:
|
||||
apk = pytest_config_global['apk']
|
||||
desired_caps['app'] = apk
|
||||
desired_caps['deviceName'] = 'nexus_5'
|
||||
desired_caps['platformName'] = 'Android'
|
||||
desired_caps['appiumVersion'] = '1.9.1'
|
||||
desired_caps['platformVersion'] = '10.0'
|
||||
desired_caps['newCommandTimeout'] = 600
|
||||
desired_caps['fullReset'] = False
|
||||
desired_caps['unicodeKeyboard'] = True
|
||||
desired_caps['automationName'] = 'UiAutomator2'
|
||||
desired_caps['setWebContentDebuggingEnabled'] = True
|
||||
return desired_caps
|
||||
|
||||
|
||||
def add_local_devices_to_capabilities():
|
||||
updated_capabilities = list()
|
||||
raw_out = re.split(r'[\r\\n]+', str(subprocess.check_output(['adb', 'devices'])).rstrip())
|
||||
for line in raw_out[1:]:
|
||||
serial = re.findall(r"(([\d.\d:]*\d+)|\bemulator-\d+)", line)
|
||||
if serial:
|
||||
capabilities = get_capabilities_local()
|
||||
capabilities['udid'] = serial[0][0]
|
||||
updated_capabilities.append(capabilities)
|
||||
return updated_capabilities
|
||||
|
||||
|
||||
def get_capabilities_sauce_lab():
|
||||
desired_caps = dict()
|
||||
desired_caps['app'] = 'sauce-storage:' + test_suite_data.apk_name
|
||||
|
||||
desired_caps['build'] = pytest_config_global['build']
|
||||
desired_caps['name'] = test_suite_data.current_test.name
|
||||
desired_caps['platformName'] = 'Android'
|
||||
desired_caps['appiumVersion'] = '1.18.1'
|
||||
desired_caps['platformVersion'] = '10.0'
|
||||
desired_caps['deviceName'] = 'Android GoogleAPI Emulator'
|
||||
desired_caps['deviceOrientation'] = "portrait"
|
||||
desired_caps['commandTimeout'] = 600
|
||||
desired_caps['idleTimeout'] = 600
|
||||
desired_caps['unicodeKeyboard'] = True
|
||||
desired_caps['automationName'] = 'UiAutomator2'
|
||||
desired_caps['setWebContentDebuggingEnabled'] = True
|
||||
desired_caps['ignoreUnimportantViews'] = False
|
||||
desired_caps['enableNotificationListener'] = True
|
||||
desired_caps['maxDuration'] = 1800
|
||||
return desired_caps
|
||||
|
||||
|
||||
def update_capabilities_sauce_lab(new_capabilities: dict):
|
||||
caps = get_capabilities_sauce_lab().copy()
|
||||
caps.update(new_capabilities)
|
||||
return caps
|
||||
|
||||
|
||||
class AbstractTestCase:
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@property
|
||||
def sauce_username(self):
|
||||
return environ.get('SAUCE_USERNAME')
|
||||
|
||||
@property
|
||||
def sauce_access_key(self):
|
||||
return environ.get('SAUCE_ACCESS_KEY')
|
||||
|
||||
@property
|
||||
def executor_sauce_lab(self):
|
||||
return 'http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (self.sauce_username, self.sauce_access_key)
|
||||
|
||||
@property
|
||||
def executor_local(self):
|
||||
return 'http://localhost:4723/wd/hub'
|
||||
|
||||
@staticmethod
|
||||
def print_sauce_lab_info(driver):
|
||||
def print_sauce_lab_info(self, driver):
|
||||
sys.stdout = sys.stderr
|
||||
print("SauceOnDemandSessionID=%s job-name=%s" % (driver.session_id,
|
||||
pytest_config_global['build']))
|
||||
|
||||
@staticmethod
|
||||
def get_translation_by_key(key):
|
||||
def get_translation_by_key(self, key):
|
||||
return transl[key]
|
||||
|
||||
def add_local_devices_to_capabilities(self):
|
||||
updated_capabilities = list()
|
||||
raw_out = re.split(r'[\r\\n]+', str(subprocess.check_output(['adb', 'devices'])).rstrip())
|
||||
for line in raw_out[1:]:
|
||||
serial = re.findall(r"(([\d.\d:]*\d+)|\bemulator-\d+)", line)
|
||||
if serial:
|
||||
capabilities = self.capabilities_local
|
||||
capabilities['udid'] = serial[0][0]
|
||||
updated_capabilities.append(capabilities)
|
||||
return updated_capabilities
|
||||
|
||||
@property
|
||||
def app_path(self):
|
||||
app_path = '/storage/emulated/0/Android/data/im.status.ethereum.pr/files/Download/' if findall(r'pr\d\d\d\d\d',
|
||||
@@ -71,53 +113,6 @@ class AbstractTestCase:
|
||||
def geth_path(self):
|
||||
return self.app_path + 'geth.log'
|
||||
|
||||
@property
|
||||
def capabilities_sauce_lab(self):
|
||||
desired_caps = dict()
|
||||
desired_caps['app'] = 'sauce-storage:' + test_suite_data.apk_name
|
||||
|
||||
desired_caps['build'] = pytest_config_global['build']
|
||||
desired_caps['name'] = test_suite_data.current_test.name
|
||||
desired_caps['platformName'] = 'Android'
|
||||
desired_caps['appiumVersion'] = '1.18.1'
|
||||
desired_caps['platformVersion'] = '10.0'
|
||||
desired_caps['deviceName'] = 'Android GoogleAPI Emulator'
|
||||
desired_caps['deviceOrientation'] = "portrait"
|
||||
desired_caps['commandTimeout'] = 600
|
||||
desired_caps['idleTimeout'] = 600
|
||||
desired_caps['unicodeKeyboard'] = True
|
||||
desired_caps['automationName'] = 'UiAutomator2'
|
||||
desired_caps['setWebContentDebuggingEnabled'] = True
|
||||
desired_caps['ignoreUnimportantViews'] = False
|
||||
desired_caps['enableNotificationListener'] = True
|
||||
desired_caps['maxDuration'] = 1800
|
||||
return desired_caps
|
||||
|
||||
def update_capabilities_sauce_lab(self, new_capabilities: dict):
|
||||
caps = self.capabilities_sauce_lab.copy()
|
||||
caps.update(new_capabilities)
|
||||
return caps
|
||||
|
||||
@property
|
||||
def capabilities_local(self):
|
||||
desired_caps = dict()
|
||||
if pytest_config_global['docker']:
|
||||
# apk is in shared volume directory
|
||||
apk = '/root/shared_volume/%s' % pytest_config_global['apk']
|
||||
else:
|
||||
apk = pytest_config_global['apk']
|
||||
desired_caps['app'] = apk
|
||||
desired_caps['deviceName'] = 'nexus_5'
|
||||
desired_caps['platformName'] = 'Android'
|
||||
desired_caps['appiumVersion'] = '1.9.1'
|
||||
desired_caps['platformVersion'] = pytest_config_global['platform_version']
|
||||
desired_caps['newCommandTimeout'] = 600
|
||||
desired_caps['fullReset'] = False
|
||||
desired_caps['unicodeKeyboard'] = True
|
||||
desired_caps['automationName'] = 'UiAutomator2'
|
||||
desired_caps['setWebContentDebuggingEnabled'] = True
|
||||
return desired_caps
|
||||
|
||||
@abstractmethod
|
||||
def setup_method(self, method):
|
||||
raise NotImplementedError('Should be overridden from a child class')
|
||||
@@ -130,10 +125,6 @@ class AbstractTestCase:
|
||||
def environment(self):
|
||||
return pytest_config_global['env']
|
||||
|
||||
@property
|
||||
def implicitly_wait(self):
|
||||
return 5
|
||||
|
||||
network_api = NetworkApi()
|
||||
github_report = GithubHtmlReport()
|
||||
|
||||
@@ -197,13 +188,13 @@ class SingleDeviceTestCase(AbstractTestCase):
|
||||
appium_container.start_appium_container(pytest_config_global['docker_shared_volume'])
|
||||
appium_container.connect_device(pytest_config_global['device_ip'])
|
||||
|
||||
(executor, capabilities) = (self.executor_sauce_lab, self.capabilities_sauce_lab) if \
|
||||
self.environment == 'sauce' else (self.executor_local, self.capabilities_local)
|
||||
(executor, capabilities) = (executor_sauce_lab, get_capabilities_sauce_lab()) if \
|
||||
self.environment == 'sauce' else (executor_local, get_capabilities_local())
|
||||
for key, value in kwargs.items():
|
||||
capabilities[key] = value
|
||||
self.driver = Driver(executor, capabilities)
|
||||
test_suite_data.current_test.testruns[-1].jobs[self.driver.session_id] = 1
|
||||
self.driver.implicitly_wait(self.implicitly_wait)
|
||||
self.driver.implicitly_wait(implicit_wait)
|
||||
self.errors = Errors()
|
||||
|
||||
if pytest_config_global['docker']:
|
||||
@@ -241,7 +232,7 @@ class LocalMultipleDeviceTestCase(AbstractTestCase):
|
||||
def teardown_method(self, method):
|
||||
for driver in self.drivers:
|
||||
try:
|
||||
self.add_alert_text_to_report(driver)
|
||||
self.add_alert_text_to_report(self.drivers[driver])
|
||||
self.drivers[driver].quit()
|
||||
except WebDriverException:
|
||||
pass
|
||||
@@ -263,12 +254,12 @@ class SauceMultipleDeviceTestCase(AbstractTestCase):
|
||||
self.drivers = self.loop.run_until_complete(start_threads(quantity,
|
||||
Driver,
|
||||
self.drivers,
|
||||
self.executor_sauce_lab,
|
||||
self.update_capabilities_sauce_lab(capabilities)))
|
||||
executor_sauce_lab,
|
||||
update_capabilities_sauce_lab(capabilities)))
|
||||
for driver in range(quantity):
|
||||
test_suite_data.current_test.testruns[-1].jobs[self.drivers[driver].session_id] = driver + 1
|
||||
self.drivers[driver].implicitly_wait(
|
||||
custom_implicitly_wait if custom_implicitly_wait else self.implicitly_wait)
|
||||
custom_implicitly_wait if custom_implicitly_wait else implicit_wait)
|
||||
|
||||
def teardown_method(self, method):
|
||||
geth_names, geth_contents = [], []
|
||||
@@ -290,10 +281,97 @@ class SauceMultipleDeviceTestCase(AbstractTestCase):
|
||||
cls.loop.close()
|
||||
|
||||
|
||||
def create_shared_drivers(quantity):
|
||||
drivers = dict()
|
||||
if pytest_config_global['env'] == 'local':
|
||||
capabilities = add_local_devices_to_capabilities()
|
||||
for i in range(quantity):
|
||||
driver = Driver(executor_local, capabilities[i])
|
||||
test_suite_data.current_test.testruns[-1].jobs[driver.session_id] = i + 1
|
||||
driver.implicitly_wait(implicit_wait)
|
||||
drivers[i] = driver
|
||||
loop = None
|
||||
else:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
capabilities = {'maxDuration': 1800}
|
||||
drivers = loop.run_until_complete(start_threads(quantity,
|
||||
Driver,
|
||||
drivers,
|
||||
executor_sauce_lab,
|
||||
update_capabilities_sauce_lab(capabilities)))
|
||||
for i in range(quantity):
|
||||
test_suite_data.current_test.testruns[-1].jobs[drivers[i].session_id] = i + 1
|
||||
drivers[i].implicitly_wait(implicit_wait)
|
||||
return drivers, loop
|
||||
|
||||
|
||||
class LocalSharedMultipleDeviceTestCase(AbstractTestCase):
|
||||
|
||||
def setup_method(self, method):
|
||||
jobs = test_suite_data.current_test.testruns[-1].jobs
|
||||
if not jobs:
|
||||
for index, driver in self.drivers.items():
|
||||
jobs[driver.session_id] = index + 1
|
||||
self.errors = Errors()
|
||||
|
||||
def teardown_method(self, method):
|
||||
for driver in self.drivers:
|
||||
try:
|
||||
self.add_alert_text_to_report(self.drivers[driver])
|
||||
except WebDriverException:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
for driver in cls.drivers:
|
||||
try:
|
||||
cls.drivers[driver].quit()
|
||||
except WebDriverException:
|
||||
pass
|
||||
|
||||
|
||||
class SauceSharedMultipleDeviceTestCase(AbstractTestCase):
|
||||
|
||||
def setup_method(self, method):
|
||||
jobs = test_suite_data.current_test.testruns[-1].jobs
|
||||
if not jobs:
|
||||
for index, driver in self.drivers.items():
|
||||
jobs[driver.session_id] = index + 1
|
||||
self.errors = Errors()
|
||||
|
||||
def teardown_method(self, method):
|
||||
geth_names, geth_contents = [], []
|
||||
for driver in self.drivers:
|
||||
try:
|
||||
self.print_sauce_lab_info(self.drivers[driver])
|
||||
self.add_alert_text_to_report(self.drivers[driver])
|
||||
geth_names.append(
|
||||
'%s_geth%s.log' % (test_suite_data.current_test.name, str(self.drivers[driver].number)))
|
||||
geth_contents.append(self.pull_geth(self.drivers[driver]))
|
||||
|
||||
except (WebDriverException, AttributeError):
|
||||
pass
|
||||
finally:
|
||||
geth = {geth_names[i]: geth_contents[i] for i in range(len(geth_names))}
|
||||
self.github_report.save_test(test_suite_data.current_test, geth)
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
for driver in cls.drivers:
|
||||
try:
|
||||
cls.drivers[driver].quit()
|
||||
except WebDriverException:
|
||||
pass
|
||||
cls.loop.close()
|
||||
|
||||
|
||||
if pytest_config_global['env'] == 'local':
|
||||
MultipleDeviceTestCase = LocalMultipleDeviceTestCase
|
||||
MultipleSharedDeviceTestCase = LocalSharedMultipleDeviceTestCase
|
||||
else:
|
||||
MultipleDeviceTestCase = SauceMultipleDeviceTestCase
|
||||
MultipleSharedDeviceTestCase = SauceSharedMultipleDeviceTestCase
|
||||
|
||||
|
||||
class NoDeviceTestCase(AbstractTestCase):
|
||||
|
||||
@@ -282,7 +282,8 @@ def pytest_runtest_protocol(item, nextitem):
|
||||
for i in range(rerun_count):
|
||||
reports = runtestprotocol(item, nextitem=nextitem)
|
||||
for report in reports:
|
||||
if report.failed and should_rerun_test(report.longreprtext):
|
||||
is_in_group = [i for i in item.iter_markers(name='xdist_group')]
|
||||
if report.failed and should_rerun_test(report.longreprtext) and not is_in_group:
|
||||
break # rerun
|
||||
else:
|
||||
return True # no need to rerun
|
||||
|
||||
@@ -399,3 +399,9 @@ class SilentButton(Button):
|
||||
def text(self):
|
||||
text = self.find_element().text
|
||||
return text
|
||||
|
||||
|
||||
class CheckBox(Button):
|
||||
def click(self):
|
||||
super(CheckBox, self).click_until_presence_of_element(Button(self.driver, accessibility_id="checkbox-on"))
|
||||
return self.navigate()
|
||||
|
||||
@@ -13,7 +13,7 @@ from selenium.common.exceptions import NoSuchElementException, TimeoutException
|
||||
|
||||
from support.device_apps import start_web_browser
|
||||
from tests import common_password, pytest_config_global, transl
|
||||
from views.base_element import Button, BaseElement, EditBox, Text
|
||||
from views.base_element import Button, BaseElement, EditBox, Text, CheckBox
|
||||
|
||||
|
||||
class BackButton(Button):
|
||||
@@ -26,6 +26,7 @@ class BackButton(Button):
|
||||
return self.navigate()
|
||||
|
||||
|
||||
|
||||
class AllowButton(Button):
|
||||
def __init__(self, driver):
|
||||
super().__init__(driver, translation_id="allow", uppercase=True)
|
||||
@@ -191,9 +192,21 @@ class AirplaneModeButton(Button):
|
||||
def __init__(self, driver):
|
||||
super().__init__(driver, accessibility_id="Airplane mode")
|
||||
|
||||
def click(self):
|
||||
def open_quick_action_menu(self):
|
||||
action = TouchAction(self.driver)
|
||||
action.press(None, 200, 0).move_to(None, 200, 300).perform()
|
||||
|
||||
def click(self):
|
||||
counter = 0
|
||||
desired_element = AirplaneModeButton(self.driver)
|
||||
while not desired_element.is_element_present() and counter <= 3:
|
||||
try:
|
||||
self.open_quick_action_menu()
|
||||
desired_element.wait_for_element(5)
|
||||
except (NoSuchElementException, TimeoutException):
|
||||
counter += 1
|
||||
else:
|
||||
self.driver.info("%s element not found" % desired_element.name)
|
||||
super(AirplaneModeButton, self).click()
|
||||
self.driver.press_keycode(4)
|
||||
|
||||
@@ -252,6 +265,9 @@ class BaseView(object):
|
||||
self.qr_code_image = Button(self.driver, accessibility_id="qr-code-image")
|
||||
self.sign_in_phrase = SignInPhraseText(self.driver)
|
||||
|
||||
# checkboxes and toggles
|
||||
self.checkbox_button = CheckBox(self.driver, accessibility_id="checkbox-off")
|
||||
|
||||
# external browser
|
||||
self.open_in_status_button = OpenInStatusButton(self.driver)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import time
|
||||
from selenium.common.exceptions import TimeoutException, NoSuchElementException
|
||||
from views.base_element import Button, Text, BaseElement, SilentButton
|
||||
from views.base_element import Button, Text, BaseElement, SilentButton, CheckBox
|
||||
from views.base_view import BaseView
|
||||
from tests import test_dapp_url
|
||||
|
||||
@@ -211,8 +211,7 @@ class HomeView(BaseView):
|
||||
# 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")
|
||||
self.remember_my_choice_checkbox = Button(self.driver, xpath="//*[@content-desc='remember-choice']"
|
||||
"//*[@content-desc='checkbox']")
|
||||
self.remember_my_choice_checkbox = CheckBox(self.driver, accessibility_id=":checkbox-on")
|
||||
|
||||
# Connection status bottom sheet
|
||||
self.connected_to_n_peers_text = Text(self.driver, accessibility_id="connected-to-n-peers")
|
||||
|
||||
@@ -144,7 +144,7 @@ class SignInView(BaseView):
|
||||
self.first_username_on_choose_chat_name = Text(self.driver,
|
||||
xpath="//*[@content-desc='select-account-button-0']//android.widget.TextView[1]")
|
||||
self.get_keycard_banner = Button(self.driver, translation_id="get-a-keycard")
|
||||
self.accept_tos_checkbox = Button(self.driver, xpath="//android.widget.CheckBox[@content-desc='checkbox']")
|
||||
self.accept_tos_checkbox = self.checkbox_button
|
||||
|
||||
# keycard recovery
|
||||
self.recover_with_keycard_button = Button(self.driver, accessibility_id="recover-with-keycard-button")
|
||||
@@ -178,9 +178,6 @@ class SignInView(BaseView):
|
||||
if not second_user:
|
||||
self.accept_tos_checkbox.click()
|
||||
self.get_started_button.click_until_presence_of_element(self.generate_key_button)
|
||||
if not self.generate_key_button.is_element_displayed():
|
||||
[button.click() for button in
|
||||
[self.accept_tos_checkbox, self.get_started_button, self.generate_key_button]]
|
||||
self.generate_key_button.click()
|
||||
|
||||
self.next_button.click_until_absense_of_element(self.element_by_translation_id("intro-wizard-title2"))
|
||||
@@ -209,9 +206,6 @@ class SignInView(BaseView):
|
||||
if not second_user:
|
||||
self.accept_tos_checkbox.click()
|
||||
self.get_started_button.click_until_presence_of_element(self.access_key_button)
|
||||
if not self.access_key_button.is_element_displayed():
|
||||
self.accept_tos_checkbox.click()
|
||||
self.get_started_button.click()
|
||||
self.access_key_button.click()
|
||||
self.enter_seed_phrase_button.click()
|
||||
self.seedphrase_input.click()
|
||||
|
||||
@@ -55,21 +55,21 @@ chats = {
|
||||
'preview':'Request address for transaction accepted',
|
||||
'messages': {
|
||||
'audio': {
|
||||
'length':'00:10',
|
||||
'timestamp' : '1:02 PM'
|
||||
'length': '00:10',
|
||||
'timestamp': '1:02 PM'
|
||||
},
|
||||
},
|
||||
'commands':{
|
||||
'incoming_ETH_shared':{
|
||||
'value':'0.01 ETH',
|
||||
'incoming_ETH_shared': {
|
||||
'value': '0.01 ETHro',
|
||||
'status': "Shared 'Ethereum account'"
|
||||
},
|
||||
'incoming_ETH_confirmed': {
|
||||
'value': '0.1 ETH',
|
||||
'value': '0.1 ETHro',
|
||||
'status': transl["status-confirmed"]
|
||||
},
|
||||
'incoming_ETH_declined': {
|
||||
'value': '20 ETH',
|
||||
'value': '20 ETHro',
|
||||
'status': transl["transaction-declined"]
|
||||
},
|
||||
'incoming_STT_confirmed': {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import time
|
||||
|
||||
from tests import common_password
|
||||
from views.base_element import Button, Text, EditBox, SilentButton
|
||||
from views.base_element import Button, Text, EditBox, SilentButton, CheckBox
|
||||
from views.base_view import BaseView
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ class TransactionHistoryButton(Button):
|
||||
return TransactionsView(self.driver)
|
||||
|
||||
|
||||
class AssetCheckBox(SilentButton):
|
||||
class AssetCheckBox(CheckBox):
|
||||
def __init__(self, driver, asset_name):
|
||||
super().__init__(driver, xpath="//*[@text='%s']" % asset_name)
|
||||
|
||||
def click(self):
|
||||
self.scroll_to_element(12).click()
|
||||
self.scroll_to_element(12)
|
||||
super().click()
|
||||
|
||||
|
||||
class BackupRecoveryPhrase(Button):
|
||||
@@ -101,12 +102,12 @@ class WalletView(BaseView):
|
||||
self.manage_assets_button = Button(self.driver, accessibility_id="wallet-manage-assets")
|
||||
self.manage_accounts_button = Button(self.driver, accessibility_id="wallet-manage-accounts")
|
||||
self.scan_tokens_button = Button(self.driver, accessibility_id="wallet-scan-token")
|
||||
self.stt_check_box = Button(self.driver,
|
||||
xpath="//*[@text='STT']/../android.view.ViewGroup[@content-desc='checkbox']")
|
||||
self.stt_check_box = CheckBox(self.driver,
|
||||
xpath="//*[@text='STT']/../android.view.ViewGroup[@content-desc='checkbox-off']")
|
||||
self.all_assets_full_names = Text(self.driver,
|
||||
xpath="//*[@content-desc='checkbox']/../android.widget.TextView[1]")
|
||||
xpath="//*[@content-desc='checkbox-off']/../android.widget.TextView[1]")
|
||||
self.all_assets_symbols = Button(self.driver,
|
||||
xpath="//*[@content-desc='checkbox']/../android.widget.TextView[2]")
|
||||
xpath="//*[@content-desc='checkbox-off']/../android.widget.TextView[2]")
|
||||
self.currency_item_text = Text(self.driver, xpath="//*[@content-desc='currency-item']//android.widget.TextView")
|
||||
|
||||
self.address_text = Text(self.driver, accessibility_id="address-text")
|
||||
|
||||
Reference in New Issue
Block a user