mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-13 03:32:21 +02:00
139 lines
5.4 KiB
Python
139 lines
5.4 KiB
Python
"""Parser and browser coverage for document heading navigation."""
|
|
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
DOC_JS = (ROOT / "static/js/document.js").read_text(encoding="utf-8")
|
|
STYLE = (ROOT / "static/style.css").read_text(encoding="utf-8")
|
|
|
|
|
|
def _run_node(script: str):
|
|
result = subprocess.run(
|
|
["node", "--input-type=module", "-e", script],
|
|
cwd=ROOT,
|
|
check=False,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
return json.loads(result.stdout)
|
|
|
|
|
|
def test_markdown_outline_parses_structure_and_ignores_fenced_code():
|
|
data = _run_node(
|
|
r"""
|
|
import { parseMarkdownOutline } from './static/js/documentOutline.js';
|
|
const source = [
|
|
'# Overview',
|
|
'',
|
|
'## [Linked section](https://example.com) ##',
|
|
'',
|
|
'```md',
|
|
'# Not a real heading',
|
|
'```',
|
|
'',
|
|
'Setext title',
|
|
'------------',
|
|
'',
|
|
'#### Final `code` section',
|
|
].join('\n');
|
|
console.log(JSON.stringify(parseMarkdownOutline(source)));
|
|
"""
|
|
)
|
|
assert [(entry["level"], entry["text"]) for entry in data] == [
|
|
(1, "Overview"),
|
|
(2, "Linked section"),
|
|
(2, "Setext title"),
|
|
(4, "Final code section"),
|
|
]
|
|
source_slice = data[1]["end"] - data[1]["start"]
|
|
assert source_slice > len("Linked section")
|
|
assert data[2]["line"] == 8
|
|
|
|
|
|
def test_outline_control_is_mode_aware_live_and_accessible():
|
|
assert 'id="doc-outline-toolbar-btn"' in DOC_JS
|
|
assert 'aria-haspopup="true"' in DOC_JS
|
|
assert "language === 'markdown' || _isRichTextLang(language)" in DOC_JS
|
|
assert "_refreshDocumentOutline();" in DOC_JS
|
|
assert "bindMenuDismiss(menu" in DOC_JS
|
|
assert ".doc-outline-menu" in STYLE
|
|
assert "@media (max-width: 768px)" in STYLE
|
|
|
|
|
|
def test_outline_jumps_in_markdown_and_rich_text_and_fits_mobile():
|
|
data = _run_node(
|
|
r"""
|
|
import { chromium } from 'playwright';
|
|
const browser = await chromium.launch({ headless: true });
|
|
const page = await browser.newPage({ viewport: { width: 900, height: 700 } });
|
|
await page.goto('http://127.0.0.1:7011/static/js/documentOutline.js');
|
|
await page.setContent('<link rel="stylesheet" href="/static/style.css?v=20260831richtexttools91"><div id="toast"></div><div id="chat-container"></div><div id="sidebar"></div>');
|
|
await page.evaluate(async () => {
|
|
const mod = await import('/static/js/document.js?v=20260831richtexttools91&outline-test=1');
|
|
mod.init('/api');
|
|
mod.injectFreshDoc({
|
|
id: 'outline-md',
|
|
title: 'Outline',
|
|
language: 'markdown',
|
|
current_content: '# Intro\n\nBody\n\n## Details\n\nMore\n\n### End',
|
|
version_count: 1,
|
|
});
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
});
|
|
|
|
await page.locator('#doc-outline-toolbar-btn').click();
|
|
const markdownLabels = await page.locator('.doc-outline-label').allTextContents();
|
|
await page.keyboard.press('ArrowDown');
|
|
await page.keyboard.press('Enter');
|
|
const selected = await page.locator('#doc-editor-textarea').evaluate(el =>
|
|
el.value.slice(el.selectionStart, el.selectionEnd));
|
|
|
|
await page.locator('#doc-outline-toolbar-btn').click();
|
|
await page.locator('#doc-editor-textarea').evaluate(el => {
|
|
el.value += '\n\n#### Added live';
|
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
});
|
|
const liveLabels = await page.locator('.doc-outline-label').allTextContents();
|
|
await page.keyboard.press('Escape');
|
|
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.locator('#doc-outline-toolbar-btn').click();
|
|
const mobileBox = await page.locator('#doc-outline-menu').boundingBox();
|
|
await page.keyboard.press('Escape');
|
|
|
|
await page.evaluate(() => {
|
|
window.documentModule.injectFreshDoc({
|
|
id: 'outline-rich',
|
|
title: 'Rich outline',
|
|
language: 'richtext',
|
|
current_content: '<h1>Rich start</h1><p>Body</p><h3>Rich details</h3>',
|
|
version_count: 1,
|
|
});
|
|
});
|
|
await page.waitForTimeout(100);
|
|
await page.locator('#doc-outline-toolbar-btn').click();
|
|
const richLabels = await page.locator('.doc-outline-label').allTextContents();
|
|
await page.locator('.doc-outline-item').nth(1).click();
|
|
const richCaretHeading = await page.evaluate(() => {
|
|
const node = getSelection()?.anchorNode;
|
|
const el = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
|
|
return el?.closest('h1,h2,h3,h4,h5,h6')?.textContent || '';
|
|
});
|
|
|
|
console.log(JSON.stringify({ markdownLabels, selected, liveLabels, mobileBox, richLabels, richCaretHeading }));
|
|
await browser.close();
|
|
"""
|
|
)
|
|
assert data["markdownLabels"] == ["Intro", "Details", "End"]
|
|
assert data["selected"] == "## Details"
|
|
assert data["liveLabels"] == ["Intro", "Details", "End", "Added live"]
|
|
box = data["mobileBox"]
|
|
assert box["x"] >= 0 and box["x"] + box["width"] <= 390
|
|
assert box["y"] >= 0 and box["y"] + box["height"] <= 844
|
|
assert data["richLabels"] == ["Rich start", "Rich details"]
|
|
assert data["richCaretHeading"] == "Rich details"
|