From d42147a73eb66a80eec9955eec89caa40d0ace11 Mon Sep 17 00:00:00 2001 From: nesquena-hermes Date: Fri, 26 Jun 2026 19:55:10 +0000 Subject: [PATCH] test(#5013): cover table-element-anchored + cell-to-prose enhanced-table copy fallthrough Adds the two remaining enhanced-table-copy boundary cases flagged as optional coverage when #4994 (fixes #4945) shipped: a selection anchored on the node itself, and a selection that starts in a cell and ends in surrounding prose, both must fall through to native copy. Test-only; the shipped guard already handles both correctly (no production change needed). --- tests/test_issue4945_markdown_table_copy.py | 99 +++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/tests/test_issue4945_markdown_table_copy.py b/tests/test_issue4945_markdown_table_copy.py index 9d4d54c43..95c5b0f43 100644 --- a/tests/test_issue4945_markdown_table_copy.py +++ b/tests/test_issue4945_markdown_table_copy.py @@ -586,3 +586,102 @@ console.log(JSON.stringify({ prevented: event.preventDefaultCalled, data: clipbo ) assert out["prevented"] is False assert out["data"] == {} + + +def test_table_element_anchored_selection_leaves_native_copy_unmodified(): + """#5013: a drag/selection anchored on the
element itself (rather than + inside corner cells) must fall through to native copy — sanitization only fires + for a true full-cell-span selection.""" + out = _run_js( + """ +const {table} = buildEnhancedTableFixture(true); + +// Selection anchored on the
node itself (a real-browser whole-table drag +// can land start/end on the table/tr rather than inside the corner text nodes). +const range = { + startContainer: table, + startOffset: 0, + endContainer: table, + endOffset: table.rows.length, + commonAncestorContainer: table, +}; + +window.getSelection = () => ({ + isCollapsed: false, + rangeCount: 1, + getRangeAt: () => range, +}); + +const clipboard = { + data: {}, + setData(type, value) { + this.data[type] = value; + } +}; + +const event = { + preventDefaultCalled: false, + preventDefault() { + this.preventDefaultCalled = true; + }, + clipboardData: clipboard, +}; + +_handleMarkdownTableCopy(event); +console.log(JSON.stringify({ prevented: event.preventDefaultCalled, data: clipboard.data })); +""" + ) + assert out["prevented"] is False + assert out["data"] == {} + + +def test_cell_to_prose_selection_leaves_native_copy_unmodified(): + """#5013: a selection that starts inside a table cell and ends in surrounding + prose must fall through to native copy (it is not a full-table selection, and + capturing it would clobber the trailing prose).""" + out = _run_js( + """ +const {root, table, body} = buildEnhancedTableFixture(true); +const after = makeElement('p'); +after.appendChild(new FakeText('trailing prose')); +root.appendChild(after); + +// Start inside a body cell's text, end in the trailing prose paragraph. +const range = { + startContainer: body.cells[0].children[0], + startOffset: 0, + endContainer: after.children[0], + endOffset: after.children[0].textContent.length, + commonAncestorContainer: root, + intersectsNode(node) { + return node === table; + }, +}; + +window.getSelection = () => ({ + isCollapsed: false, + rangeCount: 1, + getRangeAt: () => range, +}); + +const clipboard = { + data: {}, + setData(type, value) { + this.data[type] = value; + } +}; + +const event = { + preventDefaultCalled: false, + preventDefault() { + this.preventDefaultCalled = true; + }, + clipboardData: clipboard, +}; + +_handleMarkdownTableCopy(event); +console.log(JSON.stringify({ prevented: event.preventDefaultCalled, data: clipboard.data })); +""" + ) + assert out["prevented"] is False + assert out["data"] == {}