/* This file is part of the KDE libraries SPDX-FileCopyrightText: 2009 Thomas McGuire SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "krichtextedittest.h" #include #include #include #include #include #include #include #include QTEST_MAIN(KRichTextEditTest) void KRichTextEditTest::testLinebreaks() { KRichTextEdit edit; edit.enableRichTextMode(); // Enter the text with keypresses, for some strange reason a normal setText() or // setPlainText() call doesn't do the trick QTest::keyClicks(&edit, QStringLiteral("a\r\r")); edit.setTextUnderline(true); QTest::keyClicks(&edit, QStringLiteral("b\r\r\rc")); QCOMPARE(edit.toPlainText(), QStringLiteral("a\n\nb\n\n\nc")); QString html = edit.toCleanHtml(); edit.clear(); edit.setHtml(html); QCOMPARE(edit.toPlainText(), QStringLiteral("a\n\nb\n\n\nc")); } void KRichTextEditTest::testUpdateLinkAdd() { KRichTextEdit edit; edit.enableRichTextMode(); // Add text, apply initial formatting, and add a link QTextCursor cursor = edit.textCursor(); cursor.insertText(QStringLiteral("Test")); QTextCharFormat charFormat = cursor.charFormat(); // Note that QTextEdit doesn't use the palette. Black is black. QCOMPARE(charFormat.foreground().color().name(), QColor(Qt::black).name()); cursor.select(QTextCursor::BlockUnderCursor); edit.setTextCursor(cursor); edit.setTextBold(true); edit.setTextItalic(true); edit.updateLink(QStringLiteral("http://www.kde.org"), QStringLiteral("KDE")); // Validate text and formatting cursor.movePosition(QTextCursor::Start); cursor.select(QTextCursor::WordUnderCursor); edit.setTextCursor(cursor); QCOMPARE(edit.toPlainText(), QStringLiteral("KDE ")); QCOMPARE(edit.fontItalic(), true); QCOMPARE(edit.fontWeight(), static_cast(QFont::Bold)); QCOMPARE(edit.fontUnderline(), true); charFormat = cursor.charFormat(); QCOMPARE(charFormat.foreground(), QBrush(KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::LinkText).color())); QCOMPARE(charFormat.underlineColor(), KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::LinkText).color()); QCOMPARE(charFormat.underlineStyle(), QTextCharFormat::SingleUnderline); } void KRichTextEditTest::testUpdateLinkRemove() { KRichTextEdit edit; edit.enableRichTextMode(); // Add text, apply initial formatting, and add a link QTextCursor cursor = edit.textCursor(); cursor.insertText(QStringLiteral("Test")); cursor.select(QTextCursor::BlockUnderCursor); edit.setTextCursor(cursor); edit.setTextBold(true); edit.setTextItalic(true); edit.updateLink(QStringLiteral("http://www.kde.org"), QStringLiteral("KDE")); // Remove link and validate formatting cursor.movePosition(QTextCursor::Start); cursor.select(QTextCursor::WordUnderCursor); edit.setTextCursor(cursor); edit.updateLink(QString(), QStringLiteral("KDE")); cursor.movePosition(QTextCursor::Start); cursor.select(QTextCursor::WordUnderCursor); edit.setTextCursor(cursor); QCOMPARE(edit.toPlainText(), QStringLiteral("KDE ")); QCOMPARE(edit.fontItalic(), true); QCOMPARE(edit.fontWeight(), static_cast(QFont::Bold)); QCOMPARE(edit.fontUnderline(), false); QTextCharFormat charFormat = cursor.charFormat(); QCOMPARE(charFormat.foreground().color().name(), QColor(Qt::black).name()); QCOMPARE(charFormat.underlineColor().name(), QColor(Qt::black).name()); QCOMPARE(charFormat.underlineStyle(), QTextCharFormat::NoUnderline); } void KRichTextEditTest::testHTMLLineBreaks() { KRichTextEdit edit; edit.enableRichTextMode(); // Create the following text: // A // // B QTest::keyClicks(&edit, QStringLiteral("a\r")); edit.setTextUnderline(true); QTest::keyClicks(&edit, QStringLiteral("\rb")); QString html = edit.toCleanHtml(); // The problem we have is that we need to "fake" being a viewer such // as Thunderbird or MS-Outlook to unit test our html line breaks. // For now, we'll parse the 6th line (the empty one) and make sure it has the proper format // The first four (4) HTML code lines are DOCTYPE through declaration const QStringList lines = html.split(QLatin1Char('\n')); // for (int idx=0; idx line QVERIFY(line6.startsWith(QStringLiteral("

 

")), "Empty lines must have   or otherwise 3rd party " "viewers render those as non-existing lines"); } void KRichTextEditTest::testHTMLOrderedLists() { // The problem we have is that we need to "fake" being a viewer such // as Thunderbird or MS-Outlook to unit test our html lists. // For now, we'll parse the 6th line (the
    element) and make sure it has the proper format KRichTextEdit edit; edit.enableRichTextMode(); edit.setTextUnderline(true); // create a numbered (ordered) list QTextCursor cursor = edit.textCursor(); cursor.insertList(QTextListFormat::ListDecimal); QTest::keyClicks(&edit, QStringLiteral("a\rb\rc\r")); QString html = edit.toCleanHtml(); const QStringList lines = html.split(QLatin1Char('\n')); // Uncomment this section in case the first test fails to see if the HTML // rendering has actually introduced a bug, or merely a problem with the unit test itself // // for (int idx=0; idx declaration line const QString &line6 = lines.at(lines.size() - 4); // qDebug() << line6; const QRegularExpression re(QStringLiteral(" element) and make sure it has the proper format // The first four (4) HTML code lines are DOCTYPE through declaration KRichTextEdit edit; edit.enableRichTextMode(); edit.setTextUnderline(true); // create a numbered (ordered) list QTextCursor cursor = edit.textCursor(); cursor.insertList(QTextListFormat::ListDisc); QTest::keyClicks(&edit, QStringLiteral("a\rb\rc\r")); QString html = edit.toCleanHtml(); const QStringList lines = html.split(QLatin1Char('\n')); // Uncomment this section in case the first test fails to see if the HTML // rendering has actually introduced a bug, or merely a problem with the unit test itself // // for (int idx=0; idx declaration line const QString &line6 = lines.at(lines.size() - 4); // qDebug() << line6; const QRegularExpression re(QStringLiteral("(QFont::Bold)); // Make sure it doesn't clutter undo stack (a single undo is sufficient) edit.undo(); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 0); QCOMPARE(edit.fontWeight(), static_cast(QFont::Normal)); // Set heading & keep writing, the text remains a heading edit.setHeadingLevel(2); QTest::keyClicks(&edit, QStringLiteral("cd")); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 2); QCOMPARE(edit.fontWeight(), static_cast(QFont::Bold)); // Now add a new line, make sure it's no longer a heading QTest::keyClick(&edit, '\r'); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 0); QCOMPARE(edit.fontWeight(), static_cast(QFont::Normal)); // Make sure creating new line is also undoable edit.undo(); QCOMPARE(edit.textCursor().position(), 5); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 2); QCOMPARE(edit.fontWeight(), static_cast(QFont::Bold)); // Add a new line and some more text, make sure it's still normal QTest::keyClicks(&edit, QStringLiteral("\ref")); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 0); QCOMPARE(edit.fontWeight(), static_cast(QFont::Normal)); // Go to beginning of this line, press Backspace -> lines should be merged, // current line should become a heading QTest::keyClick(&edit, Qt::Key_Home); QTest::keyClick(&edit, Qt::Key_Backspace); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 2); QCOMPARE(edit.fontWeight(), static_cast(QFont::Bold)); // Make sure this is also undoable edit.undo(); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 0); QCOMPARE(edit.fontWeight(), static_cast(QFont::Normal)); // Return it back QTest::keyClick(&edit, Qt::Key_Backspace); // The line is now "bcd|ef", "|" is cursor. Press Enter, the second line should remain a heading QTest::keyClick(&edit, Qt::Key_Return); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 2); QCOMPARE(edit.fontWeight(), static_cast(QFont::Bold)); // Change the heading level back to normal edit.setHeadingLevel(0); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 0); QCOMPARE(edit.fontWeight(), static_cast(QFont::Normal)); // Go to end of previous line, press Delete -> lines should be merged again QTextCursor cursor = edit.textCursor(); cursor.movePosition(QTextCursor::PreviousBlock); cursor.movePosition(QTextCursor::EndOfBlock); edit.setTextCursor(cursor); QTest::keyClick(&edit, Qt::Key_Delete); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 2); QCOMPARE(edit.fontWeight(), static_cast(QFont::Bold)); // Make sure this is also undoable edit.undo(); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 0); QCOMPARE(edit.fontWeight(), static_cast(QFont::Normal)); // Now playing with selection. The contents are currently: // --- // a // bcd // ef // gh // --- // Let's add a new line 'gh', select everything between "c" and "g" // and change heading. It should apply to both lines QTest::keyClicks(&edit, QStringLiteral("\rgh")); cursor.setPosition(4, QTextCursor::MoveAnchor); cursor.setPosition(10, QTextCursor::KeepAnchor); edit.setTextCursor(cursor); edit.setHeadingLevel(5); // In the end, both lines should change the heading (even before the selection, i.e. 'b'!) cursor.setPosition(3); edit.setTextCursor(cursor); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 5); QCOMPARE(edit.fontWeight(), static_cast(QFont::Bold)); // (and after the selection, i.e. 'f'!) cursor.setPosition(11); edit.setTextCursor(cursor); QCOMPARE(edit.textCursor().blockFormat().headingLevel(), 5); QCOMPARE(edit.fontWeight(), static_cast(QFont::Bold)); } void KRichTextEditTest::testRulerScroll() { // This is a test for bug 195828 KRichTextEdit edit; // Add some lines, so that scroll definitely appears for (int i = 0; i < 100; i++) { QTest::keyClicks(&edit, QStringLiteral("New line\r")); } // Widget has to be shown for the scrollbar to be adjusted edit.show(); // Ensure the scrollbar actually appears QVERIFY(edit.verticalScrollBar()->value() > 0); edit.insertHorizontalRule(); // Make sure scrollbar didn't jump to the top QVERIFY(edit.verticalScrollBar()->value() > 0); } void KRichTextEditTest::testNestedLists() { KRichTextEdit edit; // Simplest test: create a list with a single element QTest::keyClicks(&edit, QStringLiteral("el1")); edit.setListStyle(-static_cast(QTextListFormat::ListSquare)); QVERIFY(edit.textCursor().currentList()); QCOMPARE(edit.textCursor().currentList()->format().style(), QTextListFormat::ListSquare); // It should not be indentable, as there is nothing above QVERIFY(!edit.canIndentList()); // But it should be dedentable QVERIFY(edit.canDedentList()); // Press enter, a new element should be added QTest::keyClicks(&edit, QStringLiteral("\rel2")); QVERIFY(edit.textCursor().currentList()); QCOMPARE(edit.textCursor().currentList()->format().style(), QTextListFormat::ListSquare); // Change indentation edit.indentListMore(); edit.setListStyle(-static_cast(QTextListFormat::ListCircle)); QCOMPARE(edit.textCursor().currentList()->format().indent(), 2); QCOMPARE(edit.textCursor().currentList()->format().style(), QTextListFormat::ListCircle); // And another one; let's then change the style of "3" and see if "2" have also changed style QTest::keyClicks(&edit, QStringLiteral("\rel3")); edit.setListStyle(-static_cast(QTextListFormat::ListDecimal)); edit.moveCursor(QTextCursor::PreviousBlock); QCOMPARE(edit.textCursor().currentList()->format().style(), QTextListFormat::ListDecimal); // Now add another element, and dedent it, so the list should look like following: // [] el1 // 1. el2 // 2. el3 // [] el4 edit.moveCursor(QTextCursor::End); QTest::keyClicks(&edit, QStringLiteral("\rel4")); edit.indentListLess(); QCOMPARE(edit.textCursor().currentList()->format().style(), QTextListFormat::ListSquare); // Let's change the style to disc and see if first element have also changed the style edit.setListStyle(-static_cast(QTextListFormat::ListDisc)); edit.moveCursor(QTextCursor::Start); QCOMPARE(edit.textCursor().currentList()->format().style(), QTextListFormat::ListDisc); // Now let's play with selection. First we add couple subelements below, so the list is: // * el1 // 1. el2 // 2. el3 // * el4 // o el5 // o el6 edit.moveCursor(QTextCursor::End); QTest::keyClicks(&edit, QStringLiteral("\rel5")); edit.indentListMore(); edit.setListStyle(-static_cast(QTextListFormat::ListCircle)); QTest::keyClicks(&edit, QStringLiteral("\rel6")); // Let's select (el3-el5) and indent them. It should become: // * el1 // 1. el2 // 1. el3 // 2. el4 // o el5 // 3. el6 QTextCursor cursor(edit.document()); cursor.setPosition(9); cursor.setPosition(17, QTextCursor::KeepAnchor); edit.setTextCursor(cursor); edit.indentListMore(); edit.moveCursor(QTextCursor::End); QCOMPARE(edit.textCursor().currentList()->count(), 3); QCOMPARE(edit.textCursor().currentList()->format().style(), QTextListFormat::ListDecimal); // Now select el2-el5 and dedent them. It should become: // * el1 // * el2 // 1. el3 // * el4 // o el5 // o el6 cursor.setPosition(6); cursor.setPosition(18, QTextCursor::KeepAnchor); edit.setTextCursor(cursor); edit.indentListLess(); edit.moveCursor(QTextCursor::End); QCOMPARE(edit.textCursor().currentList()->count(), 2); QCOMPARE(edit.textCursor().currentList()->format().style(), QTextListFormat::ListCircle); // point at "el4" cursor.setPosition(13); QCOMPARE(cursor.currentList()->count(), 3); QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListDisc); // Select el4 && el5, dedent it, so el4 becomes a simple text: // * el1 // * el2 // 1. el3 // el4 // o el5 // o el6 cursor.setPosition(17, QTextCursor::KeepAnchor); edit.setTextCursor(cursor); edit.indentListLess(); // point cursor at "el4" cursor.setPosition(13); QVERIFY(!cursor.currentList()); // point at "el5", make sure it's a separate list now cursor.setPosition(16); QCOMPARE(cursor.currentList()->count(), 1); QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListCircle); // Make sure the selection is not dedentable anymore QVERIFY(!edit.canDedentList()); } #include "moc_krichtextedittest.cpp"