File indexing completed on 2024-05-12 05:09:58
0001 /*************************************************************************** 0002 Copyright (C) 2021 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 0026 #include "fieldwidgettest.h" 0027 #include "../gui/boolfieldwidget.h" 0028 #include "../gui/choicefieldwidget.h" 0029 #include "../gui/datefieldwidget.h" 0030 #include "../gui/datewidget.h" 0031 #include "../gui/linefieldwidget.h" 0032 #include "../gui/lineedit.h" 0033 #include "../gui/numberfieldwidget.h" 0034 #include "../gui/spinbox.h" 0035 #include "../gui/parafieldwidget.h" 0036 #include "../gui/ratingwidget.h" 0037 #include "../gui/ratingfieldwidget.h" 0038 #include "../gui/tablefieldwidget.h" 0039 #include "../gui/urlfieldwidget.h" 0040 #include "../document.h" 0041 #include "../images/imagefactory.h" 0042 #include "../collections/bookcollection.h" 0043 #include "../collectionfactory.h" 0044 0045 #include <KLocalizedString> 0046 #include <KTextEdit> 0047 #include <KUrlRequester> 0048 0049 #include <QTest> 0050 #include <QCheckBox> 0051 #include <QComboBox> 0052 #include <QTableWidget> 0053 #include <QSignalSpy> 0054 #include <QStandardPaths> 0055 0056 // needs a GUI 0057 QTEST_MAIN( FieldWidgetTest ) 0058 0059 void FieldWidgetTest::initTestCase() { 0060 QStandardPaths::setTestModeEnabled(true); 0061 KLocalizedString::setApplicationDomain("tellico"); 0062 Tellico::RegisterCollection<Tellico::Data::BookCollection> registerBook(Tellico::Data::Collection::Book, "book"); 0063 Tellico::ImageFactory::init(); 0064 } 0065 0066 void FieldWidgetTest::testBool() { 0067 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("bool"), 0068 QStringLiteral("bool"), 0069 Tellico::Data::Field::Bool)); 0070 field->setDefaultValue(QStringLiteral("true")); 0071 Tellico::GUI::BoolFieldWidget w(field, nullptr); 0072 QSignalSpy spy(&w, &Tellico::GUI::FieldWidget::valueChanged); 0073 QVERIFY(!w.expands()); 0074 QVERIFY(w.text().isEmpty()); 0075 QCOMPARE(spy.count(), 0); 0076 0077 w.setText(QStringLiteral("true")); 0078 QCOMPARE(w.text(), QStringLiteral("true")); 0079 QCOMPARE(spy.count(), 0); // since the value was set explicitly, no valueChanged signal is made 0080 auto cb = dynamic_cast<QCheckBox*>(w.widget()); 0081 QVERIFY(cb); 0082 QVERIFY(cb->isChecked()); 0083 0084 // any non-empty text is interpreted as true (for better or worse) 0085 w.setText(QStringLiteral("false")); 0086 QCOMPARE(w.text(), QStringLiteral("true")); 0087 QVERIFY(cb->isChecked()); 0088 0089 w.clear(); 0090 QVERIFY(w.text().isEmpty()); 0091 QVERIFY(!cb->isChecked()); 0092 QCOMPARE(spy.count(), 0); 0093 0094 cb->setChecked(true); 0095 QCOMPARE(w.text(), QStringLiteral("true")); 0096 QCOMPARE(spy.count(), 1); 0097 0098 w.clear(); 0099 QVERIFY(w.text().isEmpty()); 0100 QVERIFY(!cb->isChecked()); 0101 0102 w.insertDefault(); 0103 QCOMPARE(w.text(), QStringLiteral("true")); 0104 QVERIFY(cb->isChecked()); 0105 } 0106 0107 void FieldWidgetTest::testChoice() { 0108 // create a Choice field 0109 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("f"), 0110 QStringLiteral("f"), 0111 QStringList())); 0112 field->setAllowed(QStringList() << QStringLiteral("choice1")); 0113 Tellico::GUI::ChoiceFieldWidget w(field, nullptr); 0114 QSignalSpy spy(&w, &Tellico::GUI::FieldWidget::valueChanged); 0115 QVERIFY(!w.expands()); 0116 QVERIFY(w.text().isEmpty()); 0117 auto cb = dynamic_cast<QComboBox*>(w.widget()); 0118 QVERIFY(cb); 0119 QCOMPARE(cb->count(), 2); // one empty value 0120 0121 field->setAllowed(QStringList() << QStringLiteral("choice1") << QStringLiteral("choice2")); 0122 w.updateField(field, field); 0123 QVERIFY(w.text().isEmpty()); 0124 QCOMPARE(spy.count(), 0); 0125 QCOMPARE(cb->count(), 3); 0126 0127 w.setText(QStringLiteral("choice2")); 0128 QCOMPARE(w.text(), QStringLiteral("choice2")); 0129 0130 field->setAllowed(QStringList() << QStringLiteral("choice1") << QStringLiteral("choice2") << QStringLiteral("choice3")); 0131 w.updateField(field, field); 0132 // selected value should remain same 0133 QCOMPARE(w.text(), QStringLiteral("choice2")); 0134 QCOMPARE(spy.count(), 0); 0135 0136 cb->setCurrentIndex(1); 0137 QCOMPARE(w.text(), QStringLiteral("choice1")); 0138 QCOMPARE(spy.count(), 1); 0139 0140 w.clear(); 0141 QVERIFY(w.text().isEmpty()); 0142 0143 // set value to something not in the list 0144 w.setText(QStringLiteral("choice4")); 0145 QCOMPARE(w.text(), QStringLiteral("choice4")); 0146 QCOMPARE(cb->count(), 5); 0147 0148 w.insertDefault(); 0149 QVERIFY(w.text().isEmpty()); 0150 } 0151 0152 void FieldWidgetTest::testDate() { 0153 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("d"), 0154 QStringLiteral("d"), 0155 Tellico::Data::Field::Date)); 0156 Tellico::GUI::DateFieldWidget w(field, nullptr); 0157 QSignalSpy spy(&w, &Tellico::GUI::FieldWidget::valueChanged); 0158 QVERIFY(w.expands()); 0159 auto dw = dynamic_cast<Tellico::GUI::DateWidget*>(w.widget()); 0160 QVERIFY(dw); 0161 QVERIFY(w.text().isEmpty()); 0162 QVERIFY(dw->date().isNull()); 0163 QCOMPARE(spy.count(), 0); 0164 0165 QDate moon(1969, 7, 20); 0166 w.setText(QStringLiteral("1969-07-20")); 0167 QCOMPARE(w.text(), QStringLiteral("1969-07-20")); 0168 QCOMPARE(dw->date(), moon); 0169 // test without leading zero 0170 w.setText(QStringLiteral("1969-7-20")); 0171 QCOMPARE(w.text(), QStringLiteral("1969-07-20")); 0172 QCOMPARE(dw->date(), moon); 0173 QCOMPARE(spy.count(), 0); 0174 0175 w.setText(QString()); 0176 QVERIFY(w.text().isEmpty()); 0177 QVERIFY(dw->date().isNull()); 0178 QCOMPARE(spy.count(), 0); 0179 0180 w.setText(QStringLiteral("1969")); 0181 // adds dashes 0182 QCOMPARE(w.text(), QStringLiteral("1969--")); 0183 QVERIFY(dw->date().isNull()); 0184 QCOMPARE(spy.count(), 0); 0185 0186 QDate sputnik(1957, 10, 4); 0187 dw->setDate(sputnik); 0188 QCOMPARE(w.text(), QStringLiteral("1957-10-04")); 0189 QCOMPARE(dw->date(), sputnik); 0190 QCOMPARE(spy.count(), 1); 0191 0192 w.clear(); 0193 QVERIFY(w.text().isEmpty()); 0194 QVERIFY(dw->date().isNull()); 0195 } 0196 0197 void FieldWidgetTest::testLine() { 0198 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("f"), 0199 QStringLiteral("f"))); 0200 field->setFlags(Tellico::Data::Field::AllowMultiple | Tellico::Data::Field::AllowCompletion); 0201 Tellico::GUI::LineFieldWidget w(field, nullptr); 0202 QSignalSpy spy(&w, &Tellico::GUI::FieldWidget::valueChanged); 0203 QVERIFY(w.expands()); 0204 QVERIFY(w.text().isEmpty()); 0205 0206 w.setText(QStringLiteral("true")); 0207 QCOMPARE(w.text(), QStringLiteral("true")); 0208 auto le = dynamic_cast<Tellico::GUI::LineEdit*>(w.widget()); 0209 QVERIFY(le); 0210 QVERIFY(!le->validator()); 0211 QCOMPARE(spy.count(), 0); 0212 0213 w.addCompletionObjectItem(QStringLiteral("new text")); 0214 le->setText(QStringLiteral("new")); 0215 QCOMPARE(spy.count(), 1); 0216 QCOMPARE(le->completionObject()->makeCompletion(le->text()), QStringLiteral("new text")); 0217 0218 le->setText(QStringLiteral("new text")); 0219 QCOMPARE(w.text(), QStringLiteral("new text")); 0220 QCOMPARE(spy.count(), 2); 0221 0222 le->setText(QStringLiteral("text1;text2")); 0223 QCOMPARE(w.text(), QStringLiteral("text1; text2")); 0224 QCOMPARE(spy.count(), 3); 0225 0226 field->setFlags(Tellico::Data::Field::AllowMultiple); 0227 w.updateField(field, field); 0228 // verify completion object is removed 0229 QVERIFY(!le->compObj()); // don't call completionObject() since it recreates it 0230 0231 w.clear(); 0232 QVERIFY(w.text().isEmpty()); 0233 } 0234 0235 void FieldWidgetTest::testPara() { 0236 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("f"), 0237 QStringLiteral("f"), 0238 Tellico::Data::Field::Para)); 0239 Tellico::GUI::ParaFieldWidget w(field, nullptr); 0240 QSignalSpy spy(&w, &Tellico::GUI::FieldWidget::valueChanged); 0241 QVERIFY(w.expands()); 0242 QVERIFY(w.text().isEmpty()); 0243 0244 w.setText(QStringLiteral("true")); 0245 QCOMPARE(w.text(), QStringLiteral("true")); 0246 auto edit = dynamic_cast<KTextEdit*>(w.widget()); 0247 QVERIFY(edit); 0248 QCOMPARE(spy.count(), 0); 0249 0250 // test replacing EOL 0251 edit->setText(QLatin1String("test1\ntest2")); 0252 QCOMPARE(w.text(), QStringLiteral("test1<br/>test2")); 0253 QCOMPARE(spy.count(), 1); 0254 0255 w.setText(QLatin1String("test1<br>test2")); 0256 QCOMPARE(edit->toPlainText(), QStringLiteral("test1\ntest2")); 0257 QCOMPARE(w.text(), QStringLiteral("test1<br/>test2")); 0258 0259 w.clear(); 0260 QVERIFY(w.text().isEmpty()); 0261 0262 QString textWithEmoji = QString::fromUtf8("Title 🏡️"); 0263 w.setText(textWithEmoji); 0264 QCOMPARE(w.text(), textWithEmoji); 0265 } 0266 0267 void FieldWidgetTest::testNumber() { 0268 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("f"), 0269 QStringLiteral("f"), 0270 Tellico::Data::Field::Number)); 0271 Tellico::GUI::NumberFieldWidget w(field, nullptr); 0272 QSignalSpy spy(&w, &Tellico::GUI::FieldWidget::valueChanged); 0273 QVERIFY(w.expands()); 0274 QVERIFY(w.text().isEmpty()); 0275 // spin box since AllowMultiple is not set 0276 QVERIFY(w.isSpinBox()); 0277 auto sb = dynamic_cast<Tellico::GUI::SpinBox*>(w.widget()); 0278 QVERIFY(sb); 0279 w.setText(QStringLiteral("1")); 0280 QCOMPARE(w.text(), QStringLiteral("1")); 0281 QCOMPARE(sb->value(), 1); 0282 w.setText(QStringLiteral("1; 2")); 0283 QCOMPARE(w.text(), QStringLiteral("1")); 0284 w.clear(); 0285 QVERIFY(w.text().isEmpty()); 0286 QCOMPARE(spy.count(), 0); 0287 0288 sb->setValue(3); 0289 QCOMPARE(w.text(), QStringLiteral("3")); 0290 QCOMPARE(spy.count(), 1); 0291 0292 // now set AllowMultiple and check that the spinbox is deleted and a line edit is used 0293 field->setFlags(Tellico::Data::Field::AllowMultiple); 0294 w.setText(QStringLiteral("1")); 0295 w.updateField(field, field); 0296 QVERIFY(!w.isSpinBox()); 0297 auto le = dynamic_cast<QLineEdit*>(w.widget()); 0298 QVERIFY(le); 0299 // value should be unchanged 0300 QCOMPARE(w.text(), QStringLiteral("1")); 0301 QCOMPARE(spy.count(), 1); 0302 w.setText(QStringLiteral("1;2")); 0303 QCOMPARE(w.text(), QStringLiteral("1; 2")); 0304 QCOMPARE(spy.count(), 1); 0305 0306 le->setText(QStringLiteral("2")); 0307 QCOMPARE(w.text(), QStringLiteral("2")); 0308 QCOMPARE(spy.count(), 2); 0309 0310 w.clear(); 0311 QVERIFY(w.text().isEmpty()); 0312 } 0313 0314 void FieldWidgetTest::testRating() { 0315 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("f"), 0316 QStringLiteral("f"), 0317 Tellico::Data::Field::Rating)); 0318 Tellico::GUI::RatingFieldWidget w(field, nullptr); 0319 QSignalSpy spy(&w, &Tellico::GUI::FieldWidget::valueChanged); 0320 QVERIFY(!w.expands()); 0321 QVERIFY(w.text().isEmpty()); 0322 auto rating = dynamic_cast<Tellico::GUI::RatingWidget*>(w.widget()); 0323 QVERIFY(rating); 0324 0325 w.setText(QStringLiteral("1")); 0326 QCOMPARE(w.text(), QStringLiteral("1")); 0327 w.setText(QStringLiteral("1; 2")); 0328 QCOMPARE(w.text(), QStringLiteral("1")); 0329 w.clear(); 0330 QVERIFY(w.text().isEmpty()); 0331 QCOMPARE(spy.count(), 0); 0332 0333 field->setProperty(QStringLiteral("minimum"), QStringLiteral("5")); 0334 field->setProperty(QStringLiteral("maximum"), QStringLiteral("7")); 0335 w.setText(QStringLiteral("4")); 0336 w.updateField(field, field); 0337 QVERIFY(w.text().isEmpty()); // empty since 4 is less than minimum 0338 QCOMPARE(spy.count(), 0); 0339 w.setText(QStringLiteral("8")); 0340 QVERIFY(w.text().isEmpty()); 0341 QCOMPARE(spy.count(), 0); 0342 0343 rating->setText(QStringLiteral("6")); 0344 QCOMPARE(w.text(), QStringLiteral("6")); 0345 QCOMPARE(spy.count(), 0); 0346 } 0347 0348 void FieldWidgetTest::testTable() { 0349 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("url"), 0350 QStringLiteral("url"), 0351 Tellico::Data::Field::Table)); 0352 field->setProperty(QStringLiteral("columns"), QStringLiteral("2")); 0353 Tellico::GUI::TableFieldWidget w(field, nullptr); 0354 QSignalSpy spy(&w, &Tellico::GUI::FieldWidget::valueChanged); 0355 QSignalSpy fieldSpy(&w, &Tellico::GUI::FieldWidget::fieldChanged); 0356 QVERIFY(w.expands()); 0357 QVERIFY(w.text().isEmpty()); 0358 QCOMPARE(w.m_columns, 2); 0359 0360 auto tw = dynamic_cast<QTableWidget*>(w.widget()); 0361 Q_ASSERT(tw); 0362 QCOMPARE(tw->columnCount(), 2); 0363 QCOMPARE(tw->rowCount(), 5); // minimum row count is 5 0364 0365 w.setText(QStringLiteral("true")); 0366 QCOMPARE(w.text(), QStringLiteral("true")); 0367 QCOMPARE(spy.count(), 0); 0368 QCOMPARE(tw->rowCount(), 5); 0369 0370 w.slotInsertRow(); 0371 tw->setItem(0, 1, new QTableWidgetItem(QStringLiteral("new text"))); 0372 QCOMPARE(w.text(), QStringLiteral("true::new text")); 0373 QCOMPARE(spy.count(), 1); 0374 QVERIFY(!w.emptyRow(0)); 0375 QCOMPARE(tw->rowCount(), 5); 0376 0377 w.m_row = 1; 0378 w.slotInsertRow(); 0379 QCOMPARE(tw->rowCount(), 6); 0380 w.slotRemoveRow(); 0381 QCOMPARE(w.text(), QStringLiteral("true::new text")); 0382 QCOMPARE(tw->rowCount(), 5); 0383 QCOMPARE(spy.count(), 1); 0384 QVERIFY(w.emptyRow(1)); 0385 QVERIFY(!w.emptyRow(0)); 0386 0387 QCOMPARE(w.text(), QStringLiteral("true::new text")); 0388 w.m_row = 0; 0389 w.slotMoveRowDown(); 0390 QCOMPARE(spy.count(), 2); 0391 QCOMPARE(w.text(), Tellico::FieldFormat::rowDelimiterString() + QStringLiteral("true::new text")); 0392 w.m_row = 1; 0393 w.slotMoveRowUp(); 0394 QCOMPARE(spy.count(), 3); 0395 QCOMPARE(w.text(), QStringLiteral("true::new text")); 0396 0397 w.m_col = 0; 0398 w.renameColumn(QStringLiteral("col name")); 0399 QCOMPARE(tw->horizontalHeaderItem(0)->text(), QStringLiteral("col name")); 0400 0401 field->setProperty(QStringLiteral("columns"), QStringLiteral("4")); 0402 w.updateField(field, field); 0403 QCOMPARE(tw->columnCount(), 4); 0404 QCOMPARE(w.text(), QStringLiteral("true::new text")); 0405 QCOMPARE(spy.count(), 3); 0406 0407 w.clear(); 0408 QVERIFY(w.text().isEmpty()); 0409 } 0410 0411 void FieldWidgetTest::testUrl() { 0412 Tellico::Data::FieldPtr field(new Tellico::Data::Field(QStringLiteral("url"), 0413 QStringLiteral("url"), 0414 Tellico::Data::Field::URL)); 0415 Tellico::GUI::URLFieldWidget w(field, nullptr); 0416 QSignalSpy spy(&w, &Tellico::GUI::FieldWidget::valueChanged); 0417 QVERIFY(w.expands()); 0418 0419 auto requester = dynamic_cast<KUrlRequester*>(w.widget()); 0420 Q_ASSERT(requester); 0421 0422 QUrl base = QUrl::fromLocalFile(QFINDTESTDATA("data/relative-link.xml")); 0423 Tellico::Data::Document::self()->setURL(base); // set the base url 0424 QUrl link = QUrl::fromLocalFile(QFINDTESTDATA("fieldwidgettest.cpp")); 0425 requester->setUrl(link); 0426 QCOMPARE(w.text(), link.url()); 0427 QCOMPARE(spy.count(), 1); 0428 0429 field->setProperty(QStringLiteral("relative"), QStringLiteral("true")); 0430 w.updateField(field, field); 0431 // will be exactly up one level 0432 QCOMPARE(w.text(), QStringLiteral("../fieldwidgettest.cpp")); 0433 0434 // check completion 0435 QCOMPARE(requester->lineEdit()->completionObject()->makeCompletion(QStringLiteral("../fieldwidgettest.c")), 0436 QStringLiteral("../fieldwidgettest.cpp")); 0437 0438 // verify value after setting the relative link explicitly 0439 w.setText(QStringLiteral("../fieldwidgettest.cpp")); 0440 QCOMPARE(w.text(), QStringLiteral("../fieldwidgettest.cpp")); 0441 QCOMPARE(spy.count(), 1); 0442 0443 field->setProperty(QStringLiteral("relative"), QStringLiteral("false")); 0444 w.updateField(field, field); 0445 // will be exactly up one level 0446 QCOMPARE(w.text(), link.url()); 0447 0448 w.clear(); 0449 QVERIFY(w.text().isEmpty()); 0450 QVERIFY(requester->url().isEmpty()); 0451 }