mirror of
https://github.com/jeertmans/manim-slides.git
synced 2025-05-19 19:46:49 +08:00

* feat(cli): allow offline HTML presentations * feat(convert): allow fully offline HTML presentation TODO: check if this is really the case, especially for nested dependencies? Closes #438 * fix(cli): typo * chore(fmt): auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore(tests): add tests * fix(cli): use full path * fix(tests): typo * chore(ci): avoid specific kernel name * fix ? * chore(lib): simplify logic --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
239 lines
6.5 KiB
Python
239 lines
6.5 KiB
Python
import shutil
|
|
from enum import EnumMeta
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from manim_slides.config import PresentationConfig
|
|
from manim_slides.convert import (
|
|
PDF,
|
|
AutoAnimateEasing,
|
|
AutoAnimateMatcher,
|
|
AutoPlayMedia,
|
|
AutoSlideMethod,
|
|
BackgroundSize,
|
|
BackgroundTransition,
|
|
ControlsBackArrows,
|
|
ControlsLayout,
|
|
Converter,
|
|
Display,
|
|
HtmlZip,
|
|
JsBool,
|
|
JsFalse,
|
|
JsNull,
|
|
JsTrue,
|
|
KeyboardCondition,
|
|
NavigationMode,
|
|
PowerPoint,
|
|
PreloadIframes,
|
|
RevealJS,
|
|
RevealTheme,
|
|
ShowSlideNumber,
|
|
SlideNumber,
|
|
Transition,
|
|
TransitionSpeed,
|
|
file_to_data_uri,
|
|
get_duration_ms,
|
|
)
|
|
|
|
|
|
def test_get_duration_ms(video_file: Path) -> None:
|
|
assert get_duration_ms(video_file) == 2000.0
|
|
|
|
|
|
def test_file_to_data_uri(video_file: Path, video_data_uri_file: Path) -> None:
|
|
assert file_to_data_uri(video_file) == video_data_uri_file.read_text().strip()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("enum_type",),
|
|
[
|
|
(JsTrue,),
|
|
(JsFalse,),
|
|
(JsBool,),
|
|
(JsNull,),
|
|
(ControlsLayout,),
|
|
(ControlsBackArrows,),
|
|
(SlideNumber,),
|
|
(ShowSlideNumber,),
|
|
(KeyboardCondition,),
|
|
(NavigationMode,),
|
|
(AutoPlayMedia,),
|
|
(PreloadIframes,),
|
|
(AutoAnimateMatcher,),
|
|
(AutoAnimateEasing,),
|
|
(AutoSlideMethod,),
|
|
(Transition,),
|
|
(TransitionSpeed,),
|
|
(BackgroundSize,),
|
|
(BackgroundTransition,),
|
|
(Display,),
|
|
(RevealTheme,),
|
|
],
|
|
)
|
|
def test_format_enum(enum_type: EnumMeta) -> None:
|
|
for enum in enum_type: # type: ignore[var-annotated]
|
|
expected = str(enum)
|
|
got = f"{enum}"
|
|
|
|
assert expected == got
|
|
|
|
got = "{enum}".format(enum=enum) # noqa: UP032
|
|
|
|
assert expected == got
|
|
|
|
got = format(enum, "")
|
|
|
|
assert expected == got
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("enum_type",),
|
|
[
|
|
(ControlsLayout,),
|
|
(ControlsBackArrows,),
|
|
(SlideNumber,),
|
|
(ShowSlideNumber,),
|
|
(KeyboardCondition,),
|
|
(NavigationMode,),
|
|
(AutoPlayMedia,),
|
|
(PreloadIframes,),
|
|
(AutoAnimateMatcher,),
|
|
(AutoAnimateEasing,),
|
|
(AutoSlideMethod,),
|
|
(Transition,),
|
|
(TransitionSpeed,),
|
|
(BackgroundSize,),
|
|
(BackgroundTransition,),
|
|
(Display,),
|
|
],
|
|
)
|
|
def test_quoted_enum(enum_type: EnumMeta) -> None:
|
|
for enum in enum_type: # type: ignore[var-annotated]
|
|
if enum in ["true", "false", "null"]:
|
|
continue
|
|
|
|
expected = "'" + enum.value + "'"
|
|
got = str(enum)
|
|
|
|
assert expected == got
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("enum_type",),
|
|
[
|
|
(JsTrue,),
|
|
(JsFalse,),
|
|
(JsBool,),
|
|
(JsNull,),
|
|
(RevealTheme,),
|
|
],
|
|
)
|
|
def test_unquoted_enum(enum_type: EnumMeta) -> None:
|
|
for enum in enum_type: # type: ignore[var-annotated]
|
|
expected = enum.value
|
|
got = str(enum)
|
|
|
|
assert expected == got
|
|
|
|
|
|
class TestConverter:
|
|
@pytest.mark.parametrize(
|
|
("name", "converter"),
|
|
[("html", RevealJS), ("pdf", PDF), ("pptx", PowerPoint), ("zip", HtmlZip)],
|
|
)
|
|
def test_from_string(self, name: str, converter: type) -> None:
|
|
assert Converter.from_string(name) == converter
|
|
|
|
def test_revealjs_converter(
|
|
self, tmp_path: Path, presentation_config: PresentationConfig
|
|
) -> None:
|
|
out_file = tmp_path / "slides.html"
|
|
RevealJS(presentation_configs=[presentation_config]).convert_to(out_file)
|
|
assert out_file.exists()
|
|
assert Path(tmp_path / "slides_assets").is_dir()
|
|
file_contents = out_file.read_text()
|
|
assert "manim" in file_contents.casefold()
|
|
|
|
def test_revealjs_offline_converter(
|
|
self, tmp_path: Path, presentation_config: PresentationConfig
|
|
) -> None:
|
|
out_file = tmp_path / "slides.html"
|
|
RevealJS(presentation_configs=[presentation_config], offline="true").convert_to(
|
|
out_file
|
|
)
|
|
assert out_file.exists()
|
|
assets_dir = Path(tmp_path / "slides_assets")
|
|
assert assets_dir.is_dir()
|
|
for file in [
|
|
"black.min.css",
|
|
"reveal.min.css",
|
|
"reveal.min.js",
|
|
"zenburn.min.css",
|
|
]:
|
|
assert (assets_dir / file).exists()
|
|
|
|
def test_htmlzip_converter(
|
|
self, tmp_path: Path, presentation_config: PresentationConfig
|
|
) -> None:
|
|
archive = tmp_path / "got.zip"
|
|
expected = tmp_path / "expected.html"
|
|
got = tmp_path / "got.html"
|
|
|
|
HtmlZip(presentation_configs=[presentation_config]).convert_to(archive)
|
|
RevealJS(presentation_configs=[presentation_config]).convert_to(expected)
|
|
|
|
shutil.unpack_archive(str(archive), extract_dir=tmp_path)
|
|
|
|
assert archive.exists()
|
|
assert got.exists()
|
|
assert expected.exists()
|
|
|
|
assert got.read_text() == expected.read_text().replace(
|
|
"expected_assets", "got_assets"
|
|
)
|
|
|
|
@pytest.mark.parametrize("num_presentation_configs", (1, 2))
|
|
def test_revealjs_multiple_scenes_converter(
|
|
self,
|
|
tmp_path: Path,
|
|
presentation_config: PresentationConfig,
|
|
num_presentation_configs: int,
|
|
) -> None:
|
|
out_file = tmp_path / "slides.html"
|
|
RevealJS(
|
|
presentation_configs=[
|
|
presentation_config for _ in range(num_presentation_configs)
|
|
]
|
|
).convert_to(out_file)
|
|
assert out_file.exists()
|
|
assets_dir = Path(tmp_path / "slides_assets")
|
|
assert assets_dir.is_dir()
|
|
|
|
got = sum(1 for _ in assets_dir.iterdir())
|
|
expected = num_presentation_configs * len(presentation_config.slides)
|
|
|
|
assert got == expected
|
|
|
|
@pytest.mark.parametrize("frame_index", ("first", "last"))
|
|
def test_pdf_converter(
|
|
self, frame_index: str, tmp_path: Path, presentation_config: PresentationConfig
|
|
) -> None:
|
|
out_file = tmp_path / "slides.pdf"
|
|
PDF(
|
|
presentation_configs=[presentation_config], frame_index=frame_index
|
|
).convert_to(out_file)
|
|
assert out_file.exists()
|
|
|
|
def test_converter_no_presentation_config(self) -> None:
|
|
with pytest.raises(ValidationError):
|
|
Converter(presentation_configs=[])
|
|
|
|
def test_pptx_converter(
|
|
self, tmp_path: Path, presentation_config: PresentationConfig
|
|
) -> None:
|
|
out_file = tmp_path / "slides.pptx"
|
|
PowerPoint(presentation_configs=[presentation_config]).convert_to(out_file)
|
|
assert out_file.exists()
|