From aa2e5a35d6f73efd1380dfda424d8a0170e41285 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 12 May 2015 17:05:01 +0200 Subject: [PATCH 1/7] Add javascript tests for position_caret.js. --- tests/javascript/base.html | 23 ++++ tests/javascript/conftest.py | 109 ++++++++++++++++++ .../position_caret/scrolled_down.html | 8 ++ tests/javascript/position_caret/simple.html | 4 + .../position_caret/test_position_caret.py | 78 +++++++++++++ 5 files changed, 222 insertions(+) create mode 100644 tests/javascript/base.html create mode 100644 tests/javascript/conftest.py create mode 100644 tests/javascript/position_caret/scrolled_down.html create mode 100644 tests/javascript/position_caret/simple.html create mode 100644 tests/javascript/position_caret/test_position_caret.py diff --git a/tests/javascript/base.html b/tests/javascript/base.html new file mode 100644 index 000000000..95085ff37 --- /dev/null +++ b/tests/javascript/base.html @@ -0,0 +1,23 @@ + + + + + + + qutebrowser javascript test + + + + {% block content %} + {% endblock %} + + diff --git a/tests/javascript/conftest.py b/tests/javascript/conftest.py new file mode 100644 index 000000000..dd623c9a4 --- /dev/null +++ b/tests/javascript/conftest.py @@ -0,0 +1,109 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2015 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + +"""pylint conftest file for javascript test.""" + +import os +import os.path + +import pytest +import jinja2 +from PyQt5.QtWebKit import QWebSettings +from PyQt5.QtWebKitWidgets import QWebView + +import qutebrowser + + +class JSTester: + + """Object returned by js_tester which provides test data and a webview. + + Attributes: + webview: The webview which is used. + _qtbot: The QtBot fixture from pytest-qt. + _jinja_env: The jinja2 environment used to get templates. + """ + + def __init__(self, webview, qtbot): + self.webview = webview + self._qtbot = qtbot + loader = jinja2.FileSystemLoader(os.path.dirname(__file__)) + self._jinja_env = jinja2.Environment(loader=loader, autoescape=True) + + def scroll_anchor(self, name): + """Scroll the main frame to the given anchor.""" + page = self.webview.page() + with self._qtbot.waitSignal(page.scrollRequested): + page.mainFrame().scrollToAnchor(name) + + def load(self, path): + """Load and display the given test data. + + Args: + path: The path to the test file, relative to the javascript/ + folder. + """ + template = self._jinja_env.get_template(path) + with self._qtbot.waitSignal(self.webview.loadFinished): + self.webview.setHtml(template.render()) + + def run_file(self, filename): + """Run a javascript file. + + Args: + filename: The javascript filename, relative to + qutebrowser/javascript. + + Return: + The javascript return value. + """ + base_path = os.path.join(os.path.dirname(qutebrowser.__file__), + 'javascript') + full_path = os.path.join(base_path, filename) + with open(full_path, 'r', encoding='utf-8') as f: + source = f.read() + return self.run(source) + + def run(self, source): + """Run the given javascript source. + + Args: + source: The source to run as a string. + + Return: + The javascript return value. + """ + assert self.webview.settings().testAttribute( + QWebSettings.JavascriptEnabled) + return self.webview.page().mainFrame().evaluateJavaScript(source) + + +@pytest.fixture +def js_tester(qtbot): + """Fixture to test javascript snippets. + + Provides a QWebView with a 640x480px size and a JSTester instance. + + Args: + qtbot: pytestqt.plugin.QtBot fixture. + """ + webview = QWebView() + qtbot.add_widget(webview) + webview.resize(640, 480) + return JSTester(webview, qtbot) diff --git a/tests/javascript/position_caret/scrolled_down.html b/tests/javascript/position_caret/scrolled_down.html new file mode 100644 index 000000000..1f4f5d8ff --- /dev/null +++ b/tests/javascript/position_caret/scrolled_down.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% block content %} +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero.

+ +

MARKER this should be the paragraph the caret is on.

+ +

Some more text

+{% endblock %} diff --git a/tests/javascript/position_caret/simple.html b/tests/javascript/position_caret/simple.html new file mode 100644 index 000000000..048ef0e11 --- /dev/null +++ b/tests/javascript/position_caret/simple.html @@ -0,0 +1,4 @@ +{% extends "base.html" %} +{% block content %} +

MARKER this should be the paragraph the caret is on.

