1#!/usr/bin/env python3
2"""Capture native outline controls during a disposable notebook inspection."""
3import argparse
4import base64
5import json
6from pathlib import Path
7import xml.etree.ElementTree as ET
8
9from native_runner import command, navigation_script, windows
10from native_xml import ns, Text
11
12
13def apply(output):
14 target = json.loads((output / 'machine.json').read_text())['name']
15 cases = json.loads((output / 'cases.json').read_text(encoding='utf-8-sig'))
16 pages = {page.get('name'): page for path in (output / 'read').glob('page-*.xml')
17 for page in [ET.parse(path).getroot()]}
18 before = output / 'before'
19 before.mkdir()
20 (output / 'notebook').rename(before / 'notebook')
21 actions = output / 'actions'
22 actions.mkdir()
23 for case in cases:
24 page = pages[case['name']]
25 outline = page.find('one:Outline', ns)
26 paragraph, = [node for node in outline.findall('.//one:OE', ns)
27 if node.find('one:T', ns) is not None
28 and ''.join(Text(node.find('one:T', ns).text or '').parts).startswith('Target ')]
29 page_id = page.get('ID')
30 object_id = (outline if case.get('delete') == 'outline' else paragraph).get('objectID')
31 stem = actions / case['name'].lower().replace(' ', '-')
32 if 'keys' in case:
33 script = navigation_script(page_id, object_id) + f'Send "{case["keys"]}"\nSleep 300\n'
34 stem.with_suffix('.ahk').write_text(script)
35 result = windows.do_exec(script, target=target, shot_delay_ms=500)
36 stem.with_suffix('.json').write_text(json.dumps({k: v for k, v in result.items() if k != 'png_b64'}, indent=2))
37 if result.get('png_b64'):
38 stem.with_suffix('.png').write_bytes(base64.b64decode(result['png_b64']))
39 if result.get('error') or result.get('exit') != 0 or not result.get('png_b64'):
40 raise RuntimeError(windows.text_result(result))
41 else:
42 if 'delete' in case:
43 page = ET.Element('delete', page=page_id, object=object_id)
44 operation = "$app.DeletePageContent($xml.DocumentElement.GetAttribute('page'), $xml.DocumentElement.GetAttribute('object'), [DateTime]::MinValue, $false)"
45 else:
46 for child in list(page):
47 if child.tag.rsplit('}', 1)[-1] in ('Outline', 'Title') and child is not outline:
48 page.remove(child)
49 if 'collapse' in case:
50 paragraph.set('collapsed', str(case['collapse']).lower())
51 for name in ('position', 'size'):
52 for key, value in case.get(name, {}).items():
53 outline.find('one:' + name.title(), ns).set(key, str(value).lower())
54 operation = "$app.UpdatePageContent($xml.OuterXml, [DateTime]::MinValue, 1, $false)"
55 ET.register_namespace('one', ns['one'])
56 payload = stem.with_suffix('.xml')
57 payload.write_bytes(ET.tostring(page, encoding='utf-8'))
58 result = windows.do_put(payload, r'C:\one-tests\outline-action.xml', target)
59 if result.get('error'):
60 raise RuntimeError(result['error'])
61 script = ("$ErrorActionPreference='Stop'\n[xml]$xml=[IO.File]::ReadAllText('C:\\one-tests\\outline-action.xml', [Text.Encoding]::UTF8)\n"
62 "$app=New-Object -ComObject OneNote.Application\n" + operation +
63 "\n[void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)\n")
64 source = stem.with_suffix('.ps1')
65 source.write_text(script, encoding='utf-8-sig')
66 result = windows.do_put(source, r'C:\one-tests\outline-action.ps1', target)
67 if result.get('error'):
68 raise RuntimeError(result['error'])
69 command(target, r'powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\one-tests\outline-action.ps1', output)
70 print(case['name'], flush=True)
71 (output / 'finish').touch()
72
73
74if __name__ == '__main__':
75 parser = argparse.ArgumentParser(description=__doc__)
76 parser.add_argument('output', type=Path)
77 apply(parser.parse_args().output)