Coverage for tests/test_scaffold.py: 100%
68 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-31 22:06 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-31 22:06 +0000
1"""Tests for creating new projects and templates."""
3from __future__ import annotations
5import dataclasses
6import pathlib
7import zipfile
9import docx
10import pptx
11import pytest
13import coursebook
14from coursebook import scaffold, settings
17def test_create_project(tmp_path: pathlib.Path) -> None:
18 """Tests a new project with the default outline."""
19 folder = tmp_path / 'torts_seminar'
20 created = scaffold.create_project(folder)
21 assert folder / 'project.ini' in created
22 assert (folder / 'visuals').is_dir()
23 project_settings, project_outline = settings.read_project_file(
24 folder / 'project.ini')
25 assert project_settings.name == 'Torts Seminar'
26 assert project_settings.style == 'coursebook'
27 assert len(project_outline) == 4
28 assert (folder / 'introduction.md').read_text(encoding = 'utf-8') == (
29 '<text>\n\n\n<slides>\n\n\n<notes>\n')
30 assert coursebook.Project.from_folder(folder).missing == []
33@pytest.mark.parametrize('file_format', scaffold.PROJECT_FILE_FORMATS)
34def test_project_file_formats(tmp_path: pathlib.Path, file_format: str) -> None:
35 """Tests that each project file format can be read back."""
36 lines = ['-: Preface', 'I: One: A Start', 'A: "Quoted"', 'II: Two']
37 scaffold.create_project(
38 tmp_path, name = 'Crazy Idea', style = 'law review',
39 outline_lines = lines, file_format = file_format)
40 project_settings, project_outline = settings.read_project_file(
41 tmp_path / f'project.{file_format}')
42 assert project_settings.name == 'Crazy Idea'
43 assert project_settings.style == 'law review'
44 assert [(entry.label, entry.title) for entry in project_outline] == [
45 ('', 'Preface'), ('I', 'One: A Start'), ('A', '"Quoted"'),
46 ('II', 'Two')]
47 # The law review style has no notes.
48 assert '<notes>' not in (tmp_path / 'two.md').read_text(encoding = 'utf-8')
51def test_shared_titles_get_own_files(tmp_path: pathlib.Path) -> None:
52 """Tests that entries with the same title get their own files."""
53 scaffold.create_project(tmp_path, outline_lines = [
54 'I: One', 'A: Overview', 'II: Two', 'A: Overview',
55 'III: Three | chapters/three.md'])
56 project = coursebook.Project.from_folder(tmp_path)
57 assert [entry.file_name for entry in project.outline] == [
58 'one.md', 'overview.md', 'two.md', 'overview_2.md',
59 'chapters/three.md']
60 assert project.missing == []
63def test_existing_files_are_kept(tmp_path: pathlib.Path) -> None:
64 """Tests that existing source files are not overwritten."""
65 (tmp_path / 'introduction.md').write_text('Mine', encoding = 'utf-8')
66 created = scaffold.create_project(tmp_path)
67 assert tmp_path / 'introduction.md' not in created
68 assert (tmp_path / 'introduction.md').read_text(encoding = 'utf-8') == (
69 'Mine')
70 with pytest.raises(FileExistsError, match = 'already has a project file'):
71 scaffold.create_project(tmp_path)
74def test_invalid_new_projects(tmp_path: pathlib.Path) -> None:
75 """Tests an unknown file format, style, and outline."""
76 with pytest.raises(ValueError, match = 'unknown project file format'):
77 scaffold.create_project(tmp_path, file_format = 'json')
78 with pytest.raises(ValueError, match = 'unknown style'):
79 scaffold.create_project(tmp_path, style = 'memo')
80 with pytest.raises(ValueError, match = 'cannot place'):
81 scaffold.create_project(tmp_path, outline_lines = ['B: Wrong'])
82 assert not list(tmp_path.iterdir())
85def test_write_templates(tmp_path: pathlib.Path) -> None:
86 """Tests writing the Word and PowerPoint templates of styles."""
87 word = scaffold.write_template('law review', tmp_path / 'lr.docx')
88 assert docx.Document(str(word)).styles['Normal'].font.name == (
89 'Times New Roman')
90 deck = scaffold.write_template('coursebook', tmp_path / 'deck.pptx')
91 presentation = pptx.Presentation(str(deck))
92 names = {layout.name for layout in presentation.slide_layouts}
93 assert 'Section Header' in names
94 with pytest.raises(FileExistsError, match = 'already exists'):
95 scaffold.write_template('coursebook', deck)
96 with pytest.raises(ValueError, match = 'must end with'):
97 scaffold.write_template('coursebook', tmp_path / 'deck.pdf')
100def test_write_templates_without_names(tmp_path: pathlib.Path) -> None:
101 """Tests a style without a PowerPoint template."""
103 @dataclasses.dataclass
104 class PlainLecture(coursebook.Coursebook):
105 slides_template: str | None = None
107 deck = scaffold.write_template('plain lecture', tmp_path / 'plain.pptx')
108 with zipfile.ZipFile(deck) as package:
109 theme = package.read('ppt/theme/theme1.xml').decode('utf-8')
110 assert 'chalkboard' not in theme