From 2ce34962e78245216f711692034f61e1214e6074 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Mon, 26 Feb 2024 14:02:00 -0300 Subject: [PATCH 01/14] Rewrite integration tests without lib day8.re-frame/test --- src/test_helpers/integration.clj | 24 --- src/test_helpers/integration.cljs | 60 +++++++- src/tests/integration_test/chat_test.cljs | 146 +++++++++---------- src/tests/integration_test/core_test.cljs | 37 ++--- src/tests/integration_test/profile_test.cljs | 113 +++++++------- 5 files changed, 204 insertions(+), 176 deletions(-) delete mode 100644 src/test_helpers/integration.clj diff --git a/src/test_helpers/integration.clj b/src/test_helpers/integration.clj deleted file mode 100644 index 0018e6dae5f..00000000000 --- a/src/test_helpers/integration.clj +++ /dev/null @@ -1,24 +0,0 @@ -(ns test-helpers.integration - (:require [day8.re-frame.test :as rf-test] - [re-frame.core :as rf])) - -(defmacro with-app-initialized - [& body] - `(do - (legacy.status-im.utils.test/init!) - (if (test-helpers.integration/app-initialized) - (do ~@body) - (do - (rf/dispatch [:app-started]) - (rf-test/wait-for [:profile/get-profiles-overview-success] - ~@body))))) - -(defmacro with-account - [& body] - `(if (test-helpers.integration/messenger-started) - (do ~@body) - (do - (test-helpers.integration/create-multiaccount!) - (rf-test/wait-for [:messenger-started] - (test-helpers.integration/assert-messenger-started) - ~@body)))) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 36f0ef48810..9dae561da10 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -1,9 +1,9 @@ (ns test-helpers.integration - (:require-macros [test-helpers.integration]) (:require - [cljs.test :refer [is]] + [cljs.test :refer [is] :as test] legacy.status-im.events legacy.status-im.subs.root + [legacy.status-im.utils.test :as legacy-test] [native-module.core :as native-module] [re-frame.core :as rf] status-im.events @@ -60,3 +60,59 @@ (defn log-headline [test-name] (log/info (str "========= " (name test-name) " =================="))) + +(defn wait-for + ([target-event-ids] + (wait-for target-event-ids 10000)) + ([target-event-ids timeout-ms] + (let [waiting-for (atom (set target-event-ids))] + (js/Promise. + (fn [promise-resolve promise-reject] + (let [cb-id (gensym "post-event-callback") + timer-id (js/setTimeout (fn [] + (rf/remove-post-event-callback cb-id) + (promise-reject (ex-info "some events did not run" + {:event-ids target-event-ids + :timeout-ms timeout-ms + :waiting-for @waiting-for} + ::timeout))) + timeout-ms)] + (rf/add-post-event-callback + cb-id + (fn [[event-id & _]] + (when (contains? @waiting-for event-id) + (swap! waiting-for disj event-id) + (when (empty? @waiting-for) + (js/clearTimeout timer-id) + (rf/remove-post-event-callback cb-id) + (promise-resolve))))))))))) + +(defn rf-test-async + [f] + (test/async + done + (let [restore-fn (rf/make-restore-fn)] + (-> (f done) + (.catch (fn [error] + (is (true? false) (str "async test failed" error)))) + (.finally (fn [] + (restore-fn) + (done))))))) + +(defn with-app-initialized + [] + (legacy-test/init!) + (if (app-initialized) + (js/Promise.resolve) + (do + (rf/dispatch [:app-started]) + (wait-for [:profile/get-profiles-overview-success])))) + +(defn with-account + [] + (if (messenger-started) + (js/Promise.resolve) + (do + (create-multiaccount!) + (-> (wait-for [:messenger-started]) + (.then #(assert-messenger-started)))))) diff --git a/src/tests/integration_test/chat_test.cljs b/src/tests/integration_test/chat_test.cljs index ce3e722f9bc..ac66fab10b7 100644 --- a/src/tests/integration_test/chat_test.cljs +++ b/src/tests/integration_test/chat_test.cljs @@ -1,7 +1,6 @@ (ns tests.integration-test.chat-test (:require [cljs.test :refer [deftest is]] - [day8.re-frame.test :as rf-test] legacy.status-im.events [legacy.status-im.multiaccounts.logout.core :as logout] legacy.status-im.subs.root @@ -16,81 +15,82 @@ "0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd") (deftest one-to-one-chat-test - (h/log-headline :one-to-one-chat-test) - (rf-test/run-test-async - (h/with-app-initialized - (h/with-account - (rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat - (rf-test/wait-for - [:chat/one-to-one-chat-created] - (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) - (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) - (h/logout) - (rf-test/wait-for [::logout/logout-method])))))) + (h/rf-test-async + (fn [] + (h/log-headline ::one-to-one-chat-test) + (-> (h/with-app-initialized) + (.then h/with-account) + (.then #(rf/dispatch-sync [:chat.ui/start-chat chat-id])) + (.then #(h/wait-for [:chat/one-to-one-chat-created])) + (.then (fn [] + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) + (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) + (h/logout))) + (.then #(h/wait-for [::logout/logout-method])))))) (deftest delete-chat-test - (h/log-headline :delete-chat-test) - (rf-test/run-test-async - (h/with-app-initialized - (h/with-account - (rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat - (rf-test/wait-for - [:chat/one-to-one-chat-created] - (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) - (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) - (is @(rf/subscribe [:chats/chat chat-id])) - (rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id]) - (rf/dispatch-sync [:chat.ui/remove-chat chat-id]) - (h/logout) - (rf-test/wait-for [::logout/logout-method])))))) + (h/rf-test-async + (fn [] + (h/log-headline ::delete-chat-test) + (-> (h/with-app-initialized) + (.then h/with-account) + (.then #(rf/dispatch-sync [:chat.ui/start-chat chat-id])) + (.then #(h/wait-for [:chat/one-to-one-chat-created])) + (.then (fn [] + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) + (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) + (is @(rf/subscribe [:chats/chat chat-id])) + (rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id]) + (rf/dispatch-sync [:chat.ui/remove-chat chat-id]) + (h/logout))) + (.then #(h/wait-for [::logout/logout-method])))))) (deftest mute-chat-test - (h/log-headline :mute-chat-test) - (rf-test/run-test-async - (h/with-app-initialized - (h/with-account - (rf/dispatch-sync [:chat.ui/start-chat chat-id]) ;; start a new chat - (rf-test/wait-for - [:chat/one-to-one-chat-created] - (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) - (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) - (is @(rf/subscribe [:chats/chat chat-id])) - (rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted]) - (rf-test/wait-for - [:chat/mute-successfully] - (is @(rf/subscribe [:chats/muted chat-id])) - (rf/dispatch-sync [:chat.ui/mute chat-id false]) - (rf-test/wait-for - [:chat/mute-successfully] - (is (not @(rf/subscribe [:chats/muted chat-id]))) - (h/logout) - (rf-test/wait-for [::logout/logout-method])))))))) + (h/rf-test-async + (fn [] + (h/log-headline ::mute-chat-test) + (-> (h/with-app-initialized) + (.then h/with-account) + (.then #(rf/dispatch-sync [:chat.ui/start-chat chat-id])) + (.then #(h/wait-for [:chat/one-to-one-chat-created])) + (.then (fn [] + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) + (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) + (is @(rf/subscribe [:chats/chat chat-id])) + (rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted]))) + (.then #(h/wait-for [:chat/mute-successfully])) + (.then (fn [] + (is @(rf/subscribe [:chats/muted chat-id])) + (rf/dispatch-sync [:chat.ui/mute chat-id false]))) + (.then #(h/wait-for [:chat/mute-successfully])) + (.then (fn [] + (is (not @(rf/subscribe [:chats/muted chat-id]))) + (h/logout))) + (.then #(h/wait-for [::logout/logout-method])))))) (deftest add-contact-test - (h/log-headline :add-contact-test) - (let - [compressed-key "zQ3shMwgSMKHVznoowceZMxWde9HUnkQEVSGvvex8UFpFNErL" - public-key (str "0x0407e9dc435fe366cb0b4c4f35cbd925438c0f46fe0" - "ed2a86050325bc8856e26898c17e31dee2602b9429c91" - "ecf65a41d62ac1f2f0823c0710dcb536e79af2763c") - primary-name "zQ3...pFNErL"] - (rf-test/run-test-async - (h/with-app-initialized - (h/with-account - ;; search for contact using compressed key - (rf/dispatch [:contacts/set-new-identity {:input compressed-key}]) - (rf-test/wait-for - [:contacts/set-new-identity-success] - (let [new-identity @(rf/subscribe [:contacts/new-identity])] - (is (= public-key (:public-key new-identity))) - (is (= :valid (:state new-identity)))) - ;; click 'view profile' button - (rf/dispatch [:chat.ui/show-profile public-key]) - (rf-test/wait-for - [:contacts/build-contact] - (rf-test/wait-for - [:contacts/build-contact-success] - (let [contact @(rf/subscribe [:contacts/current-contact])] - (is (= primary-name (:primary-name contact)))) - (h/logout) - (rf-test/wait-for [::logout/logout-method]))))))))) + (h/rf-test-async + (fn [] + (h/log-headline :add-contact-test) + (let [compressed-key "zQ3shMwgSMKHVznoowceZMxWde9HUnkQEVSGvvex8UFpFNErL" + public-key (str "0x0407e9dc435fe366cb0b4c4f35cbd925438c0f46fe0" + "ed2a86050325bc8856e26898c17e31dee2602b9429c91" + "ecf65a41d62ac1f2f0823c0710dcb536e79af2763c") + primary-name "zQ3...pFNErL"] + (-> (h/with-app-initialized) + (.then h/with-account) + ;; Search for contact using compressed key + (.then #(rf/dispatch [:contacts/set-new-identity {:input compressed-key}])) + (.then #(h/wait-for [:contacts/set-new-identity-success])) + (.then (fn [] + (let [new-identity @(rf/subscribe [:contacts/new-identity])] + (is (= public-key (:public-key new-identity))) + (is (= :valid (:state new-identity)))) + ;; click 'view profile' button + (rf/dispatch [:chat.ui/show-profile public-key]))) + (.then #(h/wait-for [:contacts/build-contact :contacts/build-contact-success])) + (.then (fn [] + (let [contact @(rf/subscribe [:contacts/current-contact])] + (is (= primary-name (:primary-name contact)))) + (h/logout))) + (.then #(h/wait-for [::logout/logout-method]))))))) diff --git a/src/tests/integration_test/core_test.cljs b/src/tests/integration_test/core_test.cljs index bc98c707032..a6a5efa0f25 100644 --- a/src/tests/integration_test/core_test.cljs +++ b/src/tests/integration_test/core_test.cljs @@ -1,34 +1,35 @@ (ns tests.integration-test.core-test (:require [cljs.test :refer [deftest]] - [day8.re-frame.test :as rf-test] legacy.status-im.events [legacy.status-im.multiaccounts.logout.core :as logout] legacy.status-im.subs.root [legacy.status-im.utils.test :as utils.test] [re-frame.core :as rf] + [status-im.common.log :as log] status-im.events status-im.navigation.core status-im.subs.root [test-helpers.integration :as h])) (deftest initialize-app-test - (h/log-headline :initialize-app-test) - (rf-test/run-test-async - (utils.test/init!) - (rf/dispatch [:app-started]) - (rf-test/wait-for - ;; use initialize-view because it has the longest avg. time and - ;; is dispatched by initialize-multiaccounts (last non-view event) - [:profile/get-profiles-overview-success] - (rf-test/wait-for - [:font/init-font-file-for-initials-avatar] - (h/assert-app-initialized))))) + (h/rf-test-async + (fn [] + (h/log-headline ::initialize-app-test) + ;; (log/setup "ERROR") + (utils.test/init!) + (rf/dispatch [:app-started]) + ;; Use initialize-view because it has the longest avg. time and is + ;; dispatched by initialize-multiaccounts (last non-view event). + (-> (h/wait-for [:profile/get-profiles-overview-success + :font/init-font-file-for-initials-avatar]) + (.then #(h/assert-app-initialized)))))) (deftest create-account-test - (h/log-headline :create-account-test) - (rf-test/run-test-async - (h/with-app-initialized - (h/with-account - (h/logout) - (rf-test/wait-for [::logout/logout-method]))))) + (h/rf-test-async + (fn [] + (h/log-headline ::create-account-test) + (-> (h/with-app-initialized) + (.then h/with-account) + (.then h/logout) + (.then #(h/wait-for [::logout/logout-method])))))) diff --git a/src/tests/integration_test/profile_test.cljs b/src/tests/integration_test/profile_test.cljs index 29c7bd0dbb5..2ae1ed0ebbc 100644 --- a/src/tests/integration_test/profile_test.cljs +++ b/src/tests/integration_test/profile_test.cljs @@ -1,7 +1,6 @@ (ns tests.integration-test.profile-test (:require [cljs.test :refer [deftest is]] - [day8.re-frame.test :as rf-test] [legacy.status-im.multiaccounts.logout.core :as logout] [legacy.status-im.utils.test :as utils.test] [status-im.contexts.profile.utils :as profile.utils] @@ -9,67 +8,63 @@ [utils.re-frame :as rf])) (deftest edit-profile-name-test - (h/log-headline :edit-profile-name-test) - (let [new-name "John Doe"] - (rf-test/run-test-async - (h/with-app-initialized - (h/with-account - (rf/dispatch [:profile/edit-name new-name]) - (rf-test/wait-for - [:navigate-back] - (rf-test/wait-for - [:toasts/upsert] - (let [profile (rf/sub [:profile/profile]) - display-name (profile.utils/displayed-name profile)] - (is (= new-name display-name))) - (h/logout) - (rf-test/wait-for [::logout/logout-method])))))))) + (h/rf-test-async + (fn [] + (h/log-headline ::edit-profile-name-test) + (let [new-name "John Doe"] + (-> (h/with-app-initialized) + (.then h/with-account) + (.then #(rf/dispatch [:profile/edit-name new-name])) + (.then #(h/wait-for [:navigate-back :toasts/upsert])) + (.then (fn [] + (let [profile (rf/sub [:profile/profile]) + display-name (profile.utils/displayed-name profile)] + (is (= new-name display-name))) + (h/logout))) + (.then #(h/wait-for [::logout/logout-method]))))))) (deftest edit-profile-picture-test - (h/log-headline :edit-profile-picture-test) - (let [mock-image "resources/images/mock2/monkey.png" - absolute-path (.resolve utils.test/path mock-image)] - (rf-test/run-test-async - (h/with-app-initialized - (h/with-account - (rf/dispatch [:profile/edit-picture absolute-path 80 80]) - (rf-test/wait-for - [:profile/update-local-picture] - (rf-test/wait-for - [:toasts/upsert] - (let [profile (rf/sub [:profile/profile])] - (is (not (nil? (:images profile))))) - (h/logout) - (rf-test/wait-for [::logout/logout-method])))))))) + (h/rf-test-async + (fn [] + (h/log-headline ::edit-profile-picture-test) + (let [mock-image "resources/images/mock2/monkey.png" + absolute-path (.resolve utils.test/path mock-image)] + (-> (h/with-app-initialized) + (.then h/with-account) + (.then #(rf/dispatch [:profile/edit-picture absolute-path 80 80])) + (.then #(h/wait-for [:profile/update-local-picture :toasts/upsert])) + (.then (fn [] + (let [profile (rf/sub [:profile/profile])] + (is (not (nil? (:images profile))))) + (h/logout))) + (.then #(h/wait-for [::logout/logout-method]))))))) (deftest delete-profile-picture-test - (h/log-headline :delete-profile-picture-test) - (rf-test/run-test-async - (h/with-app-initialized - (h/with-account - (rf/dispatch [:profile/delete-picture]) - (rf-test/wait-for - [:profile/update-local-picture] - (rf-test/wait-for - [:toasts/upsert] - (let [profile (rf/sub [:profile/profile])] - (is (nil? (:image profile)))) - (h/logout) - (rf-test/wait-for [::logout/logout-method]))))))) + (h/rf-test-async + (fn [] + (h/log-headline ::delete-profile-picture-test) + (-> (h/with-app-initialized) + (.then h/with-account) + (.then #(rf/dispatch [:profile/delete-picture])) + (.then #(h/wait-for [:profile/update-local-picture :toasts/upsert])) + (.then (fn [] + (let [profile (rf/sub [:profile/profile])] + (is (nil? (:image profile)))) + (h/logout))) + (.then #(h/wait-for [::logout/logout-method])))))) (deftest edit-profile-bio-test - (h/log-headline :edit-profile-bio-test) - (let [new-bio "New bio text"] - (rf-test/run-test-async - (h/with-app-initialized - (h/with-account - (rf/dispatch [:profile/edit-bio new-bio]) - (rf-test/wait-for - [:navigate-back] - (rf-test/wait-for - [:toasts/upsert] - (let [profile (rf/sub [:profile/profile]) - bio (:bio profile)] - (is (= new-bio bio))) - (h/logout) - (rf-test/wait-for [::logout/logout-method])))))))) + (h/rf-test-async + (fn [] + (h/log-headline ::edit-profile-bio-test) + (let [new-bio "New bio text"] + (-> (h/with-app-initialized) + (.then h/with-account) + (.then #(rf/dispatch [:profile/edit-bio new-bio])) + (.then #(h/wait-for [:navigate-back :toasts/upsert])) + (.then (fn [] + (let [profile (rf/sub [:profile/profile]) + bio (:bio profile)] + (is (= new-bio bio))) + (h/logout))) + (.then #(h/wait-for [::logout/logout-method]))))))) From 43835b3506b2aa00d5d3f2830b886e0e890d26ce Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Mon, 26 Feb 2024 15:54:42 -0300 Subject: [PATCH 02/14] Rewrite tests to use promesa --- .clj-kondo/config.edn | 1 + .zprintrc | 58 ++++++----- src/test_helpers/integration.clj | 24 +++++ src/test_helpers/integration.cljs | 40 +++++-- src/tests/integration_test/chat_test.cljs | 103 ++++++++----------- src/tests/integration_test/core_test.cljs | 19 ++-- src/tests/integration_test/profile_test.cljs | 66 +++++------- 7 files changed, 165 insertions(+), 146 deletions(-) create mode 100644 src/test_helpers/integration.clj diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index a0c50c61b2a..cc5945d3aa7 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -26,6 +26,7 @@ malli.generator malli.generator malli.transform malli.transform malli.util malli.util + promesa.core p schema.core schema status-im.feature-flags ff taoensso.timbre log}} diff --git a/.zprintrc b/.zprintrc index 142d3d00156..b7bd4cfdcc4 100644 --- a/.zprintrc +++ b/.zprintrc @@ -22,34 +22,36 @@ :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 - "defview" :arg1-body - "letsubs" :binding - "with-let" "let" - "reg-event-fx" :arg1-pair - "reg-fx" :arg1-pair - "testing" :arg1-body - "deftest-sub" :arg1-body - "wait-for" :arg1-body - "with-deps-check" :arg1-body - "schema/=>" :arg1-body - "->" [:noarg1-body - {:list {:constant-pair? false :force-nl? false} - :next-inner-restore [[:list :constant-pair?]]}] - "set!" "reset!" - "assoc-when" "assoc" - "assoc-some" "assoc" - "conj-when" "conj" - "conj-some" "conj"} + {"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 + "test/async" :arg1-body + "test/use-fixtures" :arg1-body + "global.describe" :arg1-body + "global.test" :arg1-body + "list-comp" :binding + "defview" :arg1-body + "letsubs" :binding + "with-let" "let" + "reg-event-fx" :arg1-pair + "reg-fx" :arg1-pair + "testing" :arg1-body + "deftest-sub" :arg1-body + "wait-for" :arg1-body + "with-deps-check" :arg1-body + "schema/=>" :arg1-body + "->" [:noarg1-body + {:list {:constant-pair? false :force-nl? false} + :next-inner-restore [[:list :constant-pair?]]}] + "set!" "reset!" + "assoc-when" "assoc" + "assoc-some" "assoc" + "conj-when" "conj" + "conj-some" "conj"} :style-map {:no-comma {:map {:comma? false}} diff --git a/src/test_helpers/integration.clj b/src/test_helpers/integration.clj new file mode 100644 index 00000000000..0018e6dae5f --- /dev/null +++ b/src/test_helpers/integration.clj @@ -0,0 +1,24 @@ +(ns test-helpers.integration + (:require [day8.re-frame.test :as rf-test] + [re-frame.core :as rf])) + +(defmacro with-app-initialized + [& body] + `(do + (legacy.status-im.utils.test/init!) + (if (test-helpers.integration/app-initialized) + (do ~@body) + (do + (rf/dispatch [:app-started]) + (rf-test/wait-for [:profile/get-profiles-overview-success] + ~@body))))) + +(defmacro with-account + [& body] + `(if (test-helpers.integration/messenger-started) + (do ~@body) + (do + (test-helpers.integration/create-multiaccount!) + (rf-test/wait-for [:messenger-started] + (test-helpers.integration/assert-messenger-started) + ~@body)))) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 9dae561da10..116b37ea696 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -1,10 +1,13 @@ (ns test-helpers.integration + (:require-macros [test-helpers.integration]) (:require [cljs.test :refer [is] :as test] legacy.status-im.events + [legacy.status-im.multiaccounts.logout.core :as logout] legacy.status-im.subs.root [legacy.status-im.utils.test :as legacy-test] [native-module.core :as native-module] + [promesa.core :as p] [re-frame.core :as rf] status-im.events status-im.navigation.core @@ -90,16 +93,16 @@ (defn rf-test-async [f] (test/async - done - (let [restore-fn (rf/make-restore-fn)] - (-> (f done) - (.catch (fn [error] - (is (true? false) (str "async test failed" error)))) - (.finally (fn [] - (restore-fn) - (done))))))) - -(defn with-app-initialized + done + (let [restore-fn (rf/make-restore-fn)] + (-> (f done) + (.catch (fn [error] + (is (true? false) (str "async test failed" error)))) + (.finally (fn [] + (restore-fn) + (done))))))) + +(defn setup-app [] (legacy-test/init!) (if (app-initialized) @@ -108,7 +111,7 @@ (rf/dispatch [:app-started]) (wait-for [:profile/get-profiles-overview-success])))) -(defn with-account +(defn setup-account [] (if (messenger-started) (js/Promise.resolve) @@ -116,3 +119,18 @@ (create-multiaccount!) (-> (wait-for [:messenger-started]) (.then #(assert-messenger-started)))))) + +;;;; Fixtures + +(defn fixture-logged + [] + {:before (fn [] + (test/async done + (p/do (setup-app) + (setup-account) + (done)))) + :after (fn [] + (test/async done + (p/do (logout) + (wait-for [::logout/logout-method]) + (done))))}) diff --git a/src/tests/integration_test/chat_test.cljs b/src/tests/integration_test/chat_test.cljs index ac66fab10b7..5d0c4cf262b 100644 --- a/src/tests/integration_test/chat_test.cljs +++ b/src/tests/integration_test/chat_test.cljs @@ -1,9 +1,9 @@ (ns tests.integration-test.chat-test (:require - [cljs.test :refer [deftest is]] + [cljs.test :refer [deftest is use-fixtures]] legacy.status-im.events - [legacy.status-im.multiaccounts.logout.core :as logout] legacy.status-im.subs.root + [promesa.core :as p] [re-frame.core :as rf] [status-im.constants :as constants] status-im.events @@ -11,6 +11,8 @@ status-im.subs.root [test-helpers.integration :as h])) +(use-fixtures :each (h/fixture-logged)) + (def chat-id "0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd") @@ -18,55 +20,44 @@ (h/rf-test-async (fn [] (h/log-headline ::one-to-one-chat-test) - (-> (h/with-app-initialized) - (.then h/with-account) - (.then #(rf/dispatch-sync [:chat.ui/start-chat chat-id])) - (.then #(h/wait-for [:chat/one-to-one-chat-created])) - (.then (fn [] - (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) - (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) - (h/logout))) - (.then #(h/wait-for [::logout/logout-method])))))) + (p/do + (rf/dispatch-sync [:chat.ui/start-chat chat-id]) + (h/wait-for [:chat/one-to-one-chat-created]) + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) + (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))))))) (deftest delete-chat-test (h/rf-test-async (fn [] (h/log-headline ::delete-chat-test) - (-> (h/with-app-initialized) - (.then h/with-account) - (.then #(rf/dispatch-sync [:chat.ui/start-chat chat-id])) - (.then #(h/wait-for [:chat/one-to-one-chat-created])) - (.then (fn [] - (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) - (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) - (is @(rf/subscribe [:chats/chat chat-id])) - (rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id]) - (rf/dispatch-sync [:chat.ui/remove-chat chat-id]) - (h/logout))) - (.then #(h/wait-for [::logout/logout-method])))))) + (p/do + (rf/dispatch-sync [:chat.ui/start-chat chat-id]) + (h/wait-for [:chat/one-to-one-chat-created]) + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) + (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) + (is @(rf/subscribe [:chats/chat chat-id])) + (rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id]) + (rf/dispatch-sync [:chat.ui/remove-chat chat-id]))))) (deftest mute-chat-test (h/rf-test-async (fn [] (h/log-headline ::mute-chat-test) - (-> (h/with-app-initialized) - (.then h/with-account) - (.then #(rf/dispatch-sync [:chat.ui/start-chat chat-id])) - (.then #(h/wait-for [:chat/one-to-one-chat-created])) - (.then (fn [] - (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) - (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) - (is @(rf/subscribe [:chats/chat chat-id])) - (rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted]))) - (.then #(h/wait-for [:chat/mute-successfully])) - (.then (fn [] - (is @(rf/subscribe [:chats/muted chat-id])) - (rf/dispatch-sync [:chat.ui/mute chat-id false]))) - (.then #(h/wait-for [:chat/mute-successfully])) - (.then (fn [] - (is (not @(rf/subscribe [:chats/muted chat-id]))) - (h/logout))) - (.then #(h/wait-for [::logout/logout-method])))))) + (p/do + (rf/dispatch-sync [:chat.ui/start-chat chat-id]) + (h/wait-for [:chat/one-to-one-chat-created]) + + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) + (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) + (is @(rf/subscribe [:chats/chat chat-id])) + + (rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted]) + (h/wait-for [:chat/mute-successfully]) + (is @(rf/subscribe [:chats/muted chat-id])) + + (rf/dispatch-sync [:chat.ui/mute chat-id false]) + (h/wait-for [:chat/mute-successfully]) + (is (not @(rf/subscribe [:chats/muted chat-id]))))))) (deftest add-contact-test (h/rf-test-async @@ -77,20 +68,16 @@ "ed2a86050325bc8856e26898c17e31dee2602b9429c91" "ecf65a41d62ac1f2f0823c0710dcb536e79af2763c") primary-name "zQ3...pFNErL"] - (-> (h/with-app-initialized) - (.then h/with-account) - ;; Search for contact using compressed key - (.then #(rf/dispatch [:contacts/set-new-identity {:input compressed-key}])) - (.then #(h/wait-for [:contacts/set-new-identity-success])) - (.then (fn [] - (let [new-identity @(rf/subscribe [:contacts/new-identity])] - (is (= public-key (:public-key new-identity))) - (is (= :valid (:state new-identity)))) - ;; click 'view profile' button - (rf/dispatch [:chat.ui/show-profile public-key]))) - (.then #(h/wait-for [:contacts/build-contact :contacts/build-contact-success])) - (.then (fn [] - (let [contact @(rf/subscribe [:contacts/current-contact])] - (is (= primary-name (:primary-name contact)))) - (h/logout))) - (.then #(h/wait-for [::logout/logout-method]))))))) + (p/do + ;; Search for contact using compressed key + (rf/dispatch [:contacts/set-new-identity {:input compressed-key}]) + (h/wait-for [:contacts/set-new-identity-success]) + (let [new-identity @(rf/subscribe [:contacts/new-identity])] + (is (= public-key (:public-key new-identity))) + (is (= :valid (:state new-identity)))) + + ;; Click 'view profile' button + (rf/dispatch [:chat.ui/show-profile public-key]) + (h/wait-for [:contacts/build-contact :contacts/build-contact-success]) + (let [contact @(rf/subscribe [:contacts/current-contact])] + (is (= primary-name (:primary-name contact))))))))) diff --git a/src/tests/integration_test/core_test.cljs b/src/tests/integration_test/core_test.cljs index a6a5efa0f25..b5d1e3e3b44 100644 --- a/src/tests/integration_test/core_test.cljs +++ b/src/tests/integration_test/core_test.cljs @@ -5,8 +5,8 @@ [legacy.status-im.multiaccounts.logout.core :as logout] legacy.status-im.subs.root [legacy.status-im.utils.test :as utils.test] + [promesa.core :as p] [re-frame.core :as rf] - [status-im.common.log :as log] status-im.events status-im.navigation.core status-im.subs.root @@ -16,20 +16,21 @@ (h/rf-test-async (fn [] (h/log-headline ::initialize-app-test) - ;; (log/setup "ERROR") (utils.test/init!) (rf/dispatch [:app-started]) ;; Use initialize-view because it has the longest avg. time and is ;; dispatched by initialize-multiaccounts (last non-view event). - (-> (h/wait-for [:profile/get-profiles-overview-success - :font/init-font-file-for-initials-avatar]) - (.then #(h/assert-app-initialized)))))) + (p/do + (h/wait-for [:profile/get-profiles-overview-success + :font/init-font-file-for-initials-avatar]) + (h/assert-app-initialized))))) (deftest create-account-test (h/rf-test-async (fn [] (h/log-headline ::create-account-test) - (-> (h/with-app-initialized) - (.then h/with-account) - (.then h/logout) - (.then #(h/wait-for [::logout/logout-method])))))) + (p/do + (h/setup-app) + (h/setup-account) + (h/logout) + (h/wait-for [::logout/logout-method]))))) diff --git a/src/tests/integration_test/profile_test.cljs b/src/tests/integration_test/profile_test.cljs index 2ae1ed0ebbc..6c665333e7d 100644 --- a/src/tests/integration_test/profile_test.cljs +++ b/src/tests/integration_test/profile_test.cljs @@ -1,27 +1,25 @@ (ns tests.integration-test.profile-test (:require - [cljs.test :refer [deftest is]] - [legacy.status-im.multiaccounts.logout.core :as logout] + [cljs.test :refer [deftest is use-fixtures]] [legacy.status-im.utils.test :as utils.test] + [promesa.core :as p] [status-im.contexts.profile.utils :as profile.utils] [test-helpers.integration :as h] [utils.re-frame :as rf])) +(use-fixtures :each (h/fixture-logged)) + (deftest edit-profile-name-test (h/rf-test-async (fn [] (h/log-headline ::edit-profile-name-test) (let [new-name "John Doe"] - (-> (h/with-app-initialized) - (.then h/with-account) - (.then #(rf/dispatch [:profile/edit-name new-name])) - (.then #(h/wait-for [:navigate-back :toasts/upsert])) - (.then (fn [] - (let [profile (rf/sub [:profile/profile]) - display-name (profile.utils/displayed-name profile)] - (is (= new-name display-name))) - (h/logout))) - (.then #(h/wait-for [::logout/logout-method]))))))) + (p/do + (rf/dispatch [:profile/edit-name new-name]) + (h/wait-for [:navigate-back :toasts/upsert]) + (let [profile (rf/sub [:profile/profile]) + display-name (profile.utils/displayed-name profile)] + (is (= new-name display-name)))))))) (deftest edit-profile-picture-test (h/rf-test-async @@ -29,42 +27,30 @@ (h/log-headline ::edit-profile-picture-test) (let [mock-image "resources/images/mock2/monkey.png" absolute-path (.resolve utils.test/path mock-image)] - (-> (h/with-app-initialized) - (.then h/with-account) - (.then #(rf/dispatch [:profile/edit-picture absolute-path 80 80])) - (.then #(h/wait-for [:profile/update-local-picture :toasts/upsert])) - (.then (fn [] - (let [profile (rf/sub [:profile/profile])] - (is (not (nil? (:images profile))))) - (h/logout))) - (.then #(h/wait-for [::logout/logout-method]))))))) + (p/do + (rf/dispatch [:profile/edit-picture absolute-path 80 80]) + (h/wait-for [:profile/update-local-picture :toasts/upsert]) + (let [profile (rf/sub [:profile/profile])] + (is (not (nil? (:images profile)))))))))) (deftest delete-profile-picture-test (h/rf-test-async (fn [] (h/log-headline ::delete-profile-picture-test) - (-> (h/with-app-initialized) - (.then h/with-account) - (.then #(rf/dispatch [:profile/delete-picture])) - (.then #(h/wait-for [:profile/update-local-picture :toasts/upsert])) - (.then (fn [] - (let [profile (rf/sub [:profile/profile])] - (is (nil? (:image profile)))) - (h/logout))) - (.then #(h/wait-for [::logout/logout-method])))))) + (p/do + (rf/dispatch [:profile/delete-picture]) + (h/wait-for [:profile/update-local-picture :toasts/upsert]) + (let [profile (rf/sub [:profile/profile])] + (is (nil? (:image profile)))))))) (deftest edit-profile-bio-test (h/rf-test-async (fn [] (h/log-headline ::edit-profile-bio-test) (let [new-bio "New bio text"] - (-> (h/with-app-initialized) - (.then h/with-account) - (.then #(rf/dispatch [:profile/edit-bio new-bio])) - (.then #(h/wait-for [:navigate-back :toasts/upsert])) - (.then (fn [] - (let [profile (rf/sub [:profile/profile]) - bio (:bio profile)] - (is (= new-bio bio))) - (h/logout))) - (.then #(h/wait-for [::logout/logout-method]))))))) + (p/do + (rf/dispatch [:profile/edit-bio new-bio]) + (h/wait-for [:navigate-back :toasts/upsert]) + (let [profile (rf/sub [:profile/profile]) + bio (:bio profile)] + (is (= new-bio bio)))))))) From 63dcf6e329c31c8ee21a4ddc5574a8eb26727ae1 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Tue, 27 Feb 2024 10:03:43 -0300 Subject: [PATCH 03/14] Log full keyword instead of just its name --- src/test_helpers/integration.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 116b37ea696..147f057877f 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -62,7 +62,7 @@ (defn log-headline [test-name] - (log/info (str "========= " (name test-name) " =================="))) + (log/info (str "==== " test-name " ===="))) (defn wait-for ([target-event-ids] From b3564e8a8922f7c8f1b9b6df0d9ca7760629bea1 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Tue, 27 Feb 2024 11:41:46 -0300 Subject: [PATCH 04/14] Respect order of expected event IDs --- src/test_helpers/integration.cljs | 33 +++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 147f057877f..ae86994a173 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -13,7 +13,8 @@ status-im.navigation.core status-im.subs.root [taoensso.timbre :as log] - [tests.integration-test.constants :as constants])) + [tests.integration-test.constants :as constants] + [utils.collection :as collection])) (defn initialize-app! [] @@ -65,10 +66,15 @@ (log/info (str "==== " test-name " ===="))) (defn wait-for + "Returns a promise that resolves when all `target-event-ids` are processed by re-frame, + otherwise rejects after `timeout-ms`. + + If an event ID that is expected in `target-event-ids` happens in a different + order, the promise will be rejected." ([target-event-ids] (wait-for target-event-ids 10000)) ([target-event-ids timeout-ms] - (let [waiting-for (atom (set target-event-ids))] + (let [waiting-for (atom target-event-ids)] (js/Promise. (fn [promise-resolve promise-reject] (let [cb-id (gensym "post-event-callback") @@ -83,12 +89,23 @@ (rf/add-post-event-callback cb-id (fn [[event-id & _]] - (when (contains? @waiting-for event-id) - (swap! waiting-for disj event-id) - (when (empty? @waiting-for) - (js/clearTimeout timer-id) - (rf/remove-post-event-callback cb-id) - (promise-resolve))))))))))) + (when-let [idx (collection/first-index #(= % event-id) @waiting-for)] + ;; All `target-event-ids` should be processed in their original order. + (if (zero? idx) + (do + (swap! waiting-for rest) + ;; When there's nothing else to wait for, clean up resources. + (when (empty? @waiting-for) + (js/clearTimeout timer-id) + (rf/remove-post-event-callback cb-id) + (promise-resolve))) + (do + (js/clearTimeout timer-id) + (rf/remove-post-event-callback cb-id) + (promise-reject (ex-info "event happened in unexpected order" + {:event-ids target-event-ids + :waiting-for @waiting-for} + ::out-of-order-event-id))))))))))))) (defn rf-test-async [f] From d0c3a54d13b3e7aad46a152c39bdd8323430fa11 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Tue, 27 Feb 2024 11:45:08 -0300 Subject: [PATCH 05/14] Renaming and reject with a better error message --- src/test_helpers/integration.cljs | 37 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index ae86994a173..80b7976f6a7 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -66,36 +66,37 @@ (log/info (str "==== " test-name " ===="))) (defn wait-for - "Returns a promise that resolves when all `target-event-ids` are processed by re-frame, + "Returns a promise that resolves when all `event-ids` are processed by re-frame, otherwise rejects after `timeout-ms`. - If an event ID that is expected in `target-event-ids` happens in a different - order, the promise will be rejected." - ([target-event-ids] - (wait-for target-event-ids 10000)) - ([target-event-ids timeout-ms] - (let [waiting-for (atom target-event-ids)] + If an event ID that is expected in `event-ids` happens in a different order, + the promise will be rejected." + ([event-ids] + (wait-for event-ids 10000)) + ([event-ids timeout-ms] + (let [waiting-ids (atom event-ids)] (js/Promise. (fn [promise-resolve promise-reject] (let [cb-id (gensym "post-event-callback") timer-id (js/setTimeout (fn [] (rf/remove-post-event-callback cb-id) - (promise-reject (ex-info "some events did not run" - {:event-ids target-event-ids - :timeout-ms timeout-ms - :waiting-for @waiting-for} - ::timeout))) + (promise-reject (ex-info + "timed out waiting for all event-ids to run" + {:event-ids event-ids + :waiting-ids @waiting-ids + :timeout-ms timeout-ms} + ::timeout))) timeout-ms)] (rf/add-post-event-callback cb-id (fn [[event-id & _]] - (when-let [idx (collection/first-index #(= % event-id) @waiting-for)] - ;; All `target-event-ids` should be processed in their original order. + (when-let [idx (collection/first-index #(= % event-id) @waiting-ids)] + ;; All `event-ids` should be processed in their original order. (if (zero? idx) (do - (swap! waiting-for rest) + (swap! waiting-ids rest) ;; When there's nothing else to wait for, clean up resources. - (when (empty? @waiting-for) + (when (empty? @waiting-ids) (js/clearTimeout timer-id) (rf/remove-post-event-callback cb-id) (promise-resolve))) @@ -103,8 +104,8 @@ (js/clearTimeout timer-id) (rf/remove-post-event-callback cb-id) (promise-reject (ex-info "event happened in unexpected order" - {:event-ids target-event-ids - :waiting-for @waiting-for} + {:event-ids event-ids + :waiting-for @waiting-ids} ::out-of-order-event-id))))))))))))) (defn rf-test-async From 6bd75cf76b40ce39ba40566eaaff78f4bd7d0198 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Tue, 27 Feb 2024 13:45:04 -0300 Subject: [PATCH 06/14] Fail fast when int tests fail and support silencing re-frame debug messages --- src/test_helpers/integration.cljs | 25 ++++- src/tests/integration_test/chat_test.cljs | 104 +++++++++---------- src/tests/integration_test/core_test.cljs | 36 +++---- src/tests/integration_test/profile_test.cljs | 72 ++++++------- src/utils/re_frame.cljs | 5 +- 5 files changed, 128 insertions(+), 114 deletions(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 80b7976f6a7..7b7d205b114 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -9,6 +9,7 @@ [native-module.core :as native-module] [promesa.core :as p] [re-frame.core :as rf] + [re-frame.interop :as rf.interop] status-im.events status-im.navigation.core status-im.subs.root @@ -124,7 +125,7 @@ [] (legacy-test/init!) (if (app-initialized) - (js/Promise.resolve) + (p/resolved ::app-initialized) (do (rf/dispatch [:app-started]) (wait-for [:profile/get-profiles-overview-success])))) @@ -132,12 +133,22 @@ (defn setup-account [] (if (messenger-started) - (js/Promise.resolve) + (p/resolved ::messenger-started) (do (create-multiaccount!) (-> (wait-for [:messenger-started]) (.then #(assert-messenger-started)))))) +(defn integration-test + [test-name f] + (rf-test-async + (fn [] + (log-headline test-name) + (-> (p/do (f)) + (p/catch (fn [error] + (is (nil? error)) + (js/process.exit 1))))))) + ;;;; Fixtures (defn fixture-logged @@ -152,3 +163,13 @@ (p/do (logout) (wait-for [::logout/logout-method]) (done))))}) + +(defn fixture-silent-reframe + "Disables most re-frame warnings." + [] + {:before (fn [] + ;; Set to false to stop warning about subscriptions being used in non-reactive + ;; contexts. + (set! rf.interop/debug-enabled? false)) + :after (fn [] + (set! rf.interop/debug-enabled? true))}) diff --git a/src/tests/integration_test/chat_test.cljs b/src/tests/integration_test/chat_test.cljs index 5d0c4cf262b..893c58b3b60 100644 --- a/src/tests/integration_test/chat_test.cljs +++ b/src/tests/integration_test/chat_test.cljs @@ -17,67 +17,63 @@ "0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd") (deftest one-to-one-chat-test - (h/rf-test-async - (fn [] - (h/log-headline ::one-to-one-chat-test) - (p/do - (rf/dispatch-sync [:chat.ui/start-chat chat-id]) - (h/wait-for [:chat/one-to-one-chat-created]) - (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) - (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))))))) + (h/integration-test ::one-to-one-chat + (fn [] + (p/do + (rf/dispatch-sync [:chat.ui/start-chat chat-id]) + (h/wait-for [:chat/one-to-one-chat-created]) + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) + (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))))))) (deftest delete-chat-test - (h/rf-test-async - (fn [] - (h/log-headline ::delete-chat-test) - (p/do - (rf/dispatch-sync [:chat.ui/start-chat chat-id]) - (h/wait-for [:chat/one-to-one-chat-created]) - (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) - (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) - (is @(rf/subscribe [:chats/chat chat-id])) - (rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id]) - (rf/dispatch-sync [:chat.ui/remove-chat chat-id]))))) + (h/integration-test ::delete-chat + (fn [] + (p/do + (rf/dispatch-sync [:chat.ui/start-chat chat-id]) + (h/wait-for [:chat/one-to-one-chat-created]) + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) + (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) + (is @(rf/subscribe [:chats/chat chat-id])) + (rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id]) + (rf/dispatch-sync [:chat.ui/remove-chat chat-id]))))) (deftest mute-chat-test - (h/rf-test-async - (fn [] - (h/log-headline ::mute-chat-test) - (p/do - (rf/dispatch-sync [:chat.ui/start-chat chat-id]) - (h/wait-for [:chat/one-to-one-chat-created]) + (h/integration-test ::mute-chat + (fn [] + (p/do + (rf/dispatch-sync [:chat.ui/start-chat chat-id]) + (h/wait-for [:chat/one-to-one-chat-created]) - (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) - (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) - (is @(rf/subscribe [:chats/chat chat-id])) + (rf/dispatch-sync [:chat/navigate-to-chat chat-id]) + (is (= chat-id @(rf/subscribe [:chats/current-chat-id]))) + (is @(rf/subscribe [:chats/chat chat-id])) - (rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted]) - (h/wait-for [:chat/mute-successfully]) - (is @(rf/subscribe [:chats/muted chat-id])) + (rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted]) + (h/wait-for [:chat/mute-successfully]) + (is @(rf/subscribe [:chats/muted chat-id])) - (rf/dispatch-sync [:chat.ui/mute chat-id false]) - (h/wait-for [:chat/mute-successfully]) - (is (not @(rf/subscribe [:chats/muted chat-id]))))))) + (rf/dispatch-sync [:chat.ui/mute chat-id false]) + (h/wait-for [:chat/mute-successfully]) + (is (not @(rf/subscribe [:chats/muted chat-id]))))))) (deftest add-contact-test - (h/rf-test-async - (fn [] - (h/log-headline :add-contact-test) - (let [compressed-key "zQ3shMwgSMKHVznoowceZMxWde9HUnkQEVSGvvex8UFpFNErL" - public-key (str "0x0407e9dc435fe366cb0b4c4f35cbd925438c0f46fe0" - "ed2a86050325bc8856e26898c17e31dee2602b9429c91" - "ecf65a41d62ac1f2f0823c0710dcb536e79af2763c") - primary-name "zQ3...pFNErL"] - (p/do - ;; Search for contact using compressed key - (rf/dispatch [:contacts/set-new-identity {:input compressed-key}]) - (h/wait-for [:contacts/set-new-identity-success]) - (let [new-identity @(rf/subscribe [:contacts/new-identity])] - (is (= public-key (:public-key new-identity))) - (is (= :valid (:state new-identity)))) + (h/integration-test ::add-contact + (fn [] + (let [compressed-key "zQ3shMwgSMKHVznoowceZMxWde9HUnkQEVSGvvex8UFpFNErL" + public-key (str "0x0407e9dc435fe366cb0b4c4f35cbd925438c0f46fe0" + "ed2a86050325bc8856e26898c17e31dee2602b9429c91" + "ecf65a41d62ac1f2f0823c0710dcb536e79af2763c") + primary-name "zQ3...pFNErL"] + (p/do + ;; Search for contact using compressed key + (rf/dispatch [:contacts/set-new-identity {:input compressed-key}]) + (h/wait-for [:contacts/set-new-identity-success]) + (let [new-identity @(rf/subscribe [:contacts/new-identity])] + (is (= public-key (:public-key new-identity))) + (is (= :valid (:state new-identity)))) - ;; Click 'view profile' button - (rf/dispatch [:chat.ui/show-profile public-key]) - (h/wait-for [:contacts/build-contact :contacts/build-contact-success]) - (let [contact @(rf/subscribe [:contacts/current-contact])] - (is (= primary-name (:primary-name contact))))))))) + ;; Click 'view profile' button + (rf/dispatch [:chat.ui/show-profile public-key]) + (h/wait-for [:contacts/build-contact :contacts/build-contact-success]) + (let [contact @(rf/subscribe [:contacts/current-contact])] + (is (= primary-name (:primary-name contact))))))))) diff --git a/src/tests/integration_test/core_test.cljs b/src/tests/integration_test/core_test.cljs index b5d1e3e3b44..0350ef28cfe 100644 --- a/src/tests/integration_test/core_test.cljs +++ b/src/tests/integration_test/core_test.cljs @@ -13,24 +13,22 @@ [test-helpers.integration :as h])) (deftest initialize-app-test - (h/rf-test-async - (fn [] - (h/log-headline ::initialize-app-test) - (utils.test/init!) - (rf/dispatch [:app-started]) - ;; Use initialize-view because it has the longest avg. time and is - ;; dispatched by initialize-multiaccounts (last non-view event). - (p/do - (h/wait-for [:profile/get-profiles-overview-success - :font/init-font-file-for-initials-avatar]) - (h/assert-app-initialized))))) + (h/integration-test ::initialize-app + (fn [] + (p/do + (utils.test/init!) + (rf/dispatch [:app-started]) + ;; Use initialize-view because it has the longest avg. time and is + ;; dispatched by initialize-multiaccounts (last non-view event). + (h/wait-for [:profile/get-profiles-overview-success + :font/init-font-file-for-initials-avatar]) + (h/assert-app-initialized))))) (deftest create-account-test - (h/rf-test-async - (fn [] - (h/log-headline ::create-account-test) - (p/do - (h/setup-app) - (h/setup-account) - (h/logout) - (h/wait-for [::logout/logout-method]))))) + (h/integration-test ::create-account + (fn [] + (p/do + (h/setup-app) + (h/setup-account) + (h/logout) + (h/wait-for [::logout/logout-method]))))) diff --git a/src/tests/integration_test/profile_test.cljs b/src/tests/integration_test/profile_test.cljs index 6c665333e7d..dbd3f07bc60 100644 --- a/src/tests/integration_test/profile_test.cljs +++ b/src/tests/integration_test/profile_test.cljs @@ -10,47 +10,43 @@ (use-fixtures :each (h/fixture-logged)) (deftest edit-profile-name-test - (h/rf-test-async - (fn [] - (h/log-headline ::edit-profile-name-test) - (let [new-name "John Doe"] - (p/do - (rf/dispatch [:profile/edit-name new-name]) - (h/wait-for [:navigate-back :toasts/upsert]) - (let [profile (rf/sub [:profile/profile]) - display-name (profile.utils/displayed-name profile)] - (is (= new-name display-name)))))))) + (h/integration-test ::edit-profile-name + (fn [] + (let [new-name "John Doe"] + (p/do + (rf/dispatch [:profile/edit-name new-name]) + (h/wait-for [:navigate-back :toasts/upsert]) + (let [profile (rf/sub [:profile/profile]) + display-name (profile.utils/displayed-name profile)] + (is (= new-name display-name)))))))) (deftest edit-profile-picture-test - (h/rf-test-async - (fn [] - (h/log-headline ::edit-profile-picture-test) - (let [mock-image "resources/images/mock2/monkey.png" - absolute-path (.resolve utils.test/path mock-image)] - (p/do - (rf/dispatch [:profile/edit-picture absolute-path 80 80]) - (h/wait-for [:profile/update-local-picture :toasts/upsert]) - (let [profile (rf/sub [:profile/profile])] - (is (not (nil? (:images profile)))))))))) + (h/integration-test ::edit-profile-picture + (fn [] + (let [mock-image "resources/images/mock2/monkey.png" + absolute-path (.resolve utils.test/path mock-image)] + (p/do + (rf/dispatch [:profile/edit-picture absolute-path 80 80]) + (h/wait-for [:profile/update-local-picture :toasts/upsert]) + (let [profile (rf/sub [:profile/profile])] + (is (not (nil? (:images profile)))))))))) (deftest delete-profile-picture-test - (h/rf-test-async - (fn [] - (h/log-headline ::delete-profile-picture-test) - (p/do - (rf/dispatch [:profile/delete-picture]) - (h/wait-for [:profile/update-local-picture :toasts/upsert]) - (let [profile (rf/sub [:profile/profile])] - (is (nil? (:image profile)))))))) + (h/integration-test ::delete-profile-picture + (fn [] + (p/do + (rf/dispatch [:profile/delete-picture]) + (h/wait-for [:profile/update-local-picture :toasts/upsert]) + (let [profile (rf/sub [:profile/profile])] + (is (nil? (:image profile)))))))) (deftest edit-profile-bio-test - (h/rf-test-async - (fn [] - (h/log-headline ::edit-profile-bio-test) - (let [new-bio "New bio text"] - (p/do - (rf/dispatch [:profile/edit-bio new-bio]) - (h/wait-for [:navigate-back :toasts/upsert]) - (let [profile (rf/sub [:profile/profile]) - bio (:bio profile)] - (is (= new-bio bio)))))))) + (h/integration-test ::edit-profile-bio + (fn [] + (let [new-bio "New bio text"] + (p/do + (rf/dispatch [:profile/edit-bio new-bio]) + (h/wait-for [:navigate-back :toasts/upsert]) + (let [profile (rf/sub [:profile/profile]) + bio (:bio profile)] + (is (= new-bio bio)))))))) diff --git a/src/utils/re_frame.cljs b/src/utils/re_frame.cljs index c35b5b08ead..babf23c9151 100644 --- a/src/utils/re_frame.cljs +++ b/src/utils/re_frame.cljs @@ -3,6 +3,7 @@ (:require [re-frame.core :as re-frame] [re-frame.interceptor :as interceptor] + [re-frame.interop :as rf.interop] [reagent.core :as reagent] [taoensso.timbre :as log] [utils.datetime :as datetime]) @@ -20,7 +21,9 @@ [context] (when js/goog.DEBUG (reset! handler-nesting-level 0)) - (log/debug "Handling re-frame event: " (first (interceptor/get-coeffect context :event))) + (when rf.interop/debug-enabled? + (log/debug "Handling re-frame event: " + (first (interceptor/get-coeffect context :event)))) context))) (defn- update-db From fcf84d73e70d786788b9b170a8fe74bd9b6ef961 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Tue, 27 Feb 2024 14:24:31 -0300 Subject: [PATCH 07/14] Make fail-fast behavior configurable and merge two functions --- src/test_helpers/integration.cljs | 46 +++++++++++++++++-------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 7b7d205b114..8c7560e0212 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -109,18 +109,6 @@ :waiting-for @waiting-ids} ::out-of-order-event-id))))))))))))) -(defn rf-test-async - [f] - (test/async - done - (let [restore-fn (rf/make-restore-fn)] - (-> (f done) - (.catch (fn [error] - (is (true? false) (str "async test failed" error)))) - (.finally (fn [] - (restore-fn) - (done))))))) - (defn setup-app [] (legacy-test/init!) @@ -140,14 +128,32 @@ (.then #(assert-messenger-started)))))) (defn integration-test - [test-name f] - (rf-test-async - (fn [] - (log-headline test-name) - (-> (p/do (f)) - (p/catch (fn [error] - (is (nil? error)) - (js/process.exit 1))))))) + "Runs `f` inside `cljs.test/async` macro in a restorable re-frame checkpoint. + + Option `fail-fast?`, when truthy (defaults to true), will force the test + runner to terminate on any test failure. Setting it to false can be useful + when you want the rest of the test suite to run due to a flaky test. + + When `fail-fast?` is falsey, re-frame's state is automatically restored after + a test failure, so that the next integration test can run from a pristine + state. + " + ([test-name f] + (integration-test test-name {:fail-fast? true} f)) + ([test-name {:keys [fail-fast?]} f] + (test/async + done + (let [restore-fn (rf/make-restore-fn)] + (-> (p/do + (log-headline test-name) + (f done)) + (p/catch (fn [error] + (is (nil? error)) + (when fail-fast? + (js/process.exit 1)))) + (p/finally (fn [] + (restore-fn) + (done)))))))) ;;;; Fixtures From 9cbbc7ffd68c243c093d57fe0af5446942e1a1e7 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Tue, 27 Feb 2024 14:25:25 -0300 Subject: [PATCH 08/14] Update zprint rule for h/integration-test --- .zprintrc | 61 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/.zprintrc b/.zprintrc index b7bd4cfdcc4..5a809dfcef7 100644 --- a/.zprintrc +++ b/.zprintrc @@ -22,36 +22,37 @@ :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 - "test/async" :arg1-body - "test/use-fixtures" :arg1-body - "global.describe" :arg1-body - "global.test" :arg1-body - "list-comp" :binding - "defview" :arg1-body - "letsubs" :binding - "with-let" "let" - "reg-event-fx" :arg1-pair - "reg-fx" :arg1-pair - "testing" :arg1-body - "deftest-sub" :arg1-body - "wait-for" :arg1-body - "with-deps-check" :arg1-body - "schema/=>" :arg1-body - "->" [:noarg1-body - {:list {:constant-pair? false :force-nl? false} - :next-inner-restore [[:list :constant-pair?]]}] - "set!" "reset!" - "assoc-when" "assoc" - "assoc-some" "assoc" - "conj-when" "conj" - "conj-some" "conj"} + {"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 + "test/async" :arg1-body + "test/use-fixtures" :arg1-body + "global.describe" :arg1-body + "global.test" :arg1-body + "list-comp" :binding + "defview" :arg1-body + "letsubs" :binding + "with-let" "let" + "reg-event-fx" :arg1-pair + "reg-fx" :arg1-pair + "testing" :arg1-body + "deftest-sub" :arg1-body + "h/integration-test" :arg1-body + "wait-for" :arg1-body + "with-deps-check" :arg1-body + "schema/=>" :arg1-body + "->" [:noarg1-body + {:list {:constant-pair? false :force-nl? false} + :next-inner-restore [[:list :constant-pair?]]}] + "set!" "reset!" + "assoc-when" "assoc" + "assoc-some" "assoc" + "conj-when" "conj" + "conj-some" "conj"} :style-map {:no-comma {:map {:comma? false}} From 097f4adc6942b59aa6939172f1ceeb6b91dbfd40 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Tue, 27 Feb 2024 14:45:06 -0300 Subject: [PATCH 09/14] Add docstrings to fixtures --- src/test_helpers/integration.cljs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 8c7560e0212..139ef27bed4 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -158,6 +158,12 @@ ;;;; Fixtures (defn fixture-logged + "Fixture to set up the app and a logged account before the test runs. Log out + after the test is done. + + Usage: + + (use-fixtures :each (h/fixture-logged))" [] {:before (fn [] (test/async done @@ -170,12 +176,20 @@ (wait-for [::logout/logout-method]) (done))))}) -(defn fixture-silent-reframe - "Disables most re-frame warnings." +(defn fixture-silence-reframe + "Fixture to disable most re-frame warnings. + + Example messages disabled: + + - Warning about subscriptions being used in non-reactive contexts. + - Debug message \"Handling re-frame event: XYZ\". + + Usage: + + (use-fixtures :once (h/fixture-silence-re-frame)) + " [] {:before (fn [] - ;; Set to false to stop warning about subscriptions being used in non-reactive - ;; contexts. (set! rf.interop/debug-enabled? false)) :after (fn [] (set! rf.interop/debug-enabled? true))}) From 389b40a8df39b7e6f4a8ddc172390afd68cf953d Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Tue, 27 Feb 2024 15:35:10 -0300 Subject: [PATCH 10/14] Allow integration tests to time out (default 60s) --- src/test_helpers/integration.cljs | 42 ++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 139ef27bed4..bf17c253023 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -17,6 +17,20 @@ [tests.integration-test.constants :as constants] [utils.collection :as collection])) +(def default-re-frame-wait-for-timeout-ms + "Controls the maximum time allowed to wait for all events to be processed by + re-frame on every call to `wait-for`. + + Take into consideration that some endpoints/signals may take significantly + more time to finish/arrive." + (* 10 1000)) + +(def default-integration-test-timeout-ms + "Use a high-enough value in milliseconds to timeout integration tests. Not too + small, which would cause sporadic failures, and not too high as to make you + sleepy." + (* 60 1000)) + (defn initialize-app! [] (rf/dispatch [:app-started])) @@ -70,10 +84,10 @@ "Returns a promise that resolves when all `event-ids` are processed by re-frame, otherwise rejects after `timeout-ms`. - If an event ID that is expected in `event-ids` happens in a different order, + If an event ID that is expected in `event-ids` occurs in a different order, the promise will be rejected." ([event-ids] - (wait-for event-ids 10000)) + (wait-for event-ids default-re-frame-wait-for-timeout-ms)) ([event-ids timeout-ms] (let [waiting-ids (atom event-ids)] (js/Promise. @@ -137,16 +151,23 @@ When `fail-fast?` is falsey, re-frame's state is automatically restored after a test failure, so that the next integration test can run from a pristine state. + + Option `timeout-ms` controls the total time allowed to run `f`. The value + should be high enough to account for some variability, otherwise the test may + fail more often. " ([test-name f] - (integration-test test-name {:fail-fast? true} f)) - ([test-name {:keys [fail-fast?]} f] + (integration-test test-name + {:fail-fast? true + :timeout-ms default-integration-test-timeout-ms} + f)) + ([test-name {:keys [fail-fast? timeout-ms]} f] (test/async done (let [restore-fn (rf/make-restore-fn)] - (-> (p/do - (log-headline test-name) - (f done)) + (log-headline test-name) + (-> (p/do (f done)) + (p/timeout timeout-ms) (p/catch (fn [error] (is (nil? error)) (when fail-fast? @@ -158,8 +179,8 @@ ;;;; Fixtures (defn fixture-logged - "Fixture to set up the app and a logged account before the test runs. Log out - after the test is done. + "Fixture to set up the application and a logged account before the test runs. + Log out after the test is done. Usage: @@ -179,6 +200,9 @@ (defn fixture-silence-reframe "Fixture to disable most re-frame warnings. + Avoid using this fixture for non-dev purposes because in the CI output it's + desirable to have more data to debug, not less. + Example messages disabled: - Warning about subscriptions being used in non-reactive contexts. From 8865e0da25e1e8df1b8415eaff9a90e7fa6e6b84 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Tue, 27 Feb 2024 15:45:55 -0300 Subject: [PATCH 11/14] Improve docstrings --- src/test_helpers/integration.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index bf17c253023..ab6bbc12b4b 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -146,7 +146,8 @@ Option `fail-fast?`, when truthy (defaults to true), will force the test runner to terminate on any test failure. Setting it to false can be useful - when you want the rest of the test suite to run due to a flaky test. + during development when you want the rest of the test suite to run due to a + flaky test. Prefer to fail fast in the CI to save on time & resources. When `fail-fast?` is falsey, re-frame's state is automatically restored after a test failure, so that the next integration test can run from a pristine @@ -198,7 +199,7 @@ (done))))}) (defn fixture-silence-reframe - "Fixture to disable most re-frame warnings. + "Fixture to disable most re-frame messages. Avoid using this fixture for non-dev purposes because in the CI output it's desirable to have more data to debug, not less. From d658fa414034b04cad19a484cf5bdecd1915d89f Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Fri, 1 Mar 2024 11:48:24 -0300 Subject: [PATCH 12/14] Rename fixture --- src/test_helpers/integration.cljs | 2 +- src/tests/integration_test/chat_test.cljs | 2 +- src/tests/integration_test/profile_test.cljs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index ab6bbc12b4b..4ac0b821368 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -179,7 +179,7 @@ ;;;; Fixtures -(defn fixture-logged +(defn fixture-session "Fixture to set up the application and a logged account before the test runs. Log out after the test is done. diff --git a/src/tests/integration_test/chat_test.cljs b/src/tests/integration_test/chat_test.cljs index 893c58b3b60..688f974859f 100644 --- a/src/tests/integration_test/chat_test.cljs +++ b/src/tests/integration_test/chat_test.cljs @@ -11,7 +11,7 @@ status-im.subs.root [test-helpers.integration :as h])) -(use-fixtures :each (h/fixture-logged)) +(use-fixtures :each (h/fixture-session)) (def chat-id "0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd") diff --git a/src/tests/integration_test/profile_test.cljs b/src/tests/integration_test/profile_test.cljs index dbd3f07bc60..f7cf38310fd 100644 --- a/src/tests/integration_test/profile_test.cljs +++ b/src/tests/integration_test/profile_test.cljs @@ -7,7 +7,7 @@ [test-helpers.integration :as h] [utils.re-frame :as rf])) -(use-fixtures :each (h/fixture-logged)) +(use-fixtures :each (h/fixture-session)) (deftest edit-profile-name-test (h/integration-test ::edit-profile-name From 33f4c358245b5b00cd3bf83eec5c4429e24c94f2 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Fri, 1 Mar 2024 11:55:18 -0300 Subject: [PATCH 13/14] Document the done function will be passed to f --- src/test_helpers/integration.cljs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 4ac0b821368..45b6477a263 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -144,6 +144,11 @@ (defn integration-test "Runs `f` inside `cljs.test/async` macro in a restorable re-frame checkpoint. + `f` will be called with one argument, the `done` function exposed by the + `cljs.test/async` macro. Normally, you don't need to use `done`, but you can + call it if you want to early-terminate the current test, so that the test + runner can execute the next one. + Option `fail-fast?`, when truthy (defaults to true), will force the test runner to terminate on any test failure. Setting it to false can be useful during development when you want the rest of the test suite to run due to a From f4781c9c54a519e0df8c75c2b84a629c2150b4d3 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Fri, 1 Mar 2024 11:56:49 -0300 Subject: [PATCH 14/14] Use promesa create instead of interop --- src/test_helpers/integration.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 45b6477a263..ff76b628fd0 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -90,7 +90,7 @@ (wait-for event-ids default-re-frame-wait-for-timeout-ms)) ([event-ids timeout-ms] (let [waiting-ids (atom event-ids)] - (js/Promise. + (p/create (fn [promise-resolve promise-reject] (let [cb-id (gensym "post-event-callback") timer-id (js/setTimeout (fn []