From 13ac73c7c5fbaab1cdc7f342ed8c64b9138b207c Mon Sep 17 00:00:00 2001 From: Bryce Date: Sat, 7 Feb 2026 10:12:01 -0800 Subject: [PATCH] Add location extraction for Bonanza Produce invoices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extract city/state/zip in location field - Customer address now split across 3 fields: - customer-identifier: customer name - account-number: street address - location: city, state zip - All components verified in test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ...sts-html-verification-SSRAdmin-20260207.md | 133 ++++++++++++++++++ src/clj/auto_ap/parse/templates.clj | 1 + test/clj/auto_ap/parse/templates_test.clj | 3 +- 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 docs/solutions/test-failures/route-tests-html-verification-SSRAdmin-20260207.md diff --git a/docs/solutions/test-failures/route-tests-html-verification-SSRAdmin-20260207.md b/docs/solutions/test-failures/route-tests-html-verification-SSRAdmin-20260207.md new file mode 100644 index 00000000..8c38b3dc --- /dev/null +++ b/docs/solutions/test-failures/route-tests-html-verification-SSRAdmin-20260207.md @@ -0,0 +1,133 @@ +--- +module: SSR Admin +component: testing_framework +date: '2026-02-07' +problem_type: best_practice +resolution_type: test_fix +severity: medium +root_cause: inadequate_documentation +symptoms: + - Route tests only verified HTTP status codes (200), not actual HTML content + - No verification that route responses contain expected page elements + - Could have false positives where routes return empty or wrong content +rails_version: 7.1.0 +tags: + - testing + - routes + - hiccup + - html-verification + - clojure + - str-includes +--- + +# Enhancing Route Tests with HTML Content Verification + +## Problem + +Route tests for the SSR admin modules (vendors and transaction-rules) were only verifying HTTP status codes, making them vulnerable to false positives. A route could return a 200 status but with empty or incorrect HTML content, and the tests would still pass. + +## Symptoms + +- Tests like `(is (= 200 (:status response)))` only checked HTTP status +- No assertions about the actual HTML content returned +- Route handlers could return malformed or empty hiccup vectors without test failures +- Dialog routes could return generic HTML without the expected content + +## Root Cause + +Missing best practice for route testing in Clojure SSR applications. Unlike Rails controller tests that can use `assert_select` or Capybara matchers, there was no established pattern for verifying hiccup-rendered HTML content. + +## Solution + +Enhanced route tests to verify HTML content using `clojure.string/includes?` checks on the rendered HTML string. + +### Implementation Pattern + +```clojure +;; BEFORE: Only status check +(deftest page-route-returns-html-response + (testing "Page route returns HTML response" + (let [request {:identity (admin-token)} + response ((get sut/key->handler :auto-ap.routes.admin.transaction-rules/page) request)] + (is (= 200 (:status response)))))) + +;; AFTER: Status + content verification +(deftest page-route-returns-html-response + (testing "Page route returns HTML response" + (let [request {:identity (admin-token)} + response ((get sut/key->handler :auto-ap.routes.admin.transaction-rules/page) request) + html-str (apply str (:body response))] + (is (= 200 (:status response))) + (is (str/includes? html-str "Transaction Rules"))))) +``` + +### Key Changes + +1. **Convert body to string**: Use `(apply str (:body response))` to convert hiccup vectors to HTML string +2. **Add content assertions**: Use `clojure.string/includes?` to verify expected content exists +3. **Test-specific content**: Match content unique to that route (page titles, button text, entity names) + +### Files Modified + +- `test/clj/auto_ap/ssr/admin/vendors_test.clj` + - Added `vendor-page-route-contains-vendor-content` test + +- `test/clj/auto_ap/ssr/admin/transaction_rules_test.clj` + - Enhanced 7 route tests with content verification: + - `page-route-returns-html-response` → checks for "Transaction Rules" + - `table-route-returns-table-data` → checks for "New Transaction Rule" + - `edit-dialog-route-returns-dialog` → checks for entity-specific content + - `account-typeahead-route-works` → checks for "account" + - `location-select-route-works` → checks for "location" + - `execute-dialog-route-works` → checks for "Code transactions" + - `new-dialog-route-returns-empty-form` → checks for "Transaction rule" + +### Testing Strategy + +For each route, identify the minimal but specific content that indicates the route is working: + +- **Page routes**: Check for page title or heading +- **Dialog routes**: Check for dialog-specific button text or the entity name being edited +- **Typeahead routes**: Check for the resource type (e.g., "account") +- **Table routes**: Check for action buttons or empty state messages + +## Prevention + +When writing route tests, always: + +1. ✅ Verify HTTP status code (200, 302, etc.) +2. ✅ Verify response contains expected HTML content +3. ✅ Use specific content unique to that route +4. ✅ Avoid overly generic strings that might appear on any page + +### Template for Route Tests + +```clojure +(deftest [route-name]-returns-expected-content + (testing "[Route description]" + (let [request {:identity (admin-token) + ;; Add route-params, query-params as needed + } + response ((get sut/key->handler :auto-ap.routes.[module]/[route]) request) + html-str (apply str (:body response))] + (is (= 200 (:status response))) + (is (str/includes? html-str "[Expected content]"))))) +``` + +## Tools Used + +- `clojure.string/includes?` - Simple string containment check +- `apply str` - Converts hiccup vector to HTML string +- No additional dependencies needed + +## Benefits + +- **Catches regressions**: Tests fail if route returns wrong content +- **Self-documenting**: Test assertions describe expected behavior +- **Lightweight**: No complex HTML parsing libraries required +- **Fast**: String operations are performant + +## Related + +- Similar pattern could apply to any Clojure SSR application using hiccup +- For more complex DOM assertions, consider adding hickory or enlive for structured HTML parsing diff --git a/src/clj/auto_ap/parse/templates.clj b/src/clj/auto_ap/parse/templates.clj index a13404cf..dadc71c1 100644 --- a/src/clj/auto_ap/parse/templates.clj +++ b/src/clj/auto_ap/parse/templates.clj @@ -760,6 +760,7 @@ :date #"NO\s+\d{8,}\s+(\d{2}/\d{2}/\d{2})" :customer-identifier #"(?s)I\s+([A-Z][A-Z\s]+?)\s{2,}.*?L\s+([0-9][A-Z0-9\s]+)" :account-number #"(?s)L\s+([0-9][0-9A-Z\s]+?)(?=\n|\s{2,})" + :location #"(?s)L\s+[0-9][0-9A-Z\s]+?\n\s+([A-Z][A-Z,\s]+[0-9]{5})" :total #"SHIPPED\s+[\d\.]+\s+TOTAL\s+([\d\.]+)"} :parser {:date [:clj-time "MM/dd/yy"] :total [:trim-commas nil]}}]) diff --git a/test/clj/auto_ap/parse/templates_test.clj b/test/clj/auto_ap/parse/templates_test.clj index 7f656383..bff6e4be 100644 --- a/test/clj/auto_ap/parse/templates_test.clj +++ b/test/clj/auto_ap/parse/templates_test.clj @@ -24,8 +24,9 @@ (is (= 2026 (time/year d))) (is (= 1 (time/month d))) (is (= 20 (time/day d)))) - ;; Customer identifier should include name and address + ;; Customer identifier components (is (= "NICK THE GREEK" (:customer-identifier result))) (is (= "600 VISTA WAY" (str/trim (:account-number result)))) + (is (= "MILPITAS, CA 95035" (str/trim (:location result)))) ;; Total is parsed as string, not number (per current behavior) (is (= "23.22" (:total result)))))))