Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
dialogstestutils_p.h
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#ifndef DIALOGSTESTUTILS_H
5#define DIALOGSTESTUTILS_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
19
20#include <QtTest/qtest.h>
21#include <QtTest/private/qtestresult_p.h>
22#include <QtQml/qqmlfileselector.h>
23#include <QtQuickControls2/qquickstyle.h>
24#include <QtQuickTest/QtQuickTest>
25#include <QtQuickTestUtils/private/qmlutils_p.h>
26#include <QtQuickTestUtils/private/visualtestutils_p.h>
27
28// We need these for Windows, because FolderListModel returns a lowercase drive letter; e.g.:
29// "file:///c:/blah.txt", whereas other API returns "file:///C:/blah.txt".
30#define COMPARE_URL(url1, url2)
31 QCOMPARE(QFileInfo(url1.toLocalFile()).canonicalFilePath(), QFileInfo(url2.toLocalFile()).canonicalFilePath());
32
33// Store a copy of the arguments in case { ... } list initializer syntax is used as an argument,
34// which could result in two different lists being created and passed to std::transform()
35// (and would also require it to be enclosed in parentheses everywhere it's used).
36#define COMPARE_URLS(actualUrls, expectedUrls) \
37{
38 const QList<QUrl> actualUrlsCopy = actualUrls;
39 QList<QString> actualPaths;
40 std::transform(actualUrlsCopy.begin(), actualUrlsCopy.end(), std::back_insert_iterator(actualPaths),
41 [](const QUrl &url) { return QFileInfo(url.toLocalFile()).canonicalFilePath(); });
42 const QList<QUrl> expectedUrlsCopy = expectedUrls;
43 QList<QString> expectedPaths;
44 std::transform(expectedUrlsCopy.begin(), expectedUrlsCopy.end(), std::back_insert_iterator(expectedPaths),
45 [](const QUrl &url) { return QFileInfo(url.toLocalFile()).canonicalFilePath(); });
46 QCOMPARE(actualPaths, expectedPaths); \
47}
48
49#define OPEN_QUICK_DIALOG() QVERIFY2
50 (dialogHelper.isWindowInitialized(), dialogHelper.failureMessage()); QVERIFY
51 (dialogHelper.waitForWindowActive()); QVERIFY
52 (dialogHelper.openDialog()); QTRY_VERIFY
53 (dialogHelper.isQuickDialogOpen());
54
55#define CLOSE_QUICK_DIALOG()
56 do {
57 dialogHelper.dialog->close();
58 QVERIFY(!dialogHelper.dialog->isVisible());
59 QTRY_VERIFY(!dialogHelper.quickDialog->isVisible());
60 } while (false)
61
62QT_BEGIN_NAMESPACE
63class QWindow;
64
65class QQuickListView;
66
67class QQuickAbstractButton;
68
69class QQuickDialogButtonBox;
70class QQuickFolderBreadcrumbBar;
71
73{
74
75// Saves duplicating a bunch of code in every test.
76template<typename DialogType, typename QuickDialogType>
78{
79public:
80 DialogTestHelper(QQmlDataTest *testCase, const QString &testFilePath,
81 const QStringList &qmlImportPaths = {}, const QVariantMap &initialProperties = {}) :
83 {
84 if (!appHelper.ready)
85 return;
86
87 // The QQmlFileSelector constructor will append itself to the typeloader's
88 // QQmlTypeLoaderConfiguredData::urlInterceptors list.
89 // QQmlApplicationEnginePrivate::init() does the same thing for normal quick applications,
90 // but we need to do it again here, to enable file selector functionality for dialog tests.
91 fileSelector = new QQmlFileSelector(&appHelper.engine);
92
93 dialog = appHelper.window->property("dialog").value<DialogType*>();
94 if (!dialog) {
95 appHelper.errorMessage = "\"dialog\" property is not valid";
96 return;
97 }
98
99 appHelper.window->show();
100 appHelper.window->requestActivate();
101 }
102
104 {
105 dialog->close();
106 }
107
109 {
110 return appHelper.ready;
111 }
112
114 {
115 return QTest::qWaitForWindowActive(appHelper.window);
116 }
117
118 /*
119 Opens the dialog. For non-native dialogs, it is necessary to ensure that
120 isQuickDialogOpen() returns true before trying to access its internals.
121 */
122 virtual bool openDialog()
123 {
124 dialog->open();
125 if (!dialog->isVisible()) {
126 appHelper.errorMessage = "Dialog is not visible";
127 return false;
128 }
129
130 // We might want to call this function more than once,
131 // and we only need to get these members the first time.
132 if (!quickDialog) {
133 quickDialog = appHelper.window->findChild<QuickDialogType*>();
134 if (!quickDialog) {
135 appHelper.errorMessage = "Can't find Qt Quick-based dialog";
136 return false;
137 }
138 }
139
140 return true;
141 }
142
144 {
145 return quickDialog->contentItem()->window();
146 }
147
149 {
150 if (!isQuickDialogOpen()) {
151 QByteArray msg("Dialog wasn't open, when {} was called");
152 msg.replace("{}", __func__);
153 appHelper.errorMessage = msg;
154 return false;
155 }
156 QQuickWindow *dialogPopupWindow = popupWindow();
157 if (!dialogPopupWindow)
158 return false;
159 dialogPopupWindow->requestActivate();
160 if (!QTest::qWaitForWindowActive(dialogPopupWindow))
161 return false;
162 return QQuickTest::qWaitForPolish(dialogPopupWindow);
163 }
164
165 bool isQuickDialogOpen() const
166 {
167 return quickDialog->isOpened();
168 }
169
171 {
172 return appHelper.window;
173 }
174
175 const char *failureMessage() const
176 {
177 return appHelper.errorMessage.constData();
178 }
179
181 DialogType *dialog = nullptr;
182 QuickDialogType *quickDialog = nullptr;
184};
185
186bool verifyFileDialogDelegates(QQuickListView *fileDialogListView, const QStringList &expectedFiles, QString &failureMessage);
187
188bool verifyBreadcrumbDelegates(QQuickFolderBreadcrumbBar *breadcrumbBar, const QUrl &expectedFolder, QString &failureMessage);
189
190QQuickAbstractButton *findDialogButton(QQuickDialogButtonBox *box, const QString &buttonText);
191
192void enterText(QWindow *window, const QString &textToEnter);
193}
194
195#define QTEST_QUICKDIALOGS_MAIN(TestCase) int
196 main(int argc, char *argv[]) \
197{
198 qputenv("QML_NO_TOUCH_COMPRESSION", "1");
199 QGuiApplication app(argc, argv);
200 TestCase tc;
201 QTEST_SET_MAIN_SOURCE_PATH
202 int res = 0;
203 QTest::qInit(&tc, argc, argv);
204 const QByteArray testObjectName = QTestResult::currentTestObjectName();
205 QByteArray testName;
206 const QStringList allStyles = {"Basic", "Fusion", "Material", "Universal"};
207 const QStringList styles = qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE") ? QStringList{qEnvironmentVariable("QT_QUICK_CONTROLS_STYLE")} : allStyles;
208 for (const QString &style : styles) {
209 qmlClearTypeRegistrations();
210 QQuickStyle::setStyle(style);
211 testName = testObjectName + "::" + style.toLocal8Bit();
212 QTestResult::setCurrentTestObject(testName);
213 res += QTest::qRun();
214 }
215 QTestResult::setCurrentTestObject(testObjectName);
216 QTest::qCleanup();
217 return res; \
218}
219
220
221QT_END_NAMESPACE
222
223#endif // DIALOGSTESTUTILS_H
Q_REQUIRED_RESULT bool isWindowInitialized() const
DialogTestHelper(QQmlDataTest *testCase, const QString &testFilePath, const QStringList &qmlImportPaths={}, const QVariantMap &initialProperties={})
Q_REQUIRED_RESULT bool waitForPopupWindowActiveAndPolished()
QQuickVisualTestUtils::QQuickApplicationHelper appHelper
void enterText(QWindow *window, const QString &textToEnter)
QQuickAbstractButton * findDialogButton(QQuickDialogButtonBox *box, const QString &buttonText)
bool verifyBreadcrumbDelegates(QQuickFolderBreadcrumbBar *breadcrumbBar, const QUrl &expectedFolder, QString &failureMessage)
bool verifyFileDialogDelegates(QQuickListView *fileDialogListView, const QStringList &expectedFiles, QString &failureMessage)