| 1 | from pathlib import Path |
| 2 | import hashlib |
| 3 | import json |
| 4 | import runpy |
| 5 | import shutil |
| 6 | from tempfile import TemporaryDirectory |
| 7 | import unittest |
| 8 | import xml.etree.ElementTree as ET |
| 9 | |
| 10 | from native_xml import ns |
| 11 | |
| 12 | ROOT = Path(__file__).resolve().parent.parent |
| 13 | FIXTURE = ROOT / 'corpus/custom-art' |
| 14 | compare = runpy.run_path(str(ROOT / 'tools/verify-document.py'))['compare'] |
| 15 | |
| 16 | |
| 17 | def tagged(read): |
| 18 | """The page's tagged paragraphs: text, then each tag as (name, symbol, completed).""" |
| 19 | page = ET.parse(read / 'page-000.xml').getroot() |
| 20 | definitions = {d.get('index'): (d.get('name'), int(d.get('symbol'))) for d in page.findall('one:TagDef', ns)} |
| 21 | return [((oe.find('one:T', ns).text or ''), |
| 22 | [definitions[tag.get('index')] + (tag.get('completed'),) for tag in oe.findall('one:Tag', ns)]) |
| 23 | for oe in page.iter('{%s}OE' % ns['one']) if oe.find('one:Tag', ns) is not None] |
| 24 | |
| 25 | |
| 26 | class CustomArtTest(unittest.TestCase): |
| 27 | def test_tags_with_art_cold_open_with_their_fallback_symbols(self): |
| 28 | with TemporaryDirectory() as temporary: |
| 29 | read = Path(temporary) / 'read' |
| 30 | shutil.copytree(FIXTURE / 'cold/before-read', read) |
| 31 | compare(FIXTURE / 'candidate', read) |
| 32 | self.assertEqual(tagged(FIXTURE / 'cold/before-read'), [ |
| 33 | ('Launch day', [('Launch', 13, 'true')]), |
| 34 | ('Ship the release', [('Ship it', 127, 'true')]), |
| 35 | ('Checked off', [('Done', 3, 'true')]), |
| 36 | ('Plain OneNote tag', [('Important', 13, 'true')]), |
| 37 | ]) |
| 38 | |
| 39 | def test_onenote_editing_the_page_keeps_the_mapping_and_the_folder(self): |
| 40 | edited = tagged(FIXTURE / 'cold/read') |
| 41 | self.assertEqual([text for text, _ in edited][:1], ['Launch day, moved to Friday']) |
| 42 | self.assertEqual(edited[2][1], [('Done', 3, 'false')]) |
| 43 | # Every tag the mapping names is still on the page under its name and symbol. |
| 44 | sidecar = FIXTURE / 'candidate/.snowbound' |
| 45 | mapped = {(m['name'], m['shape']): m['art'] for m in json.loads((sidecar / 'tags.json').read_text())} |
| 46 | stored = {tag[:2] for _, tags in edited for tag in tags} |
| 47 | self.assertEqual(set(mapped) - stored, set()) |
| 48 | for art in mapped.values(): |
| 49 | self.assertEqual(hashlib.sha256((sidecar / 'tags' / art).read_bytes()).hexdigest(), art.split('.')[0]) |
| 50 | # OneNote kept the folder hidden, left no table of contents in it and changed none of it. |
| 51 | left = json.loads((FIXTURE / 'cold/sidecar.json').read_text(encoding='utf-8-sig')) |
| 52 | self.assertIn('Hidden', left['attributes']) |
| 53 | files = {f['path']: f['sha256'] for f in left['files'] if f['sha256']} |
| 54 | expected = {p.relative_to(sidecar).as_posix(): hashlib.sha256(p.read_bytes()).hexdigest() |
| 55 | for p in sidecar.rglob('*') if p.is_file()} |
| 56 | self.assertEqual(files, expected) |
| 57 | self.assertEqual(sorted(left['notebook']), ['.snowbound', 'Custom art.one', 'Open Notebook.onetoc2']) |
| 58 | |
| 59 | |
| 60 | if __name__ == '__main__': |
| 61 | unittest.main() |