critique of wizard design.

This commit is contained in:
2026-06-01 09:40:49 -07:00
parent 5c2cf8a631
commit 5452b8b779
13 changed files with 2171 additions and 19 deletions

View 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"))))

View 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)))))))

View 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"))))