+{% endblock %} diff --git a/tests/javascript/position_caret/test_position_caret.py b/tests/javascript/position_caret/test_position_caret.py new file mode 100644 index 000000000..de23cda46 --- /dev/null +++ b/tests/javascript/position_caret/test_position_caret.py @@ -0,0 +1,78 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2015 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + +"""Tests for position_caret.js.""" + +import pytest + +from PyQt5.QtCore import Qt +from PyQt5.QtWebKit import QWebSettings +from PyQt5.QtWebKitWidgets import QWebPage + + +@pytest.fixture(autouse=True) +def enable_caret_browsing(): + """Fixture to enable caret browsing globally.""" + QWebSettings.globalSettings().setAttribute( + QWebSettings.CaretBrowsingEnabled, True) + + +class CaretTester: + + """Helper class (for the caret_tester fixture) for asserts. + + Attributes: + js: The js_tester fixture. + """ + + def __init__(self, js_tester): + self.js = js_tester + + def check(self): + """Check whether the caret is before the MARKER text.""" + self.js.run_file('position_caret.js') + self.js.webview.triggerPageAction(QWebPage.SelectNextWord) + assert self.js.webview.selectedText() == "MARKER" + + def check_scrolled(self): + """Check if the page is scrolled down.""" + frame = self.js.webview.page().mainFrame() + minimum = frame.scrollBarMinimum(Qt.Vertical) + value = frame.scrollBarValue(Qt.Vertical) + assert value > minimum + + +@pytest.fixture +def caret_tester(js_tester): + """Helper fixture to test caret browsing positions.""" + return CaretTester(js_tester) + + +def test_simple(caret_tester): + """Test with a simple (one-line) HTML text.""" + caret_tester.js.load('position_caret/simple.html') + caret_tester.check() + + +def test_scrolled_down(caret_tester): + """Test with multiple text blocks with the viewport scrolled down.""" + caret_tester.js.load('position_caret/scrolled_down.html') + caret_tester.js.scroll_anchor('anchor') + caret_tester.check_scrolled() + caret_tester.check() From 27a34d5499d5365859213be80a142a50464e1ba5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 12 May 2015 17:32:33 +0200 Subject: [PATCH 2/7] Close anchor. --- tests/javascript/position_caret/scrolled_down.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/javascript/position_caret/scrolled_down.html b/tests/javascript/position_caret/scrolled_down.html index 1f4f5d8ff..c517acff1 100644 --- a/tests/javascript/position_caret/scrolled_down.html +++ b/tests/javascript/position_caret/scrolled_down.html @@ -2,7 +2,7 @@ {% block content %}

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero.

-

MARKER this should be the paragraph the caret is on.

+

MARKER this should be the paragraph the caret is on.

Some more text

{% endblock %} From 2b440bc8dbe97b7e1d1d9c9ad48cc2c5e17f78ea Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 12 May 2015 17:44:06 +0200 Subject: [PATCH 3/7] Handle QWebPage javascript methods. --- tests/javascript/conftest.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/javascript/conftest.py b/tests/javascript/conftest.py index dd623c9a4..cd1a2c69b 100644 --- a/tests/javascript/conftest.py +++ b/tests/javascript/conftest.py @@ -21,15 +21,45 @@ import os import os.path +import logging import pytest import jinja2 from PyQt5.QtWebKit import QWebSettings -from PyQt5.QtWebKitWidgets import QWebView +from PyQt5.QtWebKitWidgets import QWebView, QWebPage import qutebrowser +class TestWebPage(QWebPage): + + """QWebPage subclass which overrides some test methods. + + Attributes: + _logger: The logger used for alerts. + """ + + def __init__(self, parent=None): + super().__init__(parent) + self._logger = logging.getLogger('js-tests') + + def javaScriptAlert(self, _frame, msg): + """Log javascript alerts.""" + self._logger.info("js alert: {}".format(msg)) + + def javaScriptConfirm(self, _frame, msg): + """Fail tests on js confirm() as that should never happen.""" + pytest.fail("js confirm: {}".format(msg)) + + def javaScriptPrompt(self, _frame, msg, _default): + """Fail tests on js prompt() as that should never happen.""" + pytest.fail("js prompt: {}".format(msg)) + + def javaScriptConsoleMessage(self, msg, line, source): + """Fail tests on js console messages as they're used for errors.""" + pytest.fail("js console ({}:{}): {}".format(source, line, msg)) + + class JSTester: """Object returned by js_tester which provides test data and a webview. @@ -42,6 +72,7 @@ class JSTester: def __init__(self, webview, qtbot): self.webview = webview + self.webview.setPage(TestWebPage(self.webview)) self._qtbot = qtbot loader = jinja2.FileSystemLoader(os.path.dirname(__file__)) self._jinja_env = jinja2.Environment(loader=loader, autoescape=True) From 7edfdaa2718bdcc957f3f07b969cd07e8dec2050 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 12 May 2015 19:08:54 +0200 Subject: [PATCH 4/7] Add test for invisible elements. --- tests/javascript/conftest.py | 5 +++-- tests/javascript/position_caret/test_position_caret.py | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/javascript/conftest.py b/tests/javascript/conftest.py index cd1a2c69b..d97b38625 100644 --- a/tests/javascript/conftest.py +++ b/tests/javascript/conftest.py @@ -83,16 +83,17 @@ class JSTester: with self._qtbot.waitSignal(page.scrollRequested): page.mainFrame().scrollToAnchor(name) - def load(self, path): + def load(self, path, **kwargs): """Load and display the given test data. Args: path: The path to the test file, relative to the javascript/ folder. + **kwargs: Passed to jinja's template.render(). """ template = self._jinja_env.get_template(path) with self._qtbot.waitSignal(self.webview.loadFinished): - self.webview.setHtml(template.render()) + self.webview.setHtml(template.render(**kwargs)) def run_file(self, filename): """Run a javascript file. diff --git a/tests/javascript/position_caret/test_position_caret.py b/tests/javascript/position_caret/test_position_caret.py index de23cda46..98976ea3d 100644 --- a/tests/javascript/position_caret/test_position_caret.py +++ b/tests/javascript/position_caret/test_position_caret.py @@ -76,3 +76,10 @@ def test_scrolled_down(caret_tester): caret_tester.js.scroll_anchor('anchor') caret_tester.check_scrolled() caret_tester.check() + + +@pytest.mark.parametrize('style', ['visibility: hidden', 'display: none']) +def test_invisible(caret_tester, style): + """Test with hidden text elements.""" + caret_tester.js.load('position_caret/invisible.html', style=style) + caret_tester.check() From 2775f2b2eefffb4a7d20c26f920caa1c86d30a57 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 12 May 2015 19:15:16 +0200 Subject: [PATCH 5/7] Add some more tests. --- tests/javascript/position_caret/invisible.html | 5 +++++ tests/javascript/position_caret/scrolled_down_img.html | 9 +++++++++ tests/javascript/position_caret/test_position_caret.py | 9 +++++++++ 3 files changed, 23 insertions(+) create mode 100644 tests/javascript/position_caret/invisible.html create mode 100644 tests/javascript/position_caret/scrolled_down_img.html diff --git a/tests/javascript/position_caret/invisible.html b/tests/javascript/position_caret/invisible.html new file mode 100644 index 000000000..764b3ddb5 --- /dev/null +++ b/tests/javascript/position_caret/invisible.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% block content %} +

