From dca53b1f2dac717c74cdd439175306c13a7c9857 Mon Sep 17 00:00:00 2001 From: Bart Feenstra Date: Fri, 5 Jul 2024 18:27:55 +0100 Subject: [PATCH] FileBasedConfiguration must no longer create its own project directories --- betty/app.py | 22 +- betty/cli/__init__.py | 4 +- betty/config.py | 35 +- betty/extension/demo/__init__.py | 76 +-- betty/extension/nginx/serve.py | 6 +- betty/extension/webpack/__init__.py | 24 +- betty/gui/app.py | 16 +- betty/project/__init__.py | 32 +- betty/tests/__init__.py | 5 +- betty/tests/cli/test___init__.py | 63 +-- .../tests/extension/cotton_candy/test_gui.py | 352 +++++++------- .../extension/cotton_candy/test_search.py | 362 +++++++------- betty/tests/extension/demo/test___init__.py | 18 +- .../tests/extension/deriver/test___init__.py | 67 +-- betty/tests/extension/gramps/test___init__.py | 66 +-- betty/tests/extension/gramps/test_gui.py | 122 ++--- .../extension/http_api_doc/test___init__.py | 20 +- betty/tests/extension/maps/test___init__.py | 42 +- betty/tests/extension/nginx/test___init__.py | 32 +- betty/tests/extension/nginx/test_artifact.py | 192 ++++---- betty/tests/extension/nginx/test_cli.py | 22 +- betty/tests/extension/nginx/test_gui.py | 120 ++--- .../tests/extension/nginx/test_integration.py | 109 +++-- betty/tests/extension/nginx/test_serve.py | 20 +- .../extension/privatizer/test___init__.py | 18 +- betty/tests/extension/trees/test___init__.py | 42 +- .../tests/extension/webpack/test___init__.py | 63 +-- betty/tests/extension/webpack/test_build.py | 60 +-- .../extension/wikipedia/test___init__.py | 34 +- betty/tests/extension/wikipedia/test_gui.py | 18 +- betty/tests/gramps/test_loader.py | 27 +- betty/tests/gui/test_app.py | 5 +- betty/tests/gui/test_project.py | 272 ++++++----- betty/tests/gui/test_serve.py | 2 +- betty/tests/json/test_schema.py | 2 +- betty/tests/model/test_ancestry.py | 6 +- betty/tests/project/test___init__.py | 451 +++++++++--------- betty/tests/project/test_extension.py | 2 +- betty/tests/test_config.py | 6 +- betty/tests/test_documentation.py | 4 +- betty/tests/test_generate.py | 168 ++++--- betty/tests/test_jinja2.py | 3 +- betty/tests/test_load.py | 54 +-- betty/tests/test_openapi.py | 12 +- betty/tests/test_serve.py | 3 +- betty/tests/test_url.py | 55 +-- betty/tests/test_wikipedia.py | 154 +++--- 47 files changed, 1706 insertions(+), 1582 deletions(-) diff --git a/betty/app.py b/betty/app.py index 2ee251290..41581f783 100644 --- a/betty/app.py +++ b/betty/app.py @@ -32,7 +32,6 @@ from betty.locale import LocalizerRepository, get_data, DEFAULT_LOCALE, Localizer from betty.locale.localizable import _ from betty.serde.dump import minimize, void_none, Dump, VoidableDump -from betty.warnings import deprecate if TYPE_CHECKING: from betty.cache import Cache @@ -48,33 +47,22 @@ class AppConfiguration(FileBasedConfiguration): def __init__( self, - configuration_directory_path: Path | None = None, + configuration_file_path: Path, *, locale: str | None = None, ): - if configuration_directory_path is None: - deprecate( - f"Initializing {type(self)} without a configuration directory path is deprecated as of Betty 0.3.3, and will be removed in Betty 0.4.x.", - stacklevel=2, - ) - configuration_directory_path = CONFIGURATION_DIRECTORY_PATH - super().__init__() - self._configuration_directory_path = configuration_directory_path + super().__init__(configuration_file_path) self._locale: str | None = locale @override @property def configuration_file_path(self) -> Path: - return self._configuration_directory_path / "app.json" + return self._configuration_file_path @configuration_file_path.setter def configuration_file_path(self, __: Path) -> None: pass - @configuration_file_path.deleter - def configuration_file_path(self) -> None: - pass - @property def locale(self) -> str | None: """ @@ -148,7 +136,7 @@ async def new_from_environment(cls) -> AsyncIterator[Self]: Create a new application from the environment. """ yield cls( - AppConfiguration(CONFIGURATION_DIRECTORY_PATH), + AppConfiguration(CONFIGURATION_DIRECTORY_PATH / "app.json"), Path(environ.get("BETTY_CACHE_DIRECTORY", HOME_DIRECTORY_PATH / "cache")), cache_factory=lambda app: PickledFileCache[Any]( app.localizer, app._cache_directory_path @@ -169,7 +157,7 @@ async def new_temporary(cls) -> AsyncIterator[Self]: TemporaryDirectory() as cache_directory_path_str, ): yield cls( - AppConfiguration(Path(configuration_directory_path_str)), + AppConfiguration(Path(configuration_directory_path_str) / "app.json"), Path(cache_directory_path_str), cache_factory=lambda app: NoOpCache(), ) diff --git a/betty/cli/__init__.py b/betty/cli/__init__.py index c7e5ad11c..dea72b0f8 100644 --- a/betty/cli/__init__.py +++ b/betty/cli/__init__.py @@ -157,7 +157,9 @@ def _project( ) -> Project: _init_ctx_app(ctx) app = ctx.obj["app"] - project = Project(app) + project: Project = ctx.with_resource( # type: ignore[attr-defined] + SynchronizedContextManager(Project.new_temporary(app)) + ) wait_to_thread(_read_project_configuration(project, configuration_file_path)) ctx.with_resource( # type: ignore[attr-defined] SynchronizedContextManager(project) diff --git a/betty/config.py b/betty/config.py index 7d442ba11..16f955d53 100644 --- a/betty/config.py +++ b/betty/config.py @@ -9,9 +9,7 @@ from collections import OrderedDict from collections.abc import Callable from contextlib import chdir -from pathlib import Path from reprlib import recursive_repr -from tempfile import TemporaryDirectory from typing import ( Generic, Iterable, @@ -24,7 +22,6 @@ Any, Sequence, overload, - cast, Self, TypeAlias, TYPE_CHECKING, @@ -45,6 +42,7 @@ from betty.typing import Void if TYPE_CHECKING: + from pathlib import Path from _weakref import ReferenceType @@ -112,10 +110,9 @@ class FileBasedConfiguration(Configuration): Any configuration that is stored in a file on disk. """ - def __init__(self): + def __init__(self, configuration_file_path: Path): super().__init__() - self._configuration_directory: TemporaryDirectory | None = None # type: ignore[type-arg] - self._configuration_file_path: Path | None = None + self._configuration_file_path = configuration_file_path self._autowrite = False @property @@ -196,28 +193,12 @@ async def read(self, configuration_file_path: Path | None = None) -> None: ) ) - def __del__(self) -> None: - if ( - hasattr(self, "_configuration_directory") - and self._configuration_directory is not None - ): - self._configuration_directory.cleanup() - @property def configuration_file_path(self) -> Path: """ The path to the configuration's file. """ - if self._configuration_file_path is None: - if self._configuration_directory is None: - self._configuration_directory = TemporaryDirectory() - wait_to_thread( - self._write( - Path(self._configuration_directory.name) - / f"{type(self).__name__}.json" - ) - ) - return cast(Path, self._configuration_file_path) + return self._configuration_file_path @configuration_file_path.setter def configuration_file_path(self, configuration_file_path: Path) -> None: @@ -227,14 +208,6 @@ def configuration_file_path(self, configuration_file_path: Path) -> None: formats.format_for(configuration_file_path.suffix[1:]) self._configuration_file_path = configuration_file_path - @configuration_file_path.deleter - def configuration_file_path(self) -> None: - if self._autowrite: - raise RuntimeError( - "Cannot remove the configuration file path while autowrite is enabled." - ) - self._configuration_file_path = None - ConfigurationKey: TypeAlias = SupportsIndex | Hashable | type[Any] _ConfigurationKeyT = TypeVar("_ConfigurationKeyT", bound=ConfigurationKey) diff --git a/betty/extension/demo/__init__.py b/betty/extension/demo/__init__.py index 1eba3a85c..f69ff186f 100644 --- a/betty/extension/demo/__init__.py +++ b/betty/extension/demo/__init__.py @@ -512,43 +512,43 @@ async def demo_project(app: App) -> AsyncIterator[Project]: """ from betty.extension import CottonCandy - project = Project(app) - project.configuration.name = Demo.name() - project.configuration.extensions.append(ExtensionConfiguration(Demo)) - project.configuration.extensions.append( - ExtensionConfiguration( - CottonCandy, - extension_configuration=CottonCandyConfiguration( - featured_entities=[ - EntityReference(Place, "betty-demo-amsterdam"), - EntityReference(Person, "betty-demo-liberta-lankester"), - EntityReference(Place, "betty-demo-netherlands"), - ], + async with Project.new_temporary(app) as project: + project.configuration.name = Demo.name() + project.configuration.extensions.append(ExtensionConfiguration(Demo)) + project.configuration.extensions.append( + ExtensionConfiguration( + CottonCandy, + extension_configuration=CottonCandyConfiguration( + featured_entities=[ + EntityReference(Place, "betty-demo-amsterdam"), + EntityReference(Person, "betty-demo-liberta-lankester"), + EntityReference(Place, "betty-demo-netherlands"), + ], + ), + ) + ) + # Include all of the translations Betty ships with. + project.configuration.locales.replace( + LocaleConfiguration( + "en-US", + alias="en", + ), + LocaleConfiguration( + "nl-NL", + alias="nl", + ), + LocaleConfiguration( + "fr-FR", + alias="fr", + ), + LocaleConfiguration( + "uk", + alias="uk", + ), + LocaleConfiguration( + "de-DE", + alias="de", ), ) - ) - # Include all of the translations Betty ships with. - project.configuration.locales.replace( - LocaleConfiguration( - "en-US", - alias="en", - ), - LocaleConfiguration( - "nl-NL", - alias="nl", - ), - LocaleConfiguration( - "fr-FR", - alias="fr", - ), - LocaleConfiguration( - "uk", - alias="uk", - ), - LocaleConfiguration( - "de-DE", - alias="de", - ), - ) - async with project: - yield project + async with project: + yield project diff --git a/betty/extension/nginx/serve.py b/betty/extension/nginx/serve.py index 0f3da301d..7091e9eaf 100644 --- a/betty/extension/nginx/serve.py +++ b/betty/extension/nginx/serve.py @@ -42,8 +42,8 @@ async def start(self) -> None: TemporaryDirectory() # type: ignore[arg-type] ) - isolated_project = await self._exit_stack.enter_async_context( - Project(self._project.app, ancestry=self._project.ancestry) + isolated_project: Project = await self._exit_stack.enter_async_context( + Project.new_temporary(self._project.app, ancestry=self._project.ancestry) ) isolated_project.configuration.autowrite = False isolated_project.configuration.configuration_file_path = ( @@ -55,6 +55,8 @@ async def start(self) -> None: # Work around https://github.com/bartfeenstra/betty/issues/1056. isolated_project.extensions[Nginx].configuration.https = False + await self._exit_stack.enter_async_context(isolated_project) + nginx_configuration_file_path = Path(output_directory_path_str) / "nginx.conf" docker_directory_path = Path(output_directory_path_str) dockerfile_file_path = docker_directory_path / "Dockerfile" diff --git a/betty/extension/webpack/__init__.py b/betty/extension/webpack/__init__.py index eb21f993b..448ef4f07 100644 --- a/betty/extension/webpack/__init__.py +++ b/betty/extension/webpack/__init__.py @@ -54,18 +54,18 @@ async def _prebuild_webpack_assets() -> None: """ async with App.new_temporary() as app, app: job_context = Context() - project = Project(app) - project.configuration.extensions.enable(Webpack) - project.configuration.extensions.enable( - *{ - extension_type - for extension_type in discover_extension_types() - if issubclass(extension_type, WebpackEntryPointProvider) - } - ) - async with project: - webpack = project.extensions[Webpack] - await webpack.prebuild(job_context=job_context) + async with Project.new_temporary(app) as project: + project.configuration.extensions.enable(Webpack) + project.configuration.extensions.enable( + *{ + extension_type + for extension_type in discover_extension_types() + if issubclass(extension_type, WebpackEntryPointProvider) + } + ) + async with project: + webpack = project.extensions[Webpack] + await webpack.prebuild(job_context=job_context) class WebpackEntryPointProvider: diff --git a/betty/gui/app.py b/betty/gui/app.py index 60b0dac35..6e7a05c32 100644 --- a/betty/gui/app.py +++ b/betty/gui/app.py @@ -30,7 +30,7 @@ from betty.gui.text import Text from betty.gui.window import BettyMainWindow from betty.locale.localizable import plain, Localizable, _ -from betty.project import Project +from betty.project import Project, ProjectConfiguration class BettyPrimaryWindow(BettyMainWindow): @@ -214,10 +214,9 @@ def open_project(self) -> None: ) if not configuration_file_path_str: return - project = Project(self._app) - wait_to_thread( - project.configuration.read(Path(configuration_file_path_str)) - ) + configuration = ProjectConfiguration(Path(configuration_file_path_str)) + wait_to_thread(configuration.read()) + project = Project(self._app, configuration) project_window = ProjectWindow(project) project_window.show() self.close() @@ -237,10 +236,9 @@ def new_project(self) -> None: ) if not configuration_file_path_str: return - project = Project(self._app) - wait_to_thread( - project.configuration.write(Path(configuration_file_path_str)) - ) + configuration = ProjectConfiguration(Path(configuration_file_path_str)) + wait_to_thread(configuration.write()) + project = Project(self._app, configuration) project_window = ProjectWindow(project) project_window.show() self.close() diff --git a/betty/project/__init__.py b/betty/project/__init__.py index 57ed190e7..366c120be 100644 --- a/betty/project/__init__.py +++ b/betty/project/__init__.py @@ -9,13 +9,15 @@ from __future__ import annotations import operator -from contextlib import suppress +from contextlib import suppress, asynccontextmanager from functools import reduce from graphlib import TopologicalSorter, CycleError +from pathlib import Path from reprlib import recursive_repr from typing import Any, Generic, final, Iterable, cast, Self, TYPE_CHECKING, TypeVar from urllib.parse import urlparse +from aiofiles.tempfile import TemporaryDirectory from typing_extensions import override from betty import fs @@ -101,10 +103,10 @@ from betty.assertion.error import AssertionFailed if TYPE_CHECKING: + from collections.abc import AsyncIterator from betty.app import App from betty.dispatch import Dispatcher from betty.url import LocalizedUrlGenerator, StaticUrlGenerator - from pathlib import Path from betty.jinja2 import Environment @@ -773,6 +775,7 @@ class ProjectConfiguration(FileBasedConfiguration): def __init__( self, + configuration_file_path: Path, *, base_url: str | None = None, root_path: str = "", @@ -786,7 +789,7 @@ def __init__( lifetime_threshold: int = DEFAULT_LIFETIME_THRESHOLD, name: str | None = None, ): - super().__init__() + super().__init__(configuration_file_path) self._name = name self._computed_name: str | None = None self._base_url = "https://example.com" if base_url is None else base_url @@ -1078,12 +1081,13 @@ class Project(Configurable[ProjectConfiguration], CoreComponent): def __init__( self, app: App, + configuration: ProjectConfiguration, *, ancestry: Ancestry | None = None, ): super().__init__() self._app = app - self._configuration = ProjectConfiguration() + self._configuration = configuration self._ancestry = Ancestry() if ancestry is None else ancestry self._assets: AssetRepository | None = None @@ -1097,6 +1101,26 @@ def __init__( self._entity_types: set[type[Entity]] | None = None self._event_types: set[type[EventType]] | None = None + @classmethod + @asynccontextmanager + async def new_temporary( + cls, app: App, *, ancestry: Ancestry | None = None + ) -> AsyncIterator[Self]: + """ + Creat a new, temporary, isolated project. + + The project will not leave any traces on the system, except when it uses + global Betty functionality such as caches. + """ + async with ( + TemporaryDirectory() as project_directory_path_str, + ): + yield cls( + app, + ProjectConfiguration(Path(project_directory_path_str) / "betty.json"), + ancestry=ancestry, + ) + @property def app(self) -> App: """ diff --git a/betty/tests/__init__.py b/betty/tests/__init__.py index 4c5048a18..c0b5e2bee 100644 --- a/betty/tests/__init__.py +++ b/betty/tests/__init__.py @@ -68,8 +68,9 @@ async def _render( raise RuntimeError( f"You must define one of `template_string`, `template_file`, `{class_name}.template_string`, or `{class_name}.template_file`." ) - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.debug = True if data is None: data = {} diff --git a/betty/tests/cli/test___init__.py b/betty/tests/cli/test___init__.py index 0434945a6..5eabf7681 100644 --- a/betty/tests/cli/test___init__.py +++ b/betty/tests/cli/test___init__.py @@ -148,40 +148,43 @@ async def test(self, mocker: MockerFixture, new_temporary_app: App) -> None: m_generate = mocker.patch("betty.generate.generate", new_callable=AsyncMock) m_load = mocker.patch("betty.load.load", new_callable=AsyncMock) - project = Project(new_temporary_app) - await project.configuration.write() - await to_thread( - run, "generate", "-c", str(project.configuration.configuration_file_path) - ) - - m_load.assert_called_once() - await_args = m_load.await_args - assert await_args is not None - load_args, _ = await_args - assert ( - load_args[0].configuration.configuration_file_path - == project.configuration.configuration_file_path - ) - - m_generate.assert_called_once() - generate_args, _ = m_generate.call_args - assert ( - generate_args[0].configuration.configuration_file_path - == project.configuration.configuration_file_path - ) + async with Project.new_temporary(new_temporary_app) as project: + await project.configuration.write() + await to_thread( + run, + "generate", + "-c", + str(project.configuration.configuration_file_path), + ) + + m_load.assert_called_once() + await_args = m_load.await_args + assert await_args is not None + load_args, _ = await_args + assert ( + load_args[0].configuration.configuration_file_path + == project.configuration.configuration_file_path + ) + + m_generate.assert_called_once() + generate_args, _ = m_generate.call_args + assert ( + generate_args[0].configuration.configuration_file_path + == project.configuration.configuration_file_path + ) class TestServe: async def test(self, mocker: MockerFixture, new_temporary_app: App) -> None: mocker.patch("asyncio.sleep", side_effect=KeyboardInterrupt) mocker.patch("betty.serve.BuiltinProjectServer", new=NoOpProjectServer) - project = Project(new_temporary_app) - await project.configuration.write() - await makedirs(project.configuration.www_directory_path) + async with Project.new_temporary(new_temporary_app) as project: + await project.configuration.write() + await makedirs(project.configuration.www_directory_path) - await to_thread( - run, "serve", "-c", str(project.configuration.configuration_file_path) - ) + await to_thread( + run, "serve", "-c", str(project.configuration.configuration_file_path) + ) class TestGui: @@ -263,6 +266,6 @@ async def test( "betty.cli._discover.discover_commands", return_value={"no-op": _no_op_command}, ) - project = Project(new_temporary_app) - await project.configuration.write() - await to_thread(run, verbosity, "no-op") + async with Project.new_temporary(new_temporary_app) as project: + await project.configuration.write() + await to_thread(run, verbosity, "no-op") diff --git a/betty/tests/extension/cotton_candy/test_gui.py b/betty/tests/extension/cotton_candy/test_gui.py index e7121fcc3..84c0853db 100644 --- a/betty/tests/extension/cotton_candy/test_gui.py +++ b/betty/tests/extension/cotton_candy/test_gui.py @@ -59,198 +59,220 @@ def entity_type_label_plural(cls) -> Localizable: class TestCottonCandyGuiWidget: async def test_add_featured_entities(self, betty_qtbot: BettyQtBot) -> None: - project = Project(betty_qtbot.app) - project.configuration.extensions.enable(CottonCandy) - async with project: - sut = _CottonCandyGuiWidget(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with Project.new_temporary(betty_qtbot.app) as project: + project.configuration.extensions.enable(CottonCandy) + async with project: + sut = _CottonCandyGuiWidget(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - entity_id = "123" - betty_qtbot.mouse_click( - sut._featured_entities_entity_references_collector._add_entity_reference_button - ) - # @todo Find out an elegant way to test changing the entity type. - sut._featured_entities_entity_references_collector._entity_reference_collectors[ - 0 - ]._entity_id.setText(entity_id) - assert ( - project.extensions[CottonCandy] - .configuration.featured_entities[0] - .entity_id - == entity_id - ) + entity_id = "123" + betty_qtbot.mouse_click( + sut._featured_entities_entity_references_collector._add_entity_reference_button + ) + # @todo Find out an elegant way to test changing the entity type. + sut._featured_entities_entity_references_collector._entity_reference_collectors[ + 0 + ]._entity_id.setText(entity_id) + assert ( + project.extensions[CottonCandy] + .configuration.featured_entities[0] + .entity_id + == entity_id + ) async def test_change_featured_entities(self, betty_qtbot: BettyQtBot) -> None: - project = Project(betty_qtbot.app) - project.configuration.extensions.enable(CottonCandy) - entity_reference_1 = EntityReference(CottonCandyGuiWidgetTestEntity, "123") - entity_reference_2 = EntityReference(CottonCandyGuiWidgetTestEntity, "456") - entity_reference_3 = EntityReference(CottonCandyGuiWidgetTestEntity, "789") - project.extensions[CottonCandy].configuration.featured_entities.append( - entity_reference_1, # type: ignore[arg-type] - ) - project.extensions[CottonCandy].configuration.featured_entities.append( - entity_reference_2, # type: ignore[arg-type] - ) - project.extensions[CottonCandy].configuration.featured_entities.append( - entity_reference_3, # type: ignore[arg-type] - ) - async with project: - sut = _CottonCandyGuiWidget(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() - - entity_id = "123" - # @todo Find out an elegant way to test changing the entity type. - sut._featured_entities_entity_references_collector._entity_reference_collectors[ - 1 - ]._entity_id.setText(entity_id) - assert ( - project.extensions[CottonCandy] - .configuration.featured_entities[1] - .entity_id - == entity_id + async with Project.new_temporary(betty_qtbot.app) as project: + project.configuration.extensions.enable(CottonCandy) + entity_reference_1 = EntityReference(CottonCandyGuiWidgetTestEntity, "123") + entity_reference_2 = EntityReference(CottonCandyGuiWidgetTestEntity, "456") + entity_reference_3 = EntityReference(CottonCandyGuiWidgetTestEntity, "789") + project.extensions[CottonCandy].configuration.featured_entities.append( + entity_reference_1, # type: ignore[arg-type] ) + project.extensions[CottonCandy].configuration.featured_entities.append( + entity_reference_2, # type: ignore[arg-type] + ) + project.extensions[CottonCandy].configuration.featured_entities.append( + entity_reference_3, # type: ignore[arg-type] + ) + async with project: + sut = _CottonCandyGuiWidget(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - async def test_remove_featured_entities(self, betty_qtbot: BettyQtBot) -> None: - project = Project(betty_qtbot.app) - project.configuration.extensions.enable(CottonCandy) - entity_reference_1 = EntityReference[CottonCandyGuiWidgetTestEntity]( - CottonCandyGuiWidgetTestEntity, "123" - ) - entity_reference_2 = EntityReference[CottonCandyGuiWidgetTestEntity]( - CottonCandyGuiWidgetTestEntity, "456" - ) - entity_reference_3 = EntityReference[CottonCandyGuiWidgetTestEntity]( - CottonCandyGuiWidgetTestEntity, "789" - ) - project.extensions[CottonCandy].configuration.featured_entities.append( - entity_reference_1, # type: ignore[arg-type] - ) - project.extensions[CottonCandy].configuration.featured_entities.append( - entity_reference_2, # type: ignore[arg-type] - ) - project.extensions[CottonCandy].configuration.featured_entities.append( - entity_reference_3, # type: ignore[arg-type] - ) - async with project: - sut = _CottonCandyGuiWidget(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() - - betty_qtbot.mouse_click( - sut._featured_entities_entity_references_collector._entity_reference_remove_buttons[ + entity_id = "123" + # @todo Find out an elegant way to test changing the entity type. + sut._featured_entities_entity_references_collector._entity_reference_collectors[ 1 - ] + ]._entity_id.setText(entity_id) + assert ( + project.extensions[CottonCandy] + .configuration.featured_entities[1] + .entity_id + == entity_id + ) + + async def test_remove_featured_entities(self, betty_qtbot: BettyQtBot) -> None: + async with Project.new_temporary(betty_qtbot.app) as project: + project.configuration.extensions.enable(CottonCandy) + entity_reference_1 = EntityReference[CottonCandyGuiWidgetTestEntity]( + CottonCandyGuiWidgetTestEntity, "123" + ) + entity_reference_2 = EntityReference[CottonCandyGuiWidgetTestEntity]( + CottonCandyGuiWidgetTestEntity, "456" + ) + entity_reference_3 = EntityReference[CottonCandyGuiWidgetTestEntity]( + CottonCandyGuiWidgetTestEntity, "789" ) - assert ( - entity_reference_1 - in project.extensions[CottonCandy].configuration.featured_entities + project.extensions[CottonCandy].configuration.featured_entities.append( + entity_reference_1, # type: ignore[arg-type] ) - assert ( - entity_reference_2 - not in project.extensions[CottonCandy].configuration.featured_entities + project.extensions[CottonCandy].configuration.featured_entities.append( + entity_reference_2, # type: ignore[arg-type] ) - assert ( - entity_reference_3 - in project.extensions[CottonCandy].configuration.featured_entities + project.extensions[CottonCandy].configuration.featured_entities.append( + entity_reference_3, # type: ignore[arg-type] ) + async with project: + sut = _CottonCandyGuiWidget(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() + + betty_qtbot.mouse_click( + sut._featured_entities_entity_references_collector._entity_reference_remove_buttons[ + 1 + ] + ) + assert ( + entity_reference_1 + in project.extensions[CottonCandy].configuration.featured_entities + ) + assert ( + entity_reference_2 + not in project.extensions[ + CottonCandy + ].configuration.featured_entities + ) + assert ( + entity_reference_3 + in project.extensions[CottonCandy].configuration.featured_entities + ) async def test_change_primary_inactive_color( self, betty_qtbot: BettyQtBot, mocker: MockerFixture ) -> None: - project = Project(betty_qtbot.app) - configured_hex_value = "#ffffff" - project.configuration.extensions.enable(CottonCandy) - async with project: - sut = _CottonCandyGuiWidget(project) - mocker.patch.object( - QColorDialog, - "getColor", - mocker.MagicMock(return_value=QColor.fromString(configured_hex_value)), - ) - sut._color_configurations_widget._color_configurations[0]._configure.click() - assert ( - configured_hex_value - == project.extensions[ - CottonCandy - ].configuration.primary_inactive_color.hex - ) + async with Project.new_temporary(betty_qtbot.app) as project: + configured_hex_value = "#ffffff" + project.configuration.extensions.enable(CottonCandy) + async with project: + sut = _CottonCandyGuiWidget(project) + mocker.patch.object( + QColorDialog, + "getColor", + mocker.MagicMock( + return_value=QColor.fromString(configured_hex_value) + ), + ) + sut._color_configurations_widget._color_configurations[ + 0 + ]._configure.click() + assert ( + configured_hex_value + == project.extensions[ + CottonCandy + ].configuration.primary_inactive_color.hex + ) async def test_change_primary_active_color( self, betty_qtbot: BettyQtBot, mocker: MockerFixture ) -> None: - project = Project(betty_qtbot.app) - configured_hex_value = "#ffffff" - project.configuration.extensions.enable(CottonCandy) - async with project: - sut = _CottonCandyGuiWidget(project) - mocker.patch.object( - QColorDialog, - "getColor", - mocker.MagicMock(return_value=QColor.fromString(configured_hex_value)), - ) - sut._color_configurations_widget._color_configurations[1]._configure.click() - assert ( - configured_hex_value - == project.extensions[ - CottonCandy - ].configuration.primary_active_color.hex - ) + async with Project.new_temporary(betty_qtbot.app) as project: + configured_hex_value = "#ffffff" + project.configuration.extensions.enable(CottonCandy) + async with project: + sut = _CottonCandyGuiWidget(project) + mocker.patch.object( + QColorDialog, + "getColor", + mocker.MagicMock( + return_value=QColor.fromString(configured_hex_value) + ), + ) + sut._color_configurations_widget._color_configurations[ + 1 + ]._configure.click() + assert ( + configured_hex_value + == project.extensions[ + CottonCandy + ].configuration.primary_active_color.hex + ) async def test_change_link_inactive_color( self, betty_qtbot: BettyQtBot, mocker: MockerFixture ) -> None: - project = Project(betty_qtbot.app) - configured_hex_value = "#ffffff" - project.configuration.extensions.enable(CottonCandy) - async with project: - sut = _CottonCandyGuiWidget(project) - mocker.patch.object( - QColorDialog, - "getColor", - mocker.MagicMock(return_value=QColor.fromString(configured_hex_value)), - ) - sut._color_configurations_widget._color_configurations[2]._configure.click() - assert ( - configured_hex_value - == project.extensions[CottonCandy].configuration.link_inactive_color.hex - ) + async with Project.new_temporary(betty_qtbot.app) as project: + configured_hex_value = "#ffffff" + project.configuration.extensions.enable(CottonCandy) + async with project: + sut = _CottonCandyGuiWidget(project) + mocker.patch.object( + QColorDialog, + "getColor", + mocker.MagicMock( + return_value=QColor.fromString(configured_hex_value) + ), + ) + sut._color_configurations_widget._color_configurations[ + 2 + ]._configure.click() + assert ( + configured_hex_value + == project.extensions[ + CottonCandy + ].configuration.link_inactive_color.hex + ) async def test_change_link_active_color( self, betty_qtbot: BettyQtBot, mocker: MockerFixture ) -> None: - project = Project(betty_qtbot.app) - configured_hex_value = "#ffffff" - project.configuration.extensions.enable(CottonCandy) - async with project: - sut = _CottonCandyGuiWidget(project) - mocker.patch.object( - QColorDialog, - "getColor", - mocker.MagicMock(return_value=QColor.fromString(configured_hex_value)), - ) - sut._color_configurations_widget._color_configurations[3]._configure.click() - assert ( - configured_hex_value - == project.extensions[CottonCandy].configuration.link_active_color.hex - ) + async with Project.new_temporary(betty_qtbot.app) as project: + configured_hex_value = "#ffffff" + project.configuration.extensions.enable(CottonCandy) + async with project: + sut = _CottonCandyGuiWidget(project) + mocker.patch.object( + QColorDialog, + "getColor", + mocker.MagicMock( + return_value=QColor.fromString(configured_hex_value) + ), + ) + sut._color_configurations_widget._color_configurations[ + 3 + ]._configure.click() + assert ( + configured_hex_value + == project.extensions[ + CottonCandy + ].configuration.link_active_color.hex + ) async def test_set_logo(self, betty_qtbot: BettyQtBot, tmp_path: Path) -> None: - project = Project(betty_qtbot.app) - logo = tmp_path / "logo.png" - project.configuration.extensions.enable(CottonCandy) - async with project: - sut = _CottonCandyGuiWidget(project) - sut._logo.setText(str(logo)) - assert project.extensions[CottonCandy].configuration.logo == logo + async with Project.new_temporary(betty_qtbot.app) as project: + logo = tmp_path / "logo.png" + project.configuration.extensions.enable(CottonCandy) + async with project: + sut = _CottonCandyGuiWidget(project) + sut._logo.setText(str(logo)) + assert project.extensions[CottonCandy].configuration.logo == logo async def test_unset_logo(self, betty_qtbot: BettyQtBot, tmp_path: Path) -> None: - project = Project(betty_qtbot.app) - project.configuration.extensions.enable(CottonCandy) - project.extensions[CottonCandy].configuration.logo = tmp_path / "logo.png" - async with project: - sut = _CottonCandyGuiWidget(project) - sut._logo.setText("") - assert project.extensions[CottonCandy].configuration.logo is None + async with Project.new_temporary(betty_qtbot.app) as project: + project.configuration.extensions.enable(CottonCandy) + project.extensions[CottonCandy].configuration.logo = tmp_path / "logo.png" + async with project: + sut = _CottonCandyGuiWidget(project) + sut._logo.setText("") + assert project.extensions[CottonCandy].configuration.logo is None diff --git a/betty/tests/extension/cotton_candy/test_search.py b/betty/tests/extension/cotton_candy/test_search.py index 7fdb5c8ba..b123410c8 100644 --- a/betty/tests/extension/cotton_candy/test_search.py +++ b/betty/tests/extension/cotton_candy/test_search.py @@ -13,44 +13,48 @@ class TestIndex: async def test_build_empty(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - async with project: - indexed = [ - item - async for item in Index(project, Context(), DEFAULT_LOCALIZER).build() - ] + async with project: + indexed = [ + item + async for item in Index( + project, Context(), DEFAULT_LOCALIZER + ).build() + ] - assert indexed == [] + assert indexed == [] async def test_build_person_without_names(self, new_temporary_app: App) -> None: person_id = "P1" person = Person(id=person_id) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - project.ancestry.add(person) - async with project: - indexed = [ - item - async for item in Index(project, Context(), DEFAULT_LOCALIZER).build() - ] + project.ancestry.add(person) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), DEFAULT_LOCALIZER + ).build() + ] - assert indexed == [] + assert indexed == [] async def test_build_private_person(self, new_temporary_app: App) -> None: person_id = "P1" @@ -64,23 +68,25 @@ async def test_build_private_person(self, new_temporary_app: App) -> None: individual=individual_name, ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - project.ancestry.add(person) - async with project: - indexed = [ - item - async for item in Index(project, Context(), DEFAULT_LOCALIZER).build() - ] + project.ancestry.add(person) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), DEFAULT_LOCALIZER + ).build() + ] - assert indexed == [] + assert indexed == [] @pytest.mark.parametrize( ("expected", "locale"), @@ -100,26 +106,26 @@ async def test_build_person_with_individual_name( individual=individual_name, ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - project.ancestry.add(person) - async with project: - indexed = [ - item - async for item in Index( - project, Context(), await project.localizers.get(locale) - ).build() - ] - - assert indexed[0]["text"] == "jane" - assert expected in indexed[0]["result"] + project.ancestry.add(person) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), await project.localizers.get(locale) + ).build() + ] + + assert indexed[0]["text"] == "jane" + assert expected in indexed[0]["result"] @pytest.mark.parametrize( ("expected", "locale"), @@ -139,26 +145,26 @@ async def test_build_person_with_affiliation_name( affiliation=affiliation_name, ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - project.ancestry.add(person) - async with project: - indexed = [ - item - async for item in Index( - project, Context(), await project.localizers.get(locale) - ).build() - ] - - assert indexed[0]["text"] == "doughnut" - assert expected in indexed[0]["result"] + project.ancestry.add(person) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), await project.localizers.get(locale) + ).build() + ] + + assert indexed[0]["text"] == "doughnut" + assert expected in indexed[0]["result"] @pytest.mark.parametrize( ("expected", "locale"), @@ -180,26 +186,26 @@ async def test_build_person_with_individual_and_affiliation_names( affiliation=affiliation_name, ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - project.ancestry.add(person) - async with project: - indexed = [ - item - async for item in Index( - project, Context(), await project.localizers.get(locale) - ).build() - ] - - assert indexed[0]["text"] == "jane doughnut" - assert expected in indexed[0]["result"] + project.ancestry.add(person) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), await project.localizers.get(locale) + ).build() + ] + + assert indexed[0]["text"] == "jane doughnut" + assert expected in indexed[0]["result"] @pytest.mark.parametrize( ("expected", "locale"), @@ -226,26 +232,26 @@ async def test_build_place( ], ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - project.ancestry.add(place) - async with project: - indexed = [ - item - async for item in Index( - project, Context(), await project.localizers.get(locale) - ).build() - ] - - assert indexed[0]["text"] == "netherlands nederland" - assert expected in indexed[0]["result"] + project.ancestry.add(place) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), await project.localizers.get(locale) + ).build() + ] + + assert indexed[0]["text"] == "netherlands nederland" + assert expected in indexed[0]["result"] async def test_build_private_place(self, new_temporary_app: App) -> None: place_id = "P1" @@ -260,17 +266,19 @@ async def test_build_private_place(self, new_temporary_app: App) -> None: private=True, ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.ancestry.add(place) - async with project: - indexed = [ - item - async for item in Index(project, Context(), DEFAULT_LOCALIZER).build() - ] + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.ancestry.add(place) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), DEFAULT_LOCALIZER + ).build() + ] - assert indexed == [] + assert indexed == [] async def test_build_file_without_description(self, new_temporary_app: App) -> None: file_id = "F1" @@ -279,23 +287,25 @@ async def test_build_file_without_description(self, new_temporary_app: App) -> N path=Path(__file__), ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - project.ancestry.add(file) - async with project: - indexed = [ - item - async for item in Index(project, Context(), DEFAULT_LOCALIZER).build() - ] + project.ancestry.add(file) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), DEFAULT_LOCALIZER + ).build() + ] - assert indexed == [] + assert indexed == [] @pytest.mark.parametrize( ("expected", "locale"), @@ -314,26 +324,26 @@ async def test_build_file( description='"file" is Dutch for "traffic jam"', ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - project.ancestry.add(file) - async with project: - indexed = [ - item - async for item in Index( - project, Context(), await project.localizers.get(locale) - ).build() - ] - - assert indexed[0]["text"] == '"file" is dutch for "traffic jam"' - assert expected in indexed[0]["result"] + project.ancestry.add(file) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), await project.localizers.get(locale) + ).build() + ] + + assert indexed[0]["text"] == '"file" is dutch for "traffic jam"' + assert expected in indexed[0]["result"] async def test_build_private_file(self, new_temporary_app: App) -> None: file_id = "F1" @@ -344,14 +354,16 @@ async def test_build_private_file(self, new_temporary_app: App) -> None: private=True, ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(CottonCandy) - project.configuration.locales["en-US"].alias = "en" - project.ancestry.add(file) - async with project: - indexed = [ - item - async for item in Index(project, Context(), DEFAULT_LOCALIZER).build() - ] - - assert indexed == [] + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(CottonCandy) + project.configuration.locales["en-US"].alias = "en" + project.ancestry.add(file) + async with project: + indexed = [ + item + async for item in Index( + project, Context(), DEFAULT_LOCALIZER + ).build() + ] + + assert indexed == [] diff --git a/betty/tests/extension/demo/test___init__.py b/betty/tests/extension/demo/test___init__.py index bbd78398c..06208fb4a 100644 --- a/betty/tests/extension/demo/test___init__.py +++ b/betty/tests/extension/demo/test___init__.py @@ -19,15 +19,15 @@ class TestDemo: async def test_load(self, mocker: MockerFixture, new_temporary_app: App) -> None: mocker.patch("betty.wikipedia._Populator.populate") - project = Project(new_temporary_app) - project.configuration.extensions.append(ExtensionConfiguration(Demo)) - async with project: - await load(project) - assert len(project.ancestry[Person]) != 0 - assert len(project.ancestry[Place]) != 0 - assert len(project.ancestry[Event]) != 0 - assert len(project.ancestry[Source]) != 0 - assert len(project.ancestry[Citation]) != 0 + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append(ExtensionConfiguration(Demo)) + async with project: + await load(project) + assert len(project.ancestry[Person]) != 0 + assert len(project.ancestry[Place]) != 0 + assert len(project.ancestry[Event]) != 0 + assert len(project.ancestry[Source]) != 0 + assert len(project.ancestry[Citation]) != 0 class TestDemoServer: diff --git a/betty/tests/extension/deriver/test___init__.py b/betty/tests/extension/deriver/test___init__.py index fb6570492..8a364e883 100644 --- a/betty/tests/extension/deriver/test___init__.py +++ b/betty/tests/extension/deriver/test___init__.py @@ -77,35 +77,38 @@ async def test_post_load(self, new_temporary_app: App) -> None: ) Presence(person, Subject(), event) - project = Project(new_temporary_app) - project.configuration.extensions.append(ExtensionConfiguration(Deriver)) - project.ancestry.add(person) - async with project: - with record_added(project.ancestry) as added: - await load(project) - - assert len(person.presences) == 3 - start = [ - presence - for presence in person.presences - if presence.event - and issubclass(presence.event.event_type, StartOfLifeEventType) - ][0] - assert start is not None - assert start.event is not None - assert isinstance(start.event, Event) - assert ( - DateRange(None, Date(1, 1, 1), end_is_boundary=True) == start.event.date - ) - end = [ - presence - for presence in person.presences - if presence.event - and issubclass(presence.event.event_type, EndOfLifeEventType) - ][0] - assert end is not None - assert end.event is not None - assert DateRange(Date(1, 1, 1), start_is_boundary=True) == end.event.date - assert len(added[Event]) == 2 - assert start.event in added[Event] - assert end.event in added[Event] + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append(ExtensionConfiguration(Deriver)) + project.ancestry.add(person) + async with project: + with record_added(project.ancestry) as added: + await load(project) + + assert len(person.presences) == 3 + start = [ + presence + for presence in person.presences + if presence.event + and issubclass(presence.event.event_type, StartOfLifeEventType) + ][0] + assert start is not None + assert start.event is not None + assert isinstance(start.event, Event) + assert ( + DateRange(None, Date(1, 1, 1), end_is_boundary=True) + == start.event.date + ) + end = [ + presence + for presence in person.presences + if presence.event + and issubclass(presence.event.event_type, EndOfLifeEventType) + ][0] + assert end is not None + assert end.event is not None + assert ( + DateRange(Date(1, 1, 1), start_is_boundary=True) == end.event.date + ) + assert len(added[Event]) == 2 + assert start.event in added[Event] + assert end.event in added[Event] diff --git a/betty/tests/extension/gramps/test___init__.py b/betty/tests/extension/gramps/test___init__.py index 0afc9f13e..84edc3eb2 100644 --- a/betty/tests/extension/gramps/test___init__.py +++ b/betty/tests/extension/gramps/test___init__.py @@ -125,37 +125,37 @@ async def test_load_multiple_family_trees(self, new_temporary_app: App) -> None: async with aiofiles.open(gramps_family_tree_two_path, mode="w") as f: await f.write(family_tree_two_xml) - project = Project(new_temporary_app) - project.configuration.extensions.append( - ExtensionConfiguration( - Gramps, - extension_configuration=GrampsConfiguration( - family_trees=[ - FamilyTreeConfiguration( - file_path=gramps_family_tree_one_path - ), - FamilyTreeConfiguration( - file_path=gramps_family_tree_two_path - ), - ], - ), + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append( + ExtensionConfiguration( + Gramps, + extension_configuration=GrampsConfiguration( + family_trees=[ + FamilyTreeConfiguration( + file_path=gramps_family_tree_one_path + ), + FamilyTreeConfiguration( + file_path=gramps_family_tree_two_path + ), + ], + ), + ) ) - ) - async with project: - await load(project) - assert "O0001" in project.ancestry[File] - assert "O0002" in project.ancestry[File] - assert "I0001" in project.ancestry[Person] - assert "I0002" in project.ancestry[Person] - assert "P0001" in project.ancestry[Place] - assert "P0002" in project.ancestry[Place] - assert "E0001" in project.ancestry[Event] - assert "E0002" in project.ancestry[Event] - assert "S0001" in project.ancestry[Source] - assert "S0002" in project.ancestry[Source] - assert "R0001" in project.ancestry[Source] - assert "R0002" in project.ancestry[Source] - assert "C0001" in project.ancestry[Citation] - assert "C0002" in project.ancestry[Citation] - assert "N0001" in project.ancestry[Note] - assert "N0002" in project.ancestry[Note] + async with project: + await load(project) + assert "O0001" in project.ancestry[File] + assert "O0002" in project.ancestry[File] + assert "I0001" in project.ancestry[Person] + assert "I0002" in project.ancestry[Person] + assert "P0001" in project.ancestry[Place] + assert "P0002" in project.ancestry[Place] + assert "E0001" in project.ancestry[Event] + assert "E0002" in project.ancestry[Event] + assert "S0001" in project.ancestry[Source] + assert "S0002" in project.ancestry[Source] + assert "R0001" in project.ancestry[Source] + assert "R0002" in project.ancestry[Source] + assert "C0001" in project.ancestry[Citation] + assert "C0002" in project.ancestry[Citation] + assert "N0001" in project.ancestry[Note] + assert "N0002" in project.ancestry[Note] diff --git a/betty/tests/extension/gramps/test_gui.py b/betty/tests/extension/gramps/test_gui.py index f03bb5c1b..b289b1c8c 100644 --- a/betty/tests/extension/gramps/test_gui.py +++ b/betty/tests/extension/gramps/test_gui.py @@ -21,26 +21,26 @@ async def test_add_family_tree_set_path( new_temporary_app: App, tmp_path: Path, ) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.append(ExtensionConfiguration(Gramps)) - async with project: - sut = project.extensions[Gramps] - widget = sut.gui_build() - betty_qtbot.qtbot.addWidget(widget) - widget.show() + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append(ExtensionConfiguration(Gramps)) + async with project: + sut = project.extensions[Gramps] + widget = sut.gui_build() + betty_qtbot.qtbot.addWidget(widget) + widget.show() - betty_qtbot.mouse_click(widget._family_trees._add_family_tree_button) - add_family_tree_window = betty_qtbot.assert_window(_AddFamilyTreeWindow) + betty_qtbot.mouse_click(widget._family_trees._add_family_tree_button) + add_family_tree_window = betty_qtbot.assert_window(_AddFamilyTreeWindow) - file_path = tmp_path / "family-tree.gpkg" - add_family_tree_window._file_path.setText(str(file_path)) + file_path = tmp_path / "family-tree.gpkg" + add_family_tree_window._file_path.setText(str(file_path)) - betty_qtbot.mouse_click(add_family_tree_window._save_and_close) - betty_qtbot.assert_not_window(_AddFamilyTreeWindow) + betty_qtbot.mouse_click(add_family_tree_window._save_and_close) + betty_qtbot.assert_not_window(_AddFamilyTreeWindow) - assert len(sut.configuration.family_trees) == 1 - family_tree = sut.configuration.family_trees[0] - assert family_tree.file_path == file_path + assert len(sut.configuration.family_trees) == 1 + family_tree = sut.configuration.family_trees[0] + assert family_tree.file_path == file_path async def test_add_family_tree_find_path( @@ -49,52 +49,56 @@ async def test_add_family_tree_find_path( new_temporary_app: App, tmp_path: Path, ) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.append(ExtensionConfiguration(Gramps)) - async with project: - sut = project.extensions[Gramps] - widget = sut.gui_build() - betty_qtbot.qtbot.addWidget(widget) - widget.show() - - betty_qtbot.mouse_click(widget._family_trees._add_family_tree_button) - - add_family_tree_window = betty_qtbot.assert_window(_AddFamilyTreeWindow) - file_path = tmp_path / "family-tree.gpkg" - mocker.patch.object( - QFileDialog, - "getOpenFileName", - mocker.MagicMock(return_value=[str(file_path), None]), - ) - betty_qtbot.mouse_click(add_family_tree_window._file_path_find) - betty_qtbot.mouse_click(add_family_tree_window._save_and_close) - - assert len(sut.configuration.family_trees) == 1 - family_tree = sut.configuration.family_trees[0] - assert family_tree.file_path == file_path + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append(ExtensionConfiguration(Gramps)) + async with project: + sut = project.extensions[Gramps] + widget = sut.gui_build() + betty_qtbot.qtbot.addWidget(widget) + widget.show() + + betty_qtbot.mouse_click(widget._family_trees._add_family_tree_button) + + add_family_tree_window = betty_qtbot.assert_window(_AddFamilyTreeWindow) + file_path = tmp_path / "family-tree.gpkg" + mocker.patch.object( + QFileDialog, + "getOpenFileName", + mocker.MagicMock(return_value=[str(file_path), None]), + ) + betty_qtbot.mouse_click(add_family_tree_window._file_path_find) + betty_qtbot.mouse_click(add_family_tree_window._save_and_close) + + assert len(sut.configuration.family_trees) == 1 + family_tree = sut.configuration.family_trees[0] + assert family_tree.file_path == file_path async def test_remove_family_tree( betty_qtbot: BettyQtBot, new_temporary_app: App ) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.append( - ExtensionConfiguration( - Gramps, - extension_configuration=GrampsConfiguration( - family_trees=[ - FamilyTreeConfiguration(file_path=Path("/tmp/family-tree.gpkg")), - ] - ), + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append( + ExtensionConfiguration( + Gramps, + extension_configuration=GrampsConfiguration( + family_trees=[ + FamilyTreeConfiguration( + file_path=Path("/tmp/family-tree.gpkg") + ), + ] + ), + ) ) - ) - async with project: - sut = project.extensions[Gramps] - widget = sut.gui_build() - betty_qtbot.qtbot.addWidget(widget) - widget.show() - - betty_qtbot.mouse_click(widget._family_trees._family_trees_remove_buttons[0]) - - assert len(sut.configuration.family_trees) == 0 - assert widget._family_trees._family_trees_remove_buttons == [] + async with project: + sut = project.extensions[Gramps] + widget = sut.gui_build() + betty_qtbot.qtbot.addWidget(widget) + widget.show() + + betty_qtbot.mouse_click( + widget._family_trees._family_trees_remove_buttons[0] + ) + + assert len(sut.configuration.family_trees) == 0 + assert widget._family_trees._family_trees_remove_buttons == [] diff --git a/betty/tests/extension/http_api_doc/test___init__.py b/betty/tests/extension/http_api_doc/test___init__.py index 5f589019e..982ec4759 100644 --- a/betty/tests/extension/http_api_doc/test___init__.py +++ b/betty/tests/extension/http_api_doc/test___init__.py @@ -6,13 +6,13 @@ class TestHttpApiDoc: async def test_generate(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.append(ExtensionConfiguration(HttpApiDoc)) - async with project: - await generate(project) - assert ( - project.configuration.www_directory_path / "api" / "index.html" - ).is_file() - assert ( - project.configuration.www_directory_path / "js" / "http-api-doc.js" - ).is_file() + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append(ExtensionConfiguration(HttpApiDoc)) + async with project: + await generate(project) + assert ( + project.configuration.www_directory_path / "api" / "index.html" + ).is_file() + assert ( + project.configuration.www_directory_path / "js" / "http-api-doc.js" + ).is_file() diff --git a/betty/tests/extension/maps/test___init__.py b/betty/tests/extension/maps/test___init__.py index 09c4e00b4..1b5c4dc86 100644 --- a/betty/tests/extension/maps/test___init__.py +++ b/betty/tests/extension/maps/test___init__.py @@ -8,24 +8,24 @@ class TestMaps: async def test_generate(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - project.configuration.debug = True - project.configuration.extensions.append(ExtensionConfiguration(Maps)) - async with project: - await generate(project) - async with aiofiles.open( - project.configuration.www_directory_path - / "js" - / "betty.extension.Maps.js", - encoding="utf-8", - ) as f: - betty_js = await f.read() - assert Maps.name() in betty_js - async with aiofiles.open( - project.configuration.www_directory_path - / "css" - / "betty.extension.Maps.css", - encoding="utf-8", - ) as f: - betty_css = await f.read() - assert Maps.name() in betty_css + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.debug = True + project.configuration.extensions.append(ExtensionConfiguration(Maps)) + async with project: + await generate(project) + async with aiofiles.open( + project.configuration.www_directory_path + / "js" + / "betty.extension.Maps.js", + encoding="utf-8", + ) as f: + betty_js = await f.read() + assert Maps.name() in betty_js + async with aiofiles.open( + project.configuration.www_directory_path + / "css" + / "betty.extension.Maps.css", + encoding="utf-8", + ) as f: + betty_css = await f.read() + assert Maps.name() in betty_css diff --git a/betty/tests/extension/nginx/test___init__.py b/betty/tests/extension/nginx/test___init__.py index f869c745e..e30e2a0c1 100644 --- a/betty/tests/extension/nginx/test___init__.py +++ b/betty/tests/extension/nginx/test___init__.py @@ -6,19 +6,19 @@ class TestNginx: async def test_generate(self, new_temporary_app: App): - project = Project(new_temporary_app) - project.configuration.base_url = "http://example.com" - project.configuration.extensions.append(ExtensionConfiguration(Nginx)) - async with project: - await generate(project) - assert ( - project.configuration.output_directory_path / "nginx" / "nginx.conf" - ).exists() - assert ( - project.configuration.output_directory_path - / "nginx" - / "content_negotiation.lua" - ).exists() - assert ( - project.configuration.output_directory_path / "nginx" / "Dockerfile" - ).exists() + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.base_url = "http://example.com" + project.configuration.extensions.append(ExtensionConfiguration(Nginx)) + async with project: + await generate(project) + assert ( + project.configuration.output_directory_path / "nginx" / "nginx.conf" + ).exists() + assert ( + project.configuration.output_directory_path + / "nginx" + / "content_negotiation.lua" + ).exists() + assert ( + project.configuration.output_directory_path / "nginx" / "Dockerfile" + ).exists() diff --git a/betty/tests/extension/nginx/test_artifact.py b/betty/tests/extension/nginx/test_artifact.py index c487d011e..037a3192e 100644 --- a/betty/tests/extension/nginx/test_artifact.py +++ b/betty/tests/extension/nginx/test_artifact.py @@ -39,11 +39,11 @@ async def _assert_configuration_equals(self, expected: str, project: Project): ) async def test(self, new_temporary_app: App): - project = Project(new_temporary_app) - project.configuration.base_url = "http://example.com" - project.configuration.extensions.append(ExtensionConfiguration(Nginx)) - expected = ( - r""" + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.base_url = "http://example.com" + project.configuration.extensions.append(ExtensionConfiguration(Nginx)) + expected = ( + r""" server { add_header Vary Accept-Language; add_header Cache-Control "max-age=86400"; @@ -71,27 +71,27 @@ async def test(self, new_temporary_app: App): } } """ - % project.configuration.www_directory_path - ) - async with project: - await self._assert_configuration_equals(expected, project) + % project.configuration.www_directory_path + ) + async with project: + await self._assert_configuration_equals(expected, project) async def test_multilingual(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - project.configuration.base_url = "http://example.com" - project.configuration.locales.replace( - LocaleConfiguration( - "en-US", - alias="en", - ), - LocaleConfiguration( - "nl-NL", - alias="nl", - ), - ) - project.configuration.extensions.append(ExtensionConfiguration(Nginx)) - expected = ( - r""" + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.base_url = "http://example.com" + project.configuration.locales.replace( + LocaleConfiguration( + "en-US", + alias="en", + ), + LocaleConfiguration( + "nl-NL", + alias="nl", + ), + ) + project.configuration.extensions.append(ExtensionConfiguration(Nginx)) + expected = ( + r""" server { add_header Vary Accept-Language; add_header Cache-Control "max-age=86400"; @@ -150,28 +150,28 @@ async def test_multilingual(self, new_temporary_app: App) -> None: } } """ - % project.configuration.www_directory_path - ) - async with project: - await self._assert_configuration_equals(expected, project) + % project.configuration.www_directory_path + ) + async with project: + await self._assert_configuration_equals(expected, project) async def test_multilingual_with_clean_urls(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - project.configuration.base_url = "http://example.com" - project.configuration.clean_urls = True - project.configuration.locales.replace( - LocaleConfiguration( - "en-US", - alias="en", - ), - LocaleConfiguration( - "nl-NL", - alias="nl", - ), - ) - project.configuration.extensions.append(ExtensionConfiguration(Nginx)) - expected = ( - r""" + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.base_url = "http://example.com" + project.configuration.clean_urls = True + project.configuration.locales.replace( + LocaleConfiguration( + "en-US", + alias="en", + ), + LocaleConfiguration( + "nl-NL", + alias="nl", + ), + ) + project.configuration.extensions.append(ExtensionConfiguration(Nginx)) + expected = ( + r""" server { add_header Vary Accept-Language; add_header Cache-Control "max-age=86400"; @@ -246,18 +246,18 @@ async def test_multilingual_with_clean_urls(self, new_temporary_app: App) -> Non } } """ - % project.configuration.www_directory_path - ) - async with project: - await self._assert_configuration_equals(expected, project) + % project.configuration.www_directory_path + ) + async with project: + await self._assert_configuration_equals(expected, project) async def test_with_clean_urls(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - project.configuration.base_url = "http://example.com" - project.configuration.clean_urls = True - project.configuration.extensions.append(ExtensionConfiguration(Nginx)) - expected = ( - r""" + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.base_url = "http://example.com" + project.configuration.clean_urls = True + project.configuration.extensions.append(ExtensionConfiguration(Nginx)) + expected = ( + r""" server { add_header Vary Accept-Language; add_header Cache-Control "max-age=86400"; @@ -291,17 +291,17 @@ async def test_with_clean_urls(self, new_temporary_app: App) -> None: try_files $uri $uri/ =404; } }""" - % project.configuration.www_directory_path - ) - async with project: - await self._assert_configuration_equals(expected, project) + % project.configuration.www_directory_path + ) + async with project: + await self._assert_configuration_equals(expected, project) async def test_with_https(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - project.configuration.base_url = "https://example.com" - project.configuration.extensions.append(ExtensionConfiguration(Nginx)) - expected = ( - r""" + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.base_url = "https://example.com" + project.configuration.extensions.append(ExtensionConfiguration(Nginx)) + expected = ( + r""" server { listen 80; server_name example.com; @@ -335,22 +335,22 @@ async def test_with_https(self, new_temporary_app: App) -> None: } } """ - % project.configuration.www_directory_path - ) - async with project: - await self._assert_configuration_equals(expected, project) + % project.configuration.www_directory_path + ) + async with project: + await self._assert_configuration_equals(expected, project) async def test_with_overridden_www_directory_path(self, new_temporary_app: App): - project = Project(new_temporary_app) - project.configuration.extensions.append( - ExtensionConfiguration( - Nginx, - extension_configuration=NginxConfiguration( - www_directory_path="/tmp/overridden-www", - ), + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append( + ExtensionConfiguration( + Nginx, + extension_configuration=NginxConfiguration( + www_directory_path="/tmp/overridden-www", + ), + ) ) - ) - expected = """ + expected = """ server { listen 80; server_name example.com; @@ -384,28 +384,28 @@ async def test_with_overridden_www_directory_path(self, new_temporary_app: App): } } """ - async with project: - await self._assert_configuration_equals(expected, project) + async with project: + await self._assert_configuration_equals(expected, project) class TestGenerateDockerfileFile: async def test(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.append( - ExtensionConfiguration( - Nginx, - extension_configuration=NginxConfiguration( - www_directory_path="/tmp/overridden-www", - ), + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append( + ExtensionConfiguration( + Nginx, + extension_configuration=NginxConfiguration( + www_directory_path="/tmp/overridden-www", + ), + ) ) - ) - async with project: - await generate_dockerfile_file(project) - assert ( - project.configuration.output_directory_path - / "nginx" - / "content_negotiation.lua" - ).exists() - assert ( - project.configuration.output_directory_path / "nginx" / "Dockerfile" - ).exists() + async with project: + await generate_dockerfile_file(project) + assert ( + project.configuration.output_directory_path + / "nginx" + / "content_negotiation.lua" + ).exists() + assert ( + project.configuration.output_directory_path / "nginx" / "Dockerfile" + ).exists() diff --git a/betty/tests/extension/nginx/test_cli.py b/betty/tests/extension/nginx/test_cli.py index 487373d1f..f5dd31da6 100644 --- a/betty/tests/extension/nginx/test_cli.py +++ b/betty/tests/extension/nginx/test_cli.py @@ -21,14 +21,14 @@ async def test(self, mocker: MockerFixture, new_temporary_app: App) -> None: mocker.patch( "betty.extension.nginx.serve.DockerizedNginxServer", new=NoOpServer ) - project = Project(new_temporary_app) - project.configuration.extensions.enable(Nginx) - await project.configuration.write() - await makedirs(project.configuration.www_directory_path) - async with project: - await to_thread( - run, - "serve-nginx-docker", - "-c", - str(project.configuration.configuration_file_path), - ) + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Nginx) + await project.configuration.write() + await makedirs(project.configuration.www_directory_path) + async with project: + await to_thread( + run, + "serve-nginx-docker", + "-c", + str(project.configuration.configuration_file_path), + ) diff --git a/betty/tests/extension/nginx/test_gui.py b/betty/tests/extension/nginx/test_gui.py index 215165305..17d32c28c 100644 --- a/betty/tests/extension/nginx/test_gui.py +++ b/betty/tests/extension/nginx/test_gui.py @@ -19,47 +19,47 @@ class TestNginxGuiWidget: async def test_https_with_base_url( self, betty_qtbot: BettyQtBot, new_temporary_app: App ) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.enable(Nginx) - async with project: - nginx = project.extensions[Nginx] - nginx.configuration.https = False - sut = nginx.gui_build() - betty_qtbot.qtbot.addWidget(sut) - sut.show() - - betty_qtbot.set_checked(sut._nginx_https_base_url, True) - assert nginx.configuration.https is None + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Nginx) + async with project: + nginx = project.extensions[Nginx] + nginx.configuration.https = False + sut = nginx.gui_build() + betty_qtbot.qtbot.addWidget(sut) + sut.show() + + betty_qtbot.set_checked(sut._nginx_https_base_url, True) + assert nginx.configuration.https is None async def test_https_with_https( self, betty_qtbot: BettyQtBot, new_temporary_app: App ) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.enable(Nginx) - async with project: - nginx = project.extensions[Nginx] - nginx.configuration.https = False - sut = nginx.gui_build() - betty_qtbot.qtbot.addWidget(sut) - sut.show() - - betty_qtbot.set_checked(sut._nginx_https_https, True) - assert nginx.configuration.https is True + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Nginx) + async with project: + nginx = project.extensions[Nginx] + nginx.configuration.https = False + sut = nginx.gui_build() + betty_qtbot.qtbot.addWidget(sut) + sut.show() + + betty_qtbot.set_checked(sut._nginx_https_https, True) + assert nginx.configuration.https is True async def test_https_with_http( self, betty_qtbot: BettyQtBot, new_temporary_app: App ) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.enable(Nginx) - async with project: - nginx = project.extensions[Nginx] - nginx.configuration.https = True - sut = nginx.gui_build() - betty_qtbot.qtbot.addWidget(sut) - sut.show() - - betty_qtbot.set_checked(sut._nginx_https_http, True) - assert nginx.configuration.https is False + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Nginx) + async with project: + nginx = project.extensions[Nginx] + nginx.configuration.https = True + sut = nginx.gui_build() + betty_qtbot.qtbot.addWidget(sut) + sut.show() + + betty_qtbot.set_checked(sut._nginx_https_http, True) + assert nginx.configuration.https is False async def test_www_directory_path_with_path( self, @@ -68,23 +68,23 @@ async def test_www_directory_path_with_path( new_temporary_app: App, tmp_path: Path, ) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.enable(Nginx) - async with project: - nginx = project.extensions[Nginx] - sut = nginx.gui_build() - betty_qtbot.qtbot.addWidget(sut) - sut.show() - - www_directory_path = str(tmp_path) - mocker.patch.object( - QFileDialog, - "getExistingDirectory", - mocker.MagicMock(return_value=www_directory_path), - ) - - betty_qtbot.mouse_click(sut._nginx_www_directory_path_find) - assert nginx.configuration.www_directory_path == www_directory_path + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Nginx) + async with project: + nginx = project.extensions[Nginx] + sut = nginx.gui_build() + betty_qtbot.qtbot.addWidget(sut) + sut.show() + + www_directory_path = str(tmp_path) + mocker.patch.object( + QFileDialog, + "getExistingDirectory", + mocker.MagicMock(return_value=www_directory_path), + ) + + betty_qtbot.mouse_click(sut._nginx_www_directory_path_find) + assert nginx.configuration.www_directory_path == www_directory_path async def test_www_directory_path_without_path( self, @@ -92,13 +92,13 @@ async def test_www_directory_path_without_path( new_temporary_app: App, tmp_path: Path, ) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.enable(Nginx) - async with project: - nginx = project.extensions[Nginx] - sut = nginx.gui_build() - betty_qtbot.qtbot.addWidget(sut) - sut.show() - - betty_qtbot.set_text(sut._nginx_www_directory_path, "") - assert nginx.configuration.www_directory_path is None + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Nginx) + async with project: + nginx = project.extensions[Nginx] + sut = nginx.gui_build() + betty_qtbot.qtbot.addWidget(sut) + sut.show() + + betty_qtbot.set_text(sut._nginx_www_directory_path, "") + assert nginx.configuration.www_directory_path is None diff --git a/betty/tests/extension/nginx/test_integration.py b/betty/tests/extension/nginx/test_integration.py index 08a1db766..49ad67437 100644 --- a/betty/tests/extension/nginx/test_integration.py +++ b/betty/tests/extension/nginx/test_integration.py @@ -33,8 +33,9 @@ class TestNginx: async def server( self, configuration: ProjectConfiguration ) -> AsyncIterator[Server]: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.update(configuration) async with project: await generate.generate(project) @@ -50,12 +51,16 @@ async def assert_betty_html(self, response: Response) -> None: async def assert_betty_json(self, response: Response) -> None: assert response.headers["Content-Type"] == "application/json" data = response.json() - async with App.new_temporary() as app, app, Project(app) as project: + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project, project: schema = Schema(project) await schema.validate(data) - def monolingual_configuration(self) -> ProjectConfiguration: + @pytest.fixture() + def monolingual_configuration(self, tmp_path: Path) -> ProjectConfiguration: return ProjectConfiguration( + tmp_path / "betty.json", extensions=[ ExtensionConfiguration( Nginx, @@ -66,8 +71,12 @@ def monolingual_configuration(self) -> ProjectConfiguration: ], ) - def monolingual_clean_urls_configuration(self) -> ProjectConfiguration: + @pytest.fixture() + def monolingual_clean_urls_configuration( + self, tmp_path: Path + ) -> ProjectConfiguration: return ProjectConfiguration( + tmp_path / "betty.json", extensions=[ ExtensionConfiguration( Nginx, @@ -79,8 +88,10 @@ def monolingual_clean_urls_configuration(self) -> ProjectConfiguration: clean_urls=True, ) - def multilingual_configuration(self) -> ProjectConfiguration: + @pytest.fixture() + def multilingual_configuration(self, tmp_path: Path) -> ProjectConfiguration: return ProjectConfiguration( + tmp_path / "betty.json", extensions=[ ExtensionConfiguration( Nginx, @@ -101,8 +112,12 @@ def multilingual_configuration(self) -> ProjectConfiguration: ], ) - def multilingual_clean_urls_configuration(self) -> ProjectConfiguration: + @pytest.fixture() + def multilingual_clean_urls_configuration( + self, tmp_path: Path + ) -> ProjectConfiguration: return ProjectConfiguration( + tmp_path / "betty.json", extensions=[ ExtensionConfiguration( Nginx, @@ -132,22 +147,28 @@ def _assert(response: Response) -> None: return _assert - async def test_front_page(self): - async with self.server(self.monolingual_clean_urls_configuration()) as server: + async def test_front_page( + self, monolingual_clean_urls_configuration: ProjectConfiguration + ): + async with self.server(monolingual_clean_urls_configuration) as server: await Do(requests.get, server.public_url).until( self._build_assert_status_code(200), self.assert_betty_html, ) - async def test_default_html_404(self): - async with self.server(self.monolingual_clean_urls_configuration()) as server: + async def test_default_html_404( + self, monolingual_clean_urls_configuration: ProjectConfiguration + ): + async with self.server(monolingual_clean_urls_configuration) as server: await Do(requests.get, f"{server.public_url}/non-existent-path/").until( self._build_assert_status_code(404), self.assert_betty_html, ) - async def test_negotiated_json_404(self): - async with self.server(self.monolingual_clean_urls_configuration()) as server: + async def test_negotiated_json_404( + self, monolingual_clean_urls_configuration: ProjectConfiguration + ): + async with self.server(monolingual_clean_urls_configuration) as server: await Do( requests.get, f"{server.public_url}/non-existent-path/", @@ -159,35 +180,41 @@ async def test_negotiated_json_404(self): self.assert_betty_json, ) - async def test_default_localized_front_page(self): + async def test_default_localized_front_page( + self, multilingual_configuration: ProjectConfiguration + ): async def _assert_response(response: Response) -> None: assert response.status_code == 200 assert response.headers["Content-Language"] == "en" assert f"{server.public_url}/en/" == response.url await self.assert_betty_html(response) - async with self.server(self.multilingual_configuration()) as server: + async with self.server(multilingual_configuration) as server: await Do(requests.get, server.public_url).until(_assert_response) - async def test_explicitly_localized_404(self): + async def test_explicitly_localized_404( + self, multilingual_configuration: ProjectConfiguration + ): async def _assert_response(response: Response) -> None: assert response.status_code == 404 assert response.headers["Content-Language"] == "nl" await self.assert_betty_html(response) - async with self.server(self.multilingual_configuration()) as server: + async with self.server(multilingual_configuration) as server: await Do(requests.get, f"{server.public_url}/nl/non-existent-path/").until( _assert_response ) - async def test_negotiated_localized_front_page(self): + async def test_negotiated_localized_front_page( + self, multilingual_clean_urls_configuration: ProjectConfiguration + ): async def _assert_response(response: Response) -> None: assert response.status_code == 200 assert response.headers["Content-Language"] == "nl" assert f"{server.public_url}/nl/" == response.url await self.assert_betty_html(response) - async with self.server(self.multilingual_clean_urls_configuration()) as server: + async with self.server(multilingual_clean_urls_configuration) as server: await Do( requests.get, server.public_url, @@ -196,8 +223,10 @@ async def _assert_response(response: Response) -> None: }, ).until(_assert_response) - async def test_negotiated_localized_negotiated_json_404(self): - async with self.server(self.multilingual_clean_urls_configuration()) as server: + async def test_negotiated_localized_negotiated_json_404( + self, multilingual_clean_urls_configuration: ProjectConfiguration + ): + async with self.server(multilingual_clean_urls_configuration) as server: await Do( requests.get, f"{server.public_url}/non-existent-path/", @@ -210,15 +239,19 @@ async def test_negotiated_localized_negotiated_json_404(self): self.assert_betty_json, ) - async def test_default_html_resource(self): - async with self.server(self.monolingual_clean_urls_configuration()) as server: + async def test_default_html_resource( + self, monolingual_clean_urls_configuration: ProjectConfiguration + ): + async with self.server(monolingual_clean_urls_configuration) as server: await Do(requests.get, f"{server.public_url}/place/").until( self._build_assert_status_code(200), self.assert_betty_html, ) - async def test_negotiated_html_resource(self): - async with self.server(self.monolingual_clean_urls_configuration()) as server: + async def test_negotiated_html_resource( + self, monolingual_clean_urls_configuration: ProjectConfiguration + ): + async with self.server(monolingual_clean_urls_configuration) as server: await Do( requests.get, f"{server.public_url}/place/", @@ -230,8 +263,10 @@ async def test_negotiated_html_resource(self): self.assert_betty_html, ) - async def test_negotiated_json_resource(self): - async with self.server(self.monolingual_clean_urls_configuration()) as server: + async def test_negotiated_json_resource( + self, monolingual_clean_urls_configuration: ProjectConfiguration + ): + async with self.server(monolingual_clean_urls_configuration) as server: await Do( requests.get, f"{server.public_url}/place/", @@ -243,15 +278,21 @@ async def test_negotiated_json_resource(self): self.assert_betty_json, ) - async def test_default_html_static_resource(self): - async with self.server(self.multilingual_clean_urls_configuration()) as server: + async def test_default_html_static_resource( + self, multilingual_clean_urls_configuration: ProjectConfiguration + ): + async with self.server(multilingual_clean_urls_configuration) as server: await Do(requests.get, f"{server.public_url}/non-existent-path/").until( self._build_assert_status_code(404), self.assert_betty_html, ) - async def test_negotiated_html_static_resource(self, tmp_path: Path): - async with self.server(self.multilingual_clean_urls_configuration()) as server: + async def test_negotiated_html_static_resource( + self, + multilingual_clean_urls_configuration: ProjectConfiguration, + tmp_path: Path, + ): + async with self.server(multilingual_clean_urls_configuration) as server: await Do( requests.get, f"{server.public_url}/non-existent-path/", @@ -263,8 +304,10 @@ async def test_negotiated_html_static_resource(self, tmp_path: Path): self.assert_betty_html, ) - async def test_negotiated_json_static_resource(self): - async with self.server(self.multilingual_clean_urls_configuration()) as server: + async def test_negotiated_json_static_resource( + self, multilingual_clean_urls_configuration: ProjectConfiguration + ): + async with self.server(multilingual_clean_urls_configuration) as server: await Do( requests.get, f"{server.public_url}/non-existent-path/", diff --git a/betty/tests/extension/nginx/test_serve.py b/betty/tests/extension/nginx/test_serve.py index 411555d55..49b191446 100644 --- a/betty/tests/extension/nginx/test_serve.py +++ b/betty/tests/extension/nginx/test_serve.py @@ -29,8 +29,9 @@ def _assert_response(response: Response) -> None: assert response.headers["Cache-Control"] == "no-cache" content = "Hello, and welcome to my site!" - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.extensions.append( ExtensionConfiguration( Nginx, @@ -48,8 +49,9 @@ def _assert_response(response: Response) -> None: await Do(requests.get, server.public_url).until(_assert_response) async def test_public_url_unstarted(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.extensions.enable(Nginx) async with project: sut = DockerizedNginxServer(project) @@ -59,8 +61,9 @@ async def test_public_url_unstarted(self) -> None: async def test_is_available_is_available(self, mocker: MockerFixture) -> None: m_from_env = mocker.patch("docker.from_env") m_from_env.return_value = mocker.Mock("docker.client.DockerClient") - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.extensions.enable(Nginx) async with project: sut = DockerizedNginxServer(project) @@ -69,8 +72,9 @@ async def test_is_available_is_available(self, mocker: MockerFixture) -> None: async def test_is_available_is_unavailable(self, mocker: MockerFixture) -> None: m_from_env = mocker.patch("docker.from_env") m_from_env.side_effect = DockerException() - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.extensions.enable(Nginx) async with project: sut = DockerizedNginxServer(project) diff --git a/betty/tests/extension/privatizer/test___init__.py b/betty/tests/extension/privatizer/test___init__.py index 7bc28b289..f271bc48c 100644 --- a/betty/tests/extension/privatizer/test___init__.py +++ b/betty/tests/extension/privatizer/test___init__.py @@ -49,12 +49,12 @@ async def test_post_load(self, new_temporary_app: App) -> None: ) citation.files.add(citation_file) - project = Project(new_temporary_app) - project.configuration.extensions.append(ExtensionConfiguration(Privatizer)) - project.ancestry.add(person, source, citation) - async with project: - await load(project) - - assert person.private - assert source_file.private - assert citation_file.private + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append(ExtensionConfiguration(Privatizer)) + project.ancestry.add(person, source, citation) + async with project: + await load(project) + + assert person.private + assert source_file.private + assert citation_file.private diff --git a/betty/tests/extension/trees/test___init__.py b/betty/tests/extension/trees/test___init__.py index 7205070aa..d542dc170 100644 --- a/betty/tests/extension/trees/test___init__.py +++ b/betty/tests/extension/trees/test___init__.py @@ -8,24 +8,24 @@ class TestTrees: async def test_generate(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - project.configuration.debug = True - project.configuration.extensions.append(ExtensionConfiguration(Trees)) - async with project: - await generate(project) - async with aiofiles.open( - project.configuration.www_directory_path - / "js" - / "betty.extension.Trees.js", - encoding="utf-8", - ) as f: - betty_js = await f.read() - assert Trees.name() in betty_js - async with aiofiles.open( - project.configuration.www_directory_path - / "css" - / "betty.extension.Trees.css", - encoding="utf-8", - ) as f: - betty_css = await f.read() - assert Trees.name() in betty_css + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.debug = True + project.configuration.extensions.append(ExtensionConfiguration(Trees)) + async with project: + await generate(project) + async with aiofiles.open( + project.configuration.www_directory_path + / "js" + / "betty.extension.Trees.js", + encoding="utf-8", + ) as f: + betty_js = await f.read() + assert Trees.name() in betty_js + async with aiofiles.open( + project.configuration.www_directory_path + / "css" + / "betty.extension.Trees.css", + encoding="utf-8", + ) as f: + betty_css = await f.read() + assert Trees.name() in betty_css diff --git a/betty/tests/extension/webpack/test___init__.py b/betty/tests/extension/webpack/test___init__.py index f1c090be8..a2e04b2c5 100644 --- a/betty/tests/extension/webpack/test___init__.py +++ b/betty/tests/extension/webpack/test___init__.py @@ -12,7 +12,7 @@ from betty.extension.webpack.build import webpack_build_id from betty.generate import generate from betty.job import Context -from betty.project import Project +from betty.project import Project, ProjectConfiguration from betty.requirement import RequirementError @@ -52,15 +52,15 @@ async def test_generate_with_npm( ) as f: await f.write(self._SENTINEL) - project = Project(new_temporary_app) - project.configuration.extensions.enable(Webpack) - async with project: - await generate(project) + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Webpack) + async with project: + await generate(project) - async with aiofiles.open( - project.configuration.www_directory_path / self._SENTINEL - ) as f: - assert await f.read() == self._SENTINEL + async with aiofiles.open( + project.configuration.www_directory_path / self._SENTINEL + ) as f: + assert await f.read() == self._SENTINEL async def test_generate_without_npm_with_prebuild( self, mocker: MockerFixture, new_temporary_app: App, tmp_path: Path @@ -80,14 +80,14 @@ async def test_generate_without_npm_with_prebuild( original_prebuilt_assets_directory_path = fs.PREBUILT_ASSETS_DIRECTORY_PATH fs.PREBUILT_ASSETS_DIRECTORY_PATH = tmp_path try: - project = Project(new_temporary_app) - project.configuration.extensions.enable(Webpack) - async with project: - await generate(project) - async with aiofiles.open( - project.configuration.www_directory_path / self._SENTINEL - ) as f: - assert await f.read() == self._SENTINEL + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Webpack) + async with project: + await generate(project) + async with aiofiles.open( + project.configuration.www_directory_path / self._SENTINEL + ) as f: + assert await f.read() == self._SENTINEL finally: fs.PREBUILT_ASSETS_DIRECTORY_PATH = original_prebuilt_assets_directory_path @@ -104,7 +104,10 @@ async def test_generate_without_npm_without_prebuild( Path(prebuilt_assets_directory_path) / "does-not-exist" ) try: - project = Project(new_temporary_app) + project = Project( + new_temporary_app, + ProjectConfiguration(tmp_path / "project" / "betty.json"), + ) project.configuration.extensions.enable(Webpack) async with project: with pytest.raises(ExceptionGroup) as exc_info: @@ -136,17 +139,17 @@ async def test_prebuild( fs.PREBUILT_ASSETS_DIRECTORY_PATH = prebuilt_assets_directory_path try: job_context = Context() - project = Project(new_temporary_app) - project.configuration.extensions.enable(Webpack) - async with project: - webpack = project.extensions[Webpack] - await webpack.prebuild(job_context) - async with aiofiles.open( - prebuilt_assets_directory_path - / "webpack" - / f"build-{webpack_build_id(())}" - / self._SENTINEL - ) as f: - assert await f.read() == self._SENTINEL + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Webpack) + async with project: + webpack = project.extensions[Webpack] + await webpack.prebuild(job_context) + async with aiofiles.open( + prebuilt_assets_directory_path + / "webpack" + / f"build-{webpack_build_id(())}" + / self._SENTINEL + ) as f: + assert await f.read() == self._SENTINEL finally: fs.PREBUILT_ASSETS_DIRECTORY_PATH = original_prebuilt_assets_directory_path diff --git a/betty/tests/extension/webpack/test_build.py b/betty/tests/extension/webpack/test_build.py index 3a7535501..4aae2e537 100644 --- a/betty/tests/extension/webpack/test_build.py +++ b/betty/tests/extension/webpack/test_build.py @@ -49,38 +49,40 @@ async def test_build( tmp_path: Path, webpack_build_cache_available: bool, ) -> None: - project = Project(new_temporary_app) - if with_entry_point_provider: - project.configuration.extensions.enable(DummyEntryPointProviderExtension) - job_context = Context() - async with project: - sut = Builder( - tmp_path, - ( - [project.extensions[DummyEntryPointProviderExtension]] - if with_entry_point_provider - else [] - ), - False, - project.renderer, - job_context=job_context, - localizer=DEFAULT_LOCALIZER, - ) - if npm_install_cache_available: + async with Project.new_temporary(new_temporary_app) as project: + if with_entry_point_provider: + project.configuration.extensions.enable( + DummyEntryPointProviderExtension + ) + job_context = Context() + async with project: + sut = Builder( + tmp_path, + ( + [project.extensions[DummyEntryPointProviderExtension]] + if with_entry_point_provider + else [] + ), + False, + project.renderer, + job_context=job_context, + localizer=DEFAULT_LOCALIZER, + ) + if npm_install_cache_available: + webpack_build_directory_path = await sut.build() + if not webpack_build_cache_available: + await to_thread(rmtree, webpack_build_directory_path) webpack_build_directory_path = await sut.build() - if not webpack_build_cache_available: - await to_thread(rmtree, webpack_build_directory_path) - webpack_build_directory_path = await sut.build() - assert (webpack_build_directory_path / "css" / "vendor.css").exists() - assert ( - webpack_build_directory_path / "js" / "webpack-entry-loader.js" - ).exists() - if with_entry_point_provider: + assert (webpack_build_directory_path / "css" / "vendor.css").exists() assert ( - webpack_build_directory_path - / "js" - / f"{DummyEntryPointProviderExtension.name()}.js" + webpack_build_directory_path / "js" / "webpack-entry-loader.js" ).exists() + if with_entry_point_provider: + assert ( + webpack_build_directory_path + / "js" + / f"{DummyEntryPointProviderExtension.name()}.js" + ).exists() async def test_build_with_npm_unavailable( self, mocker: MockerFixture, tmp_path: Path diff --git a/betty/tests/extension/wikipedia/test___init__.py b/betty/tests/extension/wikipedia/test___init__.py index 44be55065..8ae46498e 100644 --- a/betty/tests/extension/wikipedia/test___init__.py +++ b/betty/tests/extension/wikipedia/test___init__.py @@ -34,27 +34,27 @@ async def test_filter(self, mocker: MockerFixture, new_temporary_app: App) -> No Link("https://example.com"), ] - project = Project(new_temporary_app) - project.configuration.extensions.append(ExtensionConfiguration(Wikipedia)) - async with project: - actual = await project.jinja2_environment.from_string( - "{% for entry in (links | wikipedia) %}{{ entry.content }}{% endfor %}" - ).render_async( - job_context=Context(), - links=links, - ) - - m_get_summary.assert_called_once() - assert extract == actual + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append(ExtensionConfiguration(Wikipedia)) + async with project: + actual = await project.jinja2_environment.from_string( + "{% for entry in (links | wikipedia) %}{{ entry.content }}{% endfor %}" + ).render_async( + job_context=Context(), + links=links, + ) + + m_get_summary.assert_called_once() + assert extract == actual async def test_post_load( self, mocker: MockerFixture, new_temporary_app: App ) -> None: m_populate = mocker.patch("betty.wikipedia._Populator.populate") - project = Project(new_temporary_app) - project.configuration.extensions.append(ExtensionConfiguration(Wikipedia)) - async with project: - await load(project) + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.append(ExtensionConfiguration(Wikipedia)) + async with project: + await load(project) - m_populate.assert_called_once() + m_populate.assert_called_once() diff --git a/betty/tests/extension/wikipedia/test_gui.py b/betty/tests/extension/wikipedia/test_gui.py index 6c023af6e..3dd31e4fd 100644 --- a/betty/tests/extension/wikipedia/test_gui.py +++ b/betty/tests/extension/wikipedia/test_gui.py @@ -14,13 +14,13 @@ class TestWikipediaGuiWidget: async def test_https_with_base_url( self, betty_qtbot: BettyQtBot, new_temporary_app: App ) -> None: - project = Project(new_temporary_app) - project.configuration.extensions.enable(Wikipedia) - async with project: - wikipedia = project.extensions[Wikipedia] - sut = wikipedia.gui_build() - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(Wikipedia) + async with project: + wikipedia = project.extensions[Wikipedia] + sut = wikipedia.gui_build() + betty_qtbot.qtbot.addWidget(sut) + sut.show() - betty_qtbot.set_checked(sut._populate_images, False) - assert not wikipedia.configuration.populate_images + betty_qtbot.set_checked(sut._populate_images, False) + assert not wikipedia.configuration.populate_images diff --git a/betty/tests/gramps/test_loader.py b/betty/tests/gramps/test_loader.py index a63fe2dd4..4a4312efb 100644 --- a/betty/tests/gramps/test_loader.py +++ b/betty/tests/gramps/test_loader.py @@ -28,25 +28,28 @@ class TestGrampsLoader: async def test_load_gramps(self, new_temporary_app: App) -> None: - async with Project(new_temporary_app) as project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = GrampsLoader(project, localizer=DEFAULT_LOCALIZER) await sut.load_gramps(Path(__file__).parent / "assets" / "minimal.gramps") async def test_load_gpkg(self, new_temporary_app: App) -> None: - async with Project(new_temporary_app) as project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = GrampsLoader(project, localizer=DEFAULT_LOCALIZER) await sut.load_gpkg(Path(__file__).parent / "assets" / "minimal.gpkg") async def _load(self, xml: str) -> Ancestry: - async with App.new_temporary() as app, app, Project(app) as project: + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.name = TestGrampsLoader.__name__ - loader = GrampsLoader(project, localizer=DEFAULT_LOCALIZER) - async with TemporaryDirectory() as tree_directory_path_str: - await loader.load_xml( - xml.strip(), - Path(tree_directory_path_str), - ) - return project.ancestry + async with project: + loader = GrampsLoader(project, localizer=DEFAULT_LOCALIZER) + async with TemporaryDirectory() as tree_directory_path_str: + await loader.load_xml( + xml.strip(), + Path(tree_directory_path_str), + ) + return project.ancestry async def _load_partial(self, xml: str) -> Ancestry: return await self._load( @@ -66,14 +69,14 @@ async def _load_partial(self, xml: str) -> Ancestry: ) async def test_load_xml_with_string(self, new_temporary_app: App) -> None: - async with Project(new_temporary_app) as project: + async with Project.new_temporary(new_temporary_app) as project, project: gramps_file_path = Path(__file__).parent / "assets" / "minimal.xml" sut = GrampsLoader(project, localizer=DEFAULT_LOCALIZER) async with aiofiles.open(gramps_file_path) as f: await sut.load_xml(await f.read(), rootname(gramps_file_path)) async def test_load_xml_with_file_path(self, new_temporary_app: App) -> None: - async with Project(new_temporary_app) as project: + async with Project.new_temporary(new_temporary_app) as project, project: gramps_file_path = Path(__file__).parent / "assets" / "minimal.xml" sut = GrampsLoader(project, localizer=DEFAULT_LOCALIZER) await sut.load_xml(gramps_file_path, rootname(gramps_file_path)) diff --git a/betty/tests/gui/test_app.py b/betty/tests/gui/test_app.py index daccdfd31..2e6647d30 100644 --- a/betty/tests/gui/test_app.py +++ b/betty/tests/gui/test_app.py @@ -83,12 +83,11 @@ async def test_open_project_with_invalid_file_should_error( betty_qtbot.assert_exception_error(contained_error_type=AssertionFailed) async def test_open_project_with_valid_file_should_show_project_window( - self, - mocker: MockerFixture, - betty_qtbot: BettyQtBot, + self, mocker: MockerFixture, betty_qtbot: BettyQtBot, tmp_path: Path ) -> None: title = "My First Ancestry Site" configuration = ProjectConfiguration( + tmp_path / "betty.json", title=title, ) await configuration.write() diff --git a/betty/tests/gui/test_project.py b/betty/tests/gui/test_project.py index f21bcebe8..1f27f7908 100644 --- a/betty/tests/gui/test_project.py +++ b/betty/tests/gui/test_project.py @@ -57,27 +57,29 @@ def gui_build(self) -> QWidget: class TestProjectWindow: async def test_show(self, betty_qtbot: BettyQtBot) -> None: - project = Project(betty_qtbot.app) - sut = ProjectWindow(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() - betty_qtbot.assert_window(ProjectWindow) + async with Project.new_temporary(betty_qtbot.app) as project: + sut = ProjectWindow(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() + betty_qtbot.assert_window(ProjectWindow) async def test_autowrite(self, betty_qtbot: BettyQtBot) -> None: - project = Project(betty_qtbot.app) - project.configuration.autowrite = True + async with Project.new_temporary(betty_qtbot.app) as project: + project.configuration.autowrite = True - sut = ProjectWindow(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + sut = ProjectWindow(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - title = "My First Ancestry Site" - project.configuration.title = title + title = "My First Ancestry Site" + project.configuration.title = title - async with aiofiles.open(project.configuration.configuration_file_path) as f: - read_configuration_dump = json.loads(await f.read()) - assert read_configuration_dump == project.configuration.dump() - assert read_configuration_dump["title"] == title + async with aiofiles.open( + project.configuration.configuration_file_path + ) as f: + read_configuration_dump = json.loads(await f.read()) + assert read_configuration_dump == project.configuration.dump() + assert read_configuration_dump["title"] == title async def test_navigate_to_pane( self, @@ -89,16 +91,18 @@ async def test_navigate_to_pane( "betty.project.extension.discover_extension_types", return_value=(DummyUserFacingGuiBuilderExtension,), ) - project = Project(betty_qtbot.app) - sut = ProjectWindow(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with Project.new_temporary(betty_qtbot.app) as project: + sut = ProjectWindow(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - extension_pane_name = f"extension-{DummyUserFacingGuiBuilderExtension.name()}" - extension_pane_selector = sut._pane_selectors[extension_pane_name] - betty_qtbot.mouse_click(extension_pane_selector) - extension_pane = sut._panes[extension_pane_name] - assert extension_pane.isVisible() + extension_pane_name = ( + f"extension-{DummyUserFacingGuiBuilderExtension.name()}" + ) + extension_pane_selector = sut._pane_selectors[extension_pane_name] + betty_qtbot.mouse_click(extension_pane_selector) + extension_pane = sut._panes[extension_pane_name] + assert extension_pane.isVisible() async def test_save_project_as_should_create_duplicate_configuration_file( self, @@ -106,25 +110,27 @@ async def test_save_project_as_should_create_duplicate_configuration_file( betty_qtbot: BettyQtBot, tmp_path: Path, ) -> None: - project = Project(betty_qtbot.app) - configuration = project.configuration - await configuration.write(tmp_path / "betty.json") - sut = ProjectWindow(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() - - save_as_configuration_file_path = tmp_path / "save-as" / "betty.json" - mocker.patch.object( - QFileDialog, - "getSaveFileName", - mocker.MagicMock(return_value=[str(save_as_configuration_file_path), None]), - ) - betty_qtbot.navigate(sut, ["save_project_as_action"]) + async with Project.new_temporary(betty_qtbot.app) as project: + configuration = project.configuration + await configuration.write(tmp_path / "betty.json") + sut = ProjectWindow(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() + + save_as_configuration_file_path = tmp_path / "save-as" / "betty.json" + mocker.patch.object( + QFileDialog, + "getSaveFileName", + mocker.MagicMock( + return_value=[str(save_as_configuration_file_path), None] + ), + ) + betty_qtbot.navigate(sut, ["save_project_as_action"]) - expected_dump = minimize(configuration.dump()) - async with aiofiles.open(save_as_configuration_file_path) as f: - actual_dump = json.loads(await f.read()) - assert actual_dump == expected_dump + expected_dump = minimize(configuration.dump()) + async with aiofiles.open(save_as_configuration_file_path) as f: + actual_dump = json.loads(await f.read()) + assert actual_dump == expected_dump async def test_generate( self, betty_qtbot: BettyQtBot, mocker: MockerFixture @@ -132,13 +138,13 @@ async def test_generate( mocker.patch("betty.generate.generate", new_callable=AsyncMock) mocker.patch("betty.load.load", new_callable=AsyncMock) - project = Project(betty_qtbot.app) - sut = ProjectWindow(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with Project.new_temporary(betty_qtbot.app) as project: + sut = ProjectWindow(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - sut.generate() - betty_qtbot.assert_window(GenerateWindow) + sut.generate() + betty_qtbot.assert_window(GenerateWindow) async def test_generate_action( self, betty_qtbot: BettyQtBot, mocker: MockerFixture @@ -146,42 +152,42 @@ async def test_generate_action( mocker.patch("betty.generate.generate", new_callable=AsyncMock) mocker.patch("betty.load.load", new_callable=AsyncMock) - project = Project(betty_qtbot.app) - sut = ProjectWindow(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with Project.new_temporary(betty_qtbot.app) as project: + sut = ProjectWindow(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - betty_qtbot.navigate(sut, ["generate_action"]) - betty_qtbot.assert_window(GenerateWindow) + betty_qtbot.navigate(sut, ["generate_action"]) + betty_qtbot.assert_window(GenerateWindow) async def test_serve(self, betty_qtbot: BettyQtBot, mocker: MockerFixture) -> None: mocker.patch("betty.serve.BuiltinProjectServer", new=NoOpProjectServer) - project = Project(betty_qtbot.app) - sut = ProjectWindow(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with Project.new_temporary(betty_qtbot.app) as project: + sut = ProjectWindow(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - sut.serve() - betty_qtbot.assert_window(ServeProjectWindow) + sut.serve() + betty_qtbot.assert_window(ServeProjectWindow) async def test_serve_action( self, betty_qtbot: BettyQtBot, mocker: MockerFixture ) -> None: mocker.patch("betty.serve.BuiltinProjectServer", new=NoOpProjectServer) - project = Project(betty_qtbot.app) - sut = ProjectWindow(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with Project.new_temporary(betty_qtbot.app) as project: + sut = ProjectWindow(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - betty_qtbot.navigate(sut, ["serve_action"]) - betty_qtbot.assert_window(ServeProjectWindow) + betty_qtbot.navigate(sut, ["serve_action"]) + betty_qtbot.assert_window(ServeProjectWindow) class TestGenerateHtmlListForm: async def test(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GenerateHtmlListForm(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -194,7 +200,7 @@ async def test(self, betty_qtbot: BettyQtBot) -> None: class TestGeneralPane: async def test_name(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -204,7 +210,7 @@ async def test_name(self, betty_qtbot: BettyQtBot) -> None: assert project.configuration.name == name async def test_title(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -214,7 +220,7 @@ async def test_title(self, betty_qtbot: BettyQtBot) -> None: assert project.configuration.title == title async def test_author(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -224,7 +230,7 @@ async def test_author(self, betty_qtbot: BettyQtBot) -> None: assert project.configuration.author == author async def test_url_with_valid_url(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -234,7 +240,7 @@ async def test_url_with_valid_url(self, betty_qtbot: BettyQtBot) -> None: assert project.configuration.root_path == "my-first-ancestry" async def test_url_with_invalid_url(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -243,7 +249,7 @@ async def test_url_with_invalid_url(self, betty_qtbot: BettyQtBot) -> None: betty_qtbot.assert_invalid(sut._configuration_url) async def test_lifetime_threshold(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -254,7 +260,7 @@ async def test_lifetime_threshold(self, betty_qtbot: BettyQtBot) -> None: async def test_lifetime_threshold_with_non_digit_input( self, betty_qtbot: BettyQtBot ) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -268,7 +274,7 @@ async def test_lifetime_threshold_with_non_digit_input( async def test_lifetime_threshold_with_zero_input( self, betty_qtbot: BettyQtBot ) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -280,7 +286,7 @@ async def test_lifetime_threshold_with_zero_input( ) async def test_debug(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -291,7 +297,7 @@ async def test_debug(self, betty_qtbot: BettyQtBot) -> None: assert not project.configuration.debug async def test_clean_urls(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GeneralPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -304,7 +310,7 @@ async def test_clean_urls(self, betty_qtbot: BettyQtBot) -> None: class TestLocalizationPane: async def test(self, betty_qtbot: BettyQtBot): - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = LocalizationPane(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -333,7 +339,7 @@ async def test_enable_extension_with_unmet_enable_requirement( betty_qtbot: BettyQtBot, tmp_path: Path, ) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = ExtensionPane( project, ExtensionPaneTestExtensionWithUnmetEnableRequirement ) @@ -348,19 +354,19 @@ async def test_disable_extension_with_unmet_disable_requirement( betty_qtbot: BettyQtBot, tmp_path: Path, ) -> None: - project = Project(betty_qtbot.app) - project.configuration.extensions.enable( - ExtensionPaneTestExtensionWithUnmetDisableRequirement - ) - async with project: - sut = ExtensionPane( - project, ExtensionPaneTestExtensionWithUnmetDisableRequirement + async with Project.new_temporary(betty_qtbot.app) as project: + project.configuration.extensions.enable( + ExtensionPaneTestExtensionWithUnmetDisableRequirement ) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with project: + sut = ExtensionPane( + project, ExtensionPaneTestExtensionWithUnmetDisableRequirement + ) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - assert sut._extension_enabled.isChecked() - betty_qtbot.assert_not_interactive(sut._extension_enabled) + assert sut._extension_enabled.isChecked() + betty_qtbot.assert_not_interactive(sut._extension_enabled) # @todo Re-enable this as part of https://github.com/bartfeenstra/betty/issues/1625 # async def test_enable_extension( @@ -368,7 +374,7 @@ async def test_disable_extension_with_unmet_disable_requirement( # betty_qtbot: BettyQtBot, # tmp_path: Path, # ) -> None: - # async with Project(betty_qtbot.app) as project: + # async with Project.new_temporary(betty_qtbot.app) as project, project: # sut = ExtensionPane(project, DummyUserFacingGuiBuilderExtension) # betty_qtbot.qtbot.addWidget(sut) # sut.show() @@ -384,18 +390,18 @@ async def test_disable_extension( betty_qtbot: BettyQtBot, tmp_path: Path, ) -> None: - project = Project(betty_qtbot.app) - project.configuration.extensions.enable(DummyUserFacingGuiBuilderExtension) - async with project: - sut = ExtensionPane(project, DummyUserFacingGuiBuilderExtension) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with Project.new_temporary(betty_qtbot.app) as project: + project.configuration.extensions.enable(DummyUserFacingGuiBuilderExtension) + async with project: + sut = ExtensionPane(project, DummyUserFacingGuiBuilderExtension) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - betty_qtbot.assert_interactive(sut._extension_gui) - extension_enable_checkbox = sut._extension_enabled - betty_qtbot.assert_interactive(extension_enable_checkbox) - extension_enable_checkbox.click() - betty_qtbot.assert_not_interactive(sut._extension_gui) + betty_qtbot.assert_interactive(sut._extension_gui) + extension_enable_checkbox = sut._extension_enabled + betty_qtbot.assert_interactive(extension_enable_checkbox) + extension_enable_checkbox.click() + betty_qtbot.assert_not_interactive(sut._extension_gui) class TestGenerateWindow: @@ -409,7 +415,7 @@ async def _generate(app: App) -> None: mocker.patch("betty.generate.generate", new_callable=lambda: _generate) - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GenerateWindow(project) betty_qtbot.qtbot.addWidget(sut) @@ -424,7 +430,7 @@ async def test_serve_button_should_open_serve_window( ) -> None: mocker.patch("betty.extension.demo.DemoServer", new=NoOpProjectServer) mocker.patch("betty.serve.BuiltinProjectServer", new=NoOpProjectServer) - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GenerateWindow(project) betty_qtbot.qtbot.addWidget(sut) @@ -441,7 +447,7 @@ async def _generate(app: App) -> None: mocker.patch("betty.generate.generate", new_callable=lambda: _generate) - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = GenerateWindow(project) betty_qtbot.qtbot.addWidget(sut) @@ -451,7 +457,7 @@ async def _generate(app: App) -> None: class TestLocalesConfigurationWidget: async def test_add_locale(self, betty_qtbot: BettyQtBot) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = LocalesConfigurationWidget(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -461,34 +467,36 @@ async def test_add_locale(self, betty_qtbot: BettyQtBot) -> None: async def test_remove_locale(self, betty_qtbot: BettyQtBot) -> None: locale = "de-DE" - project = Project(betty_qtbot.app) - project.configuration.locales.append( - LocaleConfiguration("nl-NL"), - LocaleConfiguration(locale), - ) - async with project: - sut = LocalesConfigurationWidget(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() - betty_qtbot.mouse_click(sut._remove_buttons[locale]) + async with Project.new_temporary(betty_qtbot.app) as project: + project.configuration.locales.append( + LocaleConfiguration("nl-NL"), + LocaleConfiguration(locale), + ) + async with project: + sut = LocalesConfigurationWidget(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() + betty_qtbot.mouse_click(sut._remove_buttons[locale]) - assert locale not in project.configuration.locales + assert locale not in project.configuration.locales async def test_default_locale(self, betty_qtbot: BettyQtBot) -> None: locale = "de-DE" - project = Project(betty_qtbot.app) - project.configuration.locales.append( - LocaleConfiguration("nl-NL"), - LocaleConfiguration(locale), - ) - async with project: - sut = LocalesConfigurationWidget(project) - betty_qtbot.qtbot.addWidget(sut) - sut.show() + async with Project.new_temporary(betty_qtbot.app) as project: + project.configuration.locales.append( + LocaleConfiguration("nl-NL"), + LocaleConfiguration(locale), + ) + async with project: + sut = LocalesConfigurationWidget(project) + betty_qtbot.qtbot.addWidget(sut) + sut.show() - sut._default_buttons[locale].click() + sut._default_buttons[locale].click() - assert project.configuration.locales.default == LocaleConfiguration(locale) + assert project.configuration.locales.default == LocaleConfiguration( + locale + ) class TestAddLocaleWindow: @@ -496,7 +504,7 @@ async def test_without_alias( self, betty_qtbot: BettyQtBot, ) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = AddLocaleWindow(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -514,7 +522,7 @@ async def test_with_valid_alias( self, betty_qtbot: BettyQtBot, ) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = AddLocaleWindow(project) betty_qtbot.qtbot.addWidget(sut) sut.show() @@ -534,7 +542,7 @@ async def test_with_invalid_alias( self, betty_qtbot: BettyQtBot, ) -> None: - async with Project(betty_qtbot.app) as project: + async with Project.new_temporary(betty_qtbot.app) as project, project: sut = AddLocaleWindow(project) betty_qtbot.qtbot.addWidget(sut) sut.show() diff --git a/betty/tests/gui/test_serve.py b/betty/tests/gui/test_serve.py index 9065e8c81..f14e0875b 100644 --- a/betty/tests/gui/test_serve.py +++ b/betty/tests/gui/test_serve.py @@ -35,7 +35,7 @@ async def test( ) -> None: mocker.patch("betty.extension.demo.DemoServer", new=NoOpProjectServer) mocker.patch("betty.serve.BuiltinProjectServer", new=NoOpProjectServer) - async with Project(new_temporary_app) as project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = ServeProjectWindow(project) betty_qtbot.qtbot.addWidget(sut) sut.show() diff --git a/betty/tests/json/test_schema.py b/betty/tests/json/test_schema.py index 50c490668..1a341debc 100644 --- a/betty/tests/json/test_schema.py +++ b/betty/tests/json/test_schema.py @@ -24,7 +24,7 @@ async def test_build(self, clean_urls: bool, new_temporary_app: App) -> None: ) as f: json_schema_schema = stdjson.loads(await f.read()) - async with Project(new_temporary_app) as project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = Schema(project) schema = await sut.build() jsonschema.validate(json_schema_schema, schema) diff --git a/betty/tests/model/test_ancestry.py b/betty/tests/model/test_ancestry.py index af219d3bd..6451b6443 100644 --- a/betty/tests/model/test_ancestry.py +++ b/betty/tests/model/test_ancestry.py @@ -54,8 +54,7 @@ async def assert_dumps_linked_data( dumpable: LinkedDataDumpable, schema_definition: str | None = None ) -> DictDump[Dump]: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary(app) as project: project.configuration.locales["en-US"].alias = "en" project.configuration.locales.append( LocaleConfiguration( @@ -71,7 +70,8 @@ async def assert_dumps_linked_data( actual_to_be_validated = copy(actual) actual_to_be_validated["$schema"] = ( project.static_url_generator.generate( - f"schema.json#/definitions/{schema_definition}", absolute=True + f"schema.json#/definitions/{schema_definition}", + absolute=True, ) ) schema = Schema(project) diff --git a/betty/tests/project/test___init__.py b/betty/tests/project/test___init__.py index c53947858..e3291bfba 100644 --- a/betty/tests/project/test___init__.py +++ b/betty/tests/project/test___init__.py @@ -41,6 +41,7 @@ from betty.typing import Void if TYPE_CHECKING: + from pathlib import Path from betty.app import App from collections.abc import Sequence from betty.serde.dump import Dump, VoidableDump @@ -792,54 +793,54 @@ async def test_load_item(self) -> None: class TestProjectConfiguration: - async def test_name(self) -> None: - sut = ProjectConfiguration() + async def test_name(self, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") name = "MyFirstBettySite" sut.name = name assert sut.name == name - async def test_base_url(self) -> None: - sut = ProjectConfiguration() + async def test_base_url(self, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") base_url = "https://example.com" sut.base_url = base_url assert base_url == sut.base_url - async def test_base_url_without_scheme_should_error(self) -> None: - sut = ProjectConfiguration() + async def test_base_url_without_scheme_should_error(self, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") with pytest.raises(AssertionFailed): sut.base_url = "/" - async def test_base_url_without_path_should_error(self) -> None: - sut = ProjectConfiguration() + async def test_base_url_without_path_should_error(self, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") with pytest.raises(AssertionFailed): sut.base_url = "file://" - async def test_root_path(self) -> None: - sut = ProjectConfiguration() + async def test_root_path(self, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") configured_root_path = "/betty/" expected_root_path = "betty" sut.root_path = configured_root_path assert expected_root_path == sut.root_path - async def test_clean_urls(self) -> None: - sut = ProjectConfiguration() + async def test_clean_urls(self, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") clean_urls = True sut.clean_urls = clean_urls assert clean_urls == sut.clean_urls - async def test_author_without_author(self) -> None: - sut = ProjectConfiguration() + async def test_author_without_author(self, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") assert sut.author is None - async def test_author_with_author(self) -> None: - sut = ProjectConfiguration() + async def test_author_with_author(self, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") author = "Bart" sut.author = author assert author == sut.author - async def test_load_should_load_minimal(self) -> None: - dump: Any = ProjectConfiguration().dump() - sut = ProjectConfiguration() + async def test_load_should_load_minimal(self, tmp_path: Path) -> None: + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.load(dump) assert dump["base_url"] == sut.base_url assert sut.title == "Betty" @@ -848,51 +849,51 @@ async def test_load_should_load_minimal(self) -> None: assert sut.root_path == "" assert not sut.clean_urls - async def test_load_should_load_name(self) -> None: + async def test_load_should_load_name(self, tmp_path: Path) -> None: name = "MyFirstBettySite" - dump: Any = ProjectConfiguration().dump() + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["name"] = name - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.load(dump) assert sut.name == name - async def test_load_should_load_title(self) -> None: + async def test_load_should_load_title(self, tmp_path: Path) -> None: title = "My first Betty site" - dump: Any = ProjectConfiguration().dump() + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["title"] = title - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.load(dump) assert sut.title == title - async def test_load_should_load_author(self) -> None: + async def test_load_should_load_author(self, tmp_path: Path) -> None: author = "Bart" - dump: Any = ProjectConfiguration().dump() + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["author"] = author - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.load(dump) assert sut.author == author - async def test_load_should_load_locale_locale(self) -> None: + async def test_load_should_load_locale_locale(self, tmp_path: Path) -> None: locale = "nl-NL" - dump = ProjectConfiguration().dump() + dump = ProjectConfiguration(tmp_path / "betty.json").dump() dump["locales"] = { locale: {}, } - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.load(dump) assert sut.locales == LocaleConfigurationMapping([LocaleConfiguration(locale)]) - async def test_load_should_load_locale_alias(self) -> None: + async def test_load_should_load_locale_alias(self, tmp_path: Path) -> None: locale = "nl-NL" alias = "nl" locale_config = { "alias": alias, } - dump: Any = ProjectConfiguration().dump() + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["locales"] = { locale: locale_config, } - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.load(dump) assert sut.locales == LocaleConfigurationMapping( [ @@ -903,20 +904,20 @@ async def test_load_should_load_locale_alias(self) -> None: ] ) - async def test_load_should_root_path(self) -> None: + async def test_load_should_root_path(self, tmp_path: Path) -> None: configured_root_path = "/betty/" expected_root_path = "betty" - dump: Any = ProjectConfiguration().dump() + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["root_path"] = configured_root_path - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.load(dump) assert sut.root_path == expected_root_path - async def test_load_should_clean_urls(self) -> None: + async def test_load_should_clean_urls(self, tmp_path: Path) -> None: clean_urls = True - dump: Any = ProjectConfiguration().dump() + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["clean_urls"] = clean_urls - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.load(dump) assert sut.clean_urls == clean_urls @@ -927,15 +928,17 @@ async def test_load_should_clean_urls(self) -> None: False, ], ) - async def test_load_should_load_debug(self, debug: bool) -> None: - dump: Any = ProjectConfiguration().dump() + async def test_load_should_load_debug(self, debug: bool, tmp_path: Path) -> None: + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["debug"] = debug - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.load(dump) assert sut.debug == debug - async def test_load_should_load_one_extension_with_configuration(self) -> None: - dump: Any = ProjectConfiguration().dump() + async def test_load_should_load_one_extension_with_configuration( + self, tmp_path: Path + ) -> None: + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() extension_configuration = { "check": False, } @@ -944,7 +947,7 @@ async def test_load_should_load_one_extension_with_configuration(self) -> None: "configuration": extension_configuration, }, } - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") expected = ExtensionConfiguration( _DummyConfigurableExtension, extension_configuration=_DummyConfigurableExtensionConfiguration(), @@ -952,53 +955,59 @@ async def test_load_should_load_one_extension_with_configuration(self) -> None: sut.load(dump) assert sut.extensions[_DummyConfigurableExtension] == expected - async def test_load_should_load_one_extension_without_configuration(self) -> None: - dump: Any = ProjectConfiguration().dump() + async def test_load_should_load_one_extension_without_configuration( + self, tmp_path: Path + ) -> None: + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["extensions"] = { _DummyNonConfigurableExtension.name(): {}, } - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") expected = ExtensionConfiguration(_DummyNonConfigurableExtension) sut.load(dump) assert sut.extensions[_DummyNonConfigurableExtension] == expected async def test_load_extension_with_invalid_configuration_should_raise_error( - self, + self, tmp_path: Path ) -> None: - dump: Any = ProjectConfiguration().dump() + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["extensions"] = { _DummyConfigurableExtension.name(): 1337, } - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") with raises_error(error_type=AssertionFailed): sut.load(dump) - async def test_load_unknown_extension_type_name_should_error(self) -> None: - dump: Any = ProjectConfiguration().dump() + async def test_load_unknown_extension_type_name_should_error( + self, tmp_path: Path + ) -> None: + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["extensions"] = { "non.existent.type": {}, } - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") with raises_error(error_type=AssertionFailed): sut.load(dump) - async def test_load_not_an_extension_type_name_should_error(self) -> None: - dump: Any = ProjectConfiguration().dump() + async def test_load_not_an_extension_type_name_should_error( + self, tmp_path: Path + ) -> None: + dump: Any = ProjectConfiguration(tmp_path / "betty.json").dump() dump["extensions"] = { "%s.%s" % (self.__class__.__module__, self.__class__.__name__): {}, } - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") with raises_error(error_type=AssertionFailed): sut.load(dump) - async def test_load_should_error_if_invalid_config(self) -> None: + async def test_load_should_error_if_invalid_config(self, tmp_path: Path) -> None: dump: Dump = {} - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") with raises_error(error_type=AssertionFailed): sut.load(dump) - async def test_dump_should_dump_minimal(self) -> None: - sut = ProjectConfiguration() + async def test_dump_should_dump_minimal(self, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") dump: Any = sut.dump() assert dump["base_url"] == sut.base_url assert sut.title == "Betty" @@ -1007,31 +1016,31 @@ async def test_dump_should_dump_minimal(self) -> None: assert sut.root_path == "" assert not sut.clean_urls - async def test_dump_should_dump_title(self) -> None: + async def test_dump_should_dump_title(self, tmp_path: Path) -> None: title = "My first Betty site" - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.title = title dump: Any = sut.dump() assert title == dump["title"] - async def test_dump_should_dump_name(self) -> None: + async def test_dump_should_dump_name(self, tmp_path: Path) -> None: name = "MyFirstBettySite" - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.name = name dump: Any = sut.dump() assert dump["name"] == name - async def test_dump_should_dump_author(self) -> None: + async def test_dump_should_dump_author(self, tmp_path: Path) -> None: author = "Bart" - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.author = author dump: Any = sut.dump() assert author == dump["author"] - async def test_dump_should_dump_locale_locale(self) -> None: + async def test_dump_should_dump_locale_locale(self, tmp_path: Path) -> None: locale = "nl-NL" locale_configuration = LocaleConfiguration(locale) - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.locales.append(locale_configuration) sut.locales.remove("en-US") dump: Any = sut.dump() @@ -1039,14 +1048,14 @@ async def test_dump_should_dump_locale_locale(self) -> None: locale: {}, } - async def test_dump_should_dump_locale_alias(self) -> None: + async def test_dump_should_dump_locale_alias(self, tmp_path: Path) -> None: locale = "nl-NL" alias = "nl" locale_configuration = LocaleConfiguration( locale, alias=alias, ) - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.locales.append(locale_configuration) sut.locales.remove("en-US") dump: Any = sut.dump() @@ -1056,16 +1065,16 @@ async def test_dump_should_dump_locale_alias(self) -> None: }, } - async def test_dump_should_dump_root_path(self) -> None: + async def test_dump_should_dump_root_path(self, tmp_path: Path) -> None: root_path = "betty" - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.root_path = root_path dump: Any = sut.dump() assert root_path == dump["root_path"] - async def test_dump_should_dump_clean_urls(self) -> None: + async def test_dump_should_dump_clean_urls(self, tmp_path: Path) -> None: clean_urls = True - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") sut.clean_urls = clean_urls dump: Any = sut.dump() assert clean_urls == dump["clean_urls"] @@ -1077,14 +1086,16 @@ async def test_dump_should_dump_clean_urls(self) -> None: False, ], ) - async def test_dump_should_dump_debug(self, debug: bool) -> None: - sut = ProjectConfiguration() + async def test_dump_should_dump_debug(self, debug: bool, tmp_path: Path) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") sut.debug = debug dump: Any = sut.dump() assert debug == dump["debug"] - async def test_dump_should_dump_one_extension_with_configuration(self) -> None: - sut = ProjectConfiguration() + async def test_dump_should_dump_one_extension_with_configuration( + self, tmp_path: Path + ) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") sut.extensions.append( ExtensionConfiguration( _DummyConfigurableExtension, @@ -1100,8 +1111,10 @@ async def test_dump_should_dump_one_extension_with_configuration(self) -> None: } assert expected == dump["extensions"][_DummyConfigurableExtension.name()] - async def test_dump_should_dump_one_extension_without_configuration(self) -> None: - sut = ProjectConfiguration() + async def test_dump_should_dump_one_extension_without_configuration( + self, tmp_path: Path + ) -> None: + sut = ProjectConfiguration(tmp_path / "betty.json") sut.extensions.append(ExtensionConfiguration(_DummyNonConfigurableExtension)) dump: Any = sut.dump() expected = { @@ -1109,9 +1122,9 @@ async def test_dump_should_dump_one_extension_without_configuration(self) -> Non } assert expected == dump["extensions"][_DummyNonConfigurableExtension.name()] - async def test_dump_should_error_if_invalid_config(self) -> None: + async def test_dump_should_error_if_invalid_config(self, tmp_path: Path) -> None: dump: Dump = {} - sut = ProjectConfiguration() + sut = ProjectConfiguration(tmp_path / "betty.json") with raises_error(error_type=AssertionFailed): sut.load(dump) @@ -1204,224 +1217,226 @@ def default_configuration(cls) -> _ConfigurableExtensionConfiguration: class TestProject: async def test_extensions_with_one_extension(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - sut.configuration.extensions.append( - ExtensionConfiguration(_NonConfigurableExtension) - ) - async with sut: - assert isinstance( - sut.extensions[_NonConfigurableExtension], _NonConfigurableExtension + async with Project.new_temporary(new_temporary_app) as sut: + sut.configuration.extensions.append( + ExtensionConfiguration(_NonConfigurableExtension) ) + async with sut: + assert isinstance( + sut.extensions[_NonConfigurableExtension], _NonConfigurableExtension + ) async def test_extensions_with_one_configurable_extension( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - check = 1337 - sut.configuration.extensions.append( - ExtensionConfiguration( - _ConfigurableExtension, - extension_configuration=_ConfigurableExtensionConfiguration( - check=check, - ), - ) - ) - async with sut: - assert isinstance( - sut.extensions[_ConfigurableExtension], _ConfigurableExtension + async with Project.new_temporary(new_temporary_app) as sut: + check = 1337 + sut.configuration.extensions.append( + ExtensionConfiguration( + _ConfigurableExtension, + extension_configuration=_ConfigurableExtensionConfiguration( + check=check, + ), + ) ) - assert check == sut.extensions[_ConfigurableExtension].configuration.check + async with sut: + assert isinstance( + sut.extensions[_ConfigurableExtension], _ConfigurableExtension + ) + assert ( + check == sut.extensions[_ConfigurableExtension].configuration.check + ) async def test_extensions_with_one_extension_with_single_chained_dependency( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - sut.configuration.extensions.append( - ExtensionConfiguration(_DependsOnNonConfigurableExtensionExtensionExtension) - ) - async with sut: - carrier: list[_TrackableExtension] = [] - await sut.dispatcher.dispatch(_Tracker)(carrier) - assert len(carrier) == 3 - assert isinstance(carrier[0], _NonConfigurableExtension) - assert isinstance(carrier[1], _DependsOnNonConfigurableExtensionExtension) - assert isinstance( - carrier[2], _DependsOnNonConfigurableExtensionExtensionExtension + async with Project.new_temporary(new_temporary_app) as sut: + sut.configuration.extensions.append( + ExtensionConfiguration( + _DependsOnNonConfigurableExtensionExtensionExtension + ) ) + async with sut: + carrier: list[_TrackableExtension] = [] + await sut.dispatcher.dispatch(_Tracker)(carrier) + assert len(carrier) == 3 + assert isinstance(carrier[0], _NonConfigurableExtension) + assert isinstance( + carrier[1], _DependsOnNonConfigurableExtensionExtension + ) + assert isinstance( + carrier[2], _DependsOnNonConfigurableExtensionExtensionExtension + ) async def test_extensions_with_multiple_extensions_with_duplicate_dependencies( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - sut.configuration.extensions.append( - ExtensionConfiguration(_DependsOnNonConfigurableExtensionExtension) - ) - sut.configuration.extensions.append( - ExtensionConfiguration(_AlsoDependsOnNonConfigurableExtensionExtension) - ) - async with sut: - carrier: list[_TrackableExtension] = [] - await sut.dispatcher.dispatch(_Tracker)(carrier) - assert len(carrier) == 3 - assert isinstance(carrier[0], _NonConfigurableExtension) - assert _DependsOnNonConfigurableExtensionExtension in [ - type(extension) for extension in carrier - ] - assert _AlsoDependsOnNonConfigurableExtensionExtension in [ - type(extension) for extension in carrier - ] + async with Project.new_temporary(new_temporary_app) as sut: + sut.configuration.extensions.append( + ExtensionConfiguration(_DependsOnNonConfigurableExtensionExtension) + ) + sut.configuration.extensions.append( + ExtensionConfiguration(_AlsoDependsOnNonConfigurableExtensionExtension) + ) + async with sut: + carrier: list[_TrackableExtension] = [] + await sut.dispatcher.dispatch(_Tracker)(carrier) + assert len(carrier) == 3 + assert isinstance(carrier[0], _NonConfigurableExtension) + assert _DependsOnNonConfigurableExtensionExtension in [ + type(extension) for extension in carrier + ] + assert _AlsoDependsOnNonConfigurableExtensionExtension in [ + type(extension) for extension in carrier + ] async def test_extensions_with_multiple_extensions_with_cyclic_dependencies( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - sut.configuration.extensions.append( - ExtensionConfiguration(_CyclicDependencyOneExtension) - ) - async with sut: - with pytest.raises(CyclicDependencyError): # noqa PT012 - sut.extensions # noqa B018 + async with Project.new_temporary(new_temporary_app) as sut: + sut.configuration.extensions.append( + ExtensionConfiguration(_CyclicDependencyOneExtension) + ) + async with sut: + with pytest.raises(CyclicDependencyError): # noqa PT012 + sut.extensions # noqa B018 async def test_extensions_with_comes_before_with_other_extension( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - sut.configuration.extensions.append( - ExtensionConfiguration(_NonConfigurableExtension) - ) - sut.configuration.extensions.append( - ExtensionConfiguration(_ComesBeforeNonConfigurableExtensionExtension) - ) - async with sut: - carrier: list[_TrackableExtension] = [] - await sut.dispatcher.dispatch(_Tracker)(carrier) - assert len(carrier) == 2 - assert isinstance(carrier[0], _ComesBeforeNonConfigurableExtensionExtension) - assert isinstance(carrier[1], _NonConfigurableExtension) + async with Project.new_temporary(new_temporary_app) as sut: + sut.configuration.extensions.append( + ExtensionConfiguration(_NonConfigurableExtension) + ) + sut.configuration.extensions.append( + ExtensionConfiguration(_ComesBeforeNonConfigurableExtensionExtension) + ) + async with sut: + carrier: list[_TrackableExtension] = [] + await sut.dispatcher.dispatch(_Tracker)(carrier) + assert len(carrier) == 2 + assert isinstance( + carrier[0], _ComesBeforeNonConfigurableExtensionExtension + ) + assert isinstance(carrier[1], _NonConfigurableExtension) async def test_extensions_with_comes_before_without_other_extension( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - sut.configuration.extensions.append( - ExtensionConfiguration(_ComesBeforeNonConfigurableExtensionExtension) - ) - async with sut: - carrier: list[_TrackableExtension] = [] - await sut.dispatcher.dispatch(_Tracker)(carrier) - assert len(carrier) == 1 - assert isinstance(carrier[0], _ComesBeforeNonConfigurableExtensionExtension) + async with Project.new_temporary(new_temporary_app) as sut: + sut.configuration.extensions.append( + ExtensionConfiguration(_ComesBeforeNonConfigurableExtensionExtension) + ) + async with sut: + carrier: list[_TrackableExtension] = [] + await sut.dispatcher.dispatch(_Tracker)(carrier) + assert len(carrier) == 1 + assert isinstance( + carrier[0], _ComesBeforeNonConfigurableExtensionExtension + ) async def test_extensions_with_comes_after_with_other_extension( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - sut.configuration.extensions.append( - ExtensionConfiguration(_ComesAfterNonConfigurableExtensionExtension) - ) - sut.configuration.extensions.append( - ExtensionConfiguration(_NonConfigurableExtension) - ) - async with sut: - carrier: list[_TrackableExtension] = [] - await sut.dispatcher.dispatch(_Tracker)(carrier) - assert len(carrier) == 2 - assert isinstance(carrier[0], _NonConfigurableExtension) - assert isinstance(carrier[1], _ComesAfterNonConfigurableExtensionExtension) + async with Project.new_temporary(new_temporary_app) as sut: + sut.configuration.extensions.append( + ExtensionConfiguration(_ComesAfterNonConfigurableExtensionExtension) + ) + sut.configuration.extensions.append( + ExtensionConfiguration(_NonConfigurableExtension) + ) + async with sut: + carrier: list[_TrackableExtension] = [] + await sut.dispatcher.dispatch(_Tracker)(carrier) + assert len(carrier) == 2 + assert isinstance(carrier[0], _NonConfigurableExtension) + assert isinstance( + carrier[1], _ComesAfterNonConfigurableExtensionExtension + ) async def test_extensions_with_comes_after_without_other_extension( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - sut.configuration.extensions.append( - ExtensionConfiguration(_ComesAfterNonConfigurableExtensionExtension) - ) - async with sut: - carrier: list[_TrackableExtension] = [] - await sut.dispatcher.dispatch(_Tracker)(carrier) - assert len(carrier) == 1 - assert isinstance(carrier[0], _ComesAfterNonConfigurableExtensionExtension) + async with Project.new_temporary(new_temporary_app) as sut: + sut.configuration.extensions.append( + ExtensionConfiguration(_ComesAfterNonConfigurableExtensionExtension) + ) + async with sut: + carrier: list[_TrackableExtension] = [] + await sut.dispatcher.dispatch(_Tracker)(carrier) + assert len(carrier) == 1 + assert isinstance( + carrier[0], _ComesAfterNonConfigurableExtensionExtension + ) async def test_ancestry_with___init___ancestry( self, new_temporary_app: App ) -> None: ancestry = Ancestry() - sut = Project(new_temporary_app, ancestry=ancestry) - async with sut: + async with Project.new_temporary( + new_temporary_app, ancestry=ancestry + ) as sut, sut: assert sut.ancestry is ancestry async def test_ancestry_without___init___ancestry( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: sut.ancestry # noqa B018 async def test_app(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: assert sut.app is new_temporary_app async def test_assets(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: assert len(sut.assets.assets_directory_paths) > 0 async def test_discover_extension_types(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: assert len(sut.discover_extension_types()) > 0 async def test_dispatcher(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: sut.dispatcher # noqa B018 async def test_entity_types(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: assert len(sut.entity_types) > 0 async def test_event_types(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: assert len(sut.event_types) > 0 async def test_jinja2_environment(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: sut.jinja2_environment # noqa B018 async def test_localizers(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: assert len(list(sut.localizers.locales)) > 0 async def test_name_with_configuration_name(self, new_temporary_app: App) -> None: name = "hello-world" - sut = Project(new_temporary_app) - sut.configuration.name = name - async with sut: - assert sut.name == name + async with Project.new_temporary(new_temporary_app) as sut: + sut.configuration.name = name + async with sut: + assert sut.name == name async def test_name_without_configuration_name( self, new_temporary_app: App ) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: sut.name # noqa B018 async def test_renderer(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: sut.renderer # noqa B018 async def test_static_url_generator(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: sut.static_url_generator # noqa B018 async def test_url_generator(self, new_temporary_app: App) -> None: - sut = Project(new_temporary_app) - async with sut: + async with Project.new_temporary(new_temporary_app) as sut, sut: sut.url_generator # noqa B018 diff --git a/betty/tests/project/test_extension.py b/betty/tests/project/test_extension.py index 5ebc4b5f9..ff59d8fd2 100644 --- a/betty/tests/project/test_extension.py +++ b/betty/tests/project/test_extension.py @@ -37,7 +37,7 @@ async def multiply(self, term: int) -> Any: return self._multiplier * term async def test(self, new_temporary_app: App) -> None: - async with Project(new_temporary_app) as project: + async with Project.new_temporary(new_temporary_app) as project, project: extensions = ListExtensions( [ [ diff --git a/betty/tests/test_config.py b/betty/tests/test_config.py index 7a8187225..6d0fcf1f9 100644 --- a/betty/tests/test_config.py +++ b/betty/tests/test_config.py @@ -34,8 +34,10 @@ class TestFileBasedConfiguration: - async def test_configuration_file_path_should_error_unknown_format(self) -> None: - configuration = FileBasedConfiguration() + async def test_configuration_file_path_should_error_unknown_format( + self, tmp_path: Path + ) -> None: + configuration = FileBasedConfiguration(tmp_path / "betty.json") with ( NamedTemporaryFile(mode="r+", suffix=".abc") as f, pytest.raises(FormatError), diff --git a/betty/tests/test_documentation.py b/betty/tests/test_documentation.py index 65843cfd2..e047c2030 100644 --- a/betty/tests/test_documentation.py +++ b/betty/tests/test_documentation.py @@ -73,7 +73,7 @@ async def test_should_contain_cli_help(self) -> None: ], ) async def test_should_contain_valid_configuration( - self, language: str, serde_format: Format + self, language: str, serde_format: Format, tmp_path: Path ) -> None: async with aiofiles.open( ROOT_DIRECTORY_PATH @@ -91,5 +91,5 @@ async def test_should_contain_valid_configuration( assert match is not None dump = match[1] assert dump is not None - configuration = ProjectConfiguration() + configuration = ProjectConfiguration(tmp_path / "betty.json") configuration.load(serde_format.load(dump)) diff --git a/betty/tests/test_generate.py b/betty/tests/test_generate.py index 9bb571780..5162a52d8 100644 --- a/betty/tests/test_generate.py +++ b/betty/tests/test_generate.py @@ -44,8 +44,9 @@ async def entity_types(self) -> set[type[Entity]]: class TestGenerate: async def test_html_lang(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.locales["en-US"].alias = "en" project.configuration.locales.append( LocaleConfiguration( @@ -62,8 +63,9 @@ async def test_html_lang(self) -> None: assert ' None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.locales.replace( LocaleConfiguration( "nl-NL", @@ -85,8 +87,9 @@ async def test_root_redirect(self) -> None: assert meta_redirect in await f.read() async def test_links(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.locales.replace( LocaleConfiguration( "nl-NL", @@ -125,8 +128,9 @@ async def test_links(self) -> None: ) async def test_links_for_entity_pages(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.locales.replace( LocaleConfiguration( "nl-NL", @@ -143,7 +147,9 @@ async def test_links_for_entity_pages(self) -> None: await generate(project) async with aiofiles.open( await assert_betty_html( - project, f"/nl/person/{person.id}/index.html", check_links=True + project, + f"/nl/person/{person.id}/index.html", + check_links=True, ) ) as f: html = await f.read() @@ -161,7 +167,9 @@ async def test_links_for_entity_pages(self) -> None: ) async with aiofiles.open( await assert_betty_html( - project, f"/en/person/{person.id}/index.html", check_links=True + project, + f"/en/person/{person.id}/index.html", + check_links=True, ) ) as f: html = await f.read() @@ -180,8 +188,9 @@ async def test_links_for_entity_pages(self) -> None: async def test_third_party_entities(self) -> None: entity_type = _ThirdPartyEntity - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.extensions.append( ExtensionConfiguration(_ThirdPartyExtension) ) @@ -205,8 +214,9 @@ async def test_third_party_entities(self) -> None: async def test_third_party_entity(self) -> None: entity_type = _ThirdPartyEntity - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.extensions.append( ExtensionConfiguration(_ThirdPartyExtension) ) @@ -227,8 +237,9 @@ async def test_third_party_entity(self) -> None: ) async def test_files(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.configuration.entity_types.append( EntityTypeConfiguration( entity_type=File, @@ -241,8 +252,9 @@ async def test_files(self) -> None: await assert_betty_json(project, "/file/index.json", "fileCollection") async def test_file(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: with NamedTemporaryFile() as f: file = File( id="FILE1", @@ -257,16 +269,17 @@ async def test_file(self) -> None: await assert_betty_json(project, "/file/%s/index.json" % file.id) async def test_places(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) - async with project: - await generate(project) - await assert_betty_html(project, "/place/index.html", check_links=True) - await assert_betty_json(project, "/place/index.json") + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project, project: + await generate(project) + await assert_betty_html(project, "/place/index.html", check_links=True) + await assert_betty_json(project, "/place/index.json") async def test_place(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: place = Place( id="PLACE1", names=[PlaceName(name="one")], @@ -280,17 +293,18 @@ async def test_place(self) -> None: await assert_betty_json(project, "/place/%s/index.json" % place.id) async def test_people(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) - async with project: - await generate(project) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project, project: + await generate(project) await assert_betty_html(project, "/person/index.html", check_links=True) await assert_betty_json(project, "/person/index.json") async def test_person(self) -> None: person = Person(id="PERSON1") - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: project.ancestry.add(person) async with project: await generate(project) @@ -305,16 +319,17 @@ async def test_person(self) -> None: ) async def test_events(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) - async with project: - await generate(project) - await assert_betty_html(project, "/event/index.html", check_links=True) - await assert_betty_json(project, "/event/index.json", "eventCollection") + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project, project: + await generate(project) + await assert_betty_html(project, "/event/index.html", check_links=True) + await assert_betty_json(project, "/event/index.json", "eventCollection") async def test_event(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: event = Event( id="EVENT1", event_type=Birth, @@ -322,14 +337,17 @@ async def test_event(self) -> None: project.ancestry.add(event) async with project: await generate(project) - await assert_betty_html( - project, "/event/%s/index.html" % event.id, check_links=True - ) - await assert_betty_json(project, "/event/%s/index.json" % event.id, "event") + await assert_betty_html( + project, "/event/%s/index.html" % event.id, check_links=True + ) + await assert_betty_json( + project, "/event/%s/index.json" % event.id, "event" + ) async def test_citation(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: source = Source("A Little Birdie") citation = Citation( id="CITATION1", @@ -338,22 +356,27 @@ async def test_citation(self) -> None: project.ancestry.add(citation, source) async with project: await generate(project) - await assert_betty_html( - project, "/citation/%s/index.html" % citation.id, check_links=True - ) - await assert_betty_json(project, "/citation/%s/index.json" % citation.id) + await assert_betty_html( + project, + "/citation/%s/index.html" % citation.id, + check_links=True, + ) + await assert_betty_json( + project, "/citation/%s/index.json" % citation.id + ) async def test_sources(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) - async with project: - await generate(project) - await assert_betty_html(project, "/source/index.html", check_links=True) - await assert_betty_json(project, "/source/index.json") + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project, project: + await generate(project) + await assert_betty_html(project, "/source/index.html", check_links=True) + await assert_betty_json(project, "/source/index.json") async def test_source(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: source = Source( id="SOURCE1", name="A Little Birdie", @@ -369,8 +392,9 @@ async def test_source(self) -> None: class TestResourceOverride: async def test(self) -> None: - async with App.new_temporary() as app, app: - project = Project(app) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project: localized_assets_directory_path = ( Path(project.configuration.assets_directory_path) / "public" @@ -397,15 +421,15 @@ class TestSitemapGenerate: async def test_validate(self) -> None: from lxml import etree - async with App.new_temporary() as app, app: - project = Project(app) - async with project: - await generate(project) - schema_doc = etree.parse( - Path(__file__).parent / "test_generate_assets" / "sitemap.xsd" - ) - schema = etree.XMLSchema(schema_doc) - sitemap_doc = etree.parse( - project.configuration.www_directory_path / "sitemap.xml" - ) - schema.validate(sitemap_doc) + async with App.new_temporary() as app, app, Project.new_temporary( + app + ) as project, project: + await generate(project) + schema_doc = etree.parse( + Path(__file__).parent / "test_generate_assets" / "sitemap.xsd" + ) + schema = etree.XMLSchema(schema_doc) + sitemap_doc = etree.parse( + project.configuration.www_directory_path / "sitemap.xml" + ) + schema.validate(sitemap_doc) diff --git a/betty/tests/test_jinja2.py b/betty/tests/test_jinja2.py index ffece4493..853784d8c 100644 --- a/betty/tests/test_jinja2.py +++ b/betty/tests/test_jinja2.py @@ -41,8 +41,7 @@ async def test_filters(self) -> None: class TestJinja2Renderer: async def test_render_file(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = Jinja2Renderer(project.jinja2_environment, project.configuration) template = "{% if true %}true{% endif %}" expected_output = "true" diff --git a/betty/tests/test_load.py b/betty/tests/test_load.py index 2241cdeaa..46bb3d8bb 100644 --- a/betty/tests/test_load.py +++ b/betty/tests/test_load.py @@ -26,13 +26,13 @@ async def test_should_fetch_link_with_unsupported_content_type( ) link = Link(link_url) - project = Project(new_temporary_app) - project.ancestry.add(DummyHasLinks(links=[link])) - async with project: - await load(project) + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(DummyHasLinks(links=[link])) + async with project: + await load(project) - assert link.label is None - assert link.description is None + assert link.label is None + assert link.description is None @pytest.mark.parametrize( ("link_page_content_type"), @@ -57,13 +57,13 @@ async def test_should_fetch_link_with_invalid_html( ) link = Link(link_url) - project = Project(new_temporary_app) - project.ancestry.add(DummyHasLinks(links=[link])) - async with project: - await load(project) + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(DummyHasLinks(links=[link])) + async with project: + await load(project) - assert link.label is None - assert link.description is None + assert link.label is None + assert link.description is None @pytest.mark.parametrize( ("link_page_content_type"), @@ -91,12 +91,12 @@ async def test_should_fetch_link_label_from_valid_html_with_title( ) link = Link(link_url) - project = Project(new_temporary_app) - project.ancestry.add(DummyHasLinks(links=[link])) - async with project: - await load(project) + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(DummyHasLinks(links=[link])) + async with project: + await load(project) - assert link.label == link_page_title + assert link.label == link_page_title @pytest.mark.parametrize( ("link_page_content_type"), @@ -121,12 +121,12 @@ async def test_should_fetch_link_label_with_valid_html_without_title( ) link = Link(link_url) - project = Project(new_temporary_app) - project.ancestry.add(DummyHasLinks(links=[link])) - async with project: - await load(project) + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(DummyHasLinks(links=[link])) + async with project: + await load(project) - assert link.label is None + assert link.label is None @pytest.mark.parametrize( ("link_page_content_type", "meta_attr_name", "meta_attr_value"), @@ -156,9 +156,9 @@ async def test_should_fetch_link_description_from_valid_html_with_meta_descripti ) link = Link(link_url) - project = Project(new_temporary_app) - project.ancestry.add(DummyHasLinks(links=[link])) - async with project: - await load(project) + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(DummyHasLinks(links=[link])) + async with project: + await load(project) - assert link.description == link_page_meta_description + assert link.description == link_page_meta_description diff --git a/betty/tests/test_openapi.py b/betty/tests/test_openapi.py index 50bffdcbf..d3386888d 100644 --- a/betty/tests/test_openapi.py +++ b/betty/tests/test_openapi.py @@ -23,9 +23,9 @@ async def test_build(self, clean_urls: bool, new_temporary_app: App) -> None: Path(__file__).parent / "test_openapi_assets" / "openapi-schema.json" ) as f: schema = stdjson.loads(await f.read()) - project = Project(new_temporary_app) - project.configuration.clean_urls = clean_urls - async with project: - sut = Specification(project) - specification = await sut.build() - jsonschema.validate(specification, schema) + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.clean_urls = clean_urls + async with project: + sut = Specification(project) + specification = await sut.build() + jsonschema.validate(specification, schema) diff --git a/betty/tests/test_serve.py b/betty/tests/test_serve.py index 11fad7050..c6ab5ae90 100644 --- a/betty/tests/test_serve.py +++ b/betty/tests/test_serve.py @@ -14,8 +14,7 @@ class TestBuiltinProjectServer: async def test(self, mocker: MockerFixture, new_temporary_app: App) -> None: mocker.patch("webbrowser.open_new_tab") content = "Hello, and welcome to my site!" - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: await makedirs(project.configuration.www_directory_path) async with aiofiles.open( project.configuration.www_directory_path / "index.html", "w" diff --git a/betty/tests/test_url.py b/betty/tests/test_url.py index 013109f13..8c9f54d05 100644 --- a/betty/tests/test_url.py +++ b/betty/tests/test_url.py @@ -37,8 +37,7 @@ class TestLocalizedPathUrlGenerator: async def test_generate( self, expected: str, new_temporary_app: App, resource: str ) -> None: - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = LocalizedPathUrlGenerator(project) assert expected == sut.generate(resource, "text/html") @@ -54,11 +53,11 @@ async def test_generate( async def test_generate_with_clean_urls( self, expected: str, new_temporary_app: App, resource: str ) -> None: - project = Project(new_temporary_app) - project.configuration.clean_urls = True - async with project: - sut = LocalizedPathUrlGenerator(project) - assert expected == sut.generate(resource, "text/html") + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.clean_urls = True + async with project: + sut = LocalizedPathUrlGenerator(project) + assert expected == sut.generate(resource, "text/html") @pytest.mark.parametrize( ("expected", "resource"), @@ -70,8 +69,7 @@ async def test_generate_with_clean_urls( async def test_generate_absolute( self, expected: str, new_temporary_app: App, resource: str ) -> None: - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = LocalizedPathUrlGenerator(project) assert expected == sut.generate(resource, "text/html", absolute=True) @@ -89,22 +87,22 @@ async def test_generate_multilingual( new_temporary_app: App, url_generator_locale: Localey | None, ) -> None: - project = Project(new_temporary_app) - project.configuration.locales.replace( - LocaleConfiguration( - "nl-NL", - alias="nl", - ), - LocaleConfiguration( - "en-US", - alias="en", - ), - ) - async with project: - sut = LocalizedPathUrlGenerator(project) - assert expected == sut.generate( - "/index.html", "text/html", locale=url_generator_locale + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.locales.replace( + LocaleConfiguration( + "nl-NL", + alias="nl", + ), + LocaleConfiguration( + "en-US", + alias="en", + ), ) + async with project: + sut = LocalizedPathUrlGenerator(project) + assert expected == sut.generate( + "/index.html", "text/html", locale=url_generator_locale + ) class EntityUrlGeneratorTestUrlyEntity(UserFacingEntity, Entity): @@ -117,8 +115,7 @@ class EntityUrlGeneratorTestNonUrlyEntity(UserFacingEntity, Entity): class TestEntityUrlGenerator: async def test_generate(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = _EntityUrlGenerator(project, EntityUrlGeneratorTestUrlyEntity) assert ( sut.generate(EntityUrlGeneratorTestUrlyEntity("I1"), "text/html") @@ -126,8 +123,7 @@ async def test_generate(self, new_temporary_app: App) -> None: ) async def test_generate_with_invalid_value(self, new_temporary_app: App) -> None: - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = _EntityUrlGenerator(project, EntityUrlGeneratorTestUrlyEntity) with pytest.raises(ValueError): # noqa PT011 sut.generate(EntityUrlGeneratorTestNonUrlyEntity(), "text/html") @@ -179,7 +175,6 @@ class TestProjectUrlGenerator: async def test_generate( self, expected: str, new_temporary_app: App, resource: Any ) -> None: - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = ProjectUrlGenerator(project) assert expected == sut.generate(resource, "text/html") diff --git a/betty/tests/test_wikipedia.py b/betty/tests/test_wikipedia.py index 23638711b..fb1c18055 100644 --- a/betty/tests/test_wikipedia.py +++ b/betty/tests/test_wikipedia.py @@ -672,8 +672,7 @@ async def test_populate_link_should_convert_http_to_https( m_retriever = mocker.patch("betty.wikipedia._Retriever") link = Link("http://en.wikipedia.org/wiki/Amsterdam") page_language = "nl" - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = _Populator(project, m_retriever) await sut.populate_link(link, page_language) assert link.url == "https://en.wikipedia.org/wiki/Amsterdam" @@ -698,8 +697,7 @@ async def test_populate_link_should_set_media_type( "http://en.wikipedia.org/wiki/Amsterdam", media_type=media_type, ) - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = _Populator(project, m_retriever) await sut.populate_link(link, "en") assert expected == link.media_type @@ -722,8 +720,7 @@ async def test_populate_link_should_set_relationship( m_retriever = mocker.patch("betty.wikipedia._Retriever") link = Link("http://en.wikipedia.org/wiki/Amsterdam") link.relationship = relationship - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = _Populator(project, m_retriever) await sut.populate_link(link, "en") assert expected == link.relationship @@ -747,8 +744,7 @@ async def test_populate_link_should_set_locale( m_retriever = mocker.patch("betty.wikipedia._Retriever") link = Link("http://%s.wikipedia.org/wiki/Amsterdam" % page_language) link.locale = locale - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = _Populator(project, m_retriever) await sut.populate_link(link, page_language) assert expected == link.locale @@ -773,8 +769,7 @@ async def test_populate_link_should_set_description( description=description, ) page_language = "en" - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = _Populator(project, m_retriever) await sut.populate_link(link, page_language) assert expected == link.description @@ -802,8 +797,7 @@ async def test_populate_link_should_set_label( "The city of Amsterdam", "Amsterdam, such a lovely place!", ) - project = Project(new_temporary_app) - async with project: + async with Project.new_temporary(new_temporary_app) as project, project: sut = _Populator(project, m_retriever) await sut.populate_link(link, "en", summary) assert expected == link.label @@ -817,11 +811,11 @@ async def test_populate_should_ignore_resource_without_link_support( id="the_citation", source=source, ) - project = Project(new_temporary_app) - project.ancestry.add(resource) - async with project: - sut = _Populator(project, m_retriever) - await sut.populate() + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(resource) + async with project: + sut = _Populator(project, m_retriever) + await sut.populate() async def test_populate_should_ignore_resource_without_links( self, mocker: MockerFixture, new_temporary_app: App @@ -831,12 +825,12 @@ async def test_populate_should_ignore_resource_without_links( id="the_source", name="The Source", ) - project = Project(new_temporary_app) - project.ancestry.add(resource) - async with project: - sut = _Populator(project, m_retriever) - await sut.populate() - assert resource.links == [] + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(resource) + async with project: + sut = _Populator(project, m_retriever) + await sut.populate() + assert resource.links == [] async def test_populate_should_ignore_non_wikipedia_links( self, mocker: MockerFixture, new_temporary_app: App @@ -848,12 +842,12 @@ async def test_populate_should_ignore_non_wikipedia_links( name="The Source", links=[link], ) - project = Project(new_temporary_app) - project.ancestry.add(resource) - async with project: - sut = _Populator(project, m_retriever) - await sut.populate() - assert [link] == resource.links + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(resource) + async with project: + sut = _Populator(project, m_retriever) + await sut.populate() + assert [link] == resource.links async def test_populate_should_populate_existing_link( self, mocker: MockerFixture, new_temporary_app: App @@ -875,18 +869,18 @@ async def test_populate_should_populate_existing_link( name="The Source", links=[link], ) - project = Project(new_temporary_app) - project.ancestry.add(resource) - async with project: - sut = _Populator(project, m_retriever) - await sut.populate() - m_retriever.get_summary.assert_called_once_with(page_language, page_name) - assert len(resource.links) == 1 - assert link.label == "Amsterdam" - assert link.locale == "en" - assert MediaType("text/html") == link.media_type - assert link.description is not None - assert link.relationship == "external" + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(resource) + async with project: + sut = _Populator(project, m_retriever) + await sut.populate() + m_retriever.get_summary.assert_called_once_with(page_language, page_name) + assert len(resource.links) == 1 + assert link.label == "Amsterdam" + assert link.locale == "en" + assert MediaType("text/html") == link.media_type + assert link.description is not None + assert link.relationship == "external" async def test_populate_should_add_translation_links( self, mocker: MockerFixture, new_temporary_app: App @@ -922,33 +916,35 @@ async def test_populate_should_add_translation_links( name="The Source", links=[link_en], ) - project = Project(new_temporary_app) - project.configuration.locales["en-US"].alias = "en" - project.configuration.locales.append( - LocaleConfiguration( - "nl-NL", - alias="nl", + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.locales["en-US"].alias = "en" + project.configuration.locales.append( + LocaleConfiguration( + "nl-NL", + alias="nl", + ) ) - ) - project.ancestry.add(resource) - async with project: - sut = _Populator(project, m_retriever) - await sut.populate() - - m_retriever.get_summary.assert_has_calls( - [ - call(page_language, page_name), - call(added_page_language, added_page_name), - ] - ) - m_retriever.get_translations.assert_called_once_with(page_language, page_name) - assert len(resource.links) == 2 - link_nl = [link for link in resource.links if link != link_en][0] - assert link_nl.label == "Amsterdam" - assert link_nl.locale == "nl" - assert MediaType("text/html") == link_nl.media_type - assert link_nl.description is not None - assert link_nl.relationship == "external" + project.ancestry.add(resource) + async with project: + sut = _Populator(project, m_retriever) + await sut.populate() + + m_retriever.get_summary.assert_has_calls( + [ + call(page_language, page_name), + call(added_page_language, added_page_name), + ] + ) + m_retriever.get_translations.assert_called_once_with( + page_language, page_name + ) + assert len(resource.links) == 2 + link_nl = [link for link in resource.links if link != link_en][0] + assert link_nl.label == "Amsterdam" + assert link_nl.locale == "nl" + assert MediaType("text/html") == link_nl.media_type + assert link_nl.description is not None + assert link_nl.relationship == "external" async def test_populate_place_should_add_coordinates( self, mocker: MockerFixture, new_temporary_app: App @@ -965,13 +961,13 @@ async def test_populate_place_should_add_coordinates( wikipedia_link = Link(f"https://{page_language}.wikipedia.org/wiki/{page_name}") other_link = Link("https://example.com") place = Place(links=[wikipedia_link, other_link]) - project = Project(new_temporary_app) - project.ancestry.add(place) - async with project: - sut = _Populator(project, m_retriever) - await sut.populate() + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(place) + async with project: + sut = _Populator(project, m_retriever) + await sut.populate() - assert coordinates is place.coordinates + assert coordinates is place.coordinates async def test_populate_has_links( self, mocker: MockerFixture, new_temporary_app: App @@ -992,10 +988,10 @@ async def test_populate_has_links( link = Link(f"https://{page_language}.wikipedia.org/wiki/{page_name}") place = Place(links=[link]) - project = Project(new_temporary_app) - project.ancestry.add(place) - async with project: - sut = _Populator(project, m_retriever) - await sut.populate() + async with Project.new_temporary(new_temporary_app) as project: + project.ancestry.add(place) + async with project: + sut = _Populator(project, m_retriever) + await sut.populate() - assert place.files[0].path == image.path + assert place.files[0].path == image.path