diff --git a/frontend/src/chicken_master/customers.cljs b/frontend/src/chicken_master/customers.cljs index d0d4c20..29fbcad 100644 --- a/frontend/src/chicken_master/customers.cljs +++ b/frontend/src/chicken_master/customers.cljs @@ -2,7 +2,6 @@ (:require [re-frame.core :as re-frame] [reagent.core :as reagent] - [chicken-master.config :refer [settings]] [chicken-master.products :as prod] [chicken-master.subs :as subs] [chicken-master.html :as html] diff --git a/frontend/src/chicken_master/db.cljs b/frontend/src/chicken_master/db.cljs index 2a332a0..5649675 100644 --- a/frontend/src/chicken_master/db.cljs +++ b/frontend/src/chicken_master/db.cljs @@ -24,7 +24,7 @@ ;; :current-days [{:day (google.date "2020-01-01") :orders []}] - ;; :customers [] + ;; :customers [{:id 1 :who "mr blobby"}] ;; :bearer-token "user-token" ::clients {:show nil} ; customers edit modal :stock {:show nil} diff --git a/frontend/src/chicken_master/events.cljs b/frontend/src/chicken_master/events.cljs index 19ed2d4..51bf408 100644 --- a/frontend/src/chicken_master/events.cljs +++ b/frontend/src/chicken_master/events.cljs @@ -3,7 +3,7 @@ [re-frame.core :as re-frame] [chicken-master.db :as db] [chicken-master.time :as time] - [chicken-master.config :refer [settings default-settings set-item!]] + [chicken-master.config :as config] [day8.re-frame.http-fx] [ajax.edn :as edn] [goog.crypt.base64 :as b64])) @@ -13,7 +13,7 @@ :or {on-success ::process-fetched-days on-failure ::failed-request}}] {:method method - :uri (str (settings :backend-url) endpoint) + :uri (str (config/settings :backend-url) endpoint) :headers {"Content-Type" "application/edn" "authorization" (get-token)} :format (edn/edn-request-format) @@ -33,17 +33,17 @@ (re-frame/reg-event-fx ::initialize-db (fn [_ _] - (time/update-settings default-settings) + (time/update-settings config/default-settings) (let [user (some-> js/window (.-localStorage) (.getItem :bearer-token))] {:db (assoc db/default-db - :settings default-settings + :settings config/default-settings :current-user user) :dispatch [(when user ::load-db)]}))) (re-frame/reg-event-fx ::load-db (fn [_ _] - (time/update-settings default-settings) + (time/update-settings config/default-settings) {:fx [[:dispatch [::show-from-date (time/iso-date (time/today))]] [:dispatch [::start-loading]] [:dispatch [::fetch-stock]] @@ -114,7 +114,6 @@ (select-keys order [:id :day :hour :state]) (select-keys form [:id :day :hour :state :who :notes :products])))})) -;; FIXME: add test (re-frame/reg-event-db ::process-fetched-days (fn [db [_ days]] @@ -125,7 +124,6 @@ [day (if (contains? days day) (days day) orders)]))) (update :orders #(reduce (fn [m order] (assoc m (:id order) order)) % (mapcat second days)))))) -;; FIXME: add test (re-frame/reg-event-fx ::scroll-weeks (fn [{db :db} [_ offset]] @@ -136,7 +134,6 @@ (time/date-offset (* 7 offset)) time/iso-date)]]]})) -;; FIXME: add test (re-frame/reg-event-db ::show-from-date (fn [{:keys [start-date orders] :as db} [_ day]] @@ -148,7 +145,6 @@ :start-date day :current-days (map #(vector % (get filtered-orders %)) (sort days)))))) -;; FIXME: add test (re-frame/reg-event-fx ::fetch-orders (fn [_ [_ from to]] @@ -156,21 +152,18 @@ :http-xhrio (http-request :get "orders")})) ;; Customers events -;; FIXME: add test (re-frame/reg-event-fx ::show-customers (fn [{db :db} _] {:db (assoc-in db [:clients :show] true) :dispatch [::fetch-stock]})) -;; FIXME: add test (re-frame/reg-event-fx ::add-customer (fn [_ [_ customer-name]] {:http-xhrio (http-request :post "customers" :body {:name customer-name} :on-success ::process-stock)})) -;; FIXME: add test (re-frame/reg-event-fx ::remove-customer (fn [_ [_ id]] @@ -180,32 +173,31 @@ ;;; Storage events -;; FIXME: add test (re-frame/reg-event-fx ::show-stock (fn [{db :db} _] {:db (assoc-in db [:stock :show] true) :dispatch [::fetch-stock]})) -;; FIXME: add test (re-frame/reg-event-fx ::fetch-stock (fn [_ _] {:dispatch [::start-loading] :http-xhrio (http-get "stock" {} ::process-stock)})) -;; FIXME: add test -(defn assoc-if [coll key val] (if val (assoc coll key val) coll)) +(defn assoc-if [coll key source] + (if (contains? source key) + (assoc coll key (source key)) + coll)) (re-frame/reg-event-fx ::process-stock - (fn [{db :db} [_ {:keys [products customers]}]] + (fn [{db :db} [_ stock]] {:db (-> db - (assoc-if :products products) - (assoc-if :customers customers)) + (assoc-if :products stock) + (assoc-if :customers stock)) :dispatch [::stop-loading] })) -;; FIXME: add test (re-frame/reg-event-fx ::save-stock (fn [_ [_ products]] @@ -214,19 +206,16 @@ :http-xhrio (http-request :post "products" :body products :on-success ::process-stock)})) ;; Settings - -;; FIXME: add test (re-frame/reg-event-db ::show-settings (fn [db _] (assoc-in db [:settings :show] true))) -;; FIXME: add test (re-frame/reg-event-fx ::set-user (fn [{db :db} [_ user]] - (set-item! :bearer-token (b64/encodeString (str (user "name") ":" (user "password")))) + (config/set-item! :bearer-token (b64/encodeString (str (user "name") ":" (user "password")))) {:db (assoc db :current-user (b64/encodeString (str (user "name") ":" (user "password")))) :dispatch [::load-db]})) diff --git a/frontend/src/chicken_master/products.cljs b/frontend/src/chicken_master/products.cljs index 37c56f0..220ee2e 100644 --- a/frontend/src/chicken_master/products.cljs +++ b/frontend/src/chicken_master/products.cljs @@ -4,8 +4,7 @@ [re-frame.core :as re-frame] [reagent.core :as reagent] [chicken-master.html :as html] - [chicken-master.subs :as subs] - [chicken-master.events :as event])) + [chicken-master.subs :as subs])) (defn num-or-nil [val] (let [i (js/parseFloat val)] diff --git a/frontend/src/chicken_master/time.cljs b/frontend/src/chicken_master/time.cljs index 1d8d15e..d9f12cb 100644 --- a/frontend/src/chicken_master/time.cljs +++ b/frontend/src/chicken_master/time.cljs @@ -1,5 +1,5 @@ (ns chicken-master.time - (:import [goog.date DateTime Date Interval])) + (:import [goog.date Date Interval])) (def settings (atom settings)) (defn update-settings [new-settings] (reset! settings new-settings)) diff --git a/frontend/test/chicken_master/events_test.cljs b/frontend/test/chicken_master/events_test.cljs index 644505e..60a1a92 100644 --- a/frontend/test/chicken_master/events_test.cljs +++ b/frontend/test/chicken_master/events_test.cljs @@ -2,10 +2,13 @@ (:require [chicken-master.events :as sut] [chicken-master.subs :as subs] + [chicken-master.config :as config] + [chicken-master.time :as time] [cljs.test :refer-macros [deftest is testing]] [day8.re-frame.test :as rf-test] [re-frame.core :as rf])) +(time/update-settings {:first-day-offset 1}) (defn set-db [updates] (rf/reg-event-db @@ -16,6 +19,13 @@ (defn param-validator [event validator] (rf/reg-event-fx event (fn [_ [_ & params]] (validator params) nil))) +(def sample-orders + [{:id 1 :day "2020-01-02"} {:id 2 :day "2020-01-02"} {:id 3 :day "2020-01-02"} + {:id 4 :day "2020-01-04"} + {:id 5 :day "2020-01-06"} {:id 6 :day "2020-01-06"}]) + +(def sample-orders-by-day (group-by :day sample-orders)) +(def sample-orders-by-id (reduce #(assoc %1 (:id %2) %2) {} sample-orders)) (deftest hide-modal (testing "models can be hidden" @@ -182,4 +192,263 @@ :state :pending :note "asd" :who {:id 12 :name "mr blobby"} :product {:eggs 2 :milk 3}}]) - (is (nil? @(rf/subscribe [::subs/show-edit-modal])))))) + (is (nil? @(rf/subscribe [::subs/show-edit-modal]))))) + + ;; FIXME: the request handler is not being overloaded + (testing "fetching orders works" + (rf-test/run-test-sync + (param-validator :http-xhrio (fn [[{:keys [method uri]}]] + (is (= method :get)) + (is (= uri "orders")))) + (let [called (atom false)] + (param-validator ::sut/start-loading #(reset! called true)) + (rf/dispatch [::sut/fetch-orders]) + + (is @called))))) + +(deftest test-process-fetched-days + (testing "processing fetched days disables the loader" + (rf-test/run-test-sync + (set-db {:orders {} :current-days {} :loading? true}) + (rf/dispatch [::sut/process-fetched-days {}]) + + (is (nil? @(rf/subscribe [::subs/loading?]))))) + + (testing "orders get set correctly" + (rf-test/run-test-sync + (set-db {:orders {} :current-days {} :loading? true}) + (rf/dispatch [::sut/process-fetched-days sample-orders-by-day]) + + (is (= @(rf/subscribe [::subs/orders]) sample-orders-by-id)))) + + (testing "orders get updated correctly" + (rf-test/run-test-sync + (set-db {:current-days {} :loading? true + :orders {1 {:id 1 :day "2020-01-01"} + 2 {:id 2 :day "2020-01-01"} + 3 {:id 3 :day "2020-01-01"} + 14 {:id 14 :day "2020-01-14"} + 15 {:id 15 :day "2020-01-16"}}}) + (rf/dispatch [::sut/process-fetched-days sample-orders-by-day]) + + (is (= @(rf/subscribe [::subs/orders]) + (merge sample-orders-by-id + {14 {:id 14 :day "2020-01-14"} + 15 {:id 15 :day "2020-01-16"}}))))) + + (testing "current days don't get overwitten" + (rf-test/run-test-sync + (set-db {:orders {} :current-days [] :loading? true}) + (rf/dispatch [::sut/process-fetched-days sample-orders-by-day]) + + (is (= @(rf/subscribe [::subs/current-days]) [])))) + + (testing "current days get updated correctly" + (rf-test/run-test-sync + (set-db {:orders {} :loading? true + :current-days [["2020-01-01" [{:id :left-as-is :day "2020-01-01"}]] + ["2020-01-02" [{:id :will-be-replaced :day "2020-01-02"}]] + ["2020-01-03" nil] + ["2020-01-04" nil] + ["2020-01-05" nil] + ["2020-01-06" [{:id :replaced :day "2020-01-06"}]] + ["2020-01-07" nil]]}) + (rf/dispatch [::sut/process-fetched-days sample-orders-by-day]) + + (is (= @(rf/subscribe [::subs/current-days]) + [["2020-01-01" [{:id :left-as-is :day "2020-01-01"}]] + ["2020-01-02" [{:id 1 :day "2020-01-02"} {:id 2 :day "2020-01-02"} {:id 3 :day "2020-01-02"}]] + ["2020-01-03" nil] + ["2020-01-04" [{:id 4 :day "2020-01-04"}]] + ["2020-01-05" nil] + ["2020-01-06" [{:id 5 :day "2020-01-06"} {:id 6 :day "2020-01-06"}]] + ["2020-01-07" nil]]))))) + +(deftest test-show-from-date + (testing "showing days disables the loader" + (rf-test/run-test-sync + (set-db {:orders {} :current-days {} :loading? true}) + (rf/dispatch [::sut/show-from-date "2020-01-01"]) + + (is (nil? @(rf/subscribe [::subs/loading?]))))) + + (testing "showing from date works" + (rf-test/run-test-sync + (set-db {:orders sample-orders-by-id :current-days [] :loading? true}) + (rf/dispatch [::sut/show-from-date "2019-12-30"]) + + (is (= @(rf/subscribe [::subs/current-days]) + [["2019-12-30" nil] + ["2019-12-31" nil] + ["2020-01-01" nil] + ["2020-01-02" [{:id 1, :day "2020-01-02"} {:id 2, :day "2020-01-02"} {:id 3, :day "2020-01-02"}]] + ["2020-01-03" nil] + ["2020-01-04" [{:id 4, :day "2020-01-04"}]] + ["2020-01-05" nil] + ["2020-01-06" [{:id 5, :day "2020-01-06"} {:id 6, :day "2020-01-06"}]] + ["2020-01-07" nil] ["2020-01-08" nil] ["2020-01-09" nil] + ["2020-01-10" nil] ["2020-01-11" nil] ["2020-01-12" nil]])))) + + (testing "showing from date starts from the beginning of the week" + (rf-test/run-test-sync + (set-db {:orders sample-orders-by-id :current-days [] :loading? true}) + (rf/dispatch [::sut/show-from-date "2020-01-03"]) + + (is (= @(rf/subscribe [::subs/current-days]) + [["2019-12-30" nil] + ["2019-12-31" nil] + ["2020-01-01" nil] + ["2020-01-02" [{:id 1, :day "2020-01-02"} {:id 2, :day "2020-01-02"} {:id 3, :day "2020-01-02"}]] + ["2020-01-03" nil] + ["2020-01-04" [{:id 4, :day "2020-01-04"}]] + ["2020-01-05" nil] + ["2020-01-06" [{:id 5, :day "2020-01-06"} {:id 6, :day "2020-01-06"}]] + ["2020-01-07" nil] ["2020-01-08" nil] ["2020-01-09" nil] + ["2020-01-10" nil] ["2020-01-11" nil] ["2020-01-12" nil]])))) + + (testing "showing from date uses value from db if none provided" + (rf-test/run-test-sync + (set-db {:orders sample-orders-by-id :start-date "2020-01-03"}) + (rf/dispatch [::sut/show-from-date]) + + (is (= @(rf/subscribe [::subs/current-days]) + [["2019-12-30" nil] + ["2019-12-31" nil] + ["2020-01-01" nil] + ["2020-01-02" [{:id 1, :day "2020-01-02"} {:id 2, :day "2020-01-02"} {:id 3, :day "2020-01-02"}]] + ["2020-01-03" nil] + ["2020-01-04" [{:id 4, :day "2020-01-04"}]] + ["2020-01-05" nil] + ["2020-01-06" [{:id 5, :day "2020-01-06"} {:id 6, :day "2020-01-06"}]] + ["2020-01-07" nil] ["2020-01-08" nil] ["2020-01-09" nil] + ["2020-01-10" nil] ["2020-01-11" nil] ["2020-01-12" nil]]))))) + +(deftest test-customers + (testing "customers are fetched before showing" + (rf-test/run-test-sync + (set-db {:clients {}}) + (let [called (atom false)] + (param-validator ::sut/fetch-stock #(reset! called true)) + (rf/dispatch [::sut/show-customers]) + + (is @(rf/subscribe [::subs/show-customers-modal])) + (is @called)))) + + ;; FIXME: the request handler is not being overloaded + (testing "customers can be added" + (rf-test/run-test-sync + (param-validator :http-xhrio (fn [[{:keys [method uri body]}]] + (is (= method :post)) + (is (= uri "customers")) + (is (= body {:name "mr blobby"})))) + + (rf/dispatch [::sut/add-customer "mr blobby"]))) + + ;; FIXME: the request handler is not being overloaded + (testing "customers can be removed" + (rf-test/run-test-sync + (param-validator :http-xhrio (fn [[{:keys [method uri]}]] + (is (= method :delete)) + (is (= uri "customers/1")))) + + (rf/dispatch [::sut/remove-customer 1])))) + + +(deftest stock-tests + (testing "stock fetched before showing" + (rf-test/run-test-sync + (set-db {:stock {}}) + (let [called (atom false)] + (param-validator ::sut/fetch-stock #(reset! called true)) + (rf/dispatch [::sut/show-stock]) + + (is @(rf/subscribe [::subs/show-stock-modal])) + (is @called)))) + + ;; FIXME: the request handler is not being overloaded + (testing "stock gets fetched" + (rf-test/run-test-sync + (param-validator :http-xhrio (fn [[{:keys [method uri]}]] + (is (= method :get)) + (is (= uri "stock")))) + + (let [called (atom false)] + (param-validator ::sut/start-loading #(reset! called true)) + + (rf/dispatch [::sut/fetch-stock]) + + (is @called)))) + + (testing "the loader is hidden after stock gets processed" + (rf-test/run-test-sync + (let [called (atom false)] + (param-validator ::sut/stop-loading #(reset! called true)) + + (rf/dispatch [::sut/process-stock {}]) + + (is @called)))) + + (testing "stock gets processed" + (rf-test/run-test-sync + (rf/dispatch [::sut/process-stock {:products {:eggs 1 :milk 2} + :customers [{:id 1 :who "mr blobby"} + {:id 2 :who "johhny"}]}]) + + (is (= @(rf/subscribe [::subs/available-products]) {:eggs 1 :milk 2})) + (is (= @(rf/subscribe [::subs/available-customers]) + [{:id 1 :who "mr blobby"} {:id 2 :who "johhny"}])))) + + (testing "stock isn't replaced if not in items" + (rf-test/run-test-sync + (set-db {:customers [{:id 1 :who "mr blobby"} {:id 2 :who "johhny X"}]}) + (rf/dispatch [::sut/process-stock {:products {:eggs 1 :milk 2}}]) + + (is (= @(rf/subscribe [::subs/available-products]) {:eggs 1 :milk 2})) + (is (= @(rf/subscribe [::subs/available-customers]) + [{:id 1 :who "mr blobby"} {:id 2 :who "johhny X"}])))) + + (testing "stock is replaced if in items but empty" + (rf-test/run-test-sync + (set-db {:customers [{:id 1 :who "mr blobby"} {:id 2 :who "johhny X"}]}) + (rf/dispatch [::sut/process-stock {:products {:eggs 1 :milk 2} :customers nil}]) + + (is (= @(rf/subscribe [::subs/available-products]) {:eggs 1 :milk 2})) + (is (= @(rf/subscribe [::subs/available-customers]) nil)))) + + ;; FIXME: the request handler is not being overloaded + (testing "stock gets saved" + (rf-test/run-test-sync + (param-validator :http-xhrio (fn [[{:keys [method uri body]}]] + (is (= method :post)) + (is (= uri "products")) + (is (= body {:eggs 1 :milk 2})))) + (let [hidden? (atom false) + loading? (atom false)] + (param-validator ::sut/hide-modal #(reset! hidden? true)) + (param-validator ::sut/start-loading #(reset! loading? true)) + + (rf/dispatch [::sut/save-stock {:eggs 1 :milk 2}]) + + (is @hidden?) + (is @loading?))))) + + +(deftest test-settings + (testing "settings get shown" + (rf-test/run-test-sync + (set-db {:settings {}}) + (rf/dispatch [::sut/show-settings]) + (is @(rf/subscribe [::subs/show-settings-modal])))) + + (testing "users can be set" + (rf-test/run-test-sync + (let [loading? (atom false) + token "bXIgYmxvYmJ5OmJsYQ=="] + (with-redefs [config/set-item! #(is (= %2 token))] + (set-db {:current-user nil}) + (param-validator ::sut/load-db #(reset! loading? true)) + + (rf/dispatch [::sut/set-user {"name" "mr blobby" "password" "bla"}]) + + (is @loading?) + (is (= @(rf/subscribe [::subs/current-user]) token))))))) diff --git a/frontend/test/chicken_master/time_test.cljs b/frontend/test/chicken_master/time_test.cljs new file mode 100644 index 0000000..7776b36 --- /dev/null +++ b/frontend/test/chicken_master/time_test.cljs @@ -0,0 +1,73 @@ +(ns chicken-master.time-test + (:require + [chicken-master.time :as sut] + [cljs.test :refer-macros [deftest is testing]]) + (:import [goog.date Date])) + +(deftest parse-date-test + (testing "dates get parsed correctly" + (is (.equals (sut/parse-date (new Date 2020 10 10)) (new Date 2020 10 10))) + (is (.equals (sut/parse-date "2010-10-11") (new Date 2010 9 11))) + (is (.equals (sut/parse-date nil) (new Date 1970 0 1))))) + +(deftest date-offset-test + (testing "date offsets work" + (is (.equals (sut/date-offset (new Date 2020 2 2) 2) + (new Date 2020 2 4)))) + + (testing "negative offsets work" + (is (.equals (sut/date-offset (new Date 2020 2 2) -12) + (new Date 2020 1 19))))) + + +(deftest test-start-of-week + (testing "no extra offset" + (with-redefs [sut/settings (atom {:first-day-offset 0})] + (is (= (->> (sut/parse-date "2020-11-22") sut/start-of-week sut/iso-date) "2020-11-22")) + + (doseq [day ["2020-11-22" "2020-11-23" "2020-11-24" "2020-11-25" "2020-11-26" "2020-11-27" "2020-11-28"]] + (is #(= (-> (sut/parse-date %) sut/start-of-week sut/iso-date) "2020-11-22"))))) + + (testing "extra offset" + (with-redefs [sut/settings (atom {:first-day-offset 1})] + (is (= (->> (sut/parse-date "2020-11-22") sut/start-of-week sut/iso-date) "2020-11-16")) + + (doseq [day ["2020-11-23" "2020-11-24" "2020-11-25" "2020-11-26" "2020-11-27" "2020-11-28" "2020-11-29"]] + (is #(= (-> (sut/parse-date %) sut/start-of-week sut/iso-date) "2020-11-23")))))) + + +(deftest days-range-test + (testing "getting a days range works" + (is (= (map sut/iso-date (sut/days-range 12 (new Date 2020 1 1))) + ["2020-02-01" "2020-02-02" "2020-02-03" "2020-02-04" "2020-02-05" "2020-02-06" + "2020-02-07" "2020-02-08" "2020-02-09" "2020-02-10" "2020-02-11" "2020-02-12"]))) + + (testing "negative ranges are handled" + (is (= (sut/days-range -12 (new Date 2020 1 1)) [])))) + +(deftest date-comparison-tests + (testing "before" + (is (sut/before? (new Date 2020 1 2) (new Date 2020 1 4))) + (is (not (sut/before? (new Date 2020 1 2) (new Date 2020 1 2))))) + + (testing "after" + (is (sut/after? (new Date 2020 1 5) (new Date 2020 1 4))) + (is (not (sut/before? (new Date 2020 1 2) (new Date 2020 1 2))))) + + (testing "same day" + (is (sut/same-day? (new Date 2020 1 5) (new Date 2020 1 5))) + (is (not (sut/same-day? (new Date 2020 1 12) (new Date 2020 1 2)))))) + +(deftest test-format-date + (testing "don't show date" + (with-redefs [sut/settings (atom {:show-date nil})] + (is (nil? (sut/format-date (new Date 2020 01 01)))))) + + (testing "without name" + (with-redefs [sut/settings (atom {:show-date true :show-day-name-with-date nil})] + (is (= (sut/format-date (new Date 2020 01 01)) "2/1")))) + + (testing "with name" + (with-redefs [sut/settings (atom {:show-date true :show-day-name-with-date true + :day-names ["Niedz" "Pon" "Wt" "Śr" "Czw" "Pt" "Sob"]})] + (is (= (sut/format-date (new Date 2020 01 01)) "Sob 2/1")))))