mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-01-28 22:01:24 +00:00
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:
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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))
|
||||
|
||||
28
tests/qt/test_global_settings.py
Normal file
28
tests/qt/test_global_settings.py
Normal 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
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user