mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-13 19:52:20 +02:00
127 lines
6.1 KiB
Python
127 lines
6.1 KiB
Python
"""Browser coverage for contextual Rich Text selection formatting."""
|
|
|
|
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 test_selection_toolbar_uses_shared_formatting_path_and_preserves_range():
|
|
assert 'id = \'doc-rich-selection-toolbar\'' in DOC_JS
|
|
assert "_restoreRichSelectionToolbarRange(rich)" in DOC_JS
|
|
assert "applyMdFormat(button.dataset.richSelectionAction)" in DOC_JS
|
|
assert "toolbar.addEventListener('pointerdown', preserve)" in DOC_JS
|
|
assert "liveSelection.collapseToEnd()" in DOC_JS
|
|
assert "const isMobileViewport = viewportWidth <= 768;" in DOC_JS
|
|
assert ".doc-rich-selection-toolbar" in STYLE
|
|
|
|
|
|
def test_selection_toolbar_formats_and_stays_inside_desktop_and_mobile_viewports():
|
|
script = 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&selection-toolbar-test=1');
|
|
mod.init('/api');
|
|
mod.injectFreshDoc({
|
|
id: 'selection-toolbar-doc',
|
|
title: 'Selection toolbar',
|
|
language: 'richtext',
|
|
current_content: '<p><strong>Bold words</strong> and plain text for formatting.</p><p>Second paragraph.</p>',
|
|
version_count: 1,
|
|
});
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
});
|
|
|
|
await page.evaluate(() => {
|
|
const text = document.querySelector('#doc-email-richbody strong').firstChild;
|
|
const range = document.createRange();
|
|
range.selectNodeContents(text);
|
|
const selection = getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
document.dispatchEvent(new Event('selectionchange'));
|
|
});
|
|
await page.waitForSelector('#doc-rich-selection-toolbar');
|
|
const activeBold = await page.locator('[data-rich-selection-action="bold"]').evaluate(el => el.classList.contains('is-active'));
|
|
const desktopGeometry = await page.evaluate(() => {
|
|
const selected = getSelection().getRangeAt(0).getBoundingClientRect();
|
|
const toolbar = document.querySelector('#doc-rich-selection-toolbar').getBoundingClientRect();
|
|
return {
|
|
within: toolbar.left >= 0 && toolbar.right <= innerWidth && toolbar.top >= 0 && toolbar.bottom <= innerHeight,
|
|
separate: toolbar.bottom <= selected.top || toolbar.top >= selected.bottom,
|
|
};
|
|
});
|
|
await page.locator('[data-rich-selection-action="italic"]').click();
|
|
await page.waitForTimeout(40);
|
|
const formattedHtml = await page.locator('#doc-email-richbody').innerHTML();
|
|
const selectedAfterFormat = await page.evaluate(() => getSelection().toString());
|
|
await page.locator('#doc-email-richbody').press('Escape');
|
|
await page.waitForTimeout(30);
|
|
const dismissed = await page.locator('#doc-rich-selection-toolbar').count() === 0;
|
|
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.waitForTimeout(60);
|
|
await page.evaluate(() => {
|
|
const rich = document.querySelector('#doc-email-richbody');
|
|
rich.focus({ preventScroll: true });
|
|
const range = document.createRange();
|
|
range.selectNodeContents(rich.querySelector('p'));
|
|
const selection = getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
document.dispatchEvent(new Event('selectionchange'));
|
|
});
|
|
await page.waitForSelector('#doc-rich-selection-toolbar');
|
|
await page.waitForTimeout(380);
|
|
const mobileGeometry = await page.evaluate(() => {
|
|
const selected = getSelection().getRangeAt(0).getBoundingClientRect();
|
|
const toolbar = document.querySelector('#doc-rich-selection-toolbar').getBoundingClientRect();
|
|
const editor = document.querySelector('#doc-email-richbody').getBoundingClientRect();
|
|
const mainToolbar = document.querySelector('#doc-md-toolbar').getBoundingClientRect();
|
|
return {
|
|
left: toolbar.left,
|
|
right: toolbar.right,
|
|
top: toolbar.top,
|
|
bottom: toolbar.bottom,
|
|
width: toolbar.width,
|
|
separate: toolbar.bottom <= selected.top || toolbar.top >= selected.bottom,
|
|
dockedToEditorBottom: Math.abs(toolbar.bottom - editor.bottom + 4) < 1,
|
|
insideEditorTop: toolbar.top >= editor.top,
|
|
clearOfMainToolbar: toolbar.top >= mainToolbar.bottom || toolbar.bottom <= mainToolbar.top,
|
|
viewport: [innerWidth, innerHeight],
|
|
};
|
|
});
|
|
console.log(JSON.stringify({ activeBold, desktopGeometry, formattedHtml, selectedAfterFormat, dismissed, mobileGeometry }));
|
|
await browser.close();
|
|
"""
|
|
result = subprocess.run(
|
|
["node", "--input-type=module", "-e", script],
|
|
cwd=ROOT,
|
|
check=False,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
data = json.loads(result.stdout)
|
|
assert data["activeBold"] is True
|
|
assert data["desktopGeometry"] == {"within": True, "separate": True}
|
|
assert "<i>" in data["formattedHtml"] or "<em>" in data["formattedHtml"]
|
|
assert data["selectedAfterFormat"] == "Bold words"
|
|
assert data["dismissed"] is True
|
|
mobile = data["mobileGeometry"]
|
|
assert mobile["left"] >= 0 and mobile["right"] <= mobile["viewport"][0]
|
|
assert mobile["top"] >= 0 and mobile["bottom"] <= mobile["viewport"][1]
|
|
assert mobile["separate"] is True
|
|
assert mobile["dockedToEditorBottom"] is True
|
|
assert mobile["insideEditorTop"] is True
|
|
assert mobile["clearOfMainToolbar"] is True
|
|
assert mobile["width"] <= mobile["viewport"][0] - 16
|