blob: a9c9b5e65ae7fa60e00770a3d5d605fad2e0fc1f [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include "../shared/util.h"
#include "qquickabstractdialog_p.h"
#include <QtQml/QQmlComponent>
#include <QtQml/QQmlContext>
#include <QtQml/QQmlEngine>
#include <QtQuick/QQuickItem>
#include <QtQuick/QQuickView>
#include <QSignalSpy>
class tst_dialogs : public QQmlDataTest
{
Q_OBJECT
public:
private slots:
void initTestCase()
{
QQmlDataTest::initTestCase();
}
//Dialog
void dialogImplicitWidth_data();
void dialogImplicitWidth();
void dialogContentResize();
void dialogButtonHandler_data();
void dialogButtonHandler();
void dialogKeyHandler_data();
void dialogKeyHandler();
void dialogWithDynamicTitle();
// FileDialog
void fileDialogDefaultModality();
void fileDialogNonModal();
void fileDialogNameFilters();
void fileDialogDefaultSuffix();
private:
};
void tst_dialogs::dialogImplicitWidth_data()
{
QTest::addColumn<int>("standardButtons");
QTest::addColumn<int>("minimumHeight");
QTest::newRow("No buttons") <<
int(QQuickAbstractDialog::NoButton) <<
150;
QTest::newRow("OK button") <<
int(QQuickAbstractDialog::Ok) <<
160;
}
void tst_dialogs::dialogImplicitWidth()
{
QFETCH(int, standardButtons);
QFETCH(int, minimumHeight);
/* This is the outerSpacing from DefaultDialogWrapper.qml,
* which is always present */
int heightMargins = 12 * 2;
QQmlEngine engine;
engine.rootContext()->setContextProperty("buttonsFromTest", standardButtons);
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("DialogImplicitSize.qml"));
QObject *created = component.create();
QScopedPointer<QObject> cleanup(created);
QVERIFY2(created, qPrintable(component.errorString()));
QTRY_VERIFY(created->property("width").toInt() >= 400);
QTRY_VERIFY(created->property("height").toInt() >= minimumHeight + heightMargins);
}
void tst_dialogs::dialogContentResize()
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("DialogMinimumSize.qml"));
QObject *created = component.create();
QScopedPointer<QObject> cleanup(created);
QVERIFY2(created, qPrintable(component.errorString()));
QTRY_COMPARE(created->property("width").toInt(), 400);
QTRY_COMPARE(created->property("height").toInt(), 300);
// Check that the content item has been sized up from its implicit size
QQuickItem *userContent = created->findChild<QQuickItem*>("userContent");
QVERIFY(userContent);
QVERIFY(userContent->width() > 350);
QVERIFY(userContent->height() > 200);
}
void tst_dialogs::dialogButtonHandler_data()
{
QTest::addColumn<int>("standardButtons");
QTest::addColumn<bool>("mustBlock");
QTest::addColumn<QString>("expectedAction");
QTest::newRow("Cancel, ignored") <<
int(QQuickAbstractDialog::Cancel) <<
false <<
"rejected";
QTest::newRow("Cancel, blocked") <<
int(QQuickAbstractDialog::Cancel) <<
true <<
"";
QTest::newRow("OK, ignored") <<
int(QQuickAbstractDialog::Ok) <<
false <<
"accepted";
QTest::newRow("OK, blocked") <<
int(QQuickAbstractDialog::Ok) <<
true <<
"";
}
void tst_dialogs::dialogButtonHandler()
{
QFETCH(int, standardButtons);
QFETCH(bool, mustBlock);
QFETCH(QString, expectedAction);
QQmlEngine engine;
engine.rootContext()->setContextProperty("buttonsFromTest", standardButtons);
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("DialogButtonHandler.qml"));
QObject *root = component.create();
QScopedPointer<QObject> cleanup(root);
QVERIFY2(root, qPrintable(component.errorString()));
root->setProperty("visible", true);
root->setProperty("mustBlock", mustBlock);
QQuickWindow *window = root->findChild<QQuickWindow*>();
QVERIFY(QTest::qWaitForWindowExposed(window));
/* Hack to find the created buttons: since they are created by a
* QQuickRepeater, they don't appear on the hierarchy tree; therefore, we
* first need to find the repeater, and then to get its child. */
const QList<QQuickItem*> children = root->findChildren<QQuickItem*>();
QQuickItem *buttonWidget = nullptr;
for (QQuickItem *child: children) {
if (qstrcmp(child->metaObject()->className(), "QQuickRepeater") == 0 &&
child->property("count").toInt() > 0) {
int index = 0;
QMetaObject::invokeMethod(child,
"itemAt",
Q_RETURN_ARG(QQuickItem *, buttonWidget),
Q_ARG(int, index));
break;
}
}
QVERIFY(buttonWidget);
const QPointF buttonCenterF(buttonWidget->width() / 2,
buttonWidget->height() / 2);
const QPoint buttonCenter = buttonWidget->mapToScene(buttonCenterF).toPoint();
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, buttonCenter);
QTRY_VERIFY(root->property("handlerWasCalled").toBool());
QCOMPARE(root->property("buttonCode").toInt(), standardButtons);
QCOMPARE(root->property("keyCode").toInt(), 0);
QCOMPARE(root->property("actionCalled").toString(), expectedAction);
}
void tst_dialogs::dialogKeyHandler_data()
{
QTest::addColumn<int>("key");
QTest::addColumn<bool>("mustBlock");
QTest::addColumn<int>("expectedButton");
QTest::addColumn<QString>("expectedAction");
QTest::newRow("Escape, ignored") <<
int(Qt::Key_Escape) <<
false <<
int(QQuickAbstractDialog::Cancel) <<
"rejected";
QTest::newRow("Cancel, blocked") <<
int(Qt::Key_Escape) <<
true <<
int(QQuickAbstractDialog::Cancel) <<
"";
QTest::newRow("Enter, ignored") <<
int(Qt::Key_Enter) <<
false <<
int(QQuickAbstractDialog::Ok) <<
"accepted";
QTest::newRow("Enter, blocked") <<
int(Qt::Key_Enter) <<
true <<
int(QQuickAbstractDialog::Ok) <<
"";
}
void tst_dialogs::dialogKeyHandler()
{
QFETCH(int, key);
QFETCH(bool, mustBlock);
QFETCH(int, expectedButton);
QFETCH(QString, expectedAction);
QQmlEngine engine;
QQuickAbstractDialog::StandardButtons buttons =
QQuickAbstractDialog::Ok | QQuickAbstractDialog::Cancel;
engine.rootContext()->setContextProperty("buttonsFromTest", int(buttons));
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("DialogButtonHandler.qml"));
QObject *root = component.create();
QScopedPointer<QObject> cleanup(root);
QVERIFY2(root, qPrintable(component.errorString()));
root->setProperty("visible", true);
root->setProperty("mustBlock", mustBlock);
QQuickWindow *window = root->findChild<QQuickWindow*>();
QVERIFY(QTest::qWaitForWindowExposed(window));
QTest::keyClick(window, Qt::Key(key));
QTRY_VERIFY(root->property("handlerWasCalled").toBool());
QCOMPARE(root->property("buttonCode").toInt(), expectedButton);
QCOMPARE(root->property("keyCode").toInt(), key);
QCOMPARE(root->property("actionCalled").toString(), expectedAction);
}
void tst_dialogs::fileDialogDefaultModality()
{
QQuickView *window = new QQuickView;
QScopedPointer<QQuickWindow> cleanup(window);
window->setSource(testFileUrl("RectWithFileDialog.qml"));
window->setGeometry(240,240,1024,320);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
QVERIFY2(window->rootObject(), qPrintable(window->errors().value(0).toString()));
// Click to show
QObject *dlg = qvariant_cast<QObject *>(window->rootObject()->property("fileDialog"));
QSignalSpy spyVisibilityChanged(dlg, SIGNAL(visibilityChanged()));
QTest::mouseClick(window, Qt::LeftButton, {}, QPoint(1000, 100)); // show
QTRY_VERIFY(spyVisibilityChanged.count() > 0);
int visibilityChangedCount = spyVisibilityChanged.count();
// Can't hide by clicking the main window, because dialog is modal.
QTest::mouseClick(window, Qt::LeftButton, {}, QPoint(1000, 100));
/*
On OS X, if you send an event directly to a window, the modal dialog
doesn't block the event, so the window will process it normally. This
is a different code path compared to having a user click the mouse and
generate a native event; in that case the OS does the filtering itself,
and Qt will not even see the event. But simulating real events in the
test framework is generally unstable. So there isn't a good way to test
modality on OS X.
This test sometimes fails on other platforms too. Maybe it's not reliable
to try to click the main window in a location which is outside the
dialog, without checking or guaranteeing it somehow.
*/
QSKIP("Modality test is flaky in general and doesn't work at all on OS X");
// So we expect no change in visibility.
QCOMPARE(spyVisibilityChanged.count(), visibilityChangedCount);
QCOMPARE(dlg->property("visible").toBool(), true);
QMetaObject::invokeMethod(dlg, "close");
QTRY_VERIFY(spyVisibilityChanged.count() > visibilityChangedCount);
visibilityChangedCount = spyVisibilityChanged.count();
QCOMPARE(dlg->property("visible").toBool(), false);
QMetaObject::invokeMethod(dlg, "open");
QTRY_VERIFY(spyVisibilityChanged.count() > visibilityChangedCount);
QCOMPARE(dlg->property("visible").toBool(), true);
QCOMPARE(dlg->property("modality").toInt(), (int)Qt::WindowModal);
}
void tst_dialogs::fileDialogNonModal()
{
QQuickView *window = new QQuickView;
QScopedPointer<QQuickWindow> cleanup(window);
window->setSource(testFileUrl("RectWithFileDialog.qml"));
window->setGeometry(240,240,1024,320);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
QVERIFY2(window->rootObject(), qPrintable(window->errors().value(0).toString()));
// Click to toggle visibility
QObject *dlg = qvariant_cast<QObject *>(window->rootObject()->property("fileDialog"));
dlg->setProperty("modality", QVariant((int)Qt::NonModal));
QSignalSpy spyVisibilityChanged(dlg, SIGNAL(visibilityChanged()));
QTest::mouseClick(window, Qt::LeftButton, {}, QPoint(1000, 100)); // show
QTRY_VERIFY(spyVisibilityChanged.count() > 0);
int visibilityChangedCount = spyVisibilityChanged.count();
QCOMPARE(dlg->property("visible").toBool(), true);
QTest::mouseClick(window, Qt::LeftButton, {}, QPoint(1000, 100)); // hide
QTRY_VERIFY(spyVisibilityChanged.count() > visibilityChangedCount);
QCOMPARE(dlg->property("visible").toBool(), false);
#ifdef Q_OS_WIN
QCOMPARE(dlg->property("modality").toInt(), (int)Qt::ApplicationModal);
#else
QCOMPARE(dlg->property("modality").toInt(), (int)Qt::NonModal);
#endif
}
void tst_dialogs::fileDialogNameFilters()
{
QQuickView *window = new QQuickView;
QScopedPointer<QQuickWindow> cleanup(window);
window->setSource(testFileUrl("RectWithFileDialog.qml"));
window->setGeometry(240,240,1024,320);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
QVERIFY2(window->rootObject(), qPrintable(window->errors().value(0).toString()));
QObject *dlg = qvariant_cast<QObject *>(window->rootObject()->property("fileDialog"));
QStringList filters;
filters << "QML files (*.qml)";
filters << "Image files (*.jpg, *.png, *.gif)";
filters << "All files (*)";
dlg->setProperty("nameFilters", QVariant(filters));
QCOMPARE(dlg->property("selectedNameFilter").toString(), filters.first());
}
void tst_dialogs::fileDialogDefaultSuffix()
{
QQuickView *window = new QQuickView;
QScopedPointer<QQuickWindow> cleanup(window);
const QUrl sourceUrl = testFileUrl("RectWithFileDialog.qml");
window->setSource(sourceUrl);
window->setGeometry(240, 240, 1024, 320);
window->show();
QTRY_VERIFY(QTest::qWaitForWindowExposed(window));
QVERIFY(window->rootObject());
QObject *dlg = qvariant_cast<QObject *>(window->rootObject()->property("fileDialog"));
QCOMPARE(dlg->property("defaultSuffix").toString(), QString());
dlg->setProperty("defaultSuffix", "txt");
QCOMPARE(dlg->property("defaultSuffix").toString(), QString("txt"));
dlg->setProperty("defaultSuffix", ".txt");
QCOMPARE(dlg->property("defaultSuffix").toString(), QString("txt"));
dlg->setProperty("defaultSuffix", QString());
QCOMPARE(dlg->property("defaultSuffix").toString(), QString());
}
void tst_dialogs::dialogWithDynamicTitle()
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("DialogWithDynamicTitle.qml"));
QObject *dlg = component.create();
QScopedPointer<QObject> cleanup(dlg);
QVERIFY2(dlg, qPrintable(component.errorString()));
QWindow *window = dlg->findChild<QWindow *>();
QVERIFY(window);
QTRY_COMPARE(window->title(), QLatin1String("Title"));
dlg->setProperty("newTitle", true);
QTRY_COMPARE(window->title(), QLatin1String("New Title"));
}
QTEST_MAIN(tst_dialogs)
#include "tst_dialogs.moc"