Class: Unmagic::Browser::Element
- Inherits:
-
Object
- Object
- Unmagic::Browser::Element
- Defined in:
- lib/unmagic/browser/element.rb
Overview
Instance Attribute Summary collapse
-
#handle ⇒ Puppeteer::ElementHandle
(also: #driver)
readonly
The live Puppeteer handle, for work this API doesn't wrap.
Instance Method Summary collapse
-
#all(query = nil, **options) ⇒ Array<Element>
Find every element inside this one.
-
#attach(*paths) ⇒ self
Attach files to a file input.
-
#attribute(name) ⇒ String?
(also: #[])
The attribute's value, or nil when it isn't set.
-
#check ⇒ self
Tick a checkbox, or leave it ticked if it already is.
-
#checked? ⇒ Boolean
Whether a checkbox or radio is ticked.
-
#click ⇒ self
Click the element, scrolling it into view first.
-
#disabled? ⇒ Boolean
Whether the element is disabled.
-
#dispose ⇒ void
Release the handle.
-
#evaluate(script, *args) ⇒ Object
Run JavaScript with this element as the first argument.
-
#fill_in(value) ⇒ self
Replace whatever's in the field with
value. -
#find(query = nil, **options) ⇒ Element
Find one element inside this one.
-
#hover ⇒ self
Move the pointer over the element.
-
#html ⇒ String
The element's own HTML, tag included.
-
#initialize(session, handle) ⇒ Element
constructor
A new instance of Element.
-
#inner_html ⇒ String
The element's inner HTML.
-
#inspect ⇒ String
The element and the handle behind it.
-
#press(key) ⇒ self
Press a key with this element focused.
-
#screenshot ⇒ String
PNG bytes of just this element.
-
#select_option(option) ⇒ self
Pick an option in a
<select>by its visible label, or failing that its value — a caller reading the page sees labels, not values. -
#tag_name ⇒ String
The lowercased tag name, e.g.
-
#text ⇒ String
The element's visible text, whitespace collapsed.
-
#uncheck ⇒ self
Untick a checkbox, or leave it unticked if it already is.
-
#value ⇒ String?
The element's current value — what's typed into a field, what's selected in a
<select>, or nil for anything without one. -
#visible? ⇒ Boolean
Whether the element is rendered and not hidden.
Constructor Details
#initialize(session, handle) ⇒ Element
Returns a new instance of Element.
17 18 19 20 |
# File 'lib/unmagic/browser/element.rb', line 17 def initialize(session, handle) @session = session @handle = handle end |
Instance Attribute Details
#handle ⇒ Puppeteer::ElementHandle (readonly) Also known as: driver
The live Puppeteer handle, for work this API doesn't wrap.
25 26 27 |
# File 'lib/unmagic/browser/element.rb', line 25 def handle @handle end |
Instance Method Details
#all(query = nil, **options) ⇒ Array<Element>
Find every element inside this one. Same locator rules as Session#all.
214 215 216 |
# File 'lib/unmagic/browser/element.rb', line 214 def all(query = nil, **) @session.all(query, scope: self, **) end |
#attach(*paths) ⇒ self
Attach files to a file input.
179 180 181 |
# File 'lib/unmagic/browser/element.rb', line 179 def attach(*paths) act { @handle.upload_file(*paths.flatten.map(&:to_s)) } end |
#attribute(name) ⇒ String? Also known as: []
Returns the attribute's value, or nil when it isn't set.
59 60 61 |
# File 'lib/unmagic/browser/element.rb', line 59 def attribute(name) evaluate("(element, name) => element.getAttribute(name)", name.to_s) end |
#check ⇒ self
Tick a checkbox, or leave it ticked if it already is.
164 165 166 |
# File 'lib/unmagic/browser/element.rb', line 164 def check set_checked(true) end |
#checked? ⇒ Boolean
Returns whether a checkbox or radio is ticked.
77 78 79 |
# File 'lib/unmagic/browser/element.rb', line 77 def checked? !!evaluate("element => !!element.checked") end |
#click ⇒ self
Click the element, scrolling it into view first.
89 90 91 92 93 94 |
# File 'lib/unmagic/browser/element.rb', line 89 def click act do @handle.scroll_into_view_if_needed @handle.click end end |
#disabled? ⇒ Boolean
Returns whether the element is disabled.
82 83 84 |
# File 'lib/unmagic/browser/element.rb', line 82 def disabled? !!evaluate("element => !!element.disabled") end |
#dispose ⇒ void
This method returns an undefined value.
Release the handle. Elements found by a session are disposed with the page, so this is only worth calling in a long loop.
231 232 233 |
# File 'lib/unmagic/browser/element.rb', line 231 def dispose @session.synchronize { @handle.dispose } end |
#evaluate(script, *args) ⇒ Object
Run JavaScript with this element as the first argument.
223 224 225 |
# File 'lib/unmagic/browser/element.rb', line 223 def evaluate(script, *args) @session.synchronize { @handle.evaluate(script, *args) } end |
#fill_in(value) ⇒ self
Replace whatever's in the field with value.
The existing content is selected and deleted rather than assigned over, so frameworks that watch for real key events (React and friends) see the change.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/unmagic/browser/element.rb', line 114 def fill_in(value) act do @handle.scroll_into_view_if_needed @handle.focus @handle.evaluate(<<~JAVASCRIPT) (element) => { if (element.select) { element.select(); return; } const range = document.createRange(); range.selectNodeContents(element); const selection = getSelection(); selection.removeAllRanges(); selection.addRange(range); } JAVASCRIPT @handle.press("Backspace") @handle.type_text(value.to_s) unless value.to_s.empty? end end |
#find(query = nil, **options) ⇒ Element
Find one element inside this one. Same locator rules as Session#find.
205 206 207 |
# File 'lib/unmagic/browser/element.rb', line 205 def find(query = nil, **) @session.find(query, scope: self, **) end |
#hover ⇒ self
Move the pointer over the element.
99 100 101 102 103 104 |
# File 'lib/unmagic/browser/element.rb', line 99 def hover act do @handle.scroll_into_view_if_needed @handle.hover end end |
#html ⇒ String
Returns the element's own HTML, tag included.
35 36 37 |
# File 'lib/unmagic/browser/element.rb', line 35 def html evaluate("element => element.outerHTML") end |
#inner_html ⇒ String
Returns the element's inner HTML.
40 41 42 |
# File 'lib/unmagic/browser/element.rb', line 40 def inner_html evaluate("element => element.innerHTML") end |
#inspect ⇒ String
Returns the element and the handle behind it.
236 237 238 |
# File 'lib/unmagic/browser/element.rb', line 236 def inspect "#<#{self.class.name} #{@handle.inspect}>" end |
#press(key) ⇒ self
Press a key with this element focused.
187 188 189 |
# File 'lib/unmagic/browser/element.rb', line 187 def press(key) act { @handle.press(Session.key_name(key)) } end |
#screenshot ⇒ String
Returns PNG bytes of just this element.
192 193 194 195 196 197 |
# File 'lib/unmagic/browser/element.rb', line 192 def screenshot @session.synchronize do @handle.scroll_into_view_if_needed @handle.screenshot(type: "png") end end |
#select_option(option) ⇒ self
Pick an option in a <select> by its visible label, or failing that its
value — a caller reading the page sees labels, not values.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/unmagic/browser/element.rb', line 139 def select_option(option) act do found = @handle.evaluate(<<~JAVASCRIPT, option.to_s) (element, wanted) => { if (element.tagName.toLowerCase() !== "select") throw new Error("not a <select>"); const norm = (value) => (value || "").replace(/\\s+/g, " ").trim().toLowerCase(); const needle = norm(wanted); const options = Array.from(element.options); const match = options.find((option) => norm(option.label || option.textContent) === needle) || options.find((option) => norm(option.value) === needle) || options.find((option) => norm(option.label || option.textContent).indexOf(needle) !== -1); if (!match) return false; if (element.multiple) { match.selected = true; } else { element.value = match.value; } element.dispatchEvent(new Event("input", { bubbles: true })); element.dispatchEvent(new Event("change", { bubbles: true })); return true; } JAVASCRIPT raise Error::NotFound, "No option matching #{option.inspect} in this select." unless found end end |
#tag_name ⇒ String
Returns the lowercased tag name, e.g. "input".
45 46 47 |
# File 'lib/unmagic/browser/element.rb', line 45 def tag_name evaluate("element => element.tagName.toLowerCase()") end |
#text ⇒ String
Returns the element's visible text, whitespace collapsed.
29 30 31 32 |
# File 'lib/unmagic/browser/element.rb', line 29 def text evaluate("element => (element.innerText === undefined ? element.textContent : element.innerText)") .to_s.gsub(/\s+/, " ").strip end |
#uncheck ⇒ self
Untick a checkbox, or leave it unticked if it already is.
171 172 173 |
# File 'lib/unmagic/browser/element.rb', line 171 def uncheck set_checked(false) end |
#value ⇒ String?
The element's current value — what's typed into a field, what's selected
in a <select>, or nil for anything without one.
53 54 55 |
# File 'lib/unmagic/browser/element.rb', line 53 def value evaluate("element => ('value' in element ? element.value : null)") end |
#visible? ⇒ Boolean
Returns whether the element is rendered and not hidden.
65 66 67 68 69 70 71 72 73 74 |
# File 'lib/unmagic/browser/element.rb', line 65 def visible? evaluate(<<~JAVASCRIPT) (element) => { if (element.hidden) return false; const style = getComputedStyle(element); if (style.visibility === "hidden" || style.display === "none") return false; return element.getClientRects().length > 0; } JAVASCRIPT end |