Fix component tests, upgrade Jest & friends, and a few other goodies (#18276)

Fix all component tests after the latest RN upgrade.

Fixes https://github.com/status-im/status-mobile/issues/18157
Closes https://github.com/status-im/status-mobile/pull/18235

Dependency changes

- Upgraded Jest: from 26.6.3 to latest 29.7.0.
- Upgraded @testing-library/jest-native: from 5.3.0 to latest 5.4.3
- Upgraded @testing-library/react-native: from 11.5.4 to 12.4.2
- Removed explicit dependency on jest-circus, this is now the default test
  runner.
- Removed explicit dependency on jest-environment-node. This is handled by the
  package manager.
- Added jest-silent-reporter at version 0.5.0.

### Why component tests were failing?

Many tests were failing because we were using RN Testing Library (RNTL) in an
unreliable fashion. With the recent library upgrades, the unreliability was
excerbated. Other times, the tests were incorrectly arranging data.

### with-redefs does not work with async code

Generally speaking, with-redefs should not be used with async code, assume the
worst. The scope of the macro will cease to exist by the time the async code
runs. In many tests we were using with-redefs, then calling render, but for some
components that use use-effect, JS timers, animations, etc it's unreliable and
were the reason for failures.

It's easy to reproduce too:

```clojure
(defn foo []
  :foo)

(foo)
;; => :foo

(with-redefs [foo (constantly :bar)]
  (foo))
;; => :bar

(js/setTimeout
 (fn []
   (tap> [:calling-foo (foo)]))
 100)
;; Taps [:calling-foo :foo]
;; As you would expect, when running without with-redefs, it prints :foo.

;; So far so good, but whatch what happens with async code:

(with-redefs [foo (constantly :bar)]
  (js/setTimeout
   (fn []
     (tap> [:calling-foo (foo)]))
   100))
;; Taps [:calling-foo :foo]
;; ====> PROBLEM: Taps :foo, not :bar as one might expect
```

### Not waiting on wait-for

When test-helpers.component/wait-for is used, subsequent assertions/etc should
be done after the promise returned by wait-for is resolved. But remember to not
perform side-effects inside the wait-for callback (check out the docs
https://callstack.github.io/react-native-testing-library/docs/api#waitfor).
Most, if not all of our usages of wait-for were not waiting.

#### Improvement 1 - Silence Jest on demand

If you need to re-run component tests frequently, you may want to reduce the
output verbosity. By passing JEST_USE_SILENT_REPORTER=true to make
component-test or make component-test-watch you will see a lot less noise and be
able to focus on what really matters to you.

#### Improvement 2 - Selectively focus/disable tests

Because of our need to first compile CLJS to JS before running tests via Jest,
we couldn't easily skip or focus on specific tests. From this commit onwards, we
should never again have to change the list of requires in files core_spec.cljs.
Commenting out required namespaces gives a bad DX because it causes constant
rebasing issues.

#### Improvement 3 - Translations now work as in prod code (but only English)

Translations performed by *-by-translation-text can be done now without any
workaround under the hood. The query functions are now linted just like
i18n/label, which means static translation keywords must be qualified with :t/,
which is good for consistency.
This commit is contained in:
Icaro Motta 2023-12-26 11:58:23 -03:00 committed by GitHub
parent 46c5f42fad
commit 0b4a1545ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1273 additions and 2461 deletions

View File

@ -1,2 +1,8 @@
{:hooks {:analyze-call {utils.i18n/label utils.i18n/label}} {:hooks
{:analyze-call
{utils.i18n/label utils.i18n/label
test-helpers.component/get-all-by-translation-text utils.i18n/label
test-helpers.component/get-by-translation-text utils.i18n/label
test-helpers.component/query-all-by-translation-text utils.i18n/label
test-helpers.component/query-by-translation-text utils.i18n/label}}
:linters {:status-im.linter/invalid-translation-keyword {:level :error}}} :linters {:status-im.linter/invalid-translation-keyword {:level :error}}}

View File

@ -18,13 +18,17 @@
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-bl ;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-bl
:respect-bl :respect-bl
;; hang multiline left-hand-thing ;; hang multiline left-hand-thing
;; https://github.com/kkinnear/zprint/issues/273 ;; https://github.com/kkinnear/zprint/issues/273
:multi-lhs-hang] :multi-lhs-hang]
:fn-map :fn-map
{"reg-sub" :arg1-pair {"reg-sub" :arg1-pair
"h/describe" :arg1-body "h/describe" :arg1-body
"h/describe-skip" :arg1-body
"h/describe-only" :arg1-body
"h/test" :arg1-body "h/test" :arg1-body
"h/test-skip" :arg1-body
"h/test-only" :arg1-body
"global.describe" :arg1-body "global.describe" :arg1-body
"global.test" :arg1-body "global.test" :arg1-body
"list-comp" :binding "list-comp" :binding

View File

@ -327,7 +327,7 @@ lint-fix: ##@test Run code style checks and fix issues
ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \ ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \ zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \
zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \ zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \
clojure-lsp --ns-exclude-regex ".*/src/status_im/core\.cljs$$" clean-ns && \ clojure-lsp --ns-exclude-regex ".*/src/status_im/core\.cljs|.*/src/test_helpers/component_tests_preload\.cljs$$" clean-ns && \
sh scripts/lint/trailing-newline.sh --fix && \ sh scripts/lint/trailing-newline.sh --fix && \
node_modules/.bin/prettier --write . node_modules/.bin/prettier --write .
@ -364,6 +364,7 @@ android-test:
component-test-watch: export TARGET := clojure component-test-watch: export TARGET := clojure
component-test-watch: export COMPONENT_TEST := true component-test-watch: export COMPONENT_TEST := true
component-test-watch: export BABEL_ENV := test component-test-watch: export BABEL_ENV := test
component-test-watch: export JEST_USE_SILENT_REPORTER := false
component-test-watch: ##@ Watch tests and re-run no changes to cljs files component-test-watch: ##@ Watch tests and re-run no changes to cljs files
@@scripts/check-metro-shadow-process.sh @@scripts/check-metro-shadow-process.sh
rm -rf ./component-spec rm -rf ./component-spec
@ -373,6 +374,7 @@ component-test-watch: ##@ Watch tests and re-run no changes to cljs files
component-test: export TARGET := clojure component-test: export TARGET := clojure
component-test: export COMPONENT_TEST := true component-test: export COMPONENT_TEST := true
component-test: export BABEL_ENV := test component-test: export BABEL_ENV := test
component-test: export JEST_USE_SILENT_REPORTER := false
component-test: ##@test Run component tests once in NodeJS component-test: ##@test Run component tests once in NodeJS
@scripts/check-metro-shadow-process.sh @scripts/check-metro-shadow-process.sh
rm -rf ./component-spec rm -rf ./component-spec

View File

@ -12,8 +12,7 @@ stdenv.mkDerivation {
"patchBuildIdPhase" "patchBuildIdPhase"
"patchKeyChainPhase" "patchKeyChainPhase"
"patchGlogPhase" "patchGlogPhase"
"patchJestPhase" "installPhase"
"installPhase"
]; ];
# First symlink all modules as is # First symlink all modules as is
@ -56,13 +55,6 @@ stdenv.mkDerivation {
'-Wl,--build-id=none' '-Wl,--build-id=none'
''; '';
# Remove when we upgrade jest to 29
patchJestPhase = ''
substituteInPlace ./node_modules/react-native/jest/setup.js --replace \
'jest.now()' \
'Date.now'
'';
installPhase = '' installPhase = ''
mkdir -p $out mkdir -p $out
cp -R node_modules $out/ cp -R node_modules $out/

View File

@ -82,18 +82,17 @@
"@babel/register": "7.0.0", "@babel/register": "7.0.0",
"@jest/globals": "^25.1.0", "@jest/globals": "^25.1.0",
"@mapbox/node-pre-gyp": "^1.0.9", "@mapbox/node-pre-gyp": "^1.0.9",
"@testing-library/jest-native": "^5.0.0", "@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^11.2.0", "@testing-library/react-native": "^12.4.2",
"@tsconfig/react-native": "^3.0.0", "@tsconfig/react-native": "^3.0.0",
"@types/jest": "^28.1.6", "@types/jest": "^28.1.6",
"@types/react": "^18.0.24", "@types/react": "^18.0.24",
"@types/react-test-renderer": "^18.0.0", "@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.7.0", "babel-jest": "^29.7.0",
"concurrently": "^7.6.0", "concurrently": "^7.6.0",
"jest": "^26.6.3", "jest": "^29.7.0",
"jest-circus": "^26.0.0",
"jest-environment-node": "^26.6.2",
"jest-image-snapshot": "^5.1.0", "jest-image-snapshot": "^5.1.0",
"jest-silent-reporter": "^0.5.0",
"metro-react-native-babel-preset": "0.76.8", "metro-react-native-babel-preset": "0.76.8",
"nodemon": "^2.0.16", "nodemon": "^2.0.16",
"nyc": "^14.1.1", "nyc": "^14.1.1",

View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
if rg --quiet --multiline '^\(ns.*\n^\s*\(:require\n(^\s*(;|#_).*\n)*(^\s*\[status-im\.setup\.i18n-resources\W)' src/status_im/core.cljs; then if rg --quiet --multiline '^\(ns.*\n^\s*\(:require\n(^\s*(;|#_).*\n)*(^\s*\[status-im\.setup\.i18n-resources\W)' src/status_im/core.cljs src/test_helpers/component_tests_preload.cljs; then
exit 0 exit 0
elif [ $? -eq 1 ]; then elif [ $? -eq 1 ]; then
echo "status-im.setup.i18n-resources must be loaded first (be the first one in ns :require form) in status-im.core" echo "status-im.setup.i18n-resources must be loaded first (be the first one in ns :require form) in status-im.core"

View File

@ -150,9 +150,10 @@
:source-map false}} :source-map false}}
:component-test {:target :npm-module :component-test {:target :npm-module
:entries [;; We need to tell shadow-cljs to compile :entries [;; We need to tell shadow-cljs to compile
;; the preloads namespace because it will ;; the preloads namespaces because they
;; be used directly by Jest in the option ;; will be used directly by Jest in the
;; setupFilesAfterEnv. ;; option setupFilesAfterEnv.
test-helpers.component-tests-preload
status-im.setup.schema-preload status-im.setup.schema-preload
quo.core-spec quo.core-spec
@ -161,5 +162,6 @@
:output-dir "component-spec" :output-dir "component-spec"
:closure-defines {schema.core/throw-on-error? true} :closure-defines {schema.core/throw-on-error? true}
:compiler-options {:warnings-as-errors false :compiler-options {:warnings-as-errors false
:warnings {:fn-deprecated false}
:static-fns false :static-fns false
:infer-externs true}}}} :infer-externs true}}}}

