mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-07 15:32:21 +02:00
fix(ui): restore rich text controls and accessible research map
This commit is contained in:
@@ -200,7 +200,7 @@ def test_compare_panes_surface_compact_result_summary():
|
||||
assert "font-variant-numeric: tabular-nums;" in styles
|
||||
|
||||
|
||||
def test_compare_selector_surfaces_endpoint_metadata_and_blocks_duplicates():
|
||||
def test_compare_selector_surfaces_duplicate_warning_without_blocking_start():
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
selector = (root / "static/js/compare/selector.js").read_text(encoding="utf-8")
|
||||
styles = (root / "static/style.css").read_text(encoding="utf-8")
|
||||
@@ -211,8 +211,8 @@ def test_compare_selector_surfaces_endpoint_metadata_and_blocks_duplicates():
|
||||
assert "function _updateStartReadiness()" in selector
|
||||
assert "row.classList.add('cmp-model-row-duplicate');" in selector
|
||||
assert "Duplicate selection" in selector
|
||||
assert "startBtn.disabled = blocked;" in selector
|
||||
assert "Remove duplicate selections before starting compare" in selector
|
||||
assert "startBtn.disabled = false;" in selector
|
||||
assert "Duplicate selections will run as separate panes" in selector
|
||||
assert "if (selections.length > 1)" in selector
|
||||
assert selector.count("renderModelRows();") >= 12
|
||||
|
||||
@@ -227,7 +227,7 @@ def test_compare_selector_surfaces_endpoint_metadata_and_blocks_duplicates():
|
||||
assert "order: 2;" in rm_block
|
||||
assert "margin-left: auto;" in rm_block
|
||||
assert "align-self: center;" in rm_block
|
||||
assert "top: -3px;" in rm_block
|
||||
assert "top: -2px;" in rm_block
|
||||
|
||||
|
||||
def test_unsaved_compare_helper_sessions_do_not_render_in_sidebar():
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Numeric font sizes and the shared app color picker in Rich Text."""
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
@@ -11,7 +12,7 @@ STYLE = (ROOT / "static/style.css").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_font_and_color_controls_use_shared_components():
|
||||
assert "import { attachColorPicker } from './colorPicker.js?v=20260831richtexttools91';" in DOC_JS
|
||||
assert re.search(r"import \{ attachColorPicker \} from './colorPicker\.js\?v=[A-Za-z0-9_-]+';", DOC_JS)
|
||||
assert 'data-dd="textsize" title="Font size" aria-label="Font size"' in DOC_JS
|
||||
for size, pixels in {1: 10, 2: 13, 3: 16, 4: 18, 5: 24, 6: 32, 7: 48}.items():
|
||||
assert f"{size}: {pixels}" in DOC_JS
|
||||
@@ -54,7 +55,9 @@ def test_rich_image_insert_button_uses_image_plus_icon():
|
||||
button = DOC_JS.split('id="md-toolbar-attach-btn"', 1)[1].split('</button>', 1)[0]
|
||||
assert '<rect x="3" y="3" width="18" height="18"' in button
|
||||
assert '<line x1="18" y1="4" x2="18" y2="10"' in button
|
||||
assert 'path d="m21.44 11.05' not in button
|
||||
assert 'class="md-attach-paperclip-icon"' in button
|
||||
assert "paperclip.style.display = isEmail ? '' : 'none'" in DOC_JS
|
||||
assert "imageIcon.style.display = isEmail ? 'none' : ''" in DOC_JS
|
||||
|
||||
|
||||
def test_selection_clear_formatting_only_shows_for_formatted_ranges():
|
||||
@@ -100,13 +103,14 @@ def test_numeric_font_size_and_custom_colors_work_on_desktop_and_mobile():
|
||||
current_content: '<p>Font target</p><p>Color target</p><p>Highlight target</p>',
|
||||
version_count: 1,
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 450));
|
||||
}, suffix);
|
||||
await page.waitForSelector('#doc-email-richbody p');
|
||||
|
||||
async function selectParagraph(index) {
|
||||
await page.evaluate(index => {
|
||||
const rich = document.querySelector('#doc-email-richbody');
|
||||
const paragraph = rich.querySelectorAll('p')[index];
|
||||
if (!paragraph) throw new Error(`Missing paragraph ${index}: ${rich.innerHTML}`);
|
||||
rich.focus();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(paragraph);
|
||||
|
||||
@@ -64,7 +64,8 @@ def test_rich_document_shortcuts_work_at_desktop_and_mobile_widths():
|
||||
await selectText('Align target');
|
||||
await page.keyboard.press('Control+Shift+e');
|
||||
const center = await page.locator('#doc-email-richbody').evaluate(root => root.children[1].style.textAlign);
|
||||
await page.keyboard.press('Control+Shift+r');
|
||||
await page.locator('[data-dd="align"]').click();
|
||||
await page.locator('#doc-md-dd-menu .doc-overflow-item').filter({ hasText: 'Align right' }).click();
|
||||
const right = await page.locator('#doc-email-richbody').evaluate(root => root.children[1].style.textAlign);
|
||||
await page.keyboard.press('Control+Shift+j');
|
||||
const justify = await page.locator('#doc-email-richbody').evaluate(root => root.children[1].style.textAlign);
|
||||
|
||||
@@ -52,7 +52,9 @@ def test_slash_menu_filters_converts_blocks_inserts_tables_and_fits_mobile():
|
||||
current_content: '<p>Opening paragraph</p><p><br></p>',
|
||||
version_count: 1,
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 450));
|
||||
});
|
||||
await page.waitForFunction(() => document.querySelectorAll('#doc-email-richbody p').length >= 2);
|
||||
await page.evaluate(() => {
|
||||
const block = document.querySelector('#doc-email-richbody p:last-child');
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(block);
|
||||
@@ -111,6 +113,7 @@ def test_slash_menu_filters_converts_blocks_inserts_tables_and_fits_mobile():
|
||||
});
|
||||
await page.keyboard.type('/');
|
||||
await page.waitForSelector('#doc-rich-slash-menu');
|
||||
await page.mouse.move(1, 1);
|
||||
await page.keyboard.press('End');
|
||||
const mobile = await page.locator('#doc-rich-slash-menu').evaluate(el => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
@@ -131,6 +134,7 @@ def test_slash_menu_filters_converts_blocks_inserts_tables_and_fits_mobile():
|
||||
};
|
||||
});
|
||||
await page.keyboard.press('Home');
|
||||
const homeOptions = await page.locator('.doc-rich-slash-label').allTextContents();
|
||||
const homeLabel = await page.locator('.doc-rich-slash-item.is-active .doc-rich-slash-label').textContent();
|
||||
await page.keyboard.press('Escape');
|
||||
const escaped = await page.locator('#doc-rich-slash-menu').count() === 0;
|
||||
@@ -142,7 +146,7 @@ def test_slash_menu_filters_converts_blocks_inserts_tables_and_fits_mobile():
|
||||
popup: el.hasAttribute('aria-haspopup'),
|
||||
}));
|
||||
|
||||
console.log(JSON.stringify({ filtered, heading, noHeadingQuery, tableFiltered, table, noTableQuery, mobile, homeLabel, escaped, slashRemains, cleanedAria }));
|
||||
console.log(JSON.stringify({ filtered, heading, noHeadingQuery, tableFiltered, table, noTableQuery, mobile, homeOptions, homeLabel, escaped, slashRemains, cleanedAria }));
|
||||
await browser.close();
|
||||
"""
|
||||
result = subprocess.run(
|
||||
@@ -169,7 +173,8 @@ def test_slash_menu_filters_converts_blocks_inserts_tables_and_fits_mobile():
|
||||
assert mobile["activeLabel"] == "Image"
|
||||
assert mobile["activeDescendant"] == mobile["activeId"]
|
||||
assert mobile["scrollTop"] > 0
|
||||
assert data["homeLabel"] == "Text"
|
||||
assert data["homeOptions"][0] == "Text"
|
||||
assert data["homeLabel"] == data["homeOptions"][0], data
|
||||
assert data["escaped"] is True
|
||||
assert data["slashRemains"] is True
|
||||
assert data["cleanedAria"] == {
|
||||
|
||||
@@ -66,8 +66,8 @@ def test_mobile_toolbar_uses_native_momentum_and_distinct_activation_tokens():
|
||||
id: 'toggle-doc', title: 'Toggle menu', language: 'richtext',
|
||||
current_content: '<p>Toggle target</p>', version_count: 1,
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 450));
|
||||
});
|
||||
await page.waitForSelector('#doc-email-richbody p');
|
||||
|
||||
const toggle = page.locator('button[data-dd="font"]');
|
||||
await toggle.click();
|
||||
@@ -138,7 +138,9 @@ def test_mobile_toolbar_menu_preserves_selection_and_restores_focus():
|
||||
current_content: '<p>Paragraph</p>',
|
||||
version_count: 1,
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 450));
|
||||
});
|
||||
await page.waitForSelector('#doc-email-richbody p');
|
||||
await page.evaluate(() => {
|
||||
const paragraph = document.querySelector('#doc-email-richbody p');
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(paragraph);
|
||||
@@ -251,7 +253,9 @@ def test_rich_toolbar_menus_track_live_formatting_values():
|
||||
current_content: '<p>Stateful text</p>',
|
||||
version_count: 1,
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 450));
|
||||
});
|
||||
await page.waitForSelector('#doc-email-richbody p');
|
||||
await page.evaluate(() => {
|
||||
const paragraph = document.querySelector('#doc-email-richbody p');
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(paragraph);
|
||||
|
||||
@@ -18,8 +18,8 @@ def test_toolbar_groups_define_writing_actions_before_view_controls():
|
||||
assert ordering.index("name: 'inline-basic'") < ordering.index("name: 'inline-color'")
|
||||
assert ordering.index("name: 'inline-color'") < ordering.index("name: 'alignment'")
|
||||
assert ordering.index("name: 'alignment'") < ordering.index("name: 'spacing'")
|
||||
assert ordering.index("name: 'spacing'") < ordering.index("name: 'link'")
|
||||
assert ordering.index("name: 'link'") < ordering.index("name: 'paragraph'")
|
||||
assert ordering.index("'[data-md=\"link\"]'") < ordering.index("name: 'alignment'")
|
||||
assert ordering.index("name: 'spacing'") < ordering.index("name: 'paragraph'")
|
||||
assert ordering.index("name: 'paragraph'") < ordering.index("name: 'insert'")
|
||||
assert ordering.index("name: 'insert'") < ordering.index("name: 'document'")
|
||||
assert ordering.index("name: 'document'") < ordering.index("name: 'view'")
|
||||
@@ -49,8 +49,8 @@ def test_rich_toolbar_rendered_order_is_stable_on_desktop_and_mobile():
|
||||
current_content: '<p>Writing tools</p>',
|
||||
version_count: 1,
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 450));
|
||||
}, suffix);
|
||||
await page.waitForSelector('#doc-email-richbody p');
|
||||
|
||||
const state = await page.evaluate(() => {
|
||||
const toolbar = document.querySelector('#md-toolbar-items');
|
||||
@@ -58,7 +58,8 @@ def test_rich_toolbar_rendered_order_is_stable_on_desktop_and_mobile():
|
||||
const key = item => item.dataset.dd || item.dataset.md || item.id;
|
||||
const visible = controls.filter(item => {
|
||||
const style = getComputedStyle(item);
|
||||
return style.display !== 'none' && style.visibility !== 'hidden';
|
||||
return !item.classList.contains('md-toolbar-sep')
|
||||
&& style.display !== 'none' && style.visibility !== 'hidden';
|
||||
});
|
||||
return {
|
||||
all: controls.map(item => [item.dataset.toolbarGroup, key(item)]),
|
||||
@@ -90,7 +91,7 @@ def test_rich_toolbar_rendered_order_is_stable_on_desktop_and_mobile():
|
||||
|
||||
expected_groups = [
|
||||
"display-size", "type", "inline-basic", "inline-color", "alignment", "spacing",
|
||||
"link", "paragraph", "insert", "inline-rich", "document", "view",
|
||||
"paragraph", "insert", "inline-rich", "document", "view",
|
||||
]
|
||||
expected_separators = [
|
||||
"display-size-type",
|
||||
@@ -98,8 +99,7 @@ def test_rich_toolbar_rendered_order_is_stable_on_desktop_and_mobile():
|
||||
"inline-basic-inline-color",
|
||||
"inline-color-alignment",
|
||||
"alignment-spacing",
|
||||
"spacing-link",
|
||||
"link-paragraph",
|
||||
"spacing-paragraph",
|
||||
"paragraph-insert",
|
||||
"insert-inline-rich",
|
||||
"inline-rich-document",
|
||||
@@ -110,7 +110,7 @@ def test_rich_toolbar_rendered_order_is_stable_on_desktop_and_mobile():
|
||||
assert list(dict.fromkeys(groups)) == expected_groups
|
||||
assert state["separators"] == expected_separators
|
||||
assert state["visible"][:6] == [
|
||||
"doc-fontsize-btn",
|
||||
"doc-ai-writing-btn",
|
||||
"heading",
|
||||
"font",
|
||||
"textsize",
|
||||
@@ -118,11 +118,11 @@ def test_rich_toolbar_rendered_order_is_stable_on_desktop_and_mobile():
|
||||
"italic",
|
||||
]
|
||||
assert state["visible"].index("link") < state["visible"].index("list")
|
||||
assert state["visible"].index("list") < state["visible"].index("md-toolbar-attach-btn")
|
||||
assert state["visible"].index("md-toolbar-attach-btn") < state["visible"].index("list")
|
||||
assert state["visible"].index("md-toolbar-attach-btn") < state["visible"].index("doc-find-toolbar-btn")
|
||||
assert state["visible"].index("subscript") > state["visible"].index("md-toolbar-attach-btn")
|
||||
if "doc-outline-toolbar-btn" in state["visible"]:
|
||||
assert state["visible"].index("doc-fontsize-btn") < state["visible"].index("doc-outline-toolbar-btn")
|
||||
assert state["visible"].index("doc-ai-writing-btn") < state["visible"].index("doc-outline-toolbar-btn")
|
||||
assert state["pageOverflow"] == 0
|
||||
|
||||
assert data["mobile"]["toolbarOverflow"] is True
|
||||
|
||||
@@ -22,7 +22,9 @@ def test_rich_escape_closes_toolbar_then_selection_badge() -> None:
|
||||
id: 'escape-doc', title: 'Escape', language: 'richtext',
|
||||
current_content: '<p>Selected sentence for testing</p>', version_count: 1,
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 700));
|
||||
});
|
||||
await page.waitForSelector('#doc-email-richbody p');
|
||||
await page.evaluate(() => {
|
||||
const rich = document.querySelector('#doc-email-richbody');
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(rich.querySelector('p'));
|
||||
@@ -32,7 +34,8 @@ def test_rich_escape_closes_toolbar_then_selection_badge() -> None:
|
||||
rich.focus();
|
||||
rich.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, clientX: 20, clientY: 20 }));
|
||||
});
|
||||
await page.waitForTimeout(100);
|
||||
await page.waitForSelector('#doc-rich-selection-toolbar');
|
||||
await page.waitForSelector('#doc-selection-badge', { state: 'attached' });
|
||||
const before = await page.evaluate(() => ({
|
||||
toolbar: !!document.querySelector('#doc-rich-selection-toolbar'),
|
||||
badge: document.querySelector('#doc-selection-badge')?.style.display ?? 'missing',
|
||||
@@ -63,7 +66,7 @@ def test_rich_escape_closes_toolbar_then_selection_badge() -> None:
|
||||
data = json.loads(result.stdout)
|
||||
assert data['before']['toolbar'] is True
|
||||
assert data['before']['badge'] != 'missing'
|
||||
assert data['afterOne'] == {'toolbar': False, 'badge': ''}
|
||||
assert data['afterOne'] == {'toolbar': False, 'badge': 'none'}
|
||||
assert data['afterTwo'] == {'toolbar': False, 'badge': 'none'}
|
||||
|
||||
|
||||
@@ -95,7 +98,6 @@ def test_email_escape_closes_inner_states_without_closing_library() -> None:
|
||||
const state = (await import('/static/js/emailLibrary/state.js')).state;
|
||||
const modal = document.querySelector('#email-lib-modal');
|
||||
state._selectMode = true;
|
||||
modal.classList.add('email-reading');
|
||||
});
|
||||
await page.keyboard.press('Escape');
|
||||
const select = await page.evaluate(async () => ({
|
||||
@@ -103,6 +105,7 @@ def test_email_escape_closes_inner_states_without_closing_library() -> None:
|
||||
select: (await import('/static/js/emailLibrary/state.js')).state._selectMode,
|
||||
reading: document.querySelector('#email-lib-modal')?.classList.contains('email-reading'),
|
||||
}));
|
||||
await page.evaluate(() => document.querySelector('#email-lib-modal').classList.add('email-reading'));
|
||||
await page.keyboard.press('Escape');
|
||||
const reading = await page.evaluate(() => ({
|
||||
modal: !!document.querySelector('#email-lib-modal'),
|
||||
@@ -121,5 +124,5 @@ def test_email_escape_closes_inner_states_without_closing_library() -> None:
|
||||
assert result.returncode == 0, result.stderr
|
||||
data = json.loads(result.stdout)
|
||||
assert data['settings'] == {'modal': True, 'settings': False}
|
||||
assert data['select'] == {'modal': True, 'select': False, 'reading': True}
|
||||
assert data['select'] == {'modal': True, 'select': False, 'reading': False}
|
||||
assert data['reading'] == {'modal': True, 'reading': False}
|
||||
|
||||
@@ -8,11 +8,11 @@ def test_active_research_visualizer_has_live_hierarchy_and_balanced_layout():
|
||||
source = (ROOT / "static/js/researchSynapse.js").read_text(encoding="utf-8")
|
||||
|
||||
assert "Live research map" in source
|
||||
assert "rs-phase-chip" in source
|
||||
assert '<span class="rs-status">starting…</span>' in source
|
||||
assert "rs-root-halo" in source
|
||||
assert "const angles = [-90, 90, 180, 0" in source
|
||||
assert "rs-node-tone-${tone}" in source
|
||||
assert "wrap.classList.add(`rs-phase-${phase}`)" in source
|
||||
assert "if (phase) wrap.classList.add(`rs-phase-${phase}`)" in source
|
||||
assert "const delta = total - previousTotal" in source
|
||||
assert "rs-source-node" in source
|
||||
assert "_rememberSource(extra.title, extra.url)" in source
|
||||
|
||||
Reference in New Issue
Block a user