| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/test/interaction/interactive_browser_test.h" |
| |
| #include <list> |
| #include <sstream> |
| #include <tuple> |
| |
| #include "base/command_line.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/test/bind.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/ui/browser_element_identifiers.h" |
| #include "chrome/browser/ui/interaction/browser_elements.h" |
| #include "chrome/test/base/test_switches.h" |
| #include "components/constrained_window/constrained_window_views.h" |
| #include "content/public/test/browser_test.h" |
| #include "ui/base/interaction/element_identifier.h" |
| #include "ui/base/interaction/element_tracker.h" |
| #include "ui/base/interaction/expect_call_in_scope.h" |
| #include "ui/base/interaction/interaction_sequence.h" |
| #include "ui/base/mojom/ui_base_types.mojom-shared.h" |
| #include "ui/base/test/ui_controls.h" |
| #include "ui/base/ui_base_types.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/native_ui_types.h" |
| #include "ui/views/view_class_properties.h" |
| #include "ui/views/window/dialog_delegate.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsId); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContents2Id); |
| constexpr char kDocumentWithNamedElement[] = "/select.html"; |
| constexpr char kDocumentWithLinks[] = "/links.html"; |
| constexpr char kDocumentWithClickDetection[] = "/click.html"; |
| constexpr char kScrollableDocument[] = |
| "/scroll/scrollable_page_with_content.html"; |
| } // namespace |
| |
| class InteractiveBrowserTestBrowsertest : public InteractiveBrowserTest { |
| public: |
| InteractiveBrowserTestBrowsertest() = default; |
| ~InteractiveBrowserTestBrowsertest() override = default; |
| |
| void SetUp() override { |
| set_open_about_blank_on_browser_launch(true); |
| ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); |
| InteractiveBrowserTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| InteractiveBrowserTest::SetUpOnMainThread(); |
| embedded_test_server()->StartAcceptingConnections(); |
| } |
| |
| void TearDownOnMainThread() override { |
| EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); |
| InteractiveBrowserTest::TearDownOnMainThread(); |
| } |
| |
| static constexpr base::TimeDelta kDelayedActionDelay = |
| base::Milliseconds(500); |
| |
| void PostDelayedAction(base::OnceClosure action) { |
| delayed_actions_.push_back(std::move(action)); |
| if (!delayed_action_timer_.IsRunning()) { |
| delayed_action_timer_.Start( |
| FROM_HERE, kDelayedActionDelay, |
| base::BindRepeating( |
| &InteractiveBrowserTestBrowsertest::OnDelayedActionTimer, |
| base::Unretained(this))); |
| } |
| } |
| |
| private: |
| void OnDelayedActionTimer() { |
| std::move(delayed_actions_.front()).Run(); |
| delayed_actions_.pop_front(); |
| if (delayed_actions_.empty()) { |
| delayed_action_timer_.Stop(); |
| } |
| } |
| |
| std::list<base::OnceClosure> delayed_actions_; |
| base::RepeatingTimer delayed_action_timer_; |
| }; |
| |
| // This test checks that all of the UI elements in the browser can be dumped. |
| // The output must be manually verified. |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, DumpElements) { |
| auto* const incog = CreateIncognitoBrowser(); |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| InContext(BrowserElements::From(incog)->GetContext(), |
| PressButton(kToolbarAppMenuButtonElementId)), |
| DumpElements()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| EnsurePresentNotPresent) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| EnsurePresent(kWebContentsId, DeepQuery({"#select"})), |
| EnsureNotPresent(kWebContentsId, DeepQuery{"#doesNotExist"})); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| EnsureNotPresent_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| EnsureNotPresent(kWebContentsId, DeepQuery{"#select"}))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, EnsureNotVisible) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| // Element that is set to display: none. |
| ExecuteJsAt(kWebContentsId, DeepQuery{"#select"}, |
| "el => el.style.display = 'none'"), |
| EnsureNotVisible(kWebContentsId, DeepQuery({"#select"})), |
| // Element that has zero size. |
| ExecuteJsAt(kWebContentsId, DeepQuery{"p"}, |
| "el => { el.style.width = '0'; el.style.height = '0'; }"), |
| EnsureNotVisible(kWebContentsId, DeepQuery({"p"})), |
| // Element that is not present at all. |
| EnsureNotVisible(kWebContentsId, DeepQuery{"#doesNotExist"})); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| EnsureNotVisible_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| EnsureNotVisible(kWebContentsId, DeepQuery{"#select"}))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, EnsurePresent_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| EnsurePresent(kWebContentsId, DeepQuery{"#doesNotExist"}))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, ExecuteJs) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| ExecuteJs(kWebContentsId, "() => { window.value = 1; }"), |
| WithElement(kWebContentsId, base::BindOnce([](ui::TrackedElement* el) { |
| const auto result = AsInstrumentedWebContents(el)->Evaluate( |
| "() => window.value"); |
| EXPECT_EQ(1, result.GetInt()); |
| }))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| ExecuteJsFireAndForget) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| ExecuteJs(kWebContentsId, "() => { window.value = 1; }", |
| ExecuteJsMode::kFireAndForget), |
| WithElement(kWebContentsId, base::BindOnce([](ui::TrackedElement* el) { |
| const auto result = AsInstrumentedWebContents(el)->Evaluate( |
| "() => window.value"); |
| EXPECT_EQ(1, result.GetInt()); |
| }))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| ExecuteJsFailsOnThrow) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| ExecuteJs(kWebContentsId, "() => { throw new Error('an error'); }"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, CheckJsResult) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| const std::string str("a string"); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| ExecuteJs(kWebContentsId, |
| R"(() => { |
| window.intValue = 1; |
| window.boolValue = true; |
| window.doubleValue = 2.0; |
| window.stringValue = 'a string'; |
| })"), |
| CheckJsResult(kWebContentsId, "() => window.intValue"), |
| CheckJsResult(kWebContentsId, "() => window.intValue", 1), |
| CheckJsResult(kWebContentsId, "() => window.intValue", testing::Lt(2)), |
| CheckJsResult(kWebContentsId, "() => window.boolValue"), |
| CheckJsResult(kWebContentsId, "() => window.boolValue", true), |
| CheckJsResult(kWebContentsId, "() => window.boolValue", |
| testing::Ne(false)), |
| CheckJsResult(kWebContentsId, "() => window.doubleValue"), |
| CheckJsResult(kWebContentsId, "() => window.doubleValue", 2.0), |
| CheckJsResult(kWebContentsId, "() => window.doubleValue", |
| testing::Gt(1.5)), |
| CheckJsResult(kWebContentsId, "() => window.stringValue"), |
| CheckJsResult(kWebContentsId, "() => window.stringValue", "a string"), |
| CheckJsResult(kWebContentsId, "() => window.stringValue", str), |
| CheckJsResult(kWebContentsId, "() => window.stringValue", |
| std::string("a string")), |
| CheckJsResult(kWebContentsId, "() => window.stringValue", |
| testing::Ne(std::string("another string")))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResultWithPromise) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| CheckJsResult(kWebContentsId, |
| "() => new Promise((resolve, reject) => " |
| "setTimeout(() => resolve(true), 100))"), |
| CheckJsResult(kWebContentsId, |
| "() => new Promise((resolve, reject) => " |
| "setTimeout(() => resolve(1), 100))", |
| 1)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResultWithPromiseFailsOnReject) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| CheckJsResult(kWebContentsId, |
| "() => new Promise((resolve, reject) => " |
| "setTimeout(() => reject('rejected'), 100))"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, CheckJsResult_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| ExecuteJs(kWebContentsId, "() => { window.value = 1; }"), |
| CheckJsResult(kWebContentsId, "() => window.value", 2))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResult_ThrowError_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| CheckJsResult(kWebContentsId, |
| "() => { throw new Error('an error'); }", 2))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResult_NoArgument_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| ExecuteJs(kWebContentsId, "() => { window.value = 0; }"), |
| CheckJsResult(kWebContentsId, "() => window.value"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, ExecuteJsAt) { |
| const DeepQuery kWhere{"#select"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| ExecuteJsAt(kWebContentsId, kWhere, "(el) => { el.intValue = 1; }"), |
| WithElement(kWebContentsId, |
| base::BindLambdaForTesting([&kWhere](ui::TrackedElement* el) { |
| const auto result = |
| AsInstrumentedWebContents(el)->EvaluateAt( |
| kWhere, "(el) => el.intValue"); |
| EXPECT_EQ(1, result.GetInt()); |
| }))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| ExecuteJsAtFireAndForget) { |
| const DeepQuery kWhere{"#select"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| ExecuteJsAt(kWebContentsId, kWhere, "(el) => { el.intValue = 1; }", |
| ExecuteJsMode::kFireAndForget), |
| WithElement(kWebContentsId, |
| base::BindLambdaForTesting([&kWhere](ui::TrackedElement* el) { |
| const auto result = |
| AsInstrumentedWebContents(el)->EvaluateAt( |
| kWhere, "(el) => el.intValue"); |
| EXPECT_EQ(1, result.GetInt()); |
| }))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| ExecuteJsAtFailsIfElementNotPresent) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const DeepQuery kWhere{"#aaaaa"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| ExecuteJsAt(kWebContentsId, kWhere, "(el) => { el.intValue = 1; }"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| ExecuteJsAtFailsOnThrow) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const DeepQuery kWhere{"#select"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| ExecuteJsAt(kWebContentsId, kWhere, |
| "(el) => { throw new Error('an error'); }"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, CheckJsResultAt) { |
| const DeepQuery kWhere{"#select"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| const std::string str("a string"); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| ExecuteJsAt(kWebContentsId, kWhere, |
| R"((el) => { |
| el.intValue = 1; |
| el.boolValue = true; |
| el.doubleValue = 2.0; |
| el.stringValue = 'a string'; |
| })"), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.intValue"), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.intValue", 1), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.intValue", |
| testing::Lt(2)), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.boolValue"), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.boolValue", true), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.boolValue", |
| testing::Ne(false)), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.doubleValue"), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.doubleValue", 2.0), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.doubleValue", |
| testing::Gt(1.5)), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.stringValue"), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.stringValue", |
| "a string"), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.stringValue", str), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.stringValue", |
| std::string("a string")), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.stringValue", |
| testing::Ne(std::string("another string")))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResultAtWithPromise) { |
| const DeepQuery kWhere{"#select"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| ExecuteJsAt(kWebContentsId, kWhere, |
| R"((el) => { |
| el.intValue = 1; |
| el.stringValue = 'a string'; |
| })"), |
| CheckJsResultAt( |
| kWebContentsId, kWhere, |
| "(el) => new Promise((resolve, reject) => resolve(el.intValue))"), |
| CheckJsResultAt( |
| kWebContentsId, kWhere, |
| "(el) => new Promise((resolve, reject) => resolve(el.intValue))", 1), |
| CheckJsResultAt( |
| kWebContentsId, kWhere, |
| "(el) => new Promise((resolve, reject) => resolve(el.stringValue))"), |
| CheckJsResultAt( |
| kWebContentsId, kWhere, |
| "(el) => new Promise((resolve, reject) => resolve(el.stringValue))", |
| "a string")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResultAtWithPromiseFailsOnReject) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const DeepQuery kWhere{"#select"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| CheckJsResultAt(kWebContentsId, kWhere, |
| "(el) => new Promise((resolve, reject) " |
| "=> reject('rejected!'))"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResultAt_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const DeepQuery kWhere{"#select"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| ExecuteJsAt(kWebContentsId, kWhere, "(el) => { el.intValue = 1; }"), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.intValue", 2))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResultAt_ThrowsError_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const DeepQuery kWhere{"#select"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| CheckJsResultAt(kWebContentsId, kWhere, |
| "(el) => { throw new Error('an error'); }", 2))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResultAt_BadPath_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const DeepQuery kWhere{"#aaaaa"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.intValue", 2))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| CheckJsResultAt_NoArgument_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| const DeepQuery kWhere{"#select"}; |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| ExecuteJsAt(kWebContentsId, kWhere, |
| "(el) => { el.stringValue = ''; }"), |
| CheckJsResultAt(kWebContentsId, kWhere, "(el) => el.stringValue"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| InstrumentTabsAsTestSteps) { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTab1Id); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTab2Id); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTab3Id); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kIncognito1Id); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kIncognito2Id); |
| const char kIncognitoNtbName[] = "Incognito NTB"; |
| |
| auto verify_is_at_tab_index = [](Browser* where, ui::ElementIdentifier id, |
| int expected_index) { |
| return CheckElement( |
| id, base::BindLambdaForTesting([where](ui::TrackedElement* el) { |
| return where->tab_strip_model()->GetIndexOfWebContents( |
| AsInstrumentedWebContents(el)->web_contents()); |
| }), |
| expected_index); |
| }; |
| |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| const GURL url2 = embedded_test_server()->GetURL(kDocumentWithLinks); |
| |
| Browser* incognito_browser = CreateIncognitoBrowser(); |
| |
| RunTestSequence( |
| // Instrument an existing tab. |
| InstrumentTab(kTab1Id), verify_is_at_tab_index(browser(), kTab1Id, 0), |
| |
| // Instrument the next tab, then insert a tab and verify it's there. |
| InstrumentNextTab(kTab2Id), PressButton(kNewTabButtonElementId), |
| NavigateWebContents(kTab2Id, url1), |
| verify_is_at_tab_index(browser(), kTab2Id, 1), |
| |
| // Add and instrument tab all in one fell swoop. |
| AddInstrumentedTab(kTab3Id, url2), |
| verify_is_at_tab_index(browser(), kTab3Id, 2), |
| |
| // Instrument the next tab in any browser, then insert the tab and verify |
| // it's there. |
| InstrumentNextTab(kIncognito1Id, AnyBrowser()), |
| NameView( |
| kIncognitoNtbName, base::BindLambdaForTesting([incognito_browser]() { |
| return AsView( |
| ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kNewTabButtonElementId, |
| BrowserElements::From(incognito_browser)->GetContext())); |
| })), |
| PressButton(kIncognitoNtbName), |
| InAnyContext(verify_is_at_tab_index(incognito_browser, kIncognito1Id, 1)), |
| |
| Do(base::BindOnce([]() { LOG(WARNING) << 1; })), |
| |
| // Instrument a final tab by inserting it. Specify an index so the other |
| // tabs are re-ordered. |
| AddInstrumentedTab(kIncognito2Id, url2, 1, incognito_browser), |
| InAnyContext(verify_is_at_tab_index(incognito_browser, kIncognito2Id, 1)), |
| InAnyContext( |
| verify_is_at_tab_index(incognito_browser, kIncognito1Id, 2))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, ScrollIntoView) { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTabId); |
| const GURL url = embedded_test_server()->GetURL(kScrollableDocument); |
| const DeepQuery kLink{"#link"}; |
| const DeepQuery kText{"#text"}; |
| |
| constexpr char kElementIsInViewport[] = R"( |
| (el) => { |
| const bounds = el.getBoundingClientRect(); |
| return bounds.right >= 0 && bounds.bottom >= 0 && |
| bounds.x < window.innerWidth && bounds.y < window.innerHeight; |
| } |
| )"; |
| |
| RunTestSequence(InstrumentTab(kTabId), NavigateWebContents(kTabId, url), |
| CheckJsResultAt(kTabId, kLink, kElementIsInViewport, true), |
| CheckJsResultAt(kTabId, kText, kElementIsInViewport, false), |
| ScrollIntoView(kTabId, kText), |
| CheckJsResultAt(kTabId, kLink, kElementIsInViewport, false), |
| CheckJsResultAt(kTabId, kText, kElementIsInViewport, true)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForJsResultTrueAtStart) { |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url1), |
| ExecuteJs(kWebContentsId, |
| R"(() => { |
| window.intValue = 1; |
| window.boolValue = true; |
| window.doubleValue = 2.0; |
| window.stringValue = 'a string'; |
| })"), |
| WaitForJsResult(kWebContentsId, "() => window.intValue", 1), |
| WaitForJsResult(kWebContentsId, "() => window.boolValue", true), |
| WaitForJsResult(kWebContentsId, "() => window.doubleValue", |
| testing::Ge(1.0)), |
| WaitForJsResult(kWebContentsId, "() => window.stringValue", "a string"), |
| WaitForJsResult(kWebContentsId, "() => window.stringValue", |
| testing::HasSubstr("stri")), |
| WaitForJsResult(kWebContentsId, "() => window.intValue", |
| testing::AnyOf(4, 3, 1, 0))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForJsResultTrueAfterDelay) { |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url1), |
| ExecuteJs(kWebContentsId, |
| R"(() => { |
| window.intValue = 0; |
| window.boolValue = false; |
| window.doubleValue = 0.0; |
| window.stringValue = 'nothing'; |
| })"), |
| WithElement(kWebContentsId, |
| [this](ui::TrackedElement* el) { |
| auto* const util = AsInstrumentedWebContents(el); |
| PostDelayedAction(base::BindLambdaForTesting([util]() { |
| util->Execute("() => window.intValue = 1"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util]() { |
| util->Execute("() => window.boolValue = true"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util]() { |
| util->Execute("() => window.doubleValue = 2.0"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util]() { |
| util->Execute("() => window.stringValue = 'a string'"); |
| })); |
| }), |
| WaitForJsResult(kWebContentsId, "() => window.intValue", 1), |
| WaitForJsResult(kWebContentsId, "() => window.boolValue", true), |
| WaitForJsResult(kWebContentsId, "() => window.doubleValue", |
| testing::Ge(1.0)), |
| WaitForJsResult(kWebContentsId, "() => window.stringValue", |
| testing::HasSubstr("stri"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForJsResultTargetChangesAfterDelay) { |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| int target = 0; |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url1), |
| ExecuteJs(kWebContentsId, |
| R"(() => { |
| window.intValue = 1; |
| window.boolValue = true; |
| window.doubleValue = 2.0; |
| window.stringValue = 'a string'; |
| })"), |
| Do([this, &target]() { |
| PostDelayedAction( |
| base::BindLambdaForTesting([&target]() { target = 1; })); |
| }), |
| WaitForJsResult(kWebContentsId, "() => window.intValue", |
| std::ref(target))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForJsResultIsTruthyAfterDelay) { |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url1), |
| ExecuteJs(kWebContentsId, |
| R"(() => { |
| window.intValue = 0; |
| window.boolValue = false; |
| window.doubleValue = 0.0; |
| window.stringValue = null; |
| })"), |
| CheckJsResult(kWebContentsId, "() => window.intValue", |
| testing::Not(IsTruthy())), |
| CheckJsResult(kWebContentsId, "() => window.boolValue", |
| testing::Not(IsTruthy())), |
| CheckJsResult(kWebContentsId, "() => window.doubleValue", |
| testing::Not(IsTruthy())), |
| CheckJsResult(kWebContentsId, "() => window.stringValue", |
| testing::Not(IsTruthy())), |
| WithElement(kWebContentsId, |
| [this](ui::TrackedElement* el) { |
| auto* const util = AsInstrumentedWebContents(el); |
| PostDelayedAction(base::BindLambdaForTesting([util]() { |
| util->Execute("() => window.intValue = 1"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util]() { |
| util->Execute("() => window.boolValue = true"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util]() { |
| util->Execute("() => window.doubleValue = 2.0"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util]() { |
| util->Execute("() => window.stringValue = 'a string'"); |
| })); |
| }), |
| WaitForJsResult(kWebContentsId, "() => window.intValue"), |
| WaitForJsResult(kWebContentsId, "() => window.boolValue"), |
| WaitForJsResult(kWebContentsId, "() => window.doubleValue"), |
| WaitForJsResult(kWebContentsId, "() => window.stringValue")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForJsResultAtTrueAtStart) { |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| const DeepQuery kWhere{"#select"}; |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url1), |
| ExecuteJsAt(kWebContentsId, kWhere, |
| R"(el => { |
| el.intValue = 1; |
| el.boolValue = true; |
| el.doubleValue = 2.0; |
| el.stringValue = 'a string'; |
| })"), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.intValue", 1), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.boolValue", true), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.doubleValue", |
| testing::Ge(1.0)), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.stringValue", |
| "a string"), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.stringValue", |
| testing::HasSubstr("stri")), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.intValue", |
| testing::AnyOf(4, 3, 1, 0))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForJsResultAtTrueAfterDelay) { |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| const DeepQuery kWhere{"#select"}; |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url1), |
| ExecuteJsAt(kWebContentsId, kWhere, |
| R"(el => { |
| el.intValue = 0; |
| el.boolValue = false; |
| el.doubleValue = 0.0; |
| el.stringValue = 'nothing'; |
| })"), |
| WithElement( |
| kWebContentsId, |
| [this, kWhere](ui::TrackedElement* el) { |
| auto* const util = AsInstrumentedWebContents(el); |
| PostDelayedAction(base::BindLambdaForTesting([util, kWhere]() { |
| util->ExecuteAt(kWhere, "el => el.intValue = 1"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util, kWhere]() { |
| util->ExecuteAt(kWhere, "el => el.boolValue = true"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util, kWhere]() { |
| util->ExecuteAt(kWhere, "el => el.doubleValue = 2.0"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util, kWhere]() { |
| util->ExecuteAt(kWhere, "el => el.stringValue = 'a string'"); |
| })); |
| }), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.intValue", 1), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.boolValue", true), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.doubleValue", |
| testing::Ge(1.0)), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.stringValue", |
| testing::HasSubstr("stri"))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForJsResultAtTargetChangesAfterDelay) { |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| const DeepQuery kWhere{"#select"}; |
| int target = 0; |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url1), |
| ExecuteJsAt(kWebContentsId, kWhere, |
| R"(el => { |
| el.intValue = 1; |
| el.boolValue = true; |
| el.doubleValue = 2.0; |
| el.stringValue = 'a string'; |
| })"), |
| Do([this, &target]() { |
| PostDelayedAction( |
| base::BindLambdaForTesting([&target]() { target = 1; })); |
| }), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.intValue", |
| std::ref(target))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForJsResultAtIsTruthyAfterDelay) { |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| const DeepQuery kWhere{"#select"}; |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url1), |
| ExecuteJsAt(kWebContentsId, kWhere, |
| R"(el => { |
| el.intValue = 0; |
| el.boolValue = false; |
| el.doubleValue = 0.0; |
| el.stringValue = ''; |
| })"), |
| CheckJsResultAt(kWebContentsId, kWhere, "el => el.intValue", |
| testing::Not(IsTruthy())), |
| CheckJsResultAt(kWebContentsId, kWhere, "el => el.boolValue", |
| testing::Not(IsTruthy())), |
| CheckJsResultAt(kWebContentsId, kWhere, "el => el.doubleValue", |
| testing::Not(IsTruthy())), |
| CheckJsResultAt(kWebContentsId, kWhere, "el => el.stringValue", |
| testing::Not(IsTruthy())), |
| WithElement( |
| kWebContentsId, |
| [this, kWhere](ui::TrackedElement* el) { |
| auto* const util = AsInstrumentedWebContents(el); |
| PostDelayedAction(base::BindLambdaForTesting([util, kWhere]() { |
| util->ExecuteAt(kWhere, "el => el.intValue = 1"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util, kWhere]() { |
| util->ExecuteAt(kWhere, "el => el.boolValue = true"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util, kWhere]() { |
| util->ExecuteAt(kWhere, "el => el.doubleValue = 2.0"); |
| })); |
| PostDelayedAction(base::BindLambdaForTesting([util, kWhere]() { |
| util->ExecuteAt(kWhere, "el => el.stringValue = 'a string'"); |
| })); |
| }), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.intValue"), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.boolValue"), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.doubleValue"), |
| WaitForJsResultAt(kWebContentsId, kWhere, "el => el.stringValue")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForStateChangeAcrossNavigation) { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTabId); |
| DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kFoundElementEvent); |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithLinks); |
| const GURL url2 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| |
| StateChange state_change; |
| state_change.type = StateChange::Type::kExists; |
| state_change.where = {"#select"}; |
| state_change.continue_across_navigation = true; |
| state_change.event = kFoundElementEvent; |
| |
| RunTestSequence( |
| InstrumentTab(kTabId), |
| InParallel(RunSubsequence(NavigateWebContents(kTabId, url1), |
| NavigateWebContents(kTabId, url2)), |
| RunSubsequence(WaitForStateChange(kTabId, state_change)))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| WaitForStateChangeWithConditionAcrossNavigation) { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTabId); |
| DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kFoundElementEvent); |
| const GURL url1 = embedded_test_server()->GetURL(kDocumentWithLinks); |
| const GURL url2 = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| |
| StateChange state_change; |
| state_change.type = StateChange::Type::kExistsAndConditionTrue; |
| state_change.where = {"#select option[selected]"}; |
| state_change.test_function = "(el) => (el.innerText === 'Apple')"; |
| state_change.continue_across_navigation = true; |
| state_change.event = kFoundElementEvent; |
| |
| RunTestSequence( |
| InstrumentTab(kTabId), |
| InParallel(RunSubsequence(NavigateWebContents(kTabId, url1), |
| NavigateWebContents(kTabId, url2)), |
| RunSubsequence(WaitForStateChange(kTabId, state_change)))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| UninstrumentWebContents_CanReinstrument) { |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| UninstrumentWebContents(kWebContentsId), |
| // This should remove the element. |
| WaitForHide(kWebContentsId), InstrumentTab(kWebContentsId)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| UninstrumentWebContents_DoesNotFail) { |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| UninstrumentWebContents(kWebContents2Id, |
| /*fail_if_not_instrumented=*/false)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestBrowsertest, |
| UninstrumentWebContents_Fails) { |
| UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted); |
| private_test_impl().set_aborted_callback_for_testing(aborted.Get()); |
| |
| EXPECT_CALL_IN_SCOPE( |
| aborted, Run, |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| UninstrumentWebContents(kWebContents2Id))); |
| } |
| |
| using ClickElementParams = |
| std::tuple<ui_controls::MouseButton, ui_controls::AcceleratorState>; |
| |
| class InteractiveBrowserTestClickElementTest |
| : public InteractiveBrowserTestBrowsertest, |
| public testing::WithParamInterface<ClickElementParams> { |
| public: |
| InteractiveBrowserTestClickElementTest() = default; |
| ~InteractiveBrowserTestClickElementTest() override = default; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| InteractiveBrowserTestClickElementTest, |
| testing::Combine( |
| testing::Values(ui_controls::LEFT, |
| ui_controls::MIDDLE, |
| ui_controls::RIGHT), |
| testing::Values(ui_controls::kNoAccelerator, |
| ui_controls::kShift, |
| ui_controls::kControl, |
| ui_controls::kAlt, |
| ui_controls::kCommand, |
| static_cast<ui_controls::AcceleratorState>( |
| ui_controls::kAlt | ui_controls::kShift), |
| static_cast<ui_controls::AcceleratorState>( |
| ui_controls::kControl | ui_controls::kCommand | |
| ui_controls::kAlt | ui_controls::kShift))), |
| [](const testing::TestParamInfo<ClickElementParams>& params) { |
| std::ostringstream oss; |
| switch (std::get<0>(params.param)) { |
| case ui_controls::LEFT: |
| oss << "Left"; |
| break; |
| case ui_controls::MIDDLE: |
| oss << "Middle"; |
| break; |
| case ui_controls::RIGHT: |
| oss << "Right"; |
| break; |
| } |
| const auto accel = std::get<1>(params.param); |
| if (accel & ui_controls::kControl) { |
| oss << "_Control"; |
| } |
| if (accel & ui_controls::kAlt) { |
| oss << "_Alt"; |
| } |
| if (accel & ui_controls::kShift) { |
| oss << "_Shift"; |
| } |
| if (accel & ui_controls::kCommand) { |
| oss << "_Meta"; |
| } |
| return oss.str(); |
| }); |
| |
| IN_PROC_BROWSER_TEST_P(InteractiveBrowserTestClickElementTest, ClickElement) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithClickDetection); |
| const auto mouse_button = std::get<0>(GetParam()); |
| const auto modifier = std::get<1>(GetParam()); |
| const DeepQuery kButton = {"#button"}; |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| ClickElement(kWebContentsId, kButton, mouse_button, modifier), |
| CheckJsResultAt(kWebContentsId, kButton, "el => el.lastClickEvent.button", |
| static_cast<int>(mouse_button)), |
| CheckJsResultAt(kWebContentsId, kButton, "el => el.lastClickEvent.altKey", |
| (modifier & ui_controls::kAlt) != 0), |
| CheckJsResultAt(kWebContentsId, kButton, |
| "el => el.lastClickEvent.shiftKey", |
| (modifier & ui_controls::AcceleratorState::kShift) != 0), |
| CheckJsResultAt( |
| kWebContentsId, kButton, "el => el.lastClickEvent.ctrlKey", |
| (modifier & ui_controls::AcceleratorState::kControl) != 0), |
| CheckJsResultAt( |
| kWebContentsId, kButton, "el => el.lastClickEvent.metaKey", |
| (modifier & ui_controls::AcceleratorState::kCommand) != 0), |
| CheckJsResultAt(kWebContentsId, kButton, |
| R"( |
| function(el) { |
| const x = el.lastClickEvent.x; |
| const y = el.lastClickEvent.y; |
| const rect = el.getBoundingClientRect(); |
| return x >= rect.left && x < rect.right && |
| y >= rect.top && y < rect.bottom; |
| } |
| )")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestClickElementTest, |
| ClickElementOpensLink) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithClickDetection); |
| const GURL url2 = embedded_test_server()->GetURL(kDocumentWithLinks); |
| const DeepQuery kLink = {"#link"}; |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| ClickElement(kWebContentsId, kLink), |
| WaitForWebContentsNavigation(kWebContentsId, url2)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestClickElementTest, |
| MiddleClickElementOpensLink) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithClickDetection); |
| const GURL url2 = embedded_test_server()->GetURL(kDocumentWithLinks); |
| const DeepQuery kLink = {"#link"}; |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| InstrumentNextTab(kWebContents2Id), |
| ClickElement(kWebContentsId, kLink, ui_controls::MIDDLE), |
| WaitForWebContentsReady(kWebContents2Id, url2)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestClickElementTest, |
| ControlClickElementOpensLink) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithClickDetection); |
| const GURL url2 = embedded_test_server()->GetURL(kDocumentWithLinks); |
| const DeepQuery kLink = {"#link"}; |
| RunTestSequence(InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, url), |
| InstrumentNextTab(kWebContents2Id), |
| ClickElement(kWebContentsId, kLink, ui_controls::LEFT, |
| #if BUILDFLAG(IS_MAC) |
| ui_controls::kCommand |
| #else |
| ui_controls::kControl |
| #endif |
| ), |
| WaitForWebContentsReady(kWebContents2Id, url2)); |
| } |
| |
| // TODO(crbug.com/370724585): Re-enable this test. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_ShiftClickElementOpensLink DISABLED_ShiftClickElementOpensLink |
| #else |
| #define MAYBE_ShiftClickElementOpensLink ShiftClickElementOpensLink |
| #endif |
| IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestClickElementTest, |
| MAYBE_ShiftClickElementOpensLink) { |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithClickDetection); |
| const GURL url2 = embedded_test_server()->GetURL(kDocumentWithLinks); |
| const DeepQuery kLink = {"#link"}; |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), |
| InstrumentNextTab(kWebContents2Id, AnyBrowser()), |
| ClickElement(kWebContentsId, kLink, ui_controls::LEFT, |
| #if BUILDFLAG(IS_MAC) |
| static_cast<ui_controls::AcceleratorState>( |
| ui_controls::kCommand | ui_controls::kAlt) |
| #else |
| ui_controls::kShift |
| #endif |
| ), |
| InAnyContext(WaitForWebContentsReady(kWebContents2Id, url2)), |
| InSameContext(CheckView( |
| kBrowserViewElementId, |
| [](BrowserView* browser_view) { return browser_view->browser(); }, |
| testing::Ne(browser())))); |
| } |
| |
| // Parameter for WebUI coverage tests. |
| struct CoverageConfig { |
| // Whether to set the --devtools-code-coverage flag. If it's not set, nothing |
| // should be captured, and the test is simply verifying that no errors are |
| // generated as a result. |
| bool command_line_flag = false; |
| |
| // Whether coverage is actively enabled. If the command line flag is also set, |
| // the test will check whether data got written to the code coverage folder. |
| bool enable_coverage = false; |
| }; |
| |
| // Test fixture to verify that when EnableWebUICodeCoverage() is called with the |
| // correct command-line arguments, coverage data actually gets written out. It |
| // also verifies that |
| class InteractiveBrowserTestCodeCoverageBrowsertest |
| : public InteractiveBrowserTestBrowsertest, |
| public testing::WithParamInterface<CoverageConfig> { |
| public: |
| void SetUp() override { |
| { |
| // This is required for file IO. |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| CHECK(tmp_dir_.CreateUniqueTempDir()); |
| ASSERT_TRUE(base::IsDirectoryEmpty(tmp_dir_.GetPath())); |
| } |
| InteractiveBrowserTestBrowsertest::SetUp(); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| if (GetParam().command_line_flag) { |
| command_line->AppendSwitchPath(switches::kDevtoolsCodeCoverage, |
| tmp_dir_.GetPath()); |
| } else { |
| command_line->RemoveSwitch(switches::kDevtoolsCodeCoverage); |
| } |
| InteractiveBrowserTestBrowsertest::SetUpCommandLine(command_line); |
| } |
| |
| void SetUpOnMainThread() override { |
| InteractiveBrowserTestBrowsertest::SetUpOnMainThread(); |
| if (GetParam().enable_coverage) { |
| EnableWebUICodeCoverage(); |
| } |
| } |
| |
| // This is where we actually verify that the data has been written out, since |
| // coverage output doesn't happen until teardown. |
| void TearDownOnMainThread() override { |
| InteractiveBrowserTestBrowsertest::TearDownOnMainThread(); |
| |
| if (GetParam().command_line_flag) { |
| // This is required for file IO. |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| if (GetParam().enable_coverage) { |
| // Scripts and tests are special directories under the WebUI specific |
| // directory, ensure they have been created and are not empty. |
| base::FilePath coverage_dir = |
| tmp_dir_.GetPath().AppendASCII("webui_javascript_code_coverage"); |
| EXPECT_FALSE( |
| base::IsDirectoryEmpty(coverage_dir.AppendASCII("scripts"))); |
| EXPECT_FALSE(base::IsDirectoryEmpty(coverage_dir.AppendASCII("tests"))); |
| } else { |
| EXPECT_TRUE(base::IsDirectoryEmpty(tmp_dir_.GetPath())); |
| } |
| } |
| } |
| |
| protected: |
| base::ScopedTempDir tmp_dir_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(, |
| InteractiveBrowserTestCodeCoverageBrowsertest, |
| testing::Values(CoverageConfig{false, false}, |
| CoverageConfig{false, true}, |
| CoverageConfig{true, false}, |
| CoverageConfig{true, true})); |
| |
| // TODO(crbug.com/390224186) Re-enable the test after fixing the flakiness. |
| // TODO(crbug.com/430147700) Re-enable after fixing flakiness on ChromeOS. |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_TestCoverageEmits DISABLED_TestCoverageEmits |
| #else |
| #define MAYBE_TestCoverageEmits TestCoverageEmits |
| #endif |
| IN_PROC_BROWSER_TEST_P(InteractiveBrowserTestCodeCoverageBrowsertest, |
| MAYBE_TestCoverageEmits) { |
| // Navigate and load the New Tab Page, which we know works with code coverage. |
| RunTestSequence( |
| InstrumentTab(kWebContentsId), |
| NavigateWebContents(kWebContentsId, GURL("chrome://history"))); |
| } |
| |
| class InteractiveBrowserTestDialog : public views::DialogDelegateView { |
| public: |
| DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kElementId); |
| |
| InteractiveBrowserTestDialog() { |
| SetProperty(views::kElementIdentifierKey, kElementId); |
| } |
| ~InteractiveBrowserTestDialog() override = default; |
| |
| gfx::Size CalculatePreferredSize( |
| const views::SizeBounds& available_size) const override { |
| return gfx::Size(200, 200); |
| } |
| |
| static views::Widget* Show(Browser* parent, ui::mojom::ModalType modal_type) { |
| auto dialog = std::make_unique<InteractiveBrowserTestDialog>(); |
| dialog->SetModalType(modal_type); |
| views::Widget* widget = nullptr; |
| switch (modal_type) { |
| case ui::mojom::ModalType::kWindow: |
| widget = constrained_window::CreateBrowserModalDialogViews( |
| std::move(dialog), parent->window()->GetNativeWindow()); |
| break; |
| case ui::mojom::ModalType::kChild: |
| widget = constrained_window::CreateWebModalDialogViews( |
| dialog.release(), |
| parent->tab_strip_model()->GetActiveWebContents()); |
| break; |
| case ui::mojom::ModalType::kSystem: |
| case ui::mojom::ModalType::kNone: |
| widget = views::DialogDelegate::CreateDialogWidget( |
| std::move(dialog), gfx::NativeWindow(), |
| BrowserView::GetBrowserViewForBrowser(parent) |
| ->GetWidget() |
| ->GetNativeView()); |
| break; |
| } |
| widget->Show(); |
| return widget; |
| } |
| }; |
| |
| DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(InteractiveBrowserTestDialog, kElementId); |
| |
| namespace { |
| |
| // Scoped object that closes a widget it does not own. |
| class SafeWidgetRef { |
| public: |
| SafeWidgetRef() = default; |
| SafeWidgetRef(const SafeWidgetRef&) = delete; |
| SafeWidgetRef& operator=(views::Widget* widget) { |
| if (widget != widget_) { |
| Close(); |
| widget_ = widget; |
| } |
| return *this; |
| } |
| ~SafeWidgetRef() { Close(); } |
| |
| void Close() { |
| if (views::Widget* widget = widget_.get()) { |
| widget_ = nullptr; |
| if (!widget->IsClosed()) { |
| widget->CloseNow(); |
| } |
| } |
| } |
| |
| views::Widget* operator->() { return widget_.get(); } |
| |
| private: |
| raw_ptr<views::Widget> widget_ = nullptr; |
| }; |
| |
| } // namespace |
| |
| class InteractiveBrowserTestDialogBrowsertest |
| : public InteractiveBrowserTest, |
| public testing::WithParamInterface<ui::mojom::ModalType> { |
| public: |
| InteractiveBrowserTestDialogBrowsertest() = default; |
| ~InteractiveBrowserTestDialogBrowsertest() override = default; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| InteractiveBrowserTestDialogBrowsertest, |
| ::testing::Values( |
| ui::mojom::ModalType::kNone, |
| ui::mojom::ModalType::kChild, |
| ui::mojom::ModalType::kWindow |
| #if !BUILDFLAG(IS_MAC) |
| // System modals not supported on mac; see crbug.com/335864910 |
| , |
| ui::mojom::ModalType::kSystem |
| #endif |
| ), |
| [](const testing::TestParamInfo<ui::mojom::ModalType>& param) { |
| switch (param.param) { |
| case ui::mojom::ModalType::kNone: |
| return "None"; |
| case ui::mojom::ModalType::kChild: |
| return "Child"; |
| case ui::mojom::ModalType::kWindow: |
| return "Window"; |
| case ui::mojom::ModalType::kSystem: |
| return "System"; |
| } |
| }); |
| |
| IN_PROC_BROWSER_TEST_P(InteractiveBrowserTestDialogBrowsertest, |
| BrowserModalDialogContext) { |
| SafeWidgetRef widget; |
| RunTestSequence( |
| Do([this, &widget]() { |
| widget = InteractiveBrowserTestDialog::Show(browser(), GetParam()); |
| }), |
| InAnyContext(WaitForShow(InteractiveBrowserTestDialog::kElementId)), |
| InSameContext( |
| CheckView( |
| InteractiveBrowserTestDialog::kElementId, |
| [](views::View* view) { return view->GetWidget()->parent(); }, |
| BrowserView::GetBrowserViewForBrowser(browser())->GetWidget()), |
| CheckElement( |
| InteractiveBrowserTestDialog::kElementId, |
| [](ui::TrackedElement* el) { return el->context(); }, |
| private_test_impl().default_context()))); |
| } |