# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2014-2021 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 . import pytest from PyQt5.QtCore import QUrl from qutebrowser.browser import navigate from qutebrowser.utils import urlutils class TestIncDec: pytestmark = pytest.mark.usefixtures('config_stub') @pytest.mark.parametrize('incdec', ['increment', 'decrement']) @pytest.mark.parametrize('value', [ '{}foo', 'foo{}', 'foo{}bar', '42foo{}' ]) @pytest.mark.parametrize('url', [ 'http://example.com:80/v1/path/{}/test', 'http://example.com:80/v1/query_test?value={}', 'http://example.com:80/v1/anchor_test#{}', 'http://host_{}_test.com:80', 'http://m4ny.c0m:80/number5/3very?where=yes#{}', # Make sure that FullyDecoded is not used (to avoid losing information) 'http://localhost/%3A{}', 'http://localhost/:{}', 'http://localhost/?v=%3A{}', 'http://localhost/?v=:{}', 'http://localhost/#%3A{}', 'http://localhost/#:{}', # Make sure that spaces in paths work 'http://example.com/path with {} spaces', ]) def test_incdec(self, incdec, value, url, config_stub): if (value == '{}foo' and url == 'http://example.com/path with {} spaces'): pytest.xfail("https://github.com/qutebrowser/qutebrowser/issues/4917") config_stub.val.url.incdec_segments = ['host', 'path', 'query', 'anchor'] # The integer used should not affect test output, as long as it's # bigger than 1 # 20 was chosen by dice roll, guaranteed to be random base_value = value.format(20) if incdec == 'increment': expected_value = value.format(21) else: expected_value = value.format(19) base_url = QUrl(url.format(base_value)) expected_url = QUrl(url.format(expected_value)) assert navigate.incdec(base_url, 1, incdec) == expected_url def test_port(self, config_stub): config_stub.val.url.incdec_segments = ['port'] base_url = QUrl('http://localhost:8000') new_url = navigate.incdec(base_url, 1, 'increment') assert new_url == QUrl('http://localhost:8001') new_url = navigate.incdec(base_url, 1, 'decrement') assert new_url == QUrl('http://localhost:7999') def test_port_default(self, config_stub): """Test that a default port (with url.port() == -1) is not touched.""" config_stub.val.url.incdec_segments = ['port'] base_url = QUrl('http://localhost') with pytest.raises(navigate.Error): navigate.incdec(base_url, 1, 'increment') @pytest.mark.parametrize('inc_or_dec', ['increment', 'decrement']) @pytest.mark.parametrize('value', [ '{}foo', 'foo{}', 'foo{}bar', '42foo{}' ]) @pytest.mark.parametrize('url', [ 'http://example.com:80/v1/path/{}/test', 'http://example.com:80/v1/query_test?value={}', 'http://example.com:80/v1/anchor_test#{}', 'http://host_{}_test.com:80', 'http://m4ny.c0m:80/number5/3very?where=yes#{}', ]) @pytest.mark.parametrize('count', [1, 5, 100]) def test_count(self, inc_or_dec, value, url, count, config_stub): config_stub.val.url.incdec_segments = ['host', 'path', 'query', 'anchor'] base_value = value.format(20) if inc_or_dec == 'increment': expected_value = value.format(20 + count) else: if count > 20: return expected_value = value.format(20 - count) base_url = QUrl(url.format(base_value)) expected_url = QUrl(url.format(expected_value)) new_url = navigate.incdec(base_url, count, inc_or_dec) assert new_url == expected_url @pytest.mark.parametrize('number, expected, inc_or_dec', [ ('01', '02', 'increment'), ('09', '10', 'increment'), ('009', '010', 'increment'), ('02', '01', 'decrement'), ('10', '9', 'decrement'), ('010', '009', 'decrement') ]) def test_leading_zeroes(self, number, expected, inc_or_dec, config_stub): config_stub.val.url.incdec_segments = ['path'] url = 'http://example.com/{}' base_url = QUrl(url.format(number)) expected_url = QUrl(url.format(expected)) new_url = navigate.incdec(base_url, 1, inc_or_dec) assert new_url == expected_url @pytest.mark.parametrize('url, segments, expected', [ ('http://ex4mple.com/test_4?page=3#anchor2', ['host'], 'http://ex5mple.com/test_4?page=3#anchor2'), ('http://ex4mple.com/test_4?page=3#anchor2', ['host', 'path'], 'http://ex4mple.com/test_5?page=3#anchor2'), ('http://ex4mple.com/test_4?page=3#anchor5', ['host', 'path', 'query'], 'http://ex4mple.com/test_4?page=4#anchor5'), ]) def test_segment_ignored(self, url, segments, expected, config_stub): config_stub.val.url.incdec_segments = segments new_url = navigate.incdec(QUrl(url), 1, 'increment') assert new_url == QUrl(expected) @pytest.mark.parametrize('url', [ "http://example.com/long/path/but/no/number", "http://ex4mple.com/number/in/hostname", "http://example.com:42/number/in/port", "http://www2.example.com/number/in/subdomain", "http://example.com/%C3%B6/urlencoded/data", "http://example.com/number/in/anchor#5", "http://www2.ex4mple.com:42/all/of/the/%C3%A4bove#5", "http://localhost/url_encoded_in_query/?v=%3A", "http://localhost/url_encoded_in_anchor/#%3A", ]) def test_no_number(self, url): with pytest.raises(navigate.Error): navigate.incdec(QUrl(url), 1, "increment") @pytest.mark.parametrize('url, count', [ ('http://example.com/page_0.html', 1), ('http://example.com/page_1.html', 2), ]) def test_number_below_0(self, url, count): with pytest.raises(navigate.Error): navigate.incdec(QUrl(url), count, 'decrement') def test_invalid_url(self): with pytest.raises(urlutils.InvalidUrlError): navigate.incdec(QUrl(), 1, "increment") def test_wrong_mode(self): """Test if incdec rejects a wrong parameter for inc_or_dec.""" valid_url = QUrl("http://example.com/0") with pytest.raises(ValueError): navigate.incdec(valid_url, 1, "foobar") class TestUp: @pytest.mark.parametrize('url_suffix, count, expected_suffix', [ ('/one/two/three', 1, '/one/two'), ('/one/two/three?foo=bar', 1, '/one/two'), ('/one/two/three', 2, '/one'), ('/one/two%2Fthree', 1, '/one'), ('/one/two%2Fthree/four', 1, '/one/two%2Fthree'), ]) def test_up(self, url_suffix, count, expected_suffix): url_base = 'https://example.com' url = QUrl(url_base + url_suffix) assert url.isValid() new = navigate.path_up(url, count) assert new == QUrl(url_base + expected_suffix) def test_invalid_url(self): with pytest.raises(urlutils.InvalidUrlError): navigate.path_up(QUrl(), count=1) class TestStrip: @pytest.mark.parametrize('url_suffix', [ '?foo=bar', '#label', '?foo=bar#label', ]) def test_strip(self, url_suffix): url_base = 'https://example.com/test' url = QUrl(url_base + url_suffix) assert url.isValid() stripped = navigate.strip(url, count=1) assert stripped.isValid() assert stripped == QUrl(url_base) def test_count(self): with pytest.raises(navigate.Error, match='Count is not supported'): navigate.strip(QUrl('https://example.com/'), count=2) def test_invalid_url(self): with pytest.raises(urlutils.InvalidUrlError): navigate.strip(QUrl(), count=1)