From abafa3308a5d466afc60e50fc3a97df962b656fc Mon Sep 17 00:00:00 2001 From: alwx Date: Tue, 25 Jul 2017 23:29:23 +0200 Subject: [PATCH] Unit tests for input model --- src/status_im/chat/models/input.cljs | 89 +++++----- .../status_im/test/chat/models/input.cljs | 152 +++++++++++++++++- 2 files changed, 192 insertions(+), 49 deletions(-) diff --git a/src/status_im/chat/models/input.cljs b/src/status_im/chat/models/input.cljs index 198c2cc17d..5fb1622520 100644 --- a/src/status_im/chat/models/input.cljs +++ b/src/status_im/chat/models/input.cljs @@ -22,12 +22,9 @@ (aget emoji-map "char") original))))) -(defn text-ends-with-space? - "Returns true if the last symbol of `text` is space" - [text] - (when text - (= (str/last-index-of text const/spacing-char) - (dec (count text))))) +(defn text-ends-with-space? [text] + (and (not (nil? text)) + (str/ends-with? text const/spacing-char))) (defn starts-as-command? "Returns true if `text` may be treated as a command. @@ -50,12 +47,13 @@ {:keys [contacts requests]} (get-in db [:chats chat-id])] (->> contacts (map (fn [{:keys [identity]}] - (let [{:keys [commands responses]} (get-in db [:contacts/contacts identity])] - (let [commands' (mapv (fn [[k v]] [k [v :any]]) (merge global-commands commands)) - responses' (mapv (fn [{:keys [message-id type]}] - [type [(get responses type) message-id]]) - requests)] - (into commands' responses'))))) + (let [{:keys [commands responses]} (get-in db [:contacts/contacts identity])] + (let [commands' (mapv (fn [[k v]] [k [v :any]]) (merge global-commands commands)) + responses' (mapv (fn [{:keys [message-id type]}] + (when-let [response (get responses type)] + [type [response message-id]])) + requests)] + (into commands' responses'))))) (reduce (fn [m cur] (into (or m {}) cur))) (into {})))) @@ -71,30 +69,31 @@ All the complex logic inside this function aims to support wrapped arguments." [command-text] - (let [space? (text-ends-with-space? command-text) - command-text (if space? - (str command-text ".") - command-text) - command-text-normalized (if command-text - (str/replace (str/trim command-text) #" +" " ") - command-text) - splitted (cond-> (str/split command-text-normalized const/spacing-char) - space? (drop-last))] - (->> splitted - (reduce (fn [[list command-started?] arg] - (let [quotes-count (count (filter #(= % const/arg-wrapping-char) arg)) - has-quote? (and (= quotes-count 1) - (str/index-of arg const/arg-wrapping-char)) - arg (str/replace arg (re-pattern const/arg-wrapping-char) "") - new-list (if command-started? - (let [index (dec (count list))] - (update list index str const/spacing-char arg)) - (conj list arg)) - command-continues? (or (and command-started? (not has-quote?)) - (and (not command-started?) has-quote?))] - [new-list command-continues?])) - [[] false]) - (first)))) + (when command-text + (let [space? (text-ends-with-space? command-text) + command-text (if space? + (str command-text ".") + command-text) + command-text-normalized (if command-text + (str/replace (str/trim command-text) #" +" " ") + command-text) + splitted (cond-> (str/split command-text-normalized const/spacing-char) + space? (drop-last))] + (->> splitted + (reduce (fn [[list command-started?] arg] + (let [quotes-count (count (filter #(= % const/arg-wrapping-char) arg)) + has-quote? (and (= quotes-count 1) + (str/index-of arg const/arg-wrapping-char)) + arg (str/replace arg (re-pattern const/arg-wrapping-char) "") + new-list (if command-started? + (let [index (dec (count list))] + (update list index str const/spacing-char arg)) + (conj list arg)) + command-continues? (or (and command-started? (not has-quote?)) + (and (not command-started?) has-quote?))] + [new-list command-continues?])) + [[] false]) + (first))))) (defn join-command-args [args] "Transforms a list of args to a string. The opposite of `split-command-args`. @@ -108,13 +107,14 @@ Input: ['/send' 'Complex name with space in between' '1.0'] Output: '/send \"Complex name with space in between\" 1.0'" - (->> args - (map (fn [arg] - (let [arg (str/replace arg (re-pattern const/arg-wrapping-char) "")] - (if (not (str/index-of arg const/spacing-char)) - arg - (str const/arg-wrapping-char arg const/arg-wrapping-char))))) - (str/join const/spacing-char))) + (when args + (->> args + (map (fn [arg] + (let [arg (str/replace arg (re-pattern const/arg-wrapping-char) "")] + (if (not (str/index-of arg const/spacing-char)) + arg + (str const/arg-wrapping-char arg const/arg-wrapping-char))))) + (str/join const/spacing-char)))) (defn selected-chat-command "Returns a map containing `:command`, `:metadata` and `:args` keys. @@ -154,7 +154,7 @@ (defn current-chat-argument-position "Returns the position of current argument. It's just an integer number from -1 to infinity. -1 (`*no-argument-error*`) means error. It can happen if there is no selected command or selection." - [{:keys [args] :as command} input-text selection seq-arguments] + [command input-text selection seq-arguments] (if command (if (get-in command [:command :sequential-params]) (count seq-arguments) @@ -226,6 +226,7 @@ (->> args (map-indexed (fn [i value] [(keyword (get-in params [i :name])) value])) + (remove #(nil? (first %))) (into {})))) (defn command-dependent-context-params diff --git a/test/cljs/status_im/test/chat/models/input.cljs b/test/cljs/status_im/test/chat/models/input.cljs index 05de9a75fc..bc2188810e 100644 --- a/test/cljs/status_im/test/chat/models/input.cljs +++ b/test/cljs/status_im/test/chat/models/input.cljs @@ -1,8 +1,150 @@ (ns status-im.test.chat.models.input (:require [cljs.test :refer-macros [deftest is]] - [status-im.chat.models.input :as in])) + [status-im.chat.models.input :as input])) -(deftest test-split-command-args - (is (= [""] (in/split-command-args nil))) - (is (= ["@browse" "google.com"] (in/split-command-args "@browse google.com"))) - (is (= ["@browse" "google.com"] (in/split-command-args " @browse google.com ")))) +(def fake-db + {:global-commands {:command1 {:name "global-command1"}} + :chats {"test1" {:contacts [{:identity "0x1"}] + :requests nil + :seq-arguments ["arg1" "arg2"]} + "test2" {:contacts [{:identity "0x1"} + {:identity "0x2"}] + :requests [{:message-id "id1" :type :request1}]} + "test3" {:contacts [{:identity "0x1"}] + :requests [{:message-id "id1" :type :request1}]} + "test4" {:contacts [{:identity "0x1"} + {:identity "0x2"}] + :requests [{:message-id "id2" :type :request2}] + :input-metadata {:meta-k "meta-v"}}} + :contacts/contacts {"0x1" {:commands {:command2 {:name "command2"}} + :responses nil} + "0x2" {:commands {:command3 {:name "command3"}} + :responses {:request1 {:name "request1"}}}}}) + +(deftest text->emoji + (is (nil? (input/text->emoji nil))) + (is (= "" (input/text->emoji ""))) + (is (= "test" (input/text->emoji "test"))) + (is (= "word1 \uD83D\uDC4D word2" (input/text->emoji "word1 :+1: word2")))) + +(deftest starts-as-command? + (is (false? (input/starts-as-command? nil))) + (is (false? (input/text-ends-with-space? ""))) + (is (false? (input/text-ends-with-space? "word1 word2 word3"))) + (is (true? (input/text-ends-with-space? "word1 word2 ")))) + +(deftest possible-chat-actions + (is (= (input/possible-chat-actions fake-db "non-existent-chat") {})) + (is (= (input/possible-chat-actions fake-db "test1") + {:command1 [{:name "global-command1"} :any] + :command2 [{:name "command2"} :any]})) + (is (= (input/possible-chat-actions fake-db "test1") + {:command1 [{:name "global-command1"} :any] + :command2 [{:name "command2"} :any]})) + (is (= (input/possible-chat-actions fake-db "test2") + {:command1 [{:name "global-command1"} :any] + :command2 [{:name "command2"} :any] + :command3 [{:name "command3"} :any] + :request1 [{:name "request1"} "id1"]})) + (is (= (input/possible-chat-actions fake-db "test3") + {:command1 [{:name "global-command1"} :any] + :command2 [{:name "command2"} :any]})) + (is (= (input/possible-chat-actions fake-db "test4") + {:command1 [{:name "global-command1"} :any] + :command2 [{:name "command2"} :any] + :command3 [{:name "command3"} :any]}))) + +(deftest split-command-args + (is (nil? (input/split-command-args nil))) + (is (= [""] (input/split-command-args ""))) + (is (= ["@browse" "google.com"] (input/split-command-args "@browse google.com"))) + (is (= ["@browse" "google.com"] (input/split-command-args " @browse google.com "))) + (is (= ["/send" "1.0" "John Doe"] (input/split-command-args "/send 1.0 \"John Doe\""))) + (is (= ["/send" "1.0" "John Doe"] (input/split-command-args "/send 1.0 \"John Doe\" ")))) + +(deftest join-command-args + (is (nil? (input/join-command-args nil))) + (is (= "" (input/join-command-args [""]))) + (is (= "/send 1.0 \"John Doe\"" (input/join-command-args ["/send" "1.0" "John Doe"])))) + +(deftest selected-chat-command + (is (= (input/selected-chat-command fake-db "test1" "/global-command1") + {:command {:name "global-command1"} :metadata nil :args ["arg1" "arg2"]})) + (is (= (input/selected-chat-command fake-db "test2" "/global-command1") + {:command {:name "global-command1"} :metadata nil :args []})) + (is (nil? (input/selected-chat-command fake-db "test1" "/command3"))) + (is (= (input/selected-chat-command fake-db "test1" "/command2") + {:command {:name "command2"} :metadata nil :args ["arg1" "arg2"]})) + (is (= (input/selected-chat-command fake-db "test2" "/request1 arg1") + {:command {:name "request1"} :metadata {:to-message-id "id1"} :args ["arg1"]})) + (is (= (input/selected-chat-command fake-db "test4" "/command2 arg1") + {:command {:name "command2"} :metadata {:meta-k "meta-v"} :args ["arg1"]}))) + +(deftest current-chat-argument-position + (is (= (input/current-chat-argument-position + {:name "command1"} "/command1 arg1 arg2 " 0 nil) -1)) + (is (= (input/current-chat-argument-position + {:name "command1"} "/command1 argument1 arg2 " 9 nil) -1)) + (is (= (input/current-chat-argument-position + {:name "command1"} "/command1 argument1 arg2 " 10 nil) 0)) + (is (= (input/current-chat-argument-position + {:name "command1"} "/command1 argument1 arg2 " 19 nil) 0)) + (is (= (input/current-chat-argument-position + {:name "command1"} "/command1 argument1 arg2 " 20 nil) 1)) + (is (= (input/current-chat-argument-position + {:name "command2"} "/command2 \"a r g u m e n t 1\" argument2" 30 nil) 1)) + (is (= (input/current-chat-argument-position + {:name "command3" :command {:sequential-params true}} "/command3" 0 ["test1" "test2"]) 2))) + +(deftest argument-position + "Doesn't require a separate test because it simply calls `current-chat-argument-position") + +(deftest command-completion + (is (= (input/command-completion {:args ["p1" "p2"] + :command {:params [{:optional false} {:optional false}]}}) + :complete)) + (is (= (input/command-completion {:args ["p1"] + :command {:params [{:optional false} {:optional false}]}}) + :less-than-needed)) + (is (= (input/command-completion {:args ["p1" "p2" "p3"] + :command {:params [{:optional false} {:optional false}]}}) + :more-than-needed)) + (is (= (input/command-completion {:args ["p1" "p2"] + :command {:params [{:optional false} {:optional false} {:optional true}]}}) + :complete)) + (is (= (input/command-completion {:args ["p1" "p2" "p3"] + :command {:params [{:optional false} {:optional false} {:optional true}]}}) + :complete)) + (is (= (input/command-completion {:args ["p1" "p2" "p3" "p4"] + :command {:params [{:optional false} {:optional false} {:optional true}]}}) + :more-than-needed)) + (is (= (input/command-completion {:command {:params [{:optional false}]}}) + :less-than-needed)) + (is (= (input/command-completion {:command {}}) + :complete)) + (is (= (input/command-completion nil) + :no-command))) + +(deftest args->params + (is (= {} (input/args->params nil))) + (is (= {} (input/args->params {}))) + (is (= {} (input/args->params {:args ["1.0"]}))) + (is (= {:amount "1.0"} + (input/args->params {:command {:params [{:name "amount"}]} + :args ["1.0"]}))) + (is (= {:amount "1.0"} + (input/args->params {:command {:params [{:name "amount"}]} + :args ["1.0" "2.0" "3.0"]}))) + (is (= {:amount "1.0"} + (input/args->params {:command {:params [{:name "amount"} {:name "recipient"}]} + :args ["1.0"]}))) + (is (= {:amount "1.0" :recipient "John Doe"} + (input/args->params {:command {:params [{:name "amount"} {:name "recipient"}]} + :args ["1.0" "John Doe"]})))) + +(deftest command-dependent-context-params + (is (= {} (input/command-dependent-context-params "any" {:name "any"}))) + (is (= {} (input/command-dependent-context-params "console" {:name "any"})))) + +(deftest modified-db-after-change + "Just a combination of db modifications. Can be skipped now") \ No newline at end of file