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}}}

View File

@ -18,13 +18,17 @@
;; https://github.com/kkinnear/zprint/blob/main/doc/reference.md#respect-bl
:respect-bl
;; hang multiline left-hand-thing
;; hang multiline left-hand-thing
;; https://github.com/kkinnear/zprint/issues/273
:multi-lhs-hang]
:fn-map
{"reg-sub" :arg1-pair
"h/describe" :arg1-body
"h/describe-skip" :arg1-body
"h/describe-only" :arg1-body
"h/test" :arg1-body
"h/test-skip" :arg1-body
"h/test-only" :arg1-body
"global.describe" :arg1-body
"global.test" :arg1-body
"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) && \
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 && \
node_modules/.bin/prettier --write .
@ -364,6 +364,7 @@ android-test:
component-test-watch: export TARGET := clojure
component-test-watch: export COMPONENT_TEST := true
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
@@scripts/check-metro-shadow-process.sh
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 COMPONENT_TEST := true
component-test: export BABEL_ENV := test
component-test: export JEST_USE_SILENT_REPORTER := false
component-test: ##@test Run component tests once in NodeJS
@scripts/check-metro-shadow-process.sh
rm -rf ./component-spec

View File

@ -12,8 +12,7 @@ stdenv.mkDerivation {
"patchBuildIdPhase"
"patchKeyChainPhase"
"patchGlogPhase"
"patchJestPhase"
"installPhase"
"installPhase"
];
# First symlink all modules as is
@ -56,13 +55,6 @@ stdenv.mkDerivation {
'-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 = ''
mkdir -p $out
cp -R node_modules $out/

View File

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

View File

@ -1,6 +1,6 @@
#!/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
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"

View File

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

View File

@ -63,14 +63,7 @@
:track-icon :face-id})
(h/describe "slide-button"
(h/before-each
(fn []
(h/use-fake-timers)))
(h/after-each
(fn []
(h/clear-all-timers)
(h/use-real-timers)))
(h/setup-fake-timers)
(h/test "render the correct text"
(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/expect @selected)
(.toStrictEqual :blue)))))

View File

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

View File

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

View File

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

View File

@ -71,7 +71,7 @@
:icon-avatar :i/placeholder
:type :keypair}])
(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)))
(h/test "component renders in keypair type when keycard? is true"
@ -81,7 +81,7 @@
:icon-avatar :i/placeholder
:type :keypair}])
(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)))
(h/test "component renders in default-keypair type"

View File

@ -13,7 +13,10 @@
{:time-frame :empty}])
(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
{:time-frame :1-week
:data (data 7)}])

View File

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

View File

