/* Copyright (C) 2015 The Qt Company Ltd. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "../util.h" #include #include #include #include class tst_Accessibility : public QObject { Q_OBJECT public Q_SLOTS: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); private Q_SLOTS: void noPage(); void hierarchy(); void text(); void value(); void roles_data(); void roles(); }; // This will be called before the first test function is executed. // It is only called once. void tst_Accessibility::initTestCase() { } // This will be called after the last test function is executed. // It is only called once. void tst_Accessibility::cleanupTestCase() { } // This will be called before each test function is executed. void tst_Accessibility::init() { } // This will be called after every test function. void tst_Accessibility::cleanup() { } void tst_Accessibility::noPage() { QWebEngineView webView; webView.show(); QTest::qWait(1000); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); QVERIFY(view); QCOMPARE(view->role(), QAccessible::Client); QCOMPARE(view->childCount(), 1); QAccessibleInterface *document = view->child(0); QCOMPARE(document->role(), QAccessible::WebDocument); QCOMPARE(document->parent(), view); QCOMPARE(document->childCount(), 0); } void tst_Accessibility::hierarchy() { QWebEngineView webView; webView.setHtml("" \ "Hello world" \ "" \ ""); webView.show(); QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); QVERIFY(spyFinished.wait()); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); QVERIFY(view); QCOMPARE(view->role(), QAccessible::Client); QCOMPARE(view->childCount(), 1); // Wait for accessibility to be fully initialized QTRY_VERIFY(view->child(0)->childCount() == 1); QAccessibleInterface *document = view->child(0); QCOMPARE(document->role(), QAccessible::WebDocument); QCOMPARE(document->parent(), view); QCOMPARE(view->indexOfChild(document), 0); QCOMPARE(document->childCount(), 1); QAccessibleInterface *grouping = document->child(0); QVERIFY(grouping); QCOMPARE(grouping->parent(), document); QCOMPARE(document->indexOfChild(grouping), 0); QCOMPARE(grouping->childCount(), 2); QAccessibleInterface *text = grouping->child(0); QCOMPARE(text->role(), QAccessible::StaticText); QCOMPARE(text->parent(), grouping); QCOMPARE(grouping->indexOfChild(text), 0); QCOMPARE(text->childCount(), 0); QCOMPARE(text->text(QAccessible::Name), QStringLiteral("Hello world")); QCOMPARE(text->text(QAccessible::Description), QString()); QCOMPARE(text->text(QAccessible::Value), QString()); QAccessibleInterface *input = grouping->child(1); QCOMPARE(input->role(), QAccessible::EditableText); QCOMPARE(input->parent(), grouping); QCOMPARE(grouping->indexOfChild(input), 1); QCOMPARE(input->childCount(), 0); QCOMPARE(input->text(QAccessible::Name), QString()); QCOMPARE(input->text(QAccessible::Description), QString()); QCOMPARE(input->text(QAccessible::Value), QStringLiteral("some text")); QRect windowRect = webView.geometry(); QRect inputRect = input->rect(); QVERIFY(!inputRect.isEmpty()); QVERIFY(windowRect.contains(inputRect)); QPoint inputCenter = inputRect.center(); QAccessibleInterface *hitTest = view; QAccessibleInterface *child = nullptr; while (hitTest) { child = hitTest; hitTest = hitTest->childAt(inputCenter.x(), inputCenter.y()); } QCOMPARE(input, child); } void tst_Accessibility::text() { QWebEngineView webView; webView.setHtml("" \ "" \ "

Enter your name here:

" \ "" \ "

Provide both first and last name.

" \ "" \ ""); webView.show(); QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); QVERIFY(spyFinished.wait()); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); // Wait for accessibility to be fully initialized QTRY_VERIFY(view->child(0)->childCount() == 5); QAccessibleInterface *document = view->child(0); QVERIFY(document); // Good morning! [edit] QAccessibleInterface *grouping1 = document->child(0); QAccessibleInterface *input1 = grouping1; QCOMPARE(input1->role(), QAccessible::EditableText); QCOMPARE(input1->text(QAccessible::Name), QString()); QCOMPARE(input1->text(QAccessible::Description), QString()); QCOMPARE(input1->text(QAccessible::Value), QStringLiteral("Good morning!")); QAccessibleTextInterface *textInterface1 = input1->textInterface(); QVERIFY(textInterface1); QCOMPARE(textInterface1->characterCount(), 13); QCOMPARE(textInterface1->selectionCount(), 0); QCOMPARE(textInterface1->text(2, 9), QStringLiteral("od morn")); int start = -1; int end = -1; QCOMPARE(textInterface1->textAtOffset(8, QAccessible::WordBoundary, &start, &end), QStringLiteral("morning")); // Enter your name here: // my name [edit] // Provide both first and last name here. QAccessibleInterface *grouping2 = document->child(1); QAccessibleInterface *label1 = grouping2->child(0); QCOMPARE(label1->role(), QAccessible::StaticText); QCOMPARE(label1->text(QAccessible::Name), QStringLiteral("Enter your name here:")); QCOMPARE(label1->text(QAccessible::Description), QString()); QCOMPARE(label1->text(QAccessible::Value), QString()); QAccessibleInterface *grouping3 = document->child(2); QAccessibleInterface *input2 = grouping3; QCOMPARE(input2->role(), QAccessible::EditableText); QCOMPARE(input2->text(QAccessible::Name), QStringLiteral("Enter your name here:")); QCOMPARE(input2->text(QAccessible::Description), QStringLiteral("Provide both first and last name.")); QCOMPARE(input2->text(QAccessible::Value), QStringLiteral("my name")); QAccessibleInterface *grouping4 = document->child(3); QAccessibleInterface *label2 = grouping4->child(0); QCOMPARE(label2->role(), QAccessible::StaticText); QCOMPARE(label2->text(QAccessible::Name), QStringLiteral("Provide both first and last name.")); QCOMPARE(label2->text(QAccessible::Description), QString()); QCOMPARE(label2->text(QAccessible::Value), QString()); // Good day! [edit] QAccessibleInterface *grouping5 = document->child(4); QAccessibleInterface *input3 = grouping5; QCOMPARE(input3->role(), QAccessible::EditableText); QCOMPARE(input3->text(QAccessible::Name), QStringLiteral("day")); QCOMPARE(input3->text(QAccessible::Description), QString()); QCOMPARE(input3->text(QAccessible::Value), QStringLiteral("Good day!")); } void tst_Accessibility::value() { QWebEngineView webView; webView.setHtml("" \ "
" \ "
" \ ""); webView.show(); QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); QVERIFY(spyFinished.wait()); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); QTRY_COMPARE(view->child(0)->childCount(), 2); QAccessibleInterface *document = view->child(0); QCOMPARE(document->childCount(), 2); QAccessibleInterface *slider = document->child(0); QCOMPARE(slider->role(), QAccessible::Slider); QCOMPARE(slider->text(QAccessible::Name), QString()); QCOMPARE(slider->text(QAccessible::Description), QString()); QCOMPARE(slider->text(QAccessible::Value), QString()); QAccessibleValueInterface *valueInterface = slider->valueInterface(); QVERIFY(valueInterface); QCOMPARE(valueInterface->currentValue().toInt(), 4); QCOMPARE(valueInterface->minimumValue().toInt(), 1); QCOMPARE(valueInterface->maximumValue().toInt(), 10); QAccessibleInterface *progressBar = document->child(1); QCOMPARE(progressBar->role(), QAccessible::ProgressBar); QCOMPARE(progressBar->text(QAccessible::Name), QString()); QCOMPARE(progressBar->text(QAccessible::Description), QString()); QCOMPARE(progressBar->text(QAccessible::Value), QString()); QAccessibleValueInterface *progressBarValueInterface = progressBar->valueInterface(); QVERIFY(progressBarValueInterface); QCOMPARE(progressBarValueInterface->currentValue().toInt(), 77); QCOMPARE(progressBarValueInterface->minimumValue().toInt(), 22); QCOMPARE(progressBarValueInterface->maximumValue().toInt(), 99); } void tst_Accessibility::roles_data() { QTest::addColumn("html"); QTest::addColumn("isSection"); QTest::addColumn("role"); QTest::newRow("AX_ROLE_ABBR") << QString("a") << false << QAccessible::StaticText; QTest::newRow("AX_ROLE_ALERT") << QString("
alert
") << true << QAccessible::AlertMessage; QTest::newRow("AX_ROLE_ALERT_DIALOG") << QString("
alert
") << true << QAccessible::AlertMessage; //QTest::newRow("AX_ROLE_ANCHOR") << QString("target") << false << QAccessible::Link; // FIXME: The test case might be wrong (see https://codereview.chromium.org/2713193003) QTest::newRow("AX_ROLE_ANNOTATION") << QString("a") << false << QAccessible::StaticText; QTest::newRow("AX_ROLE_APPLICATION") << QString("
landmark
") << true << QAccessible::Document; QTest::newRow("AX_ROLE_ARTICLE") << QString("
a
") << true << QAccessible::Section; QTest::newRow("AX_ROLE_AUDIO") << QString("") << false << QAccessible::Sound; QTest::newRow("AX_ROLE_BANNER") << QString("
a
") << true << QAccessible::Section; QTest::newRow("AX_ROLE_BLOCKQUOTE") << QString("
a
") << true << QAccessible::Section; QTest::newRow("AX_ROLE_BUTTON") << QString("") << false << QAccessible::Button; //QTest::newRow("AX_ROLE_BUTTON_DROP_DOWN"); // TODO: Remove this during the next Chromium update: https://chromium-review.googlesource.com/842475 //QTest::newRow("AX_ROLE_CANVAS") << QString("") << true << QAccessible::Canvas; // FIXME: The test case might be wrong (see AXLayoutObject.cpp) QTest::newRow("AX_ROLE_CAPTION") << QString("
a
") << false << QAccessible::Heading; //QTest::newRow("AX_ROLE_CARET"); // Not a blink accessibility role //QTest::newRow("AX_ROLE_CELL") << QString("a") << true << QAccessible::Cell; // FIXME: Aria role 'cell' should work for QTest::newRow("AX_ROLE_CHECK_BOX") << QString("a") << false << QAccessible::CheckBox; QTest::newRow("AX_ROLE_CLIENT") << QString("") << true << QAccessible::Client; QTest::newRow("AX_ROLE_COLOR_WELL") << QString("a") << false << QAccessible::ColorChooser; //QTest::newRow("AX_ROLE_COLUMN") << QString("") << true << QAccessible::Column; // FIXME: The test case might be wrong (see AXTableColumn.h) QTest::newRow("AX_ROLE_COLUMN_HEADER") << QString("
a
") << true << QAccessible::ColumnHeader; QTest::newRow("AX_ROLE_COMBO_BOX_GROUPING") << QString("
") << true << QAccessible::ComboBox; QTest::newRow("AX_ROLE_COMBO_BOX_MENU_BUTTON") << QString("
Select
") << true << QAccessible::ComboBox; QTest::newRow("AX_ROLE_TEXT_FIELD_WITH_COMBO_BOX") << QString("") << false << QAccessible::ComboBox; QTest::newRow("AX_ROLE_COMPLEMENTARY") << QString("") << true << QAccessible::ComplementaryContent; QTest::newRow("AX_ROLE_CONTENT_INFO") << QString("
a
") << true << QAccessible::Section; QTest::newRow("AX_ROLE_DATE") << QString("") << false << QAccessible::Clock; QTest::newRow("AX_ROLE_DATE_TIME") << QString("") << false << QAccessible::Clock; QTest::newRow("AX_ROLE_DEFINITION") << QString("
landmark
") << true << QAccessible::Paragraph; QTest::newRow("AX_ROLE_DESCRIPTION_LIST") << QString("
a
") << true << QAccessible::List; QTest::newRow("AX_ROLE_DESCRIPTION_LIST_DETAIL") << QString("
a
") << true << QAccessible::Paragraph; QTest::newRow("AX_ROLE_DESCRIPTION_LIST_TERM") << QString("
a
") << true << QAccessible::ListItem; QTest::newRow("AX_ROLE_DETAILS") << QString("
a
") << true << QAccessible::Grouping; //QTest::newRow("AX_ROLE_DESKTOP"); // Not a blink accessibility role QTest::newRow("AX_ROLE_DIALOG") << QString("
") << true << QAccessible::Dialog; //QTest::newRow("AX_ROLE_DIRECTORY") << QString("
") << true << QAccessible::NoRole; // FIXME: Aria role 'directory' should work QTest::newRow("AX_ROLE_DISCLOSURE_TRIANGLE") << QString("
a
") << false << QAccessible::NoRole; QTest::newRow("AX_ROLE_GENERIC_CONTAINER") << QString("
a
") << true << QAccessible::Section; QTest::newRow("AX_ROLE_DOCUMENT") << QString("
a
") << true << QAccessible::Document; QTest::newRow("AX_ROLE_EMBEDDED_OBJECT") << QString("") << false << QAccessible::Grouping; QTest::newRow("AX_ROLE_FEED") << QString("
a
") << true << QAccessible::Section; QTest::newRow("AX_ROLE_FIGCAPTION") << QString("
a
") << true << QAccessible::Heading; QTest::newRow("AX_ROLE_FIGURE") << QString("
a
") << true << QAccessible::Section; QTest::newRow("AX_ROLE_FOOTER") << QString("
a
") << true << QAccessible::Footer; QTest::newRow("AX_ROLE_FORM") << QString("") << true << QAccessible::Form; QTest::newRow("AX_ROLE_GRID") << QString("
") << true << QAccessible::Table; QTest::newRow("AX_ROLE_GROUP") << QString("
") << true << QAccessible::Grouping; QTest::newRow("AX_ROLE_HEADING") << QString("

a

") << true << QAccessible::Heading; QTest::newRow("AX_ROLE_IFRAME") << QString("") << true << QAccessible::Section; QTest::newRow("AX_ROLE_IFRAME_PRESENTATIONAL") << QString("") << false << QAccessible::NoRole; //QTest::newRow("AX_ROLE_IGNORED") << QString("a") << true << QAccessible::NoRole; // FIXME: The HTML element should not be exposed as an element (see AXNodeObject.cpp) QTest::newRow("AX_ROLE_IMAGE") << QString("") << false << QAccessible::Graphic; //QTest::newRow("AX_ROLE_IMAGE_MAP") << QString("a") << true << QAccessible::Graphic; // FIXME: The test case might be wrong (see AXLayoutObject.cpp) QTest::newRow("AX_ROLE_INLINE_TEXT_BOX") << QString("") << false << QAccessible::EditableText; QTest::newRow("AX_ROLE_INPUT_TIME") << QString("") << false << QAccessible::SpinBox; QTest::newRow("AX_ROLE_LABEL_TEXT") << QString("") << false << QAccessible::StaticText; QTest::newRow("AX_ROLE_LEGEND") << QString("a") << true << QAccessible::StaticText; QTest::newRow("AX_ROLE_LINE_BREAK") << QString("
") << false << QAccessible::Separator; QTest::newRow("AX_ROLE_LINK") << QString("link") << false << QAccessible::Link; QTest::newRow("AX_ROLE_LIST") << QString("
    ") << true << QAccessible::List; QTest::newRow("AX_ROLE_LIST_BOX") << QString("") << false << QAccessible::ComboBox; QTest::newRow("AX_ROLE_LIST_BOX_OPTION") << QString("") << true << QAccessible::ListItem; QTest::newRow("AX_ROLE_LIST_ITEM") << QString("
  • a
  • ") << true << QAccessible::ListItem; QTest::newRow("AX_ROLE_LIST_MARKER") << QString("
    • ") << false << QAccessible::StaticText; //QTest::newRow("AX_ROLE_LOCATION_BAR"); // Not a blink accessibility role QTest::newRow("AX_ROLE_LOG") << QString("
      a
      ") << true << QAccessible::Section; QTest::newRow("AX_ROLE_MAIN") << QString("
      a
      ") << true << QAccessible::Grouping; QTest::newRow("AX_ROLE_MARK") << QString("a") << false << QAccessible::StaticText; QTest::newRow("AX_ROLE_MARQUEE") << QString("
      a
      ") << true << QAccessible::Section; QTest::newRow("AX_ROLE_MATH") << QString("a") << false << QAccessible::Equation; QTest::newRow("AX_ROLE_MENU") << QString("
      a
      ") << true << QAccessible::PopupMenu; QTest::newRow("AX_ROLE_MENU_BAR") << QString("
      a
      ") << true << QAccessible::MenuBar; QTest::newRow("AX_ROLE_MENU_ITEM") << QString("") << false << QAccessible::MenuItem; QTest::newRow("AX_ROLE_MENU_ITEM_CHECK_BOX") << QString("") << false << QAccessible::CheckBox; QTest::newRow("AX_ROLE_MENU_ITEM_RADIO") << QString("") << false << QAccessible::RadioButton; QTest::newRow("AX_ROLE_MENU_BUTTON") << QString("
      a
      ") << false << QAccessible::MenuItem; //QTest::newRow("AX_ROLE_MENU_LIST_OPTION") << QString("") << false << QAccessible::MenuItem; // FIXME: ") << false << QAccessible::ComboBox; QTest::newRow("AX_ROLE_PRE") << QString("
      a
      ") << true << QAccessible::Section; //QTest::newRow("AX_ROLE_PRESENTATIONAL") << QString("
      a
      ") << true << QAccessible::NoRole; // FIXME: Aria role 'presentation' should work QTest::newRow("AX_ROLE_PROGRESS_INDICATOR") << QString("
      ") << true << QAccessible::ProgressBar; QTest::newRow("AX_ROLE_RADIO_BUTTON") << QString("") << false << QAccessible::RadioButton; QTest::newRow("AX_ROLE_RADIO_GROUP") << QString("
      ") << true << QAccessible::Grouping; QTest::newRow("AX_ROLE_REGION") << QString("
      a
      ") << true << QAccessible::Section; //QTest::newRow("AX_ROLE_ROW") << QString("a") << true << QAccessible::Row; // FIXME: Aria role 'row' should work for //QTest::newRow("AX_ROLE_ROW_HEADER") << QString("") << true << QAccessible::RowHeader; // FIXME: Aria role 'rowheader' should work for
      a
      a QTest::newRow("AX_ROLE_RUBY") << QString("a") << false << QAccessible::StaticText; QTest::newRow("AX_ROLE_SCROLL_BAR") << QString("
      a") << true << QAccessible::ScrollBar; QTest::newRow("AX_ROLE_SEARCH") << QString("
      landmark
      ") << true << QAccessible::Section; QTest::newRow("AX_ROLE_SEARCH_BOX") << QString("") << false << QAccessible::EditableText; QTest::newRow("AX_ROLE_SLIDER") << QString("") << false << QAccessible::Slider; //QTest::newRow("AX_ROLE_SLIDER_THUMB"); // No mapping to ARIA role QTest::newRow("AX_ROLE_SPIN_BUTTON") << QString("") << false << QAccessible::SpinBox; //QTest::newRow("AX_ROLE_SPIN_BUTTON_PART"); // No mapping to ARIA role QTest::newRow("AX_ROLE_SPLITER") << QString("
      ") << true << QAccessible::Splitter; QTest::newRow("AX_ROLE_STATIC_TEXT") << QString("a") << false << QAccessible::StaticText; QTest::newRow("AX_ROLE_STATUS") << QString("a") << false << QAccessible::Indicator; QTest::newRow("AX_ROLE_SVG_ROOT") << QString("") << false << QAccessible::Graphic; QTest::newRow("AX_ROLE_SWITCH") << QString("") << false << QAccessible::Button; //QTest::newRow("AX_ROLE_TABLE") << QString("a
      ") << true << QAccessible::Table; // FIXME: The test case might be wrong (see AXTable.cpp) //QTest::newRow("AX_ROLE_TABLE_HEADER_CONTAINER"); // No mapping to ARIA role QTest::newRow("AX_ROLE_TAB") << QString("
      a
      ") << true << QAccessible::PageTab; QTest::newRow("AX_ROLE_TAB_LIST") << QString("
      a
      ") << true << QAccessible::PageTabList; QTest::newRow("AX_ROLE_TAB_PANEL") << QString("
      a
      ") << true << QAccessible::PageTab; QTest::newRow("AX_ROLE_TERM") << QString("
      a
      ") << true << QAccessible::StaticText; QTest::newRow("AX_ROLE_TEXT_FIELD") << QString("") << false << QAccessible::EditableText; QTest::newRow("AX_ROLE_TIME") << QString("") << false << QAccessible::Clock; QTest::newRow("AX_ROLE_TIMER") << QString("
      a
      ") << true << QAccessible::Clock; //QTest::newRow("AX_ROLE_TITLE_BAR"); // Not a blink accessibility role QTest::newRow("AX_ROLE_TOGGLE_BUTTON") << QString("") << false << QAccessible::Button; QTest::newRow("AX_ROLE_TOOLBAR") << QString("
      a
      ") << true << QAccessible::ToolBar; QTest::newRow("AX_ROLE_TOOLTIP") << QString("
      a
      ") << true << QAccessible::ToolTip; QTest::newRow("AX_ROLE_TREE") << QString("
      a
      ") << true << QAccessible::Tree; QTest::newRow("AX_ROLE_TREE_GRID") << QString("
      a
      ") << true << QAccessible::Tree; QTest::newRow("AX_ROLE_TREE_ITEM") << QString("
      a
      ") << true << QAccessible::TreeItem; QTest::newRow("AX_ROLE_VIDEO") << QString("") << false << QAccessible::Animation; //QTest::newRow("AX_ROLE_WINDOW"); // No mapping to ARIA role } void tst_Accessibility::roles() { QFETCH(QString, html); QFETCH(bool, isSection); QFETCH(QAccessible::Role, role); QWebEngineView webView; webView.setHtml("" + html + ""); webView.show(); QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); QVERIFY(spyFinished.wait()); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); // Corner case for Client role if (html.isEmpty()) { QCOMPARE(view->role(), QAccessible::Client); return; } QTRY_COMPARE(view->child(0)->childCount(), 1); QAccessibleInterface *document = view->child(0); QAccessibleInterface *section = document->child(0); if (isSection) { QCOMPARE(section->role(), role); return; } QVERIFY(section->childCount() > 0); QAccessibleInterface *element = section->child(0); QCOMPARE(element->role(), role); } static QByteArrayList params = QByteArrayList() << "--force-renderer-accessibility"; W_QTEST_MAIN(tst_Accessibility, params) #include "tst_accessibility.moc"