|
| 1 | +import pytest |
| 2 | + |
| 3 | +try: |
| 4 | + from cms.utils.urlutils import admin_reverse |
| 5 | +except ModuleNotFoundError: |
| 6 | + |
| 7 | + def admin_reverse(viewname, args=None, kwargs=None, current_app=None): |
| 8 | + from django.urls import reverse |
| 9 | + |
| 10 | + return reverse(f"admin:{viewname}", args, kwargs, current_app) |
| 11 | + |
| 12 | + |
| 13 | +from playwright.sync_api import expect |
| 14 | + |
| 15 | +from tests.fixtures import DJANGO_CMS4 |
| 16 | +from tests.integration.utils import login |
| 17 | + |
| 18 | + |
| 19 | +def clear_tiptap(page, tiptap): |
| 20 | + """Clear all content in the tiptap editor.""" |
| 21 | + tiptap.click() |
| 22 | + # Use tiptap's commands API to clear all content reliably |
| 23 | + page.evaluate( |
| 24 | + """() => { |
| 25 | + const el = document.querySelector('.ProseMirror.tiptap'); |
| 26 | + // Access the tiptap editor instance attached to the element |
| 27 | + if (el.editor) { |
| 28 | + el.editor.commands.clearContent(); |
| 29 | + } else { |
| 30 | + el.innerHTML = ''; |
| 31 | + } |
| 32 | + }""" |
| 33 | + ) |
| 34 | + |
| 35 | + |
| 36 | +def paste_text(page, text): |
| 37 | + """Paste plain text into the focused tiptap editor via a synthetic clipboard event.""" |
| 38 | + page.evaluate( |
| 39 | + """(text) => { |
| 40 | + const clipboardData = new DataTransfer(); |
| 41 | + clipboardData.setData('text/plain', text); |
| 42 | + const event = new ClipboardEvent('paste', { |
| 43 | + bubbles: true, |
| 44 | + cancelable: true, |
| 45 | + clipboardData: clipboardData, |
| 46 | + }); |
| 47 | + document.querySelector('.ProseMirror.tiptap').dispatchEvent(event); |
| 48 | + }""", |
| 49 | + text, |
| 50 | + ) |
| 51 | + |
| 52 | + |
| 53 | +@pytest.mark.django_db |
| 54 | +@pytest.mark.skipif(not DJANGO_CMS4, reason="Integration tests only work on Django CMS 4") |
| 55 | +def test_paste_markdown_converts_to_html(live_server, page, text_plugin, superuser): |
| 56 | + """Test that pasting markdown text into the tiptap editor converts it to HTML.""" |
| 57 | + login(live_server, page, superuser) |
| 58 | + |
| 59 | + page.goto(f"{live_server.url}{admin_reverse('cms_placeholder_edit_plugin', args=(text_plugin.pk,))}") |
| 60 | + |
| 61 | + tiptap = page.locator(".ProseMirror.tiptap") |
| 62 | + expect(tiptap).to_be_visible() |
| 63 | + |
| 64 | + clear_tiptap(page, tiptap) |
| 65 | + paste_text(page, "## Hello World\n\nThis is **bold** and *italic* text.") |
| 66 | + |
| 67 | + # Scope assertions to editor content only (exclude toolbar/plugin-selector elements) |
| 68 | + content = tiptap.locator("> h2") |
| 69 | + expect(content).to_have_text("Hello World") |
| 70 | + |
| 71 | + expect(tiptap.locator("> p strong")).to_have_text("bold") |
| 72 | + expect(tiptap.locator("> p em")).to_have_text("italic") |
| 73 | + |
| 74 | + |
| 75 | +@pytest.mark.django_db |
| 76 | +@pytest.mark.skipif(not DJANGO_CMS4, reason="Integration tests only work on Django CMS 4") |
| 77 | +def test_paste_plain_text_not_converted(live_server, page, text_plugin, superuser): |
| 78 | + """Test that pasting plain text without markdown patterns is not converted.""" |
| 79 | + login(live_server, page, superuser) |
| 80 | + |
| 81 | + page.goto(f"{live_server.url}{admin_reverse('cms_placeholder_edit_plugin', args=(text_plugin.pk,))}") |
| 82 | + |
| 83 | + tiptap = page.locator(".ProseMirror.tiptap") |
| 84 | + expect(tiptap).to_be_visible() |
| 85 | + |
| 86 | + clear_tiptap(page, tiptap) |
| 87 | + |
| 88 | + plain_text = "Just some regular text without any formatting." |
| 89 | + paste_text(page, plain_text) |
| 90 | + |
| 91 | + # Verify text was pasted as plain text (no markdown conversion) |
| 92 | + expect(tiptap.locator("> p").first).to_have_text(plain_text) |
| 93 | + assert tiptap.locator("> h1, > h2, > h3").count() == 0 |
| 94 | + assert tiptap.locator("> p strong, > p em").count() == 0 |
0 commit comments