-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: allow html to be used in LaunchModalEffect (#306)
- Loading branch information
1 parent
90613dc
commit 5976b59
Showing
22 changed files
with
414 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from .utils import render_to_string | ||
|
||
__all__ = ("render_to_string",) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from pathlib import Path | ||
|
||
import pytest | ||
|
||
from canvas_sdk.effects import Effect | ||
from canvas_sdk.events import Event, EventRequest, EventType | ||
from plugin_runner.plugin_runner import LOADED_PLUGINS | ||
|
||
|
||
@pytest.mark.parametrize("install_test_plugin", ["test_render_template"], indirect=True) | ||
def test_render_to_string_valid_template( | ||
install_test_plugin: Path, load_test_plugins: None | ||
) -> None: | ||
"""Test that the render_to_string function loads and renders a valid template.""" | ||
plugin = LOADED_PLUGINS[ | ||
"test_render_template:test_render_template.protocols.my_protocol:ValidTemplate" | ||
] | ||
result: list[Effect] = plugin["class"](Event(EventRequest(type=EventType.UNKNOWN))).compute() | ||
assert "html" in result[0].payload | ||
|
||
|
||
@pytest.mark.parametrize("install_test_plugin", ["test_render_template"], indirect=True) | ||
def test_render_to_string_invalid_template( | ||
install_test_plugin: Path, load_test_plugins: None | ||
) -> None: | ||
"""Test that the render_to_string function raises an error for invalid templates.""" | ||
plugin = LOADED_PLUGINS[ | ||
"test_render_template:test_render_template.protocols.my_protocol:InvalidTemplate" | ||
] | ||
with pytest.raises(FileNotFoundError): | ||
plugin["class"](Event(EventRequest(type=EventType.UNKNOWN))).compute() | ||
|
||
|
||
@pytest.mark.parametrize("install_test_plugin", ["test_render_template"], indirect=True) | ||
def test_render_to_string_forbidden_template( | ||
install_test_plugin: Path, load_test_plugins: None | ||
) -> None: | ||
"""Test that the render_to_string function raises an error for a template outside plugin package.""" | ||
plugin = LOADED_PLUGINS[ | ||
"test_render_template:test_render_template.protocols.my_protocol:ForbiddenTemplate" | ||
] | ||
with pytest.raises(PermissionError): | ||
plugin["class"](Event(EventRequest(type=EventType.UNKNOWN))).compute() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import inspect | ||
from pathlib import Path | ||
from typing import Any | ||
|
||
from django.template import Context, Template | ||
|
||
from settings import PLUGIN_DIRECTORY | ||
|
||
|
||
def render_to_string(template_name: str, context: dict[str, Any] | None = None) -> str | None: | ||
"""Load a template and render it with the given context. | ||
Args: | ||
template_name (str): The path to the template file, relative to the plugin package. | ||
If the path starts with a forward slash ("/"), it will be stripped during resolution. | ||
context (dict[str, Any] | None): A dictionary of variables to pass to the template | ||
for rendering. Defaults to None, which uses an empty context. | ||
Returns: | ||
str: The rendered template as a string. | ||
Raises: | ||
FileNotFoundError: If the template file does not exist within the plugin's directory | ||
or if the resolved path is invalid. | ||
""" | ||
plugins_dir = Path(PLUGIN_DIRECTORY).resolve() | ||
current_frame = inspect.currentframe() | ||
caller = current_frame.f_back if current_frame else None | ||
|
||
if not caller or "__is_plugin__" not in caller.f_globals: | ||
return None | ||
|
||
plugin_name = caller.f_globals["__name__"].split(".")[0] | ||
plugin_dir = plugins_dir / plugin_name | ||
template_path = Path(plugin_dir / template_name.lstrip("/")).resolve() | ||
|
||
if not template_path.is_relative_to(plugin_dir): | ||
raise PermissionError(f"Invalid template '{template_name}'") | ||
elif not template_path.exists(): | ||
raise FileNotFoundError(f"Template {template_name} not found.") | ||
|
||
template = Template(template_path.read_text()) | ||
|
||
return template.render(Context(context)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import shutil | ||
from collections.abc import Generator | ||
from pathlib import Path | ||
|
||
import pytest | ||
|
||
from plugin_runner.plugin_runner import EVENT_HANDLER_MAP, LOADED_PLUGINS, load_plugins | ||
|
||
|
||
@pytest.fixture | ||
def install_test_plugin(request: pytest.FixtureRequest) -> Generator[Path, None, None]: | ||
"""Copies a specified plugin from the fixtures directory to the data directory | ||
and removes it after the test. | ||
Parameters: | ||
- request.param: The name of the plugin package to copy. | ||
Yields: | ||
- Path to the copied plugin directory. | ||
""" | ||
# Define base directories | ||
base_dir = Path("./plugin_runner/tests") | ||
fixture_plugin_dir = base_dir / "fixtures" / "plugins" | ||
data_plugin_dir = base_dir / "data" / "plugins" | ||
|
||
# The plugin name should be passed as a parameter to the fixture | ||
plugin_name = request.param # Expected to be a str | ||
src_plugin_path = fixture_plugin_dir / plugin_name | ||
dest_plugin_path = data_plugin_dir / plugin_name | ||
|
||
# Ensure the data plugin directory exists | ||
data_plugin_dir.mkdir(parents=True, exist_ok=True) | ||
|
||
# Copy the specific plugin from fixtures to data | ||
try: | ||
shutil.copytree(src_plugin_path, dest_plugin_path) | ||
yield dest_plugin_path # Provide the path to the test | ||
finally: | ||
# Cleanup: remove data/plugins directory after the test | ||
if dest_plugin_path.exists(): | ||
shutil.rmtree(dest_plugin_path) | ||
|
||
|
||
@pytest.fixture | ||
def load_test_plugins() -> Generator[None, None, None]: | ||
"""Manages the lifecycle of test plugins by loading and unloading them.""" | ||
try: | ||
load_plugins() | ||
yield | ||
finally: | ||
LOADED_PLUGINS.clear() | ||
EVENT_HANDLER_MAP.clear() |
Oops, something went wrong.