blob: 2e0ec443600c1935367b992875f491c1cab2cabd [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 "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/test/bind.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/test/base/test_switches.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 "url/gurl.h"
namespace {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsId);
constexpr char kDocumentWithNamedElement[] = "/select.html";
constexpr char kDocumentWithLinks[] = "/links.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();
}
};
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, 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,
incognito_browser->window()->GetElementContext()));
})),
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,
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),
// This is needed to prevent subsequent navigation from causing the
// previous step to fail due to the element immediately losing visibility.
FlushEvents(),
InParallel(Steps(NavigateWebContents(kTabId, url1),
NavigateWebContents(kTabId, url2)),
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),
// This is needed to prevent subsequent navigation from causing the
// previous step to fail due to the element immediately losing visibility.
FlushEvents(),
InParallel(Steps(NavigateWebContents(kTabId, url1),
NavigateWebContents(kTabId, url2)),
WaitForStateChange(kTabId, state_change)));
}
// 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}));
IN_PROC_BROWSER_TEST_P(InteractiveBrowserTestCodeCoverageBrowsertest,
TestCoverageEmits) {
// Navigate and load the New Tab Page, which we know works with code coverage.
RunTestSequence(
InstrumentTab(kWebContentsId),
NavigateWebContents(kWebContentsId, GURL("chrome://history")));
}