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

1"""Tests for creating new projects and templates.""" 

2 

3from __future__ import annotations 

4 

5import dataclasses 

6import pathlib 

7import zipfile 

8 

9import docx 

10import pptx 

11import pytest 

12 

13import coursebook 

14from coursebook import scaffold, settings 

15 

16 

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 == [] 

31 

32 

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') 

49 

50 

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 == [] 

61 

62 

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) 

72 

73 

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()) 

83 

84 

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') 

98 

99 

100def test_write_templates_without_names(tmp_path: pathlib.Path) -> None: 

101 """Tests a style without a PowerPoint template.""" 

102 

103 @dataclasses.dataclass 

104 class PlainLecture(coursebook.Coursebook): 

105 slides_template: str | None = None 

106 

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