Add a :click-element command
This commit is contained in:
parent
28a6b3918c
commit
0cef4ac2db
@ -38,6 +38,7 @@ Added
|
|||||||
- New `prev-category` and `next-category` arguments to `:completion-item-focus`
|
- New `prev-category` and `next-category` arguments to `:completion-item-focus`
|
||||||
to focus the previous/next category in the completion (bound to `<Ctrl-Tab>`
|
to focus the previous/next category in the completion (bound to `<Ctrl-Tab>`
|
||||||
and `<Ctrl-Shift-Tab>` by default).
|
and `<Ctrl-Shift-Tab>` by default).
|
||||||
|
- New `:click-element` command to fake a click on a element.
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
@ -1528,6 +1528,47 @@ class CommandDispatcher:
|
|||||||
this.dispatchEvent(event);
|
this.dispatchEvent(event);
|
||||||
""".format(javascript.string_escape(text)))
|
""".format(javascript.string_escape(text)))
|
||||||
|
|
||||||
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||||
|
hide=True)
|
||||||
|
@cmdutils.argument('filter_', choices=['id'])
|
||||||
|
def click_element(self, filter_: str, value, *,
|
||||||
|
target: usertypes.ClickTarget):
|
||||||
|
"""Click the element matching the given filter.
|
||||||
|
|
||||||
|
The given filter needs to result in exactly one element, otherwise, an
|
||||||
|
error is shown.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filter: How to filter the elements.
|
||||||
|
id: Get an element based on its ID.
|
||||||
|
value: The value to filter for.
|
||||||
|
"""
|
||||||
|
tab = self._current_widget()
|
||||||
|
|
||||||
|
def single_cb(elem):
|
||||||
|
"""Click a single element."""
|
||||||
|
if elem is None:
|
||||||
|
message.error(self._win_id, "No element found!")
|
||||||
|
return
|
||||||
|
elem.click(target)
|
||||||
|
|
||||||
|
# def multiple_cb(elems):
|
||||||
|
# """Click multiple elements (with only one expected)."""
|
||||||
|
# if not elems:
|
||||||
|
# message.error(self._win_id, "No element found!")
|
||||||
|
# return
|
||||||
|
# elif len(elems) != 1:
|
||||||
|
# message.error(self._win_id, "{} elements found!".format(
|
||||||
|
# len(elems)))
|
||||||
|
# return
|
||||||
|
# elems[0].click(target)
|
||||||
|
|
||||||
|
handlers = {
|
||||||
|
'id': (tab.elements.find_id, single_cb),
|
||||||
|
}
|
||||||
|
handler, callback = handlers[filter_]
|
||||||
|
handler(value, callback)
|
||||||
|
|
||||||
def _search_cb(self, found, *, tab, old_scroll_pos, options, text, prev):
|
def _search_cb(self, found, *, tab, old_scroll_pos, options, text, prev):
|
||||||
"""Callback called from search/search_next/search_prev.
|
"""Callback called from search/search_next/search_prev.
|
||||||
|
|
||||||
|
@ -513,7 +513,12 @@ class WebKitElements(browsertab.AbstractElements):
|
|||||||
callback(elems)
|
callback(elems)
|
||||||
|
|
||||||
def find_id(self, elem_id, callback):
|
def find_id(self, elem_id, callback):
|
||||||
self.find_css('#' + elem_id, callback)
|
def find_id_cb(elems):
|
||||||
|
if not elems:
|
||||||
|
callback(None)
|
||||||
|
else:
|
||||||
|
callback(elems[0])
|
||||||
|
self.find_css('#' + elem_id, find_id_cb)
|
||||||
|
|
||||||
def find_focused(self, callback):
|
def find_focused(self, callback):
|
||||||
frame = self._widget.page().currentFrame()
|
frame = self._widget.page().currentFrame()
|
||||||
|
@ -7,5 +7,6 @@
|
|||||||
<span onclick='console.log("click_element special chars")'>"Don't", he shouted</span>
|
<span onclick='console.log("click_element special chars")'>"Don't", he shouted</span>
|
||||||
<span>Duplicate</span>
|
<span>Duplicate</span>
|
||||||
<span>Duplicate</span>
|
<span>Duplicate</span>
|
||||||
|
<form><input id='qute-input'></input></form>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -559,3 +559,15 @@ Feature: Various utility commands.
|
|||||||
And I put "{url}" into the clipboard
|
And I put "{url}" into the clipboard
|
||||||
And I run :message-info {clipboard}bar{url}
|
And I run :message-info {clipboard}bar{url}
|
||||||
Then the message "{url}barhttp://localhost:*/hello.txt" should be shown
|
Then the message "{url}barhttp://localhost:*/hello.txt" should be shown
|
||||||
|
|
||||||
|
## :click-element
|
||||||
|
|
||||||
|
Scenario: Clicking an element with unknown ID
|
||||||
|
When I open data/click_element.html
|
||||||
|
And I run :click-element id blah
|
||||||
|
Then the error "No element found!" should be shown
|
||||||
|
|
||||||
|
Scenario: Clicking an element by ID
|
||||||
|
When I open data/click_element.html
|
||||||
|
And I run :click-element id qute-input
|
||||||
|
Then "Clicked editable element!" should be logged
|
||||||
|
@ -548,21 +548,6 @@ class QuteProc(testprocess.Process):
|
|||||||
raise ValueError('Invalid response from qutebrowser: {}'
|
raise ValueError('Invalid response from qutebrowser: {}'
|
||||||
.format(message))
|
.format(message))
|
||||||
|
|
||||||
def click_element_by_id(self, elem_id):
|
|
||||||
"""Click the element with the given ID."""
|
|
||||||
script = (
|
|
||||||
'var _elem = document.getElementById("{elem_id}"); '
|
|
||||||
'if (_elem === null) {{ console.log("qute:no elem"); }} '
|
|
||||||
'else {{ console.log("qute:okay"); _elem.click(); }}'
|
|
||||||
).format(elem_id=javascript.string_escape(elem_id))
|
|
||||||
self.send_cmd(':jseval ' + script, escape=False)
|
|
||||||
message = self.wait_for_js('qute:*').message
|
|
||||||
if message.endswith('qute:no elem'):
|
|
||||||
raise ValueError('No element with ID {!r} found'.format(elem_id))
|
|
||||||
elif not message.endswith('qute:okay'):
|
|
||||||
raise ValueError('Invalid response from qutebrowser: {}'
|
|
||||||
.format(message))
|
|
||||||
|
|
||||||
def compare_session(self, expected):
|
def compare_session(self, expected):
|
||||||
"""Compare the current sessions against the given template.
|
"""Compare the current sessions against the given template.
|
||||||
|
|
||||||
|
@ -265,22 +265,6 @@ class TestClickElementByText:
|
|||||||
assert 'No element' in str(excinfo.value)
|
assert 'No element' in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
class TestClickElementById:
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def open_page(self, quteproc):
|
|
||||||
quteproc.open_path('data/click_element.html')
|
|
||||||
|
|
||||||
def test_click_element(self, quteproc):
|
|
||||||
quteproc.click_element_by_id('test')
|
|
||||||
quteproc.wait_for_js('click_element clicked')
|
|
||||||
|
|
||||||
def test_nonexistent(self, quteproc):
|
|
||||||
with pytest.raises(ValueError) as excinfo:
|
|
||||||
quteproc.click_element_by_id('blah')
|
|
||||||
assert 'No element' in str(excinfo.value)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('string, expected', [
|
@pytest.mark.parametrize('string, expected', [
|
||||||
('Test', "'Test'"),
|
('Test', "'Test'"),
|
||||||
("Don't", '"Don\'t"'),
|
("Don't", '"Don\'t"'),
|
||||||
|
Loading…
Reference in New Issue
Block a user