feat: new settings menu + settings backend (#859)

* feat: add tab widget

* refactor: move languages dict to translations.py

* refactor: move build of Settings Modal to SettingsPanel class

* feat: hide title label

* feat: global settings class

* fix: initialise settings

* fix: properly store grid files changes

* fix: placeholder text for library settings

* feat: add ui elements for remaining global settings

* feat: add page size setting

* fix: version mismatch between pydantic and typing_extensions

* fix: update test_driver.py

* fix(test_file_path_options): replace patch with change of settings

* feat: setting for dark mode

* fix: only show restart_label when necessary

* fix: change modal from "done" type to "Save/Cancel" type

* feat: add test for GlobalSettings

* docs: mark roadmap item as completed

* fix(test_filepath_setting): Mock the app field of QtDriver

* Update src/tagstudio/main.py

Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>

* fix: address review suggestions

* fix: page size setting

* feat: change dark mode option to theme dropdown

* fix: test was expecting wrong behaviour

* fix: test was testing for correct behaviour, fix behaviour instead

* fix: test fr fr

* fix: tests fr fr fr

* fix: tests fr fr fr fr

* fix: update test

* fix: tests fr fr fr fr fr

* fix: select all was selecting hidden entries

* fix: create more thumbitems as necessary
This commit is contained in:
Jann Stute
2025-03-25 23:02:53 +01:00
committed by GitHub
parent e112788466
commit adb996e1d2
27 changed files with 680 additions and 399 deletions

View File

@@ -134,13 +134,15 @@ def qt_driver(qtbot, library):
with TemporaryDirectory() as tmp_dir:
class Args:
config_file = Path(tmp_dir) / "tagstudio.ini"
settings_file = Path(tmp_dir) / "settings.toml"
cache_file = Path(tmp_dir) / "tagstudio.ini"
open = Path(tmp_dir)
ci = True
with patch("tagstudio.qt.ts_qt.Consumer"), patch("tagstudio.qt.ts_qt.CustomRunnable"):
driver = QtDriver(Args())
driver.app = Mock()
driver.main_window = Mock()
driver.preview_panel = Mock()
driver.flow_container = Mock()

View File

@@ -28,5 +28,5 @@ def test_refresh_missing_files(library: Library):
assert list(registry.fix_unlinked_entries()) == [0, 1]
# `bar.md` should be relinked to new correct path
results = library.search_library(FilterState.from_path("bar.md"))
results = library.search_library(FilterState.from_path("bar.md", page_size=500))
assert results[0].path == Path("bar.md")

View File

@@ -1,5 +1,5 @@
import os
import pathlib
from pathlib import Path
from unittest.mock import patch
import pytest
@@ -7,10 +7,13 @@ from PySide6.QtGui import (
QAction,
)
from PySide6.QtWidgets import QMenu, QMenuBar
from pytestqt.qtbot import QtBot
from tagstudio.core.enums import SettingItems, ShowFilepathOption
from tagstudio.core.library.alchemy.library import LibraryStatus
from tagstudio.core.enums import ShowFilepathOption
from tagstudio.core.library.alchemy.library import Library, LibraryStatus
from tagstudio.core.library.alchemy.models import Entry
from tagstudio.qt.modals.settings_panel import SettingsPanel
from tagstudio.qt.ts_qt import QtDriver
from tagstudio.qt.widgets.preview_panel import PreviewPanel
@@ -23,7 +26,7 @@ from tagstudio.qt.widgets.preview_panel import PreviewPanel
ShowFilepathOption.SHOW_FILENAMES_ONLY.value,
],
)
def test_filepath_setting(qtbot, qt_driver, filepath_option):
def test_filepath_setting(qtbot: QtBot, qt_driver: QtDriver, filepath_option: ShowFilepathOption):
settings_panel = SettingsPanel(qt_driver)
qtbot.addWidget(settings_panel)
@@ -31,10 +34,10 @@ def test_filepath_setting(qtbot, qt_driver, filepath_option):
with patch.object(qt_driver, "update_recent_lib_menu", return_value=None):
# Set the file path option
settings_panel.filepath_combobox.setCurrentIndex(filepath_option)
settings_panel.apply_filepath_setting()
settings_panel.update_settings(qt_driver)
# Assert the setting is applied
assert qt_driver.settings.value(SettingItems.SHOW_FILEPATH) == filepath_option
assert qt_driver.settings.show_filepath == filepath_option
# Tests to see if the file paths are being displayed correctly
@@ -43,41 +46,47 @@ def test_filepath_setting(qtbot, qt_driver, filepath_option):
[
(
ShowFilepathOption.SHOW_FULL_PATHS,
lambda library: pathlib.Path(library.library_dir / "one/two/bar.md"),
lambda library: Path(library.library_dir / "one/two/bar.md"),
),
(ShowFilepathOption.SHOW_RELATIVE_PATHS, lambda library: pathlib.Path("one/two/bar.md")),
(ShowFilepathOption.SHOW_FILENAMES_ONLY, lambda library: pathlib.Path("bar.md")),
(ShowFilepathOption.SHOW_RELATIVE_PATHS, lambda _: Path("one/two/bar.md")),
(ShowFilepathOption.SHOW_FILENAMES_ONLY, lambda _: Path("bar.md")),
],
)
def test_file_path_display(qt_driver, library, filepath_option, expected_path):
def test_file_path_display(
qt_driver: QtDriver, library: Library, filepath_option: ShowFilepathOption, expected_path
):
panel = PreviewPanel(library, qt_driver)
# Select 2
qt_driver.toggle_item_selection(2, append=False, bridge=False)
panel.update_widgets()
with patch.object(qt_driver.settings, "value", return_value=filepath_option):
# Apply the mock value
filename = library.get_entry(2).path
panel.file_attrs.update_stats(filepath=pathlib.Path(library.library_dir / filename))
qt_driver.settings.show_filepath = filepath_option
# Generate the expected file string.
# This is copied directly from the file_attributes.py file
# can be imported as a function in the future
display_path = expected_path(library)
file_str: str = ""
separator: str = f"<a style='color: #777777'><b>{os.path.sep}</a>" # Gray
for i, part in enumerate(display_path.parts):
part_ = part.strip(os.path.sep)
if i != len(display_path.parts) - 1:
file_str += f"{"\u200b".join(part_)}{separator}</b>"
else:
if file_str != "":
file_str += "<br>"
file_str += f"<b>{"\u200b".join(part_)}</b>"
# Apply the mock value
entry = library.get_entry(2)
assert isinstance(entry, Entry)
filename = entry.path
assert library.library_dir is not None
panel.file_attrs.update_stats(filepath=library.library_dir / filename)
# Assert the file path is displayed correctly
assert panel.file_attrs.file_label.text() == file_str
# Generate the expected file string.
# This is copied directly from the file_attributes.py file
# can be imported as a function in the future
display_path = expected_path(library)
file_str: str = ""
separator: str = f"<a style='color: #777777'><b>{os.path.sep}</a>" # Gray
for i, part in enumerate(display_path.parts):
part_ = part.strip(os.path.sep)
if i != len(display_path.parts) - 1:
file_str += f"{"\u200b".join(part_)}{separator}</b>"
else:
if file_str != "":
file_str += "<br>"
file_str += f"<b>{"\u200b".join(part_)}</b>"
# Assert the file path is displayed correctly
assert panel.file_attrs.file_label.text() == file_str
@pytest.mark.parametrize(
@@ -97,9 +106,9 @@ def test_file_path_display(qt_driver, library, filepath_option, expected_path):
),
],
)
def test_title_update(qtbot, qt_driver, filepath_option, expected_title):
def test_title_update(qt_driver: QtDriver, filepath_option: ShowFilepathOption, expected_title):
base_title = qt_driver.base_title
test_path = pathlib.Path("/dev/null")
test_path = Path("/dev/null")
open_status = LibraryStatus(
success=True,
library_path=test_path,
@@ -107,7 +116,7 @@ def test_title_update(qtbot, qt_driver, filepath_option, expected_title):
msg_description="",
)
# Set the file path option
qt_driver.settings.setValue(SettingItems.SHOW_FILEPATH, filepath_option)
qt_driver.settings.show_filepath = filepath_option
menu_bar = QMenuBar()
qt_driver.open_recent_library_menu = QMenu(menu_bar)
@@ -124,7 +133,7 @@ def test_title_update(qtbot, qt_driver, filepath_option, expected_title):
qt_driver.folders_to_tags_action = QAction(menu_bar)
# Trigger the update
qt_driver.init_library(pathlib.Path(test_path), open_status)
qt_driver.init_library(test_path, open_status)
# Assert the title is updated correctly
qt_driver.main_window.setWindowTitle.assert_called_with(expected_title(test_path, base_title))

