Module: Plushie::Test::Helpers
- Included in:
- Case
- Defined in:
- lib/plushie/test/helpers.rb,
lib/plushie/test/helpers.rb
Overview
Test helper methods for interacting with a Plushie test session.
Include this module in your test class to get click, find!, assert_text, and other helpers. The session is stored in Thread.current.
Constant Summary collapse
- PLACEHOLDER_A11Y_WIDGETS =
%w[text_input text_editor combo_box pick_list].freeze
- ALT_A11Y_WIDGETS =
%w[image svg qr_code].freeze
Class Method Summary collapse
- .infer_a11y(type, props) ⇒ Object
-
.resolve_a11y_for_element(element) ⇒ Hash
Apply widget-sdk-equivalent a11y inference on top of the normalized
a11yprop. - .symbolize_keys(map) ⇒ Object
Instance Method Summary collapse
-
#advance_frame(timestamp) ⇒ Object
Advance the renderer's animation clock to the given timestamp.
-
#assert_a11y(selector, expected) ⇒ Object
Assert that a widget's resolved a11y matches expected values.
-
#assert_exists(selector) ⇒ Object
Assert that a widget exists.
-
#assert_model(expected) ⇒ Object
Assert model equals expected value.
-
#assert_no_diagnostics ⇒ Object
Assert that no diagnostics have been emitted by the renderer.
-
#assert_not_exists(selector) ⇒ Object
Assert that a widget does NOT exist.
-
#assert_text(selector, expected) ⇒ Object
Assert that a widget contains the expected text.
-
#await_async(tag, timeout = 5000) ⇒ :ok
Wait for a tagged async task to complete.
-
#click(selector) ⇒ Object
Click a button widget.
-
#click_element(canvas_id, element_id) ⇒ Object
Click a canvas element by injecting a synthetic canvas_element_click event.
-
#find(selector) ⇒ Hash?
Find a widget by selector.
-
#find!(selector) ⇒ Hash
Find a widget by selector.
-
#find_by_label(label) ⇒ Hash?
Find a widget by accessibility label.
-
#find_by_role(role) ⇒ Hash?
Find a widget by accessibility role.
-
#find_focused ⇒ Hash?
Find the currently focused widget.
-
#focus_element(canvas_id, element_id) ⇒ Object
Focus a canvas element via scoped path.
-
#model ⇒ Object
Current app model.
-
#move_to(x, y) ⇒ Object
Move cursor to coordinates.
-
#plushie_start(app_class, **opts) ⇒ Object
Start a test session manually.
-
#plushie_stop ⇒ Object
Stop the current test session.
-
#press(key) ⇒ Object
Press a key (key down).
-
#register_effect_stub(kind, response) ⇒ Object
Register an effect stub with the renderer.
-
#release(key) ⇒ Object
Release a key (key up).
-
#reset ⇒ Object
Reset the session to initial state.
-
#resolved_a11y(selector) ⇒ Hash
Return the resolved a11y hash for a widget.
-
#save_screenshot(name, **opts) ⇒ Hash
Capture a screenshot and save as PNG to test/screenshots/.
-
#screenshot(name, **opts) ⇒ Hash
Capture a screenshot.
-
#select(selector, value) ⇒ Object
Select a value from pick_list, combo_box, or radio.
-
#session ⇒ Session
The current test session.
-
#skip_transitions ⇒ Object
Skip all active renderer-side transitions to completion.
-
#slide(selector, value) ⇒ Object
Slide a slider to a value.
-
#submit(selector) ⇒ Object
Submit a text_input (press Enter).
-
#text(element) ⇒ String?
Extract text content from an element hash.
-
#toggle(selector) ⇒ Object
Toggle a checkbox or toggler.
-
#tree ⇒ Hash
Current tree from the renderer.
-
#tree_hash(name) ⇒ Hash
Capture a structural tree hash.
-
#type_key(key) ⇒ Object
Type a key (press + release).
-
#type_text(selector, text) ⇒ Object
Type text into a text_input or text_editor.
-
#unregister_effect_stub(kind) ⇒ Object
Remove a previously registered effect stub.
Class Method Details
.infer_a11y(type, props) ⇒ Object
355 356 357 358 359 360 361 362 363 364 |
# File 'lib/plushie/test/helpers.rb', line 355 def self.infer_a11y(type, props) if PLACEHOLDER_A11Y_WIDGETS.include?(type) ph = props[:placeholder] || props["placeholder"] return {description: ph} if ph.is_a?(String) && !ph.empty? elsif ALT_A11Y_WIDGETS.include?(type) alt = props[:alt] || props["alt"] return {label: alt} if alt.is_a?(String) && !alt.empty? end {} end |
.resolve_a11y_for_element(element) ⇒ Hash
Apply widget-sdk-equivalent a11y inference on top of the
normalized a11y prop. Kept aligned with the Rust SDK's
resolve_a11y_for_node so cross-SDK parity holds.
347 348 349 350 351 352 353 |
# File 'lib/plushie/test/helpers.rb', line 347 def self.resolve_a11y_for_element(element) type = (element[:type] || element["type"]).to_s props = element[:props] || element["props"] || {} explicit = symbolize_keys(props[:a11y] || props["a11y"] || {}) inferred = infer_a11y(type, props) inferred.merge(explicit) end |
.symbolize_keys(map) ⇒ Object
366 367 368 369 370 371 |
# File 'lib/plushie/test/helpers.rb', line 366 def self.symbolize_keys(map) return {} unless map.is_a?(Hash) out = {} map.each { |k, v| out[k.is_a?(String) ? k.to_sym : k] = v } out end |
Instance Method Details
#advance_frame(timestamp) ⇒ Object
Advance the renderer's animation clock to the given timestamp. Causes the renderer to evaluate all active animations at that point in time, potentially triggering transition_complete events.
209 210 211 |
# File 'lib/plushie/test/helpers.rb', line 209 def advance_frame() session.command(Command.advance_frame()) end |
#assert_a11y(selector, expected) ⇒ Object
Assert that a widget's resolved a11y matches expected values. Reads through #resolved_a11y so inferred defaults compose with the author's explicit overrides.
276 277 278 279 280 281 282 283 |
# File 'lib/plushie/test/helpers.rb', line 276 def assert_a11y(selector, expected) a11y = resolved_a11y(selector) expected.each do |key, value| actual = a11y[key] || a11y[key.to_s] plushie_assert_equal value, actual, "a11y #{key.inspect} mismatch for #{selector}\nFull a11y: #{a11y.inspect}" end end |
#assert_exists(selector) ⇒ Object
Assert that a widget exists.
236 237 238 239 |
# File 'lib/plushie/test/helpers.rb', line 236 def assert_exists(selector) result = find(selector) plushie_assert result, "Expected widget #{selector} to exist, but it was not found" end |
#assert_model(expected) ⇒ Object
Assert model equals expected value.
250 251 252 |
# File 'lib/plushie/test/helpers.rb', line 250 def assert_model(expected) plushie_assert_equal expected, model end |
#assert_no_diagnostics ⇒ Object
Assert that no diagnostics have been emitted by the renderer. Clears the diagnostic list after checking.
161 162 163 164 165 166 167 |
# File 'lib/plushie/test/helpers.rb', line 161 def assert_no_diagnostics diagnostics = session.get_diagnostics return if diagnostics.empty? details = diagnostics.map { |d| " - #{d.diagnostic.inspect}" }.join("\n") plushie_flunk "Expected no diagnostics, but found:\n#{details}" end |
#assert_not_exists(selector) ⇒ Object
Assert that a widget does NOT exist.
243 244 245 246 |
# File 'lib/plushie/test/helpers.rb', line 243 def assert_not_exists(selector) result = find(selector) plushie_assert_nil result, "Expected widget #{selector} not to exist, but it was found" end |
#assert_text(selector, expected) ⇒ Object
Assert that a widget contains the expected text.
227 228 229 230 231 232 |
# File 'lib/plushie/test/helpers.rb', line 227 def assert_text(selector, expected) element = find!(selector) actual = text(element) plushie_assert_equal expected, actual, "Expected text #{expected.inspect} for #{selector}, got #{actual.inspect}" end |
#await_async(tag, timeout = 5000) ⇒ :ok
Wait for a tagged async task to complete. Async commands run synchronously on the mock backend, so this returns immediately after warning that there is nothing to wait for. Other test backends also currently complete async work before this method is needed, but do not warn.
132 133 134 135 136 137 138 |
# File 'lib/plushie/test/helpers.rb', line 132 def await_async(tag, timeout = 5000) if Plushie::Test.respond_to?(:backend) && Plushie::Test.backend == :mock warn "Plushie::Test#await_async is a no-op on the mock backend; async commands run synchronously." end :ok end |
#click(selector) ⇒ Object
Click a button widget.
32 |
# File 'lib/plushie/test/helpers.rb', line 32 def click(selector) = session.click(selector) |
#click_element(canvas_id, element_id) ⇒ Object
Click a canvas element by injecting a synthetic canvas_element_click event.
77 |
# File 'lib/plushie/test/helpers.rb', line 77 def click_element(canvas_id, element_id) = session.interact("canvas_element_click", canvas_id, element_id: element_id) |
#find(selector) ⇒ Hash?
Find a widget by selector. Returns the node hash or nil.
92 |
# File 'lib/plushie/test/helpers.rb', line 92 def find(selector) = session.find(selector) |
#find!(selector) ⇒ Hash
Find a widget by selector. Raises if not found.
97 |
# File 'lib/plushie/test/helpers.rb', line 97 def find!(selector) = session.find!(selector) |
#find_by_label(label) ⇒ Hash?
Find a widget by accessibility label.
179 180 181 |
# File 'lib/plushie/test/helpers.rb', line 179 def find_by_label(label) session.find({by: "label", value: label}) end |
#find_by_role(role) ⇒ Hash?
Find a widget by accessibility role.
172 173 174 |
# File 'lib/plushie/test/helpers.rb', line 172 def find_by_role(role) session.find({by: "role", value: role.to_s}) end |
#find_focused ⇒ Hash?
Find the currently focused widget.
185 186 187 |
# File 'lib/plushie/test/helpers.rb', line 185 def find_focused session.find({by: "focused"}) end |
#focus_element(canvas_id, element_id) ⇒ Object
Focus a canvas element via scoped path. Use Command.focus("canvas/element") for the same effect.
83 84 85 |
# File 'lib/plushie/test/helpers.rb', line 83 def focus_element(canvas_id, element_id) session.command(Command.focus("#{canvas_id}/#{element_id}")) end |
#model ⇒ Object
Returns current app model.
100 |
# File 'lib/plushie/test/helpers.rb', line 100 def model = session.model |
#move_to(x, y) ⇒ Object
Move cursor to coordinates.
72 |
# File 'lib/plushie/test/helpers.rb', line 72 def move_to(x, y) = session.move_to(x, y) |
#plushie_start(app_class, **opts) ⇒ Object
Start a test session manually.
289 290 291 292 293 |
# File 'lib/plushie/test/helpers.rb', line 289 def plushie_start(app_class, **opts) pool = Plushie::Test.pool session_id = pool.register Thread.current[:_plushie_test_session] = Session.new(app_class, pool: pool, session_id: session_id) end |
#plushie_stop ⇒ Object
Stop the current test session.
296 297 298 299 |
# File 'lib/plushie/test/helpers.rb', line 296 def plushie_stop Thread.current[:_plushie_test_session]&.stop Thread.current[:_plushie_test_session] = nil end |
#press(key) ⇒ Object
Press a key (key down).
59 |
# File 'lib/plushie/test/helpers.rb', line 59 def press(key) = session.press(key) |
#register_effect_stub(kind, response) ⇒ Object
Register an effect stub with the renderer. The renderer will return the given response immediately for any effect of the given kind.
146 147 148 |
# File 'lib/plushie/test/helpers.rb', line 146 def register_effect_stub(kind, response) session.register_effect_stub(kind.to_s, response) end |
#release(key) ⇒ Object
Release a key (key up).
63 |
# File 'lib/plushie/test/helpers.rb', line 63 def release(key) = session.release(key) |
#reset ⇒ Object
Reset the session to initial state.
121 |
# File 'lib/plushie/test/helpers.rb', line 121 def reset = session.reset |
#resolved_a11y(selector) ⇒ Hash
Return the resolved a11y hash for a widget.
Layers render-pipeline inference (placeholder -> description for
text-entry widgets, alt -> label for media widgets) on top of
the normalized a11y prop so tests see what assistive
technology will see. Normalizer-populated defaults (role,
implicit radio_group, required/validation projections, tooltip
described_by) are already carried on the tree.
265 266 267 268 |
# File 'lib/plushie/test/helpers.rb', line 265 def resolved_a11y(selector) element = find!(selector) ::Plushie::Test::Helpers.resolve_a11y_for_element(element) end |
#save_screenshot(name, **opts) ⇒ Hash
Capture a screenshot and save as PNG to test/screenshots/.
192 193 194 195 196 197 198 199 200 |
# File 'lib/plushie/test/helpers.rb', line 192 def save_screenshot(name, **opts) result = screenshot(name, **opts) if result && (rgba = result[:rgba] || result["rgba"]) dir = "test/screenshots" FileUtils.mkdir_p(dir) File.binwrite(File.join(dir, "#{name}.rgba"), rgba) end result end |
#screenshot(name, **opts) ⇒ Hash
Capture a screenshot.
118 |
# File 'lib/plushie/test/helpers.rb', line 118 def screenshot(name, **opts) = session.screenshot(name, **opts) |
#select(selector, value) ⇒ Object
Select a value from pick_list, combo_box, or radio.
50 |
# File 'lib/plushie/test/helpers.rb', line 50 def select(selector, value) = session.select(selector, value) |
#session ⇒ Session
Returns the current test session.
23 24 25 26 |
# File 'lib/plushie/test/helpers.rb', line 23 def session Thread.current[:_plushie_test_session] || raise("No Plushie test session. Use Plushie::Test::Case or call plushie_start first.") end |
#skip_transitions ⇒ Object
Skip all active renderer-side transitions to completion.
Advances the animation clock far enough to complete any reasonable animation. Triggers transition_complete events for any animations with on_complete tags.
218 219 220 |
# File 'lib/plushie/test/helpers.rb', line 218 def skip_transitions advance_frame(10_000) end |
#slide(selector, value) ⇒ Object
Slide a slider to a value.
55 |
# File 'lib/plushie/test/helpers.rb', line 55 def (selector, value) = session.(selector, value) |
#submit(selector) ⇒ Object
Submit a text_input (press Enter).
41 |
# File 'lib/plushie/test/helpers.rb', line 41 def submit(selector) = session.submit(selector) |
#text(element) ⇒ String?
Extract text content from an element hash.
108 |
# File 'lib/plushie/test/helpers.rb', line 108 def text(element) = session.element_text(element) |
#toggle(selector) ⇒ Object
Toggle a checkbox or toggler.
45 |
# File 'lib/plushie/test/helpers.rb', line 45 def toggle(selector) = session.toggle(selector) |
#tree ⇒ Hash
Returns current tree from the renderer.
103 |
# File 'lib/plushie/test/helpers.rb', line 103 def tree = session.tree |
#tree_hash(name) ⇒ Hash
Capture a structural tree hash.
113 |
# File 'lib/plushie/test/helpers.rb', line 113 def tree_hash(name) = session.tree_hash(name) |
#type_key(key) ⇒ Object
Type a key (press + release).
67 |
# File 'lib/plushie/test/helpers.rb', line 67 def type_key(key) = session.type_key(key) |
#type_text(selector, text) ⇒ Object
Type text into a text_input or text_editor.
37 |
# File 'lib/plushie/test/helpers.rb', line 37 def type_text(selector, text) = session.type_text(selector, text) |
#unregister_effect_stub(kind) ⇒ Object
Remove a previously registered effect stub.
153 154 155 |
# File 'lib/plushie/test/helpers.rb', line 153 def unregister_effect_stub(kind) session.unregister_effect_stub(kind.to_s) end |