1 /*
2     SPDX-FileCopyrightText: 2016-2021 Laurent Montel <montel@kde.org>
3     SPDX-FileCopyrightText: 2008 Thomas McGuire <mcguire@kde.org>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #undef QT_USE_FAST_CONCATENATION
9 #undef QT_USE_FAST_OPERATOR_PLUS
10 
11 #include "signaturetest.h"
12 #include "qtest.h"
13 
14 #include "signature.h"
15 
16 #include <KActionCollection>
17 #include <KConfig>
18 #include <KConfigGroup>
19 #include <KShell>
20 #include <QDir>
21 #include <QStandardPaths>
22 
23 #include <kpimtextedit/richtextcomposer.h>
24 #include <kpimtextedit/richtextcomposercontroler.h>
25 #include <kpimtextedit/richtextcomposerimages.h>
26 using namespace KIdentityManagement;
27 using namespace KPIMTextEdit;
28 
QTEST_MAIN(SignatureTester)29 QTEST_MAIN(SignatureTester)
30 
31 void SignatureTester::testEqualSignatures()
32 {
33     Signature sig1;
34     sig1.setText(QStringLiteral("Hello World"));
35     sig1.setEnabledSignature(true);
36     Signature sig2(sig1);
37     QVERIFY(sig1 == sig2);
38     QCOMPARE(sig2.text(), QStringLiteral("Hello World"));
39     QCOMPARE(sig2.type(), Signature::Inlined);
40     QCOMPARE(sig2.rawText(), QStringLiteral("Hello World"));
41     QVERIFY(!sig2.isInlinedHtml());
42     QCOMPARE(sig2.withSeparator(), QStringLiteral("-- \nHello World"));
43     QVERIFY(sig2.isEnabledSignature());
44 
45     Signature sig3 = sig1;
46     QVERIFY(sig1 == sig3);
47     QCOMPARE(sig3.text(), QStringLiteral("Hello World"));
48     QCOMPARE(sig3.type(), Signature::Inlined);
49     QCOMPARE(sig3.rawText(), QStringLiteral("Hello World"));
50     QVERIFY(!sig3.isInlinedHtml());
51     QCOMPARE(sig3.withSeparator(), QStringLiteral("-- \nHello World"));
52     QVERIFY(sig3.isEnabledSignature());
53 }
54 
testSignatures()55 void SignatureTester::testSignatures()
56 {
57     Signature sig1;
58     sig1.setText(QStringLiteral("Hello World"));
59     QCOMPARE(sig1.text(), QStringLiteral("Hello World"));
60     QCOMPARE(sig1.type(), Signature::Inlined);
61     QCOMPARE(sig1.rawText(), QStringLiteral("Hello World"));
62     QVERIFY(!sig1.isInlinedHtml());
63     QCOMPARE(sig1.withSeparator(), QStringLiteral("-- \nHello World"));
64     QVERIFY(!sig1.isEnabledSignature());
65 
66     Signature sig2;
67     sig2.setText(QStringLiteral("<b>Hello</b> World"));
68     sig2.setInlinedHtml(true);
69     QVERIFY(sig2.isInlinedHtml());
70     QCOMPARE(sig2.type(), Signature::Inlined);
71     QCOMPARE(sig2.rawText(), QStringLiteral("<b>Hello</b> World"));
72     QCOMPARE(sig2.withSeparator(), QStringLiteral("-- <br><b>Hello</b> World"));
73     QVERIFY(!sig2.isEnabledSignature());
74 
75     const QString dataFilePath = QStringLiteral(SIGNATURETEST_DATA_FILE);
76     // Read this very file in, we use it for the tests
77     QFile thisFile(dataFilePath);
78     thisFile.open(QIODevice::ReadOnly);
79     QString fileContent = QString::fromUtf8(thisFile.readAll());
80 
81     if (!QStandardPaths::findExecutable(QStringLiteral("cat")).isEmpty()) {
82         Signature sig3;
83         sig3.setPath(QStringLiteral("cat ") + KShell::quoteArg(dataFilePath), true);
84         QCOMPARE(sig3.rawText(), fileContent);
85         QVERIFY(!sig3.isInlinedHtml());
86         QVERIFY(sig3.text().isEmpty());
87         QCOMPARE(sig3.type(), Signature::FromCommand);
88         QCOMPARE(sig3.withSeparator(), QString(QStringLiteral("-- \n") + fileContent));
89         QVERIFY(!sig3.isEnabledSignature());
90     }
91 
92     Signature sig4;
93     sig4.setPath(dataFilePath, false);
94     QCOMPARE(sig4.rawText(), fileContent);
95     QVERIFY(!sig4.isInlinedHtml());
96     QVERIFY(sig4.text().isEmpty());
97     QCOMPARE(sig4.type(), Signature::FromFile);
98     QCOMPARE(sig4.withSeparator(), QString(QStringLiteral("-- \n") + fileContent));
99     QVERIFY(!sig4.isEnabledSignature());
100 }
101 
setCursorPos(QTextEdit & edit,int pos)102 static void setCursorPos(QTextEdit &edit, int pos)
103 {
104     QTextCursor cursor(edit.document());
105     cursor.setPosition(pos);
106     edit.setTextCursor(cursor);
107 }
108 
testTextEditInsertion()109 void SignatureTester::testTextEditInsertion()
110 {
111     KPIMTextEdit::RichTextComposer edit;
112     edit.createActions(new KActionCollection(this));
113     Signature sig;
114     sig.setEnabledSignature(true);
115     sig.setText(QStringLiteral("Hello World"));
116 
117     // Test inserting signature at start, with separators. Make sure two new
118     // lines are inserted before the signature
119 
120     edit.setPlainText(QStringLiteral("Bla Bla"));
121     sig.insertIntoTextEdit(Signature::Start, Signature::AddSeparator | Signature::AddNewLines, &edit);
122     QVERIFY(edit.textMode() == KPIMTextEdit::RichTextComposer::Plain);
123     QCOMPARE(edit.toPlainText(), QStringLiteral("\n\n-- \nHello World\nBla Bla"));
124 
125     // Test inserting signature at end. make sure cursor position is preserved
126     edit.clear();
127     edit.setPlainText(QStringLiteral("Bla Bla"));
128     setCursorPos(edit, 4);
129     sig.insertIntoTextEdit(Signature::End, Signature::AddSeparator | Signature::AddNewLines, &edit);
130     QCOMPARE(edit.toPlainText(), QStringLiteral("Bla Bla\n-- \nHello World"));
131     QCOMPARE(edit.textCursor().position(), 4);
132 
133     // test inserting a signature at cursor position. make sure the signature is inserted
134     // to the beginning of the line and make sure modified state is preserved
135     edit.clear();
136     edit.setPlainText(QStringLiteral("Bla Bla"));
137     setCursorPos(edit, 4);
138     edit.document()->setModified(false);
139     sig.insertIntoTextEdit(Signature::AtCursor, Signature::AddSeparator | Signature::AddNewLines, &edit);
140     QCOMPARE(edit.toPlainText(), QStringLiteral("-- \nHello World\nBla Bla"));
141     QCOMPARE(edit.textCursor().position(), 20);
142     QVERIFY(!edit.document()->isModified());
143 
144     // make sure undo undoes everything in one go
145     edit.undo();
146     QCOMPARE(edit.toPlainText(), QStringLiteral("Bla Bla"));
147 
148     // test inserting signature without separator.
149     // make sure cursor position and modified state is preserved.
150     edit.clear();
151     edit.setPlainText(QStringLiteral("Bla Bla"));
152     setCursorPos(edit, 4);
153     edit.document()->setModified(true);
154     sig.insertIntoTextEdit(Signature::End, Signature::AddNewLines, &edit);
155     QCOMPARE(edit.toPlainText(), QStringLiteral("Bla Bla\nHello World"));
156     QCOMPARE(edit.textCursor().position(), 4);
157     QVERIFY(edit.document()->isModified());
158 
159     sig.setText(QStringLiteral("Hello<br>World"));
160     sig.setInlinedHtml(true);
161 
162     // test that html signatures turn html on and have correct line endings (<br> vs \n)
163     edit.clear();
164     edit.setPlainText(QStringLiteral("Bla Bla"));
165     sig.insertIntoTextEdit(Signature::End, Signature::AddSeparator | Signature::AddNewLines, &edit);
166     QVERIFY(edit.textMode() == KPIMTextEdit::RichTextComposer::Rich);
167     QCOMPARE(edit.toPlainText(), QStringLiteral("Bla Bla\n-- \nHello\nWorld"));
168 }
169 
testBug167961()170 void SignatureTester::testBug167961()
171 {
172     KPIMTextEdit::RichTextComposer edit;
173     edit.createActions(new KActionCollection(this));
174     Signature sig;
175     sig.setEnabledSignature(true);
176     sig.setText(QStringLiteral("BLA"));
177 
178     // Test that the cursor is still at the start when appending a sig into
179     // an empty text edit
180     sig.insertIntoTextEdit(Signature::End, Signature::AddSeparator | Signature::AddNewLines, &edit);
181     QCOMPARE(edit.textCursor().position(), 0);
182 
183     // When prepending a sig, the cursor should also be at the start, see bug 211634
184     edit.clear();
185     sig.insertIntoTextEdit(Signature::Start, Signature::AddSeparator | Signature::AddNewLines, &edit);
186     QCOMPARE(edit.textCursor().position(), 0);
187 }
188 
189 // Make writeConfig() public, we need it
190 class MySignature : public Signature
191 {
192 public:
193     using Signature::readConfig;
194     using Signature::writeConfig;
195 };
196 
testImages()197 void SignatureTester::testImages()
198 {
199     KPIMTextEdit::RichTextComposer edit;
200     edit.createActions(new KActionCollection(this));
201 
202     QImage img(16, 16, QImage::Format_ARGB32_Premultiplied);
203     img.fill(Qt::green);
204     QString image1Path = QCoreApplication::applicationDirPath() + QLatin1String("/image.png");
205     img.save(image1Path);
206 
207     QImage image1;
208     QImage image2;
209     QVERIFY(image1.load(image1Path));
210     QVERIFY(image2.load(image1Path));
211     QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/emailidentities/unittest/");
212     QDir().mkpath(path);
213     QString configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/signaturetest");
214     QDir().mkpath(configPath);
215     KConfig config(configPath);
216     KConfigGroup group1 = config.group("Signature1");
217 
218     MySignature sig;
219     sig.setEnabledSignature(true);
220     sig.setImageLocation(path);
221     sig.setInlinedHtml(true);
222     sig.setText(QStringLiteral("Bla<img src=\"folder-new.png\">Bla"));
223     sig.addImage(image1, QStringLiteral("folder-new.png"));
224     sig.writeConfig(group1);
225 
226     // OK, signature saved, the image should be saved as well
227     QDir dir(path);
228     QStringList entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);
229     QCOMPARE(entryList.count(), 1);
230     QCOMPARE(entryList.first(), QStringLiteral("folder-new.png"));
231 
232     // Now, set the text of the signature to something without images, then save it.
233     // The signature class should have removed the images.
234     sig.setText(QStringLiteral("ascii ribbon campaign - against html mail"));
235     sig.writeConfig(group1);
236     entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);
237     QCOMPARE(entryList.count(), 0);
238 
239     // Enable images again, this time with two of the buggers
240     sig.setText(QStringLiteral("Bla<img src=\"folder-new.png\">Bla<img src=\"arrow-up.png\">Bla"));
241     sig.addImage(image1, QStringLiteral("folder-new.png"));
242     sig.addImage(image2, QStringLiteral("arrow-up.png"));
243     sig.writeConfig(group1);
244     entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);
245     QCOMPARE(entryList.count(), 2);
246     QCOMPARE(entryList.first(), QStringLiteral("arrow-up.png"));
247     QCOMPARE(entryList.last(), QStringLiteral("folder-new.png"));
248 
249     // Now, create a second signature instance from the same config, and make sure it can still
250     // read the images, and it does not mess up
251     MySignature sig2;
252     sig2.readConfig(group1);
253     sig2.insertIntoTextEdit(KIdentityManagement::Signature::End, Signature::AddSeparator | Signature::AddNewLines, &edit);
254     QCOMPARE(edit.composerControler()->composerImages()->embeddedImages().count(), 2);
255     QCOMPARE(sig2.text(), QStringLiteral("Bla<img src=\"folder-new.png\">Bla<img src=\"arrow-up.png\">Bla"));
256     sig2.writeConfig(group1);
257     entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);
258     QCOMPARE(entryList.count(), 2);
259     QCOMPARE(entryList.first(), QStringLiteral("arrow-up.png"));
260     QCOMPARE(entryList.last(), QStringLiteral("folder-new.png"));
261 
262     // Remove one image from the signature, and make sure only 1 file is left one file system.
263     sig2.setText(QStringLiteral("<img src=\"folder-new.png\">"));
264     sig2.writeConfig(group1);
265     edit.clear();
266     sig2.insertIntoTextEdit(Signature::End, Signature::AddSeparator | Signature::AddNewLines, &edit);
267     QCOMPARE(edit.composerControler()->composerImages()->embeddedImages().size(), 1);
268     entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);
269     QCOMPARE(entryList.count(), 1);
270 }
271 
testLinebreaks()272 void SignatureTester::testLinebreaks()
273 {
274     Signature sig;
275     sig.setEnabledSignature(true);
276     sig.setType(Signature::Inlined);
277     sig.setInlinedHtml(true);
278     sig.setText(QStringLiteral("Hans Mustermann<br>Musterstr. 42"));
279 
280     KPIMTextEdit::RichTextComposer edit;
281     edit.createActions(new KActionCollection(this));
282     sig.insertIntoTextEdit(Signature::Start, Signature::AddNothing, &edit);
283     QCOMPARE(edit.toPlainText(), QStringLiteral("Hans Mustermann\nMusterstr. 42"));
284 
285     edit.clear();
286     sig.setText(QStringLiteral("<p>Hans Mustermann</p><br>Musterstr. 42"));
287     sig.insertIntoTextEdit(Signature::Start, Signature::AddSeparator, &edit);
288     QEXPECT_FAIL("", "This test is probably bogus, since Qt doesn't seem to produce HTML like this anymore.", Continue);
289     QCOMPARE(edit.toPlainText(), QStringLiteral("-- \nHans Mustermann\nMusterstr. 42"));
290 }
291