- 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 <noreply@anthropic.com>
134 lines
5.0 KiB
Markdown
134 lines
5.0 KiB
Markdown
---
|
|
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
|