Files
rio/tests/test_cli/test_app_loading.py
2024-10-26 20:22:18 +02:00

329 lines
8.8 KiB
Python

import textwrap
from pathlib import Path
import pytest
from pyfakefs.fake_filesystem import FakeFilesystem, PatchMode
from pyfakefs.fake_filesystem_unittest import Patcher
from rio.cli.run_project.app_loading import load_user_app
from rio.project_config import RioProjectConfig
# Per default, importing from the fake file system doesn't work. So we must
# define a custom fixture which enables that.
@pytest.fixture
def fs():
with Patcher(patch_open_code=PatchMode.AUTO) as p:
yield p.fs
def create_project(
fs: FakeFilesystem,
file_hierarchy: str,
*,
app_file: str,
main_module: str,
) -> RioProjectConfig:
FILE_CONTENTS_BY_NAME = {
"foo_page.py": """
import rio
@rio.page(url_segment='foo-was-loaded-correctly')
class FooPage(rio.Component):
def build(self):
return rio.Text('foo')
"""
}
file_hierarchy = textwrap.dedent(file_hierarchy).strip()
directories_by_indent = {0: Path()}
rio_toml_location: Path | None = None
for line in file_hierarchy.splitlines():
file_name = line.lstrip()
indent = (len(line) - len(file_name)) // 4
directory = directories_by_indent[indent]
# Create the file/folder
if file_name.endswith("/"):
new_directory = directory / file_name.rstrip("/")
directories_by_indent[indent + 1] = new_directory
# new_directory.mkdir()
fs.create_dir(new_directory)
else:
new_file = directory / file_name
fs.create_file(
new_file, contents=FILE_CONTENTS_BY_NAME.get(file_name, "")
)
if file_name == "rio.toml":
rio_toml_location = new_file
(directories_by_indent[0] / app_file).write_text("""
import rio
app = rio.App(build=rio.Spacer)
""")
assert rio_toml_location is not None
return RioProjectConfig(
file_path=rio_toml_location.absolute(),
toml_dict={
"app": {
"app-type": "website",
"main-module": main_module,
}
},
dirty_keys=set(),
)
def test_project_file(fs: FakeFilesystem):
config = create_project(
fs,
"""
my-project/
my_app.py
pages/
foo_page.py
rio.toml
""",
app_file="my-project/my_app.py",
main_module="my_app",
)
app = load_user_app(config).app
assert app.name == "My App"
assert app.assets_dir == Path("my-project/assets").absolute()
assert app.pages[0].url_segment == "foo-was-loaded-correctly"
def test_src_folder(fs: FakeFilesystem):
config = create_project(
fs,
"""
my-project/
src/
assets/
my_app.py
pages/
foo_page.py
rio.toml
""",
app_file="my-project/src/my_app.py",
main_module="my_app",
)
app = load_user_app(config).app
assert app.name == "My App"
assert app.assets_dir == Path("my-project/src/assets").absolute()
assert app.pages[0].url_segment == "foo-was-loaded-correctly"
def test_simple_project_dir(fs: FakeFilesystem):
config = create_project(
fs,
"""
my-project/
my_project/
__init__.py
assets/
pages/
foo_page.py
rio.toml
""",
app_file="my-project/my_project/__init__.py",
main_module="my_project",
)
app = load_user_app(config).app
assert app.name == "My Project"
assert app.assets_dir == Path("my-project/my_project/assets").absolute()
assert app.pages[0].url_segment == "foo-was-loaded-correctly"
def test_submodule(fs: FakeFilesystem):
config = create_project(
fs,
"""
my-project/
my_project/
__init__.py
app.py
assets/
pages/
foo_page.py
rio.toml
""",
app_file="my-project/my_project/app.py",
main_module="my_project.app",
)
app = load_user_app(config).app
assert app.name == "App"
assert app.assets_dir == Path("my-project/my_project/assets").absolute()
assert app.pages[0].url_segment == "foo-was-loaded-correctly"
@pytest.mark.xfail(reason="Waiting for new pyfakefs version to be published")
def test_import_sibling_module(fs: FakeFilesystem):
config = create_project(
fs,
"""
my-project/
foo.py
bar.py
rio.toml
""",
app_file="my-project/foo.py",
main_module="bar",
)
Path("my-project/bar.py").write_text("""
# Note: The import statement doesn't work with pyfakefs, so we have to use
# importlib.
import importlib
app = importlib.import_module('foo').app
""")
app = load_user_app(config).app
assert app.name == "Bar"
assert app.assets_dir == Path("my-project").absolute()
assert not app.pages
@pytest.mark.xfail(reason="Waiting for new pyfakefs version to be published")
def test_import_from_submodule(fs: FakeFilesystem):
config = create_project(
fs,
"""
my-project/
my_project/
__init__.py
app.py
assets/
pages/
foo_page.py
helper.py
rio.toml
""",
app_file="my-project/helper.py",
main_module="my_project.app",
)
Path("my-project/my_project/app.py").write_text("""
# Note: The import statement doesn't work with pyfakefs, so we have to use
# importlib.
import importlib
app = importlib.import_module('helper').app
""")
app = load_user_app(config).app
assert app.name == "App"
assert app.assets_dir == Path("my-project/my_project/assets").absolute()
assert app.pages[0].url_segment == "foo-was-loaded-correctly"
@pytest.mark.xfail(reason="Waiting for new pyfakefs version to be published")
def test_relative_import_from_pages(fs: FakeFilesystem):
config = create_project(
fs,
"""
my-project/
my_project/
__init__.py
app.py
assets/
pages/
fancy_page.py
rio.toml
""",
app_file="my-project/my_project/app.py",
main_module="my_project.app",
)
Path("my-project/my_project/pages/fancy_page.py").write_text("""
# Note: The import statement doesn't work with pyfakefs, so we have to use
# importlib.
import importlib.util
app_module = importlib.import_module(importlib.util.resolve_name('..app', __package__))
_ = app_module.app # Just to make sure the right file was imported
import rio
@rio.page(url_segment='fancy-page-was-loaded-correctly')
class FancyPage(rio.Component):
def build(self):
return rio.Text('fancy')
""")
app = load_user_app(config).app
assert app.name == "App"
assert app.assets_dir == Path("my-project/my_project/assets").absolute()
assert app.pages[0].url_segment == "fancy-page-was-loaded-correctly"
def test_relative_paths_with_module(fs: FakeFilesystem):
config = create_project(
fs,
"""
my-project/
my_app.py
my/
assets/
pages/
foo_page.py
rio.toml
""",
app_file="my-project/my_app.py",
main_module="my_app",
)
Path("my-project/my_app.py").write_text("""
import rio
app = rio.App(
build=rio.Spacer,
assets_dir='my/assets',
pages='my/pages',
)
""")
app = load_user_app(config).app
assert app.name == "My App"
assert app.assets_dir == Path("my-project/my/assets").absolute()
assert app.pages[0].url_segment == "foo-was-loaded-correctly"
def test_relative_paths_with_package(fs: FakeFilesystem):
config = create_project(
fs,
"""
my-project/
my_project/
__init__.py
my/
pages/
foo_page.py
rio.toml
""",
app_file="my-project/my_project/__init__.py",
main_module="my_project",
)
Path("my-project/my_project/__init__.py").write_text("""
import rio
app = rio.App(
build=rio.Spacer,
assets_dir='my/assets',
pages='my/pages',
)
""")
app = load_user_app(config).app
assert app.name == "My Project"
assert app.assets_dir == Path("my-project/my_project/my/assets").absolute()
assert app.pages[0].url_segment == "foo-was-loaded-correctly"