1from pathlib import Path
2import runpy
3import shutil
4from tempfile import TemporaryDirectory
5import unittest
6import uuid
7import xml.etree.ElementTree as ET
8
9from native_xml import ns
10
11ROOT = Path(__file__).resolve().parent.parent
12FIXTURE = ROOT / 'corpus/notebook-management'
13compare = runpy.run_path(str(ROOT / 'tools/verify-document.py'))['compare']
14
15
16def file_identity(path):
17 return uuid.UUID(bytes_le=path.read_bytes()[16:32])
18
19
20def tree(node):
21 """Sections, groups and pages as OneNote lists them, with what the app set on each."""
22 out = []
23 for child in node:
24 tag = child.tag.rsplit('}', 1)[-1]
25 if tag in ('Section', 'SectionGroup'):
26 out.append((tag, child.get('name'), child.get('color'), tree(child)))
27 elif tag == 'Page':
28 out.append((tag, child.get('name'), child.get('pageLevel'), child.get('isInRecycleBin')))
29 return out
30
31
32def page(title):
33 for path in sorted((FIXTURE / 'cold/read').glob('page-*.xml')):
34 root = ET.parse(path).getroot()
35 if root.get('name') == title:
36 return root
37 raise AssertionError(f'No page titled {title!r}')
38
39
40class NotebookManagementTest(unittest.TestCase):
41 def test_the_managed_notebook_reopens_natively_as_written(self):
42 with TemporaryDirectory() as temporary:
43 read = Path(temporary) / 'read'
44 shutil.copytree(FIXTURE / 'cold/read', read)
45 compare(FIXTURE / 'candidate', read)
46 for candidate in sorted((FIXTURE / 'candidate').rglob('*.one*')):
47 relative = candidate.relative_to(FIXTURE / 'candidate')
48 self.assertEqual(file_identity(FIXTURE / 'cold/notebook' / relative),
49 file_identity(candidate), str(relative))
50
51 def test_sections_groups_and_pages_sit_where_the_app_put_them(self):
52 hierarchy = ET.parse(FIXTURE / 'cold/read/hierarchy.xml').getroot()
53 self.assertEqual(tree(hierarchy), [
54 ('Section', 'New Section 1', '#8AA8E4', [
55 ('Page', 'Garden plan', '1', None),
56 ('Page', 'Ivy page', '1', None),
57 ('Page', 'Teal page', '1', None),
58 ('Page', 'Subpage', '2', None),
59 ('Page', 'Meeting page', '1', None),
60 ]),
61 ('SectionGroup', 'Archive', None, [
62 ('Section', 'Inner', '#8AA8E4', [('Page', 'Untitled page', '1', None)]),
63 ('Section', 'Kitchen', '#F6B078', [
64 ('Page', 'Untitled page', '1', None),
65 ('Page', 'Moved page', '1', None),
66 ]),
67 ]),
68 ('SectionGroup', 'OneNote_RecycleBin', None, [
69 ('Section', 'Deleted Pages', '#E1E1E1', [('Page', 'Deleted page', '1', 'true')]),
70 ('Section', 'Gone', '#8AA8E4', [('Page', 'Untitled page', '1', 'true')]),
71 ('Section', 'Binned', '#D5A4BB', [('Page', 'Untitled page', '1', 'true')]),
72 ]),
73 ])
74 bin = hierarchy.find("one:SectionGroup[@name='OneNote_RecycleBin']", ns)
75 self.assertEqual(bin.get('isRecycleBin'), 'true')
76 deleted = bin.find("one:Section[@name='Deleted Pages']", ns)
77 self.assertEqual(deleted.get('isDeletedPages'), 'true')
78
79 def test_pages_keep_their_date_colour_and_template_art(self):
80 teal = page('Teal page')
81 self.assertEqual(teal.find('one:PageSettings', ns).get('color'), '#D4F9F2')
82 # The title's second outline holds the date and the time.
83 title = teal.find('one:Title', ns)
84 texts = [t.text for t in title.iter('{%s}T' % ns['one'])]
85 self.assertEqual(len(texts), 1)
86 ivy = page('Ivy page')
87 images = ivy.findall('one:Image', ns)
88 self.assertEqual([image.get('backgroundImage') for image in images], ['true'])
89 self.assertEqual(page('Garden plan').find('one:PageSettings', ns).get('color'), 'automatic')
90
91 def test_meeting_notes_content_reads_back_as_onenote_writes_it(self):
92 meeting = page('Meeting page')
93 self.assertEqual(len(meeting.findall('one:Image', ns)), 1)
94 outlines = meeting.findall('one:Outline', ns)
95 self.assertEqual(len(outlines), 4)
96 texts = [t.text or '' for t in meeting.iter('{%s}T' % ns['one'])]
97 for text in ('Agenda', 'Action Items', 'Important Dates', 'Meeting Details', 'Attendees:', 'Next Meeting'):
98 self.assertTrue(any(text in line for line in texts), text)
99 self.assertEqual(len(meeting.findall('.//one:Tag', ns)), 1)
100 self.assertEqual(len(meeting.findall('.//one:Number', ns)), 1)
101 self.assertEqual(len(meeting.findall('.//one:Bullet', ns)), 11)
102
103
104if __name__ == '__main__':
105 unittest.main()