critique of wizard design.
This commit is contained in:
114
test/clj/auto_ap/ssr/components/wizard_trial/core_test.clj
Normal file
114
test/clj/auto_ap/ssr/components/wizard_trial/core_test.clj
Normal file
@@ -0,0 +1,114 @@
|
||||
(ns auto-ap.ssr.components.wizard-trial.core-test
|
||||
(:require
|
||||
[auto-ap.ssr.components.wizard-trial.core :as sut]
|
||||
[auto-ap.ssr.components.wizard-trial.state :as ws]
|
||||
[clojure.test :refer [deftest is testing]]
|
||||
[hiccup.core :as hiccup]))
|
||||
|
||||
(deftest render-step-test
|
||||
(testing "render-step produces a form with hidden wizard-id and step-key inputs"
|
||||
(let [wizard-id (ws/create! {})
|
||||
step-config {:key :test-step
|
||||
:render (fn [{:keys [step-data errors request]}]
|
||||
[:div (str "data: " step-data) (when errors [:span.error (str errors)])])
|
||||
:submit-route "/test-submit"}
|
||||
result (sut/render-step {:wizard-id wizard-id
|
||||
:step-config step-config
|
||||
:request {}})]
|
||||
(is (= :form (first result)))
|
||||
(let [attrs (second result)
|
||||
children (drop 2 result)]
|
||||
(is (= "/test-submit" (:hx-post attrs)))
|
||||
(is (= "this" (:hx-target attrs)))
|
||||
(let [html (hiccup/html result)]
|
||||
(is (re-find #"name=\"wizard-id\"" html))
|
||||
(is (re-find #"value=\"" html))
|
||||
(is (re-find #"name=\"step-key\"" html))
|
||||
(is (re-find #"value=\"test-step\"" html))))))
|
||||
|
||||
(testing "render-step passes step-data, errors, and request to the render function"
|
||||
(let [wizard-id (ws/create! {:test-step {:field "value"}})
|
||||
captured (atom nil)
|
||||
step-config {:key :test-step
|
||||
:render (fn [args] (reset! captured args) [:div "rendered"])
|
||||
:submit-route "/test-submit"}
|
||||
_ (sut/render-step {:wizard-id wizard-id
|
||||
:step-config step-config
|
||||
:request {:client-id 123}
|
||||
:errors {:field ["is invalid"]}})
|
||||
{:keys [step-data errors request]} @captured]
|
||||
(is (= {:field "value"} step-data))
|
||||
(is (= {:field ["is invalid"]} errors))
|
||||
(is (= {:client-id 123} request)))))
|
||||
|
||||
(deftest handle-submit-test
|
||||
(testing "handle-submit with valid data saves step and calls done-fn"
|
||||
(let [done-result (atom nil)
|
||||
wizard-id (ws/create! {})
|
||||
step-config {:key :test-step
|
||||
:schema [:map [:name :string]]
|
||||
:fields [:name]
|
||||
:render (fn [_] [:div "rendered"])
|
||||
:submit-route "/test-submit"
|
||||
:done-fn (fn [data request]
|
||||
(reset! done-result {:data data :request request})
|
||||
{:status 200 :body "done"})}
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "test-step"
|
||||
"name" "Alice"}}
|
||||
response (sut/handle-submit step-config request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (= "done" (:body response)))
|
||||
(is (= "Alice" (get-in @done-result [:data :name])))
|
||||
(is (nil? (ws/get-wizard wizard-id)) "Wizard session should be destroyed after successful submit")))
|
||||
|
||||
(testing "handle-submit with invalid data re-renders step with errors"
|
||||
(let [wizard-id (ws/create! {})
|
||||
step-config {:key :test-step
|
||||
:schema [:map [:name :string]]
|
||||
:fields [:name]
|
||||
:render (fn [{:keys [errors]}]
|
||||
[:div (when errors [:span.error (str errors)])])
|
||||
:submit-route "/test-submit"
|
||||
:done-fn (fn [_ _] {:status 200 :body "done"})}
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "test-step"
|
||||
"name" ""}}
|
||||
response (sut/handle-submit step-config request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (string? (:body response)))
|
||||
(is (re-find #"error" (:body response)) "Response body should contain error markup")
|
||||
(is (some? (ws/get-wizard wizard-id)) "Wizard session should still exist after failed validation")))
|
||||
|
||||
(testing "handle-submit with missing required field shows validation error"
|
||||
(let [wizard-id (ws/create! {})
|
||||
step-config {:key :test-step
|
||||
:schema [:map [:name :string]]
|
||||
:fields [:name]
|
||||
:render (fn [{:keys [errors]}]
|
||||
[:div (when errors [:span.error (str errors)])])
|
||||
:submit-route "/test-submit"
|
||||
:done-fn (fn [_ _] {:status 200 :body "done"})}
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "test-step"}}
|
||||
response (sut/handle-submit step-config request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (re-find #"error" (:body response)) "Response body should contain error markup for missing field")))
|
||||
|
||||
(testing "handle-submit decodes step data using main-transformer"
|
||||
(let [done-result (atom nil)
|
||||
wizard-id (ws/create! {})
|
||||
step-config {:key :test-step
|
||||
:schema [:map [:count int?]]
|
||||
:fields [:count]
|
||||
:render (fn [_] [:div "rendered"])
|
||||
:submit-route "/test-submit"
|
||||
:done-fn (fn [data _]
|
||||
(reset! done-result data)
|
||||
{:status 200 :body "done"})}
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "test-step"
|
||||
"count" "42"}}
|
||||
response (sut/handle-submit step-config request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (= 42 (:count @done-result)) "String count should be decoded to integer"))))
|
||||
76
test/clj/auto_ap/ssr/components/wizard_trial/state_test.clj
Normal file
76
test/clj/auto_ap/ssr/components/wizard_trial/state_test.clj
Normal file
@@ -0,0 +1,76 @@
|
||||
(ns auto-ap.ssr.components.wizard-trial.state-test
|
||||
(:require
|
||||
[auto-ap.ssr.components.wizard-trial.state :as sut]
|
||||
[clojure.test :refer [deftest is testing]]))
|
||||
|
||||
(deftest create-and-get-wizard-test
|
||||
(testing "Session creation returns a non-nil wizard-id"
|
||||
(let [wizard-id (sut/create! {:foo "bar"})]
|
||||
(is (string? wizard-id))
|
||||
(is (seq wizard-id))))
|
||||
|
||||
(testing "Session retrieval returns the stored data"
|
||||
(let [wizard-id (sut/create! {:foo "bar"})
|
||||
wizard (sut/get-wizard wizard-id)]
|
||||
(is (map? wizard))
|
||||
(is (= {:foo "bar"} (:data wizard)))
|
||||
(is (inst? (:created-at wizard)))))
|
||||
|
||||
(testing "Session retrieval returns nil for unknown id"
|
||||
(is (nil? (sut/get-wizard "non-existent-id")))))
|
||||
|
||||
(deftest update-step-test
|
||||
(testing "update-step! merges data into the specified step key"
|
||||
(let [wizard-id (sut/create! {})
|
||||
_ (sut/update-step! wizard-id :step1 {:field-a "a"})
|
||||
wizard (sut/get-wizard wizard-id)]
|
||||
(is (= {:field-a "a"} (get-in wizard [:data :step1])))))
|
||||
|
||||
(testing "update-step! merges without overwriting other step keys"
|
||||
(let [wizard-id (sut/create! {})
|
||||
_ (sut/update-step! wizard-id :step1 {:field-a "a"})
|
||||
_ (sut/update-step! wizard-id :step2 {:field-b "b"})
|
||||
wizard (sut/get-wizard wizard-id)]
|
||||
(is (= {:field-a "a"} (get-in wizard [:data :step1])))
|
||||
(is (= {:field-b "b"} (get-in wizard [:data :step2])))))
|
||||
|
||||
(testing "update-step! merges within the same step key"
|
||||
(let [wizard-id (sut/create! {})
|
||||
_ (sut/update-step! wizard-id :step1 {:field-a "a"})
|
||||
_ (sut/update-step! wizard-id :step1 {:field-b "b"})
|
||||
wizard (sut/get-wizard wizard-id)]
|
||||
(is (= {:field-a "a" :field-b "b"} (get-in wizard [:data :step1]))))))
|
||||
|
||||
(deftest destroy-test
|
||||
(testing "destroy! removes the wizard session"
|
||||
(let [wizard-id (sut/create! {:foo "bar"})
|
||||
_ (sut/destroy! wizard-id)]
|
||||
(is (nil? (sut/get-wizard wizard-id)))))
|
||||
|
||||
(testing "destroy! is a no-op for unknown id"
|
||||
(sut/destroy! "non-existent-id")
|
||||
(is (nil? (sut/get-wizard "non-existent-id")))))
|
||||
|
||||
(deftest get-all-data-test
|
||||
(testing "get-all-data merges non-map values and map values from all steps"
|
||||
(let [wizard-id (sut/create! {:client-id 123})
|
||||
_ (sut/update-step! wizard-id :step1 {:vendor 456})
|
||||
_ (sut/update-step! wizard-id :step2 {:accounts [{:account 1}]})
|
||||
all-data (sut/get-all-data wizard-id)]
|
||||
(is (= {:client-id 123 :vendor 456 :accounts [{:account 1}]} all-data))))
|
||||
|
||||
(testing "get-all-data returns nil for unknown id"
|
||||
(is (nil? (sut/get-all-data "non-existent-id")))))
|
||||
|
||||
(deftest session-exists-test
|
||||
(testing "Session exists after creation"
|
||||
(let [wizard-id (sut/create! {})]
|
||||
(is (some? (sut/get-wizard wizard-id)))))
|
||||
|
||||
(testing "Session does not exist after destruction"
|
||||
(let [wizard-id (sut/create! {})
|
||||
_ (sut/destroy! wizard-id)]
|
||||
(is (nil? (sut/get-wizard wizard-id)))))
|
||||
|
||||
(testing "Session does not exist for random id"
|
||||
(is (nil? (sut/get-wizard (str (java.util.UUID/randomUUID)))))))
|
||||
104
test/clj/auto_ap/ssr/transaction/bulk_code_trial_test.clj
Normal file
104
test/clj/auto_ap/ssr/transaction/bulk_code_trial_test.clj
Normal file
@@ -0,0 +1,104 @@
|
||||
(ns auto-ap.ssr.transaction.bulk-code-trial-test
|
||||
(:require
|
||||
[auto-ap.ssr.components.wizard-trial.state :as ws]
|
||||
[auto-ap.ssr.transaction.bulk-code-trial :as sut]
|
||||
[clojure.test :refer [deftest is testing use-fixtures]]
|
||||
[mount.core :as mount]))
|
||||
|
||||
(use-fixtures :each
|
||||
(fn [test-fn]
|
||||
(mount/start #'auto-ap.datomic/conn)
|
||||
(test-fn)
|
||||
(mount/stop #'auto-ap.datomic/conn)))
|
||||
|
||||
(deftest open-trial-test
|
||||
(testing "open-trial returns modal-response with a form containing expected fields"
|
||||
(let [response (sut/open-trial {})]
|
||||
(is (= 200 (:status response)))
|
||||
(is (= "text/html" (get-in response [:headers "Content-Type"])))
|
||||
(let [body (:body response)]
|
||||
(is (string? body))
|
||||
(is (re-find #"modal-card" body) "Should contain modal card structure")
|
||||
(is (re-find #"Bulk editing" body) "Should show header with transaction count")
|
||||
(is (re-find #"Vendor" body) "Form should contain Vendor label")
|
||||
(is (re-find #"Status" body) "Form should contain Status label")
|
||||
(is (re-find #"Expense Accounts" body) "Form should contain Expense Accounts heading")
|
||||
(is (re-find #"Account" body) "Form should contain Account column header")
|
||||
(is (re-find #"Location" body) "Form should contain Location column header")
|
||||
(is (re-find #"%" body) "Form should contain percentage column header")
|
||||
(is (re-find #"Save" body) "Form should contain Save button")
|
||||
(is (re-find #"New account" body) "Form should contain New account button")
|
||||
(is (re-find #"name=\"wizard-id\"" body) "Form should contain hidden wizard-id input")
|
||||
(is (re-find #"name=\"step-key\"" body) "Form should contain hidden step-key input")))))
|
||||
|
||||
(deftest submit-trial-valid-test
|
||||
(testing "submit-trial with valid data returns success response and destroys session"
|
||||
(let [wizard-id (ws/create! {})
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "bulk-code"
|
||||
"vendor" "123"
|
||||
"approval-status" "approved"
|
||||
"accounts[0][account]" "1"
|
||||
"accounts[0][location]" "DT"
|
||||
"accounts[0][percentage]" "50"}}
|
||||
response (sut/submit-trial request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (re-find #"Transactions Coded" (:body response)) "Response should indicate success")
|
||||
(is (nil? (ws/get-wizard wizard-id)) "Wizard session should be destroyed after successful submit"))))
|
||||
|
||||
(deftest submit-trial-invalid-test
|
||||
(testing "submit-trial with invalid vendor id shows validation error"
|
||||
(let [wizard-id (ws/create! {})
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "bulk-code"
|
||||
"vendor" "not-a-number"
|
||||
"approval-status" "approved"}}
|
||||
response (sut/submit-trial request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (re-find #"error" (:body response)) "Response should contain error markup for invalid vendor")
|
||||
(is (some? (ws/get-wizard wizard-id)) "Wizard session should persist after failed validation")))
|
||||
|
||||
(testing "submit-trial with invalid account data shows validation error"
|
||||
(let [wizard-id (ws/create! {})
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "bulk-code"
|
||||
"accounts[0][account]" "not-a-number"
|
||||
"accounts[0][location]" "DT"
|
||||
"accounts[0][percentage]" "50"}}
|
||||
response (sut/submit-trial request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (re-find #"error" (:body response)) "Response should contain error markup for invalid account")
|
||||
(is (some? (ws/get-wizard wizard-id)) "Wizard session should persist after failed validation")))
|
||||
|
||||
(testing "submit-trial with percentage over 100% shows validation error"
|
||||
(let [wizard-id (ws/create! {})
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "bulk-code"
|
||||
"accounts[0][account]" "1"
|
||||
"accounts[0][location]" "DT"
|
||||
"accounts[0][percentage]" "150"}}
|
||||
response (sut/submit-trial request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (re-find #"error" (:body response)) "Response should contain error markup for percentage > 100%")
|
||||
(is (some? (ws/get-wizard wizard-id)) "Wizard session should persist after failed validation"))))
|
||||
|
||||
(deftest submit-trial-empty-test
|
||||
(testing "submit-trial with empty form data shows validation errors"
|
||||
(let [wizard-id (ws/create! {})
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "bulk-code"}}
|
||||
response (sut/submit-trial request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (not (re-find #"Bulk code applied" (:body response))) "Empty form should not succeed")
|
||||
(is (some? (ws/get-wizard wizard-id)) "Wizard session should persist after failed validation")))
|
||||
|
||||
(testing "submit-trial with no account rows selected shows validation errors"
|
||||
(let [wizard-id (ws/create! {})
|
||||
request {:form-params {"wizard-id" wizard-id
|
||||
"step-key" "bulk-code"
|
||||
"vendor" ""
|
||||
"approval-status" ""}}
|
||||
response (sut/submit-trial request)]
|
||||
(is (= 200 (:status response)))
|
||||
(is (not (re-find #"Bulk code applied" (:body response))) "Form with empty values should not succeed")
|
||||
(is (some? (ws/get-wizard wizard-id)) "Wizard session should persist after failed validation"))))
|
||||
Reference in New Issue
Block a user