View File

@ -63,14 +63,7 @@
:track-icon :face-id}) :track-icon :face-id})
(h/describe "slide-button" (h/describe "slide-button"
(h/before-each (h/setup-fake-timers)
(fn []
(h/use-fake-timers)))
(h/after-each
(fn []
(h/clear-all-timers)
(h/use-real-timers)))
(h/test "render the correct text" (h/test "render the correct text"
(h/render [slide-button/view default-props]) (h/render [slide-button/view default-props])

View File

@ -17,5 +17,3 @@
(h/fire-event :press (get (h/get-all-by-label-text :color-picker-item) 0)) (h/fire-event :press (get (h/get-all-by-label-text :color-picker-item) 0))
(-> (h/expect @selected) (-> (h/expect @selected)
(.toStrictEqual :blue))))) (.toStrictEqual :blue)))))

View File

@ -27,14 +27,18 @@
(let [selected (reagent/atom default-selected) (let [selected (reagent/atom default-selected)
{window-width :width} (rn/get-window) {window-width :width} (rn/get-window)
ref (atom nil)] ref (atom nil)]
(rn/use-effect #(js/setTimeout (fn [] (rn/use-effect
(.scrollToIndex ^js @ref (fn []
#js (js/setTimeout
{:animated true (fn []
:index (.indexOf colors/account-colors (let [index (.indexOf colors/account-colors default-selected)]
default-selected) (when (and @ref (>= index 0))
:viewPosition 0.5})) (some-> ^js @ref
50)) (.scrollToIndex #js
{:animated true
:index index
:viewPosition 0.5})))))
50)))
[rn/flat-list [rn/flat-list
{:ref #(reset! ref %) {:ref #(reset! ref %)
;; TODO: using :feng-shui? temporarily while b & w is being developed. ;; TODO: using :feng-shui? temporarily while b & w is being developed.

View File

@ -6,34 +6,25 @@
(h/describe "select-profile component" (h/describe "select-profile component"
(h/test "render component" (h/test "render component"
(h/render [strength-divider/view {:type :okay}]) (h/render [strength-divider/view {:type :okay}])
(-> (h/expect (h/get-by-label-text :strength-divider)) (h/is-truthy (h/get-by-label-text :strength-divider)))
(.toBeTruthy)))
(h/test "render component with :type :very-weak" (h/test "render component with :type :very-weak"
(h/render [strength-divider/view {:type :very-weak}]) (h/render [strength-divider/view {:type :very-weak}])
(-> (h/expect (h/get-by-translation-text :strength-divider-very-weak-label)) (h/is-truthy (h/get-by-translation-text :t/strength-divider-very-weak-label)))
(.toBeTruthy)))
(h/test "render component with :type :weak" (h/test "render component with :type :weak"
(h/render [strength-divider/view {:type :weak}]) (h/render [strength-divider/view {:type :weak}])
(-> (h/expect (h/get-by-translation-text :strength-divider-weak-label)) (h/is-truthy (h/get-by-translation-text :t/strength-divider-weak-label)))
(.toBeTruthy)))
(h/test "render component with :type :okay" (h/test "render component with :type :okay"
(h/render [strength-divider/view {:type :okay}]) (h/render [strength-divider/view {:type :okay}])
(-> (h/expect (h/get-by-translation-text :strength-divider-okay-label)) (h/is-truthy (h/get-by-translation-text :t/strength-divider-okay-label)))
(.toBeTruthy)))
(h/test "render component with :type :strong" (h/test "render component with :type :strong"
(h/render [strength-divider/view {:type :strong}]) (h/render [strength-divider/view {:type :strong}])
(-> (h/expect (h/get-by-translation-text :strength-divider-strong-label)) (h/is-truthy (h/get-by-translation-text :t/strength-divider-strong-label)))
(.toBeTruthy)))
(h/test "render component with :type :very-strong" (h/test "render component with :type :very-strong"
(h/render [strength-divider/view {:type :very-strong}]) (h/render [strength-divider/view {:type :very-strong}])
(-> (h/expect (h/get-by-translation-text :strength-divider-very-strong-label)) (h/is-truthy (h/get-by-translation-text :t/strength-divider-very-strong-label)))
(.toBeTruthy)))
(h/test "render component with :type :alert" (h/test "render component with :type :alert"
(h/render [strength-divider/view {:type :alert} "Text message"]) (h/render [strength-divider/view {:type :alert} "Text message"])
(-> (h/expect (h/get-by-text "Text message")) (h/is-truthy (h/get-by-text "Text message")))
(.toBeTruthy)))
(h/test "render component with :type :info" (h/test "render component with :type :info"
(h/render [strength-divider/view {:type :info} "Text Info"]) (h/render [strength-divider/view {:type :info} "Text Info"])
(-> (h/expect (h/get-by-text "Text Info")) (h/is-truthy (h/get-by-text "Text Info"))))
(.toBeTruthy))))

View File

@ -6,14 +6,7 @@
[test-helpers.component :as h])) [test-helpers.component :as h]))
(h/describe "drawer-buttons" (h/describe "drawer-buttons"
(h/before-each (h/setup-fake-timers)
(fn []
(h/use-fake-timers)))
(h/after-each
(fn []
(h/clear-all-timers)
(h/use-real-timers)))
(h/test "the top heading and subheading render" (h/test "the top heading and subheading render"
(h/render [drawer-buttons/view (h/render [drawer-buttons/view
@ -37,7 +30,6 @@
(-> (js/expect (h/get-by-text "bottom-sub-heading")) (-> (js/expect (h/get-by-text "bottom-sub-heading"))
(.toBeTruthy))) (.toBeTruthy)))
(h/test "it clicks the top card" (h/test "it clicks the top card"
(let [event (h/mock-fn)] (let [event (h/mock-fn)]
(with-redefs [safe-area/get-top (fn [] 10)] (with-redefs [safe-area/get-top (fn [] 10)]

View File

@ -71,7 +71,7 @@
:icon-avatar :i/placeholder :icon-avatar :i/placeholder
:type :keypair}]) :type :keypair}])
(h/is-truthy (h/get-by-text "Title")) (h/is-truthy (h/get-by-text "Title"))
(-> (h/expect (h/get-by-translation-text :on-device)) (-> (h/expect (h/get-by-translation-text :t/on-device))
(.toBeTruthy))) (.toBeTruthy)))
(h/test "component renders in keypair type when keycard? is true" (h/test "component renders in keypair type when keycard? is true"
@ -81,7 +81,7 @@
:icon-avatar :i/placeholder :icon-avatar :i/placeholder
:type :keypair}]) :type :keypair}])
(h/is-truthy (h/get-by-text "Title")) (h/is-truthy (h/get-by-text "Title"))
(-> (h/expect (h/get-by-translation-text :on-keycard)) (-> (h/expect (h/get-by-translation-text :t/on-keycard))
(.toBeTruthy))) (.toBeTruthy)))
(h/test "component renders in default-keypair type" (h/test "component renders in default-keypair type"

View File

@ -13,7 +13,10 @@
{:time-frame :empty}]) {:time-frame :empty}])
(h/is-truthy (h/get-by-label-text :illustration))) (h/is-truthy (h/get-by-label-text :illustration)))
(h/test "render 1 week wallet graph" ;; NOTE: Throws error:
;; _reactNative.Animated.timing(widthValue2, {
;; Cannot read properties of undefined (reading 'timing')
(h/test-skip "render 1 week wallet graph"
(h/render [wallet-graph/view (h/render [wallet-graph/view
{:time-frame :1-week {:time-frame :1-week
:data (data 7)}]) :data (data 7)}])

View File

@ -18,7 +18,9 @@
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input {:ens-regex ens-regex}]) (h/render [address-input/address-input {:ens-regex ens-regex}])
(h/fire-event :on-focus (h/get-by-label-text :address-text-input)) (h/fire-event :on-focus (h/get-by-label-text :address-text-input))
(h/has-prop (h/get-by-label-text :address-text-input) :placeholder-text-color colors/neutral-40))) (h/has-prop (h/get-by-label-text :address-text-input)
:placeholder-text-color
colors/neutral-40)))
(h/test "on focus with blur? true" (h/test "on focus with blur? true"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
@ -38,27 +40,33 @@
{:scanned-value scanned-value {:scanned-value scanned-value
:on-change-text on-change-text :on-change-text on-change-text
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :clear-button))) (-> (h/wait-for #(h/get-by-label-text :clear-button))
(h/was-called-with on-change-text scanned-value) (.then (fn []
(h/has-prop (h/get-by-label-text :address-text-input) :default-value scanned-value)))) (h/was-called-with on-change-text scanned-value)
(h/has-prop (h/get-by-label-text :address-text-input)
:default-value
scanned-value)))))))
(h/test "clear icon is shown when input has text" (h/test "clear icon is shown when input has text"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input (h/render [address-input/address-input
{:scanned-value "scanned value" {:scanned-value "scanned value"
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :clear-button-container))) (-> (h/wait-for #(h/get-by-label-text :clear-button-container))
(h/wait-for #(h/is-truthy (h/get-by-label-text :clear-button))))) (.then #(h/is-truthy (h/get-by-label-text :clear-button))))))
(h/test "on blur with text and blur? false" (h/test "on blur with text and blur? false"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input (h/render [address-input/address-input
{:scanned-value "scanned value" {:scanned-value "scanned value"
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :clear-button))) (-> (h/wait-for #(h/get-by-label-text :clear-button))
(h/fire-event :on-focus (h/get-by-label-text :address-text-input)) (.then (fn []
(h/fire-event :on-blur (h/get-by-label-text :address-text-input)) (h/fire-event :on-focus (h/get-by-label-text :address-text-input))
(h/has-prop (h/get-by-label-text :address-text-input) :placeholder-text-color colors/neutral-30))) (h/fire-event :on-blur (h/get-by-label-text :address-text-input))
(h/has-prop (h/get-by-label-text :address-text-input)
:placeholder-text-color
colors/neutral-30))))))
(h/test "on blur with text blur? true" (h/test "on blur with text blur? true"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
@ -66,19 +74,22 @@
{:scanned-value "scanned value" {:scanned-value "scanned value"
:blur? true :blur? true
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :clear-button))) (-> (h/wait-for #(h/get-by-label-text :clear-button))
(h/fire-event :on-focus (h/get-by-label-text :address-text-input)) (.then (fn []
(h/fire-event :on-blur (h/get-by-label-text :address-text-input)) (h/fire-event :on-focus (h/get-by-label-text :address-text-input))
(h/has-prop (h/get-by-label-text :address-text-input) (h/fire-event :on-blur (h/get-by-label-text :address-text-input))
:placeholder-text-color (h/has-prop (h/get-by-label-text :address-text-input)
colors/neutral-80-opa-20))) :placeholder-text-color
colors/neutral-80-opa-20))))))
(h/test "on blur with no text and blur? false" (h/test "on blur with no text and blur? false"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input {:ens-regex ens-regex}]) (h/render [address-input/address-input {:ens-regex ens-regex}])
(h/fire-event :on-focus (h/get-by-label-text :address-text-input)) (h/fire-event :on-focus (h/get-by-label-text :address-text-input))
(h/fire-event :on-blur (h/get-by-label-text :address-text-input)) (h/fire-event :on-blur (h/get-by-label-text :address-text-input))
(h/has-prop (h/get-by-label-text :address-text-input) :placeholder-text-color colors/neutral-40))) (h/has-prop (h/get-by-label-text :address-text-input)
:placeholder-text-color
colors/neutral-40)))
(h/test "on blur with no text blur? true" (h/test "on blur with no text blur? true"
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
@ -98,9 +109,10 @@
{:scanned-value "scanned value" {:scanned-value "scanned value"
:on-clear on-clear :on-clear on-clear
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :clear-button))) (-> (h/wait-for #(h/get-by-label-text :clear-button))
(h/fire-event :press (h/get-by-label-text :clear-button)) (.then (fn []
(h/was-called on-clear)))) (h/fire-event :press (h/get-by-label-text :clear-button))
(h/was-called on-clear)))))))
(h/test "on-focus is called" (h/test "on-focus is called"
(let [on-focus (h/mock-fn)] (let [on-focus (h/mock-fn)]
@ -122,18 +134,22 @@
(let [on-scan (h/mock-fn)] (let [on-scan (h/mock-fn)]
(with-redefs [clipboard/get-string #(% "")] (with-redefs [clipboard/get-string #(% "")]
(h/render [address-input/address-input {:on-scan on-scan}]) (h/render [address-input/address-input {:on-scan on-scan}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :scan-button))) (-> (h/wait-for #(h/get-by-label-text :scan-button))
(h/fire-event :press (h/get-by-label-text :scan-button)) (.then (fn []
(h/was-called on-scan)))) (h/fire-event :press (h/get-by-label-text :scan-button))
(h/was-called on-scan)))))))
(h/test "paste from clipboard" (h/test "paste from clipboard"
(let [clipboard "clipboard"] (let [clipboard "clipboard"]
(with-redefs [clipboard/get-string #(% clipboard)] (with-redefs [clipboard/get-string #(% clipboard)]
(h/render [address-input/address-input {:ens-regex ens-regex}]) (h/render [address-input/address-input {:ens-regex ens-regex}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :paste-button))) (h/is-truthy (h/query-by-label-text :paste-button))
(h/fire-event :press (h/get-by-label-text :paste-button)) (h/fire-event :press (h/get-by-label-text :paste-button))
(h/wait-for #(h/is-truthy (h/get-by-label-text :clear-button))) (-> (h/wait-for #(h/get-by-label-text :clear-button))
(h/has-prop (h/get-by-label-text :address-text-input) :default-value clipboard)))) (.then (fn []
(h/has-prop (h/get-by-label-text :address-text-input)
:default-value
clipboard)))))))
(h/test "ENS loading state and call on-detect-ens" (h/test "ENS loading state and call on-detect-ens"
(let [clipboard "test.eth" (let [clipboard "test.eth"
@ -142,11 +158,12 @@
(h/render [address-input/address-input (h/render [address-input/address-input
{:on-detect-ens on-detect-ens {:on-detect-ens on-detect-ens
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :paste-button))) (h/is-truthy (h/query-by-label-text :paste-button))
(h/fire-event :press (h/get-by-label-text :paste-button)) (h/fire-event :press (h/get-by-label-text :paste-button))
(h/wait-for #(h/is-falsy (h/get-by-label-text :clear-button))) (-> (h/wait-for #(h/is-falsy (h/query-by-label-text :clear-button)))
(h/wait-for #(h/is-truthy (h/get-by-label-text :loading-button-container))) (.then (fn []
(h/was-called on-detect-ens)))) (h/wait-for #(h/get-by-label-text :loading-button-container))))
(.then #(h/was-called on-detect-ens))))))
(h/test "ENS valid state and call on-detect-ens" (h/test "ENS valid state and call on-detect-ens"
(let [clipboard "test.eth" (let [clipboard "test.eth"
@ -156,11 +173,12 @@
{:on-detect-ens on-detect-ens {:on-detect-ens on-detect-ens
:valid-ens-or-address? true :valid-ens-or-address? true
:ens-regex ens-regex}]) :ens-regex ens-regex}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :paste-button))) (h/is-truthy (h/query-by-label-text :paste-button))
(h/fire-event :press (h/get-by-label-text :paste-button)) (h/fire-event :press (h/get-by-label-text :paste-button))
(h/wait-for #(h/is-falsy (h/get-by-label-text :clear-button))) (-> (h/wait-for #(h/is-falsy (h/query-by-label-text :clear-button)))
(h/wait-for #(h/is-truthy (h/get-by-label-text :positive-button-container))) (.then (fn []
(h/was-called on-detect-ens)))) (h/wait-for #(h/get-by-label-text :positive-button-container))))
(.then #(h/was-called on-detect-ens))))))
(h/test "address loading state and call on-detect-address" (h/test "address loading state and call on-detect-address"
(let [clipboard "0x2f88d65f3cb52605a54a833ae118fb1363acccd2" (let [clipboard "0x2f88d65f3cb52605a54a833ae118fb1363acccd2"
@ -169,21 +187,24 @@
(h/render [address-input/address-input (h/render [address-input/address-input
{:on-detect-address on-detect-address {:on-detect-address on-detect-address
:address-regex address-regex}]) :address-regex address-regex}])
(h/wait-for #(h/is-truthy (h/get-by-label-text :paste-button))) (h/is-truthy (h/query-by-label-text :paste-button))
(h/fire-event :press (h/get-by-label-text :paste-button)) (h/fire-event :press (h/get-by-label-text :paste-button))
(h/wait-for #(h/is-falsy (h/get-by-label-text :clear-button))) (-> (h/wait-for #(h/is-falsy (h/query-by-label-text :clear-button)))
(h/wait-for #(h/is-truthy (h/get-by-label-text :loading-button-container))) (.then (fn []
(h/was-called on-detect-address)))) (h/wait-for #(h/get-by-label-text :loading-button-container))))
(.then #(h/was-called on-detect-address))))))
(h/test "address valid state and call on-detect-address" (h/test "address valid state and call on-detect-address"
(let [clipboard "0x2f88d65f3cb52605a54a833ae118fb1363acccd2" (let [clipboard "0x2f88d65f3cb52605a54a833ae118fb1363acccd2"
on-detect-address (h/mock-fn)] on-detect-address (h/mock-fn)]
(with-redefs [clipboard/get-string #(% clipboard)] (with-redefs [clipboard/get-string #(% clipboard)]
(h/render [address-input/address-input (h/render [address-input/address-input
{:on-detect-address on-detect-address {:on-detect-address on-detect-address
:address-regex address-regex}]) :address-regex address-regex
(h/wait-for #(h/is-truthy (h/get-by-label-text :paste-button))) :valid-ens-or-address? true}])
(h/is-truthy (h/query-by-label-text :paste-button))
(h/fire-event :press (h/get-by-label-text :paste-button)) (h/fire-event :press (h/get-by-label-text :paste-button))
(h/wait-for #(h/is-falsy (h/get-by-label-text :clear-button))) (-> (h/wait-for #(h/is-falsy (h/query-by-label-text :clear-button)))
(h/wait-for #(h/is-truthy (h/get-by-label-text :positive-button-container))) (.then (fn []
(h/was-called on-detect-address))))) (h/wait-for #(h/get-by-label-text :positive-button-container))))
(.then #(h/was-called on-detect-address)))))))

View File

@ -13,16 +13,17 @@
{:value "" {:value ""
:max-length 24}]) :max-length 24}])
(h/fire-event :on-focus (h/query-by-label-text :profile-title-input)) (h/fire-event :on-focus (h/query-by-label-text :profile-title-input))
(h/wait-for #(h/is-truthy (h/query-by-text "00"))) (-> (h/wait-for #(h/get-by-text "00"))
(h/is-truthy (h/query-by-text "/24"))) (.then #(h/is-truthy (h/query-by-text "/24")))))
(h/test "renders with max length digits and character count" (h/test "renders with max length digits and character count"
(h/render [title-input/view (h/render [title-input/view
{:default-value "abc" {:default-value "abc"
:max-length 24} "abc"]) :max-length 24}
"abc"])
(h/fire-event :on-focus (h/query-by-label-text :profile-title-input)) (h/fire-event :on-focus (h/query-by-label-text :profile-title-input))
(h/wait-for #(h/is-truthy (h/query-by-text "03"))) (-> (h/wait-for #(h/get-by-text "03"))
(h/is-truthy (h/query-by-text "/24"))) (.then #(h/is-truthy (h/query-by-text "/24")))))
(h/test "text updates on change" (h/test "text updates on change"
(let [on-change-text (h/mock-fn)] (let [on-change-text (h/mock-fn)]

View File

@ -6,7 +6,9 @@
(h/describe "keycard component" (h/describe "keycard component"
(h/test "Render of keycard component when: holder-name prop is not set" (h/test "Render of keycard component when: holder-name prop is not set"
(h/render [keycard/keycard]) (h/render [keycard/keycard])
(h/is-truthy (h/get-by-translation-text :empty-keycard))) (h/is-truthy (h/get-by-translation-text :t/empty-keycard)))
(h/test "Render of keycard component when: holder-name prop is set" (h/test "Render of keycard component when: holder-name prop is set"
(h/render [keycard/keycard {:holder-name "Alisha"}]) (let [holder-name "Alisha"]
(h/is-truthy (h/get-by-translation-text :user-keycard)))) (h/render [keycard/keycard {:holder-name holder-name}])
(h/is-truthy (h/get-by-translation-text :t/user-keycard {:name holder-name})))))

View File

@ -47,11 +47,11 @@
(h/render [account/view {:type :tag}]) (h/render [account/view {:type :tag}])
(h/is-truthy (h/query-by-label-text :tag-container))) (h/is-truthy (h/query-by-label-text :tag-container)))
(h/test "renders keycard icon if title-icon? is true" (h/test "renders keycard icon if title-icon is present"
(h/render [account/view {:title-icon? true}]) (h/render [account/view {:title-icon :i/placeholder}])
(h/is-truthy (h/query-by-label-text :title-icon))) (h/is-truthy (h/query-by-label-text :title-icon)))
(h/test "doesn't render keycard icon if title-icon? is false" (h/test "doesn't render keycard icon if title-icon is missing"
(h/render [account/view]) (h/render [account/view])
(h/is-falsy (h/query-by-label-text :title-icon))) (h/is-falsy (h/query-by-label-text :title-icon)))

View File

@ -22,14 +22,14 @@
{:backgroundColor colors/white-opa-5}))) {:backgroundColor colors/white-opa-5})))
(h/test "on-press-out changes state to :active" (h/test "on-press-out changes state to :active"
(h/render [address/view]) (h/render [address/view {:active-state? true}])
(h/fire-event :on-press-in (h/get-by-label-text :container)) (h/fire-event :on-press-in (h/get-by-label-text :container))
(h/fire-event :on-press-out (h/get-by-label-text :container)) (h/fire-event :on-press-out (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container) (h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/custom-color :blue 50 10)}))) {:backgroundColor (colors/custom-color :blue 50 10)})))
(h/test "on-press-out changes state to :active with blur? enabled" (h/test "on-press-out changes state to :active with blur? enabled"
(h/render [address/view {:blur? true}]) (h/render [address/view {:active-state? true :blur? true}])
(h/fire-event :on-press-in (h/get-by-label-text :container)) (h/fire-event :on-press-in (h/get-by-label-text :container))
(h/fire-event :on-press-out (h/get-by-label-text :container)) (h/fire-event :on-press-out (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container) (h/wait-for #(h/has-style (h/query-by-label-text :container)

View File

@ -7,14 +7,7 @@
[utils.datetime :as datetime])) [utils.datetime :as datetime]))
(h/describe "record audio component" (h/describe "record audio component"
(h/before-each (h/setup-fake-timers)
(fn []
(h/use-fake-timers)))
(h/after-each
(fn []
(h/clear-all-timers)
(h/use-real-timers)))
(h/test "renders record-audio" (h/test "renders record-audio"
(h/render [record-audio/record-audio]) (h/render [record-audio/record-audio])

View File

@ -6,14 +6,7 @@
[test-helpers.component :as h])) [test-helpers.component :as h]))
(h/describe "soundtrack component" (h/describe "soundtrack component"
(h/before-each (h/setup-fake-timers)
(fn []
(h/use-fake-timers)))
(h/after-each
(fn []
(h/clear-all-timers)
(h/use-real-timers)))
(h/test "renders soundtrack" (h/test "renders soundtrack"
(with-redefs [audio/get-player-duration (fn [] 2000)] (with-redefs [audio/get-player-duration (fn [] 2000)]

View File

@ -74,8 +74,8 @@
[rn/view [rn/view
{:flex-direction :row {:flex-direction :row
:align-items :center} :align-items :center}
(for [{:keys [token-icon]} selected-tokens] (for [{:keys [token-icon id]} selected-tokens]
^{:key token-icon} ^{:key id}
[rn/view {:flex-direction :row} [rn/view {:flex-direction :row}
[rn/view (outer-resource-container size background-color) [rn/view (outer-resource-container size background-color)
[rn/image [rn/image

View File

@ -10,7 +10,7 @@
:stored :on-keycard :stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2" :derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]) :user-name "Test Name"}])
(-> (h/expect (h/get-by-translation-text :origin)) (-> (h/expect (h/get-by-translation-text :t/origin))
(.toBeTruthy))) (.toBeTruthy)))
(h/test "recovery phrase icon is visible when :type is :recovery-phrase" (h/test "recovery phrase icon is visible when :type is :recovery-phrase"
@ -59,7 +59,7 @@
:stored :on-device :stored :on-device
:derivation-path "m / 44 / 60 / 0 / 0 / 2" :derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]) :user-name "Test Name"}])
(-> (h/expect (h/get-by-translation-text :on-device)) (-> (h/expect (h/get-by-translation-text :t/on-device))
(.toBeTruthy))) (.toBeTruthy)))
(h/test "on-keycard text is visible when :stored is :on-keycard" (h/test "on-keycard text is visible when :stored is :on-keycard"
@ -68,7 +68,7 @@
:stored :on-keycard :stored :on-keycard
:derivation-path "m / 44 / 60 / 0 / 0 / 2" :derivation-path "m / 44 / 60 / 0 / 0 / 2"
:user-name "Test Name"}]) :user-name "Test Name"}])
(-> (h/expect (h/get-by-translation-text :on-keycard)) (-> (h/expect (h/get-by-translation-text :t/on-keycard))
(.toBeTruthy))) (.toBeTruthy)))
(h/test "on-press is called when :type is :recovery-phrase" (h/test "on-press is called when :type is :recovery-phrase"

View File

@ -9,48 +9,52 @@
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[react-native.core :as rn])) [react-native.core :as rn]))
(defn network-bridge-add (defn network-bridge-add
[{:keys [network state theme]}] [{:keys [network state theme]}]
[rn/view {:style (merge (style/container network state theme) (style/add-container theme))} [rn/view {:style (merge (style/container network state theme) (style/add-container theme))}
[icon/icon :i/add-circle {:size 12 :no-color true}]]) [icon/icon :i/add-circle {:size 12 :no-color true}]])
(defn- network->text
[network]
(cond (not network) ""
(= network :ethereum) "Mainnet"
:else (string/capitalize (name network))))
(defn view-internal (defn view-internal
[{:keys [theme network status amount container-style] :as args}] [{:keys [theme network status amount container-style] :as args}]
(let [network-text (if (= network :ethereum) "Mainnet" (string/capitalize (name network)))] (if (= status :add)
(if (= status :add) [network-bridge-add args]
[network-bridge-add args] [rn/view
[rn/view {:style (merge (style/container network status theme) container-style)
{:style (merge (style/container network status theme) container-style) :accessible true
:accessible true :accessibility-label :container}
:accessibility-label :container} (if (= status :loading)
(if (= status :loading)
[rn/view
{:style (style/loading-skeleton theme)
:accessible true
:accessibility-label :loading}]
[rn/view
{:style {:flex-direction :row
:justify-content :space-between}}
[text/text
{:size :paragraph-2
:weight :medium} amount]
(when (= status :locked)
[icon/icon :i/locked
{:size 12
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:accessible true
:accessibility-label :lock}])])
[rn/view [rn/view
{:style {:flex-direction :row {:style (style/loading-skeleton theme)
:align-items :center}} :accessible true
[rn/image :accessibility-label :loading}]
{:source (resources/get-network network) [rn/view
:style style/network-icon}] {:style {:flex-direction :row
:justify-content :space-between}}
[text/text [text/text
{:size :label {:size :paragraph-2
:weight :medium :weight :medium} amount]
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}} (when (= status :locked)
network-text]]]))) [icon/icon :i/locked
{:size 12
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
:accessible true
:accessibility-label :lock}])])
[rn/view
{:style {:flex-direction :row
:align-items :center}}
[rn/image
{:source (resources/get-network network)
:style style/network-icon}]
[text/text
{:size :label
:weight :medium
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
(network->text network)]]]))
(def view (quo.theme/with-theme view-internal)) (def view (quo.theme/with-theme view-internal))

View File

@ -86,10 +86,10 @@
(defn- dashed-line (defn- dashed-line
[network-name] [network-name]
[rn/view {:style style/dashed-line} (into [rn/view {:style style/dashed-line}]
(take 19 (take 19
(interleave (repeat [rn/view {:style (style/dashed-line-line network-name)}]) (interleave (repeat [rn/view {:style (style/dashed-line-line network-name)}])
(repeat [rn/view {:style style/dashed-line-space}])))]) (repeat [rn/view {:style style/dashed-line-space}])))))
(defn f-network-routing-bars (defn f-network-routing-bars
[_] [_]

View File

@ -180,8 +180,8 @@
:networks networks}] :networks networks}]
[tag-internal tag-photo tag-name tag-number theme] [tag-internal tag-photo tag-name tag-number theme]
(for [network networks] (for [network networks]
^{:key (:network network)}
(let [assoc-props #(get-network networks %)] (let [assoc-props #(get-network networks %)]
^{:key (:network network)}
[view-network (assoc-props (:network network))]))]]) [view-network (assoc-props (:network network))]))]])
(def view (quo.theme/with-theme view-internal)) (def view (quo.theme/with-theme view-internal))

View File

@ -1,107 +1,101 @@
(ns quo.core-spec (ns quo.core-spec
(:require ;; [quo.components.list-items.account.component-spec] (:require
;; [quo.components.list-items.address.component-spec] quo.components.avatars.account-avatar.component-spec
;; [quo.components.list-items.saved-address.component-spec] quo.components.avatars.user-avatar.component-spec
;; [quo.components.list-items.saved-contact-address.component-spec] quo.components.banners.banner.component-spec
;; [quo.components.list-items.saved-contact-address.component-spec] quo.components.browser.browser-input.component-spec
;; [quo.components.list-items.token-network.component-spec] quo.components.buttons.button.component-spec
[quo.components.avatars.account-avatar.component-spec] quo.components.buttons.composer-button.component-spec
[quo.components.avatars.user-avatar.component-spec] quo.components.buttons.logout-button.component-spec
[quo.components.banners.banner.component-spec] quo.components.buttons.predictive-keyboard.component-spec
[quo.components.browser.browser-input.component-spec] quo.components.buttons.slide-button.component-spec
[quo.components.buttons.button.component-spec] quo.components.buttons.wallet-button.component-spec
[quo.components.buttons.composer-button.component-spec] quo.components.buttons.wallet-ctas.component-spec
[quo.components.buttons.logout-button.component-spec] quo.components.calendar.calendar-day.component-spec
[quo.components.buttons.predictive-keyboard.component-spec] quo.components.calendar.calendar-year.component-spec
[quo.components.buttons.slide-button.component-spec] quo.components.calendar.calendar.component-spec
[quo.components.buttons.wallet-button.component-spec] quo.components.calendar.calendar.month-picker.component-spec
[quo.components.buttons.wallet-ctas.component-spec] quo.components.colors.color-picker.component-spec
[quo.components.calendar.calendar-day.component-spec] quo.components.community.community-stat.component-spec
[quo.components.calendar.calendar-year.component-spec] quo.components.counter.counter.component-spec
[quo.components.calendar.calendar.component-spec] quo.components.counter.step.component-spec
[quo.components.calendar.calendar.month-picker.component-spec] quo.components.dividers.divider-label.component-spec
[quo.components.colors.color-picker.component-spec] quo.components.dividers.strength-divider.component-spec
[quo.components.community.community-stat.component-spec] quo.components.drawers.action-drawers.component-spec
[quo.components.counter.counter.component-spec] quo.components.drawers.bottom-actions.component-spec
[quo.components.counter.step.component-spec] quo.components.drawers.documentation-drawers.component-spec
[quo.components.dividers.divider-label.component-spec] quo.components.drawers.drawer-buttons.component-spec
[quo.components.dividers.strength-divider.component-spec] quo.components.drawers.drawer-top.component-spec
[quo.components.drawers.action-drawers.component-spec] quo.components.drawers.permission-context.component-spec
[quo.components.drawers.bottom-actions.component-spec] quo.components.dropdowns.dropdown-input.component-spec
[quo.components.drawers.documentation-drawers.component-spec] quo.components.dropdowns.dropdown.component-spec
[quo.components.drawers.drawer-buttons.component-spec] quo.components.dropdowns.network-dropdown.component-spec
[quo.components.drawers.drawer-top.component-spec] quo.components.gradient.gradient-cover.component-spec
[quo.components.drawers.permission-context.component-spec] quo.components.graph.wallet-graph.component-spec
[quo.components.dropdowns.dropdown-input.component-spec] quo.components.inputs.address-input.component-spec
[quo.components.dropdowns.dropdown.component-spec] quo.components.inputs.input.component-spec
[quo.components.dropdowns.network-dropdown.component-spec] quo.components.inputs.locked-input.component-spec
[quo.components.gradient.gradient-cover.component-spec] quo.components.inputs.profile-input.component-spec
[quo.components.graph.wallet-graph.component-spec] quo.components.inputs.recovery-phrase.component-spec
[quo.components.inputs.input.component-spec] quo.components.inputs.title-input.component-spec
[quo.components.inputs.locked-input.component-spec] quo.components.keycard.component-spec
[quo.components.inputs.profile-input.component-spec] quo.components.links.link-preview.component-spec
[quo.components.inputs.recovery-phrase.component-spec] quo.components.links.url-preview-list.component-spec
[quo.components.keycard.component-spec] quo.components.links.url-preview.component-spec
[quo.components.links.link-preview.component-spec] quo.components.list-items.account.component-spec
[quo.components.links.url-preview-list.component-spec] quo.components.list-items.address.component-spec
[quo.components.links.url-preview.component-spec] quo.components.list-items.channel.component-spec
[quo.components.list-items.channel.component-spec] quo.components.list-items.community.component-spec
[quo.components.list-items.community.component-spec] quo.components.list-items.dapp.component-spec
[quo.components.list-items.dapp.component-spec] quo.components.list-items.saved-address.component-spec
[quo.components.list-items.token-value.component-spec] quo.components.list-items.saved-contact-address.component-spec
[quo.components.loaders.skeleton-list.component-spec] quo.components.list-items.token-network.component-spec
[quo.components.markdown.list.component-spec] quo.components.list-items.token-value.component-spec
[quo.components.markdown.text-component-spec] quo.components.loaders.skeleton-list.component-spec
[quo.components.navigation.top-nav.component-spec] quo.components.markdown.list.component-spec
[quo.components.notifications.notification.component-spec] quo.components.markdown.text-component-spec
[quo.components.numbered-keyboard.keyboard-key.component-spec] quo.components.navigation.top-nav.component-spec
[quo.components.onboarding.small-option-card.component-spec] quo.components.notifications.notification.component-spec
[quo.components.password.tips.component-spec] quo.components.numbered-keyboard.keyboard-key.component-spec
[quo.components.profile.link-card.component-spec] quo.components.onboarding.small-option-card.component-spec
[quo.components.profile.select-profile.component-spec] quo.components.password.tips.component-spec
[quo.components.profile.showcase-nav.component-spec] quo.components.profile.link-card.component-spec
[quo.components.record-audio.record-audio.component-spec] quo.components.profile.select-profile.component-spec
[quo.components.record-audio.soundtrack.component-spec] quo.components.profile.showcase-nav.component-spec
[quo.components.selectors.disclaimer.component-spec] quo.components.record-audio.record-audio.component-spec
[quo.components.selectors.filter.component-spec] quo.components.record-audio.soundtrack.component-spec
[quo.components.selectors.reactions-selector.component-spec] quo.components.selectors.disclaimer.component-spec
[quo.components.selectors.selectors.component-spec] quo.components.selectors.filter.component-spec
[quo.components.settings.category.component-spec] quo.components.selectors.reactions-selector.component-spec
[quo.components.settings.data-item.component-spec] quo.components.selectors.selectors.component-spec
[quo.components.settings.reorder-item.component-spec] quo.components.settings.category.component-spec
[quo.components.settings.settings-item.component-spec] quo.components.settings.data-item.component-spec
[quo.components.share.share-qr-code.component-spec] quo.components.settings.reorder-item.component-spec
[quo.components.switchers.base-card.component-spec] quo.components.settings.settings-item.component-spec
[quo.components.switchers.group-messaging-card.component-spec] quo.components.share.share-qr-code.component-spec
[quo.components.tags.network-tags.component-spec] quo.components.switchers.base-card.component-spec
[quo.components.tags.status-tags-component-spec] quo.components.switchers.group-messaging-card.component-spec
[quo.components.tags.summary-tag.component-spec] quo.components.tags.network-tags.component-spec
[quo.components.tags.tiny-tag.component-spec] quo.components.tags.status-tags-component-spec
[quo.components.text-combinations.channel-name.component-spec] quo.components.tags.summary-tag.component-spec
[quo.components.text-combinations.page-top.component-spec] quo.components.tags.tiny-tag.component-spec
[quo.components.text-combinations.username.component-spec] quo.components.text-combinations.channel-name.component-spec
[quo.components.wallet.account-card.component-spec] quo.components.text-combinations.page-top.component-spec
[quo.components.wallet.account-origin.component-spec] quo.components.text-combinations.username.component-spec
[quo.components.wallet.account-overview.component-spec] quo.components.wallet.account-card.component-spec
[quo.components.wallet.account-permissions.component-spec] quo.components.wallet.account-origin.component-spec
[quo.components.wallet.confirmation-progress.component-spec] quo.components.wallet.account-overview.component-spec
[quo.components.wallet.keypair.component-spec] quo.components.wallet.account-permissions.component-spec
[quo.components.wallet.network-amount.component-spec] quo.components.wallet.confirmation-progress.component-spec
[quo.components.wallet.network-bridge.component-spec] quo.components.wallet.keypair.component-spec
[quo.components.wallet.network-routing.component-spec] quo.components.wallet.network-amount.component-spec
[quo.components.wallet.progress-bar.component-spec] quo.components.wallet.network-bridge.component-spec
[quo.components.wallet.required-tokens.component-spec] quo.components.wallet.network-routing.component-spec
[quo.components.wallet.summary-info.component-spec] quo.components.wallet.progress-bar.component-spec
[quo.components.wallet.token-input.component-spec] quo.components.wallet.required-tokens.component-spec
[quo.components.wallet.transaction-progress.component-spec] quo.components.wallet.summary-info.component-spec
[quo.components.wallet.transaction-summary.component-spec] quo.components.wallet.token-input.component-spec
[quo.components.wallet.wallet-activity.component-spec] quo.components.wallet.transaction-progress.component-spec
[quo.components.wallet.wallet-overview.component-spec])) quo.components.wallet.transaction-summary.component-spec
quo.components.wallet.wallet-activity.component-spec
;; [quo.components.inputs.address-input.component-spec] quo.components.wallet.wallet-overview.component-spec))
;; [quo.components.inputs.title-input.component-spec]
;; [quo.components.list-items.account.component-spec]
;; [quo.components.list-items.address.component-spec]
;; [quo.components.list-items.saved-address.component-spec]
;; [quo.components.list-items.saved-contact-address.component-spec]
;; [quo.components.list-items.token-network.component-spec]

View File

@ -1,10 +1,12 @@
(ns status-im.contexts.chat.messages.content.audio.component-spec (ns status-im.contexts.chat.messages.content.audio.component-spec
(:require (:require
[re-frame.core :as re-frame]
[react-native.audio-toolkit :as audio] [react-native.audio-toolkit :as audio]
[status-im.contexts.chat.messages.content.audio.view :as audio-message] [status-im.contexts.chat.messages.content.audio.view :as audio-message]
[test-helpers.component :as h])) [test-helpers.component :as h]))
;; We can't rely on `with-redefs` with async code.
(set! audio/destroy-player #())
(def message (def message
{:audio-duration-ms 5000 {:audio-duration-ms 5000
:message-id "message-id"}) :message-id "message-id"})
@ -12,17 +14,13 @@
(def context (def context
{:in-pinned-view? false}) {:in-pinned-view? false})
(defn setup-subs
[subscriptions]
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(h/describe "audio message" (h/describe "audio message"
(h/setup-restorable-re-frame)
(h/before-each (h/before-each
#(setup-subs {:mediaserver/port 1000 (fn []
:app-state "active"})) (h/setup-subs {:mediaserver/port 1000
:app-state "active"})))
(h/test "renders correctly" (h/test "renders correctly"
(h/render [audio-message/audio-message message context]) (h/render [audio-message/audio-message message context])
@ -31,24 +29,19 @@
(h/test "press play calls audio/toggle-playpause-player" (h/test "press play calls audio/toggle-playpause-player"
(with-redefs [audio/toggle-playpause-player (js/jest.fn) (with-redefs [audio/toggle-playpause-player (js/jest.fn)
audio/new-player (fn [_ _ _] {}) audio/new-player (fn [_ _ _] {})
audio/destroy-player #()
audio/prepare-player (fn [_ on-success _] (on-success)) audio/prepare-player (fn [_ on-success _] (on-success))
audio-message/download-audio-http (fn [_ on-success] (on-success "audio-uri"))] audio-message/download-audio-http (fn [_ on-success] (on-success "audio-uri"))]
(h/render [audio-message/audio-message message context]) (h/render [audio-message/audio-message message context])
(h/fire-event (h/fire-event :on-press (h/get-by-label-text :play-pause-audio-message-button))
:on-press
(h/get-by-label-text :play-pause-audio-message-button))
(-> (h/expect audio/toggle-playpause-player) (-> (h/expect audio/toggle-playpause-player)
(.toHaveBeenCalledTimes 1)))) (.toHaveBeenCalledTimes 1))))
(h/test "press play renders error" (h/test "press play renders error"
(h/render [audio-message/audio-message message context]) (with-redefs [audio/new-player (fn [_ _ _] {})
(with-redefs [audio/toggle-playpause-player (fn [_ _ _ on-error] (on-error))
audio/new-player (fn [_ _ _] {})
audio/destroy-player #()
audio/prepare-player (fn [_ on-success _] (on-success)) audio/prepare-player (fn [_ on-success _] (on-success))
audio-message/download-audio-http (fn [_ on-success] (on-success "audio-uri"))] audio-message/download-audio-http (fn [_ on-success] (on-success "audio-uri"))]
(h/fire-event (h/render [audio-message/audio-message message context])
:on-press (h/fire-event :on-press (h/get-by-label-text :play-pause-audio-message-button)))
(h/get-by-label-text :play-pause-audio-message-button)) (with-redefs [audio/toggle-playpause-player (fn [_ _ _ on-error] (on-error))]
(h/wait-for #(h/is-truthy (h/get-by-label-text :audio-error-label)))))) (h/fire-event :on-press (h/get-by-label-text :play-pause-audio-message-button))
(h/wait-for #(h/get-by-label-text :audio-error-label)))))

View File

@ -1,189 +1,123 @@
(ns status-im.contexts.communities.actions.community-options.component-spec (ns status-im.contexts.communities.actions.community-options.component-spec
(:require (:require
[re-frame.core :as re-frame]
[status-im.contexts.communities.actions.community-options.view :as options] [status-im.contexts.communities.actions.community-options.view :as options]
[test-helpers.component :as h])) [test-helpers.component :as h]))
(defn setup-subs
[subscriptions]
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(h/describe "community options for bottom sheets" (h/describe "community options for bottom sheets"
(h/setup-restorable-re-frame)
(h/test "joined options - Non token Gated" (h/test "joined options - Non token Gated"
(setup-subs {:communities/my-pending-request-to-join nil (h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined true}}) :communities/community {:joined true}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members)) (h/is-truthy (h/get-by-translation-text :t/view-members))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(-> (h/expect (h/get-by-translation-text :view-community-rules)) (h/is-truthy (h/get-by-translation-text :t/mark-as-read))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/mute-community))
(-> (h/expect (h/get-by-translation-text :mark-as-read)) (h/is-truthy (h/get-by-translation-text :t/notification-settings))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(-> (h/expect (h/get-by-translation-text :mute-community)) (h/is-truthy (h/get-by-translation-text :t/show-qr))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/share-community))
(-> (h/expect (h/get-by-translation-text :notification-settings)) (h/is-truthy (h/get-by-translation-text :t/leave-community)))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :leave-community))
(.toBeTruthy)))
(h/test "joined options - Token Gated" (h/test "joined options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join nil (h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined true :communities/community {:joined true
:token-permissions []}}) :token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members)) (h/is-truthy (h/get-by-translation-text :t/view-members))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(-> (h/expect (h/get-by-translation-text :view-community-rules)) (h/is-truthy (h/get-by-translation-text :t/view-token-gating))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/mark-as-read))
(-> (h/expect (h/get-by-translation-text :view-token-gating)) (h/is-truthy (h/get-by-translation-text :t/mute-community))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/notification-settings))
(-> (h/expect (h/get-by-translation-text :mark-as-read)) (h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/show-qr))
(-> (h/expect (h/get-by-translation-text :mute-community)) (h/is-truthy (h/get-by-translation-text :t/share-community))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/leave-community)))
(-> (h/expect (h/get-by-translation-text :notification-settings))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :leave-community))
(.toBeTruthy)))
(h/test "admin options - Non token Gated" (h/test "admin options - Non token Gated"
(setup-subs {:communities/my-pending-request-to-join nil (h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:admin true}}) :communities/community {:admin true}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members)) (h/is-truthy (h/get-by-translation-text :t/view-members))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(-> (h/expect (h/get-by-translation-text :view-community-rules)) (h/is-truthy (h/get-by-translation-text :t/mark-as-read))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/mute-community))
(-> (h/expect (h/get-by-translation-text :mark-as-read)) (h/is-truthy (h/get-by-translation-text :t/notification-settings))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(-> (h/expect (h/get-by-translation-text :mute-community)) (h/is-truthy (h/get-by-translation-text :t/show-qr))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/share-community)))
(-> (h/expect (h/get-by-translation-text :notification-settings))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy)))
(h/test "admin options - Token Gated" (h/test "admin options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join nil (h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:admin true :communities/community {:admin true
:token-permissions []}}) :token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members)) (h/is-truthy (h/get-by-translation-text :t/view-members))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(-> (h/expect (h/get-by-translation-text :view-community-rules)) (h/is-truthy (h/get-by-translation-text :t/mark-as-read))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/mute-community))
(-> (h/expect (h/get-by-translation-text :mark-as-read)) (h/is-truthy (h/get-by-translation-text :t/notification-settings))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(-> (h/expect (h/get-by-translation-text :mute-community)) (h/is-truthy (h/get-by-translation-text :t/show-qr))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/share-community)))
(-> (h/expect (h/get-by-translation-text :notification-settings))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy)))
(h/test "request sent options - Non token Gated" (h/test "request sent options - Non token Gated"
(setup-subs {:communities/my-pending-request-to-join "mock-id" (h/setup-subs {:communities/my-pending-request-to-join "mock-id"
:communities/community {}}) :communities/community {}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members)) (h/is-truthy (h/get-by-translation-text :t/view-members))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(-> (h/expect (h/get-by-translation-text :view-community-rules)) (h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/show-qr))
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts)) (h/is-truthy (h/get-by-translation-text :t/share-community))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/cancel-request-to-join)))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :cancel-request-to-join))
(.toBeTruthy)))
(h/test "request sent options - Token Gated" (h/test "request sent options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join "mock-id" (h/setup-subs {:communities/my-pending-request-to-join "mock-id"
:communities/community {:token-permissions []}}) :communities/community {:token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts)) (h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/view-token-gating))
(-> (h/expect (h/get-by-translation-text :view-token-gating)) (h/is-truthy (h/get-by-translation-text :t/show-qr))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/share-community))
(-> (h/expect (h/get-by-translation-text :show-qr)) (h/is-truthy (h/get-by-translation-text :t/cancel-request-to-join)))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :cancel-request-to-join))
(.toBeTruthy)))
(h/test "banned options - Non token Gated" (h/test "banned options - Non token Gated"
(setup-subs {:communities/my-pending-request-to-join nil (h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:banList true}}) :communities/community {:banList true}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members)) (h/is-truthy (h/get-by-translation-text :t/view-members))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(-> (h/expect (h/get-by-translation-text :view-community-rules)) (h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/show-qr))
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts)) (h/is-truthy (h/get-by-translation-text :t/share-community)))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy)))
(h/test "banned options - Token Gated" (h/test "banned options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join nil (h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:banList 100 :communities/community {:banList 100
:token-permissions []}}) :token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts)) (h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/view-token-gating))
(-> (h/expect (h/get-by-translation-text :view-token-gating)) (h/is-truthy (h/get-by-translation-text :t/show-qr))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/share-community)))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy)))
(h/test "banned options - Token Gated" (h/test "banned options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join nil (h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:banList 100 :communities/community {:banList 100
:token-permissions []}}) :token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts)) (h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/view-token-gating))
(-> (h/expect (h/get-by-translation-text :view-token-gating)) (h/is-truthy (h/get-by-translation-text :t/show-qr))
(.toBeTruthy)) (h/is-truthy (h/get-by-translation-text :t/share-community)))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy)))
(h/test "joined and muted community" (h/test "joined and muted community"
(setup-subs {:communities/my-pending-request-to-join nil (h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined true :communities/community {:joined true
:muted true :muted true
:token-permissions []}}) :token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}]) (h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :unmute-community)) (h/is-truthy (h/get-by-translation-text :t/unmute-community))))
(.toBeTruthy))))