@ -6,7 +6,9 @@
(h/describe "keycard component"
(h/test "Render of keycard component when: holder-name prop is not set"
(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/render [keycard/keycard {:holder-name "Alisha"}])
(h/is-truthy (h/get-by-translation-text :user-keycard))))
(let [holder-name "Alisha"]
(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/is-truthy (h/query-by-label-text :tag-container)))
(h/test "renders keycard icon if title-icon? is true"
(h/render [account/view {:title-icon? true}])
(h/test "renders keycard icon if title-icon is present"
(h/render [account/view {:title-icon :i/placeholder}])
(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/is-falsy (h/query-by-label-text :title-icon)))

View File

@ -22,14 +22,14 @@
{:backgroundColor colors/white-opa-5})))
(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-out (h/get-by-label-text :container))
(h/wait-for #(h/has-style (h/query-by-label-text :container)
{:backgroundColor (colors/custom-color :blue 50 10)})))
(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-out (h/get-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]))
(h/describe "record audio component"
(h/before-each
(fn []
(h/use-fake-timers)))
(h/after-each
(fn []
(h/clear-all-timers)
(h/use-real-timers)))
(h/setup-fake-timers)
(h/test "renders record-audio"
(h/render [record-audio/record-audio])

View File

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

View File

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

View File

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

View File

@ -9,48 +9,52 @@
[quo.theme :as quo.theme]
[react-native.core :as rn]))
(defn network-bridge-add
[{:keys [network state theme]}]
[rn/view {:style (merge (style/container network state theme) (style/add-container theme))}
[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
[{:keys [theme network status amount container-style] :as args}]
(let [network-text (if (= network :ethereum) "Mainnet" (string/capitalize (name network)))]
(if (= status :add)
[network-bridge-add args]
[rn/view
{:style (merge (style/container network status theme) container-style)
:accessible true
:accessibility-label :container}
(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}])])
(if (= status :add)
[network-bridge-add args]
[rn/view
{:style (merge (style/container network status theme) container-style)
:accessible true
:accessibility-label :container}
(if (= status :loading)
[rn/view
{:style {:flex-direction :row
:align-items :center}}
[rn/image
{:source (resources/get-network network)
:style style/network-icon}]
{:style (style/loading-skeleton theme)
:accessible true
:accessibility-label :loading}]
[rn/view
{:style {:flex-direction :row
:justify-content :space-between}}
[text/text
{:size :label
:weight :medium
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
network-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
{: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))

View File

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

View File

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

View File

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

View File

@ -1,10 +1,12 @@
(ns status-im.contexts.chat.messages.content.audio.component-spec
(:require
[re-frame.core :as re-frame]
[react-native.audio-toolkit :as audio]
[status-im.contexts.chat.messages.content.audio.view :as audio-message]
[test-helpers.component :as h]))
;; We can't rely on `with-redefs` with async code.
(set! audio/destroy-player #())
(def message
{:audio-duration-ms 5000
:message-id "message-id"})
@ -12,17 +14,13 @@
(def context
{: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/setup-restorable-re-frame)
(h/before-each
#(setup-subs {:mediaserver/port 1000
:app-state "active"}))
(fn []
(h/setup-subs {:mediaserver/port 1000
:app-state "active"})))
(h/test "renders correctly"
(h/render [audio-message/audio-message message context])
@ -31,24 +29,19 @@
(h/test "press play calls audio/toggle-playpause-player"
(with-redefs [audio/toggle-playpause-player (js/jest.fn)
audio/new-player (fn [_ _ _] {})
audio/destroy-player #()
audio/prepare-player (fn [_ on-success _] (on-success))
audio-message/download-audio-http (fn [_ on-success] (on-success "audio-uri"))]
(h/render [audio-message/audio-message message context])
(h/fire-event
:on-press
(h/get-by-label-text :play-pause-audio-message-button))
(h/fire-event :on-press (h/get-by-label-text :play-pause-audio-message-button))
(-> (h/expect audio/toggle-playpause-player)
(.toHaveBeenCalledTimes 1))))
(h/test "press play renders error"
(h/render [audio-message/audio-message message context])
(with-redefs [audio/toggle-playpause-player (fn [_ _ _ on-error] (on-error))
audio/new-player (fn [_ _ _] {})
audio/destroy-player #()
(with-redefs [audio/new-player (fn [_ _ _] {})
audio/prepare-player (fn [_ on-success _] (on-success))
audio-message/download-audio-http (fn [_ on-success] (on-success "audio-uri"))]
(h/fire-event
:on-press
(h/get-by-label-text :play-pause-audio-message-button))
(h/wait-for #(h/is-truthy (h/get-by-label-text :audio-error-label))))))
(h/render [audio-message/audio-message message context])
(h/fire-event :on-press (h/get-by-label-text :play-pause-audio-message-button)))
(with-redefs [audio/toggle-playpause-player (fn [_ _ _ on-error] (on-error))]
(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
(:require
[re-frame.core :as re-frame]
[status-im.contexts.communities.actions.community-options.view :as options]
[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/setup-restorable-re-frame)
(h/test "joined options - Non token Gated"
(setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined true}})
(h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined true}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-community-rules))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :mark-as-read))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :mute-community))
(.toBeTruthy))
(-> (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/is-truthy (h/get-by-translation-text :t/view-members))
(h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(h/is-truthy (h/get-by-translation-text :t/mark-as-read))
(h/is-truthy (h/get-by-translation-text :t/mute-community))
(h/is-truthy (h/get-by-translation-text :t/notification-settings))
(h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(h/is-truthy (h/get-by-translation-text :t/show-qr))
(h/is-truthy (h/get-by-translation-text :t/share-community))
(h/is-truthy (h/get-by-translation-text :t/leave-community)))
(h/test "joined options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined true
:token-permissions []}})
(h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined true
:token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-community-rules))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-token-gating))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :mark-as-read))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :mute-community))
(.toBeTruthy))
(-> (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/is-truthy (h/get-by-translation-text :t/view-members))
(h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(h/is-truthy (h/get-by-translation-text :t/view-token-gating))
(h/is-truthy (h/get-by-translation-text :t/mark-as-read))
(h/is-truthy (h/get-by-translation-text :t/mute-community))
(h/is-truthy (h/get-by-translation-text :t/notification-settings))
(h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(h/is-truthy (h/get-by-translation-text :t/show-qr))
(h/is-truthy (h/get-by-translation-text :t/share-community))
(h/is-truthy (h/get-by-translation-text :t/leave-community)))
(h/test "admin options - Non token Gated"
(setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:admin true}})
(h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:admin true}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-community-rules))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :mark-as-read))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :mute-community))
(.toBeTruthy))
(-> (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/is-truthy (h/get-by-translation-text :t/view-members))
(h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(h/is-truthy (h/get-by-translation-text :t/mark-as-read))
(h/is-truthy (h/get-by-translation-text :t/mute-community))
(h/is-truthy (h/get-by-translation-text :t/notification-settings))
(h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(h/is-truthy (h/get-by-translation-text :t/show-qr))
(h/is-truthy (h/get-by-translation-text :t/share-community)))
(h/test "admin options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:admin true
:token-permissions []}})
(h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:admin true
:token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-community-rules))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :mark-as-read))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :mute-community))
(.toBeTruthy))
(-> (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/is-truthy (h/get-by-translation-text :t/view-members))
(h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(h/is-truthy (h/get-by-translation-text :t/mark-as-read))
(h/is-truthy (h/get-by-translation-text :t/mute-community))
(h/is-truthy (h/get-by-translation-text :t/notification-settings))
(h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(h/is-truthy (h/get-by-translation-text :t/show-qr))
(h/is-truthy (h/get-by-translation-text :t/share-community)))
(h/test "request sent options - Non token Gated"
(setup-subs {:communities/my-pending-request-to-join "mock-id"
:communities/community {}})
(h/setup-subs {:communities/my-pending-request-to-join "mock-id"
:communities/community {}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-community-rules))
(.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 :cancel-request-to-join))
(.toBeTruthy)))
(h/is-truthy (h/get-by-translation-text :t/view-members))
(h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(h/is-truthy (h/get-by-translation-text :t/show-qr))
(h/is-truthy (h/get-by-translation-text :t/share-community))
(h/is-truthy (h/get-by-translation-text :t/cancel-request-to-join)))
(h/test "request sent options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join "mock-id"
:communities/community {:token-permissions []}})
(h/setup-subs {:communities/my-pending-request-to-join "mock-id"
:communities/community {:token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-token-gating))
(.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 :cancel-request-to-join))
(.toBeTruthy)))
(h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(h/is-truthy (h/get-by-translation-text :t/view-token-gating))
(h/is-truthy (h/get-by-translation-text :t/show-qr))
(h/is-truthy (h/get-by-translation-text :t/share-community))
(h/is-truthy (h/get-by-translation-text :t/cancel-request-to-join)))
(h/test "banned options - Non token Gated"
(setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:banList true}})
(h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:banList true}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :view-members))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-community-rules))
(.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/is-truthy (h/get-by-translation-text :t/view-members))
(h/is-truthy (h/get-by-translation-text :t/view-community-rules))
(h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(h/is-truthy (h/get-by-translation-text :t/show-qr))
(h/is-truthy (h/get-by-translation-text :t/share-community)))
(h/test "banned options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:banList 100
:token-permissions []}})
(h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:banList 100
:token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-token-gating))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy)))
(h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(h/is-truthy (h/get-by-translation-text :t/view-token-gating))
(h/is-truthy (h/get-by-translation-text :t/show-qr))
(h/is-truthy (h/get-by-translation-text :t/share-community)))
(h/test "banned options - Token Gated"
(setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:banList 100
:token-permissions []}})
(h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:banList 100
:token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :invite-people-from-contacts))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :view-token-gating))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :show-qr))
(.toBeTruthy))
(-> (h/expect (h/get-by-translation-text :share-community))
(.toBeTruthy)))
(h/is-truthy (h/get-by-translation-text :t/invite-people-from-contacts))
(h/is-truthy (h/get-by-translation-text :t/view-token-gating))
(h/is-truthy (h/get-by-translation-text :t/show-qr))
(h/is-truthy (h/get-by-translation-text :t/share-community)))
(h/test "joined and muted community"
(setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined true
:muted true
:token-permissions []}})
(h/setup-subs {:communities/my-pending-request-to-join nil
:communities/community {:joined true
:muted true
:token-permissions []}})
(h/render [options/community-options-bottom-sheet {:id "test"}])
(-> (h/expect (h/get-by-translation-text :unmute-community))
(.toBeTruthy))))
(h/is-truthy (h/get-by-translation-text :t/unmute-community))))

