Headless Fulcro application support for testing and server-side execution.
This namespace provides utilities for running Fulcro applications in headless mode on the JVM. Key features:
Example:
(require '[com.fulcrologic.fulcro.headless :as h])
(require '[com.fulcrologic.fulcro.headless.hiccup :as hic])
(require '[com.fulcrologic.fulcro.headless.loopback-remotes :as lr])
(def app (h/build-test-app
{:root-class Root
:remotes {:remote (lr/sync-remote my-handler)}}))
;; Transactions are synchronous
(comp/transact! app [(my-mutation)])
;; Inspect render frames
(let [frame (h/last-frame app)
hiccup (hic/rendered-tree->hiccup (:rendered frame))]
(is (= expected (:tree frame)))
(hic/click! (hic/find-by-id hiccup "my-button")))
Headless Fulcro application support for testing and server-side execution.
This namespace provides utilities for running Fulcro applications in headless mode
on the JVM. Key features:
- Synchronous transaction processing (all operations complete before returning)
- Render frame capture (inspect before/after state for assertions)
- Raw dom-server Element tree capture (convert to hiccup or other formats as needed)
- Controlled execution (step through transaction phases)
- Integration with Ring handlers and Pathom parsers via loopback remotes
Example:
```clojure
(require '[com.fulcrologic.fulcro.headless :as h])
(require '[com.fulcrologic.fulcro.headless.hiccup :as hic])
(require '[com.fulcrologic.fulcro.headless.loopback-remotes :as lr])
(def app (h/build-test-app
{:root-class Root
:remotes {:remote (lr/sync-remote my-handler)}}))
;; Transactions are synchronous
(comp/transact! app [(my-mutation)])
;; Inspect render frames
(let [frame (h/last-frame app)
hiccup (hic/rendered-tree->hiccup (:rendered frame))]
(is (= expected (:tree frame)))
(hic/click! (hic/find-by-id hiccup "my-button")))
```Default maximum number of render frames to keep in history.
Default maximum number of render frames to keep in history.
(attr-of app element-id attr-key)(attr-of app element-id attr-key frame-index)Get an attribute value from an element by ID.
With 3 args: From the most recent frame. With 4 args: From the nth frame (0 = most recent).
Returns the attribute value, or nil if element/attr not found.
Example:
(attr-of app "email-input" :value)
;; => "user@example.com"
(attr-of app "submit-btn" :disabled)
;; => true
Get an attribute value from an element by ID. With 3 args: From the most recent frame. With 4 args: From the nth frame (0 = most recent). Returns the attribute value, or nil if element/attr not found. Example: ```clojure (attr-of app "email-input" :value) ;; => "user@example.com" (attr-of app "submit-btn" :disabled) ;; => true ```
(blur! app element-id)Trigger blur on an element by ID. Invokes the element's :onBlur handler.
Example:
(blur! app "username-input")
Trigger blur on an element by ID. Invokes the element's :onBlur handler. Example: ```clojure (blur! app "username-input") ```
(build-test-app {:keys [root-class remotes initial-state render-history-size
shared shared-fn]
:or {render-history-size *render-frame-max* remotes {}}})Create a test application configured for synchronous testing.
Options:
Returns a Fulcro app configured with:
Event capture is installed automatically. Use captured-transactions,
captured-network-events, and clear-captured-events! to inspect
what mutations were triggered and what network activity occurred.
IMPORTANT: For event capture to work, you must enable inspect notifications by setting the JVM property: -Dcom.fulcrologic.fulcro.inspect=true
Create a test application configured for synchronous testing. Options: - :root-class - Root component class (required for render frame capture) - :remotes - Map of remote-name to remote implementation - :initial-state - Initial state map (merged with root's initial state) - :render-history-size - Number of frames to keep (default 10) - :shared - Static shared props - :shared-fn - Function to compute shared props from root tree Returns a Fulcro app configured with: - Synchronous transaction processing - Frame-capturing render - Event capture (transactions and network events) - The specified remotes Event capture is installed automatically. Use `captured-transactions`, `captured-network-events`, and `clear-captured-events!` to inspect what mutations were triggered and what network activity occurred. IMPORTANT: For event capture to work, you must enable inspect notifications by setting the JVM property: -Dcom.fulcrologic.fulcro.inspect=true
(captured-network-events app)Get the list of captured network events.
Each entry is a map containing:
Example:
(df/load! app :current-user User)
(let [events (captured-network-events app)]
(is (some #(= :finished (:event-type %)) events)))
Get the list of captured network events. Each entry is a map containing: - :event-type - One of :started, :finished, or :failed - :fulcro.inspect.ui.network/remote - The remote name - :fulcro.inspect.ui.network/request-id - Unique request identifier - :fulcro.inspect.ui.network/request-edn - The EQL sent (on :started) - :fulcro.inspect.ui.network/response-edn - The response body (on :finished) - :fulcro.inspect.ui.network/error - Error info (on :failed) Example: ```clojure (df/load! app :current-user User) (let [events (captured-network-events app)] (is (some #(= :finished (:event-type %)) events))) ```
(captured-transactions app)Get the list of captured transactions (optimistic actions).
Each entry is a map containing:
Example:
(comp/transact! app [(my-mutation {:x 1})])
(let [txns (captured-transactions app)]
(is (= 1 (count txns)))
(is (= 'my-ns/my-mutation (-> txns first :fulcro.history/tx first))))
Get the list of captured transactions (optimistic actions).
Each entry is a map containing:
- :fulcro.history/tx - The transaction form (mutation-sym params)
- :fulcro.history/network-sends - Set of remotes that will be contacted
- :component - Name of the component that initiated the transaction (if any)
- :ident-ref - The ident of the component (if any)
Example:
```clojure
(comp/transact! app [(my-mutation {:x 1})])
(let [txns (captured-transactions app)]
(is (= 1 (count txns)))
(is (= 'my-ns/my-mutation (-> txns first :fulcro.history/tx first))))
```(change! app element-id event)Invoke onChange on an element with a custom event.
For simple text input, prefer type-into!.
Example:
;; For a checkbox
(change! app "remember-me" {:target {:checked true}})
;; For a select
(change! app "country-select" {:target {:value "US"}})
Invoke onChange on an element with a custom event.
For simple text input, prefer `type-into!`.
Example:
```clojure
;; For a checkbox
(change! app "remember-me" {:target {:checked true}})
;; For a select
(change! app "country-select" {:target {:value "US"}})
```(checked? app element-id)(checked? app element-id frame-index)Check if a checkbox or radio input is checked.
With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent).
Example:
(is (checked? app "remember-me"))
Check if a checkbox or radio input is checked. With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent). Example: ```clojure (is (checked? app "remember-me")) ```
(classes-of app element-id)(classes-of app element-id frame-index)Get the CSS classes of an element by ID as a set.
With 2 args: From the most recent frame. With 3 args: From the nth frame (0 = most recent).
Returns a set of class names, or nil if element not found.
Example:
(classes-of app "submit-btn")
;; => #{"btn" "btn-primary" "loading"}
Get the CSS classes of an element by ID as a set.
With 2 args: From the most recent frame.
With 3 args: From the nth frame (0 = most recent).
Returns a set of class names, or nil if element not found.
Example:
```clojure
(classes-of app "submit-btn")
;; => #{"btn" "btn-primary" "loading"}
```(clear-captured-events! app)Clear all captured transactions and network events.
Useful for isolating assertions between test phases.
Example:
;; Phase 1
(click! app "load-btn")
(is (= 1 (count (captured-transactions app))))
;; Clear for phase 2
(clear-captured-events! app)
;; Phase 2
(click! app "save-btn")
(is (= 1 (count (captured-transactions app))))
Clear all captured transactions and network events. Useful for isolating assertions between test phases. Example: ```clojure ;; Phase 1 (click! app "load-btn") (is (= 1 (count (captured-transactions app)))) ;; Clear for phase 2 (clear-captured-events! app) ;; Phase 2 (click! app "save-btn") (is (= 1 (count (captured-transactions app)))) ```
(clear-render-history! app)Clear all captured render frames.
Clear all captured render frames.
(click! app element-id)Click on an element by ID in the most recent render frame. Invokes the element's :onClick handler if present.
Example:
(click! app "submit-btn")
Click on an element by ID in the most recent render frame. Invokes the element's :onClick handler if present. Example: ```clojure (click! app "submit-btn") ```
(click-on-text! app pattern)(click-on-text! app pattern n)Click on an element by its text content in the most recent render frame. Uses event bubbling semantics: if the matched element doesn't have an onClick handler, walks up the ancestor chain to find one.
Pattern can be:
The optional n parameter specifies which match to click (0-indexed, default 0).
Also checks :text and :label attributes on React component elements.
Example:
(click-on-text! app "View All")
(click-on-text! app "New" 1) ; click second 'New' link
(click-on-text! app #"Logout")
(click-on-text! app ["Account" "View"]) ; element containing both
Click on an element by its text content in the most recent render frame. Uses event bubbling semantics: if the matched element doesn't have an onClick handler, walks up the ancestor chain to find one. Pattern can be: - A string (substring match) - A regex (pattern match) - A vector of patterns (all must match) The optional `n` parameter specifies which match to click (0-indexed, default 0). Also checks :text and :label attributes on React component elements. Example: ```clojure (click-on-text! app "View All") (click-on-text! app "New" 1) ; click second 'New' link (click-on-text! app #"Logout") (click-on-text! app ["Account" "View"]) ; element containing both ```
(count-elements app pred)(count-elements app pred frame-index)Count elements matching a predicate in the rendered hiccup.
With 2 args: Counts in the most recent frame. With 3 args: Counts in the nth frame (0 = most recent).
Example:
;; Count all list items
(is (= 5 (count-elements app #(= :li (hic/element-tag %)))))
;; Count error messages
(is (= 0 (count-elements app #(hic/has-class? % "error"))))
Count elements matching a predicate in the rendered hiccup. With 2 args: Counts in the most recent frame. With 3 args: Counts in the nth frame (0 = most recent). Example: ```clojure ;; Count all list items (is (= 5 (count-elements app #(= :li (hic/element-tag %))))) ;; Count error messages (is (= 0 (count-elements app #(hic/has-class? % "error")))) ```
(current-hiccup mounted)Get the current hiccup from a mounted component. This reflects the latest render after any state changes.
Get the current hiccup from a mounted component. This reflects the latest render after any state changes.
(current-props app component-class ident)Get denormalized props for a component at the given ident. Useful for inspecting component state in tests.
Get denormalized props for a component at the given ident. Useful for inspecting component state in tests.
(disabled? app element-id)(disabled? app element-id frame-index)Check if an element is disabled.
With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent).
Example:
(is (disabled? app "submit-btn"))
Check if an element is disabled. With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent). Example: ```clojure (is (disabled? app "submit-btn")) ```
(element app element-id)(element app element-id frame-index)Find an element by ID in the rendered hiccup.
With 2 args: Searches the most recent frame. With 3 args: Searches the nth frame (0 = most recent).
Returns the hiccup element or nil if not found.
Example:
(element app "submit-btn")
;; => [:button {:id "submit-btn" :onClick #fn} "Submit"]
;; Check element from 2 frames ago
(element app "submit-btn" 2)
Find an element by ID in the rendered hiccup.
With 2 args: Searches the most recent frame.
With 3 args: Searches the nth frame (0 = most recent).
Returns the hiccup element or nil if not found.
Example:
```clojure
(element app "submit-btn")
;; => [:button {:id "submit-btn" :onClick #fn} "Submit"]
;; Check element from 2 frames ago
(element app "submit-btn" 2)
```(elements-by-class app class-name)(elements-by-class app class-name frame-index)Find all elements with a CSS class in the rendered hiccup.
With 2 args: Searches the most recent frame. With 3 args: Searches the nth frame (0 = most recent).
Returns a vector of matching elements.
Example:
(elements-by-class app "error-message")
;; => [[:div {:className "error-message"} "Invalid email"]]
Find all elements with a CSS class in the rendered hiccup.
With 2 args: Searches the most recent frame.
With 3 args: Searches the nth frame (0 = most recent).
Returns a vector of matching elements.
Example:
```clojure
(elements-by-class app "error-message")
;; => [[:div {:className "error-message"} "Invalid email"]]
```(elements-by-tag app tag)(elements-by-tag app tag frame-index)Find all elements with a specific tag in the rendered hiccup.
With 2 args: Searches the most recent frame. With 3 args: Searches the nth frame (0 = most recent).
Returns a vector of matching elements.
Example:
(elements-by-tag app :button)
;; => [[:button {...} "Save"] [:button {...} "Cancel"]]
Find all elements with a specific tag in the rendered hiccup.
With 2 args: Searches the most recent frame.
With 3 args: Searches the nth frame (0 = most recent).
Returns a vector of matching elements.
Example:
```clojure
(elements-by-tag app :button)
;; => [[:button {...} "Save"] [:button {...} "Cancel"]]
```(exists? app element-id)(exists? app element-id frame-index)Check if an element with the given ID exists in the rendered hiccup.
With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent).
Example:
(is (exists? app "welcome-message"))
(is (not (exists? app "error-message")))
Check if an element with the given ID exists in the rendered hiccup. With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent). Example: ```clojure (is (exists? app "welcome-message")) (is (not (exists? app "error-message"))) ```
(find-by-text app pattern)(find-by-text app pattern frame-index)Find all elements whose text content matches the pattern.
With 2 args: Searches the most recent frame. With 3 args: Searches the nth frame (0 = most recent).
Pattern can be:
Returns a vector of matching hiccup elements.
Example:
(find-by-text app "Submit")
(find-by-text app #"Account.*")
Find all elements whose text content matches the pattern. With 2 args: Searches the most recent frame. With 3 args: Searches the nth frame (0 = most recent). Pattern can be: - A string (substring match) - A regex (pattern match) - A vector of patterns (all must match) Returns a vector of matching hiccup elements. Example: ```clojure (find-by-text app "Submit") (find-by-text app #"Account.*") ```
(frame-at app frame-index)Get a specific render frame by index.
frame-index is 0-indexed from most recent (0 = latest).
Get a specific render frame by index. `frame-index` is 0-indexed from most recent (0 = latest).
(frames-since app timestamp)Get all frames captured since the given timestamp.
Get all frames captured since the given timestamp.
(has-class? app element-id class-name)(has-class? app element-id class-name frame-index)Check if an element has a specific CSS class.
With 3 args: Checks the most recent frame. With 4 args: Checks the nth frame (0 = most recent).
Example:
(is (has-class? app "submit-btn" "loading"))
(is (not (has-class? app "submit-btn" "disabled")))
Check if an element has a specific CSS class. With 3 args: Checks the most recent frame. With 4 args: Checks the nth frame (0 = most recent). Example: ```clojure (is (has-class? app "submit-btn" "loading")) (is (not (has-class? app "submit-btn" "disabled"))) ```
(has-pending-work? app)Returns true if there is any pending transaction work.
Returns true if there is any pending transaction work.
(hiccup-for app component-class)(hiccup-for app component-class ident)Return the hiccup for the given component based on the app current state. You MUST supply
the ident unless the component has a constant ident (or is Root).
Return the hiccup for the given component based on the `app` current state. You MUST supply the ident unless the component has a constant ident (or is Root).
(hiccup-frame app)(hiccup-frame app n-steps-ago)Get the rendered output as hiccup from a render frame. Convenience function that converts the dom-server Element tree to hiccup.
With 1 arg: Returns hiccup from the most recent frame. With 2 args: Returns hiccup from the nth most recent frame (0 = latest).
Example:
;; Get most recent render as hiccup
(hiccup-frame app)
;; Get the render from 2 frames ago
(hiccup-frame app 2)
Get the rendered output as hiccup from a render frame. Convenience function that converts the dom-server Element tree to hiccup. With 1 arg: Returns hiccup from the most recent frame. With 2 args: Returns hiccup from the nth most recent frame (0 = latest). Example: ```clojure ;; Get most recent render as hiccup (hiccup-frame app) ;; Get the render from 2 frames ago (hiccup-frame app 2) ```
(input-value app element-id)(input-value app element-id frame-index)Get the current value of an input element by ID. Shorthand for (attr-of app id :value).
With 2 args: From the most recent frame. With 3 args: From the nth frame (0 = most recent).
Example:
(is (= "user@example.com" (input-value app "email-input")))
Get the current value of an input element by ID. Shorthand for (attr-of app id :value). With 2 args: From the most recent frame. With 3 args: From the nth frame (0 = most recent). Example: ```clojure (is (= "user@example.com" (input-value app "email-input"))) ```
(invoke-handler! app element-id handler-key event)Invoke an arbitrary handler on an element by ID. Use this for handlers not covered by the convenience functions.
Example:
(invoke-handler! app "my-element" :onMouseEnter {})
(invoke-handler! app "draggable" :onDragStart {:dataTransfer ...})
Invoke an arbitrary handler on an element by ID.
Use this for handlers not covered by the convenience functions.
Example:
```clojure
(invoke-handler! app "my-element" :onMouseEnter {})
(invoke-handler! app "draggable" :onDragStart {:dataTransfer ...})
```(last-frame app)Get the most recent render frame, or nil if none.
Get the most recent render frame, or nil if none.
(last-transaction app)Get the most recently captured transaction, or nil if none.
Convenience for the common case of checking what just happened.
Example:
(click! app "submit-btn")
(is (= 'my-ns/submit (-> (last-transaction app) :fulcro.history/tx first)))
Get the most recently captured transaction, or nil if none. Convenience for the common case of checking what just happened. Example: ```clojure (click! app "submit-btn") (is (= 'my-ns/submit (-> (last-transaction app) :fulcro.history/tx first))) ```
(mount-component ctx factory initial-props)Mount a component for testing with automatic re-render on state changes. Returns a MountedComponent handle that tracks the current hiccup.
Unlike render-component, this sets up a render callback so that when
a state setter (from useState) is called, the component automatically
re-renders. This makes tests more natural - you interact with the component
and the hiccup updates automatically.
ctx - A rendering context from hooks/make-rendering-context
factory - The component factory function (e.g., ui-counter)
initial-props - The initial props to pass to the factory
Returns a MountedComponent record. Use current-hiccup to get the latest render.
Example:
(let [ctx (hooks/make-rendering-context)
mounted (h/mount-component ctx ui-counter {:label "Count:"})]
;; Check initial render
(is (= "0" (hic/element-text (hic/find-by-id (h/current-hiccup mounted) "count"))))
;; Click increment - re-renders automatically!
(hic/click! (hic/find-by-id (h/current-hiccup mounted) "inc"))
;; State is updated
(is (= "1" (hic/element-text (hic/find-by-id (h/current-hiccup mounted) "count")))))
Mount a component for testing with automatic re-render on state changes.
Returns a MountedComponent handle that tracks the current hiccup.
Unlike `render-component`, this sets up a render callback so that when
a state setter (from useState) is called, the component automatically
re-renders. This makes tests more natural - you interact with the component
and the hiccup updates automatically.
`ctx` - A rendering context from `hooks/make-rendering-context`
`factory` - The component factory function (e.g., ui-counter)
`initial-props` - The initial props to pass to the factory
Returns a MountedComponent record. Use `current-hiccup` to get the latest render.
Example:
```clojure
(let [ctx (hooks/make-rendering-context)
mounted (h/mount-component ctx ui-counter {:label "Count:"})]
;; Check initial render
(is (= "0" (hic/element-text (hic/find-by-id (h/current-hiccup mounted) "count"))))
;; Click increment - re-renders automatically!
(hic/click! (hic/find-by-id (h/current-hiccup mounted) "inc"))
;; State is updated
(is (= "1" (hic/element-text (hic/find-by-id (h/current-hiccup mounted) "count")))))
```(pending-sends app remote-name)Get the pending send queue for a remote. Useful for verifying what would be sent to the server.
Get the pending send queue for a remote. Useful for verifying what would be sent to the server.
(render-component ctx component-instance)Render a component instance (result of factory call) with hook support. Works with either a full headless app or a lightweight rendering context.
ctx - Either a headless app from build-test-app or a rendering context from
hooks/make-rendering-context
component-instance - The result of calling a component factory, e.g., (ui-counter {:count 0})
Returns the dom-server Element tree. Use headless.hiccup/rendered-tree->hiccup to convert
to hiccup for assertions.
Example with rendering context (isolated component testing):
(let [ctx (hooks/make-rendering-context)
result (h/render-component ctx (ui-counter {:count 0}))]
(is (= [:div ...] (hic/rendered-tree->hiccup result))))
Example with headless app:
(let [app (h/build-test-app {:root-class Root})
result (h/render-component app (ui-widget {:id 1}))]
...)
Render a component instance (result of factory call) with hook support.
Works with either a full headless app or a lightweight rendering context.
`ctx` - Either a headless app from `build-test-app` or a rendering context from
`hooks/make-rendering-context`
`component-instance` - The result of calling a component factory, e.g., (ui-counter {:count 0})
Returns the dom-server Element tree. Use `headless.hiccup/rendered-tree->hiccup` to convert
to hiccup for assertions.
Example with rendering context (isolated component testing):
```clojure
(let [ctx (hooks/make-rendering-context)
result (h/render-component ctx (ui-counter {:count 0}))]
(is (= [:div ...] (hic/rendered-tree->hiccup result))))
```
Example with headless app:
```clojure
(let [app (h/build-test-app {:root-class Root})
result (h/render-component app (ui-widget {:id 1}))]
...)
```(render-component-hiccup ctx component-instance)Convenience function: render a component instance and return hiccup directly.
ctx - Either a headless app or a rendering context from hooks/make-rendering-context
component-instance - The result of calling a component factory
Example:
(let [ctx (hooks/make-rendering-context)
hiccup (h/render-component-hiccup ctx (ui-counter {:label "Count:"}))]
(is (= "Count:" (hic/element-text (hic/find-by-id hiccup "label")))))
Convenience function: render a component instance and return hiccup directly.
`ctx` - Either a headless app or a rendering context from `hooks/make-rendering-context`
`component-instance` - The result of calling a component factory
Example:
```clojure
(let [ctx (hooks/make-rendering-context)
hiccup (h/render-component-hiccup ctx (ui-counter {:label "Count:"}))]
(is (= "Count:" (hic/element-text (hic/find-by-id hiccup "label")))))
```(render-frame! app)Force a render and capture the frame in history. Returns the captured frame.
Force a render and capture the frame in history. Returns the captured frame.
(render-history app)Get the render history (newest first). Each entry is a map with:
Get the render history (newest first). Each entry is a map with: - :state - The state map at render time - :tree - The denormalized props tree - :rendered - The raw dom-server Element tree - :timestamp - System time in milliseconds
(rendered-at app frame-index)Get the raw dom-server Element tree from a specific render frame.
frame-index is 0-indexed from most recent (0 = latest).
Use headless.hiccup/rendered-tree->hiccup to convert to hiccup.
Get the raw dom-server Element tree from a specific render frame. `frame-index` is 0-indexed from most recent (0 = latest). Use headless.hiccup/rendered-tree->hiccup to convert to hiccup.
(reset-app! app)Reset the app state to initial state from root component. Useful for test isolation.
Reset the app state to initial state from root component. Useful for test isolation.
(set-props! mounted new-props)Set new props on a mounted component and re-render.
Set new props on a mounted component and re-render.
(set-remote! app remote-name remote)Set or replace a remote on the test app. Convenience wrapper around rapp/set-remote!
Set or replace a remote on the test app. Convenience wrapper around rapp/set-remote!
(state-at-render app frame-index)Get the state map from a specific render frame.
frame-index is 0-indexed from most recent (0 = latest).
Get the state map from a specific render frame. `frame-index` is 0-indexed from most recent (0 = latest).
(text-exists? app pattern)(text-exists? app pattern frame-index)Check if any element with the given text content exists in the rendered hiccup.
With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent).
Example:
(is (text-exists? app "Welcome, John!"))
(is (not (text-exists? app "Error")))
Check if any element with the given text content exists in the rendered hiccup. With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent). Example: ```clojure (is (text-exists? app "Welcome, John!")) (is (not (text-exists? app "Error"))) ```
(text-of app element-id)(text-of app element-id frame-index)Get the text content of an element by ID.
With 2 args: From the most recent frame. With 3 args: From the nth frame (0 = most recent).
Returns the concatenated text content, or nil if element not found.
Example:
(text-of app "welcome-message")
;; => "Hello, John!"
;; Compare text before and after
(let [before (text-of app "counter" 1)
after (text-of app "counter" 0)]
(is (not= before after)))
Get the text content of an element by ID.
With 2 args: From the most recent frame.
With 3 args: From the nth frame (0 = most recent).
Returns the concatenated text content, or nil if element not found.
Example:
```clojure
(text-of app "welcome-message")
;; => "Hello, John!"
;; Compare text before and after
(let [before (text-of app "counter" 1)
after (text-of app "counter" 0)]
(is (not= before after)))
```(transaction-mutations app)Extract just the mutation symbols from captured transactions.
Example:
(click! app "btn1")
(click! app "btn2")
(is (= ['ns/action1 'ns/action2] (transaction-mutations app)))
Extract just the mutation symbols from captured transactions. Example: ```clojure (click! app "btn1") (click! app "btn2") (is (= ['ns/action1 'ns/action2] (transaction-mutations app))) ```
(tree-at-render app frame-index)Get the denormalized tree from a specific render frame.
frame-index is 0-indexed from most recent (0 = latest).
Get the denormalized tree from a specific render frame. `frame-index` is 0-indexed from most recent (0 = latest).
(type-into! app element-id new-value)Type a value into an input element by ID. Invokes the element's :onChange handler with {:target {:value new-value}}.
Example:
(type-into! app "email-input" "user@example.com")
Type a value into an input element by ID.
Invokes the element's :onChange handler with {:target {:value new-value}}.
Example:
```clojure
(type-into! app "email-input" "user@example.com")
```(type-into-labeled! app label-pattern value)(type-into-labeled! app label-pattern value n)Type a value into an input field identified by its label. Finds an input element associated with a label matching the pattern, then invokes the input's :onChange handler.
Pattern can be:
The optional n parameter specifies which matching labeled field to use
(0-indexed, default 0).
Example:
(type-into-labeled! app "Username" "john.doe")
(type-into-labeled! app "Password" "secret123")
(type-into-labeled! app #"(?i)email" "user@example.com")
(type-into-labeled! app "Amount" "100.00" 1) ; second Amount field
Type a value into an input field identified by its label. Finds an input element associated with a label matching the pattern, then invokes the input's :onChange handler. Pattern can be: - A string (substring match on label text) - A regex (pattern match) - A vector of patterns (all must match) The optional `n` parameter specifies which matching labeled field to use (0-indexed, default 0). Example: ```clojure (type-into-labeled! app "Username" "john.doe") (type-into-labeled! app "Password" "secret123") (type-into-labeled! app #"(?i)email" "user@example.com") (type-into-labeled! app "Amount" "100.00" 1) ; second Amount field ```
(unmount-component! mounted)Unmount a component, running any cleanup effects. Clears the render callback and hook state.
Unmount a component, running any cleanup effects. Clears the render callback and hook state.
(update-props! mounted props-fn)Update the props of a mounted component and re-render. The props-fn receives the current props and should return the new props.
Update the props of a mounted component and re-render. The props-fn receives the current props and should return the new props.
(visible? app element-id)(visible? app element-id frame-index)Check if an element exists and is not hidden via inline style.
Note: This only checks inline display: none or visibility: hidden.
CSS classes that hide elements are not detected.
With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent).
Example:
(is (visible? app "modal"))
Check if an element exists and is not hidden via inline style. Note: This only checks inline `display: none` or `visibility: hidden`. CSS classes that hide elements are not detected. With 2 args: Checks the most recent frame. With 3 args: Checks the nth frame (0 = most recent). Example: ```clojure (is (visible? app "modal")) ```
(wait-for-idle! app)Block until all pending work is complete. With synchronous tx processing, this is usually instant, but useful after operations that might queue follow-on work.
Block until all pending work is complete. With synchronous tx processing, this is usually instant, but useful after operations that might queue follow-on work.
(with-render-tracking app body-fn)Execute body and return a map with:
Example:
(let [{:keys [result frames]} (with-render-tracking app
(comp/transact! app [(my-mutation)]))]
(is (= 1 (count frames))))
Execute body and return a map with:
- :result - The return value of body
- :frames - All frames captured during execution
- :frame-count - Number of frames captured
Example:
```clojure
(let [{:keys [result frames]} (with-render-tracking app
(comp/transact! app [(my-mutation)]))]
(is (= 1 (count frames))))
```cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |