blob: 376dd12e01e455fa3c416cfc160414a97488231d [file] [log] [blame]
// 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())));
}