Compare commits

...
Author SHA1 Message Date
pavloburykh 99ccf59b0a moved test_chat_gap to 1_1 medium group 2022-05-12 12:40:25 +03:00
pavloburykh 4efa75b758 e2e: refactoring 2022-05-11 15:14:14 +03:00
pavloburykh 81a8ed95a7 e2e: fix test_sticker_1_1_public_chat 2022-05-10 13:01:50 +03:00
Parvesh Monu 2a81814992 Switcher & navigation 2 foundation (#13167) 2022-05-10 15:08:19 +05:30
Churikova Tetiana bcdb3c4194 e2e: group chats 2022-05-09 21:43:01 +02:00
andrey 54c3030971 https://github.com/status-im/status-go/compare/640793fe85d9a9eef9eb3712cda1c5a1ceea401a...0048aaebcc7859a6f0dd7cdf0266fe029f3066fcn
[#13173] move stickers business logic to status-go

Signed-off-by: andrey <motor4ik@gmail.com>
2022-05-09 15:51:05 +02:00
jakub d765dd1f60 ci: use nix copy instead of nix-copy-closure
Also use `ssh-ng://` protocol implementation instead of `ssh://`.
Might fix the following error:
```
error (ignored): error: reached end of FramedSource
error: sized: unexpected end-of-file
error: unexpected end-of-file
```
For more details see: https://github.com/NixOS/nix/issues/6495

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2022-05-09 13:21:46 +02:00
jakub 777d0581ce nix: upgrade from 2.6.1 to 2.8.0
Minor upgrade done to match the version deployed in CI due to a bug
with `nix-copy-closure` which broken Nix cache jobs:
https://github.com/status-im/infra-ci/issues/49

This will not force upgrade on developes, nor require any `make nix-purge`.
If however and upgrade is wanted it can be simply done with `nix upgrade-nix`.

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2022-05-07 11:24:58 +02:00
jakub 6f45b5c8e8 ci: upgarde to Xcode 13.3.1
App store now requires all applications to be built with XCode 13:
https://developer.apple.com/news/?id=2t1chhp3

Related: https://github.com/status-im/infra-ci/issues/48

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2022-05-06 19:27:22 +02:00
yevh-berdnyk e219aaf2b0 Fixed adding results to TestRail 2022-05-06 17:48:30 +03:00
Churikova Tetiana 44a449660a e2e: fixes 2022-05-06 16:07:16 +02:00
yevh-berdnyk 9891469c6f Changed adding TR results 2022-05-06 13:57:24 +03:00
pavloburykh 5e9738bb7d e2e refactoring: added test_deeplink_chat_profile medium group 2022-05-05 16:46:12 +03:00
Churikova Tetiana 39e3a82228 e2e: fix failures
Signed-off-by: andrey <motor4ik@gmail.com>
2022-05-05 13:52:48 +02:00
Churikova Tetiana 23a3825237 e2e: fix for ... 2022-05-05 12:32:20 +02:00
andrey 98f78ba50b [#13203] feat: move fetch prices to status-go 2022-05-05 12:32:20 +02:00
jakub 1b5f08d680 Gradle: drop unavailable JCenter Bintray from config
This repository has been shut down a year ago:

* https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/
* https://testfairy.com/blog/jcenter-and-bintray-is-shutting-down-what-to-do/

And yet we still keep references to it in the Gradle config.

Resolves: https://github.com/status-im/status-react/issues/13320

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2022-05-05 11:50:50 +02:00
Churikova Tetiana 411fd843ca e2e: numprocesses and fixes 2022-05-04 18:27:42 +02:00
jakub 1e8d85983e nix: upgrade Nixpkgs, Go and Node.js
Notable software upgrades:

- Go `1.16.8` to `1.17.3`
- NodeJS `12.22.7` to `16.14.2`
- OpenJDK `8u272-b10` to `8u322-ga`
- Clojure `1.10.3.1029` to `1.11.1.1107`
- Git `2.33.1` to `2.35.1`
- CMake `3.18.1` to `3.22.3`
- Curl `7.79.1` to `7.82.0`
- GNU Awk `5.1.0` to `5.1.1`

The Go upgrade is done in advance of upgrade to `1.18.1`.
The Node.js upgrade is done since `12.x` security support [is over](https://endoflife.date/nodejs).

Signed-off-by: Jakub Sokołowski <jakub@status.im>
2022-05-04 16:51:32 +02:00
Daniel Fernandes 960e338f48 Fixes status-im#13263
Signed-off-by: andrey <motor4ik@gmail.com>
2022-05-04 12:57:57 +02:00
andrey 8ac0823eef placeholder icons 2022-05-04 10:31:09 +02:00
andrey 1964200332 white line android fix
Signed-off-by: andrey <motor4ik@gmail.com>
2022-05-03 13:19:14 +02:00
98 changed files with 1779 additions and 1290 deletions
+1
View File
@@ -34,3 +34,4 @@ COLLECTIBLES_ENABLED=1
COMMANDS_ENABLED=1
TWO_MINUTES_SYNCING=1
SWAP_ENABLED=1
STICKERS_TEST_ENABLED=1
+1
View File
@@ -34,3 +34,4 @@ COMMUNITIES_MANAGEMENT_ENABLED=1
METRICS_ENABLED=0
DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
STICKERS_TEST_ENABLED=1
+1
View File
@@ -37,3 +37,4 @@ METRICS_ENABLED=0
DELETE_MESSAGE_ENABLED=1
TWO_MINUTES_SYNCING=1
ENABLE_QUO_PREVIEW=1
STICKERS_TEST_ENABLED=1
+13 -18
View File
@@ -17,12 +17,12 @@ buildscript {
repositories {
flatDir { dirs "libs", "${rootDir}/app/libs" }
google()
jcenter()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.31"
classpath "com.android.tools.build:gradle:${project.ext.gradlePluginVersion}"
classpath "com.google.gms:google-services:4.3.10"
classpath "com.google.gms:google-services:4.3.10"
// WARNING: Do not place your application dependencies here!
// They belong in the individual module build.gradle files.
}
@@ -45,27 +45,22 @@ subprojects {
allprojects {
beforeEvaluate {
if (System.env.STATUS_GO_ANDROID_LIBDIR == null || System.env.STATUS_GO_ANDROID_LIBDIR == "") {
throw new GradleException('STATUS_GO_ANDROID_LIBDIR environment variable is not valid!')
}
if (System.env.STATUS_GO_ANDROID_LIBDIR == null || System.env.STATUS_GO_ANDROID_LIBDIR == "") {
throw new GradleException('STATUS_GO_ANDROID_LIBDIR environment variable is not valid!')
}
}
repositories {
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
google()
jcenter()
// for geth, function, and status-go
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
maven { url("$rootDir/../node_modules/react-native/android") }
// Android JSC is installed from npm
maven { url("$rootDir/../node_modules/jsc-android/dist") }
// For geth, function, and status-go
flatDir { dirs "libs", System.env.STATUS_GO_ANDROID_LIBDIR }
// Everything Else
google()
mavenCentral()
maven { url "https://www.jitpack.io" }
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
library 'status-jenkins-lib@v1.4.1'
pipeline {
agent { label 'linux && x86_64 && nix-2.6' }
agent { label 'linux && x86_64 && nix-2.8' }
options {
timestamps()
+1 -1
View File
@@ -1,7 +1,7 @@
library 'status-jenkins-lib@v1.4.1'
pipeline {
agent { label 'macos && x86_64 && nix-2.6 && xcode-12.5' }
agent { label 'macos && x86_64 && nix-2.8 && xcode-13.3' }
parameters {
string(
+2 -2
View File
@@ -98,8 +98,8 @@ pipeline {
nix.shell("""
find /nix/store/ -mindepth 1 -maxdepth 1 -type d \
-not -name '*.links' -and -not -name '*-status-react-*' \
| xargs nix-copy-closure -v \
--to ${params.NIX_CACHE_USER}@${params.NIX_CACHE_HOST}
| xargs nix copy \
--to ssh-ng://${params.NIX_CACHE_USER}@${params.NIX_CACHE_HOST}
""",
pure: false
)
+1 -1
View File
@@ -58,7 +58,7 @@ pipeline {
sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py'
sh """
python3 -m pytest \
--numprocesses 15 \
--numprocesses 9 \
--rerun_count=2 \
--testrail_report=True \
-m testrail_id \
+1 -1
View File
@@ -97,7 +97,7 @@ pipeline {
sh 'cp -f $TEST_ETH_ACCOUNTS_FILE users.py'
sh """
python3 -m pytest \
--numprocesses 15 \
--numprocesses 9 \
--rerun_count=2 \
--testrail_report=True \
-m \"${params.TEST_MARKERS}\" \
+1 -1
View File
@@ -8,7 +8,7 @@ Modules provided by `yarn2nix` are normally fine, but we use `react-native-*` pa
```js
repositories {
google()
jcenter()
maven { url "https://www.jitpack.io" }
}
```
And these need to be patched and replaced with `mavenLocal()` to make sure Gradle doesn't try to fetch dependencies from remote repos.
+2
View File
@@ -11,3 +11,5 @@ keep-derivations = true
keep-outputs = true
# Extra isolation for network and filesystem, doesn't work on MacOS
build-use-sandbox = false
# Enable Nix v2 interface.
experimental-features = nix-command
+3 -3
View File
@@ -33,12 +33,12 @@ in {
gradlePropParser = callPackage ./tools/gradlePropParser.nix { };
# Package version adjustments
go = super.pkgs.go_1_16;
go = super.pkgs.go_1_17;
gradle = super.pkgs.gradle_5;
nodejs = super.pkgs.nodejs-12_x;
nodejs = super.pkgs.nodejs-16_x;
openjdk = super.pkgs.openjdk8_headless;
xcodeWrapper = callPackage ./pkgs/xcodeenv/compose-xcodewrapper.nix { } {
version = "11.5";
version = "13.3";
allowHigher = true;
};
gomobile = super.gomobile.override {
+2 -2
View File
@@ -13,8 +13,8 @@ let
name = "nixpkgs-source";
owner = "status-im";
repo = "nixpkgs";
rev = "15111f8a9ad423d300886b537647691c2faa28cd";
sha256 = "05ny644x3dpxigljnb4rmams5vrs5gkbcyqjfamvlqm8rdmsi0kn";
rev = "4ba5f26780e7a27aa2e97676603cd9ef04f11e74";
sha256 = "sha256-ZpHXUuF9bvHooKwGuR1Mr/n2mE/LJ6/MCdCk2AfdD9M=";
# To get the compressed Nix sha256, use:
# nix-prefetch-url --unpack https://github.com/${ORG}/nixpkgs/archive/${REV}.tar.gz
};
+2 -2
View File
@@ -6,9 +6,9 @@ GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
source "${GIT_ROOT}/nix/scripts/lib.sh"
source "${GIT_ROOT}/scripts/colors.sh"
NIX_VERSION="2.6.1"
NIX_VERSION="2.8.0"
NIX_INSTALL_URL="https://nixos.org/releases/nix/nix-${NIX_VERSION}/install"
NIX_INSTALL_SHA256="a63732b4f3628ce97e096dbda88e3cb9851ff6b4f94b5cd43feb257126437b9d"
NIX_INSTALL_SHA256="f43bfedfca9151479462d8bc11f238615a2d52f19b14894d8e2b59980e75ef72"
NIX_INSTALL_PATH="/tmp/nix-install-${NIX_VERSION}"
install_nix() {
+1 -1
View File
@@ -66,7 +66,7 @@
"react-native-safe-area-context": "^2.0.0",
"react-native-shake": "^3.3.1",
"react-native-share": "^7.0.1",
"react-native-splash-screen": "^3.2.0",
"react-native-splash-screen": "3.3.0",
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.5.37",
"react-native-svg": "^9.8.4",
"react-native-touch-id": "^4.4.1",
Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -47,6 +47,7 @@
:ValueXY (fn [])
:View {}
:FlatList {}
:Image {}
:ScrollView {}
:Text {}}
:Easing {:bezier (fn [])
+9 -1
View File
@@ -6,7 +6,8 @@
["@react-native-community/hooks" :as hooks]
["react-native-navigation" :refer (Navigation)]
["rn-emoji-keyboard" :refer (EmojiKeyboard)]
["react-native-draggable-flatlist" :default DraggableFlatList]))
["react-native-draggable-flatlist" :default DraggableFlatList]
[status-im.utils.config :as config]))
(def rn-draggable-flatlist (reagent/adapt-react-class DraggableFlatList))
@@ -74,6 +75,9 @@
(def animated-view
(reagent/adapt-react-class (.-View ^js animated)))
(def animated-image-view
(reagent/adapt-react-class (.-Image ^js animated)))
(def ui-manager (.-UIManager ^js rn))
(def layout-animation (.-LayoutAnimation ^js rn))
@@ -103,6 +107,10 @@
:delete #js {:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}}})
(defonce enable-layout-animations
(when (and platform/android? config/quo-preview-enabled?)
(.setLayoutAnimationEnabledExperimental ^js ui-manager true)))
(def activity-indicator (reagent/adapt-react-class (.-ActivityIndicator ^js rn)))
;; Flat-list
+15 -13
View File
@@ -105,7 +105,7 @@
[_ _]
(let [pressed (reagent/atom false)]
(fn [{:keys [on-press disabled type size before after above width
on-long-press accessibility-label icon]
on-long-press accessibility-label icon style]
:or {type :primary
size 40}}
children]
@@ -126,17 +126,19 @@
{:on-press-out (fn []
(reset! pressed nil))})
[rn/view {:style (style-container
type
size
disabled
(get background-color state)
(get border-color state)
icon
above
width
before
after)}
[rn/view {:style (merge
(style-container
type
size
disabled
(get background-color state)
(get border-color state)
icon
above
width
before
after)
style)}
(when above
[rn/view
[quo2.icons/icon above {:container-style {:margin-bottom 2}
@@ -168,4 +170,4 @@
[quo2.icons/icon after {:container-style {:margin-left 4
:margin-right (if (= size 40) 12 8)}
:color icon-color
:size icon-size}]])]]))))
:size icon-size}]])]]))))
+3 -2
View File
@@ -29,5 +29,6 @@
(let [this (reagent/current-component)
props (reagent/props this)
style (text-style props)]
(into [rn/text {:style style}]
(reagent/children this))))
(into [rn/text (merge {:style style}
(dissoc props :style :size :align :weight :color))]
(reagent/children this))))
+18 -1
View File
@@ -34,6 +34,13 @@
(def neutral-50-opa-30 (alpha neutral-50 0.3))
(def neutral-50-opa-40 (alpha neutral-50 0.4))
;;70 with transparency
(def neutral-70-opa-60 (alpha neutral-70 0.6))
(def neutral-70-opa-70 (alpha neutral-70 0.7))
(def neutral-70-opa-80 (alpha neutral-70 0.8))
(def neutral-70-opa-90 (alpha neutral-70 0.9))
(def neutral-70-opa-95 (alpha neutral-70 0.95))
;;80 with transparency
(def neutral-80-opa-60 (alpha neutral-80 0.6))
(def neutral-80-opa-70 (alpha neutral-80 0.7))
@@ -176,5 +183,15 @@
(def info-50-opa-30 (alpha info-50 0.3))
(def info-50-opa-40 (alpha info-50 0.4))
;;;;Switcher
(def switcher-background "#040B14")
;;switcher-screen with transparency
(def switcher-background-opa-60 (alpha switcher-background 0.6))
(def switcher-background-opa-70 (alpha switcher-background 0.7))
(def switcher-background-opa-80 (alpha switcher-background 0.8))
(def switcher-background-opa-90 (alpha switcher-background 0.9))
(def switcher-background-opa-95 (alpha switcher-background 0.95))
(defn theme-colors [light dark]
(if (theme/dark?) dark light))
(if (theme/dark?) dark light))
-15
View File
@@ -10,7 +10,6 @@
[status-im.native-module.core :as status]
[status-im.ui.components.list-selection :as list-selection]
[status-im.navigation :as navigation]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.fx :as fx]
[status-im.utils.http :as http]
[status-im.utils.platform :as platform]
@@ -182,20 +181,6 @@
:history new-history
:history-index new-index))))))
(defmulti storage-gateway :namespace)
(defmethod storage-gateway :ipns
[{:keys [hash]}]
(str "https://" hash))
(defmethod storage-gateway :ipfs
[{:keys [hash]}]
(contenthash/ipfs-url hash))
(defmethod storage-gateway :swarm
[{:keys [hash]}]
(str "https://gateway.ethswarm.org/bzz/" hash))
(fx/defn resolve-ens-multihash-success
{:events [:browser.callback/resolve-ens-multihash-success]}
[{:keys [db] :as cofx} url]
+10
View File
@@ -10,6 +10,7 @@
[quo.design-system.colors :as colors]
[status-im.constants :as constants]
[status-im.navigation :as navigation]
[status-im.navigation2 :as navigation2]
[status-im.utils.clocks :as utils.clocks]
[status-im.utils.fx :as fx]
[status-im.utils.utils :as utils]
@@ -263,6 +264,15 @@
(navigation/pop-to-root-tab % :chat-stack))
(navigation/navigate-to-cofx :chat nil))))
(fx/defn navigate-to-chat-nav2
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
{:events [:chat.ui/navigate-to-chat-nav2]}
[{db :db :as cofx} chat-id from-switcher?]
(fx/merge cofx
{:db (assoc db :current-chat-id chat-id)}
(preload-chat-data chat-id)
(navigation2/navigate-to-nav2 :chat chat-id nil from-switcher?)))
(fx/defn handle-clear-history-response
{:events [::history-cleared]}
[{:keys [db]} chat-id response]
+12 -10
View File
@@ -14,8 +14,7 @@
[status-im.utils.fx :as fx]
["emojilib" :as emojis]
[status-im.chat.models.mentions :as mentions]
[status-im.utils.utils :as utils]
[status-im.multiaccounts.update.core :as multiaccounts.update]))
[status-im.utils.utils :as utils]))
(defn text->emoji
"Replaces emojis in a specified `text`"
@@ -230,12 +229,12 @@
:text (i18n/label :t/update-to-listen-audio {"locale" "en"})})))
(fx/defn send-sticker-message
[cofx {:keys [hash pack]} current-chat-id]
(when-not (string/blank? hash)
[cofx {:keys [hash packID]} current-chat-id]
(when-not (or (string/blank? hash) (string/blank? packID))
(chat.message/send-message cofx {:chat-id current-chat-id
:content-type constants/content-type-sticker
:sticker {:hash hash
:pack pack}
:pack (int packID)}
:text (i18n/label :t/update-to-see-sticker {"locale" "en"})})))
(fx/defn send-edited-message [{:keys [db] :as cofx} text {:keys [message-id]}]
@@ -265,13 +264,16 @@
(fx/defn chat-send-sticker
{:events [:chat/send-sticker]}
[{{:keys [current-chat-id multiaccount]} :db :as cofx} {:keys [hash] :as sticker}]
[{{:keys [current-chat-id] :as db} :db :as cofx} {:keys [hash packID] :as sticker}]
(fx/merge
cofx
(multiaccounts.update/multiaccount-update
:stickers/recent-stickers
(conj (remove #(= hash %) (:stickers/recent-stickers multiaccount)) hash)
{})
{:db (update db
:stickers/recent-stickers
(fn [recent]
(conj (remove #(= hash (:hash %)) recent) sticker)))
::json-rpc/call [{:method "stickers_addRecent"
:params [(int packID) hash]
:on-success #()}]}
(send-sticker-message sticker current-chat-id)))
(fx/defn chat-send-audio
+5 -1
View File
@@ -171,4 +171,8 @@
(def ^:const visibility-status-inactive 4)
(def ^:const wallet-connect-version-1 1)
(def ^:const wallet-connect-version-2 2)
(def ^:const wallet-connect-version-2 2)
(def ^:const sticker-pack-status-installed 1)
(def ^:const sticker-pack-status-pending 2)
(def ^:const sticker-pack-status-owned 3)
-2
View File
@@ -46,8 +46,6 @@
config/default-network))
(update :wallet/visible-tokens rpc->visible-tokens)
(update :pinned-mailservers rpc->pinned-mailservers)
(update :stickers/packs-installed rpc->stickers-packs)
(update :stickers/packs-pending set)
(update :link-previews-enabled-sites set)
(update :custom-bootnodes rpc->custom-bootnodes)
(update :custom-bootnodes-enabled? rpc->custom-bootnodes)
-1
View File
@@ -33,7 +33,6 @@
:dimensions/window (dimensions/window)
:registry {}
:visibility-status-updates {}
:stickers/packs-owned #{}
:stickers/packs-pending #{}
:keycard {:nfc-enabled? false
:pin {:original []
+1 -1
View File
@@ -138,7 +138,7 @@
#(re-frame/dispatch [::name-resolved username
(cond
(not public-key) :owned
(string/ends-with? % "0000000000000000000000000000000000000000000000000000000000000000")
(and (string/ends-with? % "0000000000000000000000000000000000000000000000000000000000000000") (not custom-domain?))
:invalid-ens
(= % public-key) :connected
:else :connected-with-different-key)
+11 -1
View File
@@ -209,6 +209,7 @@
"wallet_loadTransferByHash" {}
"wallet_getTokens" {};, %* [chainId]}
"wallet_getTokensBalancesForChainIDs" {};, %* [@[chainId], accounts, tokens]
"wallet_fetchPrices" {}
"browsers_getBrowsers" {}
"browsers_addBrowser" {}
"browsers_deleteBrowser" {}
@@ -241,7 +242,16 @@
"mailservers_getChatRequestRanges" {}
"mailservers_deleteChatRequestRange" {}
"appmetrics_saveAppMetrics" {}
"appmetrics_getAppMetrics" {}})
"appmetrics_getAppMetrics" {}
"stickers_market" {}
"stickers_installed" {}
"stickers_install" {}
"stickers_addRecent" {}
"stickers_recent" {}
"stickers_buyPrepareTx" {}
"stickers_processPending" {}
"stickers_addPending" {}
"stickers_pending" {}})
(defn on-error-retry
[call-method {:keys [method number-of-retries delay on-error] :as arg}]
+3 -1
View File
@@ -63,7 +63,9 @@
[status-im.multiaccounts.login.core :as login.core]
[status-im.signing.core :as signing]
status-im.wallet-connect.core
status-im.wallet-connect-legacy.core))
status-im.wallet-connect-legacy.core
status-im.navigation2
status-im.navigation2.core))
(re-frame/reg-fx
:dismiss-keyboard
@@ -18,7 +18,6 @@
[status-im.popover.core :as popover]
[status-im.communities.core :as communities]
[status-im.transport.core :as transport]
[status-im.stickers.core :as stickers]
[status-im.mobile-sync-settings.core :as mobile-network]
[status-im.utils.fx :as fx]
[status-im.utils.keychain.core :as keychain]
@@ -445,7 +444,6 @@
(initialize-transactions-management-enabled)
(check-network-version network-id)
(contact/initialize-contacts)
(stickers/init-stickers-packs)
(initialize-browser)
(mobile-network/on-network-status-change)
(get-group-chat-invitations)
+49
View File
@@ -0,0 +1,49 @@
(ns status-im.navigation2
(:require [status-im.utils.fx :as fx]
[status-im.utils.datetime :as datetime]))
(def parent-stack (atom :home-stack))
(fx/defn init-root-nav2
{:events [:init-root-nav2]}
[_ root-id]
{:init-root-fx-nav2 root-id})
(fx/defn open-modal-nav2
{:events [:open-modal-nav2]}
[_ modal]
{:open-modal-fx-nav2 modal})
(fx/defn close-modal-nav2
{:events [:close-modal-nav2]}
[_ modal]
{:close-modal-fx-nav2 modal})
(defn navigate-from-home-stack [go-to-view-id id db]
(reset! parent-stack go-to-view-id)
{:navigate-to-fx-nav2 [go-to-view-id id]
:db (assoc-in db [:navigation2/navigation2-stacks id] {:type go-to-view-id
:id id
:clock (datetime/timestamp)})})
(defn navigate-from-switcher [go-to-view-id id db from-home?]
(reset! parent-stack go-to-view-id)
{:navigate-from-switcher-fx [go-to-view-id id from-home?]
:db (assoc-in db [:navigation2/navigation2-stacks id] {:type go-to-view-id
:id id
:clock (datetime/timestamp)})})
(fx/defn navigate-to-nav2
{:events [:navigate-to-nav2]}
[{:keys [db] :as cofx} go-to-view-id id screen-params from-switcher?]
(let [view-id (:view-id db)
stacks (:navigation2/navigation2-stacks db)
from-home? (= view-id :home-stack)]
(if from-switcher?
(navigate-from-switcher go-to-view-id id db from-home?)
(if from-home?
(navigate-from-home-stack go-to-view-id id db)
;; TODO(parvesh) - new stacks created from other screens should be stacked on current stack, instead of creating new entry
(navigate-from-home-stack go-to-view-id id db)))))
+77
View File
@@ -0,0 +1,77 @@
(ns status-im.navigation2.core
(:require [re-frame.core :as re-frame]
[status-im.ui.screens.views :as views]
[status-im.navigation2.utils :as nav2-utils]
[status-im.navigation2.roots :as roots]
[status-im.navigation.roots :as nav-roots]
["react-native-navigation" :refer (Navigation)]))
(def tab-key-idx {:home 0
:communities 1
:wallet 2
:browser 3})
(defonce set-navigation-default-options
(.setDefaultOptions Navigation (clj->js {:options {:topBar {:visible false}}})))
;; TODO (parvesh) - improve open-modal and close-modal
(defn open-modal [comp]
(.showModal Navigation
(clj->js {:stack {:children
[{:component
{:name comp
:id comp
:options {:topBar {:visible false}}}}]}})))
(defn close-modal [_])
(defn close-all-modals []
(.dismissAllModals Navigation))
(defn get-id [comp id]
(str comp "-" id))
(defn get-options [show-topbar? options]
(if show-topbar?
(merge options
(nav-roots/status-bar-options)
(nav-roots/merge-top-bar (nav-roots/topbar-options) options))
{:topBar {:visible false}}))
(defn change-stack-root [[comp _]]
(let [{:keys [options]} (get views/screens comp)]
(.setStackRoot Navigation
(name comp)
(clj->js {:stack {:id comp
:children [{:component {:id comp
:name comp
:options (get-options false options)}}]}}))))
(defn navigate [[comp _]]
(let [{:keys [options]} (get views/screens comp)]
(reset! nav2-utils/container-stack-view-id comp)
(.push Navigation
(name :home-stack)
(clj->js {:stack {:id comp
:children [{:component {:id comp
:name comp
:options (get-options false options)}}]}}))))
(defn navigate-from-switcher [[comp id from-home?]]
(if from-home?
(navigate [comp id])
(change-stack-root [comp id])))
(re-frame/reg-fx
:init-root-fx-nav2
(fn [new-root-id]
(reset! nav2-utils/container-stack-view-id new-root-id)
(.setRoot Navigation (clj->js (get (roots/roots) new-root-id)))))
(re-frame/reg-fx :open-modal-fx-nav2 open-modal)
(re-frame/reg-fx :close-modal-fx-nav2 close-modal)
(re-frame/reg-fx :navigate-to-fx-nav2 navigate)
(re-frame/reg-fx :navigate-from-switcher-fx navigate-from-switcher)
+27
View File
@@ -0,0 +1,27 @@
(ns status-im.navigation2.home-stack
(:require [quo.react-native :as rn]
[quo2.screens.main :as quo2.preview]
[status-im.switcher.switcher :as switcher]
[status-im.ui.screens.home.views :as home]
[status-im.switcher.constants :as switcher-constants]
[status-im.ui.screens.browser.empty-tab.views :as empty-tab]
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]
[status-im.switcher.bottom-tabs :as bottom-tabs]))
;; TODO(parvesh) - improve stack changing performance (load all stacks at once)
(defn stack-view []
(let [{:keys [width height]} (switcher-constants/dimensions)]
;; bottom-tabs-height (switcher-constants/bottom-tabs-height)]
[rn/view {:style {:width width
:height (- height 80)}} ;; TODO(parvesh) - add height for ios
(case @bottom-tabs/selected-tab-id
:chats-stack [home/home]
:communities-stack [quo2.preview/main-screen]
:wallet-stack [wallet.accounts/accounts-overview]
:browser-stack [empty-tab/empty-tab])]))
(defn home []
[:<>
[stack-view]
[bottom-tabs/bottom-tabs]
[switcher/switcher :home-stack]])
+9
View File
@@ -0,0 +1,9 @@
(ns status-im.navigation2.roots)
(defn roots []
{:home-stack
{:root
{:stack {:id :home-stack
:children [{:component {:name :home-stack
:id :home-stack
:options {:topBar {:visible false}}}}]}}}})
+6
View File
@@ -0,0 +1,6 @@
(ns status-im.navigation2.screens
(:require [status-im.navigation2.home-stack :as home-stack]))
(def screens [{:name :home-stack
:insets {:top false}
:component home-stack/home}])
@@ -0,0 +1,7 @@
(ns status-im.navigation2.stack-with-switcher
(:require [status-im.switcher.switcher :as switcher]))
(defn overlap-stack [comp view-id]
[:<>
[comp]
[switcher/switcher view-id]])
+3
View File
@@ -0,0 +1,3 @@
(ns status-im.navigation2.utils)
(defonce container-stack-view-id (atom nil))
+92 -187
View File
@@ -1,24 +1,13 @@
(ns status-im.stickers.core
(:require [cljs.reader :as edn]
[clojure.set :as clojure.set]
[clojure.string :as string]
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.ethereum.abi-spec :as abi-spec]
[status-im.ethereum.contracts :as contracts]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.navigation :as navigation]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.signing.core :as signing]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.fx :as fx]
[status-im.utils.utils :as utils]))
(defn pack-data-callback
[id]
(fn [[_ _ _ _ price contenthash]]
(when-let [url (contenthash/url contenthash)]
(re-frame/dispatch [:stickers/load-pack url id price]))))
[status-im.utils.utils :as utils]
[status-im.utils.config :as config]
[status-im.constants :as constants]))
(re-frame/reg-fx
:stickers/set-pending-timeout-fx
@@ -26,193 +15,109 @@
(utils/set-timeout #(re-frame/dispatch [:stickers/pending-timeout])
10000)))
(defn eth-call-pack-data
[contract id]
(json-rpc/eth-call
{:contract contract
;; Returns vector of pack data parameters by pack id:
;; [category owner mintable timestamp price contenthash]
:method "getPackData(uint256)"
:params [id]
:outputs ["bytes4[]" "address" "bool" "uint256" "uint256" "bytes"]
:on-success (pack-data-callback id)}))
(re-frame/reg-fx
:stickers/pack-data-fx
(fn [[contract id]]
(eth-call-pack-data contract id)))
(re-frame/reg-fx
:stickers/load-packs-fx
(fn [[contract]]
(json-rpc/eth-call
{:contract contract
;; Returns number of packs registered in the contract
:method "packCount()"
:outputs ["uint256"]
:on-success
(fn [[count]]
(dotimes [id count]
(eth-call-pack-data contract id)))})))
(re-frame/reg-fx
:stickers/owned-packs-fx
(fn [[contract address]]
(json-rpc/eth-call
{:contract contract
;; Returns vector of owned tokens ids in the contract by address
:method "balanceOf(address)"
:params [address]
:outputs ["uint256"]
:number-of-retries 3
:on-success
(fn [[count]]
(dotimes [id count]
(json-rpc/eth-call
{:contract contract
;; Returns pack id in the contract by token id
:method "tokenOfOwnerByIndex(address,uint256)"
:params [address id]
:outputs ["uint256"]
:number-of-retries 3
:on-success
(fn [[token-id]]
(json-rpc/eth-call
{:contract contract
;; Returns pack id in the contract by token id
:method "tokenPackId(uint256)"
:params [token-id]
:outputs ["uint256"]
:number-of-retries 3
:on-success
(fn [[pack-id]]
(re-frame/dispatch [:stickers/pack-owned pack-id]))}))})))})))
(fx/defn init-stickers-packs
[{:keys [db] :as cofx}]
(let [sticker-packs (get-in db [:multiaccount :stickers/packs-installed])
pending-packs (get-in db [:multiaccount :stickers/packs-pending] #{})]
(cond-> {:db (assoc db
:stickers/packs-installed sticker-packs
:stickers/packs sticker-packs
:stickers/packs-pending pending-packs)}
(not-empty pending-packs)
(assoc :stickers/set-pending-timeout-fx nil))))
(fx/defn install-stickers-pack
{:events [:stickers/install-pack]}
[{{:keys [multiaccount] :as db} :db :as cofx} id]
(let [pack (get-in db [:stickers/packs id])]
(fx/merge
cofx
{:db (-> db
(assoc-in [:stickers/packs-installed id] pack))}
;;(assoc :stickers/selected-pack id))} TODO it doesn't scroll to selected pack on Android
(multiaccounts.update/multiaccount-update
:stickers/packs-installed
(assoc (:stickers/packs-installed multiaccount) id pack)
{}))))
(defn valid-sticker? [sticker]
(contains? sticker :hash))
(fx/defn load-sticker-pack-success
{:events [:stickers/load-sticker-pack-success]}
[{:keys [db] :as cofx} edn-string id price]
(let [{:keys [stickers] :as pack} (assoc (get (edn/read-string edn-string) 'meta)
:id id :price price)]
(fx/merge cofx
{:db (cond-> db
(and (seq stickers)
(every? valid-sticker? stickers))
(assoc-in [:stickers/packs id] pack))})))
(fx/defn open-sticker-pack
{:events [:stickers/open-sticker-pack]}
[{{:stickers/keys [packs packs-installed] :networks/keys [current-network] :as db} :db :as cofx} id]
(when (and id (string/starts-with? current-network "mainnet"))
(let [pack (or (get packs-installed id)
(get packs id))
contract-address (contracts/get-address db :status/stickers)
pack-contract (contracts/get-address db :status/sticker-pack)
address (ethereum/default-address db)]
(fx/merge cofx
(navigation/open-modal :stickers-pack {:id id})
#(when (and contract-address (not pack))
{:stickers/pack-data-fx [contract-address id]
:stickers/owned-packs-fx [pack-contract address]})))))
(fx/defn load-pack
{:events [:stickers/load-pack]}
[_ url id price]
{:http-get {:url url
:on-success
(fn [o]
(re-frame/dispatch [:stickers/load-sticker-pack-success o id price]))}})
[{db :db :as cofx} id]
(fx/merge
cofx
{:db (assoc-in db [:stickers/packs id :status] constants/sticker-pack-status-installed)
::json-rpc/call [{:method "stickers_install"
:params [(ethereum/chain-id db) id]
:on-success #()}]}))
(fx/defn load-packs
{:events [:stickers/load-packs]}
[{:keys [db]}]
(let [contract (contracts/get-address db :status/stickers)
pack-contract (contracts/get-address db :status/sticker-pack)
address (ethereum/default-address db)]
(when contract
{:stickers/owned-packs-fx [pack-contract address]
:stickers/load-packs-fx [contract]})))
{::json-rpc/call [{:method "stickers_market"
:params [(ethereum/chain-id db)]
:on-success #(re-frame/dispatch [:stickers/stickers-market-success %])}
{:method "stickers_installed"
:params []
:on-success #(re-frame/dispatch [:stickers/stickers-installed-success %])}
{:method "stickers_pending"
:params []
:on-success #(re-frame/dispatch [:stickers/stickers-pending-success %])}
{:method "stickers_recent"
:params []
:on-success #(re-frame/dispatch [:stickers/stickers-recent-success %])}]})
(fx/defn approve-pack
(fx/defn buy-pack
{:events [:stickers/buy-pack]}
[{db :db :as cofx} pack-id price]
(let [address (ethereum/default-address db)
sticker-market-contract (contracts/get-address db :status/sticker-market)
snt-contract (contracts/get-address db :status/snt)]
(signing/eth-transaction-call
cofx
{:contract snt-contract
:method "approveAndCall(address,uint256,bytes)"
:params [sticker-market-contract
price
(abi-spec/encode "buyToken(uint256,address,uint256)" [pack-id address price])]
:on-result [:stickers/pending-pack pack-id]})))
[{db :db} pack-id]
{::json-rpc/call [{:method "stickers_buyPrepareTx"
:params [(ethereum/chain-id db) (ethereum/default-address db) (int pack-id)]
:on-success #(re-frame/dispatch [:signing.ui/sign
{:tx-obj %
:on-result [:stickers/pending-pack pack-id]}])}]})
(fx/defn pending-pack
{:events [:stickers/pending-pack]}
[{{:keys [multiaccount] :as db} :db :as cofx} id]
(let [contract (contracts/get-address db :status/sticker-pack)
address (ethereum/default-address db)
pending (get multiaccount :stickers/packs-pending #{})]
(when contract
(fx/merge cofx
{:db (update db :stickers/packs-pending conj id)
:stickers/owned-packs-fx [contract address]}
(multiaccounts.update/multiaccount-update
:stickers/packs-pending
(conj pending id)
{})
#(when (zero? (count (:stickers/packs-pending db)))
{:stickers/set-pending-timeout-fx nil})))))
[{db :db} id]
{:db (-> db
(assoc-in [:stickers/packs id :status] constants/sticker-pack-status-pending)
(update :stickers/packs-pending conj id))
:stickers/set-pending-timeout-fx nil
::json-rpc/call [{:method "stickers_addPending"
:params [(ethereum/chain-id db) (int id)]
:on-success #()}]})
(fx/defn pending-timeout
{:events [:stickers/pending-timeout]}
[{{:stickers/keys [packs-pending packs-owned] :as db} :db :as cofx}]
(let [packs-diff (clojure.set/difference packs-pending packs-owned)
contract (contracts/get-address db :status/sticker-pack)
address (ethereum/default-address db)]
(when contract
(fx/merge cofx
(merge {:db (assoc db :stickers/packs-pending packs-diff)}
(when-not (zero? (count packs-diff))
{:stickers/owned-packs-fx [contract address]
:stickers/set-pending-timeout-fx nil}))
(multiaccounts.update/multiaccount-update
:stickers/packs-pending
packs-diff
{})))))
[{{:stickers/keys [packs-pending] :as db} :db}]
(when (seq packs-pending)
{::json-rpc/call [{:method "stickers_processPending"
:params [(ethereum/chain-id db)]
:on-success #(re-frame/dispatch [:stickers/stickers-process-pending-success %])}]}))
(fx/defn pack-owned
{:events [:stickers/pack-owned]}
[{db :db} id]
{:db (update db :stickers/packs-owned conj id)})
(fx/defn stickers-process-pending-success
{:events [:stickers/stickers-process-pending-success]}
[{{:stickers/keys [packs-pending packs] :as db} :db} purchased]
(let [purchased-ids (map :id (vals purchased))
packs-pending (apply disj packs-pending purchased-ids)
packs (reduce (fn [packs id]
(assoc-in packs [id :status] constants/sticker-pack-status-owned))
packs
purchased-ids)]
(merge
{:db (-> db
(assoc :stickers/packs packs)
(assoc :stickers/packs-pending packs-pending))}
(when (seq packs-pending)
{:stickers/set-pending-timeout-fx nil}))))
(fx/defn stickers-market-success
{:events [:stickers/stickers-market-success]}
[{:keys [db]} packs]
(let [packs (reduce (fn [acc pack] (assoc acc (:id pack) pack)) {} packs)]
{:db (update db :stickers/packs merge packs)}))
(fx/defn stickers-installed-success
{:events [:stickers/stickers-installed-success]}
[{:keys [db]} packs]
(let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)]
{:db (update db :stickers/packs merge packs)}))
(fx/defn stickers-pending-success
{:events [:stickers/stickers-pending-success]}
[{:keys [db]} packs]
(let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)]
(merge
{:db (-> db
(assoc :stickers/packs-pending (into #{} (keys packs)))
(update :stickers/packs merge packs))}
(when (seq packs)
{:stickers/set-pending-timeout-fx nil}))))
(fx/defn stickers-recent-success
{:events [:stickers/stickers-recent-success]}
[{:keys [db]} packs]
{:db (assoc db :stickers/recent-stickers packs)})
(fx/defn open-sticker-pack
{:events [:stickers/open-sticker-pack]}
[{{:networks/keys [current-network]} :db :as cofx} id]
(when (and id (or config/stickers-test-enabled? (string/starts-with? current-network "mainnet")))
(navigation/open-modal cofx :stickers-pack {:id id})))
(fx/defn select-pack
{:events [:stickers/select-pack]}
-7
View File
@@ -1,7 +0,0 @@
(ns status-im.stickers.core-test
(:require [cljs.test :refer-macros [deftest is]]
[status-im.stickers.core :as stickers]))
(deftest valid-sticker?
(is (true? (stickers/valid-sticker? {:hash ""})))
(is (false? (stickers/valid-sticker? {}))))
+28 -29
View File
@@ -87,6 +87,7 @@
(reg-root-key-sub :home-items-show-number :home-items-show-number)
(reg-root-key-sub :waku/v2-peer-stats :peer-stats)
(reg-root-key-sub :visibility-status-updates :visibility-status-updates)
(reg-root-key-sub :navigation2/navigation2-stacks :navigation2/navigation2-stacks)
;;NOTE this one is not related to ethereum network
;; it is about cellular network/ wifi network
@@ -136,9 +137,7 @@
;;stickers
(reg-root-key-sub :stickers/selected-pack :stickers/selected-pack)
(reg-root-key-sub :stickers/packs :stickers/packs)
(reg-root-key-sub :stickers/installed-packs :stickers/packs-installed)
(reg-root-key-sub :stickers/packs-owned :stickers/packs-owned)
(reg-root-key-sub :stickers/packs-pending :stickers/packs-pending)
(reg-root-key-sub :stickers/recent-stickers :stickers/recent-stickers)
;;mailserver
(reg-root-key-sub :mailserver/current-id :mailserver/current-id)
@@ -1336,7 +1335,7 @@
(fn [[{:keys [processing]} sending-image mainnet? one-to-one-chat? {:keys [public?]} reply edit]]
(let [sending-image (seq sending-image)]
{:send (not processing)
:stickers (and mainnet?
:stickers (and (or config/stickers-test-enabled? mainnet?)
(not sending-image)
(not reply))
:image (and (not reply)
@@ -1515,23 +1514,20 @@
;;STICKERS =============================================================================================================
(re-frame/reg-sub
:stickers/installed-packs-vals
:<- [:stickers/installed-packs]
:stickers/installed-packs
:<- [:stickers/packs]
(fn [packs]
(vals packs)))
(filter #(= (:status %) constants/sticker-pack-status-installed) (vals packs))))
(re-frame/reg-sub
:stickers/all-packs
:<- [:stickers/packs]
:<- [:stickers/installed-packs]
:<- [:stickers/packs-owned]
:<- [:stickers/packs-pending]
(fn [[packs installed owned pending]]
(map (fn [{:keys [id] :as pack}]
(cond-> pack
(get installed id) (assoc :installed true)
(get owned id) (assoc :owned true)
(get pending id) (assoc :pending true)))
(fn [packs]
(map (fn [{:keys [status] :as pack}]
(-> pack
(assoc :installed (= status constants/sticker-pack-status-installed))
(assoc :pending (= status constants/sticker-pack-status-pending))
(assoc :owned (= status constants/sticker-pack-status-owned))))
(vals packs))))
(re-frame/reg-sub
@@ -1541,19 +1537,6 @@
(fn [[{:keys [id]} packs]]
(first (filter #(= (:id %) id) packs))))
(defn find-pack-id-for-hash [sticker-uri packs]
(some (fn [{:keys [stickers id]}]
(when (some #(= sticker-uri (:hash %)) stickers)
id))
packs))
(re-frame/reg-sub
:stickers/recent
:<- [:multiaccount]
:<- [:stickers/installed-packs-vals]
(fn [[{:keys [:stickers/recent-stickers]} packs]]
(map (fn [hash] {:hash hash :pack (find-pack-id-for-hash hash packs)}) recent-stickers)))
;;HOME ==============================================================================================================
(def memo-home-items (atom nil))
@@ -2964,3 +2947,19 @@
:<- [:bookmarks]
(fn [bookmarks]
(into {} (remove #(:removed (second %)) bookmarks))))
;; NAVIGATION2
(re-frame/reg-sub
:navigation2/switcher-cards
:<- [:navigation2/navigation2-stacks]
(fn [stacks [_ toggle-switcher-screen]]
(sort-by :clock >
(reduce (fn [acc stack-vector]
(let [{:keys [type clock id]} (get stack-vector 1)]
(conj acc {:type type
:clock clock
:id id
:toggle-switcher-screen toggle-switcher-screen})))
'() stacks))))
+47
View File
@@ -0,0 +1,47 @@
(ns status-im.switcher.animation
(:require [quo.react-native :as rn]
[reagent.core :as reagent]
[status-im.switcher.constants :as constants]
[status-im.ui.components.animation :as anim]))
(def bottom-tabs-opacity (anim/create-value 1))
(def bottom-tabs-position (anim/create-value 0))
;; TODO(parvesh): Use 300, after using dispatch-later for opening card(otherwise pending animation issue)
;; or OnAnimationEnd
(def layout-animation #js {:duration 250
:create #js {:type (:ease-in-ease-out rn/layout-animation-types)
:property (:scale-xy rn/layout-animation-properties)}
:update #js {:type (:ease-in-ease-out rn/layout-animation-types)
:property (:scale-xy rn/layout-animation-properties)}
:delete #js {:type (:ease-in-ease-out rn/layout-animation-types)
:property (:scale-xy rn/layout-animation-properties)}})
(defn animate-layout [show? anim-values]
(let [{:keys [width height]} (constants/dimensions)
target-radius (- (max width height)
constants/switcher-button-radius)]
(rn/configure-next layout-animation)
(reset! (:switcher-screen-radius anim-values) (if show? target-radius 1))
(reagent/flush)))
(defn timing-animation [property toValue]
(anim/timing property {:toValue toValue
:duration 300
:useNativeDriver true}))
(defn animate-components [show? view-id anim-values]
(anim/start
(anim/parallel
(into
[(timing-animation (:switcher-button-opacity anim-values) (if show? 0 1))
(timing-animation (:switcher-close-button-icon-opacity anim-values) (if show? 1 0))
(timing-animation (:switcher-close-button-background-opacity anim-values) (if show? 0.2 0))]
(when (= view-id :home-stack)
[(timing-animation bottom-tabs-opacity (if show? 0 1))
(timing-animation bottom-tabs-position (if show? (constants/bottom-tabs-height) 0))])))))
(defn animate [show? view-id anim-values]
(reagent/flush)
(animate-layout show? anim-values)
(animate-components show? view-id anim-values))
+30
View File
@@ -0,0 +1,30 @@
(ns status-im.switcher.bottom-tabs
(:require [quo.react-native :as rn]
[reagent.core :as reagent]
[status-im.switcher.styles :as styles]
[status-im.ui.components.icons.icons :as icons]))
(def selected-tab-id (reagent/atom :chats-stack))
(defn bottom-tab-pressed [tab-id]
(when-not (= tab-id @selected-tab-id)
(reset! selected-tab-id tab-id)))
;; TODO(parvesh) - reimplement tab with counter, once design is complete
;; (implement natively, for performance improvement)
(defn bottom-tab [icon tab-id]
[rn/touchable-opacity {:style {:padding 15}
:active-opacity 1
:on-press #(bottom-tab-pressed tab-id)}
[icons/icon icon (styles/bottom-tab-icon
(if (= tab-id @selected-tab-id)
:bottom-tabs-selected-tab
:bottom-tabs-non-selected-tab))]])
(defn bottom-tabs []
[rn/animated-view {:style (styles/bottom-tabs)}
[bottom-tab :main-icons/redesign-messages :chats-stack]
[bottom-tab :main-icons/communities :communities-stack]
[rn/view {:width 10}]
[bottom-tab :main-icons/redesign-wallet :wallet-stack]
[bottom-tab :main-icons/browser :browser-stack]])
@@ -0,0 +1,30 @@
(ns status-im.switcher.cards.messaging-card
(:require [quo.react-native :as rn]
[quo2.components.text :as text]
[status-im.constants :as constants]
[quo2.components.button :as button]
[status-im.utils.handlers :refer [>evt <sub]]
[status-im.switcher.cards.styles :as styles]))
(defn on-press [id toggle-switcher-screen]
(toggle-switcher-screen)
(>evt [:chat.ui/navigate-to-chat-nav2 id true]))
;; TODO - add last message for other content types
(defn last-message [{:keys [content content-type]}]
(cond
(= constants/content-type-text content-type)
[text/text (styles/messaging-card-last-message-text-props) (:text content)]))
(defn card [{:keys [id toggle-switcher-screen]}]
(let [chat (<sub [:chats/chat id])]
[rn/touchable-without-feedback {:on-press #(on-press id toggle-switcher-screen)}
[rn/view {:style (styles/messaging-card-main-container)}
[rn/view {:style (styles/messaging-card-secondary-container)}
[text/text (styles/messaging-card-title-props) (:alias chat)]
[text/text (styles/messaging-card-subtitle-props) "Message"]
[rn/view {:style (styles/messaging-card-details-container)}
[last-message (:last-message chat)]]]
[rn/view {:style (styles/messaging-card-avatar-container)}]
[button/button (styles/messaging-card-close-button-props) :main-icons/close]]]))
+103
View File
@@ -0,0 +1,103 @@
(ns status-im.switcher.cards.styles
(:require [quo.theme :as theme]
[quo2.foundations.colors :as colors]))
(def themes
{:light {:messaging-card-container-background-color "#26A69A"
:messaging-card-secondary-container-background-color colors/white
:messaging-card-title-color colors/black
:messaging-card-subtitle-color colors/neutral-50
:messaging-card-last-message-text-color colors/black
:messaging-card-close-button-bg-color colors/white-opa-50
:messaging-card-close-button-icon-color colors/black}
:dark {:messaging-card-container-background-color "#26A69A"
:messaging-card-secondary-container-background-color colors/neutral-90
:messaging-card-title-color colors/white
:messaging-card-subtitle-color colors/neutral-40
:messaging-card-last-message-text-color colors/white
:messaging-card-close-button-bg-color colors/neutral-80-opa-60
:messaging-card-close-button-icon-color colors/white}})
(defn get-color [key]
(get-in themes [(theme/get-theme) key]))
;; Messaging Card
(defn messaging-card-main-container []
{:width 160
:height 172
:border-radius 16
:margin 8
:background-color (get-color :messaging-card-container-background-color)})
(defn messaging-card-secondary-container []
{:width 160
:height 132
:background-color (get-color :messaging-card-secondary-container-background-color)
:border-radius 16
:position :absolute
:bottom 0})
(defn messaging-card-title []
{:position :absolute
:top 32
:margin-horizontal 12
:color (get-color :messaging-card-title-color)})
(defn messaging-card-title-props []
{:size :paragraph-1
:weight :semi-bold
:number-of-lines 1
:ellipsize-mode :tail
:style (messaging-card-title)})
(defn messaging-card-subtitle []
{:position :absolute
:top 54
:margin-horizontal 12
:color (get-color :messaging-card-subtitle-color)})
(defn messaging-card-subtitle-props []
{:size :paragraph-2
:weight :medium
:style (messaging-card-subtitle)})
(defn messaging-card-details-container []
{:position :absolute
:bottom 12
:margin-horizontal 12
:width 136
:height 36})
(defn messaging-card-last-message-text []
{:color (get-color :messaging-card-last-message-text-color)})
(defn messaging-card-last-message-text-props []
{:size :paragraph-2
:weight :regular
:number-of-lines 2
:ellipsize-mode :tail
:style (messaging-card-last-message-text)})
(defn messaging-card-close-button []
{:position :absolute
:right 8
:top 8
:background-color (get-color :messaging-card-close-button-bg-color)
:icon-color (get-color :messaging-card-icon-color)})
(defn messaging-card-close-button-props []
{:size 24
:type :grey
:icon true
:on-press #(print "close pressed")
:style (messaging-card-close-button)})
(defn messaging-card-avatar-container []
{:width 48
:height 48
:border-radius 24
:position :absolute
:left 12
:top 16
:background-color :pink})
+32
View File
@@ -0,0 +1,32 @@
(ns status-im.switcher.constants
(:require [status-im.utils.handlers :refer [<sub]]
[status-im.utils.platform :as platform]))
(def switcher-button-radius 24)
(def switcher-button-size
(* switcher-button-radius 2))
(def switcher-bottom-positions
{:android
{:home-stack 17
:chat 57}
:ios
{:home-stack 35
:chat 67}})
(defn switcher-bottom-position [view-id]
(get-in
switcher-bottom-positions
[(keyword platform/os) view-id]))
(defn switcher-center-position [view-id]
(+ (switcher-bottom-position view-id) (/ switcher-button-size 2)))
;; TODO(parvesh) - use different height for android and ios(Confirm from Design)
(defn bottom-tabs-height []
(if platform/android? 55 80))
;; TODO - move to switcher/utils.cljs
(defn dimensions []
(<sub [:dimensions/window]))
+97
View File
@@ -0,0 +1,97 @@
(ns status-im.switcher.styles
(:require [quo.theme :as theme]
[quo2.foundations.colors :as colors]
[status-im.switcher.constants :as constants]
[status-im.switcher.animation :as animation]))
(def themes
{:light {:bottom-tabs-bg-color colors/neutral-80
:bottom-tabs-on-scroll-bg-color colors/neutral-80-opa-80
:bottom-tabs-non-selected-tab colors/neutral-50
:bottom-tabs-selected-tab colors/white}
:dark {:bottom-tabs-bg-color colors/neutral-80
:bottom-tabs-on-scroll-bg-color colors/neutral-80-opa-60
:bottom-tabs-non-selected-tab colors/neutral-40
:bottom-tabs-selected-tab colors/white}})
(defn get-color [key]
(get-in themes [(theme/get-theme) key]))
;; Bottom Tabs
(defn bottom-tab-icon [tab-state]
{:width 20
:height 20
:color (get-color tab-state)})
(defn bottom-tabs []
{:background-color (get-color :bottom-tabs-bg-color)
:flex-direction :row
:flex 1
:align-items :center
:justify-content :space-around
:height (constants/bottom-tabs-height)
:position :absolute
:bottom 0
:right 0
:left 0
:opacity animation/bottom-tabs-opacity
:transform [{:translateY animation/bottom-tabs-position}]})
;; Switcher
(defn switcher-button [opacity]
{:width constants/switcher-button-size
:height constants/switcher-button-size
:opacity opacity})
(defn merge-switcher-button-common-styles [style]
(merge
{:width constants/switcher-button-size
:height constants/switcher-button-size
:border-radius constants/switcher-button-radius
:position :absolute
:bottom 0
:z-index 3
:align-items :center
:align-self :center
:justify-content :center}
style))
(defn switcher-button-touchable [view-id]
(merge-switcher-button-common-styles
{:align-self :center
:bottom (constants/switcher-bottom-position view-id)}))
(defn switcher-close-button-background [opacity]
(merge-switcher-button-common-styles
{:background-color colors/switcher-background
:opacity opacity}))
(defn switcher-close-button-icon [opacity]
(merge-switcher-button-common-styles
{:opacity opacity}))
(defn switcher-screen [view-id radius]
(let [bottom (- (constants/switcher-center-position view-id) radius)
size (* 2 radius)]
(merge-switcher-button-common-styles
{:background-color colors/switcher-background-opa-80
:bottom bottom
:border-radius 1000
:width size
:overflow :hidden
:height size})))
(defn switcher-screen-container [view-id radius]
(let [radius radius
bottom (- radius (constants/switcher-center-position view-id))
{:keys [width height]} (constants/dimensions)]
{:position :absolute
:align-self :center
:bottom bottom
:width width
:height (- height 25)
:align-items :center}))
(defn switcher-switch-screen []
{:margin-top 40
:align-items :center})
+44
View File
@@ -0,0 +1,44 @@
(ns status-im.switcher.switcher
(:require [quo.react-native :as rn]
[reagent.core :as reagent]
[status-im.switcher.styles :as styles]
[status-im.ui.components.animation :as anim]
[status-im.switcher.animation :as animation]
[status-im.ui.components.icons.icons :as icons]
[status-im.react-native.resources :as resources]
[status-im.switcher.switcher-container :as switcher-container]))
(defn toggle-switcher-screen [switcher-opened? view-id anim-values]
(swap! switcher-opened? not)
(animation/animate @switcher-opened? view-id anim-values))
(defn switcher-button [switcher-opened? view-id anim-values]
[rn/touchable-opacity {:active-opacity 1
:on-press #(toggle-switcher-screen switcher-opened? view-id anim-values)
:style (styles/switcher-button-touchable view-id)}
[rn/animated-view {:style (styles/switcher-close-button-background
(:switcher-close-button-background-opacity anim-values))}]
[rn/animated-view {:style (styles/switcher-close-button-icon
(:switcher-close-button-icon-opacity anim-values))}
[icons/icon :main-icons/close {:color :white}]]
[rn/animated-image-view {:source (resources/get-image :status-logo)
:style (styles/switcher-button
(:switcher-button-opacity anim-values))}]])
(defn switcher-screen [switcher-opened? view-id anim-values]
[rn/view {:style (styles/switcher-screen
view-id @(:switcher-screen-radius anim-values))
:pointer-events (if switcher-opened? :auto :none)}
[switcher-container/container
view-id @(:switcher-screen-radius anim-values)
#(toggle-switcher-screen switcher-opened? view-id anim-values)]])
(defn switcher [view-id]
(let [switcher-opened? (reagent/atom false)
anim-values {:switcher-button-opacity (anim/create-value 1)
:switcher-close-button-icon-opacity (anim/create-value 0)
:switcher-close-button-background-opacity (anim/create-value 0)
:switcher-screen-radius (reagent/atom 1)}]
[:<>
[switcher-screen switcher-opened? view-id anim-values]
[switcher-button switcher-opened? view-id anim-values]]))
@@ -0,0 +1,34 @@
(ns status-im.switcher.switcher-container
(:require [quo.react-native :as rn]
status-im.switcher.cards.messaging-card
[status-im.switcher.styles :as styles]
[status-im.utils.handlers :refer [<sub]]
[status-im.switcher.cards.messaging-card :as messaging-card]))
;; TODO - use something like this to avoid multiple renders etc.
;; (defn switch-screen [toggle-switcher-screen]
;; (let [cards (<sub [:navigation2/switcher-cards])
;; new-cards (reduce (fn [acc card]
;; (conj acc (assoc card :toggle-switcher-screen toggle-switcher-screen)))
;; () cards)]
;; (fn []
;; [rn/view {:style (styles/switcher-switch-screen)}
;; [rn/flat-list {:width 352
;; :data new-cards
;; :render-fn messaging-card/card
;; :num-columns 2
;; :key-fn str}]])))
(defn switch-screen [toggle-switcher-screen]
(let [cards (<sub [:navigation2/switcher-cards toggle-switcher-screen])]
[rn/view {:style (styles/switcher-switch-screen)}
[rn/flat-list {:width 352
:data cards
:render-fn messaging-card/card
:num-columns 2
:key-fn str}]]))
(defn container [view-id switcher-screen-radius toggle-switcher-screen]
[rn/view {:style (styles/switcher-screen-container
view-id switcher-screen-radius)}
[switch-screen toggle-switcher-screen]])
+6
View File
@@ -0,0 +1,6 @@
(ns status-im.switcher.utils
(:require [re-frame.core :as re-frame]))
(def switcher-container-view-id (atom nil))
(re-frame/reg-fx :switcher-container-view-id #(reset! switcher-container-view-id %))
+3 -1
View File
@@ -8,7 +8,8 @@
[status-im.utils.handlers :as handlers]
status-im.transport.shh
[taoensso.timbre :as log]
[status-im.utils.universal-links.core :as universal-links]))
[status-im.utils.universal-links.core :as universal-links]
[status-im.stickers.core :as stickers]))
(fx/defn set-node-info
{:events [:transport.callback/node-info-fetched]}
@@ -65,4 +66,5 @@
(add-mailservers mailservers))}
(fetch-node-info-fx)
(pairing/init)
(stickers/load-packs)
(universal-links/process-stored-event)))
+1 -3
View File
@@ -18,7 +18,6 @@
[status-im.utils.debounce :as debounce]
[status-im.utils.http :as http]
[status-im.utils.js-resources :as js-res]
[status-im.utils.contenthash :as contenthash]
[status-im.ui.components.permissions :as components.permissions]
[quo.core :as quo]
[status-im.ui.screens.wallet.components.views :as components]
@@ -58,8 +57,7 @@
(reagent/as-element
[react/view styles/web-view-error
[react/image {:style {:width 140 :height 140 :margin-bottom 16}
:source {:uri (contenthash/url
"e3010170122001bbe2f5bfba0305a3bdc2047fddc47ee595a591bdee61de6040ffc2456624e1")}}]
:source {:uri "https://bafybeiabxprplp52amc2hpocar753rd64wk2len55zq54yca77bekzre4e.ipfs.cf-ipfs.com"}}]
[react/i18n-text {:style styles/web-view-error-text :key :web-view-error}]
[react/text {:style styles/web-view-error-text}
(str desc)]]))
@@ -14,7 +14,6 @@
[status-im.ui.screens.chat.message.gap :as message.gap]
[status-im.ui.screens.chat.styles.message.message :as style]
[status-im.ui.screens.chat.utils :as chat.utils]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.security :as security]
[status-im.ui.screens.chat.message.reactions :as reactions]
[status-im.ui.screens.chat.image.preview.views :as preview]
@@ -558,7 +557,7 @@
:accessibility-label :sticker-message
:on-press (fn [_]
(when pack
(re-frame/dispatch [:stickers/open-sticker-pack pack]))
(re-frame/dispatch [:stickers/open-sticker-pack (str pack)]))
(react/dismiss-keyboard!))
:delay-long-press 100
:on-long-press (fn []
@@ -568,8 +567,7 @@
(re-frame/dispatch [:chat.ui/show-profile from]))
:label (i18n/label :t/view-details)}])))})
[react/fast-image {:style {:margin 10 :width 140 :height 140}
;;TODO (perf) move to event
:source {:uri (contenthash/url (-> content :sticker :hash))}}]]
:source {:uri (str (-> content :sticker :url) "&download=true")}}]]
reaction-picker]))
(defmethod ->message constants/content-type-image
@@ -2,14 +2,12 @@
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[status-im.stickers.core :as stickers]
[status-im.ui.components.react :as react]
[status-im.ui.components.icons.icons :as icons]
[quo.design-system.colors :as colors]
[status-im.i18n.i18n :as i18n]
[quo.core :as quo]
[status-im.ui.screens.chat.stickers.styles :as styles]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.debounce :as debounce]))
(def icon-size 28)
@@ -26,25 +24,23 @@
:height 64}]
[react/text {:style {:margin-vertical 8 :font-size 17}} (i18n/label :t/you-dont-have-stickers)]
[quo/button {:type :secondary
:on-press #(do
(re-frame/dispatch [:stickers/load-packs])
(re-frame/dispatch [:navigate-to :stickers]))}
:on-press #(re-frame/dispatch [:navigate-to :stickers])}
(i18n/label :t/get-stickers)]])
(defn- stickers-panel [stickers window-width]
[react/view {:width window-width :flex 1}
[react/scroll-view
[react/view {:style styles/stickers-panel}
(for [{:keys [hash] :as sticker} stickers]
^{:key (str hash)}
(for [{:keys [url] :as sticker} stickers]
^{:key (str url)}
[react/touchable-highlight {:style {:height 75 :width 75 :margin 5}
:on-press #(debounce/dispatch-and-chill [:chat/send-sticker sticker] 1000)}
[react/fast-image {:style {:width "100%" :height "100%"}
:accessibility-label :sticker-icon
:source {:uri (contenthash/url (str "0x" hash))}}]])]]])
:source {:uri (str url "&download=true")}}]])]]])
(defview recent-stickers-panel [window-width]
(letsubs [stickers [:stickers/recent]]
(letsubs [stickers [:stickers/recent-stickers]]
(if (seq stickers)
[stickers-panel stickers window-width]
[react/view {:style {:flex 1
@@ -98,7 +94,7 @@
[recent-stickers-panel width]
(for [{:keys [stickers id]} installed-packs]
^{:key (str "sticker" id)}
[stickers-panel (map #(assoc % :pack id) (filter stickers/valid-sticker? stickers)) width])]))
[stickers-panel (map #(assoc % :pack id) stickers) width])]))
(defn pack-icon [{:keys [id on-press background-color]
:or {on-press #(re-frame/dispatch [:stickers/select-pack id])}}
@@ -118,7 +114,7 @@
(defview stickers-view []
(letsubs [selected-pack [:stickers/selected-pack]
installed-packs [:stickers/installed-packs-vals]]
installed-packs [:stickers/installed-packs]]
[react/view {:style {:background-color colors/white
:flex 1}}
(cond
@@ -126,9 +122,7 @@
(not (seq installed-packs)) [no-stickers-yet-panel]
:else [stickers-paging-panel installed-packs selected-pack])
[react/view {:style {:flex-direction :row :padding-horizontal 4}}
[pack-icon {:on-press #(do
(re-frame/dispatch [:stickers/load-packs])
(re-frame/dispatch [:navigate-to :stickers]))
[pack-icon {:on-press #(re-frame/dispatch [:navigate-to :stickers])
:selected? false :background-color colors/blue}
[icons/icon :main-icons/add {:width 20 :height 20 :color colors/white-persist}]]
[react/view {:width 2}]
@@ -140,9 +134,9 @@
:width 44
:height 44}]]
(for [{:keys [id thumbnail]} installed-packs]
^{:key id}
^{:key (str "pack-icon" id)}
[pack-icon {:id id
:background-color colors/white}
[react/fast-image {:style {:width icon-size :height icon-size :border-radius (/ icon-size 2)}
:source {:uri (contenthash/url thumbnail)}}]])]
:source {:uri (str thumbnail "&download=true")}}]])]
[scroll-indicator]]]]]))
@@ -123,6 +123,7 @@
:accessibility-label :appearance-settings-button
:chevron true
:on-press #(re-frame/dispatch [:navigate-to :quo2-preview])}])
;; :on-press #(re-frame/dispatch [:init-root-nav2 :home-stack])}])
[quo/list-item
{:icon :main-icons/appearance
:title (i18n/label :t/appearance)
+6 -1
View File
@@ -121,7 +121,9 @@
[status-im.ui.screens.wallet.recipient.views :as recipient]
[status-im.ui.screens.wallet.send.views :as wallet.send]
[quo2.screens.main :as quo2.preview]
[status-im.utils.config :as config]))
[status-im.utils.config :as config]
[status-im.navigation2.screens :as navigation2.screens]))
;[status-im.navigation2.stack-with-switcher :as stack-with-switcher]))
;[quo2.foundations.colors :as quo2.colors]))
(def components
@@ -229,6 +231,7 @@
:rightButtons (right-button-options :chat :more)}}
:right-handler chat/topbar-button
:component chat/chat}
;; :component #(stack-with-switcher/overlap-stack chat/chat :chat)}
;Pinned messages
{:name :chat-pinned-messages
@@ -914,5 +917,7 @@
quo.preview/main-screens)
(when config/quo-preview-enabled?
quo2.preview/screens)
(when config/quo-preview-enabled?
navigation2.screens/screens)
(when config/quo-preview-enabled?
quo2.preview/main-screens)))
+4 -7
View File
@@ -15,7 +15,6 @@
[status-im.ui.screens.chat.image.preview.views :as preview]
[status-im.ui.screens.chat.photos :as photos]
[status-im.ui.components.tabs :as tabs]
[status-im.utils.contenthash :as contenthash]
[status-im.ui.screens.chat.message.reactions :as reactions]
[status-im.chat.models.reactions :as models.reactions]
[status-im.ui.screens.chat.components.reply :as components.reply]
@@ -175,8 +174,6 @@
[tabs/tab-title state :timeline (i18n/label :t/timeline) (= tab :timeline)]
[tabs/tab-title state :status (i18n/label :t/my-status) (= tab :status)]]))
(def image-hash "e3010170122080c27fe972a95dbb4b0ead029d2c73d18610e849fac197e91068a918755e21b2")
(defn timeline []
(let [messages @(re-frame/subscribe [:chats/timeline-messages-stream])
loading-messages? @(re-frame/subscribe [:chats/loading-messages? constants/timeline-chat-id])
@@ -191,10 +188,10 @@
(if no-messages?
[react/view {:padding-horizontal 32
:margin-top 64}
[react/image {:style {:width 140
:height 140
:align-self :center}
:source {:uri (contenthash/url image-hash)}}]
[react/fast-image {:style {:width 140
:height 140
:align-self :center}
:source {:uri "https://bafybeieayj76s4vjlw5uwdvnakosy46rqyioqsp2ygl6sedivemhkxrbwi.ipfs.cf-ipfs.com"}}]
[react/view (styles/descr-container)
[react/text {:style {:color colors/gray
:line-height 22}}
+46 -47
View File
@@ -5,54 +5,56 @@
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react]
[status-im.ui.screens.stickers.styles :as styles]
[status-im.utils.contenthash :as contenthash]
[status-im.utils.money :as money])
[status-im.utils.money :as money]
[status-im.utils.handlers :refer [<sub]])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defn cache [url]
(str url "&download=true"))
(defn- thumbnail-icon [uri size]
[react/fast-image {:style {:width size :height size :border-radius (/ size 2)}
:source {:uri uri}}])
:source {:uri (cache uri)}}])
(defn- installed-icon []
[react/view styles/installed-icon
[icons/icon :main-icons/check {:color colors/white-persist :height 20 :width 20}]])
(defn price-badge [_]
(let [chain (<sub [:ethereum/chain-keyword])
balance (<sub [:balance-default])]
(fn [{:keys [id price pending owned installed]}]
(let [snt (if (= :mainnet chain) (:SNT balance) (:STT balance))
price (money/bignumber price)
not-enough-snt? (or (nil? snt) (money/equal-to snt 0) (money/greater-than price snt))
free (money/equal-to price 0)]
(if installed
[react/view styles/installed-icon
[icons/icon :main-icons/check {:color colors/white-persist :height 20 :width 20}]]
[react/touchable-highlight {:on-press #(cond pending nil
(or owned free)
(re-frame/dispatch [:stickers/install-pack id])
not-enough-snt? nil
:else (re-frame/dispatch [:stickers/buy-pack id]))}
[react/view (styles/price-badge (and (not (or owned free)) not-enough-snt?))
(when (and (not free) (not owned))
[icons/tiny-icon :tiny-icons/tiny-snt {:color colors/white-persist :container-style {:margin-right 6}}])
(if pending
[react/activity-indicator {:animating true
:color colors/white-persist}]
[react/text {:style {:color colors/white-persist}
:accessibility-label :sticker-pack-price}
(cond owned (i18n/label :t/install)
free (i18n/label :t/free)
:else (str (money/wei-> :eth price)))])]])))))
(defview price-badge [price id owned? pending]
(letsubs [chain [:ethereum/chain-keyword]
balance [:balance-default]]
(let [snt (money/to-number (if (= :mainnet chain) (:SNT balance) (:STT balance)))
not-enough-snt? (> price snt)
no-snt? (or (nil? snt) (zero? snt))]
[react/touchable-highlight {:on-press #(cond pending nil
(or owned? (zero? price))
(re-frame/dispatch [:stickers/install-pack id])
(or no-snt? not-enough-snt?) nil
:else (re-frame/dispatch [:stickers/buy-pack id price]))}
[react/view (styles/price-badge (and (not (or owned? (zero? price))) (or no-snt? not-enough-snt?)))
(when (and (not (zero? price)) (not owned?))
[icons/tiny-icon :tiny-icons/tiny-snt {:color colors/white-persist :container-style {:margin-right 6}}])
(if pending
[react/activity-indicator {:animating true
:color colors/white-persist}]
[react/text {:style {:color colors/white-persist}
:accessibility-label :sticker-pack-price}
(cond owned? (i18n/label :t/install)
(zero? price) (i18n/label :t/free)
:else (str (money/wei-> :eth price)))])]])))
(defn pack-badge [{:keys [name author price thumbnail preview id installed owned pending]}]
(defn pack-badge [{:keys [name author thumbnail preview id] :as pack}]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :stickers-pack {:id id}])}
[react/view {:margin-bottom 27}
[react/fast-image {:style {:height 200 :border-radius 20} :source {:uri (contenthash/url preview)}}]
[react/fast-image {:style {:height 200 :border-radius 20} :source {:uri (cache preview)}}]
[react/view {:height 64 :align-items :center :flex-direction :row}
[thumbnail-icon (contenthash/url thumbnail) 40]
[thumbnail-icon thumbnail 40]
[react/view {:padding-horizontal 16 :flex 1}
[react/text {:accessibility-label :sticker-pack-name} name]
[react/text {:style {:color colors/gray :margin-top 6}
[react/text {:style {:color colors/gray :margin-top 6}
:accessibility-label :sticker-pack-author} author]]
(if installed
[installed-icon]
[price-badge price id owned pending])]]])
[price-badge pack]]]])
(defview packs []
(letsubs [packs [:stickers/all-packs]]
@@ -70,28 +72,25 @@
(def sticker-icon-size 60)
(defview pack-main []
(letsubs [{:keys [id name author price thumbnail
stickers installed owned pending]
:as pack}
(letsubs [{:keys [name author thumbnail stickers]
:as pack}
[:stickers/get-current-pack]]
[react/keyboard-avoiding-view {:flex 1}
(if pack
[react/view {:flex 1}
[react/view {:height 74 :align-items :center :flex-direction :row :padding-horizontal 16}
[thumbnail-icon (contenthash/url thumbnail) 64]
[thumbnail-icon thumbnail 64]
[react/view {:padding-horizontal 16 :flex 1}
[react/text {:style {:typography :header}} name]
[react/text {:style {:color colors/gray :margin-top 6}} author]]
(if installed
[installed-icon]
[price-badge price id owned pending])]
[price-badge pack]]
[react/view {:style {:padding-top 8 :flex 1}}
[react/scroll-view {:keyboard-should-persist-taps :handled :style {:flex 1}}
[react/view {:flex-direction :row :flex-wrap :wrap}
(for [{:keys [hash]} stickers]
^{:key hash}
[react/fast-image {:style (styles/sticker-image sticker-icon-size)
:source {:uri (contenthash/url hash)}}])]]]]
(for [{:keys [url]} stickers]
^{:key url}
[react/fast-image {:style (styles/sticker-image sticker-icon-size)
:source {:uri (cache url)}}])]]]]
[react/view {:flex 1 :align-items :center :justify-content :center}
[react/activity-indicator {:animating true}]])]))
+1
View File
@@ -54,6 +54,7 @@
(def test-stateofus? (enabled? (get-config :TEST_STATEOFUS "0")))
(def two-minutes-syncing? (enabled? (get-config :TWO_MINUTES_SYNCING "0")))
(def swap-enabled? (enabled? (get-config :SWAP_ENABLED "0")))
(def stickers-test-enabled? (enabled? (get-config :STICKERS_TEST_ENABLED "0")))
;; CONFIG VALUES
(def log-level
-48
View File
@@ -1,48 +0,0 @@
(ns status-im.utils.contenthash
"TODO: currently we only support encoding/decoding ipfs contenthash
implementing swarm and other protocols will come later"
(:refer-clojure :exclude [cat])
(:require ["hi-base32" :as hi-base32]
[alphabase.hex :as hex]
[clojure.string :as string]
[status-im.ethereum.core :as ethereum]))
(defn decode
"TODO properly decode the CID
extract the content-type using varint ns"
[hex]
(when (and hex (not= hex "0x"))
(cond
(and (string/starts-with? hex "0xe40101"))
;; content type can be 2 or 4 bytes
;; we expect 1b20 (hash algorithm keccak256 and hash length 64)
;; before the hash so we split the contenthash there
(when-let [hash (second (string/split hex "1b20"))]
{:namespace :swarm
:hash hash})
(and (= 78 (count hex))
(string/starts-with? hex "0xe3010170"))
{:namespace :ipfs
:hash (str "b" (-> hex
(subs 6)
hex/decode
((fn [v] (.encode ^js hi-base32 v)))
(string/replace #"=" "")
string/lower-case))}
(and (string/starts-with? hex "0xe50101700"))
{:namespace :ipns
:hash (-> hex
(subs 14)
((fn [v] (str "0x" v)))
(ethereum/hex-to-utf8))})))
(defn ipfs-url [hash]
(str "https://" hash ".ipfs.cf-ipfs.com"))
(defn url-fn [hex]
(let [{:keys [namespace hash]} (decode (ethereum/normalized-hex hex))]
(case namespace
:ipfs (ipfs-url hash)
"")))
(def url (memoize url-fn))
-26
View File
@@ -1,26 +0,0 @@
(ns status-im.utils.contenthash-test
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.contenthash :as contenthash]))
;; we reuse the exemple from EIP-1577 but omit content type: dag-pb (0x70)
;; in the contenthash as we do not support it yet
(deftest contenthash-decode
(testing "decoding a valid ipfs hash"
(is (= (contenthash/decode "0xe3010170122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f")
{:namespace :ipfs
:hash "bafybeibj6lixxzqtsb45ysdjnupvqkufgdvzqbnvmhw2kf7cfkesy7r7d4"})))
(testing "decoding a valid ipns hash"
(is (= (contenthash/decode "0xe5010170000f6170702e756e69737761702e6f7267")
{:namespace :ipns
:hash "app.uniswap.org"})))
(testing "decoding a valid swarm hash"
(is (= (contenthash/decode "0xe40101fa011b20d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162")
{:namespace :swarm
:hash "d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162"})))
(testing "decoding an invalid ipfs hash"
(is (nil? (contenthash/decode "0xe301122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e2"))))
(testing "decoding random garbage"
(is (nil? (contenthash/decode "0xabcdef1234567890"))))
(testing "decoding nil"
(is (nil? (contenthash/decode nil)))))
+4
View File
@@ -41,6 +41,10 @@
[bn1 bn2]
(.greaterThan ^js bn1 bn2))
(defn equal-to
[bn1 bn2]
(.eq ^js bn1 bn2))
(defn sub [bn1 bn2]
(.sub ^js bn1 bn2))
+16 -40
View File
@@ -1,45 +1,21 @@
(ns status-im.utils.prices
(:require [clojure.string :as string]
[status-im.utils.http :as http]
[status-im.utils.types :as types]))
(:require [status-im.ethereum.json-rpc :as json-rpc]))
;; Responsible for interacting with Cryptocompare API to get current prices for
;; currencies and tokens.
;;
;; No tests since fetch API (via http-get) relies on `window` being available.
;;
;; Example usage:
;; (get-prices "ETH" "USD" println print)
(def api-url "https://min-api.cryptocompare.com/data")
(def status-identifier "extraParams=Status.im")
(defn- ->url-param-syms [syms]
((comp (partial string/join ",") (partial map name)) syms))
(defn- gen-price-url [fsyms tsyms]
(str api-url "/pricemultifull?fsyms=" (->url-param-syms fsyms) "&tsyms=" (->url-param-syms tsyms) "&" status-identifier))
(defn- format-price-resp [resp mainnet?]
;;NOTE(this check is to allow value conversion for sidechains with native currencies listed on cryptocompare
;; under a symbol different than display symbol. Specific use case xDAI.
(defn- format-price-resp [resp to mainnet?]
(if mainnet?
(when-let [RAW (:RAW (types/json->clj resp))]
(into {} (for [[from entries] RAW]
{from (into {} (for [[to entry] entries]
{to {:from (name from)
:to (name to)
:price (:PRICE entry)
:last-day (:OPEN24HOUR entry)}}))})))
(into {} (for [[_ entries] (:RAW (types/json->clj resp))]
{:ETH (into {} (for [[to entry] entries]
{to {:from "ETH"
:to (name to)
:price (:PRICE entry)
:last-day (:OPEN24HOUR entry)}}))}))))
(when (seq resp)
(into {} (for [[from price] resp]
{from {(keyword to) {:from (name from)
:to to
:price price}}})))
{:ETH (into {} (for [[_ price] resp]
{(keyword to) {:from "ETH"
:to to
:price price}}))}))
(defn get-prices [from to mainnet? on-success on-error]
(http/get
(gen-price-url from to)
(fn [resp] (on-success (format-price-resp resp mainnet?)))
on-error))
(let [to (first to)]
(json-rpc/call {:method "wallet_fetchPrices"
:params [from to]
:on-success (fn [resp] (on-success (format-price-resp resp to mainnet?)))
:on-error on-error})))
-14
View File
@@ -1,14 +0,0 @@
(ns status-im.utils.prices-test
(:require [cljs.test :refer-macros [deftest is]]
[status-im.utils.prices :as prices]))
(deftest test-format-price-resp-mainnet
(is (= (#'status-im.utils.prices/format-price-resp
"{\"RAW\":{\"ETH\":{\"USD\":{\"TYPE\":\"5\",\"MARKET\":\"CCCAGG\",\"FROMSYMBOL\":\"ETH\",\"TOSYMBOL\":\"USD\",\"FLAGS\":\"4\",\"PRICE\":677.91,\"LASTUPDATE\":1525241472,\"LASTVOLUME\":0.69661268,\"LASTVOLUMETO\":472.0874471092,\"LASTTRADEID\":\"1525241472.6714\",\"VOLUMEDAY\":109859.96594606241,\"VOLUMEDAYTO\":73924561.52673237,\"VOLUME24HOUR\":408811.69032152055,\"VOLUME24HOURTO\":270617692.0100031,\"OPENDAY\":670.81,\"HIGHDAY\":681.06,\"LOWDAY\":662.13,\"OPEN24HOUR\":658.68,\"HIGH24HOUR\":681.67,\"LOW24HOUR\":638.25,\"LASTMARKET\":\"Kraken\",\"CHANGE24HOUR\":19.230000000000018,\"CHANGEPCT24HOUR\":2.919475314264896,\"CHANGEDAY\":7.100000000000023,\"CHANGEPCTDAY\":1.0584219078427608,\"SUPPLY\":99176837.749,\"MKTCAP\":67232970078.42458,\"TOTALVOLUME24H\":1308785.4697758215,\"TOTALVOLUME24HTO\":880718916.8398683}},\"SNT\":{\"USD\":{\"TYPE\":\"5\",\"MARKET\":\"CCCAGG\",\"FROMSYMBOL\":\"SNT\",\"TOSYMBOL\":\"USD\",\"FLAGS\":\"1\",\"PRICE\":0.1562,\"LASTUPDATE\":1525241236,\"LASTVOLUME\":582.07218,\"LASTVOLUMETO\":90.919674516,\"LASTTRADEID\":\"237582608\",\"VOLUMEDAY\":259125.16382211001,\"VOLUMEDAYTO\":40450.5318867195,\"VOLUME24HOUR\":1451022.9449243098,\"VOLUME24HOURTO\":223465.71221695316,\"OPENDAY\":0.1573,\"HIGHDAY\":0.1592,\"LOWDAY\":0.1532,\"OPEN24HOUR\":0.15,\"HIGH24HOUR\":0.1657,\"LOW24HOUR\":0.1481,\"LASTMARKET\":\"Bitfinex\",\"CHANGE24HOUR\":0.006200000000000011,\"CHANGEPCT24HOUR\":4.133333333333341,\"CHANGEDAY\":-0.0010999999999999899,\"CHANGEPCTDAY\":-0.6993006993006928,\"SUPPLY\":6804870174.87817,\"MKTCAP\":1062920721.3159702,\"TOTALVOLUME24H\":239970152.87898657,\"TOTALVOLUME24HTO\":37480153.807917476}}},\"DISPLAY\":{\"ETH\":{\"USD\":{\"FROMSYMBOL\":\"Ξ\",\"TOSYMBOL\":\"$\",\"MARKET\":\"CryptoCompare Index\",\"PRICE\":\"$ 677.91\",\"LASTUPDATE\":\"Just now\",\"LASTVOLUME\":\"Ξ 0.6966\",\"LASTVOLUMETO\":\"$ 472.09\",\"LASTTRADEID\":\"1525241472.6714\",\"VOLUMEDAY\":\"Ξ 109,860.0\",\"VOLUMEDAYTO\":\"$ 73,924,561.5\",\"VOLUME24HOUR\":\"Ξ 408,811.7\",\"VOLUME24HOURTO\":\"$ 270,617,692.0\",\"OPENDAY\":\"$ 670.81\",\"HIGHDAY\":\"$ 681.06\",\"LOWDAY\":\"$ 662.13\",\"OPEN24HOUR\":\"$ 658.68\",\"HIGH24HOUR\":\"$ 681.67\",\"LOW24HOUR\":\"$ 638.25\",\"LASTMARKET\":\"Kraken\",\"CHANGE24HOUR\":\"$ 19.23\",\"CHANGEPCT24HOUR\":\"2.92\",\"CHANGEDAY\":\"$ 7.10\",\"CHANGEPCTDAY\":\"1.06\",\"SUPPLY\":\"Ξ 99,176,837.7\",\"MKTCAP\":\"$ 67.23 B\",\"TOTALVOLUME24H\":\"Ξ 1,308.79 K\",\"TOTALVOLUME24HTO\":\"$ 880.72 M\"}},\"SNT\":{\"USD\":{\"FROMSYMBOL\":\"SNT\",\"TOSYMBOL\":\"$\",\"MARKET\":\"CryptoCompare Index\",\"PRICE\":\"$ 0.1562\",\"LASTUPDATE\":\"3 min ago\",\"LASTVOLUME\":\"SNT 582.07\",\"LASTVOLUMETO\":\"$ 90.92\",\"LASTTRADEID\":\"237582608\",\"VOLUMEDAY\":\"SNT 259,125.2\",\"VOLUMEDAYTO\":\"$ 40,450.5\",\"VOLUME24HOUR\":\"SNT 1,451,022.9\",\"VOLUME24HOURTO\":\"$ 223,465.7\",\"OPENDAY\":\"$ 0.1573\",\"HIGHDAY\":\"$ 0.1592\",\"LOWDAY\":\"$ 0.1532\",\"OPEN24HOUR\":\"$ 0.1500\",\"HIGH24HOUR\":\"$ 0.1657\",\"LOW24HOUR\":\"$ 0.1481\",\"LASTMARKET\":\"Bitfinex\",\"CHANGE24HOUR\":\"$ 0.0062\",\"CHANGEPCT24HOUR\":\"4.13\",\"CHANGEDAY\":\"$ -0.0011\",\"CHANGEPCTDAY\":\"-0.70\",\"SUPPLY\":\"SNT 6,804,870,174.9\",\"MKTCAP\":\"$ 1,062.92 M\",\"TOTALVOLUME24H\":\"SNT 239.97 M\",\"TOTALVOLUME24HTO\":\"$ 37.48 M\"}}}}" true)
{:ETH {:USD {:from "ETH", :to "USD", :price 677.91, :last-day 658.68}}
:SNT {:USD {:from "SNT", :to "USD", :price 0.1562, :last-day 0.15}}})))
(deftest test-format-price-resp-non-mainnet
(is (= (#'status-im.utils.prices/format-price-resp
"{\"RAW\":{\"SNT\":{\"USD\":{\"TYPE\":\"5\",\"MARKET\":\"CCCAGG\",\"FROMSYMBOL\":\"SNT\",\"TOSYMBOL\":\"USD\",\"FLAGS\":\"4\",\"PRICE\":0.03511847069999999,\"LASTUPDATE\":1539953131,\"LASTVOLUME\":0,\"LASTVOLUMETO\":0,\"LASTTRADEID\":0,\"VOLUMEDAY\":0,\"VOLUMEDAYTO\":0,\"VOLUME24HOUR\":45900.43172688,\"VOLUME24HOURTO\":1645.4590651064077,\"OPENDAY\":0.0357005448,\"HIGHDAY\":0.0358298946,\"LOWDAY\":0.034924446,\"OPEN24HOUR\":0.0360239193,\"HIGH24HOUR\":0.03621794399999999,\"LOW24HOUR\":0.0348597711,\"LASTMARKET\":\"IDAX\",\"CHANGE24HOUR\":-0.0009054486000000112,\"CHANGEPCT24HOUR\":-2.5134649910233704,\"CHANGEDAY\":-0.0005820741000000101,\"CHANGEPCTDAY\":-1.630434782608724,\"SUPPLY\":6804870174.87817,\"MKTCAP\":238976633.8537628,\"TOTALVOLUME24H\":53502856.59548309,\"TOTALVOLUME24HTO\":1878972.0078131626}}},\"DISPLAY\":{\"SNT\":{\"USD\":{\"FROMSYMBOL\":\"SNT\",\"TOSYMBOL\":\"$\",\"MARKET\":\"CryptoCompare Index\",\"PRICE\":\"$ 0.03512\",\"LASTUPDATE\":\"Just now\",\"LASTVOLUME\":\"SNT 0\",\"LASTVOLUMETO\":\"$ 0\",\"LASTTRADEID\":0,\"VOLUMEDAY\":\"SNT 0\",\"VOLUMEDAYTO\":\"$ 0\",\"VOLUME24HOUR\":\"SNT 45,900.4\",\"VOLUME24HOURTO\":\"$ 1,645.46\",\"OPENDAY\":\"$ 0.03570\",\"HIGHDAY\":\"$ 0.03583\",\"LOWDAY\":\"$ 0.03492\",\"OPEN24HOUR\":\"$ 0.03602\",\"HIGH24HOUR\":\"$ 0.03622\",\"LOW24HOUR\":\"$ 0.03486\",\"LASTMARKET\":\"IDAX\",\"CHANGE24HOUR\":\"$ -0.00091\",\"CHANGEPCT24HOUR\":\"-2.51\",\"CHANGEDAY\":\"$ -0.00058\",\"CHANGEPCTDAY\":\"-1.63\",\"SUPPLY\":\"SNT 6,804,870,174.9\",\"MKTCAP\":\"$ 238.98 M\",\"TOTALVOLUME24H\":\"SNT 53.50 M\",\"TOTALVOLUME24HTO\":\"$ 1,878.97 K\"}}}}" false)
{:ETH {:USD {:from "ETH", :to "USD", :price 0.03511847069999999, :last-day 0.0360239193}}})))
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "v0.98.5",
"commit-sha1": "640793fe85d9a9eef9eb3712cda1c5a1ceea401a",
"src-sha256": "1k1iw69yw23k29y0b1yvnggjqlmfnwavk4mq1sf2h3wrlb8x47kd"
"version": "v0.98.6",
"commit-sha1": "0048aaebcc7859a6f0dd7cdf0266fe029f3066fc",
"src-sha256": "1px3ddfbpnlqyzkbp8v4bb8dh5f69cx4lwif8vspw60rr4gpppys"
}
+50 -23
View File
@@ -1,13 +1,16 @@
import json
import requests
import logging
import itertools
import emoji
import base64
from os import environ
from support.base_test_report import BaseTestReport
from sys import argv
import itertools
import json
import logging
import re
from json import JSONDecodeError
from os import environ
from sys import argv
import emoji
import requests
from support.base_test_report import BaseTestReport
class TestrailReport(BaseTestReport):
@@ -114,11 +117,16 @@ class TestrailReport(BaseTestReport):
test_cases['pr']['1_1_chat_commands'] = 50825
test_cases['pr']['ens'] = 50827
test_cases['pr']['sync'] = 50834
test_cases['pr']['browser'] = 50812
## Nightly e2e
test_cases['nightly']['medium'] = 736
test_cases['nightly']['chat'] = 50811
test_cases['nightly']['browser'] = 50826
test_cases['nightly']['profile'] = 50828
test_cases['nightly']['deep_link'] = 50836
test_cases['nightly']['share_profile'] = 50837
test_cases['nightly']['chat_2'] = 50838
test_cases['nightly']['group_chat'] = 50839
## Upgrade e2e
test_cases['upgrade']['general'] = 881
@@ -146,10 +154,11 @@ class TestrailReport(BaseTestReport):
return case_ids
def add_results(self):
for test in self.get_all_tests():
data = list()
all_tests = self.get_all_tests()
for test in all_tests:
test_steps = "# Steps: \n"
devices = str()
method = 'add_result_for_case/%s/%s' % (self.run_id, test.testrail_case_id)
last_testrun = test.testruns[-1]
for step in last_testrun.steps:
test_steps += step + "\n"
@@ -166,21 +175,39 @@ class TestrailReport(BaseTestReport):
comment += '%s' % ('# Error: \n %s \n' % emoji.demojize(last_testrun.error)) + devices + test_steps
else:
comment += devices + test_steps
data = {'status_id': self.outcomes['undefined_fail'] if last_testrun.error else self.outcomes['passed'],
'comment': comment}
result = self.post(method, data=data)
try:
result_id = result['id']
except KeyError:
result_id = ''
print("Got TestRail error when adding results for case %s: \n%s" % (test.testrail_case_id, result))
data.append(
{'case_id': test.testrail_case_id,
'status_id': self.outcomes['undefined_fail'] if last_testrun.error else self.outcomes['passed'],
'comment': comment})
results = self.post('add_results_for_cases/%s' % self.run_id, data={"results": data})
try:
results[0]
except IndexError:
print("Got TestRail error when adding results: \n%s" % results)
for test in all_tests:
last_testrun = test.testruns[-1]
if last_testrun.error:
try:
for geth in test.geth_paths.keys():
self.add_attachment(method='add_attachment_to_result/%s' % str(result_id),
path=test.geth_paths[geth])
except AttributeError:
pass
device = list(last_testrun.jobs.keys())[0]
except IndexError:
continue
for res in results:
if last_testrun.first_commands:
pattern = r"%s\?auth=.*#%s" % (device, str(last_testrun.first_commands[device]))
else:
pattern = device
if re.findall(pattern, res['comment']):
res_id = res['id']
try:
for geth in test.geth_paths.keys():
self.add_attachment(method='add_attachment_to_result/%s' % str(res_id),
path=test.geth_paths[geth])
except AttributeError:
pass
break
self.change_test_run_description()
def change_test_run_description(self):
+1 -1
View File
@@ -81,7 +81,7 @@ def get_capabilities_sauce_lab():
desired_caps['deviceName'] = 'Android GoogleAPI Emulator'
desired_caps['deviceOrientation'] = "portrait"
desired_caps['commandTimeout'] = 600
desired_caps['idleTimeout'] = 800
desired_caps['idleTimeout'] = 1000
desired_caps['unicodeKeyboard'] = True
desired_caps['automationName'] = 'UiAutomator2'
desired_caps['setWebContentDebuggingEnabled'] = True
+10
View File
@@ -216,6 +216,16 @@ def should_save_device_stats(config):
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
if "xdist_group" in item.keywords._markers and report.when == "setup" and report.failed:
test_suite_data.set_current_test(item.name, testrail_case_id=get_testrail_case_id(item))
test_suite_data.current_test.create_new_testrun()
test_suite_data.current_test.group_name = item.instance.__class__.__name__
error = report.longreprtext
exception = re.findall('E.*Message:|E.*Error:|E.*Failed:', error)
if exception:
error = error.replace(re.findall('E.*Message:|E.*Error:|E.*Failed:', report.longreprtext)[0], '')
test_suite_data.current_test.testruns[-1].error = "Test setup failed: \n" + error
github_report.save_test(test_suite_data.current_test)
if report.when == 'call':
is_sauce_env = item.config.getoption('env') == 'sauce'
current_test = test_suite_data.current_test
@@ -219,14 +219,62 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase):
cls.home_1 = cls.device_1.create_user(enable_notifications=True)
cls.home_2 = cls.device_2.create_user(enable_notifications=True)
cls.profile_1 = cls.home_1.profile_button.click()
cls.default_username_1 = cls.profile_1.default_username_text.text
cls.public_key_1, cls.default_username_1 = cls.profile_1.get_public_key_and_username(return_username=True)
cls.profile_1.home_button.click()
cls.profile_2 = cls.home_2.profile_button.click()
cls.default_username_2 = cls.profile_2.default_username_text.text
cls.profile_2.home_button.click()
cls.public_key_2, cls.default_username_2 = cls.home_2.get_public_key_and_username(return_username=True)
cls.chat_1 = cls.home_1.add_contact(cls.public_key_2)
cls.chat_1.send_message('hey')
cls.home_2.home_button.double_click()
cls.chat_2 = cls.home_2.get_chat(cls.default_username_1).click()
@marks.testrail_id(702267)
def test_1_1_chat_unread_counter_preview_highlited(self):
self.chat_2.get_back_to_home_view()
message_1, message_2 = 'test message2', 'test'
chat_element = self.home_2.get_chat(self.default_username_1)
self.home_2.dapp_tab_button.click()
self.chat_1.send_message(message_1)
if self.home_2.home_button.counter.text != '1':
self.errors.append('New messages counter is not shown on Home button')
self.device_2.home_button.click()
if chat_element.new_messages_counter.text != '1':
self.errors.append('New messages counter is not shown on chat element')
chat_2 = chat_element.click()
chat_2.add_to_contacts.click()
self.home_2.home_button.double_click()
if self.home_2.home_button.counter.is_element_displayed():
self.errors.append('New messages counter is shown on Home button for already seen message')
if chat_element.new_messages_counter.text == '1':
self.errors.append('New messages counter is shown on chat element for already seen message')
self.home_2.delete_chat_long_press(self.default_username_1)
self.home_2.just_fyi("Checking preview of message and chat highlighting")
self.chat_1.send_message(message_2)
chat_2_element = self.home_2.get_chat(self.default_username_1)
if chat_2_element.chat_preview.is_element_differs_from_template('highligted_preview.png', 0):
self.errors.append("Preview message is not hightligted or text is not shown! ")
self.home_2.get_chat(self.default_username_1).click()
self.home_2.home_button.double_click()
if not self.home_2.get_chat(self.default_username_1).chat_preview.is_element_differs_from_template(
'highligted_preview.png', 0):
self.errors.append("Preview message is still highlighted after opening ")
self.home_2.just_fyi("Removing user from contacts for the case 5315")
chat_element.click()
chat_2.chat_options.click()
chat_2.view_profile_button.click()
chat_2.remove_from_contacts.click()
chat_2.get_back_to_home_view()
self.errors.verify_no_errors()
@marks.testrail_id(6315)
def test_1_1_chat_message_reaction(self):
message_from_sender = "Message sender"
@@ -238,6 +286,7 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase):
self.errors.append("Counter of reaction is not updated on your own message!")
self.device_2.just_fyi("Receiver set own emoji and verifies counter on received message in 1-1 chat")
self.home_2.get_chat(self.default_username_1).click()
message_receiver = self.chat_2.chat_element_by_text(message_from_sender)
if message_receiver.emojis_below_message(own=False) != 1:
self.errors.append("Counter of reaction is not updated on received message!")
@@ -527,7 +576,7 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase):
self.chat_2.share_via_messenger()
if not self.chat_2.image_in_android_messenger.is_element_present():
self.errors.append("Can't share image")
self.chat_2.click_system_back_button()
self.chat_2.click_system_back_button_until_element_is_shown()
self.chat_2.save_image_icon_button.click()
self.chat_2.show_images_button.click()
self.chat_2.allow_button.wait_and_click()
@@ -539,6 +588,7 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase):
@marks.testrail_id(5310)
def test_1_1_chat_is_shown_message_sent_delivered_from_offline(self):
self.home_1.home_button.click()
self.home_2.home_button.click()
@@ -1007,6 +1057,7 @@ class TestEnsStickersMultipleDevicesMerged(MultipleSharedDeviceTestCase):
send_transaction.ok_got_it_button.click()
send_transaction.sign_transaction()
chat_1_sender_message = self.chat_1.get_outgoing_transaction(transaction_value=amount)
self.home_2.put_app_to_background_and_back()
self.network_api.wait_for_confirmation_of_transaction(self.sender['address'], amount, confirmations=3)
chat_1_sender_message.transaction_status.wait_for_element_text(chat_1_sender_message.confirmed)
@@ -19,8 +19,10 @@ class TestGroupChatMultipleDeviceMerged(MultipleSharedDeviceTestCase):
for key in cls.drivers:
sign_in = SignInView(cls.drivers[key])
cls.homes[key] = sign_in.create_user(enable_notifications=True)
SignInView(cls.drivers[2]).put_app_to_background_and_back()
cls.public_keys[key], cls.usernames[key] = sign_in.get_public_key_and_username(True)
sign_in.home_button.click()
SignInView(cls.drivers[0]).put_app_to_background_and_back()
cls.chat_name = cls.homes[0].get_random_chat_name()
cls.homes[0].just_fyi('Admin adds future members to contacts')
@@ -42,6 +44,7 @@ class TestGroupChatMultipleDeviceMerged(MultipleSharedDeviceTestCase):
@marks.testrail_id(3994)
def test_group_chat_push_system_messages_when_invited(self):
self.homes[1].just_fyi("Check system messages in PNs")
self.homes[2].put_app_to_background_and_back()
self.homes[1].put_app_to_background()
self.homes[1].open_notification_bar()
pns = [self.chats[0].pn_invited_to_group_chat(self.usernames[0], self.chat_name),
@@ -84,8 +84,9 @@ class TestOnboardingOneDeviceMerged(MultipleSharedDeviceTestCase):
wallet.click_system_back_button()
self.home.just_fyi("Check that can paste wallet address in chat message input")
wallet.home_button.double_click()
self.home.get_chat('#%s' % self.public_chat_name).click()
wallet.home_button.click()
if not self.chat.chat_message_input.is_element_displayed():
self.home.get_chat('#%s' % self.public_chat_name).click()
self.chat.chat_message_input.click()
self.chat.paste_text()
if self.chat.chat_message_input.text != address:
@@ -113,6 +113,7 @@ class TestPairingSyncMultipleDevicesMerged(MultipleSharedDeviceTestCase):
cls.profile_1.close_button.click()
cls.profile_1.home_button.click()
cls.profile_1.get_recovery_phrase()
cls.device_2.put_app_to_background_and_back()
cls.home_1.just_fyi('Add contact, 1-1 chat (main device): 3-random, contact with ENS, start 1-1')
cls.chat_1 = cls.home_1.add_contact(cls.contact_before_sync['public_key'])
@@ -38,9 +38,10 @@ class TestPublicChatMultipleDeviceMerged(MultipleSharedDeviceTestCase):
self.chat_2.dapp_tab_button.click()
sent_time_variants = self.chat_1.convert_device_time_to_chat_timestamp()
timestamp = self.chat_1.chat_element_by_text(message).timestamp_on_tap
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 sent_time_variants and timestamp:
if timestamp not in sent_time_variants:
self.errors.append("Timestamp is not shown, expected: '%s', in fact: '%s'" %
(sent_time_variants.join(','), timestamp))
self.chat_2.home_button.click(desired_view='chat')
for chat in self.chat_1, self.chat_2:
chat.verify_message_is_under_today_text(message, self.errors)
@@ -50,6 +51,8 @@ class TestPublicChatMultipleDeviceMerged(MultipleSharedDeviceTestCase):
@marks.testrail_id(700734)
def test_public_chat_message_edit(self):
if not self.chat_2.chat_message_input.is_element_displayed():
self.chat_2.home_button.click(desired_view='chat')
message_before_edit, message_after_edit = self.text_message, "Message AFTER edit 2"
self.chat_1.edit_message_in_chat(message_before_edit, message_after_edit)
for chat in (self.chat_1, self.chat_2):
@@ -418,8 +421,8 @@ class TestPublicChatBrowserOneDeviceMerged(MultipleSharedDeviceTestCase):
self.home.just_fyi('Delete one tab')
web_page.remove_tab(name=urls['bbc.com'])
if web_page.element_by_text_part(urls['bbc.com']).is_element_displayed():
self.errors.append('Closed tab is present after deletion')
web_page.open_tabs_button.wait_for_invisibility_of_element()
web_page.element_by_text_part(urls['bbc.com']).wait_for_invisibility_of_element()
self.home.just_fyi('Close all tabs via "Close all", relogin and check that it is not reappearing')
web_page.close_all_button.click()
@@ -425,13 +425,8 @@ class TestKeycardTxOneDeviceMerged(MultipleSharedDeviceTestCase):
self.sign_in.maybe_later_button.click_until_presence_of_element(self.sign_in.lets_go_button)
self.sign_in.lets_go_button.click_until_absense_of_element(self.sign_in.lets_go_button)
self.sign_in.profile_button.wait_for_visibility_of_element(30)
self.sign_in.just_fyi('Check that after creating keycard account balance is 0, not ...')
wallet_1 = self.sign_in.wallet_button.click()
wallet_address = wallet_1.get_wallet_address()
wallet_1.wallet_button.double_click()
if wallet_1.status_account_total_usd_value.text != '0':
self.errors.append("Account USD value is not 0, it is %s" % wallet_1.status_account_total_usd_value.text)
public_key, default_username = self.sign_in.get_public_key_and_username(return_username=True)
profile_1 = self.sign_in.get_profile_view()
profile_1.logout()
@@ -252,10 +252,9 @@ class TestWalletManagementDeviceMerged(MultipleSharedDeviceTestCase):
self.errors.verify_no_errors()
@marks.testrail_id(700762)
@marks.skip
# TODO: skipped due to #13016
def test_wallet_add_account_seed_phrase_collectibles_mainnet_rinkeby(self):
def test_wallet_add_account_seed_phrase_collectibles_rinkeby_set_as_profile_image(self):
user = wallet_users['E']
self.wallet.driver.set_network_connection(6)
account_seed_collectibles = self.account_seed_collectibles
self.wallet.get_back_to_home_view()
if not self.wallet.add_account_button.is_element_displayed(3):
@@ -280,34 +279,36 @@ class TestWalletManagementDeviceMerged(MultipleSharedDeviceTestCase):
self.wallet.account_name_input.send_keys(account_seed_collectibles)
self.wallet.add_account_generate_account_button.click()
account_button = self.wallet.get_account_by_name(account_seed_collectibles)
if not account_button.is_element_displayed():
self.wallet.accounts_status_account.swipe_left_on_element()
self.home.just_fyi('Check that collectibles are not shown on Ropsten')
account_button.click()
self.wallet.collectibles_button.click()
self.wallet.element_by_translation_id("display-collectibles").scroll_and_click()
if not self.wallet.element_by_translation_id("no-collectibles").is_element_displayed():
self.errors.append("Collectibles are shown on Ropsten network!")
self.wallet.just_fyi('Check collectibles amount in wallet')
self.home.just_fyi('Check that collectibles amount is shown on Rinkeby')
profile = self.home.profile_button.click()
profile.switch_network()
profile.switch_network('Rinkeby with upstream RPC')
profile.wallet_button.click()
if not account_button.is_element_displayed():
self.wallet.accounts_status_account.swipe_left_on_element()
account_button.click()
self.wallet.collectibles_button.click()
self.wallet.element_by_translation_id("display-collectibles").scroll_and_click()
collectible_amount = self.wallet.get_collectibles_amount()
collectible_amount.wait_for_visibility_of_element(30)
if collectible_amount.text != '1':
self.errors.append('Wrong number is shown on CK assets: %s' % collectible_amount.text)
# Workaround for situation when after switching network from Ropsten collectibles are not shown
self.wallet.pull_to_refresh(5)
if self.wallet.element_by_translation_id("no-collectibles").is_element_displayed():
self.wallet.reopen_app()
profile.wallet_button.click()
if not account_button.is_element_displayed():
self.wallet.accounts_status_account.swipe_left_on_element()
account_button.click()
self.wallet.collectibles_button.click()
for asset in user['collectibles']:
self.wallet.get_collectibles_amount(asset).scroll_to_element()
if self.wallet.get_collectibles_amount(asset).text != user['collectibles'][asset]:
self.errors.append(
'%s %s is not shown in Collectibles for Rinkeby!' % (user['collectibles'][asset], asset))
self.wallet.just_fyi('Check that you can open collectible to view')
collectible_amount.click()
nft, nft_name = 'Coins & Steel Exclusive Item Skin V2', "Warlock's Arm"
self.wallet.get_collectibles_amount(nft).click()
if not self.wallet.nft_asset_button.is_element_displayed(60):
self.driver.fail("Kitty is not shown after opening it from collectibles!")
self.driver.fail("No card is not shown for %s after opening it from collectibles!" % nft)
self.wallet.nft_asset_button.click()
self.wallet.set_collectible_as_profile_photo_button.scroll_and_click()
@@ -316,47 +317,21 @@ class TestWalletManagementDeviceMerged(MultipleSharedDeviceTestCase):
self.wallet.view_collectible_on_opensea_button.click_until_presence_of_element(
web_view.browser_previous_page_button)
web_view.wait_for_d_aap_to_load()
if not web_view.element_by_text('Princess Gunklater').is_element_displayed(30):
if not web_view.element_by_text(nft_name).is_element_displayed(30):
self.errors.append("Collectible can't be opened when tapping 'View on OpenSea' via NFT page")
self.wallet.wallet_button.click()
self.wallet.just_fyi('Check that collectibles are not shown when sending assets from wallet')
send_transaction = self.wallet.send_transaction_button.click()
send_transaction.select_asset_button.click()
if send_transaction.asset_by_name("CryptoKitties").is_element_displayed():
if send_transaction.asset_by_name(nft).is_element_displayed():
self.errors.append('Collectibles can be sent from wallet')
self.wallet.close_send_transaction_view_button.double_click()
self.wallet.just_fyi('Check "Open in OpenSea" (that user is signed in)')
self.wallet.element_by_translation_id("check-on-opensea").click_until_presence_of_element(
web_view.browser_previous_page_button)
web_view.wait_for_d_aap_to_load(10)
self.wallet.element_by_text('e2ecryptokitty').wait_for_element(60)
self.wallet.just_fyi("Check that custom image from collectible is set as profile photo")
self.wallet.profile_button.double_click()
if not profile.profile_picture.is_element_image_similar_to_template('collectible_pic.png'):
self.errors.append("Collectible image is not set as profile image")
self.home.just_fyi('Check that collectibles amount is shown on Rinkeby')
profile = self.home.profile_button.click()
profile.switch_network('Rinkeby with upstream RPC')
profile = self.home.profile_button.click()
profile.wallet_button.click()
if not account_button.is_element_displayed():
self.wallet.accounts_status_account.swipe_left_on_element()
self.wallet.scan_tokens()
account_button.click()
self.wallet.transaction_history_button.click()
self.wallet.collectibles_button.click()
self.wallet.swipe_up()
if self.wallet.element_by_translation_id("display-collectibles").is_element_displayed:
self.wallet.element_by_translation_id("display-collectibles").click()
for asset in user['collectibles']:
self.wallet.get_collectibles_amount(asset).scroll_to_element()
if self.wallet.get_collectibles_amount(asset).text != user['collectibles'][asset]:
self.errors.append(
'%s %s is not shown in Collectibles for Rinkeby!' % (user['collectibles'][asset], asset))
self.errors.verify_no_errors()
@marks.testrail_id(700766)
+312 -3
View File
@@ -1,9 +1,11 @@
import random
import time
from time import sleep
import emoji
import pytest
from tests import marks
from tests.users import ens_user
from tests.base_test_case import create_shared_drivers, MultipleSharedDeviceTestCase
from views.sign_in_view import SignInView
@@ -82,6 +84,7 @@ class TestPublicChatMultipleDeviceMergedMedium(MultipleSharedDeviceTestCase):
chat_2.add_to_contacts.click()
timeline_2 = chat_2.status_button.click()
for status in statuses:
chat_2.element_by_text_part(statuses['*formatted text*']).scroll_to_element()
expected_value = statuses[status]
if not timeline_2.element_by_text_part(expected_value).is_element_displayed():
self.errors.append(
@@ -94,8 +97,7 @@ class TestPublicChatMultipleDeviceMergedMedium(MultipleSharedDeviceTestCase):
tag_status = '#public-chat-to-redirect-long-name'
timeline_1.set_new_status(tag_status)
public_chat_2 = self.home_2.get_chat_view()
public_chat_2.element_by_text(tag_status).wait_and_click()
public_chat_2.element_by_text(tag_status).scroll_and_click('up')
public_chat_2.user_name_text.wait_for_element(30)
if not public_chat_2.user_name_text.text == tag_status:
self.errors.append('Could not redirect a user to a public chat tapping the tag message from timeline_1')
@@ -173,9 +175,76 @@ class TestChatMultipleDevice(MultipleSharedDeviceTestCase):
cls.message_1, cls.message_2, cls.message_3, cls.message_4 = "Message1", "Message2", "Message3", "Message4"
@marks.testrail_id(702268)
def test_chat_gap_in_public_and_no_gap_in_1_1_and_group(self):
self.home_2.get_back_to_home_view()
message_1 = "testing gap"
message_2 = "testing no gap"
profile_1 = self.home_1.profile_button.click()
profile_1.sync_settings_button.click()
profile_1.sync_history_for_button.click()
profile_1.element_by_translation_id("two-minutes").click()
[home.home_button.click() for home in (self.home_1, self.home_2)]
self.home_1.just_fyi("Creating 1-1 chat and sending message from device 1")
self.chat_1.send_message("HI")
self.home_1.get_back_to_home_view()
self.home_1.just_fyi("Creating group chat and sending message from device 1")
group_chat_1 = self.home_1.get_chat(self.initial_group_chat_name).click()
group_chat_1.send_message("HI")
self.home_1.get_back_to_home_view()
self.home_1.just_fyi("Creating public chat and sending message from device 1")
pub_chat_1 = self.home_1.get_chat('#' + self.public_chat_name).click()
pub_chat_1.send_message("HI")
self.device_1.toggle_airplane_mode()
self.home_2.just_fyi("Joining public chat by device 2 and sending message")
pub_chat_2 = self.home_2.get_chat('#' + self.public_chat_name).click()
pub_chat_2.send_message(message_1)
self.home_2.get_back_to_home_view()
self.home_2.just_fyi("Joining 1-1 chat by device 2 and sending message")
one_to_one_chat_2 = self.home_2.get_chat(self.default_username_1).click()
one_to_one_chat_2.send_message(message_2)
self.home_2.get_back_to_home_view()
self.home_2.just_fyi("Joining Group chat by device 2 and sending message")
group_chat_2 = self.home_2.get_chat(self.initial_group_chat_name).click()
group_chat_2.send_message(message_2)
group_chat_2.get_back_to_home_view()
# Waiting for 3 minutes and then going back online
time.sleep(180)
self.device_1.toggle_airplane_mode()
self.home_1.just_fyi("Checking gap in public chat and fetching messages")
if pub_chat_1.chat_element_by_text(message_1).is_element_displayed(10):
self.errors.append("Test message has been fetched automatically")
pub_chat_1.element_by_translation_id("fetch-messages").wait_and_click(60)
if not pub_chat_1.chat_element_by_text(message_1).is_element_displayed(10):
self.errors.append("Test message has not been fetched")
self.home_1.get_back_to_home_view()
self.home_1.just_fyi("Checking that there is no gap in 1-1/group chat and messages fetched automatically")
for chat in [self.home_1.get_chat(self.default_username_2), self.home_1.get_chat(self.initial_group_chat_name)]:
chat_view = chat.click()
if chat_view.element_by_translation_id("fetch-messages").is_element_displayed(10):
self.errors.append("Fetch messages button is displayed in {}} chat".format(chat.user_name_text.text))
if not chat_view.chat_element_by_text(message_2).is_element_displayed(10):
self.errors.append(
"Message in {} chat has not been fetched automatically".format(chat.user_name_text.text))
chat_view.back_button.click()
self.errors.verify_no_errors()
@marks.testrail_id(702066)
def test_chat_1_1_push_and_reaction_for_messages_sticker_audio_image(self):
self.home_1.get_chat(self.default_username_2).click()
self.home_2.get_chat(self.default_username_1).click()
# methods with steps to use later in loop
def navigate_to_start_state_of_both_devices():
self.chat_1.put_app_to_background()
@@ -433,4 +502,244 @@ class TestChatMultipleDevice(MultipleSharedDeviceTestCase):
self.group_chat_2.chat_element_by_text(self.message_1).pinned_by_label.is_element_present()):
self.errors.append("Message failed be unpinned by user who granted admin permissions!")
self.errors.verify_no_errors()
self.errors.verify_no_errors()
@marks.testrail_id(702258)
def test_chat_group_chat_set_nickname_and_ens_via_group_info_mention(self):
self.drivers[1].reset()
self.home_2 = SignInView(self.drivers[1]).recover_access(ens_user['passphrase'])
self.home_1.home_button.double_click()
self.profile_2 = self.home_2.profile_button.click()
ens, full_ens, username_2 = ens_user['ens'], '@%s' % ens_user['ens'], ens_user['username']
self.profile_2.connect_existing_ens(ens)
[home.home_button.click() for home in (self.home_1, self.home_2)]
self.home_1.just_fyi('Set nickname, using emojis, special chars and cyrrilic chars without adding to contact')
emoji_message = random.choice(list(emoji.EMOJI_UNICODE))
emoji_unicode = emoji.EMOJI_UNICODE[emoji_message]
special_char, cyrrilic = '"£¢€¥~`•|√π¶∆×°™®©%$@', 'стат'
nickname_to_set = emoji.emojize(emoji_message) + special_char + cyrrilic
nickname_expected = emoji_unicode + special_char + cyrrilic
chat_1 = self.home_1.add_contact(ens, add_in_contacts=False, nickname=nickname_to_set)
if chat_1.user_name_text.text != nickname_expected:
self.errors.append('Expected special char nickname %s does not match actual %s' % (nickname_expected, chat_1.user_name_text.text))
self.home_1.just_fyi('Can remove nickname without adding to contact')
chat_1.chat_options.click()
chat_1.view_profile_button.click()
chat_1.profile_nickname_button.click()
chat_1.nickname_input_field.clear()
chat_1.element_by_text('Done').click()
chat_1.close_button.click()
if chat_1.user_name_text.text != full_ens:
self.errors.append(
'Nickname was not removed! real chat name is %s instead of %s' % (chat_1.user_name_text.text, full_ens))
self.home_1.just_fyi('Adding ENS user to contacts and start group chat with him')
group_name = 'ens_group'
chat_1.add_to_contacts.click()
chat_2 = self.home_2.add_contact(self.public_key_1)
chat_2.send_message("first")
chat_2.home_button.click()
chat_1.home_button.click()
chat_1 = self.home_1.create_group_chat([full_ens], group_name)
chat_2 = self.home_2.get_chat(group_name).click()
chat_2.join_chat_button.click()
self.home_1.just_fyi('Check ENS and in group chat and suggestions list')
chat_1.element_by_text_part(full_ens).wait_for_visibility_of_element(60)
chat_1.select_mention_from_suggestion_list(ens, typed_search_pattern=ens[:2])
if chat_1.chat_message_input.text != '@' + ens + ' ':
self.errors.append(
'ENS username is not resolved in chat input after selecting it in mention suggestions list!')
additional_text = 'and more'
chat_1.send_as_keyevent(additional_text)
chat_1.send_message_button.click()
message_text = '%s %s' % (full_ens, additional_text)
if not chat_1.chat_element_by_text(message_text).is_element_displayed():
self.errors.append("ENS name is not resolved on sent message")
self.home_1.just_fyi('Set nickname via group info and check that can mention by nickname /username in group chat')
nickname = 'funny_bunny'
device_2_options = chat_1.get_user_options(full_ens)
device_2_options.view_profile_button.click()
chat_1.set_nickname(nickname, close_profile=False)
if not chat_1.element_by_text(nickname).is_element_displayed():
self.errors.append('Nickname is not shown in profile view after setting from group info')
chat_1.close_button.click()
chat_1.element_by_text(nickname).scroll_to_element()
chat_1.close_button.click()
message_text = '%s %s' % (nickname, additional_text)
if not chat_1.chat_element_by_text(message_text).is_element_displayed():
self.errors.append("ENS name was not replaced with nickname on sent message")
chat_1.chat_message_input.send_keys('@')
if not chat_1.element_by_text('%s %s' % (nickname, full_ens)).is_element_displayed():
self.errors.append("ENS name with nickname is not shown in mention input after set")
if not chat_1.element_by_text(username_2).is_element_displayed():
self.errors.append("3-random name is not shown in mention input after set from group info")
chat_1.chat_message_input.clear()
chat_1.select_mention_from_suggestion_list('%s %s' % (nickname, full_ens), typed_search_pattern=username_2[:2])
if chat_1.chat_message_input.text != '@' + ens + ' ':
self.errors.append(
'ENS is not resolved in chat input after setting nickname in mention suggestions list (search by 3-random name)!')
chat_1.chat_message_input.clear()
chat_1.select_mention_from_suggestion_list('%s %s' % (nickname, full_ens), typed_search_pattern=nickname[:2])
if chat_1.chat_message_input.text != '@' + ens + ' ':
self.errors.append(
'ENS is not resolved in chat input after setting nickname in mention suggestions list (search by nickname)!')
chat_1.chat_message_input.clear()
self.home_1.just_fyi('Can delete nickname via group info and recheck received messages')
device_2_options = chat_1.get_user_options(full_ens)
device_2_options.view_profile_button.click()
chat_1.profile_nickname_button.click()
chat_1.nickname_input_field.clear()
chat_1.element_by_text('Done').click()
chat_1.close_button.click()
chat_1.close_button.click()
message_text = '%s %s' % (full_ens, additional_text)
if not chat_1.chat_element_by_text(message_text).is_element_displayed():
self.errors.append("ENS name is not resolved on sent message after removing nickname")
chat_1.chat_message_input.send_keys('@')
if chat_1.element_by_text_part(nickname).is_element_displayed():
self.errors.append("Nickname is shown in group chat after removing!")
self.errors.verify_no_errors()
@pytest.mark.xdist_group(name="group_chat_medium_3")
@marks.medium
class TestGroupChatMultipleDevice(MultipleSharedDeviceTestCase):
@classmethod
def setup_class(cls):
cls.drivers, cls.loop = create_shared_drivers(3)
cls.sign_ins, cls.homes, cls.public_keys, cls.usernames, cls.chats = {}, {}, {}, {}, {}
for key in cls.drivers:
cls.sign_ins[key] = SignInView(cls.drivers[key])
cls.homes[key] = cls.sign_ins[key].create_user()
SignInView(cls.drivers[2]).put_app_to_background_and_back()
cls.public_keys[key], cls.usernames[key] = cls.sign_ins[key].get_public_key_and_username(True)
cls.sign_ins[key].home_button.click()
SignInView(cls.drivers[0]).put_app_to_background_and_back()
for member in (cls.public_keys[1], cls.public_keys[2]):
cls.homes[0].add_contact(member)
cls.homes[0].home_button.click()
cls.chat_name = cls.homes[0].get_random_chat_name()
cls.invite_chat_name = '%s_invite' % cls.homes[0].get_random_chat_name()
cls.chats[0] = cls.homes[0].create_group_chat([], cls.invite_chat_name)
[SignInView(cls.drivers[i]).put_app_to_background_and_back() for i in range(1, 3)]
cls.link = cls.chats[0].get_group_invite_via_group_info()
cls.chats[0].home_button.double_click()
cls.chats[0] = cls.homes[0].create_group_chat([cls.usernames[1], cls.usernames[2]], cls.chat_name)
for i in range(1, 3):
cls.chats[i] = cls.homes[i].get_chat(cls.chat_name).click()
cls.chats[i].join_chat_button.click()
@marks.testrail_id(702259)
def test_group_chat_remove_member(self):
self.chats[0].just_fyi("Admin: get options for device 2 in group chat and remove him")
removed_user = self.usernames[2]
options = self.chats[0].get_user_options(removed_user)
options.remove_user_button.click()
left_message = self.chats[0].leave_system_message(removed_user)
for key in self.chats:
if not self.chats[key].chat_element_by_text(left_message).is_element_displayed():
self.errors.append("Message with text '%s' was not received" % left_message)
self.chats[0].just_fyi("Check that input field is not available after removing")
if self.chats[2].chat_message_input.is_element_displayed():
self.errors.append("Message input is still available for removed user")
self.chats[0].just_fyi("Send message and check that it is available only for remaining users")
message = 'after removing member'
self.chats[0].send_message(message)
for chat in (self.chats[0], self.chats[1]):
if not chat.chat_element_by_text(message).is_element_displayed(30):
self.errors.append("Message '%s' was not received after removing member" % message)
if self.chats[2].chat_element_by_text(message).is_element_displayed():
self.errors.append("Message '%s' was received by removed member" % message)
self.errors.verify_no_errors()
@marks.testrail_id(702260)
def test_group_chat_make_admin(self):
self.homes[0].just_fyi('Check group info view and options of users')
self.chats[0].chat_options.click()
group_info_1 = self.chats[0].group_info.click()
if not group_info_1.user_admin(self.usernames[0]).is_element_displayed():
self.errors.append("Admin user is not marked as admin")
group_info_1.get_user_from_group_info(self.usernames[0]).click()
if self.chats[0].profile_block_contact.is_element_displayed():
self.errors.append("Admin is redirected to own profile on tapping own username from group info")
self.chats[0].just_fyi('Made admin another user and check system message')
options = group_info_1.get_username_options(self.usernames[1]).click()
options.make_admin_button.click()
admin_system_message = self.chats[0].has_been_made_admin_system_message(self.usernames[0], self.usernames[1])
for chat in (self.chats[0], self.chats[1]):
if not chat.chat_element_by_text(admin_system_message).is_element_displayed():
self.errors.append("Message with test '%s' was not received" % admin_system_message)
self.chats[1].just_fyi('Check Admin in group info and that "add members" is available')
self.chats[1].chat_options.click()
group_info_1 = self.chats[1].group_info.click()
for username in (self.usernames[0], self.usernames[1]):
if not group_info_1.user_admin(username).is_element_displayed():
self.errors.append("Admin user is not marked as admin")
if not group_info_1.add_members.is_element_displayed():
self.errors.append("Add member button is not available for new admin")
self.errors.verify_no_errors()
@marks.testrail_id(702261)
def test_group_chat_accept_decline_invite(self):
[driver.close_app() for driver in (self.drivers[1], self.drivers[2])]
self.homes[0].home_button.double_click()
self.chats[0].just_fyi('Member_1, member_2: both users send requests to join group chat')
[sign_in.open_weblink_and_login(self.link) for sign_in in (self.sign_ins[1], self.sign_ins[2])]
introduction_messages = ['message for retrying']
for i in range(1, 3):
self.homes[i].element_by_text_part(self.invite_chat_name).click()
introduction_messages.append('Please add me, member_%s to your gorgeous group chat' % str(i))
self.chats[i].request_membership_for_group_chat(introduction_messages[i])
self.chats[0].just_fyi('Admin: accept request for Member_1 and decline for Member_2')
self.homes[0].get_chat(self.invite_chat_name).click()
self.chats[0].group_membership_request_button.wait_and_click()
self.chats[0].element_by_text(self.usernames[1]).click()
if not self.chats[0].element_by_text_part(introduction_messages[1]).is_element_displayed():
self.errors.append('Introduction message is not shown!')
self.chats[0].accept_group_invitation_button.wait_and_click()
self.chats[0].accept_membership_for_group_chat_via_chat_view(self.usernames[2], accept=False)
self.chats[0].click_system_back_button()
self.chats[2].just_fyi('Member_2: retry request')
self.chats[2].retry_group_invite_button.wait_and_click()
self.chats[2].request_membership_for_group_chat(introduction_messages[0])
self.chats[2].just_fyi('Admin: decline request for Member_2')
self.chats[0].group_membership_request_button.wait_and_click()
self.chats[0].element_by_text(self.usernames[2]).click()
if not self.chats[0].element_by_text_part(introduction_messages[0]).is_element_displayed():
self.errors.append('Introduction message that was set after retrying attempt is not shown for admin!')
self.chats[0].decline_group_invitation_button.wait_and_click()
self.chats[0].click_system_back_button()
self.chats[2].just_fyi('Member_2: remove chat')
self.chats[2].remove_group_invite_button.wait_and_click()
self.chats[2].just_fyi('Double check after relogin')
if self.chats[0].group_membership_request_button.is_element_displayed():
self.errors.append('Group membership request is still shown when there are no pending requests anymore')
[self.homes[i].reopen_app() for i in range(0, 3)]
if self.homes[2].element_by_text_part(self.invite_chat_name).is_element_displayed():
self.errors.append('Group chat was not removed when removing after declining group invite')
[home.get_chat(self.invite_chat_name).click() for home in (self.homes[0], self.homes[1])]
if self.chats[0].group_membership_request_button.is_element_displayed():
self.errors.append(
'Group membership request is shown after relogin when there are no pending requests anymore')
join_system_message = self.chats[0].join_system_message(self.usernames[1])
for chat in (self.chats[1], self.chats[0]):
if not chat.chat_element_by_text(join_system_message).is_element_displayed():
self.errors.append('%s is not shown after joining to group chat via invite' % join_system_message)
self.errors.verify_no_errors()
@@ -188,7 +188,7 @@ class TestBrowserProfileOneDevice(MultipleSharedDeviceTestCase):
@marks.testrail_id(702159)
def test_profile_invite_friends(self):
self.home.home_button.click()
self.home.home_button.double_click()
self.home.just_fyi("Check it via 'Invite friends' on home view")
self.home.invite_friends_button.click()
@@ -0,0 +1,346 @@
import re
import random
import string
import pytest
from tests import marks, mailserver_ams, mailserver_gc, mailserver_hk, used_fleet, common_password, test_dapp_name,\
test_dapp_url, pair_code, unique_password
from tests.base_test_case import create_shared_drivers, MultipleSharedDeviceTestCase
from tests.users import user_mainnet, chat_users, dummy_user, recovery_users, transaction_senders, basic_user,\
wallet_users, ens_user_ropsten, ens_user
from selenium.common.exceptions import NoSuchElementException
from tests.base_test_case import SingleDeviceTestCase, MultipleDeviceTestCase
from views.send_transaction_view import SendTransactionView
from views.chat_view import ChatView
from views.sign_in_view import SignInView
@pytest.mark.xdist_group(name="deeplink_chat_profile_medium_1")
@marks.medium
class TestDeeplinkChatProfileOneDevice(MultipleSharedDeviceTestCase):
@classmethod
def setup_class(cls):
cls.drivers, cls.loop = create_shared_drivers(1)
cls.sign_in = SignInView(cls.drivers[0])
cls.home = cls.sign_in.create_user()
cls.public_key, cls.default_username = cls.home.get_public_key_and_username(return_username=True)
cls.home.home_button.click()
cls.public_chat_name = 'pubchat'
cls.nickname = 'dummy_user'
cls.search_list_1 = {
basic_user['username']: basic_user['username'],
ens_user_ropsten['username']: ens_user_ropsten['ens'],
cls.public_chat_name: cls.public_chat_name,
cls.nickname: cls.nickname,
dummy_user['username']: cls.nickname,
ens_user_ropsten['ens']: ens_user_ropsten['ens']
}
cls.public_chat = cls.home.join_public_chat(cls.public_chat_name)
cls.public_chat.get_back_to_home_view()
@marks.testrail_id(702244)
def test_deep_link_with_invalid_user_public_key_own_profile_key(self):
self.drivers[0].close_app()
self.sign_in.just_fyi('Check that no error when opening invalid deep link')
deep_link = 'status-im://u/%s' % self.public_key[:-10]
self.sign_in.open_weblink_and_login(deep_link)
self.home = self.sign_in.get_home_view()
self.home.plus_button.click_until_presence_of_element(self.home.start_new_chat_button)
if not self.home.start_new_chat_button.is_element_present():
self.errors.append(
"Can't navigate to start new chat after app opened from deep link with invalid public key")
self.drivers[0].close_app()
self.sign_in.just_fyi('Check that no error when opening own valid deep link')
deep_link = 'status-im://u/%s' % self.public_key
self.sign_in.open_weblink_and_login(deep_link)
from views.profile_view import ProfileView
profile = ProfileView(self.drivers)
if not profile.default_username_text != self.default_username:
self.errors.append("Can't navigate to profile from deep link with own public key")
self.errors.verify_no_errors()
@marks.testrail_id(702245)
def test_deep_link_open_user_profile(self):
for user_ident in ens_user['ens'], ens_user['ens_another'], ens_user['public_key']:
self.drivers[0].close_app()
deep_link = 'status-im://u/%s' % user_ident
self.sign_in.open_weblink_and_login(deep_link)
chat = self.sign_in.get_chat_view()
for text in ens_user['username'], self.sign_in.get_translation_by_key("add-to-contacts"):
if not chat.element_by_text(text).scroll_to_element(10):
self.driver.fail("User profile screen is not opened")
self.errors.verify_no_errors()
@marks.testrail_id(702246)
def test_deep_link_open_dapp(self):
self.drivers[0].close_app()
dapp_name = test_dapp_url
dapp_deep_link = 'status-im://b/%s' % dapp_name
self.sign_in.open_weblink_and_login(dapp_deep_link)
web_view = self.sign_in.get_chat_view()
try:
test_dapp_view = web_view.open_in_status_button.click()
test_dapp_view.allow_button.is_element_present()
except NoSuchElementException:
self.drivers[0].fail("DApp '%s' is not opened!" % dapp_name)
self.home.reopen_app()
self.errors.verify_no_errors()
@marks.testrail_id(702247)
def test_share_user_profile_url_one_to_one_chat(self):
self.home.home_button.click()
self.home.just_fyi('Join to one-to-one chat and share link to other user profile via messenger')
chat = self.home.add_contact(dummy_user["public_key"])
chat.chat_options.click()
chat.view_profile_button.click_until_presence_of_element(chat.remove_from_contacts)
chat.profile_details.click()
chat.share_button.click()
chat.share_via_messenger()
if not chat.element_by_text_part(
'https://join.status.im/u/%s' % dummy_user["public_key"]).is_element_present():
self.errors.append("Can't share public key of contact")
for _ in range(2):
chat.click_system_back_button()
@marks.testrail_id(702252)
def test_share_user_profile_url_public_chat(self):
self.home.just_fyi('Join to public chat and share link to it via messenger')
self.home.home_button.click()
self.home.get_chat('#' + self.public_chat_name).click()
self.public_chat.chat_options.click()
self.public_chat.share_chat_button.click()
self.public_chat.share_via_messenger()
if not self.public_chat.element_by_text_part('https://join.status.im/%s' % self.public_chat_name).is_element_present():
self.errors.append("Can't share link to public chat")
for _ in range(2):
self.public_chat.click_system_back_button()
self.public_chat.get_back_to_home_view()
@marks.testrail_id(702251)
def test_share_user_profile_url_browser(self):
self.home.just_fyi('Open URL and share link to it via messenger')
daap = self.home.dapp_tab_button.click()
browser = daap.open_url('dap.ps')
browser.options_button.click()
browser.share_url_button.click()
browser.share_via_messenger()
expeceted_text_1 = 'https://join.status.im/b/https://dap.ps'
expeceted_text_2 = 'https://join.status.im/b/http://dap.ps'
if not (browser.element_by_text_part(expeceted_text_1).is_element_present() or
browser.element_by_text_part(expeceted_text_2).is_element_present()):
self.errors.append("Can't share link to URL")
browser.click_system_back_button(1)
self.errors.verify_no_errors()
@marks.testrail_id(702248)
def test_chat_can_start_and_find_from_suggestions_using_search(self):
self.home.home_button.click()
self.home.just_fyi('Start 1-1 with username and with ENS')
for public_key in (basic_user['public_key'], dummy_user['public_key']):
chat = self.home.add_contact(public_key)
chat.get_back_to_home_view()
profile = self.home.profile_button.click()
profile.open_contact_from_profile(dummy_user['username'])
self.public_chat.set_nickname(self.nickname)
self.public_chat.home_button.click()
ens_name_status, ens_name_another_domain, public_chat_name = ens_user_ropsten['ens'], \
ens_user['ens_another'], 'some-pub-chat'
search_list_2 = {
ens_name_status: {
'home': {
'Start a new private chat',
'Join a public chat',
'%s.stateofus.eth' % ens_name_status,
'#%s' % ens_name_status
},
'navigate_to': '%s.stateofus.eth' % ens_name_status,
'chat': {
chat.add_to_contacts,
chat.element_by_text('@%s' % ens_name_status),
chat.chat_message_input
}
},
ens_name_another_domain: {
'home': {
'Start a new private chat',
},
'home_not_shown': 'Join a public chat',
'navigate_to': 'Start a new private chat',
'chat': {
chat.add_to_contacts,
chat.element_by_text('@%s' % ens_name_another_domain),
chat.chat_message_input
},
},
public_chat_name: {
'home': {
'Join a public chat',
},
'home_not_shown': 'Start a new private chat',
'navigate_to': '#%s' % public_chat_name,
'chat': {
chat.element_by_text('#%s' % public_chat_name),
chat.chat_message_input
},
},
}
self.home.swipe_down()
for keyword in search_list_2:
self.home.just_fyi('Can start chat from searching for %s' % keyword)
self.home.search_by_keyword(keyword)
if not self.home.element_by_text_part('No search results. Do you mean').is_element_displayed():
self.errors.append('"No search results" is not shown')
if 'home_not_shown' in search_list_2[keyword]:
if self.home.element_by_text(search_list_2[keyword]['home_not_shown']).is_element_displayed():
self.errors.append('%s is shown on home view while searching for %s' % (
search_list_2[keyword]['home_not_shown'], keyword))
for text in search_list_2[keyword]['home']:
if not self.home.element_by_text(text).is_element_displayed():
self.errors.append('%s is not shown on home view while searching for %s' % (text, keyword))
self.home.element_by_text(search_list_2[keyword]['navigate_to']).click()
for element in search_list_2[keyword]['chat']:
if not element.is_element_displayed():
self.errors.append(
'Requested %s element is not shown on chat view after navigating from suggestion '
'for %s' % (element.name, keyword))
self.home.back_button.click()
self.home.just_fyi('Can search for public chat name, ens name, username')
self.home.swipe_down()
for keyword in self.search_list_1:
self.home.search_by_keyword(keyword)
search_results = self.home.chat_name_text.find_elements()
if not search_results:
self.errors.append('No search results after searching by %s keyword' % keyword)
for element in search_results:
if self.search_list_1[keyword] not in element.text:
self.errors.append("'%s' is shown on the home screen after searching by '%s' keyword" %
(element.text, keyword))
self.home.cancel_button.click()
self.errors.verify_no_errors()
@marks.testrail_id(702253)
def test_chat_no_suggestions_invalid_data_search(self):
self.home.just_fyi('No suggestion at attempt to search for invalid data')
invalid_data = [' ', 'ab;', '.6', '@ana']
for text in invalid_data:
self.home.search_by_keyword(text)
if self.home.element_by_text_part('No search results. Do you mean').is_element_displayed():
self.errors.append('"No search results" is shown when searching for invalid value %s' % text)
self.home.cancel_button.click()
self.errors.verify_no_errors()
@marks.testrail_id(702249)
def test_chat_input_delete_cut_and_paste_messages(self):
chat = self.home.add_contact(transaction_senders['N']['public_key'])
message_text = 'test'
message_input = chat.chat_message_input
message_input.send_keys(message_text)
self.home.just_fyi('Deleting last 2 symbols in initial message')
message_input.delete_last_symbols(2)
current_text = message_input.text
if current_text != message_text[:-2]:
self.errors.append("Message input text '%s' doesn't match expected '%s'" % (current_text, message_text[:-2]))
"""self.home.just_fyi('Cutting message text from input field')
message_input.cut_text()
message_input.click()
if current_text != message_text[:-4]:
self.errors.append("Message input text '%s' doesn't match expected '%s'" % (current_text, message_text[:-4]))"""
message_input.cut_text()
self.home.just_fyi('Pasting the cut message back to the input field')
message_input.paste_text_from_clipboard()
if current_text != message_text[:-2]:
self.errors.append("Message input text '%s' doesn't match expected '%s'" % (current_text, message_text[:-2]))
chat.send_message_button.click()
chat.chat_element_by_text(message_text[:-2]).wait_for_visibility_of_element(2)
chat.get_back_to_home_view()
self.errors.verify_no_errors()
@marks.testrail_id(702250)
def test_chat_public_clear_history_via_options_and_long_press(self):
public_long_press = '#public-clear-options'
options_chat_name = '#' + self.public_chat_name
message = 'test message'
long_press_chat = self.home.join_public_chat(public_long_press[1:])
[long_press_chat.send_message(message) for _ in range(2)]
self.home.home_button.double_click()
self.home.element_by_text(options_chat_name).scroll_to_element()
options_chat = self.home.get_chat(options_chat_name)
chat_view = options_chat.click()
[chat_view.send_message(message) for _ in range(2)]
self.home.home_button.double_click()
self.home.element_by_text(public_long_press).scroll_to_element(direction='up')
self.home.just_fyi('Clearing history via long press')
self.home.clear_chat_long_press(public_long_press)
self.home.just_fyi('Clearing history via options')
options_chat.click()
chat_view.clear_history()
if chat_view.element_by_text(message).is_element_displayed():
self.errors.append(
'Messages in %s chat are still shown after clearing history via options' % options_chat_name)
self.home.just_fyi("Recheck that history won't reappear after relogin")
self.home.relogin()
for chat_name in options_chat_name, public_long_press:
chat = self.home.get_chat(chat_name).click()
if chat.chat_element_by_text(message).is_element_displayed():
self.errors.append('Messages in %s chat are shown after clearing history and relaunch' % chat_name)
chat.home_button.click()
self.errors.verify_no_errors()
@marks.testrail_id(702254)
# Should be the last in group as it disables network connection
def test_chat_can_search_while_offline(self):
self.home.just_fyi('Can search for public chat while offline')
self.home.toggle_airplane_mode()
self.home.search_input.click()
self.home.search_input.send_keys(self.public_chat_name)
search_results = self.home.chat_name_text.find_elements()
if not search_results:
self.errors.append('No search results after searching by %s keyword' % self.public_chat_name)
for element in search_results:
if self.search_list_1[self.public_chat_name] not in element.text:
self.errors.append("'%s' is shown on the home screen after searching by '%s' keyword" %
(element.text, self.public_chat_name))
self.errors.verify_no_errors()
@@ -292,121 +292,6 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
self.errors.verify_no_errors()
@marks.testrail_id(5362)
# TODO: can be moved to TestOneToOneChatMultipleSharedDevices - should be quick e2e
def test_1_1_chat_unread_counter_preview_highlited(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_2 = home_2.profile_button.click()
default_username_2 = profile_2.default_username_text.text
home_2 = profile_2.home_button.click()
public_key_1 = home_1.get_public_key_and_username()
home_1.home_button.click()
chat_2 = home_2.add_contact(public_key_1)
message, message_2, message_3 = 'test message', 'test message2', 'test'
chat_2.send_message(message)
chat_element = home_1.get_chat(default_username_2)
home_1.dapp_tab_button.click()
chat_2.send_message(message_2)
if home_1.home_button.counter.text != '1':
self.errors.append('New messages counter is not shown on Home button')
device_1.home_button.click()
if chat_element.new_messages_counter.text != '1':
self.errors.append('New messages counter is not shown on chat element')
chat_1 = chat_element.click()
chat_1.add_to_contacts.click()
home_1.home_button.double_click()
if home_1.home_button.counter.is_element_displayed():
self.errors.append('New messages counter is shown on Home button for already seen message')
if chat_element.new_messages_counter.text == '1':
self.errors.append('New messages counter is shown on chat element for already seen message')
home_1.delete_chat_long_press(default_username_2)
home_1.just_fyi("Checking preview of message and chat highlighting")
chat_2.send_message(message_3)
chat_1_element = home_1.get_chat(default_username_2)
if chat_1_element.chat_preview.is_element_differs_from_template('highligted_preview.png', 0):
self.errors.append("Preview message is not hightligted or text is not shown! ")
home_1.get_chat(default_username_2).click()
home_1.home_button.double_click()
if not home_1.get_chat(default_username_2).chat_preview.is_element_differs_from_template('highligted_preview.png', 0):
self.errors.append("Preview message is still highlighted after opening ")
self.errors.verify_no_errors()
@marks.testrail_id(700727)
# TODO: merge with any medium group that contains 3 types of chats
def test_chat_gap_in_public_and_no_gap_in_1_1_and_group(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()
message_1 = "testing gap"
message_2 = "testing no gap"
pub_chat_name = home_1.get_random_chat_name()
group_chat_name = home_1.get_random_chat_name()
public_key_1, username_1 = home_1.get_public_key_and_username(True)
public_key_2, username_2 = home_2.get_public_key_and_username(True)
profile_1 = home_1.profile_button.click()
profile_1.sync_settings_button.click()
profile_1.sync_history_for_button.click()
profile_1.element_by_translation_id("two-minutes").click()
[home.home_button.click() for home in (home_1, home_2)]
home_1.just_fyi("Creating 1-1 chat and sending message from device 1")
one_to_one_chat_1 = home_1.add_contact(public_key_2)
one_to_one_chat_1.send_message("HI")
home_1.get_back_to_home_view()
home_1.just_fyi("Creating group chat and sending message from device 1")
group_chat_1 = home_1.create_group_chat([username_2], group_chat_name)
group_chat_1.send_message("HI")
home_1.get_back_to_home_view()
home_1.just_fyi("Creating public chat and sending message from device 1")
pub_chat_1, pub_chat_2 = home_1.join_public_chat(pub_chat_name), home_2.join_public_chat(pub_chat_name)
pub_chat_1.send_message("HI")
device_1.toggle_airplane_mode()
home_2.just_fyi("Joining public chat by device 2 and sending message")
pub_chat_2.send_message(message_1)
home_2.get_back_to_home_view()
home_2.just_fyi("Joining 1-1 chat by device 2 and sending message")
one_to_one_chat_2 = home_2.add_contact(public_key_1)
one_to_one_chat_2.send_message(message_2)
home_2.get_back_to_home_view()
home_2.just_fyi("Joining Group chat by device 2 and sending message")
group_chat_2 = home_2.get_chat(group_chat_name).click()
group_chat_2.join_chat_button.click()
group_chat_2.send_message(message_2)
# Waiting for 3 minutes and then going back online
time.sleep(180)
device_1.toggle_airplane_mode()
home_1.just_fyi("Checking gap in public chat and fetching messages")
if pub_chat_1.chat_element_by_text(message_1).is_element_displayed(10):
self.errors.append("Test message has been fetched automatically")
pub_chat_1.element_by_translation_id("fetch-messages").wait_and_click(60)
if not pub_chat_1.chat_element_by_text(message_1).is_element_displayed(10):
self.errors.append("Test message has not been fetched")
home_1.get_back_to_home_view()
home_1.just_fyi("Checking that there is no gap in 1-1/group chat and messages fetched automatically")
for chat in [home_1.get_chat(username_2), home_1.get_chat(group_chat_name)]:
chat_view = chat.click()
if chat_view.element_by_translation_id("fetch-messages").is_element_displayed(10):
self.errors.append("Fetch messages button is displayed in {}} chat".format(chat.user_name_text.text))
if not chat_view.chat_element_by_text(message_2).is_element_displayed(10):
self.errors.append("Message in {} chat has not been fetched automatically".format(chat.user_name_text.text))
chat_view.back_button.click()
self.errors.verify_no_errors()
@marks.testrail_id(695842)
@marks.flaky
def test_community_creating_accept_membership(self):
@@ -706,268 +591,6 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
self.errors.verify_no_errors()
@marks.testrail_id(5694)
# TODO: merge with other group chats e2e
def test_group_chat_make_admin(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()
public_key_1, username_1 = device_1.get_public_key_and_username(True)
device_1.home_button.click()
chat_name = home_1.get_random_chat_name()
device_2.just_fyi('Create and join to group chat')
device_2_key, device_2_username = device_2.get_public_key_and_username(True)
device_2.home_button.click()
home_1.add_contact(device_2_key)
home_1.get_back_to_home_view()
chat_1 = home_1.create_group_chat([device_2_username], chat_name)
chat_2 = home_2.get_chat(chat_name).click()
chat_2.join_chat_button.click()
device_1.just_fyi('Check group info view and options of users')
chat_1.chat_options.click()
group_info_1 = chat_1.group_info.click()
if not group_info_1.user_admin(username_1).is_element_displayed():
self.errors.append("Admin user is not marked as admin")
group_info_1.get_user_from_group_info(username_1).click()
if chat_1.profile_block_contact.is_element_displayed():
self.errors.append("Admin is redirected to own profile on tapping own username from group info")
group_info_1.get_user_from_group_info(device_2_username).click()
if not chat_1.profile_block_contact.is_element_displayed():
self.errors.append("Admin is not redirected to user profile on tapping member username from group info")
chat_1.close_button.click()
device_1.just_fyi('Made admin another user and check system message')
options = group_info_1.get_username_options(device_2_username).click()
options.make_admin_button.click()
admin_system_message = chat_1.has_been_made_admin_system_message(username_1, device_2_username)
for chat in (chat_1, chat_2):
if not chat.chat_element_by_text(admin_system_message).is_element_displayed():
self.errors.append("Message with test '%s' was not received" % admin_system_message)
device_2.just_fyi('Check Admin in group info and that "add members" is available')
chat_2.chat_options.click()
group_info_1 = chat_2.group_info.click()
for username in (username_1, device_2_username):
if not group_info_1.user_admin(username).is_element_displayed():
self.errors.append("Admin user is not marked as admin")
if not group_info_1.add_members.is_element_displayed():
self.errors.append("Add member button is not available for new admin")
self.errors.verify_no_errors()
@marks.testrail_id(6327)
# TODO: merge with other group chats e2e
def test_group_chat_set_nickname_and_ens_via_group_info(self):
self.create_drivers(2)
home_1 = SignInView(self.drivers[0]).create_user()
home_2 = SignInView(self.drivers[1]).recover_access(ens_user['passphrase'])
profile_1, profile_2 = [home.profile_button.click() for home in (home_1, home_2)]
key_1, username_1 = profile_1.get_public_key_and_username(return_username=True)
ens, full_ens, username_2 = ens_user['ens'], '@%s' % ens_user['ens'], ens_user['username']
profile_2.connect_existing_ens(ens)
[profile.home_button.click() for profile in (profile_1, profile_2)]
home_1.just_fyi('Set nickname, using emojis, special chars and cyrrilic chars without adding to contact')
emoji_message = random.choice(list(emoji.EMOJI_UNICODE))
emoji_unicode = emoji.EMOJI_UNICODE[emoji_message]
special_char, cyrrilic = '"£¢€¥~`•|√π¶∆×°™®©%$@', 'стат'
nickname_to_set = emoji.emojize(emoji_message) + special_char + cyrrilic
nickname_expected = emoji_unicode + special_char + cyrrilic
chat_1 = home_1.add_contact(ens, add_in_contacts=False, nickname=nickname_to_set)
if chat_1.user_name_text.text != nickname_expected:
self.errors.append('Expected special char nickname %s does not match actual %s' % (nickname_expected, chat_1.user_name_text.text))
home_1.just_fyi('Can remove nickname without adding to contact')
chat_1.chat_options.click()
chat_1.view_profile_button.click()
chat_1.profile_nickname_button.click()
chat_1.nickname_input_field.clear()
chat_1.element_by_text('Done').click()
chat_1.close_button.click()
if chat_1.user_name_text.text != full_ens:
self.errors.append(
'Nickname was not removed! real chat name is %s instead of %s' % (chat_1.user_name_text.text, full_ens))
home_1.just_fyi('Adding ENS user to contacts and start group chat with him')
group_name = 'ens_group'
chat_1.add_to_contacts.click()
chat_2 = home_2.add_contact(key_1)
chat_2.send_message("first")
chat_2.home_button.click()
chat_1.home_button.click()
chat_1 = home_1.create_group_chat([full_ens], group_name)
chat_2 = home_2.get_chat(group_name).click()
chat_2.join_chat_button.click()
home_1.just_fyi('Check ENS and in group chat and suggestions list')
chat_1.element_by_text_part(full_ens).wait_for_visibility_of_element(60)
chat_1.select_mention_from_suggestion_list(ens, typed_search_pattern=ens[:2])
if chat_1.chat_message_input.text != '@' + ens + ' ':
self.errors.append(
'ENS username is not resolved in chat input after selecting it in mention suggestions list!')
additional_text = 'and more'
chat_1.send_as_keyevent(additional_text)
chat_1.send_message_button.click()
message_text = '%s %s' % (full_ens, additional_text)
if not chat_1.chat_element_by_text(message_text).is_element_displayed():
self.errors.append("ENS name is not resolved on sent message")
chat_1 = home_1.get_chat_view()
home_1.just_fyi('Set nickname via group info and check that can mention by nickname /username in group chat')
nickname = 'funny_bunny'
device_2_options = chat_1.get_user_options(full_ens)
device_2_options.view_profile_button.click()
chat_1.set_nickname(nickname, close_profile=False)
if not chat_1.element_by_text(nickname).is_element_displayed():
self.errors.append('Nickname is not shown in profile view after setting from group info')
chat_1.close_button.click()
chat_1.element_by_text(nickname).scroll_to_element()
chat_1.close_button.click()
message_text = '%s %s' % (nickname, additional_text)
if not chat_1.chat_element_by_text(message_text).is_element_displayed():
self.errors.append("ENS name was not replaced with nickname on sent message")
chat_1.chat_message_input.send_keys('@')
if not chat_1.element_by_text('%s %s' % (nickname, full_ens)).is_element_displayed():
self.errors.append("ENS name with nickname is not shown in mention input after set")
if not chat_1.element_by_text(username_2).is_element_displayed():
self.errors.append("3-random name is not shown in mention input after set from group info")
chat_1.chat_message_input.clear()
chat_1.select_mention_from_suggestion_list('%s %s' % (nickname, full_ens), typed_search_pattern=username_2[:2])
if chat_1.chat_message_input.text != '@' + ens + ' ':
self.errors.append(
'ENS is not resolved in chat input after setting nickname in mention suggestions list (search by 3-random name)!')
chat_1.chat_message_input.clear()
chat_1.select_mention_from_suggestion_list('%s %s' % (nickname, full_ens), typed_search_pattern=nickname[:2])
if chat_1.chat_message_input.text != '@' + ens + ' ':
self.errors.append(
'ENS is not resolved in chat input after setting nickname in mention suggestions list (search by nickname)!')
chat_1.chat_message_input.clear()
home_1.just_fyi('Can delete nickname via group info and recheck received messages')
device_2_options = chat_1.get_user_options(full_ens)
device_2_options.view_profile_button.click()
chat_1.profile_nickname_button.click()
chat_1.nickname_input_field.clear()
chat_1.element_by_text('Done').click()
chat_1.close_button.click()
chat_1.close_button.click()
message_text = '%s %s' % (full_ens, additional_text)
if not chat_1.chat_element_by_text(message_text).is_element_displayed():
self.errors.append("ENS name is not resolved on sent message after removing nickname")
chat_1.chat_message_input.send_keys('@')
if chat_1.element_by_text_part(nickname).is_element_displayed():
self.errors.append("Nickname is shown in group chat after removing!")
self.errors.verify_no_errors()
@marks.testrail_id(6324)
def test_group_chat_invite_handling(self):
self.create_drivers(3)
sign_ins, homes, public_keys, usernames, chats = {}, {}, {}, {}, {}
for key in self.drivers:
sign_ins[key] = SignInView(self.drivers[key])
homes[key] = sign_ins[key].create_user()
public_keys[key], usernames[key] = sign_ins[key].get_public_key_and_username(True)
sign_ins[key].home_button.click()
[driver.close_app() for driver in (self.drivers[1], self.drivers[2])]
chat_name = homes[0].get_random_chat_name()
homes[0].just_fyi('Create group chats without members')
chats[0] = homes[0].create_group_chat([], chat_name)
link = chats[0].get_group_invite_via_group_info()
chats[0].get_back_to_home_view()
chats[0].just_fyi('Member_1, member_2: both users send requests to join group chat')
[sign_in.open_weblink_and_login(link) for sign_in in (sign_ins[1], sign_ins[2])]
introduction_messages = ['message for retrying']
for i in range(1, 3):
homes[i].element_by_text_part(chat_name).click()
chats[i] = ChatView(self.drivers[i])
introduction_messages.append('Please add me, member_%s to your gorgeous group chat' % str(i))
chats[i].request_membership_for_group_chat(introduction_messages[i])
chats[0].just_fyi('Admin: accept request for Member_1 and decline for Member_2')
homes[0].get_chat(chat_name).click()
chats[0].group_membership_request_button.wait_and_click()
chats[0].element_by_text(usernames[1]).click()
if not chats[0].element_by_text_part(introduction_messages[1]).is_element_displayed():
self.errors.append('Introduction message is not shown!')
chats[0].accept_group_invitation_button.wait_and_click()
chats[0].accept_membership_for_group_chat_via_chat_view(usernames[2], accept=False)
chats[0].click_system_back_button()
chats[2].just_fyi('Member_2: retry request')
chats[2].retry_group_invite_button.wait_and_click()
chats[2].request_membership_for_group_chat(introduction_messages[0])
chats[2].just_fyi('Admin: decline request for Member_2')
chats[0].group_membership_request_button.wait_and_click()
chats[0].element_by_text(usernames[2]).click()
if not chats[0].element_by_text_part(introduction_messages[0]).is_element_displayed():
self.errors.append('Introduction message that was set after retrying attempt is not shown for admin!')
chats[0].decline_group_invitation_button.wait_and_click()
chats[0].click_system_back_button()
chats[2].just_fyi('Member_2: remove chat')
chats[2].remove_group_invite_button.wait_and_click()
chats[2].just_fyi('Double check after relogin')
if chats[0].group_membership_request_button.is_element_displayed():
self.errors.append('Group membership request is still shown when there are no pending requests anymore')
[homes[i].relogin() for i in range(0, 3)]
if homes[2].element_by_text_part(chat_name).is_element_displayed():
self.errors.append('Group chat was not removed when removing after declining group invite')
[home.get_chat(chat_name).click() for home in (homes[0], homes[1])]
if chats[0].group_membership_request_button.is_element_displayed():
self.errors.append(
'Group membership request is shown after relogin when there are no pending requests anymore')
join_system_message = chats[0].join_system_message(usernames[1])
for chat in (chats[1], chats[0]):
if not chat.chat_element_by_text(join_system_message).is_element_displayed():
self.errors.append('%s is not shown after joining to group chat via invite' % join_system_message)
self.errors.verify_no_errors()
@marks.testrail_id(4001)
def test_group_chat_remove_member(self):
self.create_drivers(3)
homes, public_keys, usernames, chats = {}, {}, {}, {}
for key in self.drivers:
sign_in_view = SignInView(self.drivers[key])
homes[key] = sign_in_view.create_user()
public_keys[key], usernames[key] = sign_in_view.get_public_key_and_username(True)
sign_in_view.home_button.click()
chat_name = homes[0].get_random_chat_name()
for i in range(1, 3):
homes[0].add_contact(public_keys[i])
homes[0].get_back_to_home_view()
chats[0] = homes[0].create_group_chat([usernames[1],
usernames[2]], chat_name)
chats[0].just_fyi('Member_1, member_2: both users join to group chat')
for i in range(1, 3):
chats[i] = homes[i].get_chat(chat_name).click()
chats[i].join_chat_button.click()
chats[0].just_fyi("Admin: get options for device 2 in group chat and remove him")
options = chats[0].get_user_options(usernames[1])
options.remove_user_button.click()
left_message = chats[0].leave_system_message(usernames[1])
for key in chats:
if not chats[key].chat_element_by_text(left_message).is_element_displayed():
self.errors.append("Message with text '%s' was not received" % left_message)
chats[0].just_fyi("Check that input field is not available after removing")
if chats[1].chat_message_input.is_element_displayed():
self.errors.append("Message input is still available for removed user")
chats[0].just_fyi("Send message and check that it is available only for remaining users")
message = 'after removing member'
chats[0].send_message(message)
for chat in (chats[0], chats[2]):
if not chat.chat_element_by_text(message).is_element_displayed(30):
self.errors.append("Message '%s' was not received after removing member" % message)
if chats[1].chat_element_by_text(message).is_element_displayed():
self.errors.append("Message '%s' was received by removed member" % message)
self.errors.verify_no_errors()
@marks.testrail_id(6294)
# TODO: may be merged with 6295 to group and add more tx tests
def test_keycard_1_1_chat_command_request_and_send_tx_stt_in_1_1_chat_offline_opened_from_push(self):
@@ -1468,7 +1091,7 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
@marks.testrail_id(695856)
# TODO: can be included to TestPairingMultipleDevicesMerged after #13257
def test_pairing_devices_sync_photo_community_group_chats(self):
def test_pair_devices_sync_photo_community_group_chats(self):
self.create_drivers(2)
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
home_1 = device_1.create_user()
@@ -1585,9 +1208,11 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
recovery_phrase = profile_1.get_recovery_phrase()
profile_1.close_button.click()
profile_1.home_button.click()
device_2.put_app_to_background_and_back()
home_3 = device_3.create_user()
public_key_3, username_3 = home_3.get_public_key_and_username(return_username=True)
device_3.home_button.click()
device_1.put_app_to_background_and_back()
device_1_name, device_2_name, group_chat_name = 'creator', 'paired', 'some group chat'
home_2 = device_2.recover_access(passphrase=' '.join(recovery_phrase.values()))
+2 -297
View File
@@ -16,200 +16,6 @@ from views.sign_in_view import SignInView
@marks.medium
class TestChatManagement(SingleDeviceTestCase):
@marks.testrail_id(5426)
# TODO: add to any group with history in public chat
def test_chat_public_clear_history_via_options_and_long_press(self):
home = SignInView(self.driver).create_user()
public_options, public_long_press = '#public-clear-options', '#public-long-options'
message = 'test message'
for pub_chat in [public_options[1:], public_long_press[1:]]:
chat = home.join_public_chat(pub_chat)
[chat.send_message(message) for _ in range(2)]
home.home_button.double_click()
home.just_fyi('Clearing history via long press')
home.clear_chat_long_press(public_long_press)
home.just_fyi('Clearing history via options')
chat = home.get_chat(public_options).click()
chat.clear_history()
if chat.element_by_text(message).is_element_displayed():
self.errors.append(
'Messages in %s chat are still shown after clearing history via options' % public_options)
home.just_fyi("Recheck that history won't reappear after relogin")
home.relogin()
for chat_name in public_options, public_long_press:
chat = home.get_chat(chat_name).click()
if chat.chat_element_by_text(message).is_element_displayed():
self.errors.append('Messages in %s chat are shown after clearing history and relaunch' % chat_name)
chat.home_button.click()
self.errors.verify_no_errors()
@marks.testrail_id(6320)
# TODO: to unite it with 5757
def test_chat_can_start_from_suggestions_using_search(self):
sign_in = SignInView(self.driver)
home = sign_in.create_user()
chat = ChatView(self.driver)
ens_name_status, ens_name_another_domain, public_chat_name = ens_user_ropsten['ens'], \
ens_user['ens_another'], 'some-pub-chat'
search_list = {
ens_name_status: {
'home': {
'Start a new private chat',
'Join a public chat',
'%s.stateofus.eth' % ens_name_status,
'#%s' % ens_name_status
},
'navigate_to': '%s.stateofus.eth' % ens_name_status,
'chat': {
chat.add_to_contacts,
chat.element_by_text('@%s' % ens_name_status),
chat.chat_message_input
}
},
ens_name_another_domain: {
'home': {
'Start a new private chat',
},
'home_not_shown': 'Join a public chat',
'navigate_to': 'Start a new private chat',
'chat': {
chat.add_to_contacts,
chat.element_by_text('@%s' % ens_name_another_domain),
chat.chat_message_input
},
},
public_chat_name: {
'home': {
'Join a public chat',
},
'home_not_shown': 'Start a new private chat',
'navigate_to': '#%s' % public_chat_name,
'chat': {
chat.element_by_text('#%s' % public_chat_name),
chat.chat_message_input
},
},
}
home.just_fyi('Join public chat to have search input on home view')
chat_name = home.get_random_chat_name()
public_chat = home.join_public_chat(chat_name)
public_chat.get_back_to_home_view()
home.swipe_down()
for keyword in search_list:
home.just_fyi('Can start chat from searching for %s' % keyword)
home.search_by_keyword(keyword)
if not home.element_by_text_part('No search results. Do you mean').is_element_displayed():
self.errors.append('"No search results" is not shown')
if 'home_not_shown' in search_list[keyword]:
if home.element_by_text(search_list[keyword]['home_not_shown']).is_element_displayed():
self.errors.append('%s is shown on home view while searching for %s' % (
search_list[keyword]['home_not_shown'], keyword))
for text in search_list[keyword]['home']:
if not home.element_by_text(text).is_element_displayed():
self.errors.append('%s is not shown on home view while searching for %s' % (text, keyword))
home.element_by_text(search_list[keyword]['navigate_to']).click()
for element in search_list[keyword]['chat']:
if not element.is_element_displayed():
self.errors.append(
'Requested %s element is not shown on chat view after navigating from suggestion '
'for %s' % (element.name, keyword))
home.back_button.click()
home.just_fyi('No suggestion at attempt to search for invalid data')
invalid_data = [' ', 'ab;', '.6', '@ana']
for text in invalid_data:
home.search_by_keyword(text)
if home.element_by_text_part('No search results. Do you mean').is_element_displayed():
self.errors.append('"No search results" is shown when searching for invalid value %s' % text)
home.cancel_button.click()
self.errors.verify_no_errors()
@marks.testrail_id(5757)
# TODO: to unite it with 6320 and cut the part about offline - to add it to the end of group for instance
def test_chat_search_on_home(self):
sign_in = SignInView(self.driver)
home = sign_in.create_user()
home.just_fyi('Join public chat, start 1-1 with username and with ENS')
chat_name = home.get_random_chat_name()
public_chat = home.join_public_chat(chat_name)
public_chat.get_back_to_home_view()
for public_key in (basic_user['public_key'], ens_user_ropsten['ens'], dummy_user['public_key']):
chat = home.add_contact(public_key)
chat.get_back_to_home_view()
profile = home.profile_button.click()
profile.open_contact_from_profile(dummy_user['username'])
nickname = 'dummy_user'
public_chat.set_nickname(nickname)
public_chat.home_button.click()
search_list = {
basic_user['username']: basic_user['username'],
ens_user_ropsten['username']: ens_user_ropsten['ens'],
chat_name: chat_name,
nickname: nickname,
dummy_user['username']: nickname,
ens_user_ropsten['ens']: ens_user_ropsten['ens']
}
home.just_fyi('Can search for public chat name, ens name, username')
home.swipe_down()
for keyword in search_list:
home.search_by_keyword(keyword)
search_results = home.chat_name_text.find_elements()
if not search_results:
self.errors.append('No search results after searching by %s keyword' % keyword)
for element in search_results:
if search_list[keyword] not in element.text:
self.errors.append("'%s' is shown on the home screen after searching by '%s' keyword" %
(element.text, keyword))
home.cancel_button.click()
home.just_fyi('Can search for public chat while offline')
home.toggle_airplane_mode()
home.search_input.click()
home.search_input.send_keys(chat_name)
search_results = home.chat_name_text.find_elements()
if not search_results:
self.errors.append('No search results after searching by %s keyword' % chat_name)
for element in search_results:
if search_list[chat_name] not in element.text:
self.errors.append("'%s' is shown on the home screen after searching by '%s' keyword" %
(element.text, chat_name))
self.errors.verify_no_errors()
@marks.testrail_id(5322)
# TODO: merge with other 1-driver medium e2e
def test_chat_input_delete_cut_and_paste_messages(self):
sign_in = SignInView(self.driver)
home = sign_in.create_user()
chat = home.add_contact(transaction_senders['N']['public_key'])
message_text = 'test'
message_input = chat.chat_message_input
message_input.send_keys(message_text)
message_input.delete_last_symbols(2)
current_text = message_input.text
if current_text != message_text[:-2]:
self.driver.fail("Message input text '%s' doesn't match expected '%s'" % (current_text, message_text[:-2]))
message_input.cut_text()
message_input.paste_text_from_clipboard()
chat.send_message_button.click()
chat.chat_element_by_text(message_text[:-2]).wait_for_visibility_of_element(2)
@marks.testrail_id(6221)
# TODO: delete? a lot of tests are already using this
@@ -249,108 +55,6 @@ class TestChatManagement(SingleDeviceTestCase):
if profile.element_by_text(basic_user["username"]).is_element_displayed():
self.driver.fail("Unblocked user not added previously in contact list added in contacts!")
@marks.testrail_id(5498)
# TODO: split into separate parts and include to group
def test_share_user_profile_url_public_chat(self):
home = SignInView(self.driver).create_user()
home.just_fyi('Join to one-to-one chat and share link to other user profile via messenger')
chat = home.add_contact(dummy_user["public_key"])
chat.chat_options.click()
chat.view_profile_button.click_until_presence_of_element(chat.remove_from_contacts)
chat.profile_details.click()
chat.share_button.click()
chat.share_via_messenger()
if not chat.element_by_text_part(
'https://join.status.im/u/%s' % dummy_user["public_key"]).is_element_present():
self.errors.append("Can't share public key of contact")
for _ in range(2):
chat.click_system_back_button()
home.just_fyi('Join to public chat and share link to it via messenger')
chat.get_back_to_home_view()
public_chat_name = 'pubchat'
public_chat = home.join_public_chat(public_chat_name)
public_chat.chat_options.click()
public_chat.share_chat_button.click()
public_chat.share_via_messenger()
if not chat.element_by_text_part('https://join.status.im/%s' % public_chat_name).is_element_present():
self.errors.append("Can't share link to public chat")
for _ in range(2):
chat.click_system_back_button()
chat.get_back_to_home_view()
home.just_fyi('Open URL and share link to it via messenger')
daap = home.dapp_tab_button.click()
browser = daap.open_url('dap.ps')
browser.options_button.click()
browser.share_url_button.click()
browser.share_via_messenger()
expeceted_text_1 = 'https://join.status.im/b/https://dap.ps'
expeceted_text_2 = 'https://join.status.im/b/http://dap.ps'
if not (chat.element_by_text_part(expeceted_text_1).is_element_present() or
chat.element_by_text_part(expeceted_text_2).is_element_present()):
self.errors.append("Can't share link to URL")
self.errors.verify_no_errors()
@marks.testrail_id(5441)
# TODO: merge with other 1-driver medium e2e
def test_deep_link_open_user_profile(self):
sign_in = SignInView(self.driver)
sign_in.create_user()
for user_ident in ens_user['ens'], ens_user['ens_another'], ens_user['public_key']:
self.driver.close_app()
deep_link = 'status-im://u/%s' % user_ident
sign_in.open_weblink_and_login(deep_link)
chat = sign_in.get_chat_view()
for text in ens_user['username'], sign_in.get_translation_by_key("add-to-contacts"):
if not chat.element_by_text(text).scroll_to_element(10):
self.driver.fail("User profile screen is not opened")
@marks.testrail_id(5442)
# TODO: merge with other 1-driver medium e2e
def test_deep_link_open_dapp(self):
sign_in_view = SignInView(self.driver)
sign_in_view.create_user()
self.driver.close_app()
dapp_name = test_dapp_url
dapp_deep_link = 'status-im://b/%s' % dapp_name
sign_in_view.open_weblink_and_login(dapp_deep_link)
web_view = sign_in_view.get_chat_view()
try:
test_dapp_view = web_view.open_in_status_button.click()
test_dapp_view.allow_button.is_element_present()
except NoSuchElementException:
self.driver.fail("DApp '%s' is not opened!" % dapp_name)
@marks.testrail_id(5781)
# TODO: merge with other 1-driver medium e2e
def test_deep_link_with_invalid_user_public_key_own_profile_key(self):
sign_in = SignInView(self.driver)
sign_in.recover_access(passphrase=basic_user['passphrase'])
self.driver.close_app()
sign_in.just_fyi('Check that no error when opening invalid deep link')
deep_link = 'status-im://u/%s' % basic_user['public_key'][:-10]
sign_in.open_weblink_and_login(deep_link)
home = sign_in.get_home_view()
home.plus_button.click_until_presence_of_element(home.start_new_chat_button)
if not home.start_new_chat_button.is_element_present():
self.errors.append(
"Can't navigate to start new chat after app opened from deep link with invalid public key")
self.driver.close_app()
sign_in.just_fyi('Check that no error when opening invalid deep link')
deep_link = 'status-im://u/%s' % basic_user['public_key']
sign_in.open_weblink_and_login(deep_link)
from views.profile_view import ProfileView
profile = ProfileView(self.driver)
if not profile.default_username_text != basic_user['username']:
self.errors.append("Can't navigate to profile from deep link with own public key")
self.errors.verify_no_errors()
@marks.testrail_id(6319)
# TODO: merge with other 1-driver medium e2e
def test_permissions_deny_access_camera_and_gallery(self):
@@ -892,7 +596,7 @@ class TestChatManagement(SingleDeviceTestCase):
wallet.click_system_back_button()
wallet.just_fyi("Send transaction to new account")
transaction_amount = '0.004'
transaction_amount = '0.006'
initial_balance = self.network_api.get_balance(status_account_address)
wallet.send_transaction(account_name=account_name, amount=transaction_amount, keycard=True)
self.network_api.wait_for_confirmation_of_transaction(status_account_address, transaction_amount)
@@ -1151,6 +855,7 @@ class TestChatManagement(SingleDeviceTestCase):
home.just_fyi("Checking setting pairing with new PIN")
profile.change_pairing_code_button.click()
keycard.enter_another_pin()
sign_in.create_password_input.wait_for_element()
sign_in.create_password_input.set_value(common_password)
sign_in.confirm_your_password_input.set_value(common_password + "1")
if not keycard.element_by_translation_id("pairing-code_error1").is_element_displayed():
+1 -1
View File
@@ -7,7 +7,7 @@ from support.api.third_parties_api import get_token_info
def get_parameters():
directory = os.sep.join(__file__.split(os.sep)[:-4])
directory = os.sep.join(__file__.split(os.sep)[:-5])
file_path = path.join(directory, 'src/status_im/ethereum/tokens.cljs')
with open(file_path, 'r') as f:
data = f.read()
+17 -1
View File
@@ -346,6 +346,21 @@ class BaseView(object):
for _ in range(times):
self.driver.press_keycode(4)
def click_system_back_button_until_element_is_shown(self, attempts=3, element='home'):
counter = 0
if element is 'home':
element = self.home_button
while not element.is_element_present(1) and counter <= attempts:
try:
self.driver.press_keycode(4)
element.is_element_present(5)
return self
except (NoSuchElementException, TimeoutException):
counter += 1
else:
self.driver.info("Could not reach %s element by pressing back" % element.name)
def get_app_from_background(self):
self.driver.info('Get Status back from Recent apps')
self.driver.press_keycode(187)
@@ -564,7 +579,8 @@ class BaseView(object):
def share_via_messenger(self):
self.driver.info("Sharing via messenger", device=False)
self.element_by_text('Messages').wait_and_click(40)
self.element_by_text('Messages').wait_for_visibility_of_element(40)
self.element_by_text('Messages').click_until_presence_of_element(self.element_by_text('New message'))
self.element_by_text('New message').wait_and_click()
self.send_as_keyevent('+0100100101')
self.confirm()
+1 -1
View File
@@ -881,7 +881,7 @@ class ChatView(BaseView):
self.get_stickers.click()
element = Button(self.driver,
xpath="//*[@content-desc='sticker-pack-name'][@text='%s']/..//*[@content-desc='sticker-pack-price']" % pack_name)
element.scroll_to_element()
element.scroll_to_element(depth=21)
element.click()
element.wait_for_invisibility_of_element()
self.back_button.click()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1006 B

After

Width:  |  Height:  |  Size: 295 B

+1 -1
View File
@@ -38,7 +38,7 @@ class AccountElementButton(SilentButton):
def color_matches(self, expected_color_image_name: str):
amount_text = Text(self.driver, xpath="%s//*[@content-desc='account-total-value']" % self.locator)
amount_text.wait_for_element_text('0', 60)
amount_text.wait_for_element_text('...', 60)
return not amount_text.is_element_differs_from_template(expected_color_image_name)
+4 -4
View File
@@ -7491,10 +7491,10 @@ react-native-share@^7.0.1:
resolved "https://registry.yarnpkg.com/react-native-share/-/react-native-share-7.0.1.tgz#1deef27afcd8275222ba0efeac337e7cea99bc4b"
integrity sha512-hq7nOirgih/zIF9UU9FuYKZ3NGvasu2c/eJesvyPKYiykTtgQZM+mvDwFk/ogEsGwRtTPJBtj8/6IyIFcGa7lw==
react-native-splash-screen@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.2.0.tgz#d47ec8557b1ba988ee3ea98d01463081b60fff45"
integrity sha512-Ls9qiNZzW/OLFoI25wfjjAcrf2DZ975hn2vr6U9gyuxi2nooVbzQeFoQS5vQcbCt9QX5NY8ASEEAtlLdIa6KVg==
react-native-splash-screen@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.3.0.tgz#3af71ed17afe50fee69590a45aec399d071ead02"
integrity sha512-rGjt6HkoSXxMqH4SQUJ1gnPQlPJV8+J47+4yhgTIan4bVvAwJhEeJH7wWt9hXSdH4+VfwTS0GTaflj1Tw83IhA==
"react-native-status-keycard@git+https://github.com/status-im/react-native-status-keycard.git#refs/tags/v2.5.37":
version "2.5.37"