From 0177dafbd004b88f8ec0a72be3d90407a0c04ae7 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sat, 6 Aug 2016 22:03:50 +0200 Subject: [PATCH 001/365] Add {clipboard} and {primary} to replace :paste :paste is deprecated and replaced by equivalents using :open and the new variables, and :open supports opening newline-separated lists of URLs. --- doc/help/commands.asciidoc | 17 +---- qutebrowser/browser/commands.py | 79 +++++++++++++++--------- qutebrowser/commands/runners.py | 10 ++- qutebrowser/config/configdata.py | 12 ++-- qutebrowser/utils/utils.py | 6 ++ tests/end2end/features/yankpaste.feature | 38 ++++++------ 6 files changed, 93 insertions(+), 69 deletions(-) diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index f43be5822..ebe703a44 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -34,7 +34,6 @@ |<>|Show a log of past messages. |<>|Open typical prev/next links or navigate using the URL path. |<>|Open a URL in the current/[count]th tab. -|<>|Open a page from the clipboard. |<>|Print the current/[count]th tab. |<>|Add a new quickmark. |<>|Delete a quickmark. @@ -473,6 +472,8 @@ Syntax: +:open [*--implicit*] [*--bg*] [*--tab*] [*--window*] ['url']+ Open a URL in the current/[count]th tab. +If the URL contains newlines, each line gets opened in its own tab. + ==== positional arguments * +'url'+: The URL to open. @@ -489,20 +490,6 @@ The tab index to open the URL in. ==== note * This command does not split arguments after the last argument and handles quotes literally. -[[paste]] -=== paste -Syntax: +:paste [*--sel*] [*--tab*] [*--bg*] [*--window*]+ - -Open a page from the clipboard. - -If the pasted text contains newlines, each line gets opened in its own tab. - -==== optional arguments -* +*-s*+, +*--sel*+: Use the primary selection instead of the clipboard. -* +*-t*+, +*--tab*+: Open in a new tab. -* +*-b*+, +*--bg*+: Open in a background tab. -* +*-w*+, +*--window*+: Open in new window. - [[print]] === print Syntax: +:print [*--preview*] [*--pdf* 'file']+ diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 3728ebac4..3bfb09fd6 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -236,6 +236,8 @@ class CommandDispatcher: bg=False, tab=False, window=False, count=None): """Open a URL in the current/[count]th tab. + If the URL contains newlines, each line gets opened in its own tab. + Args: url: The URL to open. bg: Open in a new background tab. @@ -245,37 +247,60 @@ class CommandDispatcher: clicking on a link). count: The tab index to open the URL in, or None. """ + force_search = False if url is None: if tab or bg or window: - url = config.get('general', 'default-page') + urls = [config.get('general', 'default-page')] else: raise cmdexc.CommandError("No URL given, but -t/-b/-w is not " "set!") else: - try: - url = objreg.get('quickmark-manager').get(url) - except urlmarks.Error: - try: - url = urlutils.fuzzy_url(url) - except urlutils.InvalidUrlError as e: - # We don't use cmdexc.CommandError here as this can be - # called async from edit_url - message.error(self._win_id, str(e)) - return - if tab or bg or window: - self._open(url, tab, bg, window, not implicit) - else: - curtab = self._cntwidget(count) - if curtab is None: - if count is None: - # We want to open a URL in the current tab, but none exists - # yet. - self._tabbed_browser.tabopen(url) - else: - # Explicit count with a tab that doesn't exist. - return + urllist = [u for u in url.split('\n') if u.strip()] + if (len(urllist) > 1 and + any(not urlutils.is_url(u) and + urlutils.get_path_if_valid(u, check_exists=True) + is None for u in urllist)): + urllist = [url] + force_search = True + urls = [x for x in [self._parse_url(u, force_search=force_search) + for u in urllist] if x is not None] + for i, cur_url in enumerate(urls): + if not window and i > 0: + tab = False + bg = True + if tab or bg or window: + self._open(cur_url, tab, bg, window, not implicit) else: - curtab.openurl(url) + curtab = self._cntwidget(count) + if curtab is None: + if count is None: + # We want to open a URL in the current tab, but none + # exists yet. + self._tabbed_browser.tabopen(cur_url) + else: + curtab.openurl(cur_url) + + def _parse_url(self, url, force_search=False): + """Parse a URL or quickmark or search query. + + Args: + url: The URL to parse. + force_search: Whether to force a search even if the content can be + interpreted as a URL or a path. + + Return: + A URL that can be opened.""" + try: + return objreg.get('quickmark-manager').get(url) + except urlmarks.Error: + try: + return urlutils.fuzzy_url(url, force_search=force_search) + except urlutils.InvalidUrlError as e: + # We don't use cmdexc.CommandError here as this can be + # called async from edit_url + message.error(self._win_id, str(e)) + return + @cmdutils.register(instance='command-dispatcher', name='reload', scope='window') @@ -776,7 +801,8 @@ class CommandDispatcher: else: raise cmdexc.CommandError("Last tab") - @cmdutils.register(instance='command-dispatcher', scope='window') + @cmdutils.register(instance='command-dispatcher', scope='window', + deprecated="Use :open {clipboard}") def paste(self, sel=False, tab=False, bg=False, window=False): """Open a page from the clipboard. @@ -796,9 +822,6 @@ class CommandDispatcher: sel = False target = "Clipboard" text = utils.get_clipboard(selection=sel) - if not text.strip(): - raise cmdexc.CommandError("{} is empty.".format(target)) - log.misc.debug("{} contained: {!r}".format(target, text)) text_urls = [u for u in text.split('\n') if u.strip()] if (len(text_urls) > 1 and not urlutils.is_url(text_urls[0]) and urlutils.get_path_if_valid( diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index f020b9199..91c8bf3c9 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -26,7 +26,7 @@ from PyQt5.QtCore import pyqtSlot, QUrl, QObject from qutebrowser.config import config, configexc from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.utils import message, objreg, qtutils +from qutebrowser.utils import message, objreg, qtutils, utils from qutebrowser.misc import split @@ -57,11 +57,19 @@ def replace_variables(win_id, arglist): QUrl.RemovePassword) if '{url:pretty}' in arglist: pretty_url = _current_url(tabbed_browser).toString(QUrl.RemovePassword) + if '{clipboard}' in arglist: + clipboard = utils.get_clipboard() + if '{primary}' in arglist: + primary = utils.get_clipboard(selection=True) for arg in arglist: if arg == '{url}': args.append(url) elif arg == '{url:pretty}': args.append(pretty_url) + elif arg == '{clipboard}': + args.append(clipboard) + elif arg == '{primary}': + args.append(primary) else: args.append(arg) return args diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 8cbfe3938..9cc275502 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1512,12 +1512,12 @@ KEY_DATA = collections.OrderedDict([ ('yank -ds', ['yD']), ('yank -p', ['yp']), ('yank -ps', ['yP']), - ('paste', ['pp']), - ('paste -s', ['pP']), - ('paste -t', ['Pp']), - ('paste -ts', ['PP']), - ('paste -w', ['wp']), - ('paste -ws', ['wP']), + ('open {clipboard}', ['pp']), + ('open {primary}', ['pP']), + ('open -t {clipboard}', ['Pp']), + ('open -t {primary}', ['PP']), + ('open -w {clipboard}', ['wp']), + ('open -w {primary}', ['wP']), ('quickmark-save', ['m']), ('set-cmd-text -s :quickmark-load', ['b']), ('set-cmd-text -s :quickmark-load -t', ['B']), diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index fd8419d12..983827d46 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -37,6 +37,7 @@ import pkg_resources import qutebrowser from qutebrowser.utils import qtutils, log +from qutebrowser.commands import cmdexc fake_clipboard = None @@ -810,6 +811,11 @@ def get_clipboard(selection=False): mode = QClipboard.Selection if selection else QClipboard.Clipboard data = QApplication.clipboard().text(mode=mode) + target = "Primary selection" if selection else "Clipboard" + if not data.strip(): + raise cmdexc.CommandError("{} is empty.".format(target)) + log.misc.debug("{} contained: {!r}".format(target, data)) + return data diff --git a/tests/end2end/features/yankpaste.feature b/tests/end2end/features/yankpaste.feature index db39d2380..abf9cf9c3 100644 --- a/tests/end2end/features/yankpaste.feature +++ b/tests/end2end/features/yankpaste.feature @@ -1,6 +1,6 @@ Feature: Yanking and pasting. - :yank and :paste can be used to copy/paste the URL or title from/to the - clipboard and primary selection. + :yank, {clipboard} and {primary} can be used to copy/paste the URL or title + from/to the clipboard and primary selection. Background: Given I run :tab-only @@ -45,11 +45,11 @@ Feature: Yanking and pasting. Then the message "Yanked URL to clipboard: http://localhost:(port)/data/title with spaces.html" should be shown And the clipboard should contain "http://localhost:(port)/data/title with spaces.html" - #### :paste + #### {clipboard} and {primary} Scenario: Pasting a URL When I put "http://localhost:(port)/data/hello.txt" into the clipboard - And I run :paste + And I run :open {clipboard} And I wait until data/hello.txt is loaded Then the requests should be: data/hello.txt @@ -57,32 +57,32 @@ Feature: Yanking and pasting. Scenario: Pasting a URL from primary selection When selection is supported And I put "http://localhost:(port)/data/hello2.txt" into the primary selection - And I run :paste --sel + And I run :open {primary} And I wait until data/hello2.txt is loaded Then the requests should be: data/hello2.txt Scenario: Pasting with empty clipboard When I put "" into the clipboard - And I run :paste + And I run :open {clipboard} (invalid command) Then the error "Clipboard is empty." should be shown Scenario: Pasting with empty selection When selection is supported And I put "" into the primary selection - And I run :paste --sel + And I run :open {primary} (invalid command) Then the error "Primary selection is empty." should be shown Scenario: Pasting with a space in clipboard When I put " " into the clipboard - And I run :paste + And I run :open {clipboard} (invalid command) Then the error "Clipboard is empty." should be shown Scenario: Pasting in a new tab Given I open about:blank When I run :tab-only And I put "http://localhost:(port)/data/hello.txt" into the clipboard - And I run :paste -t + And I run :open -t {clipboard} And I wait until data/hello.txt is loaded Then the following tabs should be open: - about:blank @@ -92,7 +92,7 @@ Feature: Yanking and pasting. Given I open about:blank When I run :tab-only And I put "http://localhost:(port)/data/hello.txt" into the clipboard - And I run :paste -b + And I run :open -b {clipboard} And I wait until data/hello.txt is loaded Then the following tabs should be open: - about:blank (active) @@ -101,7 +101,7 @@ Feature: Yanking and pasting. Scenario: Pasting in a new window Given I have a fresh instance When I put "http://localhost:(port)/data/hello.txt" into the clipboard - And I run :paste -w + And I run :open -w {clipboard} And I wait until data/hello.txt is loaded Then the session should look like: windows: @@ -119,7 +119,7 @@ Feature: Yanking and pasting. Scenario: Pasting an invalid URL When I set general -> auto-search to false And I put "foo bar" into the clipboard - And I run :paste + And I run :open {clipboard} Then the error "Invalid URL" should be shown Scenario: Pasting multiple urls in a new tab @@ -128,7 +128,7 @@ Feature: Yanking and pasting. http://localhost:(port)/data/hello.txt http://localhost:(port)/data/hello2.txt http://localhost:(port)/data/hello3.txt - And I run :paste -t + And I run :open -t {clipboard} And I wait until data/hello.txt is loaded And I wait until data/hello2.txt is loaded And I wait until data/hello3.txt is loaded @@ -145,7 +145,7 @@ Feature: Yanking and pasting. this url: http://qutebrowser.org should not open - And I run :paste -t + And I run :open -t {clipboard} And I wait until data/hello.txt?q=this%20url%3A%0Ahttp%3A//qutebrowser.org%0Ashould%20not%20open is loaded Then the following tabs should be open: - about:blank @@ -159,7 +159,7 @@ Feature: Yanking and pasting. text: should open as search - And I run :paste -t + And I run :open -t {clipboard} And I wait until data/hello.txt?q=text%3A%0Ashould%20open%0Aas%20search is loaded Then the following tabs should be open: - about:blank @@ -172,7 +172,7 @@ Feature: Yanking and pasting. http://localhost:(port)/data/hello.txt http://localhost:(port)/data/hello2.txt http://localhost:(port)/data/hello3.txt - And I run :paste -b + And I run :open -b {clipboard} And I wait until data/hello.txt is loaded And I wait until data/hello2.txt is loaded And I wait until data/hello3.txt is loaded @@ -188,7 +188,7 @@ Feature: Yanking and pasting. http://localhost:(port)/data/hello.txt http://localhost:(port)/data/hello2.txt http://localhost:(port)/data/hello3.txt - And I run :paste -w + And I run :open -w {clipboard} And I wait until data/hello.txt is loaded And I wait until data/hello2.txt is loaded And I wait until data/hello3.txt is loaded @@ -218,13 +218,13 @@ Feature: Yanking and pasting. Scenario: Pasting multiple urls with an empty one When I open about:blank And I put "http://localhost:(port)/data/hello.txt\n\nhttp://localhost:(port)/data/hello2.txt" into the clipboard - And I run :paste -t + And I run :open -t {clipboard} Then no crash should happen Scenario: Pasting multiple urls with an almost empty one When I open about:blank And I put "http://localhost:(port)/data/hello.txt\n \nhttp://localhost:(port)/data/hello2.txt" into the clipboard - And I run :paste -t + And I run :open -t {clipboard} Then no crash should happen #### :paste-primary From 96146c55afbbf8603d800cfddfca8b7035cec39a Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sat, 6 Aug 2016 22:25:08 +0200 Subject: [PATCH 002/365] Remove useless clipboard target information --- qutebrowser/browser/commands.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 3bfb09fd6..f0ed6d67a 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -816,11 +816,8 @@ class CommandDispatcher: window: Open in new window. """ force_search = False - if sel and utils.supports_selection(): - target = "Primary selection" - else: + if not utils.supports_selection(): sel = False - target = "Clipboard" text = utils.get_clipboard(selection=sel) text_urls = [u for u in text.split('\n') if u.strip()] if (len(text_urls) > 1 and not urlutils.is_url(text_urls[0]) and From 38508274e063c12fc8caf49c746795c0abd57b94 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sun, 7 Aug 2016 00:46:23 +0200 Subject: [PATCH 003/365] Improve clipboard exceptions, migrate bindings --- qutebrowser/browser/commands.py | 14 ++++++++++---- qutebrowser/commands/runners.py | 11 +++++++---- qutebrowser/config/configdata.py | 5 +++++ qutebrowser/misc/miscwidgets.py | 3 ++- qutebrowser/utils/utils.py | 8 ++++++-- tests/unit/config/test_config.py | 4 ++++ 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index f0ed6d67a..6dde5d40f 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -818,7 +818,10 @@ class CommandDispatcher: force_search = False if not utils.supports_selection(): sel = False - text = utils.get_clipboard(selection=sel) + try: + text = utils.get_clipboard(selection=sel) + except utils.ClipboardEmptyError as e: + raise cmdexc.CommandError(e) text_urls = [u for u in text.split('\n') if u.strip()] if (len(text_urls) > 1 and not urlutils.is_url(text_urls[0]) and urlutils.get_path_if_valid( @@ -1462,9 +1465,12 @@ class CommandDispatcher: raise cmdexc.CommandError("Focused element is not editable!") try: - sel = utils.get_clipboard(selection=True) - except utils.SelectionUnsupportedError: - sel = utils.get_clipboard() + try: + sel = utils.get_clipboard(selection=True) + except utils.SelectionUnsupportedError: + sel = utils.get_clipboard() + except utils.ClipboardEmptyError: + return log.misc.debug("Pasting primary selection into element {}".format( elem.debug_text())) diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index 91c8bf3c9..201fc4bbe 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -57,10 +57,13 @@ def replace_variables(win_id, arglist): QUrl.RemovePassword) if '{url:pretty}' in arglist: pretty_url = _current_url(tabbed_browser).toString(QUrl.RemovePassword) - if '{clipboard}' in arglist: - clipboard = utils.get_clipboard() - if '{primary}' in arglist: - primary = utils.get_clipboard(selection=True) + try: + if '{clipboard}' in arglist: + clipboard = utils.get_clipboard() + if '{primary}' in arglist: + primary = utils.get_clipboard(selection=True) + except utils.ClipboardEmptyError as e: + raise cmdexc.CommandError(e) for arg in arglist: if arg == '{url}': args.append(url) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 9cc275502..f770f5c05 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1677,4 +1677,9 @@ CHANGED_KEY_COMMANDS = [ (re.compile(r'^download-remove --all$'), r'download-clear'), (re.compile(r'^hint links fill "([^"]*)"$'), r'hint links fill \1'), + + (re.compile(r'^paste$'), r'open {clipboard}'), + (re.compile(r'^paste -([twb])$'), r'open -\1 {clipboard}'), + (re.compile(r'^paste -([twb])s$'), r'open -\1 {primary}'), + (re.compile(r'^paste -s([twb])$'), r'open -\1 {primary}'), ] diff --git a/qutebrowser/misc/miscwidgets.py b/qutebrowser/misc/miscwidgets.py index 63a5718c6..7e84300d2 100644 --- a/qutebrowser/misc/miscwidgets.py +++ b/qutebrowser/misc/miscwidgets.py @@ -47,7 +47,8 @@ class MinimalLineEditMixin: if e.key() == Qt.Key_Insert and e.modifiers() == Qt.ShiftModifier: try: text = utils.get_clipboard(selection=True) - except utils.SelectionUnsupportedError: + except (utils.SelectionUnsupportedError, + utils.ClipboardEmptyError): pass else: e.accept() diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 983827d46..32696aacf 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -37,7 +37,6 @@ import pkg_resources import qutebrowser from qutebrowser.utils import qtutils, log -from qutebrowser.commands import cmdexc fake_clipboard = None @@ -49,6 +48,11 @@ class SelectionUnsupportedError(Exception): """Raised if [gs]et_clipboard is used and selection=True is unsupported.""" +class ClipboardEmptyError(Exception): + + """Raised if get_clipboard is used and the clipboard is empty.""" + + def elide(text, length): """Elide text so it uses a maximum of length chars.""" if length < 1: @@ -813,7 +817,7 @@ def get_clipboard(selection=False): target = "Primary selection" if selection else "Clipboard" if not data.strip(): - raise cmdexc.CommandError("{} is empty.".format(target)) + raise ClipboardEmptyError("{} is empty.".format(target)) log.misc.debug("{} contained: {!r}".format(target, data)) return data diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index a6ad3d119..53664b718 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -286,6 +286,10 @@ class TestKeyConfigParser: 'hint links fill :open {hint-url}'), ('hint links fill ":open -t {hint-url}"', 'hint links fill :open -t {hint-url}'), + + ('paste', 'open {clipboard}'), + ('paste -t', 'open -t {clipboard}'), + ('paste -ws', 'open -w {primary}'), ] ) def test_migrations(self, old, new_expected): From dc69a90e69642a39dcacd4764f1d29ee1357834f Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sun, 7 Aug 2016 02:43:08 +0200 Subject: [PATCH 004/365] Add :insert-text command See #1790. :paste-primary now calls :insert-text and can be deprecated by :insert-text {primary} after #1791 is merged. --- doc/help/commands.asciidoc | 13 +++++++++++++ qutebrowser/browser/commands.py | 27 +++++++++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index f43be5822..13487a823 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -918,6 +918,7 @@ How many steps to zoom out. |<>|Enter a key mode. |<>|Follow a hint. |<>|Follow the selected text. +|<>|Insert text at cursor position. |<>|Jump to the mark named by `key`. |<>|Leave the mode we're currently in. |<>|Show an error message in the statusbar. @@ -1028,6 +1029,18 @@ Follow the selected text. ==== optional arguments * +*-t*+, +*--tab*+: Load the selected link in a new tab. +[[insert-text]] +=== insert-text +Syntax: +:insert-text 'text'+ + +Insert text at cursor position. + +==== positional arguments +* +'text'+: The text to insert. + +==== note +* This command does not split arguments after the last argument and handles quotes literally. + [[jump-mark]] === jump-mark Syntax: +:jump-mark 'key'+ diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 3728ebac4..cd60510dd 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1431,6 +1431,19 @@ class CommandDispatcher: needs_js=True, backend=usertypes.Backend.QtWebKit) def paste_primary(self): """Paste the primary selection at cursor position.""" + try: + self.insert_text(utils.get_clipboard(selection=True)) + except utils.SelectionUnsupportedError: + self.insert_text(utils.get_clipboard()) + + @cmdutils.register(instance='command-dispatcher', maxsplit=0, + modes=[KeyMode.insert], hide=True, scope='window', + needs_js=True, backend=usertypes.Backend.QtWebKit) + def insert_text(self, text): + """Insert text at cursor position. + + Args: + text: The text to insert.""" # FIXME:qtwebengine have a proper API for this tab = self._current_widget() page = tab._widget.page() # pylint: disable=protected-access @@ -1440,20 +1453,14 @@ class CommandDispatcher: raise cmdexc.CommandError("No element focused!") if not elem.is_editable(strict=True): raise cmdexc.CommandError("Focused element is not editable!") - - try: - sel = utils.get_clipboard(selection=True) - except utils.SelectionUnsupportedError: - sel = utils.get_clipboard() - - log.misc.debug("Pasting primary selection into element {}".format( + log.misc.debug("Inserting text into element {}".format( elem.debug_text())) elem.run_js_async(""" - var sel = '{}'; + var text = '{}'; var event = document.createEvent('TextEvent'); - event.initTextEvent('textInput', true, true, null, sel); + event.initTextEvent('textInput', true, true, null, text); this.dispatchEvent(event); - """.format(javascript.string_escape(sel))) + """.format(javascript.string_escape(text))) def _search_cb(self, found, *, tab, old_scroll_pos, options, text, prev): """Callback called from search/search_next/search_prev. From 864c95007fb5083da2de4ce9f6d35a8ec7523e8f Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sun, 7 Aug 2016 14:29:52 +0200 Subject: [PATCH 005/365] Unhide :insert-text and allow outside insert mode :insert-text works if a text element is focused, even outside insert mode. --- qutebrowser/browser/commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index cd60510dd..d60ba3c49 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1437,8 +1437,8 @@ class CommandDispatcher: self.insert_text(utils.get_clipboard()) @cmdutils.register(instance='command-dispatcher', maxsplit=0, - modes=[KeyMode.insert], hide=True, scope='window', - needs_js=True, backend=usertypes.Backend.QtWebKit) + scope='window', needs_js=True, + backend=usertypes.Backend.QtWebKit) def insert_text(self, text): """Insert text at cursor position. From c04adb94b21f476c2d3b708e0f543bf08670204d Mon Sep 17 00:00:00 2001 From: Moez Bouhlel Date: Thu, 4 Aug 2016 20:53:13 +0100 Subject: [PATCH 006/365] DocstringParser - support python with optimizations on --- qutebrowser/misc/earlyinit.py | 6 ++++++ qutebrowser/utils/docutils.py | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py index 284478866..256d80f1c 100644 --- a/qutebrowser/misc/earlyinit.py +++ b/qutebrowser/misc/earlyinit.py @@ -300,6 +300,11 @@ def init_log(args): log.init_log(args) log.init.debug("Log initialized.") +def check_optimize_flag(): + from qutebrowser.utils import log + if sys.flags.optimize >= 2: + log.init.warning("Running on optimize level higher than 1, " + "unexpected behaviors may occur.") def earlyinit(args): """Do all needed early initialization. @@ -327,3 +332,4 @@ def earlyinit(args): remove_inputhook() check_libraries(args) check_ssl_support() + check_optimize_flag() diff --git a/qutebrowser/utils/docutils.py b/qutebrowser/utils/docutils.py index d93e7564d..40cb0cb70 100644 --- a/qutebrowser/utils/docutils.py +++ b/qutebrowser/utils/docutils.py @@ -26,7 +26,7 @@ import os.path import collections import qutebrowser -from qutebrowser.utils import usertypes +from qutebrowser.utils import usertypes, log, utils def is_git_repo(): @@ -98,6 +98,15 @@ class DocstringParser: self.State.arg_inside: self._parse_arg_inside, self.State.misc: self._skip, } + if doc is None: + if sys.flags.optimize < 2: + log.commands.warning( + "Function {}() from {} has no docstring".format( + utils.qualname(func), + inspect.getsourcefile(func))) + self.long_desc = "" + self.short_desc = "" + return for line in doc.splitlines(): handler = handlers[self._state] stop = handler(line) From cf26201e86e6e53d0ef4240935be7faffc32bd49 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Mon, 8 Aug 2016 17:28:13 +0200 Subject: [PATCH 007/365] Extract hint tags from This is useful for very many input fields, especially prominent on GitHub itself. --- qutebrowser/browser/hints.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 278ad7d0d..045c8456a 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -1021,6 +1021,7 @@ class WordHinter: "alt": lambda elem: elem["alt"], "name": lambda elem: elem["name"], "title": lambda elem: elem["title"], + "placeholder": lambda elem: elem["placeholder"], "src": lambda elem: elem["src"].split('/')[-1], "href": lambda elem: elem["href"].split('/')[-1], "text": str, @@ -1029,7 +1030,7 @@ class WordHinter: extractable_attrs = collections.defaultdict(list, { "img": ["alt", "title", "src"], "a": ["title", "href", "text"], - "input": ["name"] + "input": ["name", "placeholder"] }) return (attr_extractors[attr](elem) From 6e279f1b1e36fdc6013eef27c028773aedbf0548 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Mon, 8 Aug 2016 17:49:35 +0200 Subject: [PATCH 008/365] Extract hint tags from - + + - + diff --git a/tests/end2end/data/prompt/jsalert.html b/tests/end2end/data/prompt/jsalert.html index d4af29850..0d5076b4f 100644 --- a/tests/end2end/data/prompt/jsalert.html +++ b/tests/end2end/data/prompt/jsalert.html @@ -10,6 +10,6 @@ - + diff --git a/tests/end2end/data/prompt/jsconfirm.html b/tests/end2end/data/prompt/jsconfirm.html index 292b5e2c8..d89137ebd 100644 --- a/tests/end2end/data/prompt/jsconfirm.html +++ b/tests/end2end/data/prompt/jsconfirm.html @@ -10,6 +10,6 @@ - + diff --git a/tests/end2end/data/prompt/jsprompt.html b/tests/end2end/data/prompt/jsprompt.html index bc4178a6f..d8c848553 100644 --- a/tests/end2end/data/prompt/jsprompt.html +++ b/tests/end2end/data/prompt/jsprompt.html @@ -10,6 +10,6 @@ - + diff --git a/tests/end2end/data/prompt/notifications.html b/tests/end2end/data/prompt/notifications.html index a4e08e50d..f96456ff2 100644 --- a/tests/end2end/data/prompt/notifications.html +++ b/tests/end2end/data/prompt/notifications.html @@ -34,6 +34,6 @@ - + diff --git a/tests/end2end/features/downloads.feature b/tests/end2end/features/downloads.feature index 5b623442d..6c094d421 100644 --- a/tests/end2end/features/downloads.feature +++ b/tests/end2end/features/downloads.feature @@ -201,8 +201,7 @@ Feature: Downloading things from a website. Scenario: Directly open a download with a very long filename When I set storage -> prompt-download-directory to true And I open data/downloads/issue1725.html - And I run :hint - And I run :follow-hint a + And I run :click-element id long-link And I wait for "Asking question text='Save file to:'>, *" in the log And I directly open the download And I wait until the download is finished diff --git a/tests/end2end/features/javascript.feature b/tests/end2end/features/javascript.feature index 979dd5cbf..bb34db70b 100644 --- a/tests/end2end/features/javascript.feature +++ b/tests/end2end/features/javascript.feature @@ -12,10 +12,8 @@ Feature: Javascript stuff Scenario: Closing a JS window twice (issue 906) When I open about:blank And I open data/javascript/issue906.html in a new tab - And I run :hint - And I run :follow-hint a + And I run :click-element id open-button And I wait for "Changing title for idx 2 to 'about:blank'" in the log And I run :tab-focus 2 - And I run :hint - And I run :follow-hint s + And I run :click-element id close-button Then "Requested to close * which does not exist!" should be logged diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index ab7599e03..b6c606551 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -372,8 +372,7 @@ Feature: Various utility commands. @pyqt>=5.3.1 Scenario: Focusing download widget via Tab (original issue) When I open data/prompt/jsprompt.html - And I run :hint - And I run :follow-hint a + And I run :click-element id button And I wait for "Entering mode KeyMode.prompt *" in the log And I press the key "" And I press the key "" @@ -519,8 +518,7 @@ Feature: Various utility commands. When I run :hint And I run :leave-mode And I run :repeat-command - And I run :follow-hint a - And I wait until data/hello.txt is loaded + And I run :click-element id link Then the following tabs should be open: - data/hints/link_blank.html - data/hello.txt (active) diff --git a/tests/end2end/features/prompts.feature b/tests/end2end/features/prompts.feature index f781cf1d3..633f465b6 100644 --- a/tests/end2end/features/prompts.feature +++ b/tests/end2end/features/prompts.feature @@ -8,7 +8,7 @@ Feature: Prompts Scenario: Javascript alert When I open data/prompt/jsalert.html - And I click the button + And I run :click-element id button And I wait for a prompt And I run :prompt-accept Then the javascript message "Alert done" should be logged @@ -16,26 +16,26 @@ Feature: Prompts Scenario: Using content -> ignore-javascript-alert When I set content -> ignore-javascript-alert to true And I open data/prompt/jsalert.html - And I click the button + And I run :click-element id button Then the javascript message "Alert done" should be logged Scenario: Javascript confirm - yes When I open data/prompt/jsconfirm.html - And I click the button + And I run :click-element id button And I wait for a prompt And I run :prompt-yes Then the javascript message "confirm reply: true" should be logged Scenario: Javascript confirm - no When I open data/prompt/jsconfirm.html - And I click the button + And I run :click-element id button And I wait for a prompt And I run :prompt-no Then the javascript message "confirm reply: false" should be logged Scenario: Javascript confirm - aborted When I open data/prompt/jsconfirm.html - And I click the button + And I run :click-element id button And I wait for a prompt And I run :leave-mode Then the javascript message "confirm reply: false" should be logged @@ -43,7 +43,7 @@ Feature: Prompts @pyqt>=5.3.1 Scenario: Javascript prompt When I open data/prompt/jsprompt.html - And I click the button + And I run :click-element id button And I wait for a prompt And I press the keys "prompt test" And I run :prompt-accept @@ -52,7 +52,7 @@ Feature: Prompts @pyqt>=5.3.1 Scenario: Rejected javascript prompt When I open data/prompt/jsprompt.html - And I click the button + And I run :click-element id button And I wait for a prompt And I press the keys "prompt test" And I run :leave-mode @@ -66,7 +66,7 @@ Feature: Prompts When selection is supported And I put "insert test" into the primary selection And I open data/prompt/jsprompt.html - And I click the button + And I run :click-element id button And I wait for a prompt And I press the keys "" And I run :prompt-accept @@ -76,7 +76,7 @@ Feature: Prompts Scenario: Using content -> ignore-javascript-prompt When I set content -> ignore-javascript-prompt to true And I open data/prompt/jsprompt.html - And I click the button + And I run :click-element id button Then the javascript message "Prompt reply: null" should be logged # SSL @@ -119,21 +119,21 @@ Feature: Prompts Scenario: Always rejecting geolocation When I set content -> geolocation to false And I open data/prompt/geolocation.html in a new tab - And I click the button + And I run :click-element id button Then the javascript message "geolocation permission denied" should be logged @ci @not_osx Scenario: Always accepting geolocation When I set content -> geolocation to true And I open data/prompt/geolocation.html in a new tab - And I click the button + And I run :click-element id button Then the javascript message "geolocation permission denied" should not be logged @ci @not_osx Scenario: geolocation with ask -> true When I set content -> geolocation to ask And I open data/prompt/geolocation.html in a new tab - And I click the button + And I run :click-element id button And I wait for a prompt And I run :prompt-yes Then the javascript message "geolocation permission denied" should not be logged @@ -141,7 +141,7 @@ Feature: Prompts Scenario: geolocation with ask -> false When I set content -> geolocation to ask And I open data/prompt/geolocation.html in a new tab - And I click the button + And I run :click-element id button And I wait for a prompt And I run :prompt-no Then the javascript message "geolocation permission denied" should be logged @@ -149,7 +149,7 @@ Feature: Prompts Scenario: geolocation with ask -> abort When I set content -> geolocation to ask And I open data/prompt/geolocation.html in a new tab - And I click the button + And I run :click-element id button And I wait for a prompt And I run :leave-mode Then the javascript message "geolocation permission denied" should be logged @@ -159,19 +159,19 @@ Feature: Prompts Scenario: Always rejecting notifications When I set content -> notifications to false And I open data/prompt/notifications.html in a new tab - And I click the button + And I run :click-element id button Then the javascript message "notification permission denied" should be logged Scenario: Always accepting notifications When I set content -> notifications to true And I open data/prompt/notifications.html in a new tab - And I click the button + And I run :click-element id button Then the javascript message "notification permission granted" should be logged Scenario: notifications with ask -> false When I set content -> notifications to ask And I open data/prompt/notifications.html in a new tab - And I click the button + And I run :click-element id button And I wait for a prompt And I run :prompt-no Then the javascript message "notification permission denied" should be logged @@ -179,7 +179,7 @@ Feature: Prompts Scenario: notifications with ask -> true When I set content -> notifications to ask And I open data/prompt/notifications.html in a new tab - And I click the button + And I run :click-element id button And I wait for a prompt And I run :prompt-yes Then the javascript message "notification permission granted" should be logged @@ -189,7 +189,7 @@ Feature: Prompts Scenario: notifications with ask -> abort When I set content -> notifications to ask And I open data/prompt/notifications.html in a new tab - And I click the button + And I run :click-element id button And I wait for a prompt And I run :leave-mode Then the javascript message "notification permission aborted" should be logged @@ -197,7 +197,7 @@ Feature: Prompts Scenario: answering notification after closing tab When I set content -> notifications to ask And I open data/prompt/notifications.html in a new tab - And I click the button + And I run :click-element id button And I wait for a prompt And I run :tab-close And I wait for "Leaving mode KeyMode.yesno (reason: aborted)" in the log diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index b3233fcb4..665931ce1 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -806,8 +806,7 @@ Feature: Tab management And I set tabs -> background-tabs to false And I open about:blank And I open data/hints/html/simple.html in a new tab - And I run :hint all tab - And I run :follow-hint a + And I run :click-element id link --target=tab And I wait until data/hello.txt is loaded Then the following tabs should be open: - about:blank @@ -819,8 +818,7 @@ Feature: Tab management And I set tabs -> background-tabs to false And I open about:blank And I open data/hints/html/simple.html in a new tab - And I run :hint all tab - And I run :follow-hint a + And I run :click-element id link --target=tab And I wait until data/hello.txt is loaded Then the following tabs should be open: - about:blank @@ -832,8 +830,7 @@ Feature: Tab management And I set tabs -> background-tabs to false And I open about:blank And I open data/hints/html/simple.html in a new tab - And I run :hint all tab - And I run :follow-hint a + And I run :click-element id link --target=tab And I wait until data/hello.txt is loaded Then the following tabs should be open: - data/hello.txt (active) @@ -846,8 +843,7 @@ Feature: Tab management And I open data/hints/html/simple.html And I open about:blank in a new tab And I run :tab-focus last - And I run :hint all tab - And I run :follow-hint a + And I run :click-element id link --target=tab And I wait until data/hello.txt is loaded Then the following tabs should be open: - data/hints/html/simple.html diff --git a/tests/end2end/features/test_prompts_bdd.py b/tests/end2end/features/test_prompts_bdd.py index d4b2dbc8c..521a08f42 100644 --- a/tests/end2end/features/test_prompts_bdd.py +++ b/tests/end2end/features/test_prompts_bdd.py @@ -33,12 +33,6 @@ def wait_ssl_page_finished_loading(quteproc, ssl_server): load_status='warn') -@bdd.when("I click the button") -def click_button(quteproc): - quteproc.send_cmd(':hint') - quteproc.send_cmd(':follow-hint a') - - @bdd.when("I wait for a prompt") def wait_for_prompt(quteproc): quteproc.wait_for(message='Entering mode KeyMode.* (reason: question ' diff --git a/tests/end2end/features/yankpaste.feature b/tests/end2end/features/yankpaste.feature index 2bd865420..6d732d7c9 100644 --- a/tests/end2end/features/yankpaste.feature +++ b/tests/end2end/features/yankpaste.feature @@ -219,9 +219,7 @@ Feature: Yanking and pasting. Scenario: Inserting text into an empty text field When I open data/paste_primary.html - # Click the text field - And I run :hint all - And I run :follow-hint a + And I run :click-element id qute-textarea And I wait for "Clicked editable element!" in the log And I run :insert-text Hello world # Compare @@ -230,9 +228,7 @@ Feature: Yanking and pasting. Scenario: Inserting text into a text field at specific position When I open data/paste_primary.html And I set the text field to "one two three four" - # Click the text field - And I run :hint all - And I run :follow-hint a + And I run :click-element id qute-textarea And I wait for "Clicked editable element!" in the log # Move to the beginning and two characters to the right And I press the keys "" @@ -244,9 +240,7 @@ Feature: Yanking and pasting. Scenario: Inserting text into a text field with undo When I open data/paste_primary.html - # Click the text field - And I run :hint all - And I run :follow-hint a + And I run :click-element id qute-textarea And I wait for "Clicked editable element!" in the log # Paste and undo And I run :insert-text This text should be undone @@ -264,9 +258,7 @@ Feature: Yanking and pasting. Scenario: Inserting text with a read-only field When I open data/paste_primary.html - # Click the text field - And I run :hint all - And I run :follow-hint s + And I run :click-element id qute-textarea-noedit And I wait for "Clicked non-editable element!" in the log And I run :enter-mode insert And I run :insert-text test diff --git a/tests/end2end/test_insert_mode.py b/tests/end2end/test_insert_mode.py index 941c2cc74..552fffbb4 100644 --- a/tests/end2end/test_insert_mode.py +++ b/tests/end2end/test_insert_mode.py @@ -25,20 +25,23 @@ import json import pytest -@pytest.mark.parametrize('file_name, source, input_text, auto_insert', [ - ('textarea.html', 'clipboard', 'qutebrowser', 'false'), - ('textarea.html', 'keypress', 'superqutebrowser', 'false'), - ('input.html', 'clipboard', 'amazingqutebrowser', 'false'), - ('input.html', 'keypress', 'awesomequtebrowser', 'false'), - ('autofocus.html', 'keypress', 'cutebrowser', 'true'), +@pytest.mark.parametrize(['file_name', 'elem_id', 'source', 'input_text', + 'auto_insert'], [ + ('textarea.html', 'qute-textarea', 'clipboard', 'qutebrowser', 'false'), + ('textarea.html', 'qute-textarea', 'keypress', 'superqutebrowser', + 'false'), + ('input.html', 'qute-input', 'clipboard', 'amazingqutebrowser', 'false'), + ('input.html', 'qute-input', 'keypress', 'awesomequtebrowser', 'false'), + ('autofocus.html', 'qute-input-autofocus', 'keypress', 'cutebrowser', + 'true'), ]) -def test_insert_mode(file_name, source, input_text, auto_insert, quteproc): +def test_insert_mode(file_name, elem_id, source, input_text, auto_insert, + quteproc): url_path = 'data/insert_mode_settings/html/{}'.format(file_name) quteproc.open_path(url_path) quteproc.set_setting('input', 'auto-insert-mode', auto_insert) - quteproc.send_cmd(':hint all') - quteproc.send_cmd(':follow-hint a') + quteproc.send_cmd(':click-element id {}'.format(elem_id)) quteproc.wait_for(message='Clicked editable element!') quteproc.send_cmd(':debug-set-fake-clipboard') From 73210fb87cb73d74657901a646e13d7fca2afc0d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 16:16:56 +0200 Subject: [PATCH 282/365] QtWebEngine: Fix tab passed to WebEngineElement --- qutebrowser/browser/webengine/webenginetab.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 7f4d29b5d..3f804e9d5 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -326,7 +326,7 @@ class WebEngineElements(browsertab.AbstractElements): """ elems = [] for js_elem in js_elems: - elem = webengineelem.WebEngineElement(js_elem, tab=self) + elem = webengineelem.WebEngineElement(js_elem, tab=self._tab) elems.append(elem) callback(elems) @@ -342,7 +342,7 @@ class WebEngineElements(browsertab.AbstractElements): if js_elem is None: callback(None) else: - elem = webengineelem.WebEngineElement(js_elem, tab=self) + elem = webengineelem.WebEngineElement(js_elem, tab=self._tab) callback(elem) def find_css(self, selector, callback, *, only_visible=False): From 927f378c6da6a3775249ae9b92f3238f197c1431 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 16:52:53 +0200 Subject: [PATCH 283/365] Fix eslint --- qutebrowser/javascript/position_caret.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/qutebrowser/javascript/position_caret.js b/qutebrowser/javascript/position_caret.js index 01f530dd7..09d2301ad 100644 --- a/qutebrowser/javascript/position_caret.js +++ b/qutebrowser/javascript/position_caret.js @@ -33,9 +33,7 @@ "use strict"; (function() { - // FIXME:qtwebengine integrate this with other window._qutebrowser code? - function isElementInViewport(node) { // eslint-disable-line complexity var i; var boundingRect = (node.getClientRects()[0] || From 205c12c530458209941a309736debfb322592bdb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 16:53:28 +0200 Subject: [PATCH 284/365] Fix docs for :click-element --- doc/help/commands.asciidoc | 17 +++++++++++++++++ qutebrowser/browser/commands.py | 5 +++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 082997884..e73d6cf80 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -944,6 +944,7 @@ How many steps to zoom out. |============== |Command|Description |<>|Clear the currently entered key chain. +|<>|Click the element matching the given filter. |<>|Execute the command currently in the commandline. |<>|Go forward in the commandline history. |<>|Go back in the commandline history. @@ -1007,6 +1008,22 @@ How many steps to zoom out. === clear-keychain Clear the currently entered key chain. +[[click-element]] +=== click-element +Syntax: +:click-element [*--target* 'target'] 'filter' 'value'+ + +Click the element matching the given filter. + +The given filter needs to result in exactly one element, otherwise, an error is shown. + +==== positional arguments +* +'filter'+: How to filter the elements. id: Get an element based on its ID. + +* +'value'+: The value to filter for. + +==== optional arguments +* +*-t*+, +*--target*+: How to open the clicked element (normal/tab/tab-bg/window). + [[command-accept]] === command-accept Execute the command currently in the commandline. diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 797e84ae7..14e0df85d 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1540,9 +1540,10 @@ class CommandDispatcher: error is shown. Args: - filter: How to filter the elements. - id: Get an element based on its ID. + filter_: How to filter the elements. + id: Get an element based on its ID. value: The value to filter for. + target: How to open the clicked element (normal/tab/tab-bg/window). """ tab = self._current_widget() From d7110069bb1f7e42e6b081fe7beec288e6e9da77 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 16:57:47 +0200 Subject: [PATCH 285/365] Fix broken :repeat-command test --- tests/end2end/features/misc.feature | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index b6c606551..286ee8009 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -518,7 +518,8 @@ Feature: Various utility commands. When I run :hint And I run :leave-mode And I run :repeat-command - And I run :click-element id link + And I run :follow-hint a + And I wait until data/hello.txt is loaded Then the following tabs should be open: - data/hints/link_blank.html - data/hello.txt (active) From 745614e45d1cbb1754af9a8fc37c2d37aeca9d3d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 17:21:50 +0200 Subject: [PATCH 286/365] BDD: Wait until hints are ready after hinting With QtWebEngine, hinting happens async, so we need to wait for "hints: ..." in the log before we can actually follow a hint. --- tests/end2end/features/conftest.py | 14 +++ tests/end2end/features/downloads.feature | 12 +-- tests/end2end/features/hints.feature | 121 +++++++++-------------- tests/end2end/features/marks.feature | 6 +- tests/end2end/features/misc.feature | 2 +- tests/end2end/features/tabs.feature | 3 +- tests/end2end/test_insert_mode.py | 2 + 7 files changed, 73 insertions(+), 87 deletions(-) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 8b01bd6ef..9b3e84258 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -309,6 +309,20 @@ def fill_clipboard_multiline(quteproc, httpbin, what, content): fill_clipboard(quteproc, httpbin, what, textwrap.dedent(content)) +@bdd.when(bdd.parsers.parse('I hint with args "{args}"')) +def hint(quteproc, args): + quteproc.send_cmd(':hint {}'.format(args)) + quteproc.wait_for(message='hints: *') + + +@bdd.when(bdd.parsers.parse('I hint with args "{args}" and follow {letter}')) +def hint_and_follow(quteproc, args, letter): + args = args.replace('(testdata)', utils.abs_datapath()) + quteproc.send_cmd(':hint {}'.format(args)) + quteproc.wait_for(message='hints: *') + quteproc.send_cmd(':follow-hint {}'.format(letter)) + + ## Then diff --git a/tests/end2end/features/downloads.feature b/tests/end2end/features/downloads.feature index 6c094d421..35ad7da12 100644 --- a/tests/end2end/features/downloads.feature +++ b/tests/end2end/features/downloads.feature @@ -7,8 +7,7 @@ Feature: Downloading things from a website. Scenario: Downloading which redirects with closed tab (issue 889) When I set tabs -> last-close to blank And I open data/downloads/issue889.html - And I run :hint links download - And I run :follow-hint a + And I hint with args "links download" and follow a And I run :tab-close And I wait for "* Handling redirect" in the log Then no crash should happen @@ -16,8 +15,7 @@ Feature: Downloading things from a website. Scenario: Downloading with error in closed tab (issue 889) When I set tabs -> last-close to blank And I open data/downloads/issue889.html - And I run :hint links download - And I run :follow-hint s + And I hint with args "links download" and follow s And I run :tab-close And I wait for the error "Download error: * - server replied: NOT FOUND" And I run :download-retry @@ -28,8 +26,7 @@ Feature: Downloading things from a website. When I set completion -> download-path-suggestion to filename And I set storage -> prompt-download-directory to true And I open data/downloads/issue1243.html - And I run :hint links download - And I run :follow-hint a + And I hint with args "links download" and follow a And I wait for "Asking question text='Save file to:'>, *" in the log Then the error "Download error: No handler found for qute://!" should be shown @@ -37,8 +34,7 @@ Feature: Downloading things from a website. When I set completion -> download-path-suggestion to filename And I set storage -> prompt-download-directory to true And I open data/downloads/issue1214.html - And I run :hint links download - And I run :follow-hint a + And I hint with args "links download" and follow a And I wait for "Asking question text='Save file to:'>, *" in the log And I run :leave-mode Then no crash should happen diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature index f66f8d1a0..08818717f 100644 --- a/tests/end2end/features/hints.feature +++ b/tests/end2end/features/hints.feature @@ -6,24 +6,21 @@ Feature: Using hints Scenario: Using :follow-hint with an invalid index. When I open data/hints/html/simple.html - And I run :hint links normal - And I run :follow-hint xyz + And I hint with args "links normal" and follow xyz Then the error "No hint xyz!" should be shown ### Opening in current or new tab Scenario: Following a hint and force to open in current tab. When I open data/hints/link_blank.html - And I run :hint links current - And I run :follow-hint a + And I hint with args "links current" and follow a And I wait until data/hello.txt is loaded Then the following tabs should be open: - data/hello.txt (active) Scenario: Following a hint and allow to open in new tab. When I open data/hints/link_blank.html - And I run :hint links normal - And I run :follow-hint a + And I hint with args "links normal" and follow a And I wait until data/hello.txt is loaded Then the following tabs should be open: - data/hints/link_blank.html @@ -32,74 +29,65 @@ Feature: Using hints Scenario: Following a hint to link with sub-element and force to open in current tab. When I open data/hints/link_span.html And I run :tab-close - And I run :hint links current - And I run :follow-hint a + And I hint with args "links current" and follow a And I wait until data/hello.txt is loaded Then the following tabs should be open: - data/hello.txt (active) Scenario: Entering and leaving hinting mode (issue 1464) When I open data/hints/html/simple.html - And I run :hint + And I hint with args "all" And I run :fake-key -g Then no crash should happen Scenario: Using :hint spawn with flags and -- (issue 797) When I open data/hints/html/simple.html - And I run :hint -- all spawn -v echo - And I run :follow-hint a + And I hint with args "-- all spawn -v echo" and follow a Then the message "Command exited successfully." should be shown Scenario: Using :hint spawn with flags (issue 797) When I open data/hints/html/simple.html - And I run :hint all spawn -v echo - And I run :follow-hint a + And I hint with args "all spawn -v echo" and follow a Then the message "Command exited successfully." should be shown Scenario: Using :hint spawn with flags and --rapid (issue 797) When I open data/hints/html/simple.html - And I run :hint --rapid all spawn -v echo - And I run :follow-hint a + And I hint with args "--rapid all spawn -v echo" and follow a Then the message "Command exited successfully." should be shown @posix Scenario: Using :hint spawn with flags passed to the command (issue 797) When I open data/hints/html/simple.html - And I run :hint --rapid all spawn -v echo -e foo - And I run :follow-hint a + And I hint with args "--rapid all spawn -v echo -e foo" and follow a Then the message "Command exited successfully." should be shown Scenario: Using :hint run When I open data/hints/html/simple.html - And I run :hint all run message-info {hint-url} - And I run :follow-hint a + And I hint with args "all run message-info {hint-url}" and follow a Then the message "http://localhost:(port)/data/hello.txt" should be shown Scenario: Using :hint fill When I open data/hints/html/simple.html - And I run :hint all fill :message-info {hint-url} - And I run :follow-hint a + And I hint with args "all fill :message-info {hint-url}" and follow a And I press the key "" Then the message "http://localhost:(port)/data/hello.txt" should be shown @posix Scenario: Using :hint userscript When I open data/hints/html/simple.html - And I run :hint all userscript (testdata)/userscripts/echo_hint_text - And I run :follow-hint a + And I hint with args "all userscript (testdata)/userscripts/echo_hint_text" and follow a Then the message "Follow me!" should be shown Scenario: Yanking to primary selection without it being supported (#1336) When selection is not supported And I run :debug-set-fake-clipboard And I open data/hints/html/simple.html - And I run :hint links yank-primary - And I run :follow-hint a + And I hint with args "links yank-primary" and follow a Then the clipboard should contain "http://localhost:(port)/data/hello.txt" Scenario: Using hint --rapid to hit multiple buttons When I open data/hints/buttons.html - And I run :hint --rapid + And I hint with args "--rapid" And I run :follow-hint s And I run :follow-hint d And I run :follow-hint f @@ -109,20 +97,17 @@ Feature: Using hints Scenario: Using :hint run with a URL containing spaces When I open data/hints/html/with_spaces.html - And I run :hint all run message-info {hint-url} - And I run :follow-hint a + And I hint with args "all run message-info {hint-url}" and follow a Then the message "http://localhost:(port)/data/hello.txt" should be shown Scenario: Clicking an invalid link When I open data/invalid_link.html - And I run :hint all - And I run :follow-hint a + And I hint with args "all" and follow a Then the error "Invalid link clicked - *" should be shown Scenario: Hinting inputs without type When I open data/hints/input.html - And I run :hint inputs - And I run :follow-hint a + And I hint with args "inputs" and follow a And I wait for "Entering mode KeyMode.insert (reason: click)" in the log And I run :leave-mode # The actual check is already done above @@ -132,31 +117,26 @@ Feature: Using hints Scenario: Using :follow-hint inside an iframe When I open data/hints/iframe.html - And I run :hint links normal - And I run :follow-hint a + And I hint with args "links normal" and follow a Then "navigation request: url http://localhost:*/data/hello.txt, type NavigationTypeLinkClicked, *" should be logged ### FIXME currenly skipped, see https://github.com/The-Compiler/qutebrowser/issues/1525 @xfail_norun Scenario: Using :follow-hint inside a scrolled iframe When I open data/hints/iframe_scroll.html - And I run :hint all normal - And I run :follow-hint a + And I hint with args "all normal" and follow a And I run :scroll bottom - And I run :hint links normal - And I run :follow-hint a + And I hint wht args "links normal" and follow a Then "navigation request: url http://localhost:*/data/hello2.txt, type NavigationTypeLinkClicked, *" should be logged Scenario: Opening a link inside a specific iframe When I open data/hints/iframe_target.html - And I run :hint links normal - And I run :follow-hint a + And I hint with args "links normal" and follow a Then "navigation request: url http://localhost:*/data/hello.txt, type NavigationTypeLinkClicked, *" should be logged Scenario: Opening a link with specific target frame in a new tab When I open data/hints/iframe_target.html - And I run :hint links tab - And I run :follow-hint a + And I hint with args "links tab" and follow a And I wait until data/hello.txt is loaded Then the following tabs should be open: - data/hints/iframe_target.html @@ -169,7 +149,7 @@ Feature: Using hints And I set hints -> mode to number And I run :bind --force , message-error "This should not happen" And I open data/hints/html/simple.html - And I run :hint all + And I hint with args "all" And I press the key "f" And I wait until data/hello.txt is loaded And I press the key "," @@ -182,7 +162,7 @@ Feature: Using hints And I set hints -> mode to number And I run :bind --force , message-info "Keypress worked!" And I open data/hints/html/simple.html - And I run :hint all + And I hint with args "all" And I press the key "f" And I wait until data/hello.txt is loaded And I press the key "," @@ -193,9 +173,8 @@ Feature: Using hints Scenario: Hinting with a too short dictionary When I open data/hints/short_dict.html And I set hints -> mode to word - And I run :hint # Test letter fallback - And I run :follow-hint d + And I hint with args "all" and follow d Then the error "Not enough words in the dictionary." should be shown And data/numbers/5.txt should be loaded @@ -205,7 +184,7 @@ Feature: Using hints Scenario: Renumbering hints when filtering When I open data/hints/number.html And I set hints -> mode to number - And I run :hint all + And I hint with args "all" And I press the key "s" And I run :follow-hint 1 Then data/numbers/7.txt should be loaded @@ -214,7 +193,7 @@ Feature: Using hints Scenario: Keeping hint filter in rapid mode When I open data/hints/number.html And I set hints -> mode to number - And I run :hint all tab-bg --rapid + And I hint with args "all tab-bg --rapid" And I press the key "t" And I run :follow-hint 0 And I run :follow-hint 1 @@ -225,7 +204,7 @@ Feature: Using hints Scenario: Keeping hints filter when using backspace When I open data/hints/issue1186.html And I set hints -> mode to number - And I run :hint all + And I hint with args "all" And I press the key "x" And I press the key "0" And I press the key "" @@ -238,7 +217,7 @@ Feature: Using hints And I set hints -> mode to number And I set hints -> auto-follow to unique-match And I set hints -> auto-follow-timeout to 0 - And I run :hint all + And I hint with args "all" And I press the keys "ten pos" Then data/numbers/11.txt should be loaded @@ -246,15 +225,14 @@ Feature: Using hints When I open data/hints/number.html And I set hints -> mode to number And I set hints -> scatter to true - And I run :hint all - And I run :follow-hint 00 + And I hint with args "all" and follow 00 Then data/numbers/1.txt should be loaded # https://github.com/The-Compiler/qutebrowser/issues/1559 Scenario: Filtering all hints in number mode When I open data/hints/number.html And I set hints -> mode to number - And I run :hint all + And I hint with args "all" And I press the key "2" And I wait for "Leaving mode KeyMode.hint (reason: all filtered)" in the log Then no crash should happen @@ -263,16 +241,15 @@ Feature: Using hints Scenario: Using rapid number hinting twice When I open data/hints/number.html And I set hints -> mode to number - And I run :hint --rapid + And I hint with args "--rapid" And I run :leave-mode - And I run :hint --rapid - And I run :follow-hint 00 + And I hint with args "--rapid" and follow 00 Then data/numbers/1.txt should be loaded Scenario: Using a specific hints mode When I open data/hints/number.html And I set hints -> mode to letter - And I run :hint --mode number all + And I hint with args "--mode number all" And I press the key "s" And I run :follow-hint 1 Then data/numbers/7.txt should be loaded @@ -283,7 +260,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to letter And I set hints -> auto-follow to always - And I run :hint + And I hint with args "all" Then data/hello.txt should be loaded # unique-match is actually the same as full-match in letter mode @@ -291,7 +268,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to letter And I set hints -> auto-follow to unique-match - And I run :hint + And I hint with args "all" And I press the key "a" Then data/hello.txt should be loaded @@ -299,7 +276,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to letter And I set hints -> auto-follow to full-match - And I run :hint + And I hint with args "all" And I press the key "a" Then data/hello.txt should be loaded @@ -307,7 +284,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to letter And I set hints -> auto-follow to never - And I run :hint + And I hint with args "all" And I press the key "a" Then "Leaving mode KeyMode.hint (reason: followed)" should not be logged @@ -315,7 +292,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to letter And I set hints -> auto-follow to never - And I run :hint + And I hint with args "all" And I press the key "a" And I press the key "" Then data/hello.txt should be loaded @@ -324,14 +301,14 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to number And I set hints -> auto-follow to always - And I run :hint + And I hint with args "all" Then data/hello.txt should be loaded Scenario: Using hints -> auto-follow == 'unique-match' in number mode When I open data/hints/html/simple.html And I set hints -> mode to number And I set hints -> auto-follow to unique-match - And I run :hint + And I hint with args "all" And I press the key "f" Then data/hello.txt should be loaded @@ -339,7 +316,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to number And I set hints -> auto-follow to full-match - And I run :hint + And I hint with args "all" # this actually presses the keys one by one And I press the key "follow me!" Then data/hello.txt should be loaded @@ -348,7 +325,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to number And I set hints -> auto-follow to never - And I run :hint + And I hint with args "all" # this actually presses the keys one by one And I press the key "follow me!" Then "Leaving mode KeyMode.hint (reason: followed)" should not be logged @@ -357,7 +334,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to number And I set hints -> auto-follow to never - And I run :hint + And I hint with args "all" # this actually presses the keys one by one And I press the key "follow me!" And I press the key "" @@ -367,14 +344,14 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to word And I set hints -> auto-follow to always - And I run :hint + And I hint with args "all" Then data/hello.txt should be loaded Scenario: Using hints -> auto-follow == 'unique-match' in word mode When I open data/hints/html/simple.html And I set hints -> mode to word And I set hints -> auto-follow to unique-match - And I run :hint + And I hint with args "all" # the link gets "hello" as the hint And I press the key "h" Then data/hello.txt should be loaded @@ -383,7 +360,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to word And I set hints -> auto-follow to full-match - And I run :hint + And I hint with args "all" # this actually presses the keys one by one And I press the key "hello" Then data/hello.txt should be loaded @@ -392,7 +369,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to word And I set hints -> auto-follow to never - And I run :hint + And I hint with args "all" # this actually presses the keys one by one And I press the key "hello" Then "Leaving mode KeyMode.hint (reason: followed)" should not be logged @@ -401,7 +378,7 @@ Feature: Using hints When I open data/hints/html/simple.html And I set hints -> mode to word And I set hints -> auto-follow to never - And I run :hint + And I hint with args "all" # this actually presses the keys one by one And I press the key "hello" And I press the key "" diff --git a/tests/end2end/features/marks.feature b/tests/end2end/features/marks.feature index 6491a67e1..665c0d707 100644 --- a/tests/end2end/features/marks.feature +++ b/tests/end2end/features/marks.feature @@ -65,8 +65,7 @@ Feature: Setting positional marks Then the page should be scrolled to 10 10 Scenario: Jumping back after following a link - When I run :hint links normal - And I run :follow-hint s + When I hint with args "links normal" and follow s And I wait until data/marks.html#bottom is loaded And I run :jump-mark "'" Then the page should be scrolled to 0 0 @@ -86,7 +85,6 @@ Feature: Setting positional marks Scenario: Hovering a hint does not set the ' mark When I run :scroll-px 30 20 And I run :scroll-perc 0 - And I run :hint links hover - And I run :follow-hint s + And I hint with args "links hover" and follow s And I run :jump-mark "'" Then the page should be scrolled to 30 20 diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 286ee8009..8d93ceede 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -515,7 +515,7 @@ Feature: Various utility commands. Scenario: :repeat-command with mode-switching command Given I open data/hints/link_blank.html And I run :tab-only - When I run :hint + When I hint with args "all" And I run :leave-mode And I run :repeat-command And I run :follow-hint a diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 665931ce1..3486ee05a 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -794,8 +794,7 @@ Feature: Tab management Scenario: opening links with tabs->background-tabs true When I set tabs -> background-tabs to true And I open data/hints/html/simple.html - And I run :hint all tab - And I run :follow-hint a + And I hint with args "all tab" and follow a And I wait until data/hello.txt is loaded Then the following tabs should be open: - data/hints/html/simple.html (active) diff --git a/tests/end2end/test_insert_mode.py b/tests/end2end/test_insert_mode.py index 552fffbb4..ad4cce33c 100644 --- a/tests/end2end/test_insert_mode.py +++ b/tests/end2end/test_insert_mode.py @@ -52,6 +52,7 @@ def test_insert_mode(file_name, elem_id, source, input_text, auto_insert, quteproc.send_cmd(':insert-text {clipboard}') quteproc.send_cmd(':hint all') + quteproc.wait_for(message='hints: *') quteproc.send_cmd(':follow-hint a') quteproc.wait_for(message='Clicked editable element!') quteproc.send_cmd(':enter-mode caret') @@ -76,6 +77,7 @@ def test_auto_leave_insert_mode(quteproc): quteproc.press_keys('abcd') quteproc.send_cmd(':hint all') + quteproc.wait_for(message='hints: *') # Select the disabled input box to leave insert mode quteproc.send_cmd(':follow-hint s') From 3dccd156631532f068c129f1e303648e0e970c09 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 17:41:34 +0200 Subject: [PATCH 287/365] bdd: Handle @qtwebengine_todo tags --- tests/conftest.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index e6fff31ae..801b699a2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -184,13 +184,12 @@ def pytest_sessionfinish(exitstatus): if not getattr(sys, 'frozen', False): - def pytest_bdd_apply_tag(tag, function): + def _get_version_tag(tag): """Handle tags like pyqt>=5.3.1 for BDD tests. This transforms e.g. pyqt>=5.3.1 into an appropriate @pytest.mark.skip marker, and falls back to pytest-bdd's implementation for all other casesinto an appropriate @pytest.mark.skip marker, and falls back to - pytest-bdd's implementation for all other cases """ version_re = re.compile(r""" (?Pqt|pyqt) @@ -200,7 +199,6 @@ if not getattr(sys, 'frozen', False): match = version_re.match(tag) if not match: - # Use normal tag mapping return None operators = { @@ -217,15 +215,33 @@ if not getattr(sys, 'frozen', False): version = match.group('version') if package == 'qt': - mark = pytest.mark.skipif(qtutils.version_check(version, op), + return pytest.mark.skipif(qtutils.version_check(version, op), reason='Needs ' + tag) elif package == 'pyqt': major, minor, patch = [int(e) for e in version.split('.')] hex_version = (major << 16) | (minor << 8) | patch - mark = pytest.mark.skipif(not op(PYQT_VERSION, hex_version), + return pytest.mark.skipif(not op(PYQT_VERSION, hex_version), reason='Needs ' + tag) else: raise ValueError("Invalid package {!r}".format(package)) - mark(function) - return True + def _get_qtwebengine_tag(tag): + """Handle a @qtwebengine_todo tag.""" + if not tag.startswith('qtwebengine_todo:'): + return None + desc = tag.split(':', maxsplit=1)[1] + return pytest.mark.qtwebengine_todo(desc) + + def pytest_bdd_apply_tag(tag, function): + """Handle custom tags for BDD tests. + + This tries various functions, and if none knows how to handle this tag, + it returns None so it falls back to pytest-bdd's implementation. + """ + funcs = [_get_version_tag, _get_qtwebengine_tag] + for func in funcs: + mark = func(tag) + if mark is not None: + mark(function) + return True + return None From 5e8254d4702622b86bef4b5cdf599c44b9636a1d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 17:43:50 +0200 Subject: [PATCH 288/365] bdd: Mark some hint tests as @qtwebengine_todo --- tests/end2end/features/hints.feature | 3 +++ tests/end2end/features/misc.feature | 1 + 2 files changed, 4 insertions(+) diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature index 08818717f..f60db845f 100644 --- a/tests/end2end/features/hints.feature +++ b/tests/end2end/features/hints.feature @@ -11,6 +11,7 @@ Feature: Using hints ### Opening in current or new tab + @qtwebengine_todo: createWindow is not implemented yet Scenario: Following a hint and force to open in current tab. When I open data/hints/link_blank.html And I hint with args "links current" and follow a @@ -18,6 +19,7 @@ Feature: Using hints Then the following tabs should be open: - data/hello.txt (active) + @qtwebengine_todo: createWindow is not implemented yet Scenario: Following a hint and allow to open in new tab. When I open data/hints/link_blank.html And I hint with args "links normal" and follow a @@ -26,6 +28,7 @@ Feature: Using hints - data/hints/link_blank.html - data/hello.txt (active) + @qtwebengine_todo: createWindow is not implemented yet Scenario: Following a hint to link with sub-element and force to open in current tab. When I open data/hints/link_span.html And I run :tab-close diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 8d93ceede..f0fb14c96 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -512,6 +512,7 @@ Feature: Various utility commands. Then the page should not be scrolled And the error "prompt-accept: This command is only allowed in prompt/yesno mode." should be shown + @qtwebengine_todo: createWindow is not implemented yet Scenario: :repeat-command with mode-switching command Given I open data/hints/link_blank.html And I run :tab-only From 0b9aec873f1c1196ea87723a0c0511fac7bef74c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 17:44:35 +0200 Subject: [PATCH 289/365] tests: Accept HTTP "not modified" as status It seems like QtWebEngine sends some caching headers QtWebKit didn't? --- tests/end2end/fixtures/webserver.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/end2end/fixtures/webserver.py b/tests/end2end/fixtures/webserver.py index 0d9357dff..1e4e25826 100644 --- a/tests/end2end/fixtures/webserver.py +++ b/tests/end2end/fixtures/webserver.py @@ -73,9 +73,10 @@ class Request(testprocess.Line): '/status/404': [http.client.NOT_FOUND], '/cookies/set': [http.client.FOUND], } + default_statuses = [http.client.OK, http.client.NOT_MODIFIED] sanitized = QUrl('http://localhost' + self.path).path() # Remove ?foo - expected_statuses = path_to_statuses.get(sanitized, [http.client.OK]) + expected_statuses = path_to_statuses.get(sanitized, default_statuses) assert self.status in expected_statuses def __eq__(self, other): From 2a0e503644e73336f9e3f37ee4c9d462b150fbb0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 17:46:12 +0200 Subject: [PATCH 290/365] QtWebEngine: Don't raise CommandError with no hint --- qutebrowser/browser/hints.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 59887a1ec..538f91928 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -567,7 +567,8 @@ class HintManager(QObject): filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True) elems = [e for e in elems if filterfunc(e)] if not elems: - raise cmdexc.CommandError("No elements found.") + message.error(self._win_id, "No elements found.", immediately=True) + return strings = self._hint_strings(elems) log.hints.debug("hints: {}".format(', '.join(strings))) From 4b7a3db0ebe8090df5df9b25727748f61cd594c0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 17:50:52 +0200 Subject: [PATCH 291/365] tests: QtWebEngine: Make hints.feature work --- tests/end2end/features/hints.feature | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature index f60db845f..be79dcfa1 100644 --- a/tests/end2end/features/hints.feature +++ b/tests/end2end/features/hints.feature @@ -118,6 +118,7 @@ Feature: Using hints ### iframes + @qtwebengine_todo: Hinting in iframes is not implemented yet Scenario: Using :follow-hint inside an iframe When I open data/hints/iframe.html And I hint with args "links normal" and follow a @@ -132,11 +133,13 @@ Feature: Using hints And I hint wht args "links normal" and follow a Then "navigation request: url http://localhost:*/data/hello2.txt, type NavigationTypeLinkClicked, *" should be logged + @qtwebengine_todo: createWindow is not implemented yet Scenario: Opening a link inside a specific iframe When I open data/hints/iframe_target.html And I hint with args "links normal" and follow a Then "navigation request: url http://localhost:*/data/hello.txt, type NavigationTypeLinkClicked, *" should be logged + @qtwebengine_todo: createWindow is not implemented yet Scenario: Opening a link with specific target frame in a new tab When I open data/hints/iframe_target.html And I hint with args "links tab" and follow a @@ -193,6 +196,7 @@ Feature: Using hints Then data/numbers/7.txt should be loaded # https://github.com/The-Compiler/qutebrowser/issues/576 + @qtwebengine_todo: createWindow is not implemented yet Scenario: Keeping hint filter in rapid mode When I open data/hints/number.html And I set hints -> mode to number From dfed2f9c9c90c28ea768e72194b4d91b0fe0a771 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:05:48 +0200 Subject: [PATCH 292/365] WebEngine: Don't save title if generated from URL --- qutebrowser/browser/browsertab.py | 4 +--- qutebrowser/browser/webengine/webenginetab.py | 16 +++++++++++++++- qutebrowser/browser/webkit/webkittab.py | 6 ++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index b1e0e44f1..8e6b30d25 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -638,9 +638,7 @@ class AbstractTab(QWidget): @pyqtSlot() def _on_history_trigger(self): """Emit add_history_item when triggered by backend-specific signal.""" - url = self.url() - requested_url = self.url(requested=True) - self.add_history_item.emit(url, requested_url, self.title()) + raise NotImplementedError @pyqtSlot(int) def _on_load_progress(self, perc): diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 3f804e9d5..f43622208 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -24,7 +24,7 @@ import functools -from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPoint +from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPoint, QUrl from PyQt5.QtGui import QKeyEvent, QIcon from PyQt5.QtWidgets import QApplication # pylint: disable=no-name-in-module,import-error,useless-suppression @@ -487,6 +487,20 @@ class WebEngineTab(browsertab.AbstractTab): def clear_ssl_errors(self): log.stub() + @pyqtSlot() + def _on_history_trigger(self): + url = self.url() + requested_url = self.url(requested=True) + + # Don't save the title if it's generated from the URL + title = self.title() + title_url = QUrl(url) + title_url.setScheme('') + if title == title_url.toDisplayString(QUrl.RemoveScheme).strip('/'): + title = "" + + self.add_history_item.emit(url, requested_url, title) + def _connect_signals(self): view = self._widget page = view.page() diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 16f5a8a15..bd7f0a0e2 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -640,6 +640,12 @@ class WebKitTab(browsertab.AbstractTab): nam = self._widget.page().networkAccessManager() nam.clear_all_ssl_errors() + @pyqtSlot() + def _on_history_trigger(self): + url = self.url() + requested_url = self.url(requested=True) + self.add_history_item.emit(url, requested_url, self.title()) + def set_html(self, html, base_url): self._widget.setHtml(html, base_url) From 8d381aaa01ac79ba8647ad01235df4cb7176bda3 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:16:33 +0200 Subject: [PATCH 293/365] tests: Improve @qtwebengine_* markers This uses xfail for @qtwebengine_todo and adds a new @qtwebengine_skip marker. --- pytest.ini | 1 + tests/end2end/features/conftest.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/pytest.ini b/pytest.ini index 3c63d16f9..812063e54 100644 --- a/pytest.ini +++ b/pytest.ini @@ -16,6 +16,7 @@ markers = ci: Tests which should only run on CI. flaky_once: Try to rerun this test once if it fails qtwebengine_todo: Features still missing with QtWebEngine + qtwebengine_skip: Tests not applicable with QtWebEngine qt_log_level_fail = WARNING qt_log_ignore = ^SpellCheck: .* diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 9b3e84258..97f134b64 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -39,11 +39,20 @@ def pytest_collection_modifyitems(config, items): """Apply @qtwebengine_* markers.""" webengine = config.getoption('--qute-bdd-webengine') + markers = { + 'qtwebengine_todo': ('QtWebEngine TODO', pytest.mark.xfail), + 'qtwebengine_skip': ('Skipped with QtWebEngine', pytest.mark.skipif), + } + for item in items: - marker = item.get_marker('qtwebengine_todo') - if marker: - text = 'QtWebEngine TODO: {}'.format(marker.args[0]) - item.add_marker(pytest.mark.skipif(webengine, reason=text)) + for name, (prefix, pytest_mark) in markers.items(): + marker = item.get_marker(name) + if marker: + if marker.args: + text = '{}: {}'.format(prefix, marker.args[0]) + else: + text = prefix + item.add_marker(pytest_mark(webengine, reason=text)) @pytest.hookimpl(hookwrapper=True) From e950b0902718070274d2f2d69e3d194d47a9bb80 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:17:01 +0200 Subject: [PATCH 294/365] tests: Get history.feature to work with WebEngine --- tests/end2end/features/history.feature | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/end2end/features/history.feature b/tests/end2end/features/history.feature index 1cd680d7e..badb26aac 100644 --- a/tests/end2end/features/history.feature +++ b/tests/end2end/features/history.feature @@ -34,13 +34,14 @@ Feature: Page history Then the history file should contain: http://localhost:(port)/data/%C3%A4%C3%B6%C3%BC.html Chäschüechli - @flaky_once + @flaky_once @qtwebengine_todo: Error page message is not implemented Scenario: History with an error When I run :open file:///does/not/exist And I wait for "Error while loading file:///does/not/exist: Error opening /does/not/exist: *" in the log Then the history file should contain: file:///does/not/exist Error loading page: file:///does/not/exist + @qtwebengine_todo: Error page message is not implemented Scenario: History with a 404 When I open status/404 without waiting And I wait for "Error while loading http://localhost:*/status/404: NOT FOUND" in the log @@ -54,6 +55,7 @@ Feature: Page history ## Bugs + @qtwebengine_skip Scenario: Opening a valid URL which turns out invalid When I set general -> auto-search to true And I run :open http://foo%40bar@baz From 2d97ffa3235658def264e966c343e37a149a23ca Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:20:25 +0200 Subject: [PATCH 295/365] bdd: xfail in compare_session with WebEngine --- tests/end2end/features/conftest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 97f134b64..844738499 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -423,12 +423,14 @@ def javascript_message_not_logged(quteproc, message): @bdd.then(bdd.parsers.parse("The session should look like:\n{expected}")) -def compare_session(quteproc, expected): +def compare_session(request, quteproc, expected): """Compare the current sessions against the given template. partial_compare is used, which means only the keys/values listed will be compared. """ + if request.config.getoption('--qute-bdd-webengine'): + pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented") quteproc.compare_session(expected) From 001e839ca9296cfeba9bca09966db932045ebdcd Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:22:44 +0200 Subject: [PATCH 296/365] Remove qtwebengine_todo for backforward.feature --- tests/end2end/features/test_backforward_bdd.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/end2end/features/test_backforward_bdd.py b/tests/end2end/features/test_backforward_bdd.py index dd3ad7a25..ede51988a 100644 --- a/tests/end2end/features/test_backforward_bdd.py +++ b/tests/end2end/features/test_backforward_bdd.py @@ -17,9 +17,5 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -import pytest import pytest_bdd as bdd bdd.scenarios('backforward.feature') - - -pytestmark = pytest.mark.qtwebengine_todo("Tests broken for various reasons") From ab4e442602808ec66b2370db2f367de249e4647e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:26:31 +0200 Subject: [PATCH 297/365] QtWebEngine: Don't bother running caret.feature --- tests/end2end/features/conftest.py | 3 ++- tests/end2end/features/test_caret_bdd.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 844738499..1509a4ddd 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -52,7 +52,8 @@ def pytest_collection_modifyitems(config, items): text = '{}: {}'.format(prefix, marker.args[0]) else: text = prefix - item.add_marker(pytest_mark(webengine, reason=text)) + item.add_marker(pytest_mark(webengine, reason=text, + **marker.kwargs)) @pytest.hookimpl(hookwrapper=True) diff --git a/tests/end2end/features/test_caret_bdd.py b/tests/end2end/features/test_caret_bdd.py index 56dabdd72..aa42241c4 100644 --- a/tests/end2end/features/test_caret_bdd.py +++ b/tests/end2end/features/test_caret_bdd.py @@ -24,7 +24,8 @@ import pytest_bdd as bdd from end2end.features.test_yankpaste_bdd import init_fake_clipboard -pytestmark = pytest.mark.qtwebengine_todo("Caret mode is not implemented") +pytestmark = pytest.mark.qtwebengine_todo("Caret mode is not implemented", + run=False) bdd.scenarios('caret.feature') From f03cd5022e10f780df17e86437fc9fb10edbf506 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:27:24 +0200 Subject: [PATCH 298/365] pylint doesn't know pytest.xfail --- tests/end2end/features/conftest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 1509a4ddd..da6451b75 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -431,7 +431,9 @@ def compare_session(request, quteproc, expected): compared. """ if request.config.getoption('--qute-bdd-webengine'): + # pylint: disable=no-member pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented") + # pylint: enable=no-member quteproc.compare_session(expected) From 0c50e7dfb9df887af65122cadda8ff8053c73607 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:28:41 +0200 Subject: [PATCH 299/365] WebEngine: Don't bother running downloads.feature --- tests/end2end/features/test_downloads_bdd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/end2end/features/test_downloads_bdd.py b/tests/end2end/features/test_downloads_bdd.py index 66b922c50..3549b3c61 100644 --- a/tests/end2end/features/test_downloads_bdd.py +++ b/tests/end2end/features/test_downloads_bdd.py @@ -26,7 +26,8 @@ import pytest_bdd as bdd bdd.scenarios('downloads.feature') -pytestmark = pytest.mark.qtwebengine_todo("Downloads not implemented yet") +pytestmark = pytest.mark.qtwebengine_todo("Downloads not implemented yet", + run=False) @bdd.given("I set up a temporary download dir") From e477b810bd1142e60241e6e3c38c2c9f4be934a5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:29:10 +0200 Subject: [PATCH 300/365] bdd: Mark backforwards test as qtwebengine_todo --- tests/end2end/features/backforward.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/end2end/features/backforward.feature b/tests/end2end/features/backforward.feature index b37ed3165..e9e5f0c7b 100644 --- a/tests/end2end/features/backforward.feature +++ b/tests/end2end/features/backforward.feature @@ -19,6 +19,7 @@ Feature: Going back and forward. - active: true url: http://localhost:*/data/backforward/2.txt + @qtwebengine_todo: FIXME why is this broken? Scenario: Going back in a new tab Given I open data/backforward/1.txt When I open data/backforward/2.txt From 2cbaf0ccb3d906d8ab9b1bc262b517c03a9c0419 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:44:07 +0200 Subject: [PATCH 301/365] Add @qtwebengine_todo in javascript.feature --- tests/end2end/features/javascript.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/end2end/features/javascript.feature b/tests/end2end/features/javascript.feature index bb34db70b..b95e4e7d7 100644 --- a/tests/end2end/features/javascript.feature +++ b/tests/end2end/features/javascript.feature @@ -9,6 +9,7 @@ Feature: Javascript stuff # https://github.com/The-Compiler/qutebrowser/issues/906 + @qtwebengine_todo: createWindow is not implemented yet Scenario: Closing a JS window twice (issue 906) When I open about:blank And I open data/javascript/issue906.html in a new tab From c0ffcfc585f764904929e8518860bbcc336e8751 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 18:44:33 +0200 Subject: [PATCH 302/365] QtWebEngine: Make :fake-key work --- qutebrowser/browser/commands.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 14e0df85d..4357732dd 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1946,20 +1946,20 @@ class CommandDispatcher: keyinfo.modifiers, keyinfo.text) if global_: - receiver = QApplication.focusWindow() - if receiver is None: + window = QApplication.focusWindow() + if window is None: raise cmdexc.CommandError("No focused window!") + QApplication.postEvent(window, press_event) + QApplication.postEvent(window, release_event) else: try: tab = objreg.get('tab', scope='tab', tab='current') except objreg.RegistryUnavailableError: raise cmdexc.CommandError("No focused webview!") - # pylint: disable=protected-access - receiver = tab._widget - # pylint: enable=protected-access - QApplication.postEvent(receiver, press_event) - QApplication.postEvent(receiver, release_event) + tab = self._current_widget() + tab.post_event(press_event) + tab.post_event(release_event) @cmdutils.register(instance='command-dispatcher', scope='window', debug=True) From b6c96855c828ad7a8b5c66b2ea5d7514928c2ce4 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Thu, 18 Aug 2016 19:05:35 +0200 Subject: [PATCH 303/365] Rewrite paste -s/pP --- qutebrowser/config/configdata.py | 1 + tests/unit/config/test_config.py | 1 + 2 files changed, 2 insertions(+) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index ab2638ec4..a4c34bc12 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1712,6 +1712,7 @@ CHANGED_KEY_COMMANDS = [ (re.compile(r'^yank-selected'), r'yank selection'), (re.compile(r'^paste$'), r'open -- {clipboard}'), + (re.compile(r'^paste -s$'), r'open -- {primary}'), (re.compile(r'^paste -([twb])$'), r'open -\1 -- {clipboard}'), (re.compile(r'^paste -([twb])s$'), r'open -\1 -- {primary}'), (re.compile(r'^paste -s([twb])$'), r'open -\1 -- {primary}'), diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index 0194e4480..70d9e0ddf 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -356,6 +356,7 @@ class TestKeyConfigParser: ('yank -ps', 'yank pretty-url -s'), ('paste', 'open -- {clipboard}'), + ('paste -s', 'open -- {primary}'), ('paste -t', 'open -t -- {clipboard}'), ('paste -ws', 'open -w -- {primary}'), From 105c1952a8834a147a238cbcd718125de28434dc Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 19:20:48 +0200 Subject: [PATCH 304/365] bdd: Skip scroll checks with QtWebEngine --- tests/end2end/features/conftest.py | 12 ++++++++++-- tests/end2end/features/test_marks_bdd.py | 7 ++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index da6451b75..0276e6244 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -558,7 +558,11 @@ def _get_scroll_values(quteproc): @bdd.then(bdd.parsers.re(r"the page should be scrolled " r"(?Phorizontally|vertically)")) -def check_scrolled(quteproc, direction): +def check_scrolled(request, quteproc, direction): + if request.config.getoption('--qute-bdd-webengine'): + # pylint: disable=no-member + pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented") + # pylint: enable=no-member x, y = _get_scroll_values(quteproc) if direction == 'horizontally': assert x != 0 @@ -569,7 +573,11 @@ def check_scrolled(quteproc, direction): @bdd.then("the page should not be scrolled") -def check_not_scrolled(quteproc): +def check_not_scrolled(request, quteproc): + if request.config.getoption('--qute-bdd-webengine'): + # pylint: disable=no-member + pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented") + # pylint: enable=no-member x, y = _get_scroll_values(quteproc) assert x == 0 assert y == 0 diff --git a/tests/end2end/features/test_marks_bdd.py b/tests/end2end/features/test_marks_bdd.py index b2777cbe4..01d9aa1ae 100644 --- a/tests/end2end/features/test_marks_bdd.py +++ b/tests/end2end/features/test_marks_bdd.py @@ -17,12 +17,17 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . +import pytest import pytest_bdd as bdd bdd.scenarios('marks.feature') @bdd.then(bdd.parsers.parse("the page should be scrolled to {x} {y}")) -def check_y(quteproc, x, y): +def check_y(request, quteproc, x, y): + if request.config.getoption('--qute-bdd-webengine'): + # pylint: disable=no-member + pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented") + # pylint: enable=no-member data = quteproc.get_session() pos = data['windows'][0]['tabs'][0]['history'][-1]['scroll-pos'] assert int(x) == pos['x'] From d298787b1a6c42e023ff3997211df3f118f82dda Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 19:22:18 +0200 Subject: [PATCH 305/365] bdd: Make marks.feature work with QtWebEngine --- tests/end2end/features/marks.feature | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/end2end/features/marks.feature b/tests/end2end/features/marks.feature index 665c0d707..af09187bb 100644 --- a/tests/end2end/features/marks.feature +++ b/tests/end2end/features/marks.feature @@ -55,6 +55,7 @@ Feature: Setting positional marks And I run :jump-mark b Then the error "Mark b is not set" should be shown + @qtwebengine_todo: Does not emit loaded signal for fragments? Scenario: Jumping to a local mark after changing fragments When I open data/marks.html#top And I run :scroll 'top' @@ -64,6 +65,7 @@ Feature: Setting positional marks And I run :jump-mark a Then the page should be scrolled to 10 10 + @qtwebengine_todo: Does not emit loaded signal for fragments? Scenario: Jumping back after following a link When I hint with args "links normal" and follow s And I wait until data/marks.html#bottom is loaded From 577f1b850aa39a2d553a9c2815b5716b479c43db Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 19:26:06 +0200 Subject: [PATCH 306/365] bdd: Make :jseval tests work with QtWebEngine --- tests/end2end/features/misc.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index f0fb14c96..dea364fd5 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -64,19 +64,19 @@ Feature: Various utility commands. Scenario: :jseval When I set general -> log-javascript-console to info And I run :jseval console.log("Hello from JS!"); - And I wait for "[:0] Hello from JS!" in the log + And I wait for "[:*] Hello from JS!" in the log Then the message "No output or error" should be shown Scenario: :jseval without logging When I set general -> log-javascript-console to none And I run :jseval console.log("Hello from JS!"); Then the message "No output or error" should be shown - And "[:0] Hello from JS!" should not be logged + And "[:*] Hello from JS!" should not be logged Scenario: :jseval with --quiet When I set general -> log-javascript-console to info And I run :jseval --quiet console.log("Hello from JS!"); - And I wait for "[:0] Hello from JS!" in the log + And I wait for "[:*] Hello from JS!" in the log Then "No output or error" should not be logged Scenario: :jseval with a value From 788eebc1ad1d67c2e62ac32bb5bc733d1d5b4563 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 19:29:34 +0200 Subject: [PATCH 307/365] bdd: Ignore "Running without the SUID sandbox!" --- tests/end2end/fixtures/quteprocess.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index af6cff432..5a6385ead 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -196,6 +196,9 @@ class QuteProc(testprocess.Process): except testprocess.InvalidLine: if not line.strip(): return None + elif 'Running without the SUID sandbox!' in line: + # QtWebEngine error + return None elif is_ignored_qt_message(line): return None else: From ccc676c04fbbf78c0911fa6fd4ed4313b0d3c835 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 19:32:16 +0200 Subject: [PATCH 308/365] Handle CommandError in show_source_cb --- qutebrowser/browser/commands.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 4357732dd..2ee90d843 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1356,7 +1356,11 @@ class CommandDispatcher: formatter = pygments.formatters.HtmlFormatter(full=True, linenos='table') highlighted = pygments.highlight(source, lexer, formatter) - current_url = self._current_url() + try: + current_url = self._current_url() + except cmdexc.CommandError as e: + message.error(self._win_id, str(e)) + return new_tab = self._tabbed_browser.tabopen(explicit=True) new_tab.set_html(highlighted, current_url) new_tab.data.viewing_source = True From ec59bfb5840bbf0da9b0a5d6fb2c0465442d87a8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 20:33:53 +0200 Subject: [PATCH 309/365] bdd: Handle @qtwebengine_skip tag --- tests/conftest.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 801b699a2..f08208406 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -226,11 +226,15 @@ if not getattr(sys, 'frozen', False): raise ValueError("Invalid package {!r}".format(package)) def _get_qtwebengine_tag(tag): - """Handle a @qtwebengine_todo tag.""" - if not tag.startswith('qtwebengine_todo:'): + """Handle a @qtwebengine_* tag.""" + pytest_marks = { + 'qtwebengine_todo': pytest.mark.qtwebengine_todo, + 'qtwebengine_skip': pytest.mark.qtwebengine_skip, + } + if not any(tag.startswith(t + ':') for t in pytest_marks): return None - desc = tag.split(':', maxsplit=1)[1] - return pytest.mark.qtwebengine_todo(desc) + name, desc = tag.split(':', maxsplit=1) + return pytest_marks[name](desc) def pytest_bdd_apply_tag(tag, function): """Handle custom tags for BDD tests. From 25faa0419643d00765918ad3e7d2753b8b20b718 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 20:34:03 +0200 Subject: [PATCH 310/365] bdd: Add qtwebengine tags in misc.feature --- tests/end2end/features/misc.feature | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index dea364fd5..8dfe8c4dc 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -190,6 +190,7 @@ Feature: Various utility commands. # :view-source + @qtwebengine_skip: Flaky due to :view-source being async Scenario: :view-source Given I open data/hello.txt When I run :tab-only @@ -204,6 +205,7 @@ Feature: Various utility commands. history: [] And the page source should look like misc/hello.txt.html + @qtwebengine_skip: Flaky due to :view-source being async Scenario: :view-source on source page. When I open data/hello.txt And I run :view-source @@ -222,6 +224,7 @@ Feature: Various utility commands. # :help + @qtwebengine_todo: :help is not implemented yet Scenario: :help without topic When I run :tab-only And I run :help @@ -233,6 +236,7 @@ Feature: Various utility commands. When I run :help foo Then the error "Invalid help topic foo!" should be shown + @qtwebengine_todo: :help is not implemented yet Scenario: :help with command When the documentation is up to date And I run :tab-only @@ -245,6 +249,7 @@ Feature: Various utility commands. When I run :help :foo Then the error "Invalid command foo!" should be shown + @qtwebengine_todo: :help is not implemented yet Scenario: :help with setting When the documentation is up to date And I run :tab-only @@ -265,6 +270,7 @@ Feature: Various utility commands. When I run :help general->bar Then the error "Invalid option bar!" should be shown + @qtwebengine_todo: :help is not implemented yet Scenario: :help with -t When I open about:blank And I run :tab-only @@ -288,18 +294,21 @@ Feature: Various utility commands. # pdfjs support + @qtwebengine_todo: pdfjs is not implemented yet Scenario: pdfjs is used for pdf files Given pdfjs is available When I set content -> enable-pdfjs to true And I open data/misc/test.pdf Then the javascript message "PDF * [*] (PDF.js: *)" should be logged + @qtwebengine_todo: pdfjs is not implemented yet Scenario: pdfjs is not used when disabled When I set content -> enable-pdfjs to false And I set storage -> prompt-download-directory to false And I open data/misc/test.pdf Then "Download finished" should be logged + @qtwebengine_todo: pdfjs is not implemented yet Scenario: Downloading a pdf via pdf.js button (issue 1214) Given pdfjs is available # WORKAROUND to prevent the "Painter ended with 2 saved states" warning @@ -339,6 +348,8 @@ Feature: Various utility commands. And I run :debug-pyeval QApplication.instance().activeModalWidget().close() Then no crash should happen + # FIXME:qtwebengine use a finer skipping here + @qtwebengine_skip: printing to pdf is not implemented with older Qt versions Scenario: print --pdf When I open data/hello.txt And I run :print --pdf (tmpdir)/hello.pdf @@ -346,12 +357,13 @@ Feature: Various utility commands. Then the PDF hello.pdf should exist in the tmpdir # :pyeval - + @qtwebengine_todo: qute:pyeval is not implemented yet Scenario: Running :pyeval When I run :debug-pyeval 1+1 And I wait until qute:pyeval is loaded Then the page should contain the plaintext "2" + @qtwebengine_todo: qute:pyeval is not implemented yet Scenario: Causing exception in :pyeval When I run :debug-pyeval 1/0 And I wait until qute:pyeval is loaded @@ -370,6 +382,7 @@ Feature: Various utility commands. Then no crash should happen @pyqt>=5.3.1 + @qtwebengine_todo: JS prompt is not implemented yet Scenario: Focusing download widget via Tab (original issue) When I open data/prompt/jsprompt.html And I run :click-element id button @@ -380,6 +393,7 @@ Feature: Various utility commands. ## Custom headers + @qtwebengine_todo: Custom headers are not implemented yet Scenario: Setting a custom header When I set network -> custom-headers to {"X-Qute-Test": "testvalue"} And I open headers @@ -387,6 +401,7 @@ Feature: Various utility commands. ## :messages + @qtwebengine_todo: qute:log is not implemented yet Scenario: Showing error messages When I run :message-error the-error-message And I run :message-warning the-warning-message @@ -399,6 +414,7 @@ Feature: Various utility commands. And the page should not contain the plaintext "the-warning-message" And the page should not contain the plaintext "the-info-message" + @qtwebengine_todo: qute:log is not implemented yet Scenario: Showing messages of type 'warning' or greater When I run :message-error the-error-message And I run :message-warning the-warning-message @@ -411,6 +427,7 @@ Feature: Various utility commands. And the page should contain the plaintext "the-warning-message" And the page should not contain the plaintext "the-info-message" + @qtwebengine_todo: qute:log is not implemented yet Scenario: Showing messages of type 'info' or greater When I run :message-error the-error-message And I run :message-warning the-warning-message @@ -423,24 +440,29 @@ Feature: Various utility commands. And the page should contain the plaintext "the-warning-message" And the page should contain the plaintext "the-info-message" + @qtwebengine_skip: Flaky for some reason? Scenario: Showing messages of an invalid level When I run :messages cataclysmic Then the error "Invalid log level cataclysmic!" should be shown + @qtwebengine_todo: qute:log is not implemented yet Scenario: Using qute:log directly When I open qute:log Then no crash should happen + @qtwebengine_todo: qute:log is not implemented yet Scenario: Using qute:plainlog directly When I open qute:plainlog Then no crash should happen + @qtwebengine_todo: qute:log is not implemented yet Scenario: Using :messages without messages Given I have a fresh instance When I run :messages Then qute://log?level=error should be loaded And the page should contain the plaintext "No messages to show." + @qtwebengine_todo: qute:log is not implemented yet Scenario: Using :debug-log-capacity When I run :debug-log-capacity 100 And I run :message-info oldstuff @@ -472,6 +494,7 @@ Feature: Various utility commands. ## https://github.com/The-Compiler/qutebrowser/issues/1219 + @qtwebengine_todo: private browsing is not implemented yet Scenario: Sharing cookies with private browsing When I set general -> private-browsing to true And I open cookies/set?qute-test=42 without waiting @@ -481,6 +504,7 @@ Feature: Various utility commands. ## https://github.com/The-Compiler/qutebrowser/issues/1742 + @qtwebengine_todo: private browsing is not implemented yet Scenario: Private browsing is activated in QtWebKit without restart When I set general -> private-browsing to true And I open data/javascript/localstorage.html From 0557fea79e50086fdcef6b51fa1da4eeda28000b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 21:36:43 +0200 Subject: [PATCH 311/365] Use QApplication.sendEvent instead of postEvent From the QApplication.postEvent docs: http://doc.qt.io/qt-5/qcoreapplication.html#postEvent The event must be allocated on the heap since the post event queue will take ownership of the event and delete it once it has been posted. It is not safe to access the event after it has been posted. We can't reliably guarantee that from Python, so we need to use sendEvent instead. --- qutebrowser/browser/browsertab.py | 2 +- qutebrowser/browser/commands.py | 8 ++++---- qutebrowser/browser/webelem.py | 4 ++-- qutebrowser/browser/webengine/webenginetab.py | 10 ++++------ qutebrowser/browser/webkit/webkittab.py | 6 ++---- tests/end2end/features/scroll.feature | 5 +++++ tests/unit/utils/test_debug.py | 2 +- 7 files changed, 19 insertions(+), 18 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 8e6b30d25..0c86da8f9 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -562,7 +562,7 @@ class AbstractTab(QWidget): self._load_status = val self.load_status_changed.emit(val.name) - def post_event(self, evt): + def send_event(self, evt): """Send the given event to the underlying widget.""" raise NotImplementedError diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 2ee90d843..6b2bcc2ff 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1953,8 +1953,8 @@ class CommandDispatcher: window = QApplication.focusWindow() if window is None: raise cmdexc.CommandError("No focused window!") - QApplication.postEvent(window, press_event) - QApplication.postEvent(window, release_event) + QApplication.sendEvent(window, press_event) + QApplication.sendEvent(window, release_event) else: try: tab = objreg.get('tab', scope='tab', tab='current') @@ -1962,8 +1962,8 @@ class CommandDispatcher: raise cmdexc.CommandError("No focused webview!") tab = self._current_widget() - tab.post_event(press_event) - tab.post_event(release_event) + tab.send_event(press_event) + tab.send_event(release_event) @cmdutils.register(instance='command-dispatcher', scope='window', debug=True) diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py index 31f4f0c57..bf9a5da4d 100644 --- a/qutebrowser/browser/webelem.py +++ b/qutebrowser/browser/webelem.py @@ -384,7 +384,7 @@ class AbstractWebElement(collections.abc.MutableMapping): ] for evt in events: - self._tab.post_event(evt) + self._tab.send_event(evt) def after_click(): """Move cursor to end and reset override_target after clicking.""" @@ -398,4 +398,4 @@ class AbstractWebElement(collections.abc.MutableMapping): pos = self._mouse_pos() event = QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton, Qt.NoModifier) - self._tab.post_event(event) + self._tab.send_event(event) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index f43622208..acc3c19ee 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -201,8 +201,8 @@ class WebEngineScroller(browsertab.AbstractScroller): press_evt = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier, 0, 0, 0) release_evt = QKeyEvent(QEvent.KeyRelease, key, Qt.NoModifier, 0, 0, 0) for _ in range(count): - self._tab.post_event(press_evt) - self._tab.post_event(release_evt) + self._tab.send_event(press_evt) + self._tab.send_event(release_evt) @pyqtSlot() def _update_pos(self): @@ -523,8 +523,6 @@ class WebEngineTab(browsertab.AbstractTab): except AttributeError: log.stub('contentsSizeChanged, on Qt < 5.7') - def post_event(self, evt): - # If we get a segfault here, we might want to try sendEvent - # instead. + def send_event(self, evt): recipient = self._widget.focusProxy() - QApplication.postEvent(recipient, evt) + QApplication.sendEvent(recipient, evt) diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index bd7f0a0e2..1d4d36a02 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -696,7 +696,5 @@ class WebKitTab(browsertab.AbstractTab): frame.initialLayoutCompleted.connect(self._on_history_trigger) page.link_clicked.connect(self._on_link_clicked) - def post_event(self, evt): - # If we get a segfault here, we might want to try sendEvent - # instead. - QApplication.postEvent(self._widget, evt) + def send_event(self, evt): + QApplication.sendEvent(self._widget, evt) diff --git a/tests/end2end/features/scroll.feature b/tests/end2end/features/scroll.feature index 4468ea35b..dec0ddce2 100644 --- a/tests/end2end/features/scroll.feature +++ b/tests/end2end/features/scroll.feature @@ -72,6 +72,11 @@ Feature: Scrolling And I run :scroll left Then the page should not be scrolled + # causes segfault with postEvent instead of sendEvent + Scenario: Scrolling down with count 10 + When I run :scroll down with count 10 + Then no crash should happen + Scenario: Scrolling with page down When I run :scroll page-down Then the page should be scrolled vertically diff --git a/tests/unit/utils/test_debug.py b/tests/unit/utils/test_debug.py index 035bd3e08..7b00502f7 100644 --- a/tests/unit/utils/test_debug.py +++ b/tests/unit/utils/test_debug.py @@ -39,7 +39,7 @@ class EventObject(QObject): def test_log_events(qapp, caplog): obj = EventObject() - qapp.postEvent(obj, QEvent(QEvent.User)) + qapp.sendEvent(obj, QEvent(QEvent.User)) qapp.processEvents() assert len(caplog.records) == 1 assert caplog.records[0].msg == 'Event in test_debug.EventObject: User' From 69514df126a9b6e450386f75aeb4ab845c71fec5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:22:56 +0200 Subject: [PATCH 312/365] hints.feature: Skip flaky test on QtWebEngine --- tests/end2end/features/hints.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature index be79dcfa1..6f05b7d6b 100644 --- a/tests/end2end/features/hints.feature +++ b/tests/end2end/features/hints.feature @@ -196,7 +196,7 @@ Feature: Using hints Then data/numbers/7.txt should be loaded # https://github.com/The-Compiler/qutebrowser/issues/576 - @qtwebengine_todo: createWindow is not implemented yet + @qtwebengine_skip: Flaky for some reason Scenario: Keeping hint filter in rapid mode When I open data/hints/number.html And I set hints -> mode to number From d5131aa0a424fe4ea62cf85cdfee4eb263a5cb93 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:23:11 +0200 Subject: [PATCH 313/365] navigate.feature: Add @qtwebengine_todo --- tests/end2end/features/navigate.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/end2end/features/navigate.feature b/tests/end2end/features/navigate.feature index 1041fcdee..956229f09 100644 --- a/tests/end2end/features/navigate.feature +++ b/tests/end2end/features/navigate.feature @@ -81,6 +81,7 @@ Feature: Using :navigate And I run :navigate increment Then the error "No number found in URL!" should be shown + @qtwebengine_todo: Doesn't find any elements Scenario: Navigating multiline links When I open data/navigate/multilinelinks.html And I run :navigate next From 1c581cf1cf56b91b97b747d163e6acdb32e84cca Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:23:29 +0200 Subject: [PATCH 314/365] quteproc: Ignore failing messages on xfail When a test calls pytest.xfail it might stop early, so the message doesn't get marked as ignored. --- tests/end2end/fixtures/quteprocess.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index 5a6385ead..4d977da6e 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -620,7 +620,9 @@ def quteproc(quteproc_process, httpbin, request): request.node._quteproc_log = quteproc_process.captured_log quteproc_process.before_test() yield quteproc_process - quteproc_process.after_test(did_fail=request.node.rep_call.failed) + call = request.node.rep_call + did_fail = call.failed or hasattr(call, 'wasxfail') + quteproc_process.after_test(did_fail=did_fail) @pytest.yield_fixture @@ -634,5 +636,6 @@ def quteproc_new(qapp, httpbin, request): request.node._quteproc_log = proc.captured_log # Not calling before_test here as that would start the process yield proc - proc.after_test(did_fail=request.node.rep_call.failed) + did_fail = call.failed or hasattr(call, 'wasxfail') + proc.after_test(did_fail=did_fail) proc.terminate() From 8da942ddc759d9dcacd4a3a5f03492d72103e0cb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:46:32 +0200 Subject: [PATCH 315/365] bdd: Skip tests on WebEngine causing memory leaks --- qutebrowser/browser/webengine/webenginetab.py | 3 +++ tests/end2end/features/scroll.feature | 3 +++ 2 files changed, 6 insertions(+) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index acc3c19ee..99464468d 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -182,6 +182,9 @@ class WebEngineScroller(browsertab.AbstractScroller): """QtWebEngine implementations related to scrolling.""" + # FIXME:qtwebengine + # using stuff here with a big count/argument causes memory leaks and hangs + def __init__(self, tab, parent=None): super().__init__(tab, parent) self._pos_perc = (0, 0) diff --git a/tests/end2end/features/scroll.feature b/tests/end2end/features/scroll.feature index dec0ddce2..b14155ab1 100644 --- a/tests/end2end/features/scroll.feature +++ b/tests/end2end/features/scroll.feature @@ -121,6 +121,7 @@ Feature: Scrolling And I run :scroll left Then the page should not be scrolled + @qtwebengine_skip: Causes memory leak... Scenario: Scrolling down with a very big count When I run :scroll down with count 99999999999 # Make sure it doesn't hang @@ -186,6 +187,7 @@ Feature: Scrolling When I run :scroll-perc with count 50 Then the page should be scrolled vertically + @qtwebengine_skip: Causes memory leak... Scenario: :scroll-perc with a very big value When I run :scroll-perc 99999999999 Then no crash should happen @@ -248,6 +250,7 @@ Feature: Scrolling When I run :scroll-page --top-navigate prev 0 -1 Then data/hello3.txt should be loaded + @qtwebengine_skip: Causes memory leak... Scenario: :scroll-page with a very big value When I run :scroll-page 99999999999 99999999999 Then the error "Numeric argument is too large for internal int representation." should be shown From 84b8ea856dd4fa52233601ee48f1f26b4d53a705 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:46:48 +0200 Subject: [PATCH 316/365] bdd: Skip :scroll-page+navigate tests on webengine --- tests/end2end/features/scroll.feature | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/end2end/features/scroll.feature b/tests/end2end/features/scroll.feature index b14155ab1..9904f1b09 100644 --- a/tests/end2end/features/scroll.feature +++ b/tests/end2end/features/scroll.feature @@ -241,11 +241,13 @@ Feature: Scrolling And I run :scroll-page -1 0 Then the page should not be scrolled + @qtwebengine_todo: at_bottom is not implemented yet Scenario: :scroll-page with --bottom-navigate When I run :scroll-perc 100 And I run :scroll-page --bottom-navigate next 0 1 Then data/hello2.txt should be loaded + @qtwebengine_todo: at_top is not implemented yet Scenario: :scroll-page with --top-navigate When I run :scroll-page --top-navigate prev 0 -1 Then data/hello3.txt should be loaded From 2969d3dc915f1da58be1f942f472a125818e5f88 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:47:11 +0200 Subject: [PATCH 317/365] bdd: Mark prompt.feature as TODO on QtWebEngine --- tests/end2end/features/test_prompts_bdd.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/end2end/features/test_prompts_bdd.py b/tests/end2end/features/test_prompts_bdd.py index 521a08f42..d98acab9a 100644 --- a/tests/end2end/features/test_prompts_bdd.py +++ b/tests/end2end/features/test_prompts_bdd.py @@ -17,10 +17,14 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . +import pytest import pytest_bdd as bdd bdd.scenarios('prompts.feature') +pytestmark = pytest.mark.qtwebengine_todo("Prompts are not implemented") + + @bdd.when("I load an SSL page") def load_ssl_page(quteproc, ssl_server): # We don't wait here as we can get an SSL question. From 1763f9bb5838a1de3465d2703a8e09689175719a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:50:57 +0200 Subject: [PATCH 318/365] bdd: Mark search/sessions as qtwebengine_todo --- tests/end2end/features/test_search_bdd.py | 3 +++ tests/end2end/features/test_sessions_bdd.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/tests/end2end/features/test_search_bdd.py b/tests/end2end/features/test_search_bdd.py index a2f94f569..12e5d9480 100644 --- a/tests/end2end/features/test_search_bdd.py +++ b/tests/end2end/features/test_search_bdd.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . +import pytest import pytest_bdd as bdd # pylint: disable=unused-import @@ -24,3 +25,5 @@ from end2end.features.test_yankpaste_bdd import init_fake_clipboard bdd.scenarios('search.feature') + +pytestmark = pytest.mark.qtwebengine_skip("Searched text is not selected...") diff --git a/tests/end2end/features/test_sessions_bdd.py b/tests/end2end/features/test_sessions_bdd.py index 0af29f414..d4eec436c 100644 --- a/tests/end2end/features/test_sessions_bdd.py +++ b/tests/end2end/features/test_sessions_bdd.py @@ -17,5 +17,9 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . +import pytest import pytest_bdd as bdd bdd.scenarios('sessions.feature') + + +pytestmark = pytest.mark.qtwebengine_todo("Sessions are not implemented") From 15f142880e8ec012b871b9094ec946c5df893249 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:52:36 +0200 Subject: [PATCH 319/365] bdd: Mark qute:settings test as @qtwebengine_todo --- tests/end2end/features/set.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/end2end/features/set.feature b/tests/end2end/features/set.feature index aabb07f7e..6611b7ca0 100644 --- a/tests/end2end/features/set.feature +++ b/tests/end2end/features/set.feature @@ -56,6 +56,7 @@ Feature: Setting settings. When I run :set -t colors statusbar.bg green Then colors -> statusbar.bg should be green + @qtwebengine_todo: qute:settings is not implemented yet Scenario: Opening qute:settings When I run :set And I wait until qute:settings is loaded From 50031c5aaec456d340a5b6f01cb38456d8814062 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:53:42 +0200 Subject: [PATCH 320/365] bdd: xfail in check_open_tabs for QtWebEngine --- tests/end2end/features/conftest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index 0276e6244..be72ad653 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -497,13 +497,17 @@ def check_contents_json(quteproc, text): @bdd.then(bdd.parsers.parse("the following tabs should be open:\n{tabs}")) -def check_open_tabs(quteproc, tabs): +def check_open_tabs(quteproc, request, tabs): """Check the list of open tabs in the session. This is a lightweight alternative for "The session should look like: ...". It expects a list of URLs, with an optional "(active)" suffix. """ + if request.config.getoption('--qute-bdd-webengine'): + # pylint: disable=no-member + pytest.xfail(reason="QtWebEngine TODO: Sessions are not implemented") + # pylint: enable=no-member session = quteproc.get_session() active_suffix = ' (active)' tabs = tabs.splitlines() From 63628d2f97150219df9b6121dcdd30801a4b6ad1 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 22:57:21 +0200 Subject: [PATCH 321/365] bdd: Wait for any focus object in tabs.feature With QtWebEngine, we get a "Focus object changed" logged pointing to the QOpenGLWidget, not the tab. --- tests/end2end/features/tabs.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 3486ee05a..6e8242b14 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -877,7 +877,7 @@ Feature: Tab management And I open data/caret.html in a new window And I open data/paste_primary.html in a new tab And I run :buffer "Scrolling" - And I wait for "Focus object changed: " in the log + And I wait for "Focus object changed: *" in the log Then the session should look like: windows: - active: true @@ -916,7 +916,7 @@ Feature: Tab management And I open data/paste_primary.html in a new tab And I wait until data/caret.html is loaded And I run :buffer "0/2" - And I wait for "Focus object changed: " in the log + And I wait for "Focus object changed: *" in the log Then the session should look like: windows: - active: true From d2f69db0fff7bd607635461b7f0b80bf671676bc Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:00:10 +0200 Subject: [PATCH 322/365] bdd: Mark qute:bookmarks tests as qtwebengine_todo --- tests/end2end/features/urlmarks.feature | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/end2end/features/urlmarks.feature b/tests/end2end/features/urlmarks.feature index 595a93dcc..8ae5aae35 100644 --- a/tests/end2end/features/urlmarks.feature +++ b/tests/end2end/features/urlmarks.feature @@ -220,6 +220,7 @@ Feature: quickmarks and bookmarks And I run :quickmark-del Then the quickmark file should not contain "nineteen http://localhost:*/data/numbers/19.txt" + @qtwebengine_todo: qute:bookmarks is not implemented yet Scenario: Listing quickmarks When I run :quickmark-add http://localhost:(port)/data/numbers/20.txt twenty And I run :quickmark-add http://localhost:(port)/data/numbers/21.txt twentyone @@ -227,6 +228,7 @@ Feature: quickmarks and bookmarks Then the page should contain the plaintext "twenty" And the page should contain the plaintext "twentyone" + @qtwebengine_todo: qute:bookmarks is not implemented yet Scenario: Listing bookmarks When I open data/title.html And I run :bookmark-add From 339ac42623e68aa8fa1676d83d26d3dc081c1872 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:04:03 +0200 Subject: [PATCH 323/365] bdd: Mark :insert-text tests as @qtwebengine_todo --- tests/end2end/features/yankpaste.feature | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/end2end/features/yankpaste.feature b/tests/end2end/features/yankpaste.feature index 6d732d7c9..c738c9e26 100644 --- a/tests/end2end/features/yankpaste.feature +++ b/tests/end2end/features/yankpaste.feature @@ -217,6 +217,7 @@ Feature: Yanking and pasting. #### :insert-text + @qtwebengine_todo: :insert-text is not implemented yet Scenario: Inserting text into an empty text field When I open data/paste_primary.html And I run :click-element id qute-textarea @@ -225,6 +226,7 @@ Feature: Yanking and pasting. # Compare Then the text field should contain "Hello world" + @qtwebengine_todo: :insert-text is not implemented yet Scenario: Inserting text into a text field at specific position When I open data/paste_primary.html And I set the text field to "one two three four" @@ -238,6 +240,7 @@ Feature: Yanking and pasting. # Compare Then the text field should contain "onHello worlde two three four" + @qtwebengine_todo: :insert-text is not implemented yet Scenario: Inserting text into a text field with undo When I open data/paste_primary.html And I run :click-element id qute-textarea @@ -250,12 +253,14 @@ Feature: Yanking and pasting. # Compare Then the text field should contain "This text should stay" + @qtwebengine_todo: :insert-text is not implemented yet Scenario: Inserting text without a focused field When I open data/paste_primary.html And I run :enter-mode insert And I run :insert-text test Then the error "No element focused!" should be shown + @qtwebengine_todo: :insert-text is not implemented yet Scenario: Inserting text with a read-only field When I open data/paste_primary.html And I run :click-element id qute-textarea-noedit From 94e3d7b05084533ea86c19dd43564a333ef83d2d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:05:31 +0200 Subject: [PATCH 324/365] Mark test_dirbrowser as qtwebengine_skip --- tests/end2end/test_dirbrowser.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/end2end/test_dirbrowser.py b/tests/end2end/test_dirbrowser.py index a0c0a1aa2..2359020d7 100644 --- a/tests/end2end/test_dirbrowser.py +++ b/tests/end2end/test_dirbrowser.py @@ -29,6 +29,10 @@ from PyQt5.QtCore import QUrl from qutebrowser.utils import urlutils +pytestmark = pytest.mark.qtwebengine_skip("Title is empty when parsing for " + "some reason?") + + class DirLayout: """Provide a fake directory layout to test dirbrowser.""" From 63cc73d56dd7a3ad6d83866ba47f6f1e8bdeebd0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:10:37 +0200 Subject: [PATCH 325/365] Try to make test_hints work with QtWebEngine --- tests/end2end/test_hints_html.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/end2end/test_hints_html.py b/tests/end2end/test_hints_html.py index 58a19eb21..5b18966db 100644 --- a/tests/end2end/test_hints_html.py +++ b/tests/end2end/test_hints_html.py @@ -40,7 +40,13 @@ def collect_tests(): @pytest.mark.parametrize('zoom_level', [100, 66, 33]) @pytest.mark.parametrize('find_implementation', ['javascript', 'python']) def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, - quteproc): + quteproc, request): + if zoom_text_only and request.config.getoption('--qute-bdd-webengine'): + pytest.skip("QtWebEngine doesn't have zoom-text-only") + if (find_implementation == 'python' and + request.config.getoption('--qute-bdd-webengine')): + pytest.skip("QtWebEngine doesn't have a python find implementation") + file_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data', 'hints', 'html', test_name) url_path = 'data/hints/html/{}'.format(test_name) @@ -70,8 +76,10 @@ def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, test_name, ', '.join(set(parsed.keys())))) # setup - quteproc.set_setting('ui', 'zoom-text-only', str(zoom_text_only)) - quteproc.set_setting('hints', 'find-implementation', find_implementation) + if not request.config.getoption('--qute-bdd-webengine'): + quteproc.set_setting('ui', 'zoom-text-only', str(zoom_text_only)) + quteproc.set_setting('hints', 'find-implementation', + find_implementation) quteproc.send_cmd(':zoom {}'.format(zoom_level)) # follow hint quteproc.send_cmd(':hint links normal') @@ -80,8 +88,9 @@ def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, quteproc.wait_for_load_finished('data/' + parsed['target']) # reset quteproc.send_cmd(':zoom 100') - quteproc.set_setting('ui', 'zoom-text-only', 'false') - quteproc.set_setting('hints', 'find-implementation', 'javascript') + if not request.config.getoption('--qute-bdd-webengine'): + quteproc.set_setting('ui', 'zoom-text-only', 'false') + quteproc.set_setting('hints', 'find-implementation', 'javascript') def test_word_hints_issue1393(quteproc, tmpdir): From 1eb0eabdab1e3987395afc9ec71572dcacc4c7e4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:14:21 +0200 Subject: [PATCH 326/365] test_hints_html: Split off _parse_file --- tests/end2end/test_hints_html.py | 62 ++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/tests/end2end/test_hints_html.py b/tests/end2end/test_hints_html.py index 5b18966db..630770cad 100644 --- a/tests/end2end/test_hints_html.py +++ b/tests/end2end/test_hints_html.py @@ -26,6 +26,7 @@ import yaml import pytest import bs4 import textwrap +import collections def collect_tests(): @@ -35,6 +36,39 @@ def collect_tests(): return files +ParsedFile = collections.namedtuple('ParsedFile', ['target']) + + +def _parse_file(test_name): + """Parse the given HTML file.""" + file_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), + 'data', 'hints', 'html', test_name) + with open(file_path, 'r', encoding='utf-8') as html: + soup = bs4.BeautifulSoup(html, 'html.parser') + + comment = soup.find(text=lambda text: isinstance(text, bs4.Comment)) + + if comment is None: + pytest.fail("No comment found in {}, please read " + "tests/end2end/data/hints/html/README.md".format( + test_name)) + + data = yaml.load(comment) + if not isinstance(data, dict): + pytest.fail("Invalid comment found in {}, please read " + "tests/end2end/data/hints/html/README.md - " + "expected yaml dict but got {}".format( + test_name, type(data).__name__)) + + if set(data.keys()) != {'target'}: + pytest.fail("Invalid comment found in {}, please read " + "tests/end2end/data/hints/html/README.md - " + "expected key 'target' but found {}".format( + test_name, ', '.join(set(data.keys())))) + + return ParsedFile(target=data['target']) + + @pytest.mark.parametrize('test_name', collect_tests()) @pytest.mark.parametrize('zoom_text_only', [True, False]) @pytest.mark.parametrize('zoom_level', [100, 66, 33]) @@ -47,34 +81,10 @@ def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, request.config.getoption('--qute-bdd-webengine')): pytest.skip("QtWebEngine doesn't have a python find implementation") - file_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), - 'data', 'hints', 'html', test_name) + parsed = _parse_file(test_name) url_path = 'data/hints/html/{}'.format(test_name) quteproc.open_path(url_path) - with open(file_path, 'r', encoding='utf-8') as html: - soup = bs4.BeautifulSoup(html, 'html.parser') - - comment = soup.find(text=lambda text: isinstance(text, bs4.Comment)) - - if comment is None: - pytest.fail("No comment found in {}, please read " - "tests/end2end/data/hints/html/README.md".format( - test_name)) - - parsed = yaml.load(comment) - if not isinstance(parsed, dict): - pytest.fail("Invalid comment found in {}, please read " - "tests/end2end/data/hints/html/README.md - " - "expected yaml dict but got {}".format( - test_name, type(parsed).__name__)) - - if set(parsed.keys()) != {'target'}: - pytest.fail("Invalid comment found in {}, please read " - "tests/end2end/data/hints/html/README.md - " - "expected key 'target' but found {}".format( - test_name, ', '.join(set(parsed.keys())))) - # setup if not request.config.getoption('--qute-bdd-webengine'): quteproc.set_setting('ui', 'zoom-text-only', str(zoom_text_only)) @@ -85,7 +95,7 @@ def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, quteproc.send_cmd(':hint links normal') quteproc.wait_for(message='hints: a', category='hints') quteproc.send_cmd(':follow-hint a') - quteproc.wait_for_load_finished('data/' + parsed['target']) + quteproc.wait_for_load_finished('data/' + parsed.target) # reset quteproc.send_cmd(':zoom 100') if not request.config.getoption('--qute-bdd-webengine'): From 322a4323cbf23a8df7e808960e6a28fd62ad9789 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:20:20 +0200 Subject: [PATCH 327/365] Improve file validating in test_hints --- tests/end2end/test_hints_html.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/tests/end2end/test_hints_html.py b/tests/end2end/test_hints_html.py index 630770cad..69fff41d7 100644 --- a/tests/end2end/test_hints_html.py +++ b/tests/end2end/test_hints_html.py @@ -39,6 +39,14 @@ def collect_tests(): ParsedFile = collections.namedtuple('ParsedFile', ['target']) +class InvalidFile(Exception): + + def __init__(self, test_name, msg): + super().__init__("Invalid comment found in {}, please read " + "tests/end2end/data/hints/html/README.md - {}".format( + test_name, msg)) + + def _parse_file(test_name): """Parse the given HTML file.""" file_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), @@ -49,22 +57,21 @@ def _parse_file(test_name): comment = soup.find(text=lambda text: isinstance(text, bs4.Comment)) if comment is None: - pytest.fail("No comment found in {}, please read " - "tests/end2end/data/hints/html/README.md".format( - test_name)) + raise InvalidFile(test_name, "no comment found") data = yaml.load(comment) if not isinstance(data, dict): - pytest.fail("Invalid comment found in {}, please read " - "tests/end2end/data/hints/html/README.md - " - "expected yaml dict but got {}".format( - test_name, type(data).__name__)) + raise InvalidFile(test_name, "expected yaml dict but got {}".format( + type(data).__name__)) - if set(data.keys()) != {'target'}: - pytest.fail("Invalid comment found in {}, please read " - "tests/end2end/data/hints/html/README.md - " - "expected key 'target' but found {}".format( - test_name, ', '.join(set(data.keys())))) + allowed_keys = {'target'} + if not set(data.keys()).issubset(allowed_keys): + raise InvalidFile(test_name, "expected keys {} but found {}".format( + ', '.join(allowed_keys), + ', '.join(set(data.keys())))) + + if not 'target' in data: + raise InvalidFile(test_name, "'target' key not found") return ParsedFile(target=data['target']) From 4d1ae999c61c9fc59d2fe3a44a10349d5656b0cc Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:28:37 +0200 Subject: [PATCH 328/365] tests: Allow to mark files as qtwebengine_todo --- tests/end2end/data/hints/html/wrapped.html | 5 ++++- .../data/hints/html/zoom_precision.html | 5 ++++- tests/end2end/test_hints_html.py | 22 ++++++++++++------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/tests/end2end/data/hints/html/wrapped.html b/tests/end2end/data/hints/html/wrapped.html index dcc05c8c7..2ebde1a24 100644 --- a/tests/end2end/data/hints/html/wrapped.html +++ b/tests/end2end/data/hints/html/wrapped.html @@ -1,6 +1,9 @@ - + diff --git a/tests/end2end/data/hints/html/zoom_precision.html b/tests/end2end/data/hints/html/zoom_precision.html index 5b9b73f99..25a828399 100644 --- a/tests/end2end/data/hints/html/zoom_precision.html +++ b/tests/end2end/data/hints/html/zoom_precision.html @@ -1,6 +1,9 @@ - + diff --git a/tests/end2end/test_hints_html.py b/tests/end2end/test_hints_html.py index 69fff41d7..362f38371 100644 --- a/tests/end2end/test_hints_html.py +++ b/tests/end2end/test_hints_html.py @@ -36,7 +36,8 @@ def collect_tests(): return files -ParsedFile = collections.namedtuple('ParsedFile', ['target']) +ParsedFile = collections.namedtuple('ParsedFile', ['target', + 'qtwebengine_todo']) class InvalidFile(Exception): @@ -64,7 +65,7 @@ def _parse_file(test_name): raise InvalidFile(test_name, "expected yaml dict but got {}".format( type(data).__name__)) - allowed_keys = {'target'} + allowed_keys = {'target', 'qtwebengine_todo'} if not set(data.keys()).issubset(allowed_keys): raise InvalidFile(test_name, "expected keys {} but found {}".format( ', '.join(allowed_keys), @@ -73,7 +74,9 @@ def _parse_file(test_name): if not 'target' in data: raise InvalidFile(test_name, "'target' key not found") - return ParsedFile(target=data['target']) + qtwebengine_todo = data.get('qtwebengine_todo', None) + + return ParsedFile(target=data['target'], qtwebengine_todo=qtwebengine_todo) @pytest.mark.parametrize('test_name', collect_tests()) @@ -82,18 +85,21 @@ def _parse_file(test_name): @pytest.mark.parametrize('find_implementation', ['javascript', 'python']) def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, quteproc, request): - if zoom_text_only and request.config.getoption('--qute-bdd-webengine'): + webengine = bool(request.config.getoption('--qute-bdd-webengine')) + if zoom_text_only and webengine: pytest.skip("QtWebEngine doesn't have zoom-text-only") - if (find_implementation == 'python' and - request.config.getoption('--qute-bdd-webengine')): + if find_implementation == 'python' and webengine: pytest.skip("QtWebEngine doesn't have a python find implementation") parsed = _parse_file(test_name) + if parsed.qtwebengine_todo is not None and webengine: + pytest.xfail("QtWebEngine TODO: {}".format(parsed.qtwebengine_todo)) + url_path = 'data/hints/html/{}'.format(test_name) quteproc.open_path(url_path) # setup - if not request.config.getoption('--qute-bdd-webengine'): + if not webengine: quteproc.set_setting('ui', 'zoom-text-only', str(zoom_text_only)) quteproc.set_setting('hints', 'find-implementation', find_implementation) @@ -105,7 +111,7 @@ def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, quteproc.wait_for_load_finished('data/' + parsed.target) # reset quteproc.send_cmd(':zoom 100') - if not request.config.getoption('--qute-bdd-webengine'): + if not webengine: quteproc.set_setting('ui', 'zoom-text-only', 'false') quteproc.set_setting('hints', 'find-implementation', 'javascript') From 33755b09df8d5f2d54b47ecd7494f3bf1f736522 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:30:35 +0200 Subject: [PATCH 329/365] Skip :insert-text in test_insert_mode in webengine --- tests/end2end/test_insert_mode.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/end2end/test_insert_mode.py b/tests/end2end/test_insert_mode.py index ad4cce33c..31a9be5ee 100644 --- a/tests/end2end/test_insert_mode.py +++ b/tests/end2end/test_insert_mode.py @@ -36,7 +36,7 @@ import pytest 'true'), ]) def test_insert_mode(file_name, elem_id, source, input_text, auto_insert, - quteproc): + quteproc, request): url_path = 'data/insert_mode_settings/html/{}'.format(file_name) quteproc.open_path(url_path) @@ -48,6 +48,11 @@ def test_insert_mode(file_name, elem_id, source, input_text, auto_insert, if source == 'keypress': quteproc.press_keys(input_text) elif source == 'clipboard': + if request.config.getoption('--qute-bdd-webengine'): + # pylint: disable=no-member + pytest.xfail(reason="QtWebEngine TODO: :insert-text is not " + "implemented") + # pylint: enable=no-member quteproc.send_cmd(':debug-set-fake-clipboard "{}"'.format(input_text)) quteproc.send_cmd(':insert-text {clipboard}') From 914ce85780f1c5549041c40c34ee97001df86cd0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:31:18 +0200 Subject: [PATCH 330/365] tests: Fix quteproc_new fixture --- tests/end2end/fixtures/quteprocess.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index 4d977da6e..60b0c14fe 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -636,6 +636,7 @@ def quteproc_new(qapp, httpbin, request): request.node._quteproc_log = proc.captured_log # Not calling before_test here as that would start the process yield proc + call = request.node.rep_call did_fail = call.failed or hasattr(call, 'wasxfail') proc.after_test(did_fail=did_fail) proc.terminate() From 44c74c08873d1dbe8d2ff158e6ace291cdfba633 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:32:26 +0200 Subject: [PATCH 331/365] tests: Mark test_mhtml_e2e as qtwebengine_todo --- tests/end2end/test_mhtml_e2e.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/end2end/test_mhtml_e2e.py b/tests/end2end/test_mhtml_e2e.py index edd760a41..91ed693f6 100644 --- a/tests/end2end/test_mhtml_e2e.py +++ b/tests/end2end/test_mhtml_e2e.py @@ -27,6 +27,10 @@ import collections import pytest +pytestmark = pytest.mark.qtwebengine_todo("mhtml downloads are not " + "implemented") + + def collect_tests(): basedir = os.path.dirname(__file__) datadir = os.path.join(basedir, 'data', 'downloads', 'mhtml') From ea0f137fd116784dfac97c99254fe6faeb663a35 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:33:56 +0200 Subject: [PATCH 332/365] bdd: Tag flaky backforward test qtwebengine_skip --- tests/end2end/features/backforward.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/end2end/features/backforward.feature b/tests/end2end/features/backforward.feature index e9e5f0c7b..835ad9351 100644 --- a/tests/end2end/features/backforward.feature +++ b/tests/end2end/features/backforward.feature @@ -103,6 +103,7 @@ Feature: Going back and forward. Then the error "At beginning of history." should be shown And the message "Still alive!" should be shown + @qtwebengine_skip: flaky for some reason? Scenario: Going back in a new window Given I clean up open tabs When I open data/backforward/1.txt From fa0bde631d76d7ed91cd321bcc1c21e53d6bf400 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:47:09 +0200 Subject: [PATCH 333/365] bdd: at_top is actually implemented (scroll) --- tests/end2end/features/scroll.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/end2end/features/scroll.feature b/tests/end2end/features/scroll.feature index 9904f1b09..6b50a5522 100644 --- a/tests/end2end/features/scroll.feature +++ b/tests/end2end/features/scroll.feature @@ -247,7 +247,6 @@ Feature: Scrolling And I run :scroll-page --bottom-navigate next 0 1 Then data/hello2.txt should be loaded - @qtwebengine_todo: at_top is not implemented yet Scenario: :scroll-page with --top-navigate When I run :scroll-page --top-navigate prev 0 -1 Then data/hello3.txt should be loaded From 044b3df872aa1b2f56bb2f0dcdc6b3f5c416661c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:47:27 +0200 Subject: [PATCH 334/365] bdd: Skip another flaky test for QtWebEngine --- tests/end2end/features/backforward.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/end2end/features/backforward.feature b/tests/end2end/features/backforward.feature index 835ad9351..88385f5a6 100644 --- a/tests/end2end/features/backforward.feature +++ b/tests/end2end/features/backforward.feature @@ -137,6 +137,7 @@ Feature: Going back and forward. When I run :forward Then the error "At end of history." should be shown + @qtwebengine_skip: Causes 'Ignoring invalid URL being added to history' sometimes? Scenario: Going forward too much with count. Given I open data/backforward/1.txt When I open data/backforward/2.txt From a88adcca17615b3b043b238caeef42feb14f87be Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 18 Aug 2016 23:58:33 +0200 Subject: [PATCH 335/365] bdd: Stop checking requests in yankpaste.feature It's not needed and only makes the test more unreliable (e.g. flaky with QtWebEngine) --- tests/end2end/features/yankpaste.feature | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/end2end/features/yankpaste.feature b/tests/end2end/features/yankpaste.feature index c738c9e26..e5f783e4f 100644 --- a/tests/end2end/features/yankpaste.feature +++ b/tests/end2end/features/yankpaste.feature @@ -50,17 +50,13 @@ Feature: Yanking and pasting. Scenario: Pasting a URL When I put "http://localhost:(port)/data/hello.txt" into the clipboard And I run :open {clipboard} - And I wait until data/hello.txt is loaded - Then the requests should be: - data/hello.txt + Then data/hello.txt should be loaded Scenario: Pasting a URL from primary selection When selection is supported And I put "http://localhost:(port)/data/hello2.txt" into the primary selection And I run :open {primary} - And I wait until data/hello2.txt is loaded - Then the requests should be: - data/hello2.txt + Then data/hello2.txt should be loaded Scenario: Pasting with empty clipboard When I put "" into the clipboard From 3d1859b13e56baa30490ff3e72584da21e87cf70 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 00:00:35 +0200 Subject: [PATCH 336/365] Fix lint --- tests/end2end/test_hints_html.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/end2end/test_hints_html.py b/tests/end2end/test_hints_html.py index 362f38371..c5b2fd6bd 100644 --- a/tests/end2end/test_hints_html.py +++ b/tests/end2end/test_hints_html.py @@ -71,7 +71,7 @@ def _parse_file(test_name): ', '.join(allowed_keys), ', '.join(set(data.keys())))) - if not 'target' in data: + if 'target' not in data: raise InvalidFile(test_name, "'target' key not found") qtwebengine_todo = data.get('qtwebengine_todo', None) @@ -93,7 +93,9 @@ def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, parsed = _parse_file(test_name) if parsed.qtwebengine_todo is not None and webengine: + # pylint: disable=no-member pytest.xfail("QtWebEngine TODO: {}".format(parsed.qtwebengine_todo)) + # pylint: enable=no-member url_path = 'data/hints/html/{}'.format(test_name) quteproc.open_path(url_path) From a40dd7edf6f0547f082b26539a432f310c295fee Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 08:30:10 +0200 Subject: [PATCH 337/365] Allow empty string for tabs -> title-format --- CHANGELOG.asciidoc | 1 + qutebrowser/config/configdata.py | 3 ++- qutebrowser/mainwindow/tabwidget.py | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 661758c7e..045402ea2 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -96,6 +96,7 @@ Changed it caused some issues and then never re-applied. - Sending a command to an existing instance (via "qutebrowser :reload") now doesn't mark it as urgent anymore. +- `tabs -> title-format` now treats an empty string as valid. Deprecated ~~~~~~~~~~ diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index ab2638ec4..b9461c09d 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -650,7 +650,8 @@ def data(readonly=False): ('title-format', SettingValue(typ.FormatString( fields=['perc', 'perc_raw', 'title', 'title_sep', 'index', - 'id', 'scroll_pos', 'host']), '{index}: {title}'), + 'id', 'scroll_pos', 'host'], none_ok=True), + '{index}: {title}'), "The format to use for the tab title. The following placeholders " "are defined:\n\n" "* `{perc}`: The percentage as a string like `[10%]`.\n" diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 58ccc470b..dcf289452 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -106,7 +106,8 @@ class TabWidget(QTabWidget): fields['index'] = idx + 1 fmt = config.get('tabs', 'title-format') - self.tabBar().setTabText(idx, fmt.format(**fields)) + title = '' if fmt is None else fmt.format(**fields) + self.tabBar().setTabText(idx, title) def get_tab_fields(self, idx): """Get the tab field data.""" From a846a5b89f151e25eb48cead9f036c7591d22fdb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 10:10:40 +0200 Subject: [PATCH 338/365] Move send_event to AbstractTab This means subclasses only need to implement _event_target. --- qutebrowser/browser/browsertab.py | 10 ++++++++-- qutebrowser/browser/webengine/webenginetab.py | 6 ++---- qutebrowser/browser/webkit/webkittab.py | 5 ++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 0c86da8f9..66116b34c 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -23,7 +23,7 @@ import itertools from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF from PyQt5.QtGui import QIcon -from PyQt5.QtWidgets import QWidget +from PyQt5.QtWidgets import QWidget, QApplication from qutebrowser.keyinput import modeman from qutebrowser.config import config @@ -562,9 +562,15 @@ class AbstractTab(QWidget): self._load_status = val self.load_status_changed.emit(val.name) + def _event_target(self): + """Return the widget events should be sent to.""" + raise NotImplementedError + def send_event(self, evt): """Send the given event to the underlying widget.""" - raise NotImplementedError + recipient = self._event_target() + QApplication.sendEvent(recipient, evt) + @pyqtSlot(QUrl) def _on_link_clicked(self, url): diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 99464468d..6ec26c3df 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -26,7 +26,6 @@ import functools from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPoint, QUrl from PyQt5.QtGui import QKeyEvent, QIcon -from PyQt5.QtWidgets import QApplication # pylint: disable=no-name-in-module,import-error,useless-suppression from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript # pylint: enable=no-name-in-module,import-error,useless-suppression @@ -526,6 +525,5 @@ class WebEngineTab(browsertab.AbstractTab): except AttributeError: log.stub('contentsSizeChanged, on Qt < 5.7') - def send_event(self, evt): - recipient = self._widget.focusProxy() - QApplication.sendEvent(recipient, evt) + def _event_target(self): + return self._widget.focusProxy() diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 1d4d36a02..a8895f6e5 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -28,7 +28,6 @@ from PyQt5.QtCore import (pyqtSlot, Qt, QEvent, QUrl, QPoint, QTimer, QSizeF, from PyQt5.QtGui import QKeyEvent from PyQt5.QtWebKitWidgets import QWebPage, QWebFrame from PyQt5.QtWebKit import QWebSettings -from PyQt5.QtWidgets import QApplication from PyQt5.QtPrintSupport import QPrinter from qutebrowser.browser import browsertab @@ -696,5 +695,5 @@ class WebKitTab(browsertab.AbstractTab): frame.initialLayoutCompleted.connect(self._on_history_trigger) page.link_clicked.connect(self._on_link_clicked) - def send_event(self, evt): - QApplication.sendEvent(self._widget, evt) + def _event_target(self): + return self._widget From 4362d42c505b5b7f9cff8ec55a54844b484d603d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 10:13:07 +0200 Subject: [PATCH 339/365] Add postpone argument to AbstractTab.send_event --- qutebrowser/browser/browsertab.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 66116b34c..d218deebc 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -566,11 +566,18 @@ class AbstractTab(QWidget): """Return the widget events should be sent to.""" raise NotImplementedError - def send_event(self, evt): - """Send the given event to the underlying widget.""" - recipient = self._event_target() - QApplication.sendEvent(recipient, evt) + def send_event(self, evt, *, postpone=False): + """Send the given event to the underlying widget. + Args: + postpone: Postpone the event to be handled later instead of + immediately. Using this might cause crashes in Qt. + """ + recipient = self._event_target() + if postpone: + QApplication.postEvent(recipient, evt) + else: + QApplication.sendEvent(recipient, evt) @pyqtSlot(QUrl) def _on_link_clicked(self, url): From 388d771a2e79094c45618ab846c5d0268fe3cf92 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 10:14:18 +0200 Subject: [PATCH 340/365] Use send_event with postpone=True in webelem.click For some reason, since 0557fea79e50086fdcef6b51fa1da4eeda28000b we're getting segfaults in misc.feature... --- qutebrowser/browser/webelem.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py index bf9a5da4d..0887bb745 100644 --- a/qutebrowser/browser/webelem.py +++ b/qutebrowser/browser/webelem.py @@ -384,7 +384,9 @@ class AbstractWebElement(collections.abc.MutableMapping): ] for evt in events: - self._tab.send_event(evt) + # For some reason, postpone=True is needed here to *not* cause + # segfaults in misc.feature because of :fake-key later... + self._tab.send_event(evt, postpone=True) def after_click(): """Move cursor to end and reset override_target after clicking.""" From c0c327942472a2b6f54fbd3d2e3c884f0b682960 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 11:25:59 +0200 Subject: [PATCH 341/365] Fix test_quteprocess.test_set with QtWebEngine network -> accept-language is not available for QtWebEngine, and we simply need a setting which accepts an arbitrary string. --- tests/end2end/fixtures/test_quteprocess.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/end2end/fixtures/test_quteprocess.py b/tests/end2end/fixtures/test_quteprocess.py index 6df34a21f..374421089 100644 --- a/tests/end2end/fixtures/test_quteprocess.py +++ b/tests/end2end/fixtures/test_quteprocess.py @@ -281,6 +281,6 @@ def test_xpath_escape(string, expected): 'foo"bar', # Make sure a " is preserved ]) def test_set(quteproc, value): - quteproc.set_setting('network', 'accept-language', value) - read_back = quteproc.get_setting('network', 'accept-language') + quteproc.set_setting('general', 'default-encoding', value) + read_back = quteproc.get_setting('general', 'default-encoding') assert read_back == value From 6cd890aa19a30c906fff8d8cd770cd0b32d92990 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 11:48:51 +0200 Subject: [PATCH 342/365] bdd: Set request object on QuteProc --- tests/end2end/fixtures/quteprocess.py | 70 +++++++++------------- tests/end2end/fixtures/test_quteprocess.py | 61 +++++++++++++++++-- 2 files changed, 83 insertions(+), 48 deletions(-) diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index 60b0c14fe..9f0e2f6ee 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -140,17 +140,14 @@ class QuteProc(testprocess.Process): """A running qutebrowser process used for tests. Attributes: - _delay: Delay to wait between commands. _ipc_socket: The IPC socket of the started instance. - _httpbin: The HTTPBin webserver. _webengine: Whether to use QtWebEngine basedir: The base directory for this instance. + _request: The request object for the current test. _focus_ready: Whether the main window got focused. _load_ready: Whether the about:blank page got loaded. - _profile: If True, do profiling of the subprocesses. _instance_id: A unique ID for this QuteProc instance _run_counter: A counter to get a unique ID for each run. - _config: The pytest config object Signals: got_error: Emitted when there was an error log line. @@ -161,20 +158,16 @@ class QuteProc(testprocess.Process): KEYS = ['timestamp', 'loglevel', 'category', 'module', 'function', 'line', 'message'] - def __init__(self, httpbin, delay, *, webengine=False, profile=False, - config=None, parent=None): + def __init__(self, request, *, parent=None): super().__init__(parent) - self._webengine = webengine - self._profile = profile - self._delay = delay - self._httpbin = httpbin + self._webengine = request.config.getoption('--qute-bdd-webengine') self._ipc_socket = None self.basedir = None self._focus_ready = False self._load_ready = False self._instance_id = next(instance_counter) self._run_counter = itertools.count() - self._config = config + self._request = request def _is_ready(self, what): """Called by _parse_line if loading/focusing is done. @@ -204,7 +197,7 @@ class QuteProc(testprocess.Process): else: raise - log_line.use_color = self._config.getoption('--color') != 'no' + log_line.use_color = self._request.config.getoption('--color') != 'no' self._log(log_line) start_okay_message_load = ( @@ -244,15 +237,16 @@ class QuteProc(testprocess.Process): return log_line def _executable_args(self): + profile = self._request.config.getoption('--qute-profile-subprocs') if hasattr(sys, 'frozen'): - if self._profile: + if profile: raise Exception("Can't profile with sys.frozen!") executable = os.path.join(os.path.dirname(sys.executable), 'qutebrowser') args = [] else: executable = sys.executable - if self._profile: + if profile: profile_dir = os.path.join(os.getcwd(), 'prof') profile_id = '{}_{}'.format(self._instance_id, next(self._run_counter)) @@ -283,9 +277,10 @@ class QuteProc(testprocess.Process): if path.startswith('about:') or path.startswith('qute:'): return path else: + httpbin = self._request.getfuncargvalue('httpbin') return '{}://localhost:{}/{}'.format( 'https' if https else 'http', - self._httpbin.port if port is None else port, + httpbin.port if port is None else port, path if path != '/' else '') def wait_for_js(self, message): @@ -341,20 +336,20 @@ class QuteProc(testprocess.Process): for sect, opt, value in settings: self.set_setting(sect, opt, value) - def after_test(self, did_fail): - """Handle unexpected/skip logging and clean up after each test. - - Args: - did_fail: Set if the main test failed already, then logged errors - are ignored. - """ + def after_test(self): + """Handle unexpected/skip logging and clean up after each test.""" __tracebackhide__ = True bad_msgs = [msg for msg in self._data if self._is_error_logline(msg) and not msg.expected] - if did_fail: - super().after_test() - return + try: + call = self._request.node.rep_call + except AttributeError: + pass + else: + if call.failed or hasattr(call, 'wasxfail'): + super().after_test() + return try: if bad_msgs: @@ -370,7 +365,8 @@ class QuteProc(testprocess.Process): def send_ipc(self, commands, target_arg=''): """Send a raw command to the running IPC socket.""" - time.sleep(self._delay / 1000) + delay = self._request.config.getoption('--qute-delay') + time.sleep(delay / 1000) assert self._ipc_socket is not None ipc.send_to_running_instance(self._ipc_socket, commands, target_arg) @@ -604,11 +600,8 @@ def _xpath_escape(text): @pytest.yield_fixture(scope='module') def quteproc_process(qapp, httpbin, request): """Fixture for qutebrowser process which is started once per file.""" - delay = request.config.getoption('--qute-delay') - profile = request.config.getoption('--qute-profile-subprocs') - webengine = request.config.getoption('--qute-bdd-webengine') - proc = QuteProc(httpbin, delay, webengine=webengine, profile=profile, - config=request.config) + # Passing request so it has an initial config + proc = QuteProc(request) proc.start() yield proc proc.terminate() @@ -619,24 +612,17 @@ def quteproc(quteproc_process, httpbin, request): """Per-test qutebrowser fixture which uses the per-file process.""" request.node._quteproc_log = quteproc_process.captured_log quteproc_process.before_test() + quteproc_process.request = request yield quteproc_process - call = request.node.rep_call - did_fail = call.failed or hasattr(call, 'wasxfail') - quteproc_process.after_test(did_fail=did_fail) + quteproc_process.after_test() @pytest.yield_fixture def quteproc_new(qapp, httpbin, request): """Per-test qutebrowser process to test invocations.""" - delay = request.config.getoption('--qute-delay') - profile = request.config.getoption('--qute-profile-subprocs') - webengine = request.config.getoption('--qute-bdd-webengine') - proc = QuteProc(httpbin, delay, webengine=webengine, profile=profile, - config=request.config) + proc = QuteProc(request) request.node._quteproc_log = proc.captured_log # Not calling before_test here as that would start the process yield proc - call = request.node.rep_call - did_fail = call.failed or hasattr(call, 'wasxfail') - proc.after_test(did_fail=did_fail) + proc.after_test() proc.terminate() diff --git a/tests/end2end/fixtures/test_quteprocess.py b/tests/end2end/fixtures/test_quteprocess.py index 374421089..9511f76d4 100644 --- a/tests/end2end/fixtures/test_quteprocess.py +++ b/tests/end2end/fixtures/test_quteprocess.py @@ -22,6 +22,7 @@ import logging import datetime import json +import collections import pytest @@ -29,27 +30,75 @@ from end2end.fixtures import quteprocess, testprocess from qutebrowser.utils import log +class FakeRepCall: + + """Fake for request.node.rep_call.""" + + def __init__(self): + self.failed = False + + +class FakeConfig: + + """Fake for request.config.""" + + ARGS = { + '--qute-delay': 0, + '--color': True, + } + + def getoption(self, name): + return self.ARGS[name] + + +class FakeRequest: + + """Fake for request.""" + + def __init__(self, node, config, httpbin): + self.node = node + self.config = config + self._httpbin = httpbin + + def getfuncargvalue(self, name): + assert name == 'httpbin' + return self._httpbin + + +@pytest.fixture +def request_mock(quteproc, monkeypatch, httpbin): + """Patch out a pytest request.""" + fake_call = FakeRepCall() + fake_config = FakeConfig() + fake_node = collections.namedtuple('FakeNode', ['rep_call'])(fake_call) + fake_request = FakeRequest(fake_node, fake_config, httpbin) + assert not hasattr(fake_request.node.rep_call, 'wasxfail') + monkeypatch.setattr(quteproc, '_request', fake_request) + return fake_request + + @pytest.mark.parametrize('cmd', [ ':message-error test', ':jseval console.log("[FAIL] test");' ]) -def test_quteproc_error_message(qtbot, quteproc, cmd): +def test_quteproc_error_message(qtbot, quteproc, cmd, request_mock): """Make sure the test fails with an unexpected error message.""" with qtbot.waitSignal(quteproc.got_error): quteproc.send_cmd(cmd) # Usually we wouldn't call this from inside a test, but here we force the # error to occur during the test rather than at teardown time. with pytest.raises(pytest.fail.Exception): - quteproc.after_test(did_fail=False) + quteproc.after_test() -def test_quteproc_error_message_did_fail(qtbot, quteproc): +def test_quteproc_error_message_did_fail(qtbot, quteproc, request_mock): """Make sure the test does not fail on teardown if the main test failed.""" + request_mock.node.rep_call.failed = True with qtbot.waitSignal(quteproc.got_error): quteproc.send_cmd(':message-error test') # Usually we wouldn't call this from inside a test, but here we force the # error to occur during the test rather than at teardown time. - quteproc.after_test(did_fail=True) + quteproc.after_test() def test_quteproc_skip_via_js(qtbot, quteproc): @@ -59,7 +108,7 @@ def test_quteproc_skip_via_js(qtbot, quteproc): # Usually we wouldn't call this from inside a test, but here we force # the error to occur during the test rather than at teardown time. - quteproc.after_test(did_fail=False) + quteproc.after_test() assert str(excinfo.value) == 'test' @@ -83,7 +132,7 @@ def test_quteprocess_quitting(qtbot, quteproc_process): with qtbot.waitSignal(quteproc_process.proc.finished, timeout=15000): quteproc_process.send_cmd(':quit') with pytest.raises(testprocess.ProcessExited): - quteproc_process.after_test(did_fail=False) + quteproc_process.after_test() @pytest.mark.parametrize('data, attrs', [ From 8378e16139ee90034a8ea4ae5471464db293f33a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 12:20:23 +0200 Subject: [PATCH 343/365] Fix ignored tag in misc.feature Seems like pytest-bdd ignores that second tag... --- tests/end2end/features/misc.feature | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 8dfe8c4dc..f8414999e 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -381,8 +381,7 @@ Feature: Various utility commands. And I press the key "" Then no crash should happen - @pyqt>=5.3.1 - @qtwebengine_todo: JS prompt is not implemented yet + @pyqt>=5.3.1 @qtwebengine_todo: JS prompt is not implemented yet Scenario: Focusing download widget via Tab (original issue) When I open data/prompt/jsprompt.html And I run :click-element id button From 362c23692aab18240658922379fd457dc037398e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 13:05:59 +0200 Subject: [PATCH 344/365] Change quteproc._request back to quteproc.request Otherwise our per-test quteproc fixture wouldn't set the 'request' object properly from the outside, and quteproc always had a module as request.node. --- tests/end2end/fixtures/quteprocess.py | 14 +++++++------- tests/end2end/fixtures/test_quteprocess.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index 9f0e2f6ee..df2818888 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -143,7 +143,7 @@ class QuteProc(testprocess.Process): _ipc_socket: The IPC socket of the started instance. _webengine: Whether to use QtWebEngine basedir: The base directory for this instance. - _request: The request object for the current test. + request: The request object for the current test. _focus_ready: Whether the main window got focused. _load_ready: Whether the about:blank page got loaded. _instance_id: A unique ID for this QuteProc instance @@ -167,7 +167,7 @@ class QuteProc(testprocess.Process): self._load_ready = False self._instance_id = next(instance_counter) self._run_counter = itertools.count() - self._request = request + self.request = request def _is_ready(self, what): """Called by _parse_line if loading/focusing is done. @@ -197,7 +197,7 @@ class QuteProc(testprocess.Process): else: raise - log_line.use_color = self._request.config.getoption('--color') != 'no' + log_line.use_color = self.request.config.getoption('--color') != 'no' self._log(log_line) start_okay_message_load = ( @@ -237,7 +237,7 @@ class QuteProc(testprocess.Process): return log_line def _executable_args(self): - profile = self._request.config.getoption('--qute-profile-subprocs') + profile = self.request.config.getoption('--qute-profile-subprocs') if hasattr(sys, 'frozen'): if profile: raise Exception("Can't profile with sys.frozen!") @@ -277,7 +277,7 @@ class QuteProc(testprocess.Process): if path.startswith('about:') or path.startswith('qute:'): return path else: - httpbin = self._request.getfuncargvalue('httpbin') + httpbin = self.request.getfuncargvalue('httpbin') return '{}://localhost:{}/{}'.format( 'https' if https else 'http', httpbin.port if port is None else port, @@ -343,7 +343,7 @@ class QuteProc(testprocess.Process): if self._is_error_logline(msg) and not msg.expected] try: - call = self._request.node.rep_call + call = self.request.node.rep_call except AttributeError: pass else: @@ -365,7 +365,7 @@ class QuteProc(testprocess.Process): def send_ipc(self, commands, target_arg=''): """Send a raw command to the running IPC socket.""" - delay = self._request.config.getoption('--qute-delay') + delay = self.request.config.getoption('--qute-delay') time.sleep(delay / 1000) assert self._ipc_socket is not None diff --git a/tests/end2end/fixtures/test_quteprocess.py b/tests/end2end/fixtures/test_quteprocess.py index 9511f76d4..d2d7a5f41 100644 --- a/tests/end2end/fixtures/test_quteprocess.py +++ b/tests/end2end/fixtures/test_quteprocess.py @@ -73,7 +73,7 @@ def request_mock(quteproc, monkeypatch, httpbin): fake_node = collections.namedtuple('FakeNode', ['rep_call'])(fake_call) fake_request = FakeRequest(fake_node, fake_config, httpbin) assert not hasattr(fake_request.node.rep_call, 'wasxfail') - monkeypatch.setattr(quteproc, '_request', fake_request) + monkeypatch.setattr(quteproc, 'request', fake_request) return fake_request From 2eae6a06030f72a224bad1fe258f9f0f28edae3e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 13:11:29 +0200 Subject: [PATCH 345/365] bdd: Wait less for xfailing tests We now divide all timeouts by ten for xfailing tests, with the hope to still catch newly passing tests, but not spend too much time waiting. With a quick test, this reduced the testsuite run length from 12 to 7-8 minutes. --- tests/end2end/fixtures/quteprocess.py | 9 +++++++++ tests/end2end/fixtures/test_quteprocess.py | 13 +++++++++++-- tests/end2end/fixtures/testprocess.py | 6 +++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index df2818888..7a6e83413 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -293,6 +293,15 @@ class QuteProc(testprocess.Process): function='javaScriptConsoleMessage', message='[*] {}'.format(message)) + def wait_for(self, timeout=None, **kwargs): + """Extend wait_for to add divisor if a test is xfailing.""" + xfail = self.request.node.get_marker('xfail') + if xfail and xfail.args[0]: + kwargs['divisor'] = 10 + else: + kwargs['divisor'] = 1 + return super().wait_for(timeout=timeout, **kwargs) + def _is_error_logline(self, msg): """Check if the given LogLine is some kind of error message.""" is_js_error = (msg.category == 'js' and diff --git a/tests/end2end/fixtures/test_quteprocess.py b/tests/end2end/fixtures/test_quteprocess.py index d2d7a5f41..84c88f3d6 100644 --- a/tests/end2end/fixtures/test_quteprocess.py +++ b/tests/end2end/fixtures/test_quteprocess.py @@ -22,7 +22,6 @@ import logging import datetime import json -import collections import pytest @@ -50,6 +49,16 @@ class FakeConfig: def getoption(self, name): return self.ARGS[name] +class FakeNode: + + """Fake for request.node""" + + def __init__(self, call): + self.rep_call = call + + def get_marker(self, _name): + return None + class FakeRequest: @@ -70,7 +79,7 @@ def request_mock(quteproc, monkeypatch, httpbin): """Patch out a pytest request.""" fake_call = FakeRepCall() fake_config = FakeConfig() - fake_node = collections.namedtuple('FakeNode', ['rep_call'])(fake_call) + fake_node = FakeNode(fake_call) fake_request = FakeRequest(fake_node, fake_config, httpbin) assert not hasattr(fake_request.node.rep_call, 'wasxfail') monkeypatch.setattr(quteproc, 'request', fake_request) diff --git a/tests/end2end/fixtures/testprocess.py b/tests/end2end/fixtures/testprocess.py index 1b624ce29..cb45671ad 100644 --- a/tests/end2end/fixtures/testprocess.py +++ b/tests/end2end/fixtures/testprocess.py @@ -424,7 +424,7 @@ class Process(QObject): pass def wait_for(self, timeout=None, *, override_waited_for=False, - do_skip=False, **kwargs): + do_skip=False, divisor=1, **kwargs): """Wait until a given value is found in the data. Keyword arguments to this function get interpreted as attributes of the @@ -436,6 +436,7 @@ class Process(QObject): override_waited_for: If set, gets triggered by previous messages again. do_skip: If set, call pytest.skip on a timeout. + divisor: A factor to decrease the timeout by. Return: The matched line. @@ -449,6 +450,9 @@ class Process(QObject): timeout = 15000 else: timeout = 5000 + + timeout /= divisor + if not kwargs: raise TypeError("No keyword arguments given!") for key in kwargs: From f7a7e829393c49eb15b78d7742e97aef66502c13 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 13:15:45 +0200 Subject: [PATCH 346/365] bdd: Skip another flaky backforward test with qtwe --- tests/end2end/features/backforward.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/end2end/features/backforward.feature b/tests/end2end/features/backforward.feature index 88385f5a6..1b43cac4d 100644 --- a/tests/end2end/features/backforward.feature +++ b/tests/end2end/features/backforward.feature @@ -88,6 +88,7 @@ Feature: Going back and forward. - url: http://localhost:*/data/backforward/2.txt - url: http://localhost:*/data/backforward/3.txt + @qtwebengine_skip: Causes 'Ignoring invalid URL being added to history' sometimes? Scenario: Going back too much with count. Given I open data/backforward/1.txt When I open data/backforward/2.txt From b5444338bac5f6949baee792577b050191eab55d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 13:35:39 +0200 Subject: [PATCH 347/365] Fix lint --- tests/end2end/fixtures/test_quteprocess.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/end2end/fixtures/test_quteprocess.py b/tests/end2end/fixtures/test_quteprocess.py index 84c88f3d6..80094c588 100644 --- a/tests/end2end/fixtures/test_quteprocess.py +++ b/tests/end2end/fixtures/test_quteprocess.py @@ -49,9 +49,10 @@ class FakeConfig: def getoption(self, name): return self.ARGS[name] + class FakeNode: - """Fake for request.node""" + """Fake for request.node.""" def __init__(self, call): self.rep_call = call From 056c0c3c14c2f0aad7603bcdb069e067ab7268dd Mon Sep 17 00:00:00 2001 From: nanjekyejoannah Date: Tue, 16 Aug 2016 14:45:20 +0300 Subject: [PATCH 348/365] Make command keys configurable Fixes #672 --- qutebrowser/config/configdata.py | 3 +++ qutebrowser/keyinput/modeparsers.py | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 739799b28..608ae2b27 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1477,6 +1477,9 @@ KEY_DATA = collections.OrderedDict([ ('set-cmd-text :open -b -i {url:pretty}', ['xO']), ('set-cmd-text -s :open -w', ['wo']), ('set-cmd-text :open -w {url:pretty}', ['wO']), + ('set-cmd-text -s :search', ['/']), + ('set-cmd-text -s :search -r', ['?']), + ('set-cmd-text -s : ', [':']), ('open -t', ['ga', '']), ('open -w', ['']), ('tab-close', ['d', '']), diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index fb923bf92..455330f93 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -70,9 +70,6 @@ class NormalKeyParser(keyparser.CommandKeyParser): self._debug_log("Ignoring key '{}', because the normal mode is " "currently inhibited.".format(txt)) return self.Match.none - if not self._keystring and any(txt == c for c in STARTCHARS): - message.set_cmd_text(self._win_id, txt) - return self.Match.definitive match = super()._handle_single_key(e) if match == self.Match.partial: timeout = config.get('input', 'partial-timeout') @@ -231,7 +228,7 @@ class HintKeyParser(keyparser.CommandKeyParser): if keytype == self.Type.chain: hintmanager = objreg.get('hintmanager', scope='tab', window=self._win_id, tab='current') - hintmanager.handle_partial_key(cmdstr) + hintmanager.fire(cmdstr) else: # execute as command super().execute(cmdstr, keytype, count) From 29cd878902b3b82d50542f9c185bf244bd8dbfaa Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 13:39:12 +0200 Subject: [PATCH 349/365] Fix lint --- qutebrowser/config/configdata.py | 2 +- qutebrowser/keyinput/modeparsers.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 608ae2b27..699feb9a3 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1479,7 +1479,7 @@ KEY_DATA = collections.OrderedDict([ ('set-cmd-text :open -w {url:pretty}', ['wO']), ('set-cmd-text -s :search', ['/']), ('set-cmd-text -s :search -r', ['?']), - ('set-cmd-text -s : ', [':']), + ('set-cmd-text -s :', [':']), ('open -t', ['ga', '']), ('open -w', ['']), ('tab-close', ['d', '']), diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 455330f93..c5dd341fe 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -25,7 +25,6 @@ Module attributes: from PyQt5.QtCore import pyqtSlot, Qt -from qutebrowser.utils import message from qutebrowser.config import config from qutebrowser.keyinput import keyparser from qutebrowser.utils import usertypes, log, objreg, utils From 5a3328d834cede37da1d7a063fbad74f99420088 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 13:41:21 +0200 Subject: [PATCH 350/365] Update docs --- CHANGELOG.asciidoc | 2 ++ README.asciidoc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 045402ea2..a11969268 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -97,6 +97,8 @@ Changed - Sending a command to an existing instance (via "qutebrowser :reload") now doesn't mark it as urgent anymore. - `tabs -> title-format` now treats an empty string as valid. +- Bindings for `:`, `/` and `?` are now configured explicitly and not hardcoded + anymore. Deprecated ~~~~~~~~~~ diff --git a/README.asciidoc b/README.asciidoc index 147e9fa03..ebcc58316 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -172,9 +172,9 @@ Contributors, sorted by the number of commits in descending order: * ZDarian * Milan Svoboda * John ShaggyTwoDope Jenkins +* nanjekyejoannah * Peter Vilim * Clayton Craft -* nanjekyejoannah * Oliver Caldwell * Jonas Schürmann * error800 From d25fde4f29f8af5a1d2b1c26278c49e57723e1ab Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 13:42:38 +0200 Subject: [PATCH 351/365] Whoops... --- qutebrowser/keyinput/modeparsers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index c5dd341fe..aa788ddc3 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -227,7 +227,7 @@ class HintKeyParser(keyparser.CommandKeyParser): if keytype == self.Type.chain: hintmanager = objreg.get('hintmanager', scope='tab', window=self._win_id, tab='current') - hintmanager.fire(cmdstr) + hintmanager.handle_partial_key(cmdstr) else: # execute as command super().execute(cmdstr, keytype, count) From 1d5a3a617572f1c53f84da885753164463a4ef7f Mon Sep 17 00:00:00 2001 From: knaggita Date: Tue, 9 Aug 2016 14:56:26 +0300 Subject: [PATCH 352/365] Add :debug-loglevel :debug-logfilter commands --- qutebrowser/misc/utilcmds.py | 29 +++++++++++++++++++++++++++++ qutebrowser/utils/log.py | 26 +++++++++++++++++++++++++- tests/end2end/features/misc.feature | 2 +- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index e99ad45fb..e192cecda 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -256,3 +256,32 @@ def window_only(current_win_id): for win_id, window in objreg.window_registry.items(): if win_id != current_win_id: window.close() + + +@cmdutils.register(debug=True) +@cmdutils.argument('level', choices=[level.lower() + for level in log.LOG_LEVELS]) +def debug_log_level(level: str): + """Change the log level for console logging. + + Args: + level: log level for console log. + """ + log.console_handler.setLevel(log.LOG_LEVELS[level.upper()]) + + +@cmdutils.register(debug=True) +def debug_log_filter(filter_names: str): + """Change the log filter for console logging. + + Args: + filter_names: log filters for console log. + """ + if set(filter_names.split(',')).issubset(log.LOGGER_NAMES): + log.console_handler.removeFilter(log.console_filter) + log.console_filter = log.LogFilter(filter_names.split(',')) + log.console_handler.addFilter(log.console_filter) + else: + raise cmdexc.CommandError("Invalid argument, {} choose from {}". + format( + filter_names, ','.join(log.LOGGER_NAMES))) diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index 177c263f8..6fd9c4d62 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -87,6 +87,15 @@ LOG_LEVELS = { 'CRITICAL': logging.CRITICAL, } +LOGGER_NAMES = [ + 'statusbar', 'completion', 'init', 'url', + 'destroy', 'modes', 'webview', 'misc', + 'mouse', 'procs', 'hints', 'keyboard', + 'commands', 'signals', 'downloads', + 'js', 'qt', 'rfc6266', 'ipc', 'shlexer', + 'save', 'message', 'config', 'sessions' +] + def vdebug(self, msg, *args, **kwargs): """Log with a VDEBUG level. @@ -131,6 +140,8 @@ sessions = logging.getLogger('sessions') ram_handler = None +console_handler = None +console_filter = None def stub(suffix=''): @@ -149,6 +160,7 @@ class CriticalQtWarning(Exception): def init_log(args): """Init loggers based on the argparse namespace passed.""" + global console level = args.loglevel.upper() try: numeric_level = getattr(logging, level) @@ -161,9 +173,11 @@ def init_log(args): console, ram = _init_handlers(numeric_level, args.color, args.force_color, args.json_logging, args.loglines) root = logging.getLogger() + global console_filter if console is not None: if args.logfilter is not None: - console.addFilter(LogFilter(args.logfilter.split(','))) + console_filter = LogFilter(args.logfilter.split(',')) + console.addFilter(console_filter) root.addHandler(console) if ram is not None: root.addHandler(ram) @@ -175,6 +189,10 @@ def init_log(args): _log_inited = True +def change(filters): + console.addFilter(LogFilter(filters.split(','))) + + def _init_py_warnings(): """Initialize Python warning handling.""" warnings.simplefilter('default') @@ -210,6 +228,7 @@ def _init_handlers(level, color, force_color, json_logging, ram_capacity): json_logging: Output log lines in JSON (this disables all colors). """ global ram_handler + global console_handler console_fmt, ram_fmt, html_fmt, use_colorama = _init_formatters( level, color, force_color, json_logging) @@ -236,6 +255,11 @@ def _init_handlers(level, color, force_color, json_logging, ram_capacity): return console_handler, ram_handler +def change_loglevel(level): + value = LOG_LEVELS[level.upper()] + console_handler.setLevel(value) + + def _init_formatters(level, color, force_color, json_logging): """Init log formatters. diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index f8414999e..c61924de9 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -474,7 +474,7 @@ Feature: Various utility commands. ## https://github.com/The-Compiler/qutebrowser/issues/1523 Scenario: Completing a single option argument - When I run :set-cmd-text -s :-- + When I run :set-cmd-text -s :-- Then no crash should happen ## https://github.com/The-Compiler/qutebrowser/issues/1386 From bd6f4ae7c05f0673ecada9df8996013b87c240d2 Mon Sep 17 00:00:00 2001 From: knaggita Date: Fri, 12 Aug 2016 14:59:49 +0300 Subject: [PATCH 353/365] Add end2end tests issue52 --- tests/end2end/features/misc.feature | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index c61924de9..20b465c91 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -603,3 +603,27 @@ Feature: Various utility commands. And the following tabs should be open: - data/click_element.html - data/hello.txt (active) + + ## logfilter + + Scenario: Using :debug-log-filter with commands argument + When I run :debug-log-filter commands + And I run :message-info "Hello World" + Then the message "Hello World" should be shown + + Scenario: Using debug-log-filter with invalid filter + When I run :debug-log-filter hello + Then the error "Invalid argument, hello choose from statusbar,completion,init,url,destroy,modes,webview,misc,mouse,procs,hints,keyboard,commands,signals,downloads,js,qt,rfc6266,ipc,shlexer,save,message,config,sessions" should be shown + + Scenario: Using debug-log-level with invalid level + When I run :debug-log-level hello + Then the error "Invalid value hello - expected one of: debug, + error, vdebug, info, warning, critical" should be shown + + Scenario: :debug-log-level with warning argument + When I run :message-error the-error-message + And I run :message-warning the-warning-message + And I run :message-info the-info-message + And I run :debug-log-level warning + Then the error "the-error-message" should be shown + And the warning "the-warning-message" should be shown From 37758131f511994baec66eeee8c18a57ac4fa48a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 14:43:42 +0200 Subject: [PATCH 354/365] Delete broken :debug-log-{filter,level} tests --- tests/end2end/features/misc.feature | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 20b465c91..6e6f6f360 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -604,26 +604,10 @@ Feature: Various utility commands. - data/click_element.html - data/hello.txt (active) - ## logfilter - - Scenario: Using :debug-log-filter with commands argument - When I run :debug-log-filter commands - And I run :message-info "Hello World" - Then the message "Hello World" should be shown - - Scenario: Using debug-log-filter with invalid filter - When I run :debug-log-filter hello - Then the error "Invalid argument, hello choose from statusbar,completion,init,url,destroy,modes,webview,misc,mouse,procs,hints,keyboard,commands,signals,downloads,js,qt,rfc6266,ipc,shlexer,save,message,config,sessions" should be shown + # :debug-log-level + # Other :debug-log-{level,filter} features are tested in + # unit/utils/test_log.py as using them would break end2end tests. Scenario: Using debug-log-level with invalid level When I run :debug-log-level hello - Then the error "Invalid value hello - expected one of: debug, - error, vdebug, info, warning, critical" should be shown - - Scenario: :debug-log-level with warning argument - When I run :message-error the-error-message - And I run :message-warning the-warning-message - And I run :message-info the-info-message - And I run :debug-log-level warning - Then the error "the-error-message" should be shown - And the warning "the-warning-message" should be shown + Then the error "Invalid value hello - expected one of: debug, error, vdebug, info, warning, critical" should be shown From 75c3b1a9f8480e73ebf3164ca441d61f9a0d3025 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 14:57:21 +0200 Subject: [PATCH 355/365] Fix test for :debug-log-level with invalid level Since we're getting dictionary keys in choices=..., we need to sort them so we get a consistent message. --- qutebrowser/misc/utilcmds.py | 5 +++-- tests/end2end/features/misc.feature | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index e192cecda..84d666c58 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -259,8 +259,9 @@ def window_only(current_win_id): @cmdutils.register(debug=True) -@cmdutils.argument('level', choices=[level.lower() - for level in log.LOG_LEVELS]) +@cmdutils.argument('level', choices=sorted( + (level.lower() for level in log.LOG_LEVELS), + key=lambda e: log.LOG_LEVELS[e.upper()])) def debug_log_level(level: str): """Change the log level for console logging. diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 6e6f6f360..535c342aa 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -610,4 +610,4 @@ Feature: Various utility commands. Scenario: Using debug-log-level with invalid level When I run :debug-log-level hello - Then the error "Invalid value hello - expected one of: debug, error, vdebug, info, warning, critical" should be shown + Then the error "level: Invalid value hello - expected one of: vdebug, debug, info, warning, error, critical" should be shown From fd0965703e605aaaf75bd99c6bd47a4f64e418ed Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 14:57:55 +0200 Subject: [PATCH 356/365] Remove log.change_loglevel --- qutebrowser/utils/log.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index 6fd9c4d62..badf719bf 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -255,11 +255,6 @@ def _init_handlers(level, color, force_color, json_logging, ram_capacity): return console_handler, ram_handler -def change_loglevel(level): - value = LOG_LEVELS[level.upper()] - console_handler.setLevel(value) - - def _init_formatters(level, color, force_color, json_logging): """Init log formatters. From 71b0876188606911090d777518423ce508325aba Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 15:03:19 +0200 Subject: [PATCH 357/365] Improve :debug-log-filter message This changes the message so it resembles the default choices=... one, and also changes the argument to "filters" because that sounds nicer as a metavar. --- qutebrowser/misc/utilcmds.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index 84d666c58..e8324e2b8 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -272,17 +272,17 @@ def debug_log_level(level: str): @cmdutils.register(debug=True) -def debug_log_filter(filter_names: str): +def debug_log_filter(filters: str): """Change the log filter for console logging. Args: - filter_names: log filters for console log. + filters: log filters for console log. """ - if set(filter_names.split(',')).issubset(log.LOGGER_NAMES): + if set(filters.split(',')).issubset(log.LOGGER_NAMES): log.console_handler.removeFilter(log.console_filter) - log.console_filter = log.LogFilter(filter_names.split(',')) + log.console_filter = log.LogFilter(filters.split(',')) log.console_handler.addFilter(log.console_filter) else: - raise cmdexc.CommandError("Invalid argument, {} choose from {}". - format( - filter_names, ','.join(log.LOGGER_NAMES))) + raise cmdexc.CommandError("filters: Invalid value {} - expected one " + "of: {}".format(filters, + ', '.join(log.LOGGER_NAMES))) From 33e71525ed492be134a5bb1ce97e05ee4ae5b519 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 15:04:11 +0200 Subject: [PATCH 358/365] bdd: Test :debug-log-filter with invalid filter --- tests/end2end/features/misc.feature | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 535c342aa..a5b31db2a 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -604,10 +604,14 @@ Feature: Various utility commands. - data/click_element.html - data/hello.txt (active) - # :debug-log-level + # :debug-log-level / :debug-log-filter # Other :debug-log-{level,filter} features are tested in # unit/utils/test_log.py as using them would break end2end tests. Scenario: Using debug-log-level with invalid level When I run :debug-log-level hello Then the error "level: Invalid value hello - expected one of: vdebug, debug, info, warning, error, critical" should be shown + + Scenario: Using debug-log-filter with invalid filter + When I run :debug-log-filter blah + Then the error "filters: Invalid value blah - expected one of: statusbar, *" should be shown From 6781f6409bf84b6340af74331c7a60f29126f190 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 15:05:20 +0200 Subject: [PATCH 359/365] bdd: Test :debug-log-capacity with negative value --- tests/end2end/features/misc.feature | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index a5b31db2a..0e96aa3c3 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -471,6 +471,10 @@ Feature: Various utility commands. Then the page should contain the plaintext "newstuff" And the page should not contain the plaintext "oldstuff" + Scenario: Using :debug-log-capacity with negative capacity + When I run :debug-log-capacity -1 + Then the error "Can't set a negative log capacity!" should be shown + ## https://github.com/The-Compiler/qutebrowser/issues/1523 Scenario: Completing a single option argument From 3b897d6a64e20ce4e38ff99a1f46db16ea0176ca Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 15:05:45 +0200 Subject: [PATCH 360/365] bdd: Move :debug-log-* near :log-capacity tests --- tests/end2end/features/misc.feature | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 0e96aa3c3..39f44e31e 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -475,6 +475,18 @@ Feature: Various utility commands. When I run :debug-log-capacity -1 Then the error "Can't set a negative log capacity!" should be shown + # :debug-log-level / :debug-log-filter + # Other :debug-log-{level,filter} features are tested in + # unit/utils/test_log.py as using them would break end2end tests. + + Scenario: Using debug-log-level with invalid level + When I run :debug-log-level hello + Then the error "level: Invalid value hello - expected one of: vdebug, debug, info, warning, error, critical" should be shown + + Scenario: Using debug-log-filter with invalid filter + When I run :debug-log-filter blah + Then the error "filters: Invalid value blah - expected one of: statusbar, *" should be shown + ## https://github.com/The-Compiler/qutebrowser/issues/1523 Scenario: Completing a single option argument @@ -607,15 +619,3 @@ Feature: Various utility commands. And the following tabs should be open: - data/click_element.html - data/hello.txt (active) - - # :debug-log-level / :debug-log-filter - # Other :debug-log-{level,filter} features are tested in - # unit/utils/test_log.py as using them would break end2end tests. - - Scenario: Using debug-log-level with invalid level - When I run :debug-log-level hello - Then the error "level: Invalid value hello - expected one of: vdebug, debug, info, warning, error, critical" should be shown - - Scenario: Using debug-log-filter with invalid filter - When I run :debug-log-filter blah - Then the error "filters: Invalid value blah - expected one of: statusbar, *" should be shown From e1cd905163360a1ea169b893eaa4876be85ba94a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 15:06:08 +0200 Subject: [PATCH 361/365] Move :window-only below :debug-log-* in utilcmds --- qutebrowser/misc/utilcmds.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index e8324e2b8..dcb1ce7d0 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -249,15 +249,6 @@ def log_capacity(capacity: int): log.ram_handler.change_log_capacity(capacity) -@cmdutils.register() -@cmdutils.argument('current_win_id', win_id=True) -def window_only(current_win_id): - """Close all windows except for the current one.""" - for win_id, window in objreg.window_registry.items(): - if win_id != current_win_id: - window.close() - - @cmdutils.register(debug=True) @cmdutils.argument('level', choices=sorted( (level.lower() for level in log.LOG_LEVELS), @@ -286,3 +277,12 @@ def debug_log_filter(filters: str): raise cmdexc.CommandError("filters: Invalid value {} - expected one " "of: {}".format(filters, ', '.join(log.LOGGER_NAMES))) + + +@cmdutils.register() +@cmdutils.argument('current_win_id', win_id=True) +def window_only(current_win_id): + """Close all windows except for the current one.""" + for win_id, window in objreg.window_registry.items(): + if win_id != current_win_id: + window.close() From 7e3d1ccd2437f4cd34595141bfb0ef3e650bd8ea Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 15:08:15 +0200 Subject: [PATCH 362/365] Simplify :debug-log-filter implementation --- qutebrowser/misc/utilcmds.py | 4 +--- qutebrowser/utils/log.py | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index dcb1ce7d0..20fce5e47 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -270,9 +270,7 @@ def debug_log_filter(filters: str): filters: log filters for console log. """ if set(filters.split(',')).issubset(log.LOGGER_NAMES): - log.console_handler.removeFilter(log.console_filter) - log.console_filter = log.LogFilter(filters.split(',')) - log.console_handler.addFilter(log.console_filter) + log.console_filter.names = filters.split(',') else: raise cmdexc.CommandError("filters: Invalid value {} - expected one " "of: {}".format(filters, diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index badf719bf..1ee91bad5 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -467,16 +467,16 @@ class LogFilter(logging.Filter): def __init__(self, names): super().__init__() - self._names = names + self.names = names def filter(self, record): """Determine if the specified record is to be logged.""" - if self._names is None: + if self.names is None: return True if record.levelno > logging.DEBUG: # More important than DEBUG, so we won't filter at all return True - for name in self._names: + for name in self.names: if record.name == name: return True elif not record.name.startswith(name): From 5367434a13e8c26874cffa42f2bf569d27d5110c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 15:20:08 +0200 Subject: [PATCH 363/365] Add a unit test for :debug-log-filter --- tests/unit/utils/test_log.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/unit/utils/test_log.py b/tests/unit/utils/test_log.py index 4bf12c526..c386b9694 100644 --- a/tests/unit/utils/test_log.py +++ b/tests/unit/utils/test_log.py @@ -29,6 +29,7 @@ import pytest import pytest_catchlog from qutebrowser.utils import log +from qutebrowser.misc import utilcmds @pytest.yield_fixture(autouse=True) @@ -167,6 +168,19 @@ class TestLogFilter: record = self._make_record(logger, "bacon", level=logging.INFO) assert logfilter.filter(record) + @pytest.mark.parametrize('category, logged_before, logged_after', [ + ('init', True, False), ('url', False, True), ('js', False, True)]) + def test_debug_log_filter_cmd(self, monkeypatch, logger, category, + logged_before, logged_after): + logfilter = log.LogFilter(["init"]) + monkeypatch.setattr(log, 'console_filter', logfilter) + + record = self._make_record(logger, category) + + assert logfilter.filter(record) == logged_before + utilcmds.debug_log_filter('url,js') + assert logfilter.filter(record) == logged_after + class TestRAMHandler: From e074192cc47e7f0b62efd63d9b697bea0d01d9f9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 15:27:56 +0200 Subject: [PATCH 364/365] Update docs --- CHANGELOG.asciidoc | 2 ++ README.asciidoc | 2 +- doc/help/commands.asciidoc | 20 ++++++++++++++++++++ qutebrowser/misc/utilcmds.py | 5 +++-- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index a11969268..d16e7570a 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -39,6 +39,8 @@ Added to focus the previous/next category in the completion (bound to `` and `` by default). - New `:click-element` command to fake a click on a element. +- New `:debug-log-filter` command to change console log filtering on-the-fly. +- New `:debug-log-level` command to change the console loglevel on-the-fly. Changed ~~~~~~~ diff --git a/README.asciidoc b/README.asciidoc index ebcc58316..6af28d775 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -199,6 +199,7 @@ Contributors, sorted by the number of commits in descending order: * Brian Jackson * sbinix * neeasade +* knaggita * jnphilipp * Tobias Patzl * Stefan Tatschner @@ -224,7 +225,6 @@ Contributors, sorted by the number of commits in descending order: * zwarag * xd1le * oniondreams -* knaggita * issue * haxwithaxe * evan diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index e73d6cf80..eb76fa84f 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -1449,6 +1449,8 @@ These commands are mainly intended for debugging. They are hidden if qutebrowser |<>|Crash for debugging purposes. |<>|Dump the current page's content to a file. |<>|Change the number of log lines to be stored in RAM. +|<>|Change the log filter for console logging. +|<>|Change the log level for console logging. |<>|Evaluate a python string and display the results as a web page. |<>|Put data into the fake clipboard and enable logging, used for tests. |<>|Trace executed code via hunter. @@ -1500,6 +1502,24 @@ Change the number of log lines to be stored in RAM. ==== positional arguments * +'capacity'+: Number of lines for the log. +[[debug-log-filter]] +=== debug-log-filter +Syntax: +:debug-log-filter 'filters'+ + +Change the log filter for console logging. + +==== positional arguments +* +'filters'+: A comma separated list of logger names. + +[[debug-log-level]] +=== debug-log-level +Syntax: +:debug-log-level 'level'+ + +Change the log level for console logging. + +==== positional arguments +* +'level'+: The log level to set. + [[debug-pyeval]] === debug-pyeval Syntax: +:debug-pyeval [*--quiet*] 's'+ diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index 20fce5e47..4d9ebb4a3 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -22,6 +22,7 @@ import functools import types import traceback +import logging try: import hunter @@ -257,7 +258,7 @@ def debug_log_level(level: str): """Change the log level for console logging. Args: - level: log level for console log. + level: The log level to set. """ log.console_handler.setLevel(log.LOG_LEVELS[level.upper()]) @@ -267,7 +268,7 @@ def debug_log_filter(filters: str): """Change the log filter for console logging. Args: - filters: log filters for console log. + filters: A comma separated list of logger names. """ if set(filters.split(',')).issubset(log.LOGGER_NAMES): log.console_filter.names = filters.split(',') From 1cfadbf034b9a43f80b88aa4fec201bc8fb212dd Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 19 Aug 2016 15:30:02 +0200 Subject: [PATCH 365/365] Fix / ? : bindings This was broken in #1859 and I didn't notice... --- qutebrowser/config/configdata.py | 10 +++++++--- tests/unit/config/test_config.py | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 699feb9a3..19e4f8ddf 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1477,9 +1477,9 @@ KEY_DATA = collections.OrderedDict([ ('set-cmd-text :open -b -i {url:pretty}', ['xO']), ('set-cmd-text -s :open -w', ['wo']), ('set-cmd-text :open -w {url:pretty}', ['wO']), - ('set-cmd-text -s :search', ['/']), - ('set-cmd-text -s :search -r', ['?']), - ('set-cmd-text -s :', [':']), + ('set-cmd-text /', ['/']), + ('set-cmd-text ?', ['?']), + ('set-cmd-text :', [':']), ('open -t', ['ga', '']), ('open -w', ['']), ('tab-close', ['d', '']), @@ -1730,4 +1730,8 @@ CHANGED_KEY_COMMANDS = [ (re.compile(r'^open -([twb]) {primary}$'), r'open -\1 -- {primary}'), (re.compile(r'^paste-primary$'), r'insert-text {primary}'), + + (re.compile(r'^set-cmd-text -s :search$'), r'set-cmd-text /'), + (re.compile(r'^set-cmd-text -s :search -r$'), r'set-cmd-text ?'), + (re.compile(r'^set-cmd-text -s :$'), r'set-cmd-text :'), ] diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index 70d9e0ddf..f796f8152 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -363,6 +363,10 @@ class TestKeyConfigParser: ('open {clipboard}', 'open -- {clipboard}'), ('open -t {clipboard}', 'open -t -- {clipboard}'), ('open -b {primary}', 'open -b -- {primary}'), + + ('set-cmd-text -s :search', 'set-cmd-text /'), + ('set-cmd-text -s :search -r', 'set-cmd-text ?'), + ('set-cmd-text -s :', 'set-cmd-text :'), ] ) def test_migrations(self, old, new_expected):