kde-playground/kdepimlibs/kpimtextedit/tests/textedittest.cpp

448 lines
18 KiB
C++
Raw Normal View History

/*
Copyright (c) 2009 Thomas McGuire <mcguire@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "qtest_kde.h"
#include "textedittest.h"
#include "../textedit.h"
#include "kmime/kmime_codecs.h"
#include <KIconLoader>
#include <QTextCursor>
#include <qtestevent.h>
using namespace KPIMTextEdit;
QTEST_KDEMAIN( TextEditTester, GUI )
void TextEditTester::testFormattingUsed()
{
// This method tries to test everything that krichtextedit makes available, so
// we can sure that in KMail, when the user uses some formatting, the mail is actually
// sent as HTML mail
TextEdit textEdit;
QVERIFY( !textEdit.isFormattingUsed() );
// Insert some text.
QTextCursor cursor( textEdit.document() );
cursor.insertText( QLatin1String( "Hello World!!" ) );
QVERIFY( !textEdit.isFormattingUsed() );
cursor.setPosition( 1 );
textEdit.setTextCursor( cursor );
//
// Test link
//
QString someUrl = QLatin1String( "www.test.de" );
QString altText = QLatin1String( "Hello" );
textEdit.updateLink( someUrl, altText );
QVERIFY( textEdit.isFormattingUsed() );
QCOMPARE( textEdit.currentLinkUrl(), someUrl );
QCOMPARE( textEdit.currentLinkText(), altText );
cursor.setPosition( 1 );
textEdit.setTextCursor( cursor );
textEdit.updateLink( QString(), QLatin1String( "Hello" ) );
QVERIFY( textEdit.currentLinkUrl().isEmpty() );
QVERIFY( !textEdit.currentLinkText().isEmpty() );
QVERIFY( !textEdit.isFormattingUsed() );
//
// Test alignment
//
cursor.setPosition( 1 );
textEdit.setTextCursor( cursor );
textEdit.alignRight();
QVERIFY( textEdit.isFormattingUsed() );
QCOMPARE( textEdit.alignment(), Qt::AlignRight );
textEdit.alignLeft();
QVERIFY( !textEdit.isFormattingUsed() );
textEdit.alignCenter();
QCOMPARE( textEdit.alignment(), Qt::AlignHCenter );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.alignJustify();
QCOMPARE( textEdit.alignment(), Qt::AlignJustify );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.alignLeft();
QCOMPARE( textEdit.alignment(), Qt::AlignLeft );
QVERIFY( !textEdit.isFormattingUsed() );
//
// Test layout direction
//
textEdit.selectAll();
QTextCharFormat direction;
direction.setLayoutDirection( Qt::RightToLeft );
textEdit.mergeCurrentCharFormat( direction );
QVERIFY( textEdit.isFormattingUsed() );
direction.setLayoutDirection( Qt::LeftToRight );
textEdit.mergeCurrentCharFormat( direction );
QVERIFY( textEdit.isFormattingUsed() );
//
// Test lists
//
textEdit.setListStyle( QTextListFormat::ListCircle );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setListStyle( 0 );
QVERIFY( textEdit.isFormattingUsed() );
//
// Test font attributes
//
textEdit.setFontFamily( QLatin1String( "Times" ) );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setFontFamily( textEdit.document()->defaultFont().family() );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setFontSize( 48 );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setFontSize( textEdit.document()->defaultFont().pointSize() );
QVERIFY( textEdit.isFormattingUsed() );
QFont myFont = textEdit.document()->defaultFont();
myFont.setStyle( QFont::StyleOblique );
textEdit.setFont( myFont );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setFont( textEdit.document()->defaultFont() );
QVERIFY( textEdit.isFormattingUsed() );
//
// Test bold, italic, underline and strikeout
//
textEdit.setTextBold( true );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextBold( false );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextUnderline( true );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextUnderline( false );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextItalic( true );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextItalic( false );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextStrikeOut( true );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextStrikeOut( false );
QVERIFY( textEdit.isFormattingUsed() );
//
// Color
//
QColor oldForeground = textEdit.document()->firstBlock().charFormat().foreground().color();
textEdit.setTextForegroundColor( Qt::red );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextForegroundColor( oldForeground );
QVERIFY( textEdit.isFormattingUsed() );
QColor oldBackground = textEdit.document()->firstBlock().charFormat().background().color();
textEdit.setTextBackgroundColor( Qt::red );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextBackgroundColor( oldBackground );
QVERIFY( textEdit.isFormattingUsed() );
//
// Horizontal rule
//
textEdit.insertHorizontalRule();
QVERIFY( textEdit.isFormattingUsed() );
// No way to easily remove the horizontal line, so clear the text edit and start over
textEdit.clear();
cursor.insertText( QLatin1String( "Hello World!!" ) );
QVERIFY( !textEdit.isFormattingUsed() );
cursor.setPosition( 1 );
textEdit.setTextCursor( cursor );
//
// Sub and superscript
//
textEdit.setTextSuperScript( true );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextSuperScript( false );
QVERIFY( !textEdit.isFormattingUsed() );
textEdit.setTextSubScript( true );
QVERIFY( textEdit.isFormattingUsed() );
textEdit.setTextSubScript( false );
QVERIFY( !textEdit.isFormattingUsed() );
//
// Image
//
QString imagePath = KIconLoader::global()->iconPath( QLatin1String( "folder-new" ), KIconLoader::Small, false );
textEdit.addImage( imagePath );
QVERIFY( textEdit.isFormattingUsed() );
cursor = textEdit.textCursor();
cursor.movePosition( QTextCursor::Left, QTextCursor::KeepAnchor, 1 );
cursor.removeSelectedText();
QVERIFY( !textEdit.isFormattingUsed() );
}
void TextEditTester::testQuoting()
{
TextEdit edit;
QVERIFY( edit.isLineQuoted( QLatin1String( "> Hello" ) ) );
QVERIFY( edit.isLineQuoted( QLatin1String( ">Hello" ) ) );
QVERIFY( !edit.isLineQuoted( QLatin1String( "Hello" ) ) );
QCOMPARE( edit.quoteLength( QLatin1String( "Hello" ) ), 0 );
QCOMPARE( edit.quoteLength( QLatin1String( ">Hello" ) ), 1 );
QCOMPARE( edit.quoteLength( QLatin1String( "> Hello" ) ), 2 );
QCOMPARE( edit.quoteLength( QLatin1String( ">>>Hello" ) ), 3 );
QCOMPARE( edit.quoteLength( QLatin1String( "> > > Hello" ) ), 6 );
QCOMPARE( edit.quoteLength( QLatin1String( "|Hello" ) ), 1 );
QCOMPARE( edit.quoteLength( QLatin1String( "| |Hello" ) ), 3 );
}
void TextEditTester::testCleanText()
{
TextEdit edit;
QLatin1String html( "<html><head></head><body>Heelllo&nbsp;World<br>Bye!</body></html>" );
QLatin1String plain( "Heelllo World\nBye!" );
edit.setTextOrHtml( html );
edit.addImage( KIconLoader::global()->iconPath( QLatin1String( "folder-new" ), KIconLoader::Small, false ) );
QVERIFY( edit.textMode() == TextEdit::Rich );
QCOMPARE( edit.toCleanPlainText(), plain );
edit.show(); // < otherwise toWrappedPlainText can't work, it needs a layout
QCOMPARE( edit.toWrappedPlainText(), plain );
}
void TextEditTester::testEnter_data()
{
QTest::addColumn<QString>( "initalText" );
QTest::addColumn<QString>( "expectedText" );
QTest::addColumn<int>( "cursorPos" );
QTest::newRow( "" ) << QString::fromLatin1( "> Hello World" )
<< QString::fromLatin1( "> Hello \n> World" )
<< 8;
QTest::newRow( "" ) << QString::fromLatin1( "Hello World" )
<< QString::fromLatin1( "Hello \nWorld" )
<< 6;
QTest::newRow( "" ) << QString::fromLatin1( "> Hello World" )
<< QString::fromLatin1( "> Hello World\n" )
<< 13;
QTest::newRow( "" ) << QString::fromLatin1( ">Hello World" )
<< QString::fromLatin1( ">Hello \n>World" )
<< 7;
QTest::newRow( "" ) << QString::fromLatin1( "> > Hello World" )
<< QString::fromLatin1( "> > Hello \n> > World" )
<< 10;
QTest::newRow( "" ) << QString::fromLatin1( "| | Hello World" )
<< QString::fromLatin1( "| | Hello \n| | World" )
<< 10;
}
void TextEditTester::testEnter()
{
QFETCH( QString, initalText );
QFETCH( QString, expectedText );
QFETCH( int, cursorPos );
TextEdit edit;
edit.setPlainText( initalText );
QTextCursor textCursor( edit.document() );
textCursor.setPosition( cursorPos );
edit.setTextCursor( textCursor );
QTest::keyClick( &edit, Qt::Key_Return );
QCOMPARE( edit.toPlainText(), expectedText );
}
void TextEditTester::testImages()
{
TextEdit edit;
QString image1Path = KIconLoader::global()->iconPath( QLatin1String( "folder-new" ), KIconLoader::Small, false );
QString image2Path = KIconLoader::global()->iconPath( QLatin1String( "arrow-up" ), KIconLoader::Small, false );
// Add one image, check that embeddedImages() returns the right stuff
edit.addImage( image1Path );
KPIMTextEdit::ImageList images = edit.embeddedImages();
KPIMTextEdit::ImageWithNameList imagesWithNames = edit.imagesWithName();
QCOMPARE( images.size(), 1 );
QCOMPARE( imagesWithNames.size(), 1 );
EmbeddedImage *image = images.first().data();
ImageWithName *imageWithName = imagesWithNames.first().data();
QCOMPARE( image->imageName, QString::fromLatin1( "folder-new.png" ) );
QCOMPARE( imageWithName->name, QString::fromLatin1( "folder-new.png" ) );
// Also check that it loads the correct image
QImage diskImage( image1Path );
QBuffer buffer;
buffer.open( QIODevice::WriteOnly );
diskImage.save( &buffer, "PNG" );
QBuffer imageWithNameBuffer;
imageWithNameBuffer.open( QIODevice::WriteOnly );
imageWithName->image.save( &imageWithNameBuffer, "PNG" );
QByteArray encodedImage = KMime::Codec::codecForName( "base64" )->encode( buffer.buffer() );
QCOMPARE( image->image, encodedImage );
QCOMPARE( buffer.buffer(), imageWithNameBuffer.buffer() );
// No image should be there after clearing
edit.clear();
QVERIFY( edit.embeddedImages().isEmpty() );
QVERIFY( edit.imagesWithName().isEmpty() );
// Check that manually removing the image also empties the image list
edit.addImage( image1Path );
QCOMPARE( edit.embeddedImages().size(), 1 );
QCOMPARE( edit.imagesWithName().size(), 1 );
QTextCursor cursor = edit.textCursor();
cursor.setPosition( 0, QTextCursor::MoveAnchor );
cursor.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, 1 );
cursor.removeSelectedText();
QVERIFY( edit.embeddedImages().isEmpty() );
QVERIFY( edit.imagesWithName().isEmpty() );
// Check that adding the identical image two times only adds the image once
edit.addImage( image1Path );
edit.addImage( image1Path );
QCOMPARE( edit.embeddedImages().size(), 1 );
QCOMPARE( edit.imagesWithName().size(), 1 );
// Another different image added, and we should have two images
edit.clear();
edit.addImage( image1Path );
edit.addImage( image2Path );
images = edit.embeddedImages();
imagesWithNames = edit.imagesWithName();
QCOMPARE( images.size(), 2 );
QCOMPARE( imagesWithNames.size(), 2 );
KPIMTextEdit::EmbeddedImage *image1 = images.first().data();
KPIMTextEdit::EmbeddedImage *image2 = images.last().data();
KPIMTextEdit::ImageWithName *imageWithName1 = imagesWithNames.first().data();
KPIMTextEdit::ImageWithName *imageWithName2 = imagesWithNames.last().data();
QCOMPARE( image1->imageName, QString::fromLatin1( "folder-new2.png" ) ); // ### FIXME: should be folder-new.png, but QTextEdit provides no way to remove cached resources!
QCOMPARE( imageWithName1->name, QString::fromLatin1( "folder-new2.png" ) );
QCOMPARE( image2->imageName, QString::fromLatin1( "arrow-up.png" ) );
QCOMPARE( imageWithName2->name, QString::fromLatin1( "arrow-up.png" ) );
QVERIFY( image1->contentID != image2->contentID );
}
void TextEditTester::testImageHtmlCode()
{
TextEdit edit;
QString image1Path = KIconLoader::global()->iconPath( QLatin1String( "folder-new" ), KIconLoader::Small, false );
QString image2Path = KIconLoader::global()->iconPath( QLatin1String( "arrow-up" ), KIconLoader::Small, false );
edit.addImage( image1Path );
edit.addImage( image2Path );
KPIMTextEdit::ImageList images = edit.embeddedImages();
QCOMPARE( images.size(), 2 );
KPIMTextEdit::EmbeddedImage *image1 = images.first().data();
KPIMTextEdit::EmbeddedImage *image2 = images.last().data();
QString startHtml = QLatin1String( "<img src=\"arrow-up.png\"><img src=\"folder-new.png\">Bla<b>Blub</b>" );
QString endHtml = QString( QLatin1String( "<img src=\"cid:%1\"><img src=\"cid:%2\">Bla<b>Blub</b>" ) )
.arg( image2->contentID ).arg( image1->contentID );
QCOMPARE( TextEdit::imageNamesToContentIds( startHtml.toLatin1(), images ), endHtml.toAscii() );
}
void TextEditTester::testDeleteLine_data()
{
QTest::addColumn<QString>( "initalText" );
QTest::addColumn<QString>( "expectedText" );
QTest::addColumn<int>( "cursorPos" );
QTest::newRow( "" ) << QString::fromLatin1( "line1\nline2\nline3" )
<< QString::fromLatin1( "line1\nline3" )
<< 6;
QTest::newRow( "" ) << QString::fromLatin1( "line1\nline2\nline3" )
<< QString::fromLatin1( "line2\nline3" )
<< 5;
QTest::newRow( "" ) << QString::fromLatin1( "line1\nline2\nline3" )
<< QString::fromLatin1( "line1\nline3" )
<< 11;
QTest::newRow( "" ) << QString::fromLatin1( "line1\nline2\nline3" )
<< QString::fromLatin1( "line2\nline3" )
<< 0;
QTest::newRow( "" ) << QString::fromLatin1( "line1\nline2\nline3" )
<< QString::fromLatin1( "line1\nline2" )
<< 17;
QTest::newRow( "" ) << QString::fromLatin1( "line1" )
<< QString::fromLatin1( "" )
<< 0;
QTest::newRow( "" ) << QString::fromLatin1( "line1" )
<< QString::fromLatin1( "" )
<< 5;
// Now, test deletion with word wrapping. The line with the Ms is so long that it will get wrapped
QTest::newRow( "" ) << QString::fromLatin1( "line1\nMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3" )
<< QString::fromLatin1( "line1\nMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3" )
<< 6;
QTest::newRow( "" ) << QString::fromLatin1( "line1\nMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3" )
<< QString::fromLatin1( "line1\nMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3" )
<< 13;
}
void TextEditTester::testDeleteLine()
{
QFETCH( QString, initalText );
QFETCH( QString, expectedText );
QFETCH( int, cursorPos );
TextEdit edit;
edit.setPlainText( initalText );
QTextCursor cursor = edit.textCursor();
cursor.setPosition( cursorPos );
edit.setTextCursor( cursor );
edit.show(); // we need a layout for this to work
edit.deleteCurrentLine();
QCOMPARE( edit.toPlainText(), expectedText );
}
void TextEditTester::testLoadImage()
{
TextEdit edit;
QString image1Path = KIconLoader::global()->iconPath( QLatin1String( "folder-new" ), KIconLoader::Small, false );
QString image2Path = KIconLoader::global()->iconPath( QLatin1String( "arrow-up" ), KIconLoader::Small, false );
QImage image1, image2;
QVERIFY( image1.load( image1Path ) );
QVERIFY( image2.load( image1Path ) );
edit.setHtml( QLatin1String( "Bla<img src=\"folder-new.png\">Bla" ) );
// First try to load an image with a name that doesn't match, it should fail
edit.loadImage( image1, QString::fromLatin1( "doesntmatch" ), QString::fromLatin1( "folder-new" ) );
QVERIFY( !edit.document()->resource( QTextDocument::ImageResource, QUrl( QLatin1String( "folder-new" ) ) ).isValid() );
// Now, load the image for real
edit.loadImage( image1, QString::fromLatin1( "folder-new.png" ), QString::fromLatin1( "folder-new" ) );
QVERIFY( edit.document()->resource( QTextDocument::ImageResource, QUrl( QLatin1String( "folder-new" ) ) ).isValid() );
// New test with a new textedit (so that we don't use the cached resources
// This example has two images in the same text block, make sure that doesn't crash (bug 204214)
TextEdit edit2;
edit2.setHtml( QLatin1String( "<img src=\"folder-new.png\"><img src=\"folder-new.png\">" ) );
edit2.loadImage( image1, QString::fromLatin1( "folder-new.png" ), QString::fromLatin1( "folder-new" ) );
QVERIFY( edit.document()->resource( QTextDocument::ImageResource, QUrl( QLatin1String( "folder-new" ) ) ).isValid() );
QCOMPARE( edit.embeddedImages().size(), 1 );
}
void TextEditTester::testWrappedPlainText()
{
TextEdit edit;
QLatin1String text( "http://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test\n https://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test\ntest ftp://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test\nftps://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test\n ldap://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test" );
edit.setPlainText( text );
edit.show(); // < otherwise toWrappedPlainText can't work, it needs a layout
QCOMPARE( edit.toWrappedPlainText(), text );
}