This line is hidden.

+

MARKER this should be the paragraph the caret is on.

+{% endblock %} diff --git a/tests/javascript/position_caret/scrolled_down_img.html b/tests/javascript/position_caret/scrolled_down_img.html new file mode 100644 index 000000000..af302fcc0 --- /dev/null +++ b/tests/javascript/position_caret/scrolled_down_img.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% block content %} +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero.

+ +
+ +

MARKER this should be the paragraph the caret is on.

+

Some more text

+{% endblock %} diff --git a/tests/javascript/position_caret/test_position_caret.py b/tests/javascript/position_caret/test_position_caret.py index 98976ea3d..0fbf4e3f1 100644 --- a/tests/javascript/position_caret/test_position_caret.py +++ b/tests/javascript/position_caret/test_position_caret.py @@ -83,3 +83,12 @@ def test_invisible(caret_tester, style): """Test with hidden text elements.""" caret_tester.js.load('position_caret/invisible.html', style=style) caret_tester.check() + + +def test_scrolled_down_img(caret_tester): + """Test with an image at the top with the viewport scrolled down.""" + caret_tester.js.load('position_caret/scrolled_down_img.html') + caret_tester.js.scroll_anchor('anchor') + caret_tester.check_scrolled() + caret_tester.check() + From 9fde38d96af118c9f1b3541b3485352a8a43f4ae Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 13 May 2015 06:31:48 +0200 Subject: [PATCH 6/7] Reset CaretBrowsingEnabled to original value. --- tests/javascript/position_caret/test_position_caret.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/javascript/position_caret/test_position_caret.py b/tests/javascript/position_caret/test_position_caret.py index 0fbf4e3f1..6fdfef11e 100644 --- a/tests/javascript/position_caret/test_position_caret.py +++ b/tests/javascript/position_caret/test_position_caret.py @@ -26,11 +26,14 @@ from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWebKitWidgets import QWebPage -@pytest.fixture(autouse=True) +@pytest.yield_fixture(autouse=True) def enable_caret_browsing(): """Fixture to enable caret browsing globally.""" - QWebSettings.globalSettings().setAttribute( - QWebSettings.CaretBrowsingEnabled, True) + settings = QWebSettings.globalSettings() + old_value = settings.testAttribute(QWebSettings.CaretBrowsingEnabled) + settings.setAttribute(QWebSettings.CaretBrowsingEnabled, True) + yield + settings.setAttribute(QWebSettings.CaretBrowsingEnabled, old_value) class CaretTester: From e35d284282d2a061e882a900511d68a0bfd3dbe5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 13 May 2015 06:32:09 +0200 Subject: [PATCH 7/7] Remove blank line. --- tests/javascript/position_caret/test_position_caret.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/javascript/position_caret/test_position_caret.py b/tests/javascript/position_caret/test_position_caret.py index 6fdfef11e..a44cfc87d 100644 --- a/tests/javascript/position_caret/test_position_caret.py +++ b/tests/javascript/position_caret/test_position_caret.py @@ -94,4 +94,3 @@ def test_scrolled_down_img(caret_tester): caret_tester.js.scroll_anchor('anchor') caret_tester.check_scrolled() caret_tester.check() -