import sys from pathlib import Path # Import module from same folder sys.path.insert(0, str(Path(__file__).parent)) import orgpatch as op EXAMPLE_ORG = """* Main Title ** Introduction Some intro text. #+NAME: intro-para This is a named paragraph that will be replaced. It continues until the first blank line. ** Data #+NAME: mytable | Item | Value | |------+-------| | A | 1 | | B | 2 | ** Code #+NAME: code-snippet #+BEGIN_SRC python print("hello world") #+END_SRC ** My Section This section body will be replaced. It ends at the next heading of level ** or *. ** Another Section Content of another section. """ def write(p: Path, text: str): p.write_text(text, encoding="utf-8") def test_parse_headings_and_sections(tmp_path: Path): org = tmp_path / "example.org" write(org, EXAMPLE_ORG) lines = op.read_lines(str(org)) heads = op.parse_headings(lines) titles = [h['title'] for h in heads] assert "Introduction" in titles assert "My Section" in titles b = op.section_bounds(lines, heads, "** My Section") assert b is not None s, e = b body = ''.join(lines[s:e]) assert "This section body will be replaced." in body def test_parse_named_elements(tmp_path: Path): org = tmp_path / "example.org" write(org, EXAMPLE_ORG) lines = op.read_lines(str(org)) elems = op.parse_named_elements(lines) names = {e['name']: e for e in elems} assert names['intro-para']['type'] == 'para' assert names['mytable']['type'] == 'table' assert names['code-snippet']['type'] == 'block' def test_replace_section(tmp_path: Path): org = tmp_path / "example.org" write(org, EXAMPLE_ORG) lines = op.read_lines(str(org)) out = op.replace_section(lines, "** My Section", "New body\nMore\n") joined = ''.join(out) assert "New body" in joined assert "This section body will be replaced." not in joined def test_replace_named_block_table_para(tmp_path: Path): org = tmp_path / "example.org" write(org, EXAMPLE_ORG) lines = op.read_lines(str(org)) out = op.replace_named(lines, "code-snippet", "print('updated')\n") assert "print('updated')" in ''.join(out) out2 = op.replace_named(out, "mytable", "| X | 9 |\n") assert "| X | 9 |" in ''.join(out2) out3 = op.replace_named(out2, "intro-para", "New intro\nSecond\n") j3 = ''.join(out3) assert "New intro" in j3 assert "named paragraph that will be replaced" not in j3 def test_sync_in_and_out(tmp_path: Path): org = tmp_path / "example.org" write(org, EXAMPLE_ORG) code_src = tmp_path / "code.py" tbl_src = tmp_path / "tbl.org" sec_src = tmp_path / "section.txt" para_src = tmp_path / "intro.txt" write(code_src, "print('via in')\n") write(tbl_src, "| Col | Val |\n|-----+-----|\n| A | 1 |\n") write(sec_src, "Replaced via sync in.\nSecond line.\n") write(para_src, "Intro via sync in.\n\n") mapping = [ {"name": "code-snippet", "file": str(code_src)}, {"name": "mytable", "file": str(tbl_src)}, {"section": "** My Section", "file": str(sec_src)}, {"name": "intro-para", "file": str(para_src)}, ] lines = op.read_lines(str(org)) new_lines = op.sync_apply_in(lines, mapping) out = ''.join(new_lines) assert "print('via in')" in out assert "| Col | Val |" in out assert "Replaced via sync in." in out assert "Intro via sync in." in out export_dir = tmp_path / "exported" export_dir.mkdir() m_out = [ {"name": "code-snippet", "file": str(export_dir / "code.py")}, {"name": "mytable", "file": str(export_dir / "table.org")}, {"section": "** My Section", "file": str(export_dir / "section.txt")}, {"name": "intro-para", "file": str(export_dir / "intro.txt")}, ] op.sync_apply_out(new_lines, m_out, mkdirs=True, overwrite=True) assert (export_dir / "code.py").read_text(encoding="utf-8").strip() == "print('via in')" assert "| Col | Val |" in (export_dir / "table.org").read_text(encoding="utf-8") assert "Replaced via sync in." in (export_dir / "section.txt").read_text(encoding="utf-8") assert "Intro via sync in." in (export_dir / "intro.txt").read_text(encoding="utf-8")