blob: d86144c92782fcd48236400b8ad6509bb2c5c0fc [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.h"
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "ui/accessibility/ax_tree_update.h"
#include "ui/accessibility/platform/ax_snapshot_node_android_platform.h"
namespace arc {
namespace {
class AXTreeSnapshotWaiter {
public:
AXTreeSnapshotWaiter() = default;
void Wait() { loop_.Run(); }
const ui::AXTreeUpdate& snapshot() const { return snapshot_; }
void ReceiveSnapshot(const ui::AXTreeUpdate& snapshot) {
snapshot_ = snapshot;
loop_.Quit();
}
private:
ui::AXTreeUpdate snapshot_;
base::RunLoop loop_;
DISALLOW_COPY_AND_ASSIGN(AXTreeSnapshotWaiter);
};
} // namespace
class ArcVoiceInteractionArcHomeServiceTest : public InProcessBrowserTest {
public:
ArcVoiceInteractionArcHomeServiceTest() = default;
~ArcVoiceInteractionArcHomeServiceTest() override = default;
protected:
mojom::VoiceInteractionStructurePtr GetVoiceInteractionStructure(
const std::string& html) {
GURL url("data:text/html," + html);
ui_test_utils::NavigateToURL(browser(), url);
auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
AXTreeSnapshotWaiter waiter;
web_contents->RequestAXTreeSnapshot(base::BindOnce(
&AXTreeSnapshotWaiter::ReceiveSnapshot, base::Unretained(&waiter)));
waiter.Wait();
auto node = ui::AXSnapshotNodeAndroid::Create(waiter.snapshot(), false);
return ArcVoiceInteractionArcHomeService::
CreateVoiceInteractionStructureForTesting(*node);
}
private:
DISALLOW_COPY_AND_ASSIGN(ArcVoiceInteractionArcHomeServiceTest);
};
IN_PROC_BROWSER_TEST_F(ArcVoiceInteractionArcHomeServiceTest,
VoiceInteractionStructurePositionTest) {
auto result = GetVoiceInteractionStructure(
"<div style='position:absolute;width:200px;height:200px'>"
"<p style='position:absolute;top:20px;left:20px;margin:0'>Hello</p>"
"</div>");
ASSERT_FALSE(result.is_null());
ASSERT_EQ(result->children.size(), 1ul);
ASSERT_EQ(result->children[0]->children.size(), 1ul);
auto& child = result->children[0]->children[0];
ASSERT_EQ(base::UTF16ToUTF8(child->text), "Hello");
ASSERT_EQ(child->rect.x(), 20);
ASSERT_EQ(child->rect.y(), 20);
}
IN_PROC_BROWSER_TEST_F(ArcVoiceInteractionArcHomeServiceTest,
VoiceInteractionStructureInputSelectionTest) {
auto result = GetVoiceInteractionStructure(
"<html>"
" <body>"
" <input id='input' value='Hello, world'/>"
" <script type='text/javascript'>"
" var input = document.getElementById('input');"
" input.select();"
" input.selectionStart = 0;"
" input.selectionEnd = 5;"
" </script>"
" </body>"
"</html>");
auto& content_root = result->children[0];
ASSERT_EQ(content_root->children.size(), 1ul);
auto& child = content_root->children[0];
ASSERT_FALSE(child.is_null());
ASSERT_EQ(base::UTF16ToUTF8(child->text), "Hello, world");
EXPECT_EQ(gfx::Range(0, 5), child->selection);
}
IN_PROC_BROWSER_TEST_F(ArcVoiceInteractionArcHomeServiceTest,
VoiceInteractionStructureSectionTest) {
// Help ensure accessibility states are tested correctly.
// When the states are not tested correctly (bit shifted), the div appears to
// be focusable, causing the child text to be aggregated for the div.
auto result = GetVoiceInteractionStructure(
"<div role='section' aria-expanded='false'>"
"Hello<img>"
"</div>");
ASSERT_FALSE(result.is_null());
auto& child = result->children[0];
ASSERT_EQ(base::UTF16ToUTF8(child->text), "");
}
IN_PROC_BROWSER_TEST_F(ArcVoiceInteractionArcHomeServiceTest,
VoiceInteractionStructureSelectTest) {
// Help ensure accessibility states are tested correctly.
// When the states are not tested correctly (bit shifted), the option appears
// to have ax::mojom::State::kProtected, and text is incorrectly set as
// password dots.
auto result = GetVoiceInteractionStructure(
"<div><select><option>1</option></select></div>");
ASSERT_FALSE(result.is_null());
auto& child = result->children[0];
ASSERT_EQ(base::UTF16ToUTF8(child->text), "");
ASSERT_EQ(base::UTF16ToUTF8(child->children[0]->text), "1");
}
IN_PROC_BROWSER_TEST_F(ArcVoiceInteractionArcHomeServiceTest,
VoiceInteractionStructureMultipleSelectionTest) {
auto result = GetVoiceInteractionStructure(
"<html>"
" <body>"
" <b id='node1'>foo</b><b>middle</b><b id='node2'>bar</b>"
" <script type='text/javascript'>"
" var element1 = document.getElementById('node1');"
" var node1 = element1.childNodes.item(0);"
" var range=document.createRange();"
" range.setStart(node1, 1);"
" var element2 = document.getElementById('node2');"
" var node2 = element2.childNodes.item(0);"
" range.setEnd(node2, 1);"
" var selection=window.getSelection();"
" selection.removeAllRanges();"
" selection.addRange(range);"
" </script>"
" </body>"
"</html>");
ASSERT_EQ(result->children.size(), 1ul);
auto& content_root = result->children[0];
ASSERT_EQ(content_root->children.size(), 3ul);
auto& grand_child1 = content_root->children[0];
EXPECT_EQ(gfx::Range(1, 3), grand_child1->selection);
auto& grand_child2 = content_root->children[1];
EXPECT_EQ(gfx::Range(0, 6), grand_child2->selection);
auto& grand_child3 = content_root->children[2];
EXPECT_EQ(gfx::Range(0, 1), grand_child3->selection);
}
} // namespace arc