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 }