View File

@ -1,38 +1,30 @@
(ns status-im.contexts.wallet.add-address-to-watch.component-spec
(: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.events
[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/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"
(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)
"0x12E838Ae1f769147b12956485dc56e57138f3AC8")
(h/is-truthy (h/get-by-translation-text :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/is-truthy (h/get-by-translation-text :t/address-already-in-use)))
(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
(:require
[re-frame.core :as re-frame]
[status-im.contexts.wallet.add-address-to-watch.confirm-address.view :as confirm-address]
[test-helpers.component :as h]
[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/setup-restorable-re-frame)
(h/test "Create Account button is disabled while no account name exists"
(let [callback (h/mock-fn)]
(with-redefs [rf/dispatch #(callback)]
(setup-subs {:profile/wallet-accounts []
:get-screen-params {:address "0xmock-address"}})
(h/setup-subs {:profile/wallet-accounts []
:get-screen-params {:address "0xmock-address"}})
(h/render [confirm-address/view {}])
(h/is-truthy (h/get-by-text "0xmock-address"))
(h/was-not-called callback)

View File

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

View File

@ -1,15 +1,14 @@
(ns status-im.contexts.wallet.send.input-amount.component-spec
(: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]
[test-helpers.component :as h]))
[test-helpers.component :as h]
[utils.debounce :as debounce]
[utils.re-frame :as rf]))
(defn setup-subs
[subscriptions]
(doseq [keyval subscriptions]
(re-frame/reg-sub
(key keyval)
(fn [_] (val keyval)))))
(set! rf/dispatch #())
(set! debounce/debounce-and-dispatch #())
(def sub-mocks
{:profile/profile {:currency :usd}
@ -45,74 +44,111 @@
:wallet/wallet-send-route {:route []}})
(h/describe "Send > input amount screen"
(h/setup-restorable-re-frame)
(h/test "Default render"
(with-redefs [re-frame/dispatch #()]
(setup-subs sub-mocks)
(h/render [input-amount/view {}])
(h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "ETH"))
(h/is-truthy (h/get-by-text "$0.00"))
(h/is-disabled (h/get-by-label-text :button-one))))
(h/setup-subs sub-mocks)
(h/render [input-amount/view {}])
(h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "ETH"))
(h/is-truthy (h/get-by-text "$0.00"))
(h/is-disabled (h/get-by-label-text :button-one)))
(h/test "Fill token input and confirm"
(with-redefs [re-frame/dispatch #()]
(setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)]
(h/render [input-amount/view
{:on-confirm on-confirm
:rate 10
:limit 1000}])
(h/setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)]
(h/render [input-amount/view
{:on-confirm on-confirm
:rate 10
: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-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/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/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))
(h/was-called on-confirm))))
(let [on-confirm (h/mock-fn)]
(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"
(with-redefs [re-frame/dispatch #()]
(setup-subs sub-mocks)
(h/render [input-amount/view
{:rate 10
:limit 286}])
(h/setup-subs sub-mocks)
(h/render [input-amount/view
{:rate 10
: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-9))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(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/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/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/is-truthy (h/get-by-text "$2850.00")))))
(h/test "Try to fill more than limit"
(h/setup-subs sub-mocks)
(h/render [input-amount/view
{: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"
(with-redefs [re-frame/dispatch #()]
(setup-subs sub-mocks)
(h/render [input-amount/view
{:rate 10
:limit 250}])
(h/setup-subs sub-mocks)
(h/render [input-amount/view
{:rate 10
: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-0))
(h/wait-for #(h/is-truthy (h/get-by-text "$200.00")))
(h/fire-event :press (h/query-by-label-text :reorder))
(h/wait-for #(h/is-truthy (h/get-by-text "2.00 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/is-truthy (h/get-by-text "205.50 ETH")))
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(h/is-truthy (h/get-by-text "205.50 ETH"))))))
(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/wait-for #(h/get-by-text "$200.00"))
(.then (fn []
(h/fire-event :press (h/query-by-label-text :reorder))
(h/wait-for #(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/wait-for #(h/get-by-text "205.50 ETH"))))
(.then (fn []
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
(h/wait-for #(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.communities.actions.community-options.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.create-account.edit-derivation-path.component-spec]
;; [status-im.contexts.wallet.send.input-amount.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.send.input-amount.component-spec]))

View File

@ -97,7 +97,7 @@
(malli.instrument/unstrument!)
(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
;; 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".
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
[description & body]
`(js/global.test
~description
(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
[description & body]
`(js/beforeEach

View File

@ -5,6 +5,7 @@
[camel-snake-kebab.core :as camel-snake-kebab]
[oops.core :as oops]
[quo.theme :as quo.theme]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[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-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
([translation]
(get-all-by-translation-text rtl/screen translation))
([^js node translation & args]
(apply (with-node-or-screen :get-all-by-text) node (prepare-translation translation) args)))
(get-all-by-translation-text rtl/screen translation nil))
([translation translation-opts]
(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
([translation]
(get-by-translation-text rtl/screen translation))
([^js node translation & args]
(apply (with-node-or-screen :get-by-text) node (prepare-translation translation) args)))
(get-by-translation-text rtl/screen translation nil))
([translation translation-opts]
(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
([translation]
(query-by-translation-text rtl/screen translation))
([^js node translation & args]
(apply (with-node-or-screen :query-by-text) node (prepare-translation translation) args)))
(query-by-translation-text rtl/screen translation nil))
([translation translation-opts]
(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
([translation]
(query-all-by-translation-text rtl/screen translation))
([^js node translation & args]
(apply (with-node-or-screen :query-all-by-text) node (prepare-translation translation) args)))
(query-all-by-translation-text rtl/screen translation nil))
([translation translation-opts]
(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
@ -251,3 +264,32 @@
(let [rerender-fn (oops/oget component "rerender")
react-element (reagent/as-element component-updated)]
(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 = {
preset: 'react-native',
setupFilesAfterEnv: [
'@testing-library/jest-native/extend-expect',
'../component-spec/status_im.setup.schema_preload.js',
'../component-spec/test_helpers.component_tests_preload.js',
'../test/jest/jestSetup.js',
],
reporters: reporters(),
setupFiles: [],
testPathIgnorePatterns: [],
moduleNameMapper: {
@ -12,14 +53,19 @@ module.exports = {
'<rootDir>/../node_modules/react-native/Libraries/Image/RelativeImageStub',
},
testTimeout: 60000,
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)/).*/',
],
transformIgnorePatterns: transformIgnorePatterns(),
// 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: {
__TEST__: true,
},
testEnvironment: 'node',
timers: 'fake',
rootDir: '../../component-spec',
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-fs', () => ({
default: {},
}));
jest.mock('react-native-navigation', () => ({
getNavigationConstants: () => ({ constants: [] }),
Navigation: {

2459
yarn.lock

File diff suppressed because it is too large Load Diff