View File

@ -1,38 +1,30 @@
(ns status-im.contexts.wallet.add-address-to-watch.component-spec (ns status-im.contexts.wallet.add-address-to-watch.component-spec
(:require (:require
[re-frame.core :as re-frame]
[status-im.contexts.wallet.add-address-to-watch.view :as add-address-to-watch] [status-im.contexts.wallet.add-address-to-watch.view :as add-address-to-watch]
status-im.contexts.wallet.events
[test-helpers.component :as h])) [test-helpers.component :as h]))
(defn setup-subs
[subscriptions]
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(h/describe "select address for watch only account" (h/describe "select address for watch only account"
(h/setup-restorable-re-frame)
(h/before-each
(fn []
(h/setup-subs {:wallet/scanned-address nil
:wallet/addresses #{"0x12E838Ae1f769147b12956485dc56e57138f3AC8"
"0x22E838Ae1f769147b12956485dc56e57138f3AC8"}
:wallet/watch-address-activity-state nil
:profile/customization-color :blue})))
(h/test "validation messages show for already used addressed" (h/test "validation messages show for already used addressed"
(setup-subs {:wallet/scanned-address nil
:wallet/addresses #{"0x12E838Ae1f769147b12956485dc56e57138f3AC8"
"0x22E838Ae1f769147b12956485dc56e57138f3AC8"}
:wallet/watch-address-activity-state nil
:profile/customization-color :blue})
(h/render [add-address-to-watch/view]) (h/render [add-address-to-watch/view])
(h/is-falsy (h/query-by-label-text :error-message)) (h/is-falsy (h/query-by-label-text :error-message))
(h/fire-event :change-text (h/fire-event :change-text
(h/get-by-label-text :add-address-to-watch) (h/get-by-label-text :add-address-to-watch)
"0x12E838Ae1f769147b12956485dc56e57138f3AC8") "0x12E838Ae1f769147b12956485dc56e57138f3AC8")
(h/is-truthy (h/get-by-translation-text :address-already-in-use)))) (h/is-truthy (h/get-by-translation-text :t/address-already-in-use)))
(h/test "validation messages show for invalid address"
(setup-subs {:wallet/scanned-address nil
:wallet/addresses #{"0x12E838Ae1f769147b12956485dc56e57138f3AC8"
"0x22E838Ae1f769147b12956485dc56e57138f3AC8"}
:wallet/watch-address-activity-state nil
:profile/customization-color :blue})
(h/render [add-address-to-watch/view])
(h/is-falsy (h/query-by-label-text :error-message))
(h/fire-event :change-text (h/get-by-label-text :add-address-to-watch) "0x12E838Ae1f769147b")
(h/is-truthy (h/get-by-translation-text :invalid-address)))
(h/test "validation messages show for invalid address"
(h/render [add-address-to-watch/view])
(h/is-falsy (h/query-by-label-text :error-message))
(h/fire-event :change-text (h/get-by-label-text :add-address-to-watch) "0x12E838Ae1f769147b")
(h/is-truthy (h/get-by-translation-text :t/invalid-address))))

View File

@ -1,23 +1,17 @@
(ns status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec (ns status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec
(:require (:require
[re-frame.core :as re-frame]
[status-im.contexts.wallet.add-address-to-watch.confirm-address.view :as confirm-address] [status-im.contexts.wallet.add-address-to-watch.confirm-address.view :as confirm-address]
[test-helpers.component :as h] [test-helpers.component :as h]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn setup-subs
[subscriptions]
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(h/describe "Add Watch Only Account Page" (h/describe "Add Watch Only Account Page"
(h/setup-restorable-re-frame)
(h/test "Create Account button is disabled while no account name exists" (h/test "Create Account button is disabled while no account name exists"
(let [callback (h/mock-fn)] (let [callback (h/mock-fn)]
(with-redefs [rf/dispatch #(callback)] (with-redefs [rf/dispatch #(callback)]
(setup-subs {:profile/wallet-accounts [] (h/setup-subs {:profile/wallet-accounts []
:get-screen-params {:address "0xmock-address"}}) :get-screen-params {:address "0xmock-address"}})
(h/render [confirm-address/view {}]) (h/render [confirm-address/view {}])
(h/is-truthy (h/get-by-text "0xmock-address")) (h/is-truthy (h/get-by-text "0xmock-address"))
(h/was-not-called callback) (h/was-not-called callback)

View File

@ -48,7 +48,6 @@
(rf/reg-event-fx :wallet/send-select-amount (rf/reg-event-fx :wallet/send-select-amount
(fn [{:keys [db]} [{:keys [amount]}]] (fn [{:keys [db]} [{:keys [amount]}]]
(js/alert "Not implemented yet")
{:db (assoc-in db [:wallet :ui :send :amount] amount)})) {:db (assoc-in db [:wallet :ui :send :amount] amount)}))
(rf/reg-event-fx :wallet/get-suggested-routes (rf/reg-event-fx :wallet/get-suggested-routes

View File

@ -1,15 +1,14 @@
(ns status-im.contexts.wallet.send.input-amount.component-spec (ns status-im.contexts.wallet.send.input-amount.component-spec
(:require (:require
[re-frame.core :as re-frame] status-im.contexts.wallet.events
status-im.contexts.wallet.send.events
[status-im.contexts.wallet.send.input-amount.view :as input-amount] [status-im.contexts.wallet.send.input-amount.view :as input-amount]
[test-helpers.component :as h])) [test-helpers.component :as h]
[utils.debounce :as debounce]
[utils.re-frame :as rf]))
(defn setup-subs (set! rf/dispatch #())
[subscriptions] (set! debounce/debounce-and-dispatch #())
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(def sub-mocks (def sub-mocks
{:profile/profile {:currency :usd} {:profile/profile {:currency :usd}
@ -45,74 +44,111 @@
:wallet/wallet-send-route {:route []}}) :wallet/wallet-send-route {:route []}})
(h/describe "Send > input amount screen" (h/describe "Send > input amount screen"
(h/setup-restorable-re-frame)
(h/test "Default render" (h/test "Default render"
(with-redefs [re-frame/dispatch #()] (h/setup-subs sub-mocks)
(setup-subs sub-mocks) (h/render [input-amount/view {}])
(h/render [input-amount/view {}]) (h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "0")) (h/is-truthy (h/get-by-text "ETH"))
(h/is-truthy (h/get-by-text "ETH")) (h/is-truthy (h/get-by-text "$0.00"))
(h/is-truthy (h/get-by-text "$0.00")) (h/is-disabled (h/get-by-label-text :button-one)))
(h/is-disabled (h/get-by-label-text :button-one))))
(h/test "Fill token input and confirm" (h/test "Fill token input and confirm"
(with-redefs [re-frame/dispatch #()] (h/setup-subs sub-mocks)
(setup-subs sub-mocks) (let [on-confirm (h/mock-fn)]
(let [on-confirm (h/mock-fn)] (h/render [input-amount/view
(h/render [input-amount/view {:on-confirm on-confirm
{:on-confirm on-confirm :rate 10
:rate 10 :limit 1000}])
:limit 1000}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1)) (h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2)) (h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-3)) (h/fire-event :press (h/query-by-label-text :keyboard-key-3))
(h/fire-event :press (h/query-by-label-text :keyboard-key-.)) (h/fire-event :press (h/query-by-label-text :keyboard-key-.))
(h/fire-event :press (h/query-by-label-text :keyboard-key-4)) (h/fire-event :press (h/query-by-label-text :keyboard-key-4))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5)) (h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "$1234.50"))) (-> (h/wait-for #(h/get-by-text "$1234.50"))
(.then (fn []
(h/is-truthy (h/get-by-label-text :button-one))
(h/fire-event :press (h/get-by-label-text :button-one))
(h/was-called on-confirm))))))
(h/is-truthy (h/get-by-label-text :button-one)) (h/test "Fill token input and confirm"
(h/setup-subs sub-mocks)
(h/fire-event :press (h/get-by-label-text :button-one)) (let [on-confirm (h/mock-fn)]
(h/was-called on-confirm)))) (h/render [input-amount/view
{:rate 10
:limit 1000
:on-confirm on-confirm}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-3))
(h/fire-event :press (h/query-by-label-text :keyboard-key-.))
(h/fire-event :press (h/query-by-label-text :keyboard-key-4))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(-> (h/wait-for #(h/get-by-text "$1234.50"))
(.then (fn []
(h/is-truthy (h/get-by-label-text :button-one))
(h/fire-event :press (h/get-by-label-text :button-one))
(h/was-called on-confirm))))))
(h/test "Try to fill more than limit" (h/test "Try to fill more than limit"
(with-redefs [re-frame/dispatch #()] (h/setup-subs sub-mocks)
(setup-subs sub-mocks) (h/render [input-amount/view
(h/render [input-amount/view {:rate 10
{:rate 10 :limit 286}])
:limit 286}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2)) (h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9)) (h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5)) (h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "$290.00"))) (-> (h/wait-for #(h/is-truthy (h/get-by-text "$290.00")))
(.then (fn []
(h/fire-event :press (h/query-by-label-text :keyboard-key-backspace))
(h/fire-event :press (h/query-by-label-text :keyboard-key-8))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/get-by-text "$2850.00"))))))
(h/fire-event :press (h/query-by-label-text :keyboard-key-backspace)) (h/test "Try to fill more than limit"
(h/fire-event :press (h/query-by-label-text :keyboard-key-8)) (h/setup-subs sub-mocks)
(h/fire-event :press (h/query-by-label-text :keyboard-key-5)) (h/render [input-amount/view
(h/wait-for #(h/is-truthy (h/get-by-text "$2850.00"))))) {:rate 10
:limit 286
:on-confirm #()}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(-> (h/wait-for #(h/get-by-text "$290.00"))
(.then (fn []
(h/fire-event :press (h/query-by-label-text :keyboard-key-backspace))
(h/fire-event :press (h/query-by-label-text :keyboard-key-8))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/get-by-text "$2850.00"))))))
(h/test "Switch from crypto to fiat and check limit" (h/test "Switch from crypto to fiat and check limit"
(with-redefs [re-frame/dispatch #()] (h/setup-subs sub-mocks)
(setup-subs sub-mocks) (h/render [input-amount/view
(h/render [input-amount/view {:rate 10
{:rate 10 :limit 250
:limit 250}]) :on-confirm #()}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2)) (h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-0)) (h/fire-event :press (h/query-by-label-text :keyboard-key-0))
(h/wait-for #(h/is-truthy (h/get-by-text "$200.00"))) (-> (h/wait-for #(h/get-by-text "$200.00"))
(.then (fn []
(h/fire-event :press (h/query-by-label-text :reorder)) (h/fire-event :press (h/query-by-label-text :reorder))
(h/wait-for #(h/get-by-text "2.00 ETH"))))
(h/wait-for #(h/is-truthy (h/get-by-text "2.00 ETH"))) (.then (fn []
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5)) (h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5)) (h/wait-for #(h/get-by-text "205.50 ETH"))))
(.then (fn []
(h/wait-for #(h/is-truthy (h/get-by-text "205.50 ETH"))) (h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5)) (h/wait-for #(h/get-by-text "205.50 ETH")))))))
(h/wait-for #(h/is-truthy (h/get-by-text "205.50 ETH"))))))

View File

@ -4,7 +4,6 @@
[status-im.contexts.chat.messages.content.audio.component-spec] [status-im.contexts.chat.messages.content.audio.component-spec]
[status-im.contexts.communities.actions.community-options.component-spec] [status-im.contexts.communities.actions.community-options.component-spec]
[status-im.contexts.wallet.add-address-to-watch.component-spec] [status-im.contexts.wallet.add-address-to-watch.component-spec]
[status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec])) [status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec]
[status-im.contexts.wallet.create-account.edit-derivation-path.component-spec]
;; [status-im.contexts.wallet.create-account.edit-derivation-path.component-spec] [status-im.contexts.wallet.send.input-amount.component-spec]))
;; [status-im.contexts.wallet.send.input-amount.component-spec]

View File

@ -97,7 +97,7 @@
(malli.instrument/unstrument!) (malli.instrument/unstrument!)
(malli.dev/start! {:report (schema/reporter)}) (malli.dev/start! {:report (schema/reporter)})
(println "Schemas initialized.") (log/debug "Schemas initialized.")
;; It is relatively easy to write invalid schemas, but we don't want to ;; It is relatively easy to write invalid schemas, but we don't want to
;; block the app from initializing if such errors happen, at least not until ;; block the app from initializing if such errors happen, at least not until

View File

@ -10,12 +10,40 @@
;; warning: "Describe callback must not return a value". ;; warning: "Describe callback must not return a value".
js/undefined))) js/undefined)))
(defmacro describe-skip
[description & body]
`(js/global.describe.skip
~description
(fn []
~@body
js/undefined)))
(defmacro describe-only
[description & body]
`(js/global.describe.only
~description
(fn []
~@body
js/undefined)))
(defmacro test (defmacro test
[description & body] [description & body]
`(js/global.test `(js/global.test
~description ~description
(fn [] ~@body))) (fn [] ~@body)))
(defmacro test-skip
[description & body]
`(js/global.test.skip
~description
(fn [] ~@body)))
(defmacro test-only
[description & body]
`(js/global.test.only
~description
(fn [] ~@body)))
(defmacro before-each (defmacro before-each
[description & body] [description & body]
`(js/beforeEach `(js/beforeEach

View File

@ -5,6 +5,7 @@
[camel-snake-kebab.core :as camel-snake-kebab] [camel-snake-kebab.core :as camel-snake-kebab]
[oops.core :as oops] [oops.core :as oops]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[re-frame.core :as re-frame]
[reagent.core :as reagent] [reagent.core :as reagent]
[utils.i18n :as i18n])) [utils.i18n :as i18n]))
@ -124,37 +125,49 @@
(def query-all-by-test-id (with-node-or-screen :query-all-by-test-id)) (def query-all-by-test-id (with-node-or-screen :query-all-by-test-id))
(def query-by-test-id (with-node-or-screen :query-by-test-id)) (def query-by-test-id (with-node-or-screen :query-by-test-id))
(defn- prepare-translation
[translation]
(if (exists? js/jest)
;; Translations are treated differently when running with Jest. See
;; test/jest/jestSetup.js for more details.
(str "tx:" (name translation))
(i18n/label translation)))
(defn get-all-by-translation-text (defn get-all-by-translation-text
([translation] ([translation]
(get-all-by-translation-text rtl/screen translation)) (get-all-by-translation-text rtl/screen translation nil))
([^js node translation & args] ([translation translation-opts]
(apply (with-node-or-screen :get-all-by-text) node (prepare-translation translation) args))) (get-all-by-translation-text rtl/screen translation translation-opts))
([^js node translation translation-opts & args]
(apply (with-node-or-screen :get-all-by-text)
node
(i18n/label translation translation-opts)
args)))
(defn get-by-translation-text (defn get-by-translation-text
([translation] ([translation]
(get-by-translation-text rtl/screen translation)) (get-by-translation-text rtl/screen translation nil))
([^js node translation & args] ([translation translation-opts]
(apply (with-node-or-screen :get-by-text) node (prepare-translation translation) args))) (get-by-translation-text rtl/screen translation translation-opts))
([^js node translation translation-opts & args]
(apply (with-node-or-screen :get-by-text)
node
(i18n/label translation translation-opts)
args)))
(defn query-by-translation-text (defn query-by-translation-text
([translation] ([translation]
(query-by-translation-text rtl/screen translation)) (query-by-translation-text rtl/screen translation nil))
([^js node translation & args] ([translation translation-opts]
(apply (with-node-or-screen :query-by-text) node (prepare-translation translation) args))) (query-by-translation-text rtl/screen translation translation-opts))
([^js node translation translation-opts & args]
(apply (with-node-or-screen :query-by-text)
node
(i18n/label translation translation-opts)
args)))
(defn query-all-by-translation-text (defn query-all-by-translation-text
([translation] ([translation]
(query-all-by-translation-text rtl/screen translation)) (query-all-by-translation-text rtl/screen translation nil))
([^js node translation & args] ([translation translation-opts]
(apply (with-node-or-screen :query-all-by-text) node (prepare-translation translation) args))) (query-all-by-translation-text rtl/screen translation translation-opts))
([^js node translation translation-opts & args]
(apply (with-node-or-screen :query-all-by-text)
node
(i18n/label translation translation-opts)
args)))
;;; Jest utilities ;;; Jest utilities
@ -251,3 +264,32 @@
(let [rerender-fn (oops/oget component "rerender") (let [rerender-fn (oops/oget component "rerender")
react-element (reagent/as-element component-updated)] react-element (reagent/as-element component-updated)]
(rerender-fn react-element)))) (rerender-fn react-element))))
(defn setup-subs
"Registers `subscriptions`, a map of key (sub ID) to value (sub computation)."
[subscriptions]
(doseq [[sub-id v] subscriptions]
(re-frame/reg-sub sub-id
(fn [_] v))))
(defn setup-restorable-re-frame
[]
(let [restorer (atom nil)]
(js/beforeEach
(fn []
(reset! restorer (re-frame/make-restore-fn))))
(js/afterEach
(fn []
(@restorer)))))
(defn setup-fake-timers
[]
(js/beforeEach
(fn []
(use-fake-timers)))
(js/afterEach
(fn []
(clear-all-timers)
(use-real-timers))))

View File

@ -0,0 +1,22 @@
(ns test-helpers.component-tests-preload
{:dev/always true}
(:require
;; NOTE: Do NOT sort i18n-resources because it MUST be loaded first.
[status-im.setup.i18n-resources :as i18n-resources]
#_{:clj-kondo/ignore [:unsorted-required-namespaces]}
[status-im.setup.interceptors :as interceptors]
[utils.i18n :as i18n]))
(defn- setup
"Prerequisites to run some component tests, for example, the ones in
`status-im.contexts.wallet.send.input-amount.component-spec`.
Because of the way Jest and ShadowCLJS are set up, this is a preload file that
should never be directly required. However, it will be loaded automatically
before any component test runs."
[]
(interceptors/register-global-interceptors)
(i18n/set-language "en")
(i18n-resources/load-language "en"))
(setup)

View File

@ -1,10 +1,51 @@
const transformIgnorePatterns = () => {
const libs = [
'@react-native',
'@react-native-community',
'@react-native-community/blur',
'react-native',
'react-native-background-timer',
'react-native-gifted-charts',
'react-native-haptic-feedback',
'react-native-hole-view',
'react-native-image-crop-picker',
'react-native-languages',
'react-native-linear-gradient',
'react-native-permissions',
'react-native-reanimated',
'react-native-redash',
'react-native-redash',
'react-native-shake',
'react-native-static-safe-area-insets',
'rn-emoji-keyboard',
].join('|');
return [`/node_modules/(?!(${libs})/).*/`];
};
const shouldUseSilentReporter = () => {
return process.env.JEST_USE_SILENT_REPORTER === 'true';
};
const reporters = () => {
let reporters = [];
if (shouldUseSilentReporter()) {
reporters.push(['jest-silent-reporter', { useDots: true }]);
} else {
reporters.push('default');
}
return reporters;
};
module.exports = { module.exports = {
preset: 'react-native', preset: 'react-native',
setupFilesAfterEnv: [ setupFilesAfterEnv: [
'@testing-library/jest-native/extend-expect', '@testing-library/jest-native/extend-expect',
'../component-spec/status_im.setup.schema_preload.js', '../component-spec/status_im.setup.schema_preload.js',
'../component-spec/test_helpers.component_tests_preload.js',
'../test/jest/jestSetup.js', '../test/jest/jestSetup.js',
], ],
reporters: reporters(),
setupFiles: [], setupFiles: [],
testPathIgnorePatterns: [], testPathIgnorePatterns: [],
moduleNameMapper: { moduleNameMapper: {
@ -12,14 +53,19 @@ module.exports = {
'<rootDir>/../node_modules/react-native/Libraries/Image/RelativeImageStub', '<rootDir>/../node_modules/react-native/Libraries/Image/RelativeImageStub',
}, },
testTimeout: 60000, testTimeout: 60000,
transformIgnorePatterns: [ transformIgnorePatterns: transformIgnorePatterns(),
'/node_modules/(?!(@react-native|react-native-haptic-feedback|react-native-redash|react-native-image-crop-picker|@react-native-community|react-native-linear-gradient|react-native-background-timer|react-native|rn-emoji-keyboard|react-native-languages|react-native-shake|react-native-reanimated|react-native-redash|react-native-permissions|@react-native-community/blur|react-native-static-safe-area-insets|react-native-hole-view|react-native-gifted-charts)/).*/', // This is a workaround after upgrading to Jest 29.7.0, otherwise we get:
], //
// SyntaxError: node_modules/@react-native/js-polyfills/error-guard.js:
// Missing semicolon. (14:4)
//
transform: {
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { configFile: './babel.config.js' }],
},
globals: { globals: {
__TEST__: true, __TEST__: true,
}, },
testEnvironment: 'node', testEnvironment: 'node',
timers: 'fake',
rootDir: '../../component-spec', rootDir: '../../component-spec',
testMatch: ['**/*__tests__*', '**/*.component_spec.js'], testMatch: ['**/*__tests__*', '**/*.component_spec.js'],
}; };

View File

@ -7,6 +7,10 @@ require('react-native-reanimated/src/reanimated2/jestUtils').setUpTests();
jest.mock('@react-native-async-storage/async-storage', () => mockAsyncStorage); jest.mock('@react-native-async-storage/async-storage', () => mockAsyncStorage);
jest.mock('react-native-fs', () => ({
default: {},
}));
jest.mock('react-native-navigation', () => ({ jest.mock('react-native-navigation', () => ({
getNavigationConstants: () => ({ constants: [] }), getNavigationConstants: () => ({ constants: [] }),
Navigation: { Navigation: {

2459
yarn.lock

File diff suppressed because it is too large Load Diff