Changeset View
Changeset View
Standalone View
Standalone View
tests/featurelib/zanshincontext.cpp
- This file was added.
1 | /* This file is part of Zanshin | ||||
---|---|---|---|---|---|
2 | | ||||
3 | Copyright 2014-2019 Kevin Ottens <ervin@kde.org> | ||||
4 | | ||||
5 | This program is free software; you can redistribute it and/or | ||||
6 | modify it under the terms of the GNU General Public License as | ||||
7 | published by the Free Software Foundation; either version 2 of | ||||
8 | the License or (at your option) version 3 or any later version | ||||
9 | accepted by the membership of KDE e.V. (or its successor approved | ||||
10 | by the membership of KDE e.V.), which shall act as a proxy | ||||
11 | defined in Section 14 of version 3 of the license. | ||||
12 | | ||||
13 | This program is distributed in the hope that it will be useful, | ||||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
16 | GNU General Public License for more details. | ||||
17 | | ||||
18 | You should have received a copy of the GNU General Public License | ||||
19 | along with this program; if not, write to the Free Software | ||||
20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, | ||||
21 | USA. | ||||
22 | */ | ||||
23 | | ||||
24 | #include "zanshincontext.h" | ||||
25 | | ||||
26 | #include "akonadi/akonadiapplicationselectedattribute.h" | ||||
27 | #include "akonadi/akonadicachingstorage.h" | ||||
28 | #include "akonadi/akonadistorageinterface.h" | ||||
29 | #include "akonadi/akonaditimestampattribute.h" | ||||
30 | | ||||
31 | #include "presentation/applicationmodel.h" | ||||
32 | #include "presentation/errorhandler.h" | ||||
33 | #include "presentation/querytreemodelbase.h" | ||||
34 | | ||||
35 | #include "testlib/akonadifakedataxmlloader.h" | ||||
36 | #include "testlib/monitorspy.h" | ||||
37 | | ||||
38 | #include "utils/dependencymanager.h" | ||||
39 | #include "utils/jobhandler.h" | ||||
40 | | ||||
41 | #include <AkonadiCore/AttributeFactory> | ||||
42 | | ||||
43 | #include <KConfigGroup> | ||||
44 | #include <KSharedConfig> | ||||
45 | | ||||
46 | #include <QSortFilterProxyModel> | ||||
47 | #include <QStandardItemModel> | ||||
48 | #include <QTest> | ||||
49 | | ||||
50 | namespace App | ||||
51 | { | ||||
52 | void initializeDependencies(); | ||||
53 | } | ||||
54 | | ||||
55 | void FakeErrorHandler::doDisplayMessage(const QString &) | ||||
56 | { | ||||
57 | } | ||||
58 | | ||||
59 | ZanshinContext::ZanshinContext(QObject *parent) | ||||
60 | : QObject(parent), | ||||
61 | m_presentation(nullptr), | ||||
62 | m_editor(nullptr), | ||||
63 | m_proxyModel(new QSortFilterProxyModel(this)), | ||||
64 | m_model(nullptr), | ||||
65 | m_sourceModel(nullptr), | ||||
66 | m_monitorSpy(nullptr) | ||||
67 | { | ||||
68 | qputenv("ZANSHIN_OVERRIDE_DATE", "2015-03-10"); | ||||
69 | | ||||
70 | static bool initializedDependencies = false; | ||||
71 | | ||||
72 | if (!initializedDependencies) { | ||||
73 | App::initializeDependencies(); | ||||
74 | MonitorSpy::setExpirationDelay(200); | ||||
75 | initializedDependencies = true; | ||||
76 | } | ||||
77 | | ||||
78 | Akonadi::AttributeFactory::registerAttribute<Akonadi::ApplicationSelectedAttribute>(); | ||||
79 | Akonadi::AttributeFactory::registerAttribute<Akonadi::TimestampAttribute>(); | ||||
80 | | ||||
81 | const auto xmlFile = QString::fromLocal8Bit(ZANSHIN_USER_XMLDATA); | ||||
82 | if (xmlFile.isEmpty()) { | ||||
83 | qDebug() << "FATAL ERROR! ZANSHIN_USER_XMLDATA WAS NOT PROVIDED\n\n"; | ||||
84 | exit(1); | ||||
85 | } | ||||
86 | | ||||
87 | auto searchCollection = Akonadi::Collection(1); | ||||
88 | searchCollection.setParentCollection(Akonadi::Collection::root()); | ||||
89 | searchCollection.setName(QStringLiteral("Search")); | ||||
90 | m_data.createCollection(searchCollection); | ||||
91 | | ||||
92 | auto loader = Testlib::AkonadiFakeDataXmlLoader(&m_data); | ||||
93 | loader.load(xmlFile); | ||||
94 | | ||||
95 | // Swap regular dependencies for the fake data ones | ||||
96 | auto &deps = Utils::DependencyManager::globalInstance(); | ||||
97 | deps.add<Akonadi::MonitorInterface, | ||||
98 | Utils::DependencyManager::UniqueInstance>( | ||||
99 | [this] (Utils::DependencyManager *) { | ||||
100 | return m_data.createMonitor(); | ||||
101 | } | ||||
102 | ); | ||||
103 | deps.add<Akonadi::StorageInterface, | ||||
104 | Utils::DependencyManager::UniqueInstance>( | ||||
105 | [this] (Utils::DependencyManager *deps) { | ||||
106 | return new Akonadi::CachingStorage(deps->create<Akonadi::Cache>(), | ||||
107 | Akonadi::StorageInterface::Ptr(m_data.createStorage())); | ||||
108 | } | ||||
109 | ); | ||||
110 | | ||||
111 | using namespace Presentation; | ||||
112 | m_proxyModel->setDynamicSortFilter(true); | ||||
113 | | ||||
114 | auto appModel = ApplicationModel::Ptr::create(); | ||||
115 | | ||||
116 | appModel->setErrorHandler(&m_errorHandler); | ||||
117 | | ||||
118 | m_appModel = appModel; | ||||
119 | | ||||
120 | auto monitor = Utils::DependencyManager::globalInstance().create<Akonadi::MonitorInterface>(); | ||||
121 | m_monitorSpy = new MonitorSpy(monitor.data(), this); | ||||
122 | } | ||||
123 | | ||||
124 | // Note that setModel might invalidate the 'index' member variable, due to proxyModel->setSourceModel. | ||||
125 | void ZanshinContext::setModel(QAbstractItemModel *model) | ||||
126 | { | ||||
127 | if (m_sourceModel == model) | ||||
128 | return; | ||||
129 | m_sourceModel = model; | ||||
130 | if (!qobject_cast<QSortFilterProxyModel *>(model)) { | ||||
131 | m_proxyModel->setObjectName(QStringLiteral("m_proxyModel_in_ZanshinContext")); | ||||
132 | m_proxyModel->setSourceModel(model); | ||||
133 | m_proxyModel->setSortRole(Qt::DisplayRole); | ||||
134 | m_proxyModel->sort(0); | ||||
135 | m_model = m_proxyModel; | ||||
136 | } else { | ||||
137 | m_model = model; | ||||
138 | } | ||||
139 | } | ||||
140 | | ||||
141 | QAbstractItemModel *ZanshinContext::sourceModel() const | ||||
142 | { | ||||
143 | return m_sourceModel; | ||||
144 | } | ||||
145 | | ||||
146 | QAbstractItemModel *ZanshinContext::model() const | ||||
147 | { | ||||
148 | return m_model; | ||||
149 | } | ||||
150 | | ||||
151 | Domain::Task::Ptr ZanshinContext::currentTask() const | ||||
152 | { | ||||
153 | return m_index.data(Presentation::QueryTreeModelBase::ObjectRole) | ||||
154 | .value<Domain::Task::Ptr>(); | ||||
155 | } | ||||
156 | | ||||
157 | void ZanshinContext::waitForEmptyJobQueue() | ||||
158 | { | ||||
159 | while (Utils::JobHandler::jobCount() != 0) { | ||||
160 | QTest::qWait(20); | ||||
161 | } | ||||
162 | } | ||||
163 | | ||||
164 | void ZanshinContext::waitForStableState() | ||||
165 | { | ||||
166 | waitForEmptyJobQueue(); | ||||
167 | m_monitorSpy->waitForStableState(); | ||||
168 | } | ||||
169 | | ||||
170 | void ZanshinContext::collectIndicesImpl(const QModelIndex &root) | ||||
171 | { | ||||
172 | QAbstractItemModel *model = m_model; | ||||
173 | for (int row = 0; row < model->rowCount(root); row++) { | ||||
174 | const QModelIndex index = model->index(row, 0, root); | ||||
175 | m_indices << index; | ||||
176 | if (model->rowCount(index) > 0) | ||||
177 | collectIndicesImpl(index); | ||||
178 | } | ||||
179 | } | ||||
180 | | ||||
181 | void ZanshinContext::collectIndices() | ||||
182 | { | ||||
183 | m_indices.clear(); | ||||
184 | collectIndicesImpl(); | ||||
185 | } | ||||
186 | | ||||
187 | namespace Zanshin { | ||||
188 | | ||||
189 | QString indexString(const QModelIndex &index, int role = Qt::DisplayRole) | ||||
190 | { | ||||
191 | if (role != Qt::DisplayRole) | ||||
192 | return index.data(role).toString(); | ||||
193 | | ||||
194 | QString data = index.data(role).toString(); | ||||
195 | | ||||
196 | if (index.parent().isValid()) | ||||
197 | return indexString(index.parent(), role) + " / " + data; | ||||
198 | else | ||||
199 | return data; | ||||
200 | } | ||||
201 | | ||||
202 | QModelIndex findIndex(QAbstractItemModel *model, | ||||
203 | const QString &string, | ||||
204 | int role = Qt::DisplayRole, | ||||
205 | const QModelIndex &root = QModelIndex()) | ||||
206 | { | ||||
207 | for (int row = 0; row < model->rowCount(root); row++) { | ||||
208 | const QModelIndex index = model->index(row, 0, root); | ||||
209 | if (indexString(index, role) == string) | ||||
210 | return index; | ||||
211 | | ||||
212 | if (model->rowCount(index) > 0) { | ||||
213 | const QModelIndex found = findIndex(model, string, role, index); | ||||
214 | if (found.isValid()) | ||||
215 | return found; | ||||
216 | } | ||||
217 | } | ||||
218 | | ||||
219 | return QModelIndex(); | ||||
220 | } | ||||
221 | | ||||
222 | void dumpIndices(const QList<QPersistentModelIndex> &indices) | ||||
223 | { | ||||
224 | qDebug() << "Dumping list of size:" << indices.size(); | ||||
225 | for (int row = 0; row < indices.size(); row++) { | ||||
226 | qDebug() << row << indexString(indices.at(row)); | ||||
227 | } | ||||
228 | } | ||||
229 | | ||||
230 | inline bool verify(bool statement, const char *str, | ||||
231 | const char *file, int line) | ||||
232 | { | ||||
233 | if (statement) | ||||
234 | return true; | ||||
235 | | ||||
236 | qDebug() << "Statement" << str << "returned FALSE"; | ||||
237 | qDebug() << "Loc:" << file << line; | ||||
238 | return false; | ||||
239 | } | ||||
240 | | ||||
241 | template <typename T> | ||||
242 | inline bool compare(T const &t1, T const &t2, | ||||
243 | const char *actual, const char *expected, | ||||
244 | const char *file, int line) | ||||
245 | { | ||||
246 | if (t1 == t2) | ||||
247 | return true; | ||||
248 | | ||||
249 | qDebug() << "Compared values are not the same"; | ||||
250 | qDebug() << "Actual (" << actual << ") :" << QTest::toString<T>(t1); | ||||
251 | qDebug() << "Expected (" << expected << ") :" << QTest::toString<T>(t2); | ||||
252 | qDebug() << "Loc:" << file << line; | ||||
253 | return false; | ||||
254 | } | ||||
255 | | ||||
256 | } // namespace Zanshin | ||||
257 | | ||||
258 | #define COMPARE(actual, expected) \ | ||||
259 | do {\ | ||||
260 | if (!Zanshin::compare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ | ||||
261 | return false;\ | ||||
262 | } while (0) | ||||
263 | | ||||
264 | // Note: you should make sure that m_indices is filled in before calling this, | ||||
265 | // e.g. calling Zanshin::collectIndices(context.get()) if not already done. | ||||
266 | #define COMPARE_OR_DUMP(actual, expected) \ | ||||
267 | do {\ | ||||
268 | if (!Zanshin::compare(actual, expected, #actual, #expected, __FILE__, __LINE__)) {\ | ||||
269 | Zanshin::dumpIndices(m_indices); \ | ||||
270 | return false;\ | ||||
271 | }\ | ||||
272 | } while (0) | ||||
273 | | ||||
274 | #define VERIFY(statement) \ | ||||
275 | do {\ | ||||
276 | if (!Zanshin::verify((statement), #statement, __FILE__, __LINE__))\ | ||||
277 | return false;\ | ||||
278 | } while (0) | ||||
279 | | ||||
280 | // Note: you should make sure that m_indices is filled in before calling this, | ||||
281 | // e.g. calling Zanshin::collectIndices(context.get()) if not already done. | ||||
282 | #define VERIFY_OR_DUMP(statement) \ | ||||
283 | do {\ | ||||
284 | if (!Zanshin::verify((statement), #statement, __FILE__, __LINE__)) {\ | ||||
285 | Zanshin::dumpIndices(m_indices); \ | ||||
286 | return false;\ | ||||
287 | }\ | ||||
288 | } while (0) | ||||
289 | | ||||
290 | #define VERIFY_OR_DO(statement, whatToDo) \ | ||||
291 | do {\ | ||||
292 | if (!Zanshin::verify((statement), #statement, __FILE__, __LINE__)) {\ | ||||
293 | whatToDo; \ | ||||
294 | return false;\ | ||||
295 | }\ | ||||
296 | } while (0) | ||||
297 | | ||||
298 | | ||||
299 | bool ZanshinContext::I_display_the_available_data_sources() | ||||
300 | { | ||||
301 | auto availableSources = m_appModel->property("availableSources").value<QObject*>(); | ||||
302 | VERIFY(availableSources); | ||||
303 | | ||||
304 | auto sourceListModel = availableSources->property("sourceListModel").value<QAbstractItemModel*>(); | ||||
305 | VERIFY(sourceListModel); | ||||
306 | | ||||
307 | m_presentation = availableSources; | ||||
308 | setModel(sourceListModel); | ||||
309 | | ||||
310 | return true; | ||||
311 | } | ||||
312 | | ||||
313 | bool ZanshinContext::I_display_the_available_pages() | ||||
314 | { | ||||
315 | m_presentation = m_appModel->property("availablePages").value<QObject*>(); | ||||
316 | setModel(m_presentation->property("pageListModel").value<QAbstractItemModel*>()); | ||||
317 | return true; | ||||
318 | } | ||||
319 | | ||||
320 | bool ZanshinContext::I_display_the_page(const QString &pageName) | ||||
321 | { | ||||
322 | auto availablePages = m_appModel->property("availablePages").value<QObject*>(); | ||||
323 | VERIFY(availablePages); | ||||
324 | | ||||
325 | auto pageListModel = availablePages->property("pageListModel").value<QAbstractItemModel*>(); | ||||
326 | VERIFY(pageListModel); | ||||
327 | waitForEmptyJobQueue(); | ||||
328 | | ||||
329 | QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageName); | ||||
330 | VERIFY(pageIndex.isValid()); | ||||
331 | | ||||
332 | QObject *page = nullptr; | ||||
333 | QMetaObject::invokeMethod(availablePages, "createPageForIndex", | ||||
334 | Q_RETURN_ARG(QObject*, page), | ||||
335 | Q_ARG(QModelIndex, pageIndex)); | ||||
336 | VERIFY(page); | ||||
337 | | ||||
338 | VERIFY(m_appModel->setProperty("currentPage", QVariant::fromValue(page))); | ||||
339 | m_presentation = m_appModel->property("currentPage").value<QObject*>(); | ||||
340 | | ||||
341 | return true; | ||||
342 | } | ||||
343 | | ||||
344 | bool ZanshinContext::there_is_an_item_in_the_central_list(const QString &taskName) | ||||
345 | { | ||||
346 | auto m = m_presentation->property("centralListModel").value<QAbstractItemModel*>(); | ||||
347 | setModel(m); | ||||
348 | waitForEmptyJobQueue(); | ||||
349 | | ||||
350 | collectIndices(); | ||||
351 | m_index = Zanshin::findIndex(model(), taskName); | ||||
352 | VERIFY_OR_DUMP(m_index.isValid()); | ||||
353 | | ||||
354 | return true; | ||||
355 | } | ||||
356 | | ||||
357 | bool ZanshinContext::there_is_an_item_in_the_available_data_sources(const QString &sourceName) | ||||
358 | { | ||||
359 | auto availableSources = m_appModel->property("availableSources").value<QObject*>(); | ||||
360 | VERIFY(availableSources); | ||||
361 | auto m = availableSources->property("sourceListModel").value<QAbstractItemModel*>(); | ||||
362 | VERIFY(m); | ||||
363 | waitForEmptyJobQueue(); | ||||
364 | setModel(m); | ||||
365 | | ||||
366 | collectIndices(); | ||||
367 | m_index = Zanshin::findIndex(model(), sourceName); | ||||
368 | VERIFY_OR_DUMP(m_index.isValid()); | ||||
369 | | ||||
370 | return true; | ||||
371 | } | ||||
372 | | ||||
373 | bool ZanshinContext::the_central_list_contains_items_named(const QStringList &taskNames) | ||||
374 | { | ||||
375 | m_dragIndices.clear(); | ||||
376 | | ||||
377 | auto m = m_presentation->property("centralListModel").value<QAbstractItemModel*>(); | ||||
378 | waitForEmptyJobQueue(); | ||||
379 | setModel(m); | ||||
380 | | ||||
381 | for (const auto &taskName : taskNames) { | ||||
382 | QModelIndex index = Zanshin::findIndex(model(), taskName); | ||||
383 | VERIFY_OR_DO(index.isValid(), Zanshin::dumpIndices(m_dragIndices)); | ||||
384 | m_dragIndices << index; | ||||
385 | } | ||||
386 | | ||||
387 | return true; | ||||
388 | } | ||||
389 | | ||||
390 | bool ZanshinContext::I_look_at_the_central_list() | ||||
391 | { | ||||
392 | auto m = m_presentation->property("centralListModel").value<QAbstractItemModel*>(); | ||||
393 | setModel(m); | ||||
394 | waitForStableState(); | ||||
395 | return true; | ||||
396 | } | ||||
397 | | ||||
398 | bool ZanshinContext::I_check_the_item() | ||||
399 | { | ||||
400 | VERIFY(model()->setData(m_index, Qt::Checked, Qt::CheckStateRole)); | ||||
401 | waitForStableState(); | ||||
402 | return true; | ||||
403 | } | ||||
404 | | ||||
405 | bool ZanshinContext::I_uncheck_the_item() | ||||
406 | { | ||||
407 | VERIFY(model()->setData(m_index, Qt::Unchecked, Qt::CheckStateRole)); | ||||
408 | waitForStableState(); | ||||
409 | return true; | ||||
410 | } | ||||
411 | | ||||
412 | bool ZanshinContext::I_remove_the_item() | ||||
413 | { | ||||
414 | VERIFY(QMetaObject::invokeMethod(m_presentation, "removeItem", Q_ARG(QModelIndex, m_index))); | ||||
415 | waitForStableState(); | ||||
416 | return true; | ||||
417 | } | ||||
418 | | ||||
419 | bool ZanshinContext::I_promote_the_item() | ||||
420 | { | ||||
421 | VERIFY(QMetaObject::invokeMethod(m_presentation, "promoteItem", Q_ARG(QModelIndex, m_index))); | ||||
422 | waitForStableState(); | ||||
423 | | ||||
424 | return true; | ||||
425 | } | ||||
426 | | ||||
427 | bool ZanshinContext::I_add_a_project(const QString &projectName, const QString &parentSourceName) | ||||
428 | { | ||||
429 | auto availableSources = m_appModel->property("availableSources").value<QObject*>(); | ||||
430 | VERIFY(availableSources); | ||||
431 | auto sourceList = availableSources->property("sourceListModel").value<QAbstractItemModel*>(); | ||||
432 | VERIFY(sourceList); | ||||
433 | waitForStableState(); | ||||
434 | QModelIndex index = Zanshin::findIndex(sourceList, parentSourceName); | ||||
435 | VERIFY(index.isValid()); | ||||
436 | auto source = index.data(Presentation::QueryTreeModelBase::ObjectRole) | ||||
437 | .value<Domain::DataSource::Ptr>(); | ||||
438 | VERIFY(source); | ||||
439 | | ||||
440 | VERIFY(QMetaObject::invokeMethod(m_presentation, "addProject", | ||||
441 | Q_ARG(QString, projectName), | ||||
442 | Q_ARG(Domain::DataSource::Ptr, source))); | ||||
443 | waitForStableState(); | ||||
444 | | ||||
445 | return true; | ||||
446 | } | ||||
447 | | ||||
448 | bool ZanshinContext::I_add_a_context(const QString &contextName) | ||||
449 | { | ||||
450 | waitForStableState(); | ||||
451 | | ||||
452 | VERIFY(QMetaObject::invokeMethod(m_presentation, | ||||
453 | "addContext", | ||||
454 | Q_ARG(QString, contextName))); | ||||
455 | waitForStableState(); | ||||
456 | | ||||
457 | return true; | ||||
458 | | ||||
459 | } | ||||
460 | | ||||
461 | bool ZanshinContext::I_add_a_task(const QString &taskName) | ||||
462 | { | ||||
463 | waitForStableState(); | ||||
464 | | ||||
465 | VERIFY(QMetaObject::invokeMethod(m_presentation, | ||||
466 | "addItem", | ||||
467 | Q_ARG(QString, taskName))); | ||||
468 | waitForStableState(); | ||||
469 | | ||||
470 | return true; | ||||
471 | } | ||||
472 | | ||||
473 | bool ZanshinContext::I_rename_a_page(const QString &path, const QString &oldName, const QString &newName) | ||||
474 | { | ||||
475 | const QString pageNodeName = path + " / "; | ||||
476 | | ||||
477 | VERIFY(!pageNodeName.isEmpty()); | ||||
478 | | ||||
479 | auto availablePages = m_appModel->property("availablePages").value<QObject*>(); | ||||
480 | VERIFY(availablePages); | ||||
481 | | ||||
482 | auto pageListModel = availablePages->property("pageListModel").value<QAbstractItemModel*>(); | ||||
483 | VERIFY(pageListModel); | ||||
484 | waitForStableState(); | ||||
485 | | ||||
486 | QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageNodeName + oldName); | ||||
487 | VERIFY(pageIndex.isValid()); | ||||
488 | | ||||
489 | pageListModel->setData(pageIndex, newName); | ||||
490 | waitForStableState(); | ||||
491 | | ||||
492 | return true; | ||||
493 | } | ||||
494 | | ||||
495 | bool ZanshinContext::I_remove_a_page(const QString &path, const QString &pageName) | ||||
496 | { | ||||
497 | const QString pageNodeName = path + " / "; | ||||
498 | | ||||
499 | VERIFY(!pageNodeName.isEmpty()); | ||||
500 | | ||||
501 | auto availablePages = m_appModel->property("availablePages").value<QObject*>(); | ||||
502 | VERIFY(availablePages); | ||||
503 | | ||||
504 | auto pageListModel = availablePages->property("pageListModel").value<QAbstractItemModel*>(); | ||||
505 | VERIFY(pageListModel); | ||||
506 | waitForStableState(); | ||||
507 | | ||||
508 | QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageNodeName + pageName); | ||||
509 | VERIFY(pageIndex.isValid()); | ||||
510 | | ||||
511 | VERIFY(QMetaObject::invokeMethod(availablePages, "removeItem", | ||||
512 | Q_ARG(QModelIndex, pageIndex))); | ||||
513 | waitForStableState(); | ||||
514 | | ||||
515 | return true; | ||||
516 | } | ||||
517 | | ||||
518 | bool ZanshinContext::I_add_a_task_child(const QString &childName, const QString &parentName) | ||||
519 | { | ||||
520 | waitForStableState(); | ||||
521 | | ||||
522 | auto parentIndex = QModelIndex(); | ||||
523 | for (int row = 0; row < m_indices.size(); row++) { | ||||
524 | auto index = m_indices.at(row); | ||||
525 | if (Zanshin::indexString(index) == parentName) { | ||||
526 | parentIndex = index; | ||||
527 | break; | ||||
528 | } | ||||
529 | } | ||||
530 | | ||||
531 | VERIFY_OR_DUMP(parentIndex.isValid()); | ||||
532 | | ||||
533 | VERIFY(QMetaObject::invokeMethod(m_presentation, | ||||
534 | "addItem", | ||||
535 | Q_ARG(QString, childName), | ||||
536 | Q_ARG(QModelIndex, parentIndex))); | ||||
537 | waitForStableState(); | ||||
538 | | ||||
539 | return true; | ||||
540 | } | ||||
541 | | ||||
542 | bool ZanshinContext::I_list_the_items() | ||||
543 | { | ||||
544 | waitForStableState(); | ||||
545 | collectIndices(); | ||||
546 | waitForStableState(); | ||||
547 | | ||||
548 | return true; | ||||
549 | } | ||||
550 | | ||||
551 | bool ZanshinContext::I_open_the_item_in_the_editor() | ||||
552 | { | ||||
553 | auto task = currentTask(); | ||||
554 | VERIFY(task); | ||||
555 | m_editor = m_appModel->property("editor").value<QObject*>(); | ||||
556 | VERIFY(m_editor); | ||||
557 | VERIFY(m_editor->setProperty("task", QVariant::fromValue(task))); | ||||
558 | | ||||
559 | return true; | ||||
560 | } | ||||
561 | | ||||
562 | bool ZanshinContext::I_mark_the_item_done_in_the_editor() | ||||
563 | { | ||||
564 | VERIFY(m_editor->setProperty("done", true)); | ||||
565 | return true; | ||||
566 | } | ||||
567 | | ||||
568 | bool ZanshinContext::I_change_the_editor_field(const QString &field, const QVariant &value) | ||||
569 | { | ||||
570 | const QByteArray property = (field == QStringLiteral("text")) ? field.toUtf8() | ||||
571 | : (field == QStringLiteral("title")) ? field.toUtf8() | ||||
572 | : (field == QStringLiteral("start date")) ? "startDate" | ||||
573 | : (field == QStringLiteral("due date")) ? "dueDate" | ||||
574 | : QByteArray(); | ||||
575 | | ||||
576 | VERIFY(value.isValid()); | ||||
577 | VERIFY(!property.isEmpty()); | ||||
578 | | ||||
579 | VERIFY(m_editor->setProperty("editingInProgress", true)); | ||||
580 | VERIFY(m_editor->setProperty(property, value)); | ||||
581 | | ||||
582 | return true; | ||||
583 | } | ||||
584 | | ||||
585 | bool ZanshinContext::I_rename_the_item(const QString &taskName) | ||||
586 | { | ||||
587 | VERIFY(m_editor->setProperty("editingInProgress", false)); | ||||
588 | VERIFY(model()->setData(m_index, taskName, Qt::EditRole)); | ||||
589 | waitForStableState(); | ||||
590 | | ||||
591 | return true; | ||||
592 | } | ||||
593 | | ||||
594 | bool ZanshinContext::I_open_the_item_in_the_editor_again() | ||||
595 | { | ||||
596 | auto task = currentTask(); | ||||
597 | VERIFY(task); | ||||
598 | VERIFY(m_editor->setProperty("task", QVariant::fromValue(Domain::Task::Ptr()))); | ||||
599 | VERIFY(m_editor->setProperty("task", QVariant::fromValue(task))); | ||||
600 | waitForStableState(); | ||||
601 | | ||||
602 | return true; | ||||
603 | } | ||||
604 | | ||||
605 | bool ZanshinContext::I_drop_the_item_on_the_central_list(const QString &dropSiteName) | ||||
606 | { | ||||
607 | VERIFY(m_index.isValid()); | ||||
608 | const QMimeData *data = model()->mimeData(QModelIndexList() << m_index); | ||||
609 | | ||||
610 | QAbstractItemModel *destModel = model(); | ||||
611 | QModelIndex dropIndex = Zanshin::findIndex(destModel, dropSiteName); | ||||
612 | VERIFY(dropIndex.isValid()); | ||||
613 | VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); | ||||
614 | waitForStableState(); | ||||
615 | | ||||
616 | return true; | ||||
617 | } | ||||
618 | | ||||
619 | bool ZanshinContext::I_drop_the_item_on_the_blank_area_of_the_central_list() | ||||
620 | { | ||||
621 | VERIFY(m_index.isValid()); | ||||
622 | const QMimeData *data = model()->mimeData(QModelIndexList() << m_index); | ||||
623 | | ||||
624 | QAbstractItemModel *destModel = model(); | ||||
625 | VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, QModelIndex())); | ||||
626 | waitForStableState(); | ||||
627 | | ||||
628 | return true; | ||||
629 | } | ||||
630 | | ||||
631 | bool ZanshinContext::I_drop_items_on_the_central_list(const QString &dropSiteName) | ||||
632 | { | ||||
633 | VERIFY(!m_dragIndices.isEmpty()); | ||||
634 | QModelIndexList indexes; | ||||
635 | bool allValid = true; | ||||
636 | std::transform(m_dragIndices.constBegin(), m_dragIndices.constEnd(), | ||||
637 | std::back_inserter(indexes), | ||||
638 | [&allValid] (const QPersistentModelIndex &index) { | ||||
639 | allValid &= index.isValid(); | ||||
640 | return index; | ||||
641 | }); | ||||
642 | VERIFY(allValid); | ||||
643 | | ||||
644 | const QMimeData *data = model()->mimeData(indexes); | ||||
645 | | ||||
646 | QAbstractItemModel *destModel = model(); | ||||
647 | QModelIndex dropIndex = Zanshin::findIndex(destModel, dropSiteName); | ||||
648 | VERIFY(dropIndex.isValid()); | ||||
649 | VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); | ||||
650 | waitForStableState(); | ||||
651 | | ||||
652 | return true; | ||||
653 | } | ||||
654 | | ||||
655 | bool ZanshinContext::I_drop_the_item_on_the_page_list(const QString &pageName) | ||||
656 | { | ||||
657 | VERIFY(m_index.isValid()); | ||||
658 | const QMimeData *data = model()->mimeData(QModelIndexList() << m_index); | ||||
659 | | ||||
660 | auto availablePages = m_appModel->property("availablePages").value<QObject*>(); | ||||
661 | VERIFY(availablePages); | ||||
662 | | ||||
663 | auto destModel = availablePages->property("pageListModel").value<QAbstractItemModel*>(); | ||||
664 | VERIFY(destModel); | ||||
665 | waitForStableState(); | ||||
666 | | ||||
667 | QModelIndex dropIndex = Zanshin::findIndex(destModel, pageName); | ||||
668 | VERIFY(dropIndex.isValid()); | ||||
669 | VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); | ||||
670 | waitForStableState(); | ||||
671 | | ||||
672 | return true; | ||||
673 | } | ||||
674 | | ||||
675 | bool ZanshinContext::I_drop_items_on_the_page_list(const QString &pageName) | ||||
676 | { | ||||
677 | VERIFY(!m_dragIndices.isEmpty()); | ||||
678 | QModelIndexList indexes; | ||||
679 | bool allValid = true; | ||||
680 | std::transform(m_dragIndices.constBegin(), m_dragIndices.constEnd(), | ||||
681 | std::back_inserter(indexes), | ||||
682 | [&allValid] (const QPersistentModelIndex &index) { | ||||
683 | allValid &= index.isValid(); | ||||
684 | return index; | ||||
685 | }); | ||||
686 | VERIFY(allValid); | ||||
687 | | ||||
688 | const QMimeData *data = model()->mimeData(indexes); | ||||
689 | | ||||
690 | auto availablePages = m_appModel->property("availablePages").value<QObject*>(); | ||||
691 | VERIFY(availablePages); | ||||
692 | | ||||
693 | auto destModel = availablePages->property("pageListModel").value<QAbstractItemModel*>(); | ||||
694 | VERIFY(destModel); | ||||
695 | waitForStableState(); | ||||
696 | | ||||
697 | QModelIndex dropIndex = Zanshin::findIndex(destModel, pageName); | ||||
698 | VERIFY(dropIndex.isValid()); | ||||
699 | VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); | ||||
700 | waitForStableState(); | ||||
701 | | ||||
702 | return true; | ||||
703 | } | ||||
704 | | ||||
705 | bool ZanshinContext::I_change_the_setting(const QString &key, qint64 id) | ||||
706 | { | ||||
707 | KConfigGroup config(KSharedConfig::openConfig(), "General"); | ||||
708 | config.writeEntry(key, id); | ||||
709 | return true; | ||||
710 | } | ||||
711 | | ||||
712 | bool ZanshinContext::I_change_the_default_data_source(const QString &sourceName) | ||||
713 | { | ||||
714 | waitForStableState(); | ||||
715 | auto sourceIndex = Zanshin::findIndex(model(), sourceName); | ||||
716 | auto availableSources = m_appModel->property("availableSources").value<QObject*>(); | ||||
717 | VERIFY(availableSources); | ||||
718 | VERIFY(QMetaObject::invokeMethod(availableSources, "setDefaultItem", Q_ARG(QModelIndex, sourceIndex))); | ||||
719 | waitForStableState(); | ||||
720 | | ||||
721 | return true; | ||||
722 | } | ||||
723 | | ||||
724 | bool ZanshinContext::the_list_is(const TableData &data) | ||||
725 | { | ||||
726 | auto roleNames = model()->roleNames(); | ||||
727 | QVector<int> usedRoles; | ||||
728 | | ||||
729 | for (const auto &roleName : data.roles) { | ||||
730 | const int role = roleNames.key(roleName, -1); | ||||
731 | VERIFY_OR_DUMP(role != -1 && !usedRoles.contains(role)); | ||||
732 | usedRoles << role; | ||||
733 | } | ||||
734 | | ||||
735 | QStandardItemModel inputModel; | ||||
736 | for (const auto &row : data.rows) { | ||||
737 | VERIFY_OR_DUMP(usedRoles.size() == row.size()); | ||||
738 | | ||||
739 | QStandardItem *item = new QStandardItem; | ||||
740 | for (int i = 0; i < row.size(); ++i) { | ||||
741 | const auto role = usedRoles.at(i); | ||||
742 | const auto value = row.at(i); | ||||
743 | item->setData(value, role); | ||||
744 | } | ||||
745 | inputModel.appendRow(item); | ||||
746 | } | ||||
747 | | ||||
748 | QSortFilterProxyModel proxy; | ||||
749 | | ||||
750 | QAbstractItemModel *referenceModel; | ||||
751 | if (!qobject_cast<QSortFilterProxyModel *>(sourceModel())) { | ||||
752 | referenceModel = &proxy; | ||||
753 | proxy.setSourceModel(&inputModel); | ||||
754 | proxy.setSortRole(Qt::DisplayRole); | ||||
755 | proxy.sort(0); | ||||
756 | proxy.setObjectName(QStringLiteral("the_list_is_proxy")); | ||||
757 | } else { | ||||
758 | referenceModel = &inputModel; | ||||
759 | } | ||||
760 | | ||||
761 | for (int row = 0; row < m_indices.size(); row++) { | ||||
762 | QModelIndex expectedIndex = referenceModel->index(row, 0); | ||||
763 | QModelIndex resultIndex = m_indices.at(row); | ||||
764 | | ||||
765 | foreach (const auto &role, usedRoles) { | ||||
766 | COMPARE_OR_DUMP(Zanshin::indexString(resultIndex, role), | ||||
767 | Zanshin::indexString(expectedIndex, role)); | ||||
768 | } | ||||
769 | } | ||||
770 | COMPARE_OR_DUMP(m_indices.size(), referenceModel->rowCount()); | ||||
771 | | ||||
772 | return true; | ||||
773 | } | ||||
774 | | ||||
775 | bool ZanshinContext::the_list_contains(const QString &itemName) | ||||
776 | { | ||||
777 | for (int row = 0; row < m_indices.size(); row++) { | ||||
778 | if (Zanshin::indexString(m_indices.at(row)) == itemName) | ||||
779 | return true; | ||||
780 | } | ||||
781 | | ||||
782 | VERIFY_OR_DUMP(false); | ||||
783 | return false; | ||||
784 | } | ||||
785 | | ||||
786 | bool ZanshinContext::the_list_does_not_contain(const QString &itemName) | ||||
787 | { | ||||
788 | for (int row = 0; row < m_indices.size(); row++) { | ||||
789 | VERIFY_OR_DUMP(Zanshin::indexString(m_indices.at(row)) != itemName); | ||||
790 | } | ||||
791 | | ||||
792 | return true; | ||||
793 | } | ||||
794 | | ||||
795 | bool ZanshinContext::the_task_corresponding_to_the_item_is_done() | ||||
796 | { | ||||
797 | auto task = currentTask(); | ||||
798 | VERIFY(task); | ||||
799 | VERIFY(task->isDone()); | ||||
800 | | ||||
801 | return true; | ||||
802 | } | ||||
803 | | ||||
804 | bool ZanshinContext::the_editor_shows_the_task_as_done() | ||||
805 | { | ||||
806 | VERIFY(m_editor->property("done").toBool()); | ||||
807 | return true; | ||||
808 | } | ||||
809 | | ||||
810 | bool ZanshinContext::the_editor_shows_the_field(const QString &field, const QVariant &expectedValue) | ||||
811 | { | ||||
812 | const QByteArray property = (field == QStringLiteral("text")) ? field.toUtf8() | ||||
813 | : (field == QStringLiteral("title")) ? field.toUtf8() | ||||
814 | : (field == QStringLiteral("start date")) ? "startDate" | ||||
815 | : (field == QStringLiteral("due date")) ? "dueDate" | ||||
816 | : QByteArray(); | ||||
817 | | ||||
818 | VERIFY(expectedValue.isValid()); | ||||
819 | VERIFY(!property.isEmpty()); | ||||
820 | | ||||
821 | COMPARE(m_editor->property(property), expectedValue); | ||||
822 | | ||||
823 | return true; | ||||
824 | } | ||||
825 | | ||||
826 | bool ZanshinContext::the_default_data_source_is(const QString &expectedName) | ||||
827 | { | ||||
828 | waitForStableState(); | ||||
829 | auto expectedIndex = Zanshin::findIndex(model(), expectedName); | ||||
830 | VERIFY(expectedIndex.isValid()); | ||||
831 | auto defaultRole = model()->roleNames().key("default", -1); | ||||
832 | VERIFY(expectedIndex.data(defaultRole).toBool()); | ||||
833 | | ||||
834 | return true; | ||||
835 | } | ||||
836 | | ||||
837 | bool ZanshinContext::the_setting_is(const QString &key, qint64 expectedId) | ||||
838 | { | ||||
839 | KConfigGroup config(KSharedConfig::openConfig(), "General"); | ||||
840 | const qint64 id = config.readEntry(key, -1); | ||||
841 | COMPARE(id, expectedId); | ||||
842 | | ||||
843 | return true; | ||||
844 | } |