Class: Dommy::Interaction::FieldInteractor
- Inherits:
-
Object
- Object
- Dommy::Interaction::FieldInteractor
- Defined in:
- lib/dommy/interaction/field_interactor.rb
Overview
Drives form fields: fills text inputs, toggles radios / checkboxes, selects options, attaches files. Mutates the live Dommy elements AND fires the browser's input/change (and focus) events so JS handlers (React onChange, Stimulus, …) react. The value is written directly on the element — the React "native setter" path — so a framework's value tracker sees the change rather than swallowing it.
Constant Summary collapse
- MIME_TYPES =
Minimal extension → MIME map for attach_file (core stays Rack-free).
{ ".txt" => "text/plain", ".html" => "text/html", ".htm" => "text/html", ".json" => "application/json", ".csv" => "text/csv", ".xml" => "application/xml", ".png" => "image/png", ".jpg" => "image/jpeg", ".jpeg" => "image/jpeg", ".gif" => "image/gif", ".pdf" => "application/pdf" }.freeze
- TEXT_ENTRY_INPUT_TYPES =
%w[text search email url tel password number].freeze
Instance Method Summary collapse
- #attach_file(locator, path) ⇒ Object
-
#backspace(element) ⇒ Object
Delete the last character of a text-entry field's value.
-
#cancel_composition_text(element, base) ⇒ Object
Cancel a composition: restore the field's pre-composition value and fire a deleteCompositionText input.
- #check(locator) ⇒ Object
- #choose(locator) ⇒ Object
- #fill_in(locator, with:) ⇒ Object
-
#initialize(finder, document) ⇒ FieldInteractor
constructor
A new instance of FieldInteractor.
-
#insert_text(element, text) ⇒ Object
Append
textto a text-entry field's value (no caret/selection model: insertion is at the end), firing cancelable beforeinput then input. - #select(value, from:) ⇒ Object
-
#set_composition_text(element, base, update) ⇒ Object
Show composition state
updatein a text-entry field (value becomes base + update) with the insertCompositionText beforeinput/input pair. - #text_entry_field?(element) ⇒ Boolean
- #uncheck(locator) ⇒ Object
- #unselect(value, from:) ⇒ Object
Constructor Details
#initialize(finder, document) ⇒ FieldInteractor
Returns a new instance of FieldInteractor.
20 21 22 23 |
# File 'lib/dommy/interaction/field_interactor.rb', line 20 def initialize(finder, document) @finder = finder @document = document end |
Instance Method Details
#attach_file(locator, path) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/dommy/interaction/field_interactor.rb', line 51 def attach_file(locator, path) input = @finder.find_field(locator) raise FileNotFoundError, "no such file: #{path}" unless ::File.exist?(path) file = Dommy::File.new( [::File.binread(path)], ::File.basename(path), "type" => mime_type_for(path) ) input.__driver_set_files__([file]) EventSynthesis.change(input) input end |
#backspace(element) ⇒ Object
Delete the last character of a text-entry field's value. Same return contract as #insert_text.
112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/dommy/interaction/field_interactor.rb', line 112 def backspace(element) return false unless text_entry_field?(element) value = element.value.to_s return false if value.empty? return false if EventSynthesis.beforeinput(element, nil, "deleteContentBackward") element.value = value[0...-1] EventSynthesis.input(element, nil, "deleteContentBackward") true end |
#cancel_composition_text(element, base) ⇒ Object
Cancel a composition: restore the field's pre-composition value and fire a deleteCompositionText input. Same return contract.
138 139 140 141 142 143 144 |
# File 'lib/dommy/interaction/field_interactor.rb', line 138 def cancel_composition_text(element, base) return false unless text_entry_field?(element) element.value = base EventSynthesis.input(element, nil, "deleteCompositionText") true end |
#check(locator) ⇒ Object
43 44 45 |
# File 'lib/dommy/interaction/field_interactor.rb', line 43 def check(locator) toggle(@finder.find_field(locator), true) end |
#choose(locator) ⇒ Object
34 35 36 37 38 39 40 41 |
# File 'lib/dommy/interaction/field_interactor.rb', line 34 def choose(locator) radio = @finder.find_field(locator) clear_radio_group(radio) radio.checked = true EventSynthesis.input(radio) EventSynthesis.change(radio) radio end |
#fill_in(locator, with:) ⇒ Object
25 26 27 28 29 30 31 32 |
# File 'lib/dommy/interaction/field_interactor.rb', line 25 def fill_in(locator, with:) field = @finder.find_field(locator) EventSynthesis.focus(field) field.value = with.to_s EventSynthesis.input(field, with.to_s) EventSynthesis.change(field) field end |
#insert_text(element, text) ⇒ Object
Append text to a text-entry field's value (no caret/selection model:
insertion is at the end), firing cancelable beforeinput then input.
Returns whether the field accepted the insertion (false for a
non-text-entry field or a prevented beforeinput) — used by Driver's
send_keys to decide whether a key's default action ran.
101 102 103 104 105 106 107 108 |
# File 'lib/dommy/interaction/field_interactor.rb', line 101 def insert_text(element, text) return false unless text_entry_field?(element) return false if EventSynthesis.beforeinput(element, text, "insertText") element.value = element.value.to_s + text EventSynthesis.input(element, text) true end |
#select(value, from:) ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/dommy/interaction/field_interactor.rb', line 63 def select(value, from:) select_el = @finder.find_field(from) option = @finder.find_option(select_el, value) raise ElementNotFoundError, "no option #{value.inspect} in #{from.inspect}" unless option select_el..each { |o| o.remove_attribute("selected") } unless select_el.multiple option.set_attribute("selected", "") EventSynthesis.input(select_el) EventSynthesis.change(select_el) select_el end |
#set_composition_text(element, base, update) ⇒ Object
Show composition state update in a text-entry field (value becomes
base + update) with the insertCompositionText beforeinput/input pair.
No beforeinput veto: composition inputs are not cancelable (spec).
Same return contract as #insert_text.
128 129 130 131 132 133 134 |
# File 'lib/dommy/interaction/field_interactor.rb', line 128 def set_composition_text(element, base, update) return false unless text_entry_field?(element) element.value = base + update EventSynthesis.composition_input(element, update) true end |
#text_entry_field?(element) ⇒ Boolean
88 89 90 91 92 93 94 |
# File 'lib/dommy/interaction/field_interactor.rb', line 88 def text_entry_field?(element) return true if element.local_name == "textarea" return false unless element.local_name == "input" type = (element.get_attribute("type") || "text").downcase TEXT_ENTRY_INPUT_TYPES.include?(type) end |
#uncheck(locator) ⇒ Object
47 48 49 |
# File 'lib/dommy/interaction/field_interactor.rb', line 47 def uncheck(locator) toggle(@finder.find_field(locator), false) end |
#unselect(value, from:) ⇒ Object
75 76 77 78 79 80 81 82 83 84 |
# File 'lib/dommy/interaction/field_interactor.rb', line 75 def unselect(value, from:) select_el = @finder.find_field(from) option = @finder.find_option(select_el, value) return select_el unless option option.remove_attribute("selected") EventSynthesis.input(select_el) EventSynthesis.change(select_el) select_el end |