1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | |
30 | #include <QtTest/QtTest> |
31 | #include <QtTest/qtestaccessible.h> |
32 | |
33 | #include <QtGui/qaccessible.h> |
34 | #include <QtGui/private/qguiapplication_p.h> |
35 | #include <qpa/qplatformnativeinterface.h> |
36 | #include <qpa/qplatformintegration.h> |
37 | #include <qpa/qplatformaccessibility.h> |
38 | |
39 | #include <QtQuick/qquickview.h> |
40 | #include <QtQuick/qquickitem.h> |
41 | |
42 | #include <QtQml/qqmlengine.h> |
43 | #include <QtQml/qqmlproperty.h> |
44 | #include <QtQuick/private/qquickaccessibleattached_p.h> |
45 | #include <QtQuick/private/qquicklistview_p.h> |
46 | #include <QtQuick/private/qquicktext_p.h> |
47 | |
48 | #include "../../shared/util.h" |
49 | #include "../shared/visualtestutil.h" |
50 | |
51 | #define EXPECT(cond) \ |
52 | do { \ |
53 | if (!errorAt && !(cond)) { \ |
54 | errorAt = __LINE__; \ |
55 | qWarning("level: %d, middle: %d, role: %d (%s)", treelevel, middle, iface->role(), #cond); \ |
56 | } \ |
57 | } while (0) |
58 | |
59 | |
60 | //TESTED_FILES= |
61 | |
62 | class tst_QQuickAccessible : public QQmlDataTest |
63 | { |
64 | Q_OBJECT |
65 | public: |
66 | tst_QQuickAccessible(); |
67 | virtual ~tst_QQuickAccessible(); |
68 | |
69 | public slots: |
70 | void initTestCase(); |
71 | void cleanupTestCase(); |
72 | void init(); |
73 | void cleanup(); |
74 | |
75 | private slots: |
76 | void commonTests_data(); |
77 | void commonTests(); |
78 | |
79 | void quickAttachedProperties(); |
80 | void basicPropertiesTest(); |
81 | void hitTest(); |
82 | void checkableTest(); |
83 | void ignoredTest(); |
84 | }; |
85 | |
86 | tst_QQuickAccessible::tst_QQuickAccessible() |
87 | { |
88 | |
89 | } |
90 | |
91 | tst_QQuickAccessible::~tst_QQuickAccessible() |
92 | { |
93 | |
94 | } |
95 | |
96 | void tst_QQuickAccessible::initTestCase() |
97 | { |
98 | QQmlDataTest::initTestCase(); |
99 | QTestAccessibility::initialize(); |
100 | QPlatformIntegration *pfIntegration = QGuiApplicationPrivate::platformIntegration(); |
101 | if (!pfIntegration->accessibility()) |
102 | QSKIP("This platform does not support accessibility" ); |
103 | pfIntegration->accessibility()->setActive(true); |
104 | } |
105 | |
106 | void tst_QQuickAccessible::cleanupTestCase() |
107 | { |
108 | QTestAccessibility::cleanup(); |
109 | } |
110 | |
111 | void tst_QQuickAccessible::init() |
112 | { |
113 | QTestAccessibility::clearEvents(); |
114 | } |
115 | |
116 | void tst_QQuickAccessible::cleanup() |
117 | { |
118 | const EventList list = QTestAccessibility::events(); |
119 | if (!list.isEmpty()) { |
120 | qWarning(msg: "%d accessibility event(s) were not handled in testfunction '%s':" , list.count(), |
121 | QString(QTest::currentTestFunction()).toLatin1().constData()); |
122 | for (int i = 0; i < list.count(); ++i) |
123 | qWarning(msg: " %d: Object: %p Event: '%s' Child: %d" , i + 1, list.at(i)->object(), |
124 | qAccessibleEventString(event: list.at(i)->type()), list.at(i)->child()); |
125 | } |
126 | QTestAccessibility::clearEvents(); |
127 | } |
128 | |
129 | void tst_QQuickAccessible::commonTests_data() |
130 | { |
131 | QTest::addColumn<QString>(name: "accessibleRoleFileName" ); |
132 | |
133 | QTest::newRow(dataTag: "Text" ) << "text.qml" ; |
134 | QTest::newRow(dataTag: "PushButton" ) << "pushbutton.qml" ; |
135 | } |
136 | |
137 | void tst_QQuickAccessible::commonTests() |
138 | { |
139 | QFETCH(QString, accessibleRoleFileName); |
140 | |
141 | qDebug() << "testing" << accessibleRoleFileName; |
142 | |
143 | QQuickView *view = new QQuickView(); |
144 | // view->setFixedSize(240,320); |
145 | view->setSource(testFileUrl(fileName: accessibleRoleFileName)); |
146 | view->show(); |
147 | // view->setFocus(); |
148 | QVERIFY(view->rootObject() != nullptr); |
149 | |
150 | QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(view); |
151 | QVERIFY(iface); |
152 | |
153 | delete view; |
154 | QTestAccessibility::clearEvents(); |
155 | } |
156 | |
157 | void tst_QQuickAccessible::quickAttachedProperties() |
158 | { |
159 | { |
160 | QQmlEngine engine; |
161 | QQmlComponent component(&engine); |
162 | component.setData("import QtQuick 2.0\nItem {\n" |
163 | "}" , baseUrl: QUrl()); |
164 | QObject *object = component.create(); |
165 | QVERIFY(object != nullptr); |
166 | |
167 | QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(obj: object); |
168 | QCOMPARE(attachedObject, static_cast<QObject*>(nullptr)); |
169 | delete object; |
170 | } |
171 | |
172 | // Attaching to non-item |
173 | { |
174 | QObject parent; |
175 | QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: QML QtObject: Accessible must be attached to an Item" ); |
176 | QQuickAccessibleAttached *attachedObj = new QQuickAccessibleAttached(&parent); |
177 | |
178 | QCOMPARE(attachedObj->ignored(), false); |
179 | attachedObj->setIgnored(true); |
180 | QCOMPARE(attachedObj->ignored(), false); |
181 | attachedObj->setIgnored(false); |
182 | QCOMPARE(attachedObj->ignored(), false); |
183 | } |
184 | |
185 | // Attached property |
186 | { |
187 | QQuickItem parent; |
188 | QQuickAccessibleAttached *attachedObj = new QQuickAccessibleAttached(&parent); |
189 | |
190 | attachedObj->name(); |
191 | |
192 | QVariant pp = attachedObj->property(name: "name" ); |
193 | QQmlEngine engine; |
194 | QQmlComponent component(&engine); |
195 | component.setData("import QtQuick 2.0\nItem {\n" |
196 | "Accessible.role: Accessible.Button\n" |
197 | "}" , baseUrl: QUrl()); |
198 | QObject *object = component.create(); |
199 | QVERIFY(object != nullptr); |
200 | |
201 | const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>( |
202 | object: QQuickAccessibleAttached::attachedProperties(obj: object)); |
203 | QVERIFY(attachedObject); |
204 | if (attachedObject) { |
205 | QVariant p = attachedObject->property(name: "role" ); |
206 | QCOMPARE(p.isNull(), false); |
207 | QCOMPARE(p.toInt(), int(QAccessible::PushButton)); |
208 | p = attachedObject->property(name: "name" ); |
209 | QCOMPARE(p.isNull(), true); |
210 | p = attachedObject->property(name: "description" ); |
211 | QCOMPARE(p.isNull(), true); |
212 | QCOMPARE(attachedObject->wasNameExplicitlySet(), false); |
213 | } |
214 | delete object; |
215 | } |
216 | |
217 | // Attached property |
218 | { |
219 | QQmlEngine engine; |
220 | QQmlComponent component(&engine); |
221 | component.setData("import QtQuick 2.0\nItem {\n" |
222 | "Accessible.role: Accessible.Button\n" |
223 | "Accessible.name: \"Donald\"\n" |
224 | "Accessible.description: \"Duck\"\n" |
225 | "}" , baseUrl: QUrl()); |
226 | QObject *object = component.create(); |
227 | QVERIFY(object != nullptr); |
228 | |
229 | const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>( |
230 | object: QQuickAccessibleAttached::attachedProperties(obj: object)); |
231 | QVERIFY(attachedObject); |
232 | if (attachedObject) { |
233 | QVariant p = attachedObject->property(name: "role" ); |
234 | QCOMPARE(p.isNull(), false); |
235 | QCOMPARE(p.toInt(), int(QAccessible::PushButton)); |
236 | p = attachedObject->property(name: "name" ); |
237 | QCOMPARE(p.isNull(), false); |
238 | QCOMPARE(p.toString(), QLatin1String("Donald" )); |
239 | p = attachedObject->property(name: "description" ); |
240 | QCOMPARE(p.isNull(), false); |
241 | QCOMPARE(p.toString(), QLatin1String("Duck" )); |
242 | QCOMPARE(attachedObject->wasNameExplicitlySet(), true); |
243 | } |
244 | delete object; |
245 | } |
246 | |
247 | // Check overriding of attached role for Text |
248 | { |
249 | QQmlEngine engine; |
250 | QQmlComponent component(&engine); |
251 | component.setData("import QtQuick 2.0\nText {\n" |
252 | "Accessible.role: Accessible.Button\n" |
253 | "Accessible.name: \"TextButton\"\n" |
254 | "Accessible.description: \"Text Button\"\n" |
255 | "}" , baseUrl: QUrl()); |
256 | QObject *object = component.create(); |
257 | QVERIFY(object != nullptr); |
258 | |
259 | QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(obj: object); |
260 | QVERIFY(attachedObject); |
261 | if (attachedObject) { |
262 | QVariant p = attachedObject->property(name: "role" ); |
263 | QCOMPARE(p.isNull(), false); |
264 | QCOMPARE(p.toInt(), int(QAccessible::PushButton)); |
265 | p = attachedObject->property(name: "name" ); |
266 | QCOMPARE(p.isNull(), false); |
267 | QCOMPARE(p.toString(), QLatin1String("TextButton" )); |
268 | p = attachedObject->property(name: "description" ); |
269 | QCOMPARE(p.isNull(), false); |
270 | QCOMPARE(p.toString(), QLatin1String("Text Button" )); |
271 | } |
272 | delete object; |
273 | } |
274 | // Check overriding of attached role for Text |
275 | { |
276 | QQmlEngine engine; |
277 | QQmlComponent component(&engine); |
278 | component.setData("import QtQuick 2.0\nListView {\n" |
279 | "id: list\n" |
280 | "model: 5\n" |
281 | "delegate: Text {\n" |
282 | "objectName: \"acc_text\"\n" |
283 | "Accessible.role: Accessible.Button\n" |
284 | "Accessible.name: \"TextButton\"\n" |
285 | "Accessible.description: \"Text Button\"\n" |
286 | "}\n" |
287 | "}" , baseUrl: QUrl()); |
288 | QObject *object = component.create(); |
289 | QVERIFY(object != nullptr); |
290 | |
291 | QQuickListView *listview = qobject_cast<QQuickListView *>(object); |
292 | QVERIFY(listview != nullptr); |
293 | QQuickItem *contentItem = listview->contentItem(); |
294 | QQuickText *childItem = QQuickVisualTestUtil::findItem<QQuickText>(parent: contentItem, objectName: "acc_text" ); |
295 | QVERIFY(childItem != nullptr); |
296 | |
297 | QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(obj: childItem); |
298 | QVERIFY(attachedObject); |
299 | if (attachedObject) { |
300 | QVariant p = attachedObject->property(name: "role" ); |
301 | QCOMPARE(p.isNull(), false); |
302 | QCOMPARE(p.toInt(), int(QAccessible::PushButton)); |
303 | p = attachedObject->property(name: "name" ); |
304 | QCOMPARE(p.isNull(), false); |
305 | QCOMPARE(p.toString(), QLatin1String("TextButton" )); |
306 | p = attachedObject->property(name: "description" ); |
307 | QCOMPARE(p.isNull(), false); |
308 | QCOMPARE(p.toString(), QLatin1String("Text Button" )); |
309 | } |
310 | delete object; |
311 | } |
312 | // Check that a name can be implicitly set. |
313 | { |
314 | QQmlEngine engine; |
315 | QQmlComponent component(&engine); |
316 | component.setData(R"( |
317 | import QtQuick 2.0 |
318 | Text { |
319 | Accessible.role: Accessible.Button |
320 | Accessible.description: "Text Button" |
321 | })" , baseUrl: QUrl()); |
322 | QScopedPointer<QObject> object(component.create()); |
323 | QVERIFY(object); |
324 | |
325 | const auto attachedObject = qobject_cast<QQuickAccessibleAttached*>( |
326 | object: QQuickAccessibleAttached::attachedProperties(obj: object.data())); |
327 | QVERIFY(attachedObject); |
328 | QVERIFY(!attachedObject->wasNameExplicitlySet()); |
329 | |
330 | attachedObject->setNameImplicitly(QLatin1String("Implicit" )); |
331 | QCOMPARE(attachedObject->name(), QLatin1String("Implicit" )); |
332 | QVERIFY(!attachedObject->wasNameExplicitlySet()); |
333 | |
334 | attachedObject->setName(QLatin1String("Explicit" )); |
335 | QCOMPARE(attachedObject->name(), QLatin1String("Explicit" )); |
336 | QVERIFY(attachedObject->wasNameExplicitlySet()); |
337 | } |
338 | QTestAccessibility::clearEvents(); |
339 | } |
340 | |
341 | |
342 | void tst_QQuickAccessible::basicPropertiesTest() |
343 | { |
344 | QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp); |
345 | QCOMPARE(app->childCount(), 0); |
346 | |
347 | QQuickView *window = new QQuickView(); |
348 | window->setSource(testFileUrl(fileName: "text.qml" )); |
349 | window->show(); |
350 | QCOMPARE(app->childCount(), 1); |
351 | |
352 | QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window); |
353 | QVERIFY(iface); |
354 | QCOMPARE(iface->childCount(), 1); |
355 | |
356 | QAccessibleInterface *item = iface->child(index: 0); |
357 | QVERIFY(item); |
358 | QCOMPARE(item->childCount(), 5); |
359 | QCOMPARE(item->rect().size(), QSize(400, 400)); |
360 | QCOMPARE(item->role(), QAccessible::Client); |
361 | QCOMPARE(iface->indexOfChild(item), 0); |
362 | |
363 | QAccessibleInterface *text = item->child(index: 0); |
364 | QVERIFY(text); |
365 | QCOMPARE(text->childCount(), 0); |
366 | |
367 | QCOMPARE(text->text(QAccessible::Name), QLatin1String("Hello Accessibility" )); |
368 | QCOMPARE(text->rect().size(), QSize(200, 50)); |
369 | QCOMPARE(text->rect().x(), item->rect().x() + 100); |
370 | QCOMPARE(text->rect().y(), item->rect().y() + 20); |
371 | QCOMPARE(text->role(), QAccessible::StaticText); |
372 | QCOMPARE(item->indexOfChild(text), 0); |
373 | |
374 | QAccessibleInterface *text2 = item->child(index: 1); |
375 | QVERIFY(text2); |
376 | QCOMPARE(text2->childCount(), 0); |
377 | |
378 | QCOMPARE(text2->text(QAccessible::Name), QLatin1String("The Hello 2 accessible text" )); |
379 | QCOMPARE(text2->rect().size(), QSize(100, 40)); |
380 | QCOMPARE(text2->rect().x(), item->rect().x() + 100); |
381 | QCOMPARE(text2->rect().y(), item->rect().y() + 40); |
382 | QCOMPARE(text2->role(), QAccessible::StaticText); |
383 | QCOMPARE(item->indexOfChild(text2), 1); |
384 | QCOMPARE(text2->state().editable, 0u); |
385 | QCOMPARE(text2->state().readOnly, 1); |
386 | QCOMPARE(text2->state().focusable, 1); |
387 | |
388 | QCOMPARE(iface->indexOfChild(text2), -1); |
389 | QCOMPARE(text2->indexOfChild(item), -1); |
390 | |
391 | // TextInput |
392 | QAccessibleInterface *textInput = item->child(index: 2); |
393 | QVERIFY(textInput); |
394 | QCOMPARE(textInput->childCount(), 0); |
395 | QCOMPARE(textInput->role(), QAccessible::EditableText); |
396 | QCOMPARE(textInput->state().editable, 1); |
397 | QCOMPARE(textInput->state().readOnly, 0); |
398 | QCOMPARE(textInput->state().multiLine, 0); |
399 | QCOMPARE(textInput->state().focusable, 1); |
400 | QCOMPARE(textInput->text(QAccessible::Value), "A text input" ); |
401 | auto textInterface = textInput->textInterface(); |
402 | QVERIFY(textInterface); |
403 | auto editableTextInterface = textInput->editableTextInterface(); |
404 | QEXPECT_FAIL("" , "EditableTextInterface is not implemented" , Continue); |
405 | QVERIFY(editableTextInterface); |
406 | auto newText = QString("a new text" ); |
407 | textInput->setText(t: QAccessible::Value, text: newText); |
408 | QCOMPARE(textInput->text(QAccessible::Value), newText); |
409 | |
410 | // TextEdit |
411 | QAccessibleInterface *textEdit = item->child(index: 3); |
412 | QVERIFY(textEdit); |
413 | QCOMPARE(textEdit->childCount(), 0); |
414 | QCOMPARE(textEdit->role(), QAccessible::EditableText); |
415 | QCOMPARE(textEdit->state().editable, 1); |
416 | QCOMPARE(textEdit->state().readOnly, 0); |
417 | QCOMPARE(textEdit->state().focusable, 1); |
418 | QCOMPARE(textEdit->text(QAccessible::Value), "A multi-line text edit\nTesting Accessibility." ); |
419 | auto textEditTextInterface = textEdit->textInterface(); |
420 | QVERIFY(textEditTextInterface); |
421 | auto textEditEditableTextInterface = textEdit->editableTextInterface(); |
422 | QEXPECT_FAIL("" , "EditableTextInterface is not implemented" , Continue); |
423 | QVERIFY(textEditEditableTextInterface); |
424 | textEdit->setText(t: QAccessible::Value, text: newText); |
425 | QCOMPARE(textEdit->text(QAccessible::Value), newText); |
426 | QEXPECT_FAIL("" , "multi line is not implemented" , Continue); |
427 | QCOMPARE(textInput->state().multiLine, 1); |
428 | |
429 | // Text "Hello 3" |
430 | QAccessibleInterface *text3 = item->child(index: 4); |
431 | QVERIFY(text3); |
432 | QCOMPARE(text3->childCount(), 0); |
433 | QCOMPARE(text3->text(QAccessible::Name), QLatin1String("Hello 3" )); |
434 | QCOMPARE(text3->role(), QAccessible::StaticText); |
435 | QCOMPARE(item->indexOfChild(text3), 4); |
436 | QCOMPARE(text3->state().editable, 0); |
437 | QCOMPARE(text3->state().readOnly, 0); |
438 | // test implicit state values due to role change |
439 | QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(obj: text3->object()); |
440 | attached->setRole(QAccessible::StaticText); |
441 | QCOMPARE(text3->role(), QAccessible::StaticText); |
442 | QCOMPARE(text3->state().readOnly, 1); |
443 | |
444 | // see if implicit changes back |
445 | attached->setRole(QAccessible::EditableText); |
446 | QEXPECT_FAIL("" , "EditableText does not implicitly set readOnly to false" , Continue); |
447 | QCOMPARE(text3->state().readOnly, 0); |
448 | // explicitly set state |
449 | attached->set_readOnly(false); |
450 | attached->setRole(QAccessible::StaticText); |
451 | QCOMPARE(text3->state().readOnly, 0); |
452 | |
453 | delete window; |
454 | QTestAccessibility::clearEvents(); |
455 | } |
456 | |
457 | QAccessibleInterface *topLevelChildAt(QAccessibleInterface *iface, int x, int y) |
458 | { |
459 | QAccessibleInterface *child = iface->childAt(x, y); |
460 | if (!child) |
461 | return nullptr; |
462 | |
463 | QAccessibleInterface *childOfChild; |
464 | while ( ( childOfChild = child->childAt(x, y)) ) { |
465 | child = childOfChild; |
466 | } |
467 | return child; |
468 | } |
469 | |
470 | void tst_QQuickAccessible::hitTest() |
471 | { |
472 | QQuickView *window = new QQuickView; |
473 | window->setSource(testFileUrl(fileName: "hittest.qml" )); |
474 | window->show(); |
475 | |
476 | QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(window); |
477 | QVERIFY(windowIface); |
478 | QAccessibleInterface *rootItem = windowIface->child(index: 0); |
479 | QRect rootRect = rootItem->rect(); |
480 | |
481 | // check the root item from app |
482 | QAccessibleInterface *appIface = QAccessible::queryAccessibleInterface(qApp); |
483 | QVERIFY(appIface); |
484 | QAccessibleInterface *itemHit = appIface->childAt(x: rootRect.x() + 200, y: rootRect.y() + 50); |
485 | QVERIFY(itemHit); |
486 | QCOMPARE(itemHit->rect(), rootRect); |
487 | |
488 | QAccessibleInterface *rootItemIface; |
489 | for (int c = 0; c < rootItem->childCount(); ++c) { |
490 | QAccessibleInterface *iface = rootItem->child(index: c); |
491 | QString name = iface->text(t: QAccessible::Name); |
492 | if (name == QLatin1String("rect1" )) { |
493 | // hit rect1 |
494 | QAccessibleInterface *rect1 = iface; |
495 | QRect rect1Rect = rect1->rect(); |
496 | QAccessibleInterface *rootItemIface = rootItem->childAt(x: rect1Rect.x() + 10, y: rect1Rect.y() + 10); |
497 | QVERIFY(rootItemIface); |
498 | QCOMPARE(rect1Rect, rootItemIface->rect()); |
499 | QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect1" )); |
500 | |
501 | // should also work from top level (app) |
502 | QAccessibleInterface *app(QAccessible::queryAccessibleInterface(qApp)); |
503 | QAccessibleInterface *itemHit2(topLevelChildAt(iface: app, x: rect1Rect.x() + 10, y: rect1Rect.y() + 10)); |
504 | QVERIFY(itemHit2); |
505 | QCOMPARE(itemHit2->rect(), rect1Rect); |
506 | QCOMPARE(itemHit2->text(QAccessible::Name), QLatin1String("rect1" )); |
507 | } else if (name == QLatin1String("rect2" )) { |
508 | QAccessibleInterface *rect2 = iface; |
509 | // FIXME: This is seems broken on OS X |
510 | // QCOMPARE(rect2->rect().translated(rootItem->rect().x(), rootItem->rect().y()), QRect(0, 50, 100, 100)); |
511 | QAccessibleInterface *rect20 = rect2->child(index: 0); |
512 | QVERIFY(rect20); |
513 | QCOMPARE(rect20->text(QAccessible::Name), QLatin1String("rect20" )); |
514 | QPoint p = rect20->rect().bottomRight() + QPoint(20, 20); |
515 | QAccessibleInterface *rect201 = rect20->childAt(x: p.x(), y: p.y()); |
516 | QVERIFY(rect201); |
517 | QCOMPARE(rect201->text(QAccessible::Name), QLatin1String("rect201" )); |
518 | rootItemIface = topLevelChildAt(iface: windowIface, x: p.x(), y: p.y()); |
519 | QVERIFY(rootItemIface); |
520 | QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect201" )); |
521 | |
522 | } |
523 | } |
524 | |
525 | delete window; |
526 | QTestAccessibility::clearEvents(); |
527 | } |
528 | |
529 | void tst_QQuickAccessible::checkableTest() |
530 | { |
531 | QScopedPointer<QQuickView> window(new QQuickView()); |
532 | window->setSource(testFileUrl(fileName: "checkbuttons.qml" )); |
533 | window->show(); |
534 | |
535 | QQuickItem *contentItem = window->contentItem(); |
536 | QVERIFY(contentItem); |
537 | QQuickItem *rootItem = contentItem->childItems().first(); |
538 | QVERIFY(rootItem); |
539 | |
540 | // the window becomes active |
541 | QAccessible::State activatedChange; |
542 | activatedChange.active = true; |
543 | |
544 | QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.data()); |
545 | QVERIFY(iface); |
546 | QAccessibleInterface *root = iface->child(index: 0); |
547 | |
548 | QAccessibleInterface *button1 = root->child(index: 0); |
549 | QCOMPARE(button1->role(), QAccessible::Button); |
550 | QVERIFY(!(button1->state().checked)); |
551 | QVERIFY(!(button1->state().checkable)); |
552 | |
553 | QVERIFY(button1->state().focusable); |
554 | QVERIFY(!button1->state().focused); |
555 | |
556 | QTestAccessibility::clearEvents(); |
557 | |
558 | // set properties |
559 | QQuickItem *button1item = qobject_cast<QQuickItem*>(object: rootItem->childItems().at(i: 0)); |
560 | QVERIFY(button1item); |
561 | QCOMPARE(button1item->objectName(), QLatin1String("button1" )); |
562 | button1item->forceActiveFocus(); |
563 | QVERIFY(button1->state().focusable); |
564 | QVERIFY(button1->state().focused); |
565 | |
566 | QAccessibleEvent focusEvent(button1item, QAccessible::Focus); |
567 | QVERIFY_EVENT(&focusEvent); |
568 | |
569 | QAccessibleInterface *button2 = root->child(index: 1); |
570 | QVERIFY(!(button2->state().checked)); |
571 | QVERIFY(button2->state().checkable); |
572 | QQuickItem *button2item = qobject_cast<QQuickItem*>(object: rootItem->childItems().at(i: 1)); |
573 | QVERIFY(button2item); |
574 | QCOMPARE(button2item->objectName(), QLatin1String("button2" )); |
575 | |
576 | QAccessibleInterface *button3 = root->child(index: 2); |
577 | QVERIFY(button3->state().checked); |
578 | QVERIFY(button3->state().checkable); |
579 | |
580 | QAccessibleInterface *checkBox1 = root->child(index: 3); |
581 | QCOMPARE(checkBox1->role(), QAccessible::CheckBox); |
582 | QVERIFY(checkBox1->state().checked); |
583 | QVERIFY(checkBox1->state().checkable); |
584 | QQuickItem *checkbox1item = qobject_cast<QQuickItem*>(object: rootItem->childItems().at(i: 3)); |
585 | QVERIFY(checkbox1item); |
586 | QCOMPARE(checkbox1item->objectName(), QLatin1String("checkbox1" )); |
587 | |
588 | checkbox1item->setProperty(name: "checked" , value: false); |
589 | QVERIFY(!checkBox1->state().checked); |
590 | QAccessible::State checkState; |
591 | checkState.checked = true; |
592 | QAccessibleStateChangeEvent checkChanged(checkbox1item, checkState); |
593 | QVERIFY_EVENT(&checkChanged); |
594 | |
595 | checkbox1item->setProperty(name: "checked" , value: true); |
596 | QVERIFY(checkBox1->state().checked); |
597 | QVERIFY_EVENT(&checkChanged); |
598 | |
599 | QAccessibleInterface *checkBox2 = root->child(index: 4); |
600 | QVERIFY(!(checkBox2->state().checked)); |
601 | QVERIFY(checkBox2->state().checkable); |
602 | |
603 | QTestAccessibility::clearEvents(); |
604 | } |
605 | |
606 | void tst_QQuickAccessible::ignoredTest() |
607 | { |
608 | QScopedPointer<QQuickView> window(new QQuickView()); |
609 | window->setSource(testFileUrl(fileName: "ignored.qml" )); |
610 | window->show(); |
611 | |
612 | QQuickItem *contentItem = window->contentItem(); |
613 | QVERIFY(contentItem); |
614 | QQuickItem *rootItem = contentItem->childItems().first(); |
615 | QVERIFY(rootItem); |
616 | |
617 | // the window becomes active |
618 | QAccessible::State activatedChange; |
619 | activatedChange.active = true; |
620 | |
621 | QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.data()); |
622 | QVERIFY(iface); |
623 | QAccessibleInterface *rectangleA = iface->child(index: 0); |
624 | |
625 | QCOMPARE(rectangleA->role(), QAccessible::StaticText); |
626 | QCOMPARE(rectangleA->text(QAccessible::Name), QLatin1String("A" )); |
627 | static const char *expected = "BEFIHD" ; |
628 | // check if node "C" and "G" is skipped and that the order is as expected. |
629 | for (int i = 0; i < rectangleA->childCount(); ++i) { |
630 | QAccessibleInterface *child = rectangleA->child(index: i); |
631 | QCOMPARE(child->text(QAccessible::Name), QString(QLatin1Char(expected[i]))); |
632 | } |
633 | QTestAccessibility::clearEvents(); |
634 | } |
635 | |
636 | QTEST_MAIN(tst_QQuickAccessible) |
637 | |
638 | #include "tst_qquickaccessible.moc" |
639 | |