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