Add a :click-element command

This commit is contained in:
Florian Bruhin 2016-08-18 15:30:04 +02:00
parent 28a6b3918c
commit 0cef4ac2db
7 changed files with 61 additions and 32 deletions

View File

@ -38,6 +38,7 @@ Added
- New `prev-category` and `next-category` arguments to `:completion-item-focus`
to focus the previous/next category in the completion (bound to `<Ctrl-Tab>`
and `<Ctrl-Shift-Tab>` by default).
- New `:click-element` command to fake a click on a element.
Changed
~~~~~~~

View File

@ -1528,6 +1528,47 @@ class CommandDispatcher:
this.dispatchEvent(event);
""".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):
"""Callback called from search/search_next/search_prev.

View File

@ -513,7 +513,12 @@ class WebKitElements(browsertab.AbstractElements):
callback(elems)
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):
frame = self._widget.page().currentFrame()

View File

@ -7,5 +7,6 @@
<span onclick='console.log("click_element special chars")'>"Don't", he shouted</span>
<span>Duplicate</span>
<span>Duplicate</span>
<form><input id='qute-input'></input></form>
</body>
</html>

View File

@ -559,3 +559,15 @@ Feature: Various utility commands.
And I put "{url}" into the clipboard
And I run :message-info {clipboard}bar{url}
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

View File

@ -548,21 +548,6 @@ class QuteProc(testprocess.Process):
raise ValueError('Invalid response from qutebrowser: {}'
.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):
"""Compare the current sessions against the given template.

View File

@ -265,22 +265,6 @@ class TestClickElementByText:
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', [
('Test', "'Test'"),
("Don't", '"Don\'t"'),