This commit is contained in:
Benjamin Neil 2026-01-07 16:35:48 -08:00 committed by GitHub
commit b348222c78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 104 additions and 0 deletions

View File

@ -452,6 +452,63 @@ def history_clear(force=False):
title="Clear all browsing history?")
def _normalize_url(url: str) -> QUrl:
if not url:
return QUrl()
if '://' not in url:
url = f'https://{url}'
return QUrl(url)
def _find_matching_history_urls(qurl: QUrl) -> list[str]:
if not qurl.isValid():
return []
target_host = qurl.host().lower()
matching_urls = []
for entry in web_history:
entry_qurl = QUrl(entry.url)
if entry_qurl.host().lower() == target_host:
matching_urls.append(entry.url)
return matching_urls
def _delete_history_urls(urls: list[str]) -> None:
for url in urls:
web_history.delete_url(url)
@cmdutils.register()
def history_clear_site(url: str, force: bool = False) -> None:
"""Clear browsing history for a specific site.
Args:
url: URL or domain to clear (e.g., "example.com", "https://example.com/page")
force: Don't ask for confirmation.
"""
qurl = _normalize_url(url)
if not qurl.isValid():
raise cmdutils.CommandError(f"Invalid URL: {url}")
matching_urls = _find_matching_history_urls(qurl)
if not matching_urls:
message.info(f"No history entries found for: {url}")
return
if force:
_delete_history_urls(matching_urls)
message.info(f"Cleared {len(matching_urls)} history entries for: {url}")
else:
title = f"Clear {len(matching_urls)} history entries for {url}?"
message.confirm_async(
yes_action=lambda: _delete_history_urls(matching_urls),
title=title
)
@cmdutils.register(debug=True)
def debug_dump_history(dest):
"""Dump the history to a file in the old pre-SQL format.

View File

@ -326,6 +326,53 @@ class TestInit:
assert default_interface is None
class TestHistoryClearSite:
def test_normalize_url(self):
from qutebrowser.browser.history import _normalize_url
assert _normalize_url('example.com').toString() == 'https://example.com'
assert _normalize_url('https://example.com/page').toString() == 'https://example.com/page'
assert _normalize_url('').toString() == ''
def test_find_matching_history_urls_exact(self, web_history):
from qutebrowser.browser.history import _find_matching_history_urls, _normalize_url
web_history.add_url(QUrl('https://example.com/page'), atime=12345)
qurl = _normalize_url('https://example.com/page')
matches = _find_matching_history_urls(qurl)
assert len(matches) == 1
def test_find_matching_history_urls_no_match(self, web_history):
from qutebrowser.browser.history import _find_matching_history_urls, _normalize_url
web_history.add_url(QUrl('https://example.com/page'), atime=12345)
qurl = _normalize_url('https://other.com')
matches = _find_matching_history_urls(qurl)
assert len(matches) == 0
def test_history_clear_site_force(self, web_history, mocker):
web_history.add_url(QUrl('https://example.com/page'), atime=12345)
m = mocker.patch('qutebrowser.browser.history.message.info')
history.history_clear_site('https://example.com/page', force=True)
assert m.called
assert not list(web_history)
def test_history_clear_site_no_match(self, web_history, mocker):
web_history.add_url(QUrl('https://example.com/page'), atime=12345)
m = mocker.patch('qutebrowser.browser.history.message.info')
history.history_clear_site('https://other.com', force=True)
m.assert_called_once_with("No history entries found for: https://other.com")
assert len(web_history) == 1
def test_history_clear_site_invalid_url(self):
with pytest.raises(cmdutils.CommandError, match="Invalid URL"):
history.history_clear_site('not a url', force=True)
def test_history_clear_site_confirm(self, web_history, mocker):
web_history.add_url(QUrl('https://example.com/page'), atime=12345)
m = mocker.patch('qutebrowser.browser.history.message.confirm_async')
history.history_clear_site('https://example.com/page')
assert m.called
class TestDump:
def test_debug_dump_history(self, web_history, tmpdir):