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 }