Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QTest>
#include <QtTest/qtestaccessible.h>
#include <QtCore/qregularexpression.h>
#include <QtGui/qaccessible.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qaccessiblecache_p.h>
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformaccessibility.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlproperty.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qaccessiblequickitem_p.h>
#include <QtQuick/private/qaccessiblequickflickable_p.h>
#include <QtQuick/private/qaccessiblequicklistview_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QQmlComponent>
#include <QQmlApplicationEngine>
using namespace Qt::StringLiterals;
using namespace QQuickViewTestUtils;
#define EXPECT(cond) \
do { \
if (!errorAt && !(cond)) { \
errorAt = __LINE__; \
qWarning("level: %d, middle: %d, role: %d (%s)", treelevel, middle, iface->role(), #cond); \
} \
} while (0)
//TESTED_FILES=
class tst_QQuickAccessible : public QQmlDataTest
{
Q_OBJECT
public:
tst_QQuickAccessible();
virtual ~tst_QQuickAccessible();
public slots:
void initTestCase() override;
void cleanupTestCase();
void init() override;
void cleanup();
private slots:
void commonTests_data();
void commonTests();
void quickAttachedProperties();
void quickAttachedProperties_description_data();
void quickAttachedProperties_description();
void attachedWins();
void basicPropertiesTest();
void hitTest();
void checkableTest();
void expandableTest();
void ignoredTest();
void windowContainer();
void passwordTest();
void announceTest();
void eventTest();
void relations_data();
void relations();
void controlsThatShouldSendObjectShow_data();
void controlsThatShouldSendObjectShow();
void editableTextInterface_data();
void editableTextInterface();
void labelForNullptrTest();
void flickableViewportTest_data();
void flickableViewportTest();
void listViewViewportTest_data();
void listViewViewportTest();
void listViewSetPositionTest_data();
void listViewSetPositionTest();
};
tst_QQuickAccessible::tst_QQuickAccessible()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
}
tst_QQuickAccessible::~tst_QQuickAccessible()
{
}
void tst_QQuickAccessible::initTestCase()
{
QQmlDataTest::initTestCase();
QTestAccessibility::initialize();
QPlatformIntegration *pfIntegration = QGuiApplicationPrivate::platformIntegration();
if (!pfIntegration->accessibility())
QSKIP("This platform does not support accessibility");
pfIntegration->accessibility()->setActive(true);
}
void tst_QQuickAccessible::cleanupTestCase()
{
QTestAccessibility::cleanup();
}
void tst_QQuickAccessible::init()
{
QQmlDataTest::init();
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::cleanup()
{
const auto list = QTestAccessibility::events();
if (!list.isEmpty()) {
qWarning().noquote() << list.size()
<< "accessibility event(s) were not handled in testfunction '"
<< QTest::currentTestFunction() << "':";
for (int i = 0; i < list.size(); ++i) {
auto object = list.at(i)->object();
QString objectInfo = object ? QDebug::toString(object)
: u"[deleted object]"_s;
qWarning().noquote() << " " << (i + 1) << objectInfo
<< "Event: '" << qAccessibleEventString(list.at(i)->type())
<< "' Child: " << list.at(i)->child();
}
}
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::commonTests_data()
{
QTest::addColumn<QString>("accessibleRoleFileName");
QTest::newRow("Text") << "text.qml";
QTest::newRow("PushButton") << "pushbutton.qml";
}
void tst_QQuickAccessible::commonTests()
{
QFETCH(QString, accessibleRoleFileName);
qDebug() << "testing" << accessibleRoleFileName;
auto view = std::make_unique<QQuickView>();
// view->setFixedSize(240,320);
view->setSource(testFileUrl(accessibleRoleFileName));
view->show();
// view->setFocus();
QVERIFY(view->rootObject() != nullptr);
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(view.get());
QVERIFY(iface);
view.reset();
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::quickAttachedProperties()
{
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\nItem {\n"
"}", QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object.get());
QCOMPARE(attachedObject, static_cast<QObject*>(nullptr));
}
// Attaching to non-item
{
QObject parent;
QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML QtObject: Accessible attached property must be attached to an object deriving from Item or Action");
QQuickAccessibleAttached *attachedObj = new QQuickAccessibleAttached(&parent);
QCOMPARE(attachedObj->ignored(), false);
attachedObj->setIgnored(true);
QCOMPARE(attachedObj->ignored(), false);
attachedObj->setIgnored(false);
QCOMPARE(attachedObj->ignored(), false);
}
// Attached property
{
QQuickItem parent;
QQuickAccessibleAttached *attachedObj = new QQuickAccessibleAttached(&parent);
attachedObj->name();
QVariant pp = attachedObj->property("name");
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\nItem {\n"
"Accessible.role: Accessible.Button\n"
"}", QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>(
QQuickAccessibleAttached::attachedProperties(object.get()));
QVERIFY(attachedObject);
if (attachedObject) {
QVariant p = attachedObject->property("role");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toInt(), int(QAccessible::PushButton));
p = attachedObject->property("name");
QCOMPARE(p.typeId(), QMetaType::QString);
QVERIFY2(p.value<QString>().isEmpty(), QTest::toString(p));
p = attachedObject->property("description");
QCOMPARE(p.typeId(), QMetaType::QString);
QVERIFY2(p.value<QString>().isEmpty(), QTest::toString(p));
QCOMPARE(attachedObject->wasNameExplicitlySet(), false);
}
}
// Attached property: QTBUG-133564
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick\n"
"Item {\n"
" Accessible.role: Accessible.Button\n"
" property int value: 0\n"
" property int cursorPosition: 0\n"
"}",
QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
const auto attachedObject = qobject_cast<QQuickAccessibleAttached *>(
QQuickAccessibleAttached::attachedProperties(object.get()));
QVERIFY(attachedObject);
const auto events = QTestAccessibility::events();
// If the value interface is not implemented then
// the valueChanged() signal should not be connected.
object->setProperty("value", 1);
QCOMPARE(QTestAccessibility::events(), events);
// If the text interface is not implemented then
// the cursorPositionChanged() signal should not be connected.
object->setProperty("cursorPosition", 1);
QCOMPARE(QTestAccessibility::events(), events);
}
// Attached property
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\nItem {\n"
"Accessible.role: Accessible.Button\n"
"Accessible.name: \"Donald\"\n"
"Accessible.description: \"Duck\"\n"
"}", QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>(
QQuickAccessibleAttached::attachedProperties(object.get()));
QVERIFY(attachedObject);
if (attachedObject) {
QVariant p = attachedObject->property("role");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toInt(), int(QAccessible::PushButton));
p = attachedObject->property("name");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toString(), QLatin1String("Donald"));
p = attachedObject->property("description");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toString(), QLatin1String("Duck"));
QCOMPARE(attachedObject->wasNameExplicitlySet(), true);
}
}
// Check overriding of attached role for Text
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\nText {\n"
"Accessible.role: Accessible.Button\n"
"Accessible.name: \"TextButton\"\n"
"Accessible.description: \"Text Button\"\n"
"}", QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object.get());
QVERIFY(attachedObject);
if (attachedObject) {
QVariant p = attachedObject->property("role");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toInt(), int(QAccessible::PushButton));
p = attachedObject->property("name");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toString(), QLatin1String("TextButton"));
p = attachedObject->property("description");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toString(), QLatin1String("Text Button"));
}
}
// Check overriding of attached role for Text
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\nListView {\n"
"id: list\n"
"model: 5\n"
"delegate: Text {\n"
"objectName: \"acc_text\"\n"
"Accessible.role: Accessible.Button\n"
"Accessible.name: \"TextButton\"\n"
"Accessible.description: \"Text Button\"\n"
"}\n"
"}", QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
QQuickListView *listview = qobject_cast<QQuickListView *>(object.get());
QVERIFY(listview != nullptr);
QQuickItem *contentItem = listview->contentItem();
QQuickText *childItem = QQuickVisualTestUtils::findItem<QQuickText>(contentItem, "acc_text");
QVERIFY(childItem != nullptr);
QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(childItem);
QVERIFY(attachedObject);
if (attachedObject) {
QVariant p = attachedObject->property("role");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toInt(), int(QAccessible::PushButton));
p = attachedObject->property("name");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toString(), QLatin1String("TextButton"));
p = attachedObject->property("description");
QCOMPARE(p.isNull(), false);
QCOMPARE(p.toString(), QLatin1String("Text Button"));
}
}
// Check that a name can be implicitly set.
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(R"(
import QtQuick 2.0
Text {
Accessible.role: Accessible.Button
Accessible.description: "Text Button"
})", QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object);
const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>(
QQuickAccessibleAttached::attachedProperties(object.get()));
QVERIFY(attachedObject);
QVERIFY(!attachedObject->wasNameExplicitlySet());
attachedObject->setNameImplicitly(QLatin1String("Implicit"));
QCOMPARE(attachedObject->name(), QLatin1String("Implicit"));
QVERIFY(!attachedObject->wasNameExplicitlySet());
attachedObject->setName(QLatin1String("Explicit"));
QCOMPARE(attachedObject->name(), QLatin1String("Explicit"));
QVERIFY(attachedObject->wasNameExplicitlySet());
}
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::quickAttachedProperties_description_data()
{
QTest::addColumn<QString>("qmlCode");
QTest::addColumn<QString>("initialExplicitDescription");
QTest::addColumn<QString>("implicitDescription");
QTest::addColumn<QString>("implicitDescription2");
QTest::addColumn<QString>("explicitDescription2");
QTest::addColumn<QString>("implicitDescription3");
QTest::addColumn<QAccessible::Role>("expectedRole");
QTest::addColumn<QString>("expectedDescription");
QTest::newRow("TextArea with initial explicit description") << R"(
import QtQuick
import QtQuick.Controls
TextArea {
Accessible.role: Accessible.EditableText
%1
})"
<< "Explicit description"
<< "Initial implicit" // implicitDescription
<< "Updated implicit" // implicitDescription2
<< "Explicit" // explicitDescription2
<< "Updated implicit again" // implicitDescription3
// EXPECTED
<< QAccessible::EditableText
<< "Explicit description";
QTest::newRow("TextArea, no initial explicit description") << R"(
import QtQuick
import QtQuick.Controls
TextArea {
Accessible.role: Accessible.EditableText
%1
})"
<< QString()
<< "Initial implicit" // implicitDescription
<< "Updated implicit" // implicitDescription2
<< "Explicit" // explicitDescription2
<< "Updated implicit again" // implicitDescription3
// EXPECTED
<< QAccessible::EditableText
<< "Explicit description";
}
void tst_QQuickAccessible::quickAttachedProperties_description()
{
QFETCH(QString, qmlCode);
QFETCH(QString, initialExplicitDescription);
QFETCH(QString, implicitDescription);
QFETCH(QString, implicitDescription2);
QFETCH(QString, explicitDescription2);
QFETCH(QString, implicitDescription3);
QFETCH(QAccessible::Role, expectedRole);
QFETCH(QString, expectedDescription);
QQmlEngine engine;
QQmlComponent component(&engine);
QString extraCode;
if (!initialExplicitDescription.isNull())
extraCode.append(QString::fromLatin1(R"(Accessible.description: "%1")").arg(initialExplicitDescription));
extraCode.append(QString::fromLatin1("\nplaceholderText: \"%2\"").arg(implicitDescription));
component.setData(qmlCode.arg(extraCode).toUtf8(), QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object);
const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>(
QQuickAccessibleAttached::attachedProperties(object.get()));
QVERIFY(attachedObject);
QCOMPARE(attachedObject->role(), expectedRole);
const QString &explicitDesc = initialExplicitDescription;
QCOMPARE(attachedObject->description(), explicitDesc.isNull() ? implicitDescription : explicitDesc);
object->setProperty("placeholderText", implicitDescription2);
QCOMPARE(attachedObject->description(), explicitDesc.isNull() ? implicitDescription2 : explicitDesc);
attachedObject->setDescription(explicitDescription2);
QCOMPARE(attachedObject->description(), explicitDescription2);
object->setProperty("placeholderText", implicitDescription2);
QCOMPARE(attachedObject->description(), explicitDescription2);
QTestAccessibility::clearEvents();
}
// Verify that a role can be explicitly set, and that the values from the
// attached object are used even if the item has a default role - QTBUG-110114
void tst_QQuickAccessible::attachedWins()
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(R"(
import QtQuick
import QtQuick.Controls
Button {
text: "Button"
objectName: "button"
Accessible.role: Accessible.RadioButton
Accessible.description: "Radio Button"
})", QUrl());
auto button = std::unique_ptr<QObject>(component.create());
QVERIFY(button);
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(button.get());
QVERIFY(iface);
QCOMPARE(iface->role(), QAccessible::RadioButton);
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::basicPropertiesTest()
{
QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp);
QCOMPARE(app->childCount(), 0);
auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("text.qml"));
window->show();
QCOMPARE(app->childCount(), 1);
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get());
QVERIFY(iface);
QCOMPARE(iface->childCount(), 1);
QAccessibleInterface *item = iface->child(0);
QVERIFY(item);
QCOMPARE(item->childCount(), 7);
QCOMPARE(item->rect().size(), QSize(400, 400));
QCOMPARE(item->role(), QAccessible::Client);
QCOMPARE(iface->indexOfChild(item), 0);
QAccessibleInterface *text = item->child(0);
QVERIFY(text);
QCOMPARE(text->childCount(), 0);
QCOMPARE(text->text(QAccessible::Name), QLatin1String("Hello Accessibility"));
QCOMPARE(text->rect().size(), QSize(200, 50));
QCOMPARE(text->rect().x(), item->rect().x() + 100);
QCOMPARE(text->rect().y(), item->rect().y() + 20);
QCOMPARE(text->role(), QAccessible::StaticText);
QCOMPARE(item->indexOfChild(text), 0);
QAccessibleInterface *text2 = item->child(1);
QVERIFY(text2);
QCOMPARE(text2->childCount(), 0);
QCOMPARE(text2->text(QAccessible::Name), QLatin1String("The Hello 2 accessible text"));
QCOMPARE(text2->rect().size(), QSize(100, 40));
QCOMPARE(text2->rect().x(), item->rect().x() + 100);
QCOMPARE(text2->rect().y(), item->rect().y() + 40);
QCOMPARE(text2->role(), QAccessible::StaticText);
QCOMPARE(item->indexOfChild(text2), 1);
QVERIFY(!text2->state().editable);
QVERIFY(text2->state().readOnly);
QVERIFY(text2->state().focusable);
QCOMPARE(iface->indexOfChild(text2), -1);
QCOMPARE(text2->indexOfChild(item), -1);
// TextInput
QAccessibleInterface *textInput = item->child(2);
QVERIFY(textInput);
QCOMPARE(textInput->childCount(), 0);
QCOMPARE(textInput->role(), QAccessible::EditableText);
QVERIFY(textInput->state().editable);
QVERIFY(!textInput->state().readOnly);
QVERIFY(!textInput->state().multiLine);
QVERIFY(textInput->state().focusable);
QCOMPARE(textInput->text(QAccessible::Value), "A text input");
auto textInterface = textInput->textInterface();
QVERIFY(textInterface);
auto editableTextInterface = textInput->editableTextInterface();
QVERIFY(editableTextInterface);
auto newText = QString("a new text");
textInput->setText(QAccessible::Value, newText);
QCOMPARE(textInput->text(QAccessible::Value), newText);
QCOMPARE(textInterface->selectionCount(), 0);
textInterface->setSelection(0, 1, 4);
QCOMPARE(textInterface->selectionCount(), 1);
int selectionStartOffset = 0, selectionEndOffset = 0;
textInterface->selection(0, &selectionStartOffset, &selectionEndOffset);
QCOMPARE(selectionStartOffset, 1);
QCOMPARE(selectionEndOffset, 4);
textInterface->removeSelection(0);
QCOMPARE(textInterface->selectionCount(), 0);
// TextEdit
QAccessibleInterface *textEdit = item->child(3);
QVERIFY(textEdit);
QCOMPARE(textEdit->childCount(), 0);
QCOMPARE(textEdit->role(), QAccessible::EditableText);
QVERIFY(textEdit->state().editable);
QVERIFY(!textEdit->state().readOnly);
QVERIFY(textEdit->state().focusable);
QCOMPARE(textEdit->text(QAccessible::Value), "A multi-line text edit\nTesting Accessibility.");
auto textEditTextInterface = textEdit->textInterface();
QVERIFY(textEditTextInterface);
QCOMPARE(textEditTextInterface->selectionCount(), 0);
textEditTextInterface->setSelection(0, 1, 4);
QCOMPARE(textEditTextInterface->selectionCount(), 1);
int selectionStart = 0, selectionEnd = 0;
textEditTextInterface->selection(0, &selectionStart, &selectionEnd);
QCOMPARE(selectionStart, 1);
QCOMPARE(selectionEnd, 4);
textEditTextInterface->removeSelection(0);
QCOMPARE(textEditTextInterface->selectionCount(), 0);
auto textEditEditableTextInterface = textEdit->editableTextInterface();
QVERIFY(textEditEditableTextInterface);
textEdit->setText(QAccessible::Value, newText);
QCOMPARE(textEdit->text(QAccessible::Value), newText);
QEXPECT_FAIL("", "multi line is not implemented", Continue);
QVERIFY(textInput->state().multiLine);
// Text "Hello 3"
QAccessibleInterface *text3 = item->child(4);
QVERIFY(text3);
QCOMPARE(text3->childCount(), 0);
QCOMPARE(text3->text(QAccessible::Name), QLatin1String("Hello 3"));
QCOMPARE(text3->role(), QAccessible::StaticText);
QCOMPARE(item->indexOfChild(text3), 4);
QVERIFY(!text3->state().editable);
QVERIFY(!text3->state().readOnly);
// test implicit state values due to role change
QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(text3->object());
attached->setRole(QAccessible::StaticText);
QCOMPARE(text3->role(), QAccessible::StaticText);
QVERIFY(text3->state().readOnly);
// Text "Rich text"
QAccessibleInterface *richText = item->child(5);
QVERIFY(richText);
QCOMPARE(richText->childCount(), 2);
QCOMPARE(richText->text(QAccessible::Name), QLatin1String("Rich text with links:\nWebsite or blog"));
QCOMPARE(richText->role(), QAccessible::StaticText);
QCOMPARE(item->indexOfChild(richText), 5);
QVERIFY(!richText->state().editable);
QVERIFY(!richText->state().readOnly);
// Check for hyperlink child nodes
for (int i = 0; i < richText->childCount(); ++i) {
static const char *linkUrls[2][2] = {
{"Website", "https://qt.io"},
{"blog", "https://qt.io/blog"}
};
QAccessibleInterface *link1 = richText->child(i);
QVERIFY(link1);
QCOMPARE(link1->role(), QAccessible::Link);
QAccessibleHyperlinkInterface *link = link1->hyperlinkInterface();
QVERIFY(link);
QCOMPARE(link->anchor(), QLatin1String(linkUrls[i][0]));
QCOMPARE(link->anchorTarget(), QLatin1String(linkUrls[i][1]));
}
// TextField
QAccessibleInterface *textField = item->child(6);
QVERIFY(textField);
QCOMPARE(textField->childCount(), 0);
QCOMPARE(textField->text(QAccessible::Value), QLatin1String("text is TextField"));
QCOMPARE(textField->text(QAccessible::Description), QLatin1String("This is the TextField description"));
QCOMPARE(textField->role(), QAccessible::EditableText);
QCOMPARE(item->indexOfChild(textField), 6);
QVERIFY(textField->state().editable);
QVERIFY(!textField->state().readOnly);
// see if implicit changes back
attached->setRole(QAccessible::EditableText);
QEXPECT_FAIL("", "EditableText does not implicitly set readOnly to false", Continue);
QVERIFY(!text3->state().readOnly);
// explicitly set state
attached->set_readOnly(false);
attached->setRole(QAccessible::StaticText);
QVERIFY(!text3->state().readOnly);
window.reset();
QTestAccessibility::clearEvents();
}
QAccessibleInterface *topLevelChildAt(QAccessibleInterface *iface, int x, int y)
{
QAccessibleInterface *child = iface->childAt(x, y);
if (!child)
return nullptr;
QAccessibleInterface *childOfChild;
while ( ( childOfChild = child->childAt(x, y)) ) {
child = childOfChild;
}
return child;
}
void tst_QQuickAccessible::hitTest()
{
auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("hittest.qml"));
window->show();
QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(window.get());
QVERIFY(windowIface);
QAccessibleInterface *rootItem = windowIface->child(0);
// on Android the main window is always shown fullscreen
QRect rootRect = QRect(window->x(), window->y(), window->width(), window->height());
// check the root item from app
QAccessibleInterface *appIface = QAccessible::queryAccessibleInterface(qApp);
QVERIFY(appIface);
QAccessibleInterface *itemHit = appIface->childAt(rootRect.x() + 200, rootRect.y() + 50);
QVERIFY(itemHit);
QCOMPARE(itemHit->rect(), rootRect);
QAccessibleInterface *rootItemIface;
for (int c = 0; c < rootItem->childCount(); ++c) {
QAccessibleInterface *iface = rootItem->child(c);
QString name = iface->text(QAccessible::Name);
if (name == QLatin1String("rect1")) {
// hit rect1
QAccessibleInterface *rect1 = iface;
QRect rect1Rect = rect1->rect();
QAccessibleInterface *rootItemIface = rootItem->childAt(rect1Rect.x() + 10, rect1Rect.y() + 10);
QVERIFY(rootItemIface);
QCOMPARE(rect1Rect, rootItemIface->rect());
QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect1"));
// should also work from top level (app)
QAccessibleInterface *app(QAccessible::queryAccessibleInterface(qApp));
QAccessibleInterface *itemHit2(topLevelChildAt(app, rect1Rect.x() + 10, rect1Rect.y() + 10));
QVERIFY(itemHit2);
QCOMPARE(itemHit2->rect(), rect1Rect);
QCOMPARE(itemHit2->text(QAccessible::Name), QLatin1String("rect1"));
} else if (name == QLatin1String("rect2")) {
QAccessibleInterface *rect2 = iface;
// FIXME: This is seems broken on OS X
// QCOMPARE(rect2->rect().translated(rootItem->rect().x(), rootItem->rect().y()), QRect(0, 50, 100, 100));
QAccessibleInterface *rect20 = rect2->child(0);
QVERIFY(rect20);
QCOMPARE(rect20->text(QAccessible::Name), QLatin1String("rect20"));
QPoint p = rect20->rect().bottomRight() + QPoint(20, 20);
QAccessibleInterface *rect201 = rect20->childAt(p.x(), p.y());
QVERIFY(rect201);
QCOMPARE(rect201->text(QAccessible::Name), QLatin1String("rect201"));
rootItemIface = topLevelChildAt(windowIface, p.x(), p.y());
QVERIFY(rootItemIface);
QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect201"));
}
}
window.reset();
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::checkableTest()
{
auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("checkbuttons.qml"));
window->show();
QQuickItem *contentItem = window->contentItem();
QVERIFY(contentItem);
QQuickItem *rootItem = contentItem->childItems().first();
QVERIFY(rootItem);
// the window becomes active
QAccessible::State activatedChange;
activatedChange.active = true;
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get());
QVERIFY(iface);
QAccessibleInterface *root = iface->child(0);
QAccessibleInterface *button1 = root->child(0);
QCOMPARE(button1->role(), QAccessible::Button);
QVERIFY(!(button1->state().checked));
QVERIFY(!(button1->state().checkable));
QVERIFY(button1->state().focusable);
QVERIFY(!button1->state().focused);
QTestAccessibility::clearEvents();
// set properties
QQuickItem *button1item = qobject_cast<QQuickItem*>(rootItem->childItems().at(0));
QVERIFY(button1item);
QCOMPARE(button1item->objectName(), QLatin1String("button1"));
button1item->forceActiveFocus();
QVERIFY(button1->state().focusable);
QVERIFY(button1->state().focused);
QAccessibleEvent focusEvent(button1item, QAccessible::Focus);
QVERIFY_EVENT(&focusEvent);
QAccessibleInterface *button2 = root->child(1);
QVERIFY(!(button2->state().checked));
QVERIFY(button2->state().checkable);
QQuickItem *button2item = qobject_cast<QQuickItem*>(rootItem->childItems().at(1));
QVERIFY(button2item);
QCOMPARE(button2item->objectName(), QLatin1String("button2"));
QAccessibleInterface *button3 = root->child(2);
QVERIFY(button3->state().checked);
QVERIFY(button3->state().checkable);
QAccessibleInterface *checkBox1 = root->child(3);
QCOMPARE(checkBox1->role(), QAccessible::CheckBox);
QVERIFY(checkBox1->state().checked);
QVERIFY(checkBox1->state().checkable);
QQuickItem *checkbox1item = qobject_cast<QQuickItem*>(rootItem->childItems().at(3));
QVERIFY(checkbox1item);
QCOMPARE(checkbox1item->objectName(), QLatin1String("checkbox1"));
checkbox1item->setProperty("checked", false);
QVERIFY(!checkBox1->state().checked);
QAccessible::State checkState;
checkState.checked = true;
QAccessibleStateChangeEvent checkChanged(checkbox1item, checkState);
QVERIFY_EVENT(&checkChanged);
checkbox1item->setProperty("checked", true);
QVERIFY(checkBox1->state().checked);
QVERIFY_EVENT(&checkChanged);
QAccessibleInterface *checkBox2 = root->child(4);
QVERIFY(!(checkBox2->state().checked));
QVERIFY(checkBox2->state().checkable);
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::expandableTest() {
auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("expandable.qml"));
window->show();
QQuickItem *contentItem = window->contentItem();
QVERIFY(contentItem);
QQuickItem *rootItem = contentItem->childItems().first();
QVERIFY(rootItem);
QAccessibleInterface *iface =
QAccessible::queryAccessibleInterface(window.get());
QVERIFY(iface);
QAccessibleInterface *root = iface->child(0);
QAccessibleInterface *button1 = root->child(0);
QCOMPARE(button1->role(), QAccessible::Button);
QVERIFY(!(button1->state().expanded));
QVERIFY(!(button1->state().expandable));
QAccessibleInterface *button2 = root->child(1);
QVERIFY(!(button2->state().expanded));
QVERIFY(button2->state().expandable);
QQuickItem *button2item =
qobject_cast<QQuickItem *>(rootItem->childItems().at(1));
QVERIFY(button2item);
QCOMPARE(button2item->objectName(), QLatin1String("button2"));
QAccessibleInterface *button3 = root->child(2);
QVERIFY(button3->state().expanded);
QVERIFY(button3->state().expandable);
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::ignoredTest()
{
auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("ignored.qml"));
window->show();
QQuickItem *contentItem = window->contentItem();
QVERIFY(contentItem);
QQuickItem *rootItem = contentItem->childItems().first();
QVERIFY(rootItem);
// the window becomes active
QAccessible::State activatedChange;
activatedChange.active = true;
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.get());
QVERIFY(iface);
QAccessibleInterface *rectangleA = iface->child(0);
QCOMPARE(rectangleA->role(), QAccessible::StaticText);
QCOMPARE(rectangleA->text(QAccessible::Name), QLatin1String("A"));
static const char *expected = "BEFIHD";
// check if node "C" and "G" is skipped and that the order is as expected.
for (int i = 0; i < rectangleA->childCount(); ++i) {
QAccessibleInterface *child = rectangleA->child(i);
QCOMPARE(child->text(QAccessible::Name), QString(QLatin1Char(expected[i])));
}
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::windowContainer()
{
QQuickView view;
view.setSource(testFileUrl("windowcontainer.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QQuickItem *container = view.rootObject()->findChild<QQuickItem *>("container");
QVERIFY(container);
QAccessibleInterface *containerIface = QAccessible::queryAccessibleInterface(container);
QVERIFY(containerIface);
// The container is a node in its own right, and is not skipped when the
// scene collects the accessible children of the non-accessible root item
QAccessibleInterface *viewIface = QAccessible::queryAccessibleInterface(&view);
QVERIFY(viewIface);
QCOMPARE(viewIface->childCount(), 2);
QCOMPARE(viewIface->child(0), containerIface);
// And it reflects the accessibility hierarchy of the window it hosts
QWindow *hostedWindow = container->property("window").value<QWindow *>();
QVERIFY(hostedWindow);
QCOMPARE(containerIface->childCount(), 1);
QAccessibleInterface *hostedIface = containerIface->child(0);
QVERIFY(hostedIface);
QCOMPARE(hostedIface, hostedWindow->accessibleRoot());
QCOMPARE(containerIface->indexOfChild(hostedIface), 0);
QAccessibleInterface *hostedChild = hostedIface->child(0);
QVERIFY(hostedChild);
QCOMPARE(hostedChild->role(), QAccessible::Button);
QCOMPARE(hostedChild->text(QAccessible::Name), QLatin1String("hosted"));
// A container without a window has nothing to reflect
QQuickItem *emptyContainer = view.rootObject()->findChild<QQuickItem *>("emptyContainer");
QVERIFY(emptyContainer);
QAccessibleInterface *emptyIface = QAccessible::queryAccessibleInterface(emptyContainer);
QVERIFY(emptyIface);
QCOMPARE(emptyIface->childCount(), 0);
QCOMPARE(emptyIface->child(0), nullptr);
QCOMPARE(emptyIface->indexOfChild(containerIface), -1);
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::passwordTest()
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick\nTextInput {\n"
"Accessible.role: Accessible.EditableText\n"
"Accessible.name: \"Password\"\n"
"Accessible.passwordEdit: true\n"
"echoMode: TextInput.Password\n"
"text: \"Green\"\n"
"}", QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(object.get());
QVERIFY(textInput != nullptr);
const auto passwordCharacter = textInput->passwordCharacter();
const auto passwordLength = textInput->text().length();
const auto password = passwordCharacter.repeated(passwordLength);
QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(object.get());
QVERIFY(iface);
QCOMPARE(iface->text(QAccessible::Value), password);
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::announceTest()
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData("import QtQuick\nItem {\n"
"Component.onCompleted: Accessible.announce('I am complete!')"
"}",
QUrl());
auto object = std::unique_ptr<QObject>(component.create());
QVERIFY(object != nullptr);
QAccessibleEvent createdEvent(object.get(), QAccessible::ObjectCreated);
QVERIFY_EVENT(&createdEvent);
QAccessibleAnnouncementEvent event(object.get(), QStringLiteral("I am complete!"));
QVERIFY_EVENT(&event);
QTestAccessibility::clearEvents();
}
void tst_QQuickAccessible::eventTest()
{
auto clearEvents = qScopeGuard([]{ QTestAccessibility::clearEvents(); });
std::unique_ptr<QQuickView> window(new QQuickView());
window->setSource(testFileUrl("eventTest.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.get()));
QQuickItem *contentItem = window->contentItem();
QVERIFY(contentItem);
QQuickItem *rootItem = contentItem->childItems().first();
QVERIFY(rootItem);
// move an item that is not accessible
QQuickItem *textItem = rootItem->findChild<QQuickItem*>(QLatin1String("text"));
QTestAccessibility::clearEvents();
textItem->setX(textItem->x() + 2);
QCOMPARE(QTestAccessibility::events().size(), 0);
// move an item that is accessible
QQuickItem *buttonItem = rootItem->findChild<QQuickItem*>(QLatin1String("button"));
auto buttonIface = QAccessible::queryAccessibleInterface(buttonItem);
QVERIFY(buttonIface);
QTestAccessibility::clearEvents();
buttonItem->setX(buttonItem->x() + 2);
QCOMPARE(QTestAccessibility::events().size(), 1);
QAccessibleEvent ev(buttonItem, QAccessible::LocationChanged);
QTestAccessibility::verifyEvent(&ev);
auto windowIface = QAccessible::queryAccessibleInterface(window.get());
QAccessibleObjectDestroyedEvent ev2(buttonIface);
QAccessibleObjectDestroyedEvent ev3(windowIface);
window.reset();
QTestAccessibility::verifyEvent(&ev2);
QTestAccessibility::verifyEvent(&ev3);
}
void tst_QQuickAccessible::relations_data()
{
QTest::addColumn<QString>("item");
QTest::addColumn<QString>("other");
QTest::addColumn<QAccessible::Relation>("relation");
QTest::addRow("label labels textInput")
<< "label" << "textInput"
<< QAccessible::Relation(QAccessible::Labelled);
QTest::addRow("textInput labelled by label")
<< "textInput" << "label"
<< QAccessible::Relation(QAccessible::Label);
}
void tst_QQuickAccessible::relations()
{
auto clearEvents = qScopeGuard([]{ QTestAccessibility::clearEvents(); });
QFETCH(const QString, item);
QFETCH(const QString, other);
QFETCH(const QAccessible::Relation, relation);
auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("relations.qml"));
window->show();
QObject *itemObject = window->findChild<QQuickItem *>(item);
QVERIFY(itemObject);
QAccessibleInterface *itemIface = QAccessible::queryAccessibleInterface(itemObject);
QVERIFY(itemIface);
QObject *otherObject = window->findChild<QQuickItem *>(other);
QVERIFY(otherObject);
QAccessibleInterface *otherIface = QAccessible::queryAccessibleInterface(otherObject);
QVERIFY(otherIface);
const QList<std::pair<QAccessibleInterface *, QAccessible::Relation>> expected{
{otherIface, relation}
};
const auto itemRelations = itemIface->relations();
QCOMPARE(itemRelations, expected);
const auto otherRelations = otherIface->relations();
QVERIFY(!otherRelations.isEmpty());
}
void tst_QQuickAccessible::controlsThatShouldSendObjectShow_data()
{
QTest::addColumn<QByteArray>("qmlSnippet");
QTest::newRow("BusyIndicator") << QByteArray("BusyIndicator { running: true }");
QTest::newRow("Button") << QByteArray("Button { text: 'Button' }");
QTest::newRow("ComboBox") << QByteArray("ComboBox { model: 3 }");
QTest::newRow("Dial") << QByteArray("Dial { value: 0.5 }");
// Label without a text is a bit unusual,
// but the background can be an image with meaningful info...
QTest::newRow("Label") << QByteArray("Label {\nbackground: Rectangle {\ncolor: 'red'\n}\nwidth: 50\nheight: 20}");
QTest::newRow("ProgressBar") << QByteArray("ProgressBar { value: 0.5 }");
QTest::newRow("RangeSlider") << QByteArray("RangeSlider { from: 1; to: 100; second.value: 50 }");
QTest::newRow("RoundButton") << QByteArray("RoundButton { text: 'Yes, please' }");
QTest::newRow("Slider") << QByteArray("Slider { value: 0.5 }");
QTest::newRow("Switch") << QByteArray("Switch { text: 'Switch me' }");
QTest::newRow("TextArea") << QByteArray("TextArea { width: 50}");
QTest::newRow("TextField") << QByteArray("TextField { width: 50}");
QTest::newRow("CheckBox") << QByteArray("CheckBox { text: 'checkBox' }");
QTest::newRow("DelayButton") << QByteArray("DelayButton { text: 'Are you really sure?' }");
QTest::newRow("RadioButton") << QByteArray("RadioButton { text: 'RadioButton' }");
QTest::newRow("TabButton") << QByteArray("TabButton { text: 'Home' }");
}
void tst_QQuickAccessible::controlsThatShouldSendObjectShow()
{
QFETCH(QByteArray, qmlSnippet);
auto clearEvents = qScopeGuard([]{ QTestAccessibility::clearEvents(); });
QQmlApplicationEngine engine;
engine.loadData(QByteArray(R"(import QtQuick
import QtQuick.Controls
Window { visible: true
)") + qmlSnippet + "}",
QUrl());
QVERIFY(engine.rootObjects().count() > 0);
QQuickWindow *window = qobject_cast<QQuickWindow*>(engine.rootObjects().first());
QQuickItem *contentItem = window->contentItem();
QVERIFY(contentItem);
QQuickItem *rootItem = contentItem->childItems().first();
QVERIFY(rootItem);
QAccessibleEvent ev(rootItem, QAccessible::ObjectShow);
// Don't use QVERIFY_EVENT, because it gets very noisy when additional events are found
QVERIFY(QTestAccessibility::containsEvent(&ev));
}
void tst_QQuickAccessible::editableTextInterface_data()
{
QTest::addColumn<QString>("objectName");
QTest::addColumn<bool>("editable");
QTest::addColumn<bool>("readOnly");
QTest::newRow("Label") << "label" << false << true;
QTest::newRow("TextInput") << "textInput" << true << false;
QTest::newRow("TextInput_readOnly") << "textInput" << true << true;
QTest::newRow("TextField") << "textField" << true << false;
QTest::newRow("TextField_readOnly") << "textField" << true << true;
QTest::newRow("TextEdit") << "textEdit" << true << false;
QTest::newRow("TextEdit_readOnly") << "textEdit" << true << true;
QTest::newRow("TextArea") << "textArea" << true << false;
QTest::newRow("TextArea_readOnly") << "textArea" << true << true;
}
void tst_QQuickAccessible::editableTextInterface()
{
QFETCH(QString, objectName);
QFETCH(bool, editable);
QFETCH(bool, readOnly);
auto clearEvents = qScopeGuard([]{ QTestAccessibility::clearEvents(); });
auto window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("textInterfaces.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowActive(window.get()));
QQuickItem *item = window->findChild<QQuickItem *>(objectName);
QVERIFY(item);
QAccessibleInterface *itemIface = QAccessible::queryAccessibleInterface(item);
QVERIFY(itemIface);
QAccessibleQuickItem *accessibleQuickItem = static_cast<QAccessibleQuickItem *>(itemIface);
QVERIFY(accessibleQuickItem);
QAccessibleTextInterface *textIface = itemIface->textInterface();
QVERIFY(textIface);
// The read-only text items does not have editable text interface.
// But they could be editable, for example a read-only TextEdit.
if (editable)
QVERIFY2(item->setProperty("readOnly", readOnly), "Failed to set readOnly property");
QAccessibleEditableTextInterface *editableIface = itemIface->editableTextInterface();
if (!editable || readOnly) {
QVERIFY(editableIface == nullptr);
} else {
QVERIFY(editableIface != nullptr);
itemIface->setText(QAccessible::Value, QString("This is a text"));
QCOMPARE(accessibleQuickItem->text(QAccessible::Value), QString("This is a text"));
editableIface->replaceText(10, 14, "test");
QCOMPARE(accessibleQuickItem->text(QAccessible::Value), QString("This is a test"));
editableIface->insertText(9, " new");
QCOMPARE(accessibleQuickItem->text(QAccessible::Value), QString("This is a new test"));
editableIface->deleteText(9, 13);
QCOMPARE(accessibleQuickItem->text(QAccessible::Value), QString("This is a test"));
}
}
void tst_QQuickAccessible::labelForNullptrTest()
{
// Verify that passing nullptr to setLabelFor/setLabelledBy emits a warning
// and does not crash (QTBUG-146127)
auto clearEvents = qScopeGuard([]{ QTestAccessibility::clearEvents(); });
QQuickItem parent;
auto *attached = new QQuickAccessibleAttached(&parent);
QTestAccessibility::clearEvents();
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("labelFor cannot be null"));
attached->setLabelFor(nullptr);
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("labelledBy cannot be null"));
attached->setLabelledBy(nullptr);
}
void tst_QQuickAccessible::flickableViewportTest_data()
{
QTest::addColumn<QSizeF>("size");
QTest::addColumn<QSizeF>("contentSize");
QTest::addColumn<QPointF>("content");
QTest::addColumn<QPointF>("position");
QTest::addColumn<QSizeF>("viewportSize");
QTest::addRow("Fully visible") << QSizeF(100, 200) << QSizeF(100, 200) << QPointF(0, 0)
<< QPointF(0, 0) << QSizeF(1, 1);
QTest::addRow("Quarter visible") << QSizeF(50, 100) << QSizeF(100, 200) << QPointF(0, 0)
<< QPointF(0, 0) << QSizeF(0.5, 0.5);
QTest::addRow("Quarter visible - centered")
<< QSizeF(50, 100) << QSizeF(100, 200) << QPointF(25, 50) << QPointF(0.25, 0.25)
<< QSizeF(0.5, 0.5);
QTest::addRow("Quarter visible - at end")
<< QSizeF(50, 100) << QSizeF(100, 200) << QPointF(50, 100) << QPointF(0.5, 0.5)
<< QSizeF(0.5, 0.5);
QTest::addRow("Zero-size content")
<< QSizeF(50, 100) << QSizeF(0, 0) << QPointF(0, 0) << QPointF(0, 0) << QSizeF(1, 1);
QTest::addRow("Content smaller than Flickable")
<< QSizeF(100, 200) << QSizeF(50, 100) << QPointF(0, 0) << QPointF(0, 0)
<< QSizeF(1, 1);
}
void tst_QQuickAccessible::flickableViewportTest()
{
auto clearEvents = qScopeGuard([] { QTestAccessibility::clearEvents(); });
QFETCH(QSizeF, size);
QFETCH(QSizeF, contentSize);
QFETCH(QPointF, content);
QFETCH(QPointF, position);
QFETCH(QSizeF, viewportSize);
QQuickFlickable flickable;
flickable.setWidth(size.width());
flickable.setHeight(size.height());
flickable.setContentWidth(contentSize.width());
flickable.setContentHeight(contentSize.height());
flickable.setContentX(content.x());
flickable.setContentY(content.y());
QAccessibleQuickFlickable a11yFlickable(&flickable);
QCOMPARE(a11yFlickable.contentSize(), contentSize);
QCOMPARE(a11yFlickable.position(), position);
QCOMPARE(a11yFlickable.viewportSize(), viewportSize);
QCOMPARE(a11yFlickable.isIndexed(), false);
}
void tst_QQuickAccessible::listViewViewportTest_data()
{
QTest::addColumn<int>("rowCount");
QTest::addColumn<QQuickListView::Orientation>("orientation");
QTest::addColumn<int>("index");
QTest::addColumn<QSizeF>("contentSize");
QTest::addColumn<QPointF>("position");
QTest::addColumn<QSizeF>("viewportSize");
QTest::addRow("Vertical - empty")
<< 0 << QQuickListView::Vertical << 0 << QSizeF(0, 0) << QPointF(0, 0) << QSizeF(1, 1);
QTest::addRow("Vertical - index 0") << 100 << QQuickListView::Vertical << 0 << QSizeF(1, 100)
<< QPointF(0, 0) << QSizeF(1, 0.3);
QTest::addRow("Vertical - index 10") << 100 << QQuickListView::Vertical << 10 << QSizeF(1, 100)
<< QPointF(0, 0.1) << QSizeF(1, 0.3);
QTest::addRow("Vertical - index 50") << 100 << QQuickListView::Vertical << 50 << QSizeF(1, 100)
<< QPointF(0, 0.5) << QSizeF(1, 0.3);
QTest::addRow("Vertical - index 70") << 100 << QQuickListView::Vertical << 70 << QSizeF(1, 100)
<< QPointF(0, 0.7) << QSizeF(1, 0.3);
// 30 delegates fit in the view's height
QTest::addRow("Vertical - index 90") << 100 << QQuickListView::Vertical << 90 << QSizeF(1, 100)
<< QPointF(0, 0.7) << QSizeF(1, 0.3);
QTest::addRow("Vertical - index 99") << 100 << QQuickListView::Vertical << 99 << QSizeF(1, 100)
<< QPointF(0, 0.7) << QSizeF(1, 0.3);
QTest::addRow("Horizontal - empty") << 0 << QQuickListView::Horizontal << 0 << QSizeF(0, 0)
<< QPointF(0, 0) << QSizeF(1, 1);
QTest::addRow("Horizontal - index 0") << 100 << QQuickListView::Horizontal << 0
<< QSizeF(100, 1) << QPointF(0, 0) << QSizeF(0.05, 1);
QTest::addRow("Horizontal - index 10") << 100 << QQuickListView::Horizontal << 10
<< QSizeF(100, 1) << QPointF(0.1, 0) << QSizeF(0.05, 1);
QTest::addRow("Horizontal - index 50") << 100 << QQuickListView::Horizontal << 50
<< QSizeF(100, 1) << QPointF(0.5, 0) << QSizeF(0.05, 1);
QTest::addRow("Horizontal - index 70") << 100 << QQuickListView::Horizontal << 70
<< QSizeF(100, 1) << QPointF(0.7, 0) << QSizeF(0.05, 1);
// 5 delegates fit in the view's width
QTest::addRow("Horizontal - index 90") << 100 << QQuickListView::Horizontal << 90
<< QSizeF(100, 1) << QPointF(0.9, 0) << QSizeF(0.05, 1);
QTest::addRow("Horizontal - index 99") << 100 << QQuickListView::Horizontal << 99
<< QSizeF(100, 1) << QPointF(0.95, 0) << QSizeF(0.05, 1);
}
void tst_QQuickAccessible::listViewViewportTest()
{
auto clearEvents = qScopeGuard([] { QTestAccessibility::clearEvents(); });
QFETCH(int, rowCount);
QFETCH(QQuickListView::Orientation, orientation);
QFETCH(int, index);
QFETCH(QSizeF, contentSize);
QFETCH(QPointF, position);
QFETCH(QSizeF, viewportSize);
QScopedPointer<QQuickView> window(createView());
window->rootContext()->setContextProperty("listModel", rowCount);
window->setSource(testFileUrl("listview.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickListView *listView = qobject_cast<QQuickListView *>(window->rootObject());
QVERIFY(listView);
auto *a11yIface = QAccessible::queryAccessibleInterface(listView);
auto *a11yListView = dynamic_cast<QAccessibleQuickListView *>(a11yIface);
QVERIFY(a11yListView);
listView->setOrientation(orientation);
listView->positionViewAtIndex(index, QQuickListView::Beginning);
QCOMPARE(a11yListView->contentSize(), contentSize);
QCOMPARE(a11yListView->position(), position);
QCOMPARE(a11yListView->viewportSize(), viewportSize);
QCOMPARE(a11yListView->isIndexed(), true);
}
void tst_QQuickAccessible::listViewSetPositionTest_data()
{
QTest::addColumn<int>("rowCount");
QTest::addColumn<QQuickListView::Orientation>("orientation");
QTest::addColumn<QPointF>("position");
QTest::addRow("Vertical - empty") << 0 << QQuickListView::Vertical << QPointF(0, 0);
QTest::addRow("Vertical - 10") << 100 << QQuickListView::Vertical << QPointF(0, 0.1);
QTest::addRow("Vertical - 50") << 100 << QQuickListView::Vertical << QPointF(0, 0.5);
QTest::addRow("Vertical - 70") << 100 << QQuickListView::Vertical << QPointF(0, 0.7);
// 30 delegates fit in the view's height
QTest::addRow("Vertical - 90") << 100 << QQuickListView::Vertical << QPointF(0, 0.7);
QTest::addRow("Vertical - 99") << 100 << QQuickListView::Vertical << QPointF(0, 0.7);
QTest::addRow("Horizontal - empty") << 0 << QQuickListView::Horizontal << QPointF(0, 0);
QTest::addRow("Horizontal - 10") << 100 << QQuickListView::Horizontal << QPointF(0.1, 0);
QTest::addRow("Horizontal - 50") << 100 << QQuickListView::Horizontal << QPointF(0.5, 0);
QTest::addRow("Horizontal - 70") << 100 << QQuickListView::Horizontal << QPointF(0.7, 0);
// 5 delegates fit in the view's height
QTest::addRow("Horizontal - 90") << 100 << QQuickListView::Horizontal << QPointF(0.95, 0);
QTest::addRow("Horizontal - 99") << 100 << QQuickListView::Horizontal << QPointF(0.95, 0);
}
void tst_QQuickAccessible::listViewSetPositionTest()
{
auto clearEvents = qScopeGuard([] { QTestAccessibility::clearEvents(); });
QFETCH(int, rowCount);
QFETCH(QQuickListView::Orientation, orientation);
QFETCH(QPointF, position);
QScopedPointer<QQuickView> window(createView());
window->rootContext()->setContextProperty("listModel", rowCount);
window->setSource(testFileUrl("listview.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QQuickListView *listView = qobject_cast<QQuickListView *>(window->rootObject());
QVERIFY(listView);
auto *a11yIface = QAccessible::queryAccessibleInterface(listView);
auto *a11yListView = dynamic_cast<QAccessibleQuickListView *>(a11yIface);
QVERIFY(a11yListView);
listView->setOrientation(orientation);
a11yListView->setPosition(position);
QCOMPARE(a11yListView->position(), position);
}
QTEST_MAIN(tst_QQuickAccessible)
#include "tst_qquickaccessible.moc"