View File

@@ -0,0 +1,28 @@
from pathlib import Path
from tempfile import TemporaryDirectory
from tagstudio.core.global_settings import GlobalSettings, Theme
def test_read_settings():
with TemporaryDirectory() as tmp_dir:
settings_path = Path(tmp_dir) / "settings.toml"
with open(settings_path, "a") as settings_file:
settings_file.write("""
language = "de"
open_last_loaded_on_startup = true
autoplay = true
show_filenames_in_grid = true
page_size = 1337
show_filepath = 0
dark_mode = 2
""")
settings = GlobalSettings.read_settings(settings_path)
assert settings.language == "de"
assert settings.open_last_loaded_on_startup
assert settings.autoplay
assert settings.show_filenames_in_grid
assert settings.page_size == 1337
assert settings.show_filepath == 0
assert settings.theme == Theme.SYSTEM

View File

@@ -1,7 +1,12 @@
from typing import TYPE_CHECKING
from tagstudio.core.library.alchemy.enums import FilterState
from tagstudio.core.library.json.library import ItemType
from tagstudio.qt.widgets.item_thumb import ItemThumb
if TYPE_CHECKING:
from tagstudio.qt.ts_qt import QtDriver
# def test_update_thumbs(qt_driver):
# qt_driver.frame_content = [
# Entry(
@@ -61,7 +66,7 @@ from tagstudio.qt.widgets.item_thumb import ItemThumb
# assert qt_driver.selected == [0, 1, 2]
def test_library_state_update(qt_driver):
def test_library_state_update(qt_driver: "QtDriver"):
# Given
for entry in qt_driver.lib.get_entries(with_joins=True):
thumb = ItemThumb(ItemType.ENTRY, qt_driver.lib, qt_driver, (100, 100))
@@ -73,7 +78,7 @@ def test_library_state_update(qt_driver):
assert len(qt_driver.frame_content) == 2
# filter by tag
state = FilterState.from_tag_name("foo").with_page_size(10)
state = FilterState.from_tag_name("foo", page_size=10)
qt_driver.filter_items(state)
assert qt_driver.filter.page_size == 10
assert len(qt_driver.frame_content) == 1
@@ -88,7 +93,7 @@ def test_library_state_update(qt_driver):
assert list(entry.tags)[0].name == "foo"
# When state property is changed, previous one is overwritten
state = FilterState.from_path("*bar.md")
state = FilterState.from_path("*bar.md", page_size=qt_driver.settings.page_size)
qt_driver.filter_items(state)
assert len(qt_driver.frame_content) == 1
entry = qt_driver.lib.get_entry_full(qt_driver.frame_content[0])

View File

@@ -7,18 +7,19 @@ from PySide6.QtCore import QSettings
from tagstudio.core.constants import TS_FOLDER_NAME
from tagstudio.core.driver import DriverMixin
from tagstudio.core.enums import SettingItems
from tagstudio.core.global_settings import GlobalSettings
from tagstudio.core.library.alchemy.library import LibraryStatus
class TestDriver(DriverMixin):
def __init__(self, settings):
def __init__(self, settings: GlobalSettings, cache: QSettings):
self.settings = settings
self.cached_values = cache
def test_evaluate_path_empty():
# Given
settings = QSettings()
driver = TestDriver(settings)
driver = TestDriver(GlobalSettings(), QSettings())
# When
result = driver.evaluate_path(None)
@@ -29,8 +30,7 @@ def test_evaluate_path_empty():
def test_evaluate_path_missing():
# Given
settings = QSettings()
driver = TestDriver(settings)
driver = TestDriver(GlobalSettings(), QSettings())
# When
result = driver.evaluate_path("/0/4/5/1/")
@@ -41,9 +41,9 @@ def test_evaluate_path_missing():
def test_evaluate_path_last_lib_not_exists():
# Given
settings = QSettings()
settings.setValue(SettingItems.LAST_LIBRARY, "/0/4/5/1/")
driver = TestDriver(settings)
cache = QSettings()
cache.setValue(SettingItems.LAST_LIBRARY, "/0/4/5/1/")
driver = TestDriver(GlobalSettings(), cache)
# When
result = driver.evaluate_path(None)
@@ -55,13 +55,16 @@ def test_evaluate_path_last_lib_not_exists():
def test_evaluate_path_last_lib_present():
# Given
with TemporaryDirectory() as tmpdir:
settings_file = tmpdir + "/test_settings.ini"
settings = QSettings(settings_file, QSettings.Format.IniFormat)
settings.setValue(SettingItems.LAST_LIBRARY, tmpdir)
settings.sync()
cache_file = tmpdir + "/test_settings.ini"
cache = QSettings(cache_file, QSettings.Format.IniFormat)
cache.setValue(SettingItems.LAST_LIBRARY, tmpdir)
cache.sync()
settings = GlobalSettings()
settings.open_last_loaded_on_startup = True
makedirs(Path(tmpdir) / TS_FOLDER_NAME)
driver = TestDriver(settings)
driver = TestDriver(settings, cache)
# When
result = driver.evaluate_path(None)

View File

@@ -10,7 +10,7 @@ from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry, Tag
def test_library_add_alias(library, generate_tag):
def test_library_add_alias(library: Library, generate_tag):
tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
@@ -19,50 +19,64 @@ def test_library_add_alias(library, generate_tag):
alias_names: set[str] = set()
alias_names.add("test_alias")
library.update_tag(tag, parent_ids, alias_names, alias_ids)
alias_ids = library.get_tag(tag.id).alias_ids
tag = library.get_tag(tag.id)
assert tag is not None
alias_ids = set(tag.alias_ids)
assert len(alias_ids) == 1
def test_library_get_alias(library, generate_tag):
def test_library_get_alias(library: Library, generate_tag):
tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
parent_ids: set[int] = set()
alias_ids: set[int] = set()
alias_ids: list[int] = []
alias_names: set[str] = set()
alias_names.add("test_alias")
library.update_tag(tag, parent_ids, alias_names, alias_ids)
alias_ids = library.get_tag(tag.id).alias_ids
tag = library.get_tag(tag.id)
assert tag is not None
alias_ids = tag.alias_ids
assert library.get_alias(tag.id, alias_ids[0]).name == "test_alias"
alias = library.get_alias(tag.id, alias_ids[0])
assert alias is not None
assert alias.name == "test_alias"
def test_library_update_alias(library, generate_tag):
tag: Tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
def test_library_update_alias(library: Library, generate_tag):
tag: Tag | None = library.add_tag(generate_tag("xxx", id=123))
assert tag is not None
parent_ids: set[int] = set()
alias_ids: set[int] = set()
alias_ids: list[int] = []
alias_names: set[str] = set()
alias_names.add("test_alias")
library.update_tag(tag, parent_ids, alias_names, alias_ids)
alias_ids = library.get_tag(tag.id).alias_ids
tag = library.get_tag(tag.id)
assert tag is not None
alias_ids = tag.alias_ids
assert library.get_alias(tag.id, alias_ids[0]).name == "test_alias"
alias = library.get_alias(tag.id, alias_ids[0])
assert alias is not None
assert alias.name == "test_alias"
alias_names.remove("test_alias")
alias_names.add("alias_update")
library.update_tag(tag, parent_ids, alias_names, alias_ids)
tag = library.get_tag(tag.id)
assert tag is not None
assert len(tag.alias_ids) == 1
assert library.get_alias(tag.id, tag.alias_ids[0]).name == "alias_update"
alias = library.get_alias(tag.id, tag.alias_ids[0])
assert alias is not None
assert alias.name == "alias_update"
@pytest.mark.parametrize("library", [TemporaryDirectory()], indirect=True)
def test_library_add_file(library):
def test_library_add_file(library: Library):
"""Check Entry.path handling for insert vs lookup"""
assert library.folder is not None
entry = Entry(
path=Path("bar.txt"),
@@ -75,7 +89,7 @@ def test_library_add_file(library):
assert library.has_path_entry(entry.path)
def test_create_tag(library, generate_tag):
def test_create_tag(library: Library, generate_tag):
# tag already exists
assert not library.add_tag(generate_tag("foo", id=1000))
@@ -85,10 +99,11 @@ def test_create_tag(library, generate_tag):
assert tag.id == 123
tag_inc = library.add_tag(generate_tag("yyy"))
assert tag_inc is not None
assert tag_inc.id > 1000
def test_tag_self_parent(library, generate_tag):
def test_tag_self_parent(library: Library, generate_tag):
# tag already exists
assert not library.add_tag(generate_tag("foo", id=1000))
@@ -97,24 +112,25 @@ def test_tag_self_parent(library, generate_tag):
assert tag
assert tag.id == 123
library.update_tag(tag, {tag.id}, {}, {})
library.update_tag(tag, {tag.id}, [], [])
tag = library.get_tag(tag.id)
assert tag is not None
assert len(tag.parent_ids) == 0
def test_library_search(library, generate_tag, entry_full):
def test_library_search(library: Library, generate_tag, entry_full):
assert library.entries_count == 2
tag = list(entry_full.tags)[0]
results = library.search_library(
FilterState.from_tag_name(tag.name),
FilterState.from_tag_name(tag.name, page_size=500),
)
assert results.total_count == 1
assert len(results) == 1
def test_tag_search(library):
def test_tag_search(library: Library):
tag = library.tags[0]
assert library.search_tags(tag.name.lower())
@@ -130,24 +146,26 @@ def test_get_entry(library: Library, entry_min):
assert len(result.tags) == 1
def test_entries_count(library):
def test_entries_count(library: Library):
assert library.folder is not None
entries = [Entry(path=Path(f"{x}.txt"), folder=library.folder, fields=[]) for x in range(10)]
new_ids = library.add_entries(entries)
assert len(new_ids) == 10
results = library.search_library(FilterState.show_all().with_page_size(5))
results = library.search_library(FilterState.show_all(page_size=5))
assert results.total_count == 12
assert len(results) == 5
def test_parents_add(library, generate_tag):
def test_parents_add(library: Library, generate_tag):
# Given
tag: Tag = library.tags[0]
tag: Tag | None = library.tags[0]
assert tag.id is not None
parent_tag = generate_tag("parent_tag_01")
parent_tag = library.add_tag(parent_tag)
assert parent_tag is not None
assert parent_tag.id is not None
# When
@@ -156,10 +174,11 @@ def test_parents_add(library, generate_tag):
# Then
assert tag.id is not None
tag = library.get_tag(tag.id)
assert tag is not None
assert tag.parent_ids
def test_remove_tag(library, generate_tag):
def test_remove_tag(library: Library, generate_tag):
tag = library.add_tag(generate_tag("food", id=123))
assert tag
@@ -171,7 +190,7 @@ def test_remove_tag(library, generate_tag):
@pytest.mark.parametrize("is_exclude", [True, False])
def test_search_filter_extensions(library, is_exclude):
def test_search_filter_extensions(library: Library, is_exclude: bool):
# Given
entries = list(library.get_entries())
assert len(entries) == 2, entries
@@ -181,7 +200,7 @@ def test_search_filter_extensions(library, is_exclude):
# When
results = library.search_library(
FilterState.show_all(),
FilterState.show_all(page_size=500),
)
# Then
@@ -192,7 +211,7 @@ def test_search_filter_extensions(library, is_exclude):
assert (entry.path.suffix == ".txt") == is_exclude
def test_search_library_case_insensitive(library):
def test_search_library_case_insensitive(library: Library):
# Given
entries = list(library.get_entries(with_joins=True))
assert len(entries) == 2, entries
@@ -202,7 +221,7 @@ def test_search_library_case_insensitive(library):
# When
results = library.search_library(
FilterState.from_tag_name(tag.name.upper()),
FilterState.from_tag_name(tag.name.upper(), page_size=500),
)
# Then
@@ -212,12 +231,12 @@ def test_search_library_case_insensitive(library):
assert results[0].id == entry.id
def test_preferences(library):
def test_preferences(library: Library):
for pref in LibraryPrefs:
assert library.prefs(pref) == pref.default
def test_remove_entry_field(library, entry_full):
def test_remove_entry_field(library: Library, entry_full):
title_field = entry_full.text_fields[0]
library.remove_entry_field(title_field, [entry_full.id])
@@ -226,7 +245,7 @@ def test_remove_entry_field(library, entry_full):
assert not entry.text_fields
def test_remove_field_entry_with_multiple_field(library, entry_full):
def test_remove_field_entry_with_multiple_field(library: Library, entry_full):
# Given
title_field = entry_full.text_fields[0]
@@ -242,7 +261,7 @@ def test_remove_field_entry_with_multiple_field(library, entry_full):
assert len(entry.text_fields) == 1
def test_update_entry_field(library, entry_full):
def test_update_entry_field(library: Library, entry_full):
title_field = entry_full.text_fields[0]
library.update_entry_field(
@@ -255,7 +274,7 @@ def test_update_entry_field(library, entry_full):
assert entry.text_fields[0].value == "new value"
def test_update_entry_with_multiple_identical_fields(library, entry_full):
def test_update_entry_with_multiple_identical_fields(library: Library, entry_full):
# Given
title_field = entry_full.text_fields[0]
@@ -278,6 +297,7 @@ def test_update_entry_with_multiple_identical_fields(library, entry_full):
def test_mirror_entry_fields(library: Library, entry_full):
# new entry
assert library.folder is not None
target_entry = Entry(
folder=library.folder,
path=Path("xxx"),
@@ -295,12 +315,14 @@ def test_mirror_entry_fields(library: Library, entry_full):
# get new entry from library
new_entry = library.get_entry_full(entry_id)
assert new_entry is not None
# mirror fields onto new entry
library.mirror_entry_fields(new_entry, entry_full)
# get new entry from library again
entry = library.get_entry_full(entry_id)
assert entry is not None
# make sure fields are there after getting it from the library again
assert len(entry.fields) == 2
@@ -311,6 +333,7 @@ def test_mirror_entry_fields(library: Library, entry_full):
def test_merge_entries(library: Library):
assert library.folder is not None
a = Entry(
folder=library.folder,
path=Path("a"),
@@ -327,10 +350,14 @@ def test_merge_entries(library: Library):
try:
ids = library.add_entries([a, b])
entry_a = library.get_entry_full(ids[0])
assert entry_a is not None
entry_b = library.get_entry_full(ids[1])
assert entry_b is not None
tag_0 = library.add_tag(Tag(id=1000, name="tag_0"))
tag_1 = library.add_tag(Tag(id=1001, name="tag_1"))
assert tag_1 is not None
tag_2 = library.add_tag(Tag(id=1002, name="tag_2"))
assert tag_2 is not None
library.add_tags_to_entries(ids[0], [tag_0.id, tag_2.id])
library.add_tags_to_entries(ids[1], [tag_1.id])
library.merge_entries(entry_a, entry_b)
@@ -345,7 +372,7 @@ def test_merge_entries(library: Library):
AssertionError()
def test_remove_tags_from_entries(library, entry_full):
def test_remove_tags_from_entries(library: Library, entry_full):
removed_tag_id = -1
for tag in entry_full.tags:
removed_tag_id = tag.id
@@ -370,7 +397,7 @@ def test_search_entry_id(library: Library, query_name: int, has_result):
assert (result is not None) == has_result
def test_update_field_order(library, entry_full):
def test_update_field_order(library: Library, entry_full):
# Given
title_field = entry_full.text_fields[0]
@@ -416,98 +443,100 @@ def test_library_prefs_multiple_identical_vals():
def test_path_search_ilike(library: Library):
results = library.search_library(FilterState.from_path("bar.md"))
results = library.search_library(FilterState.from_path("bar.md", page_size=500))
assert results.total_count == 1
assert len(results.items) == 1
def test_path_search_like(library: Library):
results = library.search_library(FilterState.from_path("BAR.MD"))
results = library.search_library(FilterState.from_path("BAR.MD", page_size=500))
assert results.total_count == 0
assert len(results.items) == 0
def test_path_search_default_with_sep(library: Library):
results = library.search_library(FilterState.from_path("one/two"))
results = library.search_library(FilterState.from_path("one/two", page_size=500))
assert results.total_count == 1
assert len(results.items) == 1
def test_path_search_glob_after(library: Library):
results = library.search_library(FilterState.from_path("foo*"))
results = library.search_library(FilterState.from_path("foo*", page_size=500))
assert results.total_count == 1
assert len(results.items) == 1
def test_path_search_glob_in_front(library: Library):
results = library.search_library(FilterState.from_path("*bar.md"))
results = library.search_library(FilterState.from_path("*bar.md", page_size=500))
assert results.total_count == 1
assert len(results.items) == 1
def test_path_search_glob_both_sides(library: Library):
results = library.search_library(FilterState.from_path("*one/two*"))
results = library.search_library(FilterState.from_path("*one/two*", page_size=500))
assert results.total_count == 1
assert len(results.items) == 1
def test_path_search_ilike_glob_equality(library: Library):
results_ilike = library.search_library(FilterState.from_path("one/two"))
results_glob = library.search_library(FilterState.from_path("*one/two*"))
results_ilike = library.search_library(FilterState.from_path("one/two", page_size=500))
results_glob = library.search_library(FilterState.from_path("*one/two*", page_size=500))
assert [e.id for e in results_ilike.items] == [e.id for e in results_glob.items]
results_ilike, results_glob = None, None
results_ilike = library.search_library(FilterState.from_path("bar.md"))
results_glob = library.search_library(FilterState.from_path("*bar.md*"))
results_ilike = library.search_library(FilterState.from_path("bar.md", page_size=500))
results_glob = library.search_library(FilterState.from_path("*bar.md*", page_size=500))
assert [e.id for e in results_ilike.items] == [e.id for e in results_glob.items]
results_ilike, results_glob = None, None
results_ilike = library.search_library(FilterState.from_path("bar"))
results_glob = library.search_library(FilterState.from_path("*bar*"))
results_ilike = library.search_library(FilterState.from_path("bar", page_size=500))
results_glob = library.search_library(FilterState.from_path("*bar*", page_size=500))
assert [e.id for e in results_ilike.items] == [e.id for e in results_glob.items]
results_ilike, results_glob = None, None
results_ilike = library.search_library(FilterState.from_path("bar.md"))
results_glob = library.search_library(FilterState.from_path("*bar.md*"))
results_ilike = library.search_library(FilterState.from_path("bar.md", page_size=500))
results_glob = library.search_library(FilterState.from_path("*bar.md*", page_size=500))
assert [e.id for e in results_ilike.items] == [e.id for e in results_glob.items]
results_ilike, results_glob = None, None
def test_path_search_like_glob_equality(library: Library):
results_ilike = library.search_library(FilterState.from_path("ONE/two"))
results_glob = library.search_library(FilterState.from_path("*ONE/two*"))
results_ilike = library.search_library(FilterState.from_path("ONE/two", page_size=500))
results_glob = library.search_library(FilterState.from_path("*ONE/two*", page_size=500))
assert [e.id for e in results_ilike.items] == [e.id for e in results_glob.items]
results_ilike, results_glob = None, None
results_ilike = library.search_library(FilterState.from_path("BAR.MD"))
results_glob = library.search_library(FilterState.from_path("*BAR.MD*"))
results_ilike = library.search_library(FilterState.from_path("BAR.MD", page_size=500))
results_glob = library.search_library(FilterState.from_path("*BAR.MD*", page_size=500))
assert [e.id for e in results_ilike.items] == [e.id for e in results_glob.items]
results_ilike, results_glob = None, None
results_ilike = library.search_library(FilterState.from_path("BAR.MD"))
results_glob = library.search_library(FilterState.from_path("*bar.md*"))
results_ilike = library.search_library(FilterState.from_path("BAR.MD", page_size=500))
results_glob = library.search_library(FilterState.from_path("*bar.md*", page_size=500))
assert [e.id for e in results_ilike.items] != [e.id for e in results_glob.items]
results_ilike, results_glob = None, None
results_ilike = library.search_library(FilterState.from_path("bar.md"))
results_glob = library.search_library(FilterState.from_path("*BAR.MD*"))
results_ilike = library.search_library(FilterState.from_path("bar.md", page_size=500))
results_glob = library.search_library(FilterState.from_path("*BAR.MD*", page_size=500))
assert [e.id for e in results_ilike.items] != [e.id for e in results_glob.items]
results_ilike, results_glob = None, None
@pytest.mark.parametrize(["filetype", "num_of_filetype"], [("md", 1), ("txt", 1), ("png", 0)])
def test_filetype_search(library, filetype, num_of_filetype):
results = library.search_library(FilterState.from_filetype(filetype))
def test_filetype_search(library: Library, filetype, num_of_filetype):
results = library.search_library(FilterState.from_filetype(filetype, page_size=500))
assert len(results.items) == num_of_filetype
@pytest.mark.parametrize(["filetype", "num_of_filetype"], [("png", 2), ("apng", 1), ("ng", 0)])
def test_filetype_return_one_filetype(file_mediatypes_library, filetype, num_of_filetype):
results = file_mediatypes_library.search_library(FilterState.from_filetype(filetype))
def test_filetype_return_one_filetype(file_mediatypes_library: Library, filetype, num_of_filetype):
results = file_mediatypes_library.search_library(
FilterState.from_filetype(filetype, page_size=500)
)
assert len(results.items) == num_of_filetype
@pytest.mark.parametrize(["mediatype", "num_of_mediatype"], [("plaintext", 2), ("image", 0)])
def test_mediatype_search(library, mediatype, num_of_mediatype):
results = library.search_library(FilterState.from_mediatype(mediatype))
def test_mediatype_search(library: Library, mediatype, num_of_mediatype):
results = library.search_library(FilterState.from_mediatype(mediatype, page_size=500))
assert len(results.items) == num_of_mediatype

View File

@@ -6,7 +6,7 @@ from tagstudio.core.query_lang.util import ParsingError
def verify_count(lib: Library, query: str, count: int):
results = lib.search_library(FilterState.from_search_query(query))
results = lib.search_library(FilterState.from_search_query(query, page_size=500))
assert results.total_count == count
assert len(results.items) == count
@@ -136,4 +136,4 @@ def test_parent_tags(search_library: Library, query: str, count: int):
)
def test_syntax(search_library: Library, invalid_query: str):
with pytest.raises(ParsingError) as e_info: # noqa: F841
search_library.search_library(FilterState.from_search_query(invalid_query))
search_library.search_library(FilterState.from_search_query(invalid_query, page_size=500))