blob: 5f0f02d3125e2db0be4a6efe0622db1cd1e3322b [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/webcontents_interaction_test_util.h"
#include <sstream>
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-shared.h"
#include "chrome/browser/resource_coordinator/tab_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/interaction/interaction_test_util_browser.h"
#include "chrome/test/interaction/interactive_browser_test.h"
#include "chrome/test/interaction/tracked_element_webcontents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.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/page_transition_types.h"
#include "url/gurl.h"
namespace {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsElementId);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsElementId2);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kInteractionTestUtilCustomEventType);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kInteractionTestUtilCustomEventType2);
constexpr char kEmptyDocumentURL[] = "/empty.html";
constexpr char kDocumentWithTitle1URL[] = "/title1.html";
constexpr char kDocumentWithTitle2URL[] = "/title2.html";
constexpr char kDocumentWithLinksURL[] = "/links.html";
} // namespace
class WebContentsInteractionTestUtilTest : public InProcessBrowserTest {
public:
WebContentsInteractionTestUtilTest() = default;
~WebContentsInteractionTestUtilTest() override = default;
void SetUp() override {
set_open_about_blank_on_browser_launch(true);
ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
embedded_test_server()->StartAcceptingConnections();
}
void TearDownOnMainThread() override {
EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
InProcessBrowserTest::TearDownOnMainThread();
}
};
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ElementCreatedForExistingPage) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
// Using this constructor hits all of the rest of the constructors, saving us
// the hassle of writing three identical tests.
auto util = WebContentsInteractionTestUtil::ForExistingTabInContext(
browser()->window()->GetElementContext(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_TRUE(element->IsA<TrackedElementWebContents>());
EXPECT_EQ(
util.get(),
element->AsA<TrackedElementWebContents>()->owner());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ElementCreatedForExistingWebContentsWithoutWebView) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForTabWebContents(
browser()->tab_strip_model()->GetWebContentsAt(0), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_TRUE(element->IsA<TrackedElementWebContents>());
EXPECT_EQ(
util.get(),
element->AsA<TrackedElementWebContents>()->owner());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ElementRecreatedOnNavigate) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
NavigateParams navigate_params(
browser(), url, ui::PAGE_TRANSITION_TYPED);
Navigate(&navigate_params);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kHidden)
.SetTransitionOnlyOnEvent(true)
.SetElementID(kWebContentsElementId)
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest, LoadPage) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->LoadPage(url);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetTransitionOnlyOnEvent(true)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(url, util->web_contents()->GetURL());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest, IsPageLoaded) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_TRUE(util->is_page_loaded());
NavigateParams navigate_params(
browser(), url, ui::PAGE_TRANSITION_TYPED);
Navigate(&navigate_params);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kHidden)
.SetElementID(kWebContentsElementId)
.SetTransitionOnlyOnEvent(true)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_FALSE(util->is_page_loaded());
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_TRUE(util->is_page_loaded());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ElementRecreatedWithDifferentIdOnNavigate) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->set_page_identifier(kWebContentsElementId2);
NavigateParams navigate_params(
browser(), url, ui::PAGE_TRANSITION_TYPED);
Navigate(&navigate_params);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kHidden)
.SetElementID(kWebContentsElementId)
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId2)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ElementRecreatedWithDifferentIdOnBackForward) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
// Do two navigations, then go back, then forward again.
const GURL url = embedded_test_server()->GetURL(kDocumentWithTitle1URL);
const GURL url2 = embedded_test_server()->GetURL(kDocumentWithTitle2URL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
// Load the first page and make sure we wait for the page transition.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->LoadPage(url);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetMustRemainVisible(false)
.SetTransitionOnlyOnEvent(true)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(url, util->web_contents()->GetURL());
// Load the second page and wait for it to finish
// loading.
util->set_page_identifier(kWebContentsElementId2);
util->LoadPage(url2);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId2)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(url2, util->web_contents()->GetURL());
EXPECT_TRUE(chrome::CanGoBack(browser()));
util->set_page_identifier(kWebContentsElementId);
chrome::GoBack(browser(),
WindowOpenDisposition::CURRENT_TAB);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(url, util->web_contents()->GetURL());
EXPECT_TRUE(chrome::CanGoForward(browser()));
util->set_page_identifier(kWebContentsElementId2);
chrome::GoForward(
browser(), WindowOpenDisposition::CURRENT_TAB);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId2)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(url2, util->web_contents()->GetURL());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest, EvaluateInt) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(1, util->Evaluate("() => 1").GetInt());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest, EvaluateString) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(
std::string("The quick brown fox"),
util->Evaluate("() => 'The quick brown fox'")
.GetString());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest, EvaluatePromise) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
constexpr char kPromiseScript[] =
"() => new Promise((resolve) => setTimeout(resolve(123), 300))";
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(123,
util->Evaluate(kPromiseScript).GetInt());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnStateChangeOnCurrentCondition) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->Evaluate("function() { window.value = 1; }");
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.test_function = "() => window.value";
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnStateChangeOnDelayedCondition) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->Evaluate(
R"(function () {
window.value = 0;
setTimeout(
function() { window.value = 1; },
300);
})");
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.test_function = "() => window.value";
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
StateChangeTimeoutSendsEvent) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->Evaluate(
R"(function () {
window.value = 0;
setTimeout(
function() { window.value = 1; },
1000);
})");
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.test_function = "() => window.value";
state_change.event =
kInteractionTestUtilCustomEventType;
state_change.timeout = base::Milliseconds(300);
state_change.timeout_event =
kInteractionTestUtilCustomEventType2;
util->SendEventOnStateChange(state_change);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType2)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
StateChangeOnPromise) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
constexpr base::TimeDelta kPollTime = base::Milliseconds(50);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.test_function = R"(() => {
return new Promise(r => {
setTimeout(() => r(1), 100);
});
})";
state_change.event =
kInteractionTestUtilCustomEventType;
state_change.polling_interval = kPollTime;
util->SendEventOnStateChange(state_change);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendStateChangeEventsForDifferentDataTypes) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
// Poll significantly faster than the value in the page is expected to
// change; this allows us to verify that the value changes after a non-zero
// amount of time.
constexpr base::TimeDelta kPollTime = base::Milliseconds(50);
constexpr int kScriptDelayMs = 150;
base::ElapsedTimer timer;
base::TimeDelta last;
// Sets window.value to an initial value, and then some time later, sets it
// to a final value.
const auto post_and_listen = [&](base::Value initial, base::Value final) {
std::string script = content::JsReplace(
R"(function() {
window.value = $1;
setTimeout(function() { window.value = $2; }, $3);
})",
std::move(initial), std::move(final), kScriptDelayMs);
util->Evaluate(script);
WebContentsInteractionTestUtil::StateChange state_change;
state_change.test_function = "() => window.value";
state_change.event = kInteractionTestUtilCustomEventType;
state_change.polling_interval = kPollTime;
util->SendEventOnStateChange(state_change);
};
// Verifies that multiple polling intervals have passed before the condition
// we were watching becomes true.
const auto check_elapsed = [&]() {
auto next = timer.Elapsed();
EXPECT_GT(next, last + 2 * kPollTime);
last = next;
};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
last = timer.Elapsed();
// Integers:
post_and_listen(base::Value(0), base::Value(1));
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
check_elapsed();
// Booleans:
post_and_listen(base::Value(false),
base::Value(true));
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
check_elapsed();
// Strings:
post_and_listen(base::Value(""),
base::Value("foo"));
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
check_elapsed();
// Doubles:
post_and_listen(base::Value(0.0),
base::Value(6.1));
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
check_elapsed();
base::Value::List list;
list.Append(false);
post_and_listen(base::Value(),
base::Value(std::move(list)));
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
check_elapsed();
base::Value::Dict dict;
dict.Set("foo", "bar");
post_and_listen(base::Value(),
base::Value(std::move(dict)));
}))
.Build())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) { check_elapsed(); }))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnStateChangeOnAlreadyExists) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
// This is an element in the document.
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"a#title1"};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
// Wait for an element which is already in the document to exist and
// fire an event when it does (which should be almost immediate).
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.type =
WebContentsInteractionTestUtil::StateChange::
Type::kExists;
state_change.where = kQuery;
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
}))
.Build())
// Once the event is received, verify synchronouosly that the element
// does exist.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_TRUE(util->Exists(kQuery));
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnStateChangeOnExistsAfterDelay) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
// This is an element that is not (yet) in the document.
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"ul#foo"};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
// Queue a method that will cause an element to be added to the
// document in 300 ms, then wait for it to become present and fire an
// event.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// Inject JS that will add the element later.
util->Evaluate(
R"(function () {
setTimeout(
function() {
let el = document.createElement('ul');
el.id = 'foo';
document.body.appendChild(el);
},
300);
})");
// Set up the waiter.
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.type =
WebContentsInteractionTestUtil::StateChange::
Type::kExists;
state_change.where = kQuery;
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
// Verify that the element doesn't exist yet.
EXPECT_FALSE(util->Exists(kQuery));
}))
.Build())
// Once the event is received, verify synchronouosly that the element
// does exist.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_TRUE(util->Exists(kQuery));
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
// TODO(crbug.com/1492005): Resolve flakiness on ChromeOS and re-enable the
// test.
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_StateChangeExistsTimeoutSendsEvent \
DISABLED_StateChangeExistsTimeoutSendsEvent
#else
#define MAYBE_StateChangeExistsTimeoutSendsEvent \
StateChangeExistsTimeoutSendsEvent
#endif
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
MAYBE_StateChangeExistsTimeoutSendsEvent) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
// This element is not yet present in the document.
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"ul#foo"};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
// Queue a method that will cause an element to be added to the
// document in 1000 ms, then wait for it to become present and fire an
// event with only a 300ms timeout - the timeout event will be fired
// instead.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// Inject JS into the document that will add the
// element in 1000 ms.
util->Evaluate(
R"(function () {
setTimeout(
function() {
let el = document.createElement('ul');
el.id = 'foo';
document.body.appendChild(el);
},
1000);
})");
// Set up a waiter that will only wait 300 ms.
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.type =
WebContentsInteractionTestUtil::StateChange::
Type::kExists;
state_change.where = kQuery;
state_change.event =
kInteractionTestUtilCustomEventType;
state_change.timeout = base::Milliseconds(300);
state_change.timeout_event =
kInteractionTestUtilCustomEventType2;
util->SendEventOnStateChange(state_change);
}))
.Build())
// Verify that the timeout event was sent. The sequence will fail if
// the event is not received.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType2)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnStateChangeOnAlreadyDoesNotExist) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
// This element is not actually in the document.
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"a#title5"};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
// Wait for an element which is already not in the document to not
// exist and fire an event (which should be almost immediate).
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.type =
WebContentsInteractionTestUtil::StateChange::
Type::kDoesNotExist;
state_change.where = kQuery;
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
}))
.Build())
// Once the event is received, ensure that the element is not present.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_FALSE(util->Exists(kQuery));
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnStateChangeOnDoesNotExistAfterDelay) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
// This element is not initially present in the document.
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"ul#foo"};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
// Add the element referenced above and then remove it in 300 ms.
// In the interim, wait for the element to be removed and send an
// event when it is.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// This adds the element and queues up a callback
// to remove it again in 300 ms.
util->Evaluate(
R"(() => {
let el = document.createElement('ul');
el.id = 'foo';
document.body.appendChild(el);
setTimeout(
function() {
el.remove();
},
300);
})");
// Wait for the element to be removed.
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.type =
WebContentsInteractionTestUtil::StateChange::
Type::kDoesNotExist;
state_change.where = kQuery;
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
// Verify that the element is currently present.
EXPECT_TRUE(util->Exists(kQuery));
}))
.Build())
// Wait for the event on removal and verify synchronously that the
// element is actually gone.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_FALSE(util->Exists(kQuery));
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
StateChangeDoesNotExistTimeoutSendsEvent) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
// This element is not initially in the document.
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"ul#foo"};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
// Add the above-referenced element to the document, and then set up
// a callback to remove it 1000 ms later. Wait only 300 ms for the
// element to disappear (sending an event on timeout).
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// Adds the element and then queues up its removal
// 1000 ms later.
util->Evaluate(
R"(() => {
let el = document.createElement('ul');
el.id = 'foo';
document.body.appendChild(el);
setTimeout(
function() {
el.remove();
},
1000);
})");
// Wait for the element to go away, but only 300
// ms. This should send the timeout event instead
// of the success event.
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.type =
WebContentsInteractionTestUtil::StateChange::
Type::kDoesNotExist;
state_change.where = kQuery;
state_change.event =
kInteractionTestUtilCustomEventType;
state_change.timeout = base::Milliseconds(300);
state_change.timeout_event =
kInteractionTestUtilCustomEventType2;
util->SendEventOnStateChange(state_change);
}))
.Build())
// Verify that the timeout event was sent. The sequence will fail if
// the event is not received.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType2)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnStateChangeOnAlreadyExistsAndConditionTrue) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"a#title1"};
const char kTestCondition[] = "el => (el.innerText == 'Go to title1')";
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.type =
WebContentsInteractionTestUtil::StateChange::
Type::kExistsAndConditionTrue;
state_change.test_function = kTestCondition;
state_change.where = kQuery;
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
}))
.Build())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_TRUE(
util->EvaluateAt(kQuery, kTestCondition).GetBool());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(
WebContentsInteractionTestUtilTest,
SendEventOnStateChangeOnExistsAndConditionTrueAfterDelay) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"h1#foo"};
const char kTestCondition[] = "el => (el.innerText == 'bar')";
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->Evaluate(
R"(function () {
setTimeout(
function() {
let el = document.createElement('h1');
el.id = 'foo';
document.body.appendChild(el);
setTimeout(
function() {
let el = document.querySelector(
'h1#foo');
el.innerText = 'bar';
},
100);
},
300);
})");
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.type =
WebContentsInteractionTestUtil::StateChange::
Type::kExistsAndConditionTrue;
state_change.where = kQuery;
state_change.test_function = kTestCondition;
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
EXPECT_FALSE(util->Exists(kQuery));
}))
.Build())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_TRUE(
util->EvaluateAt(kQuery, kTestCondition).GetBool());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
StateChangeExistsAndConditionTrueTimeoutSendsEvent) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"h1#foo"};
const char kTestCondition[] = "el => (el.innerText == 'bar')";
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->Evaluate(
R"(function () {
let el = document.createElement('h1');
el.id = 'foo';
document.body.appendChild(el);
setTimeout(
function() {
let el = document.querySelector(
'h1#foo');
el.innerText = 'bar';
},
1000);
})");
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.type =
WebContentsInteractionTestUtil::StateChange::
Type::kExistsAndConditionTrue;
state_change.where = kQuery;
state_change.test_function = kTestCondition;
state_change.event =
kInteractionTestUtilCustomEventType;
state_change.timeout = base::Milliseconds(300);
state_change.timeout_event =
kInteractionTestUtilCustomEventType2;
util->SendEventOnStateChange(state_change);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType2)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ExecuteCatchesJavascriptError) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
std::string err;
util->Evaluate(
"() => { throw new Error('an error'); }",
&err);
EXPECT_FALSE(err.empty());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ExecuteCanChangePageState) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// This is an artificial value that is not
// initially true.
const char kCheckFunction[] = "() => !!window.value";
EXPECT_FALSE(util->Evaluate(kCheckFunction).GetBool());
// Prepare to send an event when the condition
// becomes true.
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.test_function = kCheckFunction;
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
// Immediately set a truthy value.
util->Execute("() => { window.value = 1; }");
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ExecuteAtCanChangePageState) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
const WebContentsInteractionTestUtil::DeepQuery kQuery = {"a#title1"};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// This is an artificial value that is not
// initially true.
const char kCheckFunction[] =
"el => (el.innerText === 'abcde')";
// Verify that the check function is false.
EXPECT_FALSE(
util->EvaluateAt(kQuery, kCheckFunction).GetBool());
// Set up a condition check for a text string that
// doesn't exist in the document.
WebContentsInteractionTestUtil::StateChange
state_change;
state_change.where = kQuery;
state_change.test_function = kCheckFunction;
state_change.event =
kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(state_change);
// Set the expected text using ExecuteAt().
// The check function should become true.
util->ExecuteAt(kQuery,
"el => { el.innerText = 'abcde'; }");
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
NavigatePageFromScriptCreatesNewElement) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->Evaluate(content::JsReplace(
"function() { window.location = $1; }",
url.spec()));
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kHidden)
.SetElementID(kWebContentsElementId)
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ElementRemovedOnMoveToNewBrowser) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
Browser* const other_browser = CreateBrowser(browser()->profile());
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
chrome::MoveTabsToExistingWindow(
browser(), other_browser,
{browser()->tab_strip_model()->active_index()});
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kHidden)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
EXPECT_THAT(ui::ElementTracker::GetElementTracker()
->GetAllMatchingElementsInAnyContext(kWebContentsElementId),
testing::IsEmpty());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ElementRemovedOnPageClosed) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
browser()->tab_strip_model()->CloseSelectedTabs();
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kHidden)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
OpenPageInNewTabInactive) {
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto* const model = browser()->tab_strip_model();
const int count = model->GetTabCount();
const int index = model->active_index();
util->LoadPageInNewTab(url, false);
EXPECT_EQ(count + 1, model->GetTabCount());
EXPECT_EQ(index, model->active_index());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
OpenPageInNewTabActive) {
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto* const model = browser()->tab_strip_model();
const int count = model->GetTabCount();
const int index = model->active_index();
util->LoadPageInNewTab(url, true);
EXPECT_EQ(count + 1, model->GetTabCount());
EXPECT_EQ(index + 1, model->active_index());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ForNextTabInContext) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto util2 = WebContentsInteractionTestUtil::ForNextTabInContext(
browser()->window()->GetElementContext(), kWebContentsElementId2);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->LoadPageInNewTab(url, false);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId2)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
EXPECT_EQ(url, util2->web_contents()->GetURL());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ForNextTabInBrowser) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
Browser* const browser2 = CreateBrowser(browser()->profile());
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto util2 = WebContentsInteractionTestUtil::ForNextTabInBrowser(
browser2, kWebContentsElementId2);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util2->LoadPageInNewTab(url, true);
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
// Wait for the element in the other browser to appear.
// TODO(dfried): when we support cross-context sequences, these can be
// combined.
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback,
completed2);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted2);
auto sequence2 =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed2.Get())
.SetAbortedCallback(aborted2.Get())
.SetContext(browser2->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId2)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed2, Run,
sequence2->RunSynchronouslyForTesting());
EXPECT_EQ(url, util2->web_contents()->GetURL());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ForNextTabInAnyBrowserFreshBrowser) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
Browser* browser2 = nullptr;
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto util2 = WebContentsInteractionTestUtil::ForNextTabInAnyBrowser(
kWebContentsElementId2);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// Open a completely new browser, we'll detect it
// opened and capture its first tab.
browser2 = CreateBrowser(browser()->profile());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
// Wait for the element in the other browser to appear.
// TODO(dfried): when we support cross-context sequences, these can be
// combined.
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback,
completed2);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted2);
auto sequence2 =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed2.Get())
.SetAbortedCallback(aborted2.Get())
.SetContext(browser2->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId2)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed2, Run,
sequence2->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ForNextTabInAnyBrowserSameBrowser) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const GURL url = embedded_test_server()->GetURL(kEmptyDocumentURL);
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto util2 = WebContentsInteractionTestUtil::ForNextTabInAnyBrowser(
kWebContentsElementId2);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->LoadPageInNewTab(url, false);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId2)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
EXPECT_EQ(url, util2->web_contents()->GetURL());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
MovePageToNewBrowserTriggersTabInAnyBrowser) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
Browser* const other_browser = CreateBrowser(browser()->profile());
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto util2 = WebContentsInteractionTestUtil::ForNextTabInAnyBrowser(
kWebContentsElementId2);
auto get_element2 = [&]() {
const auto result =
ui::ElementTracker::GetElementTracker()
->GetAllMatchingElementsInAnyContext(kWebContentsElementId2);
return result.empty() ? nullptr : result.front();
};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(nullptr, get_element2());
chrome::MoveTabsToExistingWindow(
browser(), other_browser,
{browser()->tab_strip_model()->active_index()});
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kHidden)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
auto* const element = get_element2();
EXPECT_NE(nullptr, element);
EXPECT_EQ(other_browser->window()->GetElementContext(), element->context());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
MovePageToNewBrowserTriggersNextTabInBrowser) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
Browser* const other_browser = CreateBrowser(browser()->profile());
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
auto util2 = WebContentsInteractionTestUtil::ForNextTabInBrowser(
other_browser, kWebContentsElementId2);
auto get_element2 = [&]() {
const auto result =
ui::ElementTracker::GetElementTracker()
->GetAllMatchingElementsInAnyContext(kWebContentsElementId2);
return result.empty() ? nullptr : result.front();
};
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_EQ(nullptr, get_element2());
chrome::MoveTabsToExistingWindow(
browser(), other_browser,
{browser()->tab_strip_model()->active_index()});
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kHidden)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
auto* const element = get_element2();
EXPECT_NE(nullptr, element);
EXPECT_EQ(other_browser->window()->GetElementContext(), element->context());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest, ExistsInWebUIPage) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const WebContentsInteractionTestUtil::DeepQuery kQuery1{
"settings-ui", "settings-main#main", "div#noSearchResults"};
const WebContentsInteractionTestUtil::DeepQuery kQuery2{
"settings-ui", "settings-main#foo", "div#noSearchResults"};
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
util->LoadPage(GURL("chrome://settings"));
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
EXPECT_TRUE(util->Exists(kQuery1));
std::string failed;
EXPECT_FALSE(util->Exists(kQuery2, &failed));
EXPECT_EQ(kQuery2[1], failed);
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
EvaluateAtInWebUIPage) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const WebContentsInteractionTestUtil::DeepQuery kQuery{
"settings-ui", "settings-main#main", "div#noSearchResults"};
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
util->LoadPage(GURL("chrome://settings"));
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
const auto result =
util->EvaluateAt(kQuery, "el => el.innerText");
EXPECT_TRUE(result.is_string());
EXPECT_FALSE(result.GetString().empty());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
EvaluateAtNotExistElement) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const WebContentsInteractionTestUtil::DeepQuery kQuery{
"settings-ui", "settings-main#main", "not-exists-element"};
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
util->LoadPage(GURL("chrome://settings"));
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
const auto result =
util->EvaluateAt(kQuery, "(el, err) => !!el");
EXPECT_TRUE(result.is_bool());
EXPECT_FALSE(result.GetBool());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
ExistsInStandardPage) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const WebContentsInteractionTestUtil::DeepQuery kQuery1{"#ref"};
const WebContentsInteractionTestUtil::DeepQuery kQuery2{"#not-present"};
// These queries check that we can properly escape quotes:
const WebContentsInteractionTestUtil::DeepQuery kQuery3{"[id=\"ref\"]"};
const WebContentsInteractionTestUtil::DeepQuery kQuery4{"[id='ref']"};
// These queries check that we can return strings with quotes on failure:
const WebContentsInteractionTestUtil::DeepQuery kQuery5{
"[id=\"not-present\"]"};
const WebContentsInteractionTestUtil::DeepQuery kQuery6{"[id='not-present']"};
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// Using DeepQuery.
EXPECT_TRUE(util->Exists(kQuery1));
std::string failed;
EXPECT_FALSE(util->Exists(kQuery2, &failed));
EXPECT_EQ(kQuery2[0], failed);
EXPECT_TRUE(util->Exists(kQuery3));
EXPECT_TRUE(util->Exists(kQuery4));
EXPECT_FALSE(util->Exists(kQuery5, &failed));
EXPECT_EQ(kQuery5[0], failed);
EXPECT_FALSE(util->Exists(kQuery6, &failed));
EXPECT_EQ(kQuery6[0], failed);
// Using the simple string selector version.
EXPECT_TRUE(util->Exists(kQuery1[0]));
EXPECT_FALSE(util->Exists(kQuery2[0]));
EXPECT_TRUE(util->Exists(kQuery3[0]));
EXPECT_TRUE(util->Exists(kQuery4[0]));
EXPECT_FALSE(util->Exists(kQuery5[0]));
EXPECT_FALSE(util->Exists(kQuery6[0]));
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
EvaluateAtInStandardPage) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const WebContentsInteractionTestUtil::DeepQuery kQuery{"#ref"};
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// Test evaluate at with a DeepQuery.
auto result =
util->EvaluateAt(kQuery, "el => el.innerText");
EXPECT_TRUE(result.is_string());
EXPECT_EQ("ref link", result.GetString());
// Test evaluate at with a plain string selector.
result = util->EvaluateAt(kQuery[0],
"el => el.innerText");
EXPECT_TRUE(result.is_string());
EXPECT_EQ("ref link", result.GetString());
}))
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnConditionStateChangeAtInStandardPage) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const WebContentsInteractionTestUtil::DeepQuery kQuery{"#ref"};
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->EvaluateAt(kQuery,
R"(el => {
el.innerText = '';
setTimeout(() => el.innerText = 'foo',
300);
})");
WebContentsInteractionTestUtil::StateChange change;
change.test_function = "el => el.innerText";
change.where = kQuery;
change.event = kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(change);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnExistsStateChangeAtInStandardPage) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const WebContentsInteractionTestUtil::DeepQuery kQuery1{"#ref"};
const WebContentsInteractionTestUtil::DeepQuery kQuery2{"#ref", "p#pp"};
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithLinksURL);
util->LoadPage(url);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
util->EvaluateAt(kQuery1,
R"(el => {
el.innerText = '';
setTimeout(() =>
el.innerHTML =
'<p id="pp">foo</p>',
300);
})");
WebContentsInteractionTestUtil::StateChange change;
change.where = kQuery2;
change.event = kInteractionTestUtilCustomEventType;
util->SendEventOnStateChange(change);
}))
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
SendEventOnExistsStateChangeContinueAcrossNavigation) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
const WebContentsInteractionTestUtil::DeepQuery kQuery{"#ref"};
auto util = WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kWebContentsElementId);
const GURL url1 = embedded_test_server()->GetURL(kDocumentWithTitle1URL);
const GURL url2 = embedded_test_server()->GetURL(kDocumentWithLinksURL);
// Navigate to the first page.
util->LoadPage(url1);
auto sequence =
ui::InteractionSequence::Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(browser()->window()->GetElementContext())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kWebContentsElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* sequence,
ui::TrackedElement* element) {
// Wait for an element that is only present on the
// second page, to be loaded below.
WebContentsInteractionTestUtil::StateChange change;
change.type = WebContentsInteractionTestUtil::
StateChange::Type::kExists;
change.where = kQuery;
change.event = kInteractionTestUtilCustomEventType;
change.continue_across_navigation = true;
util->SendEventOnStateChange(change);
// Navigate to the second page.
util->LoadPage(url2);
}))
.SetMustRemainVisible(false)
.Build())
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kCustomEvent,
kInteractionTestUtilCustomEventType)
.SetElementID(kWebContentsElementId)
.SetMustBeVisibleAtStart(false)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
// This is a regression test for the case where we open a new tab in a way that
// causes it not to have a URL; previously, it would not create an element
// because navigating_away_from_ was empty.
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilTest,
CreatesElementForPageWithBlankURL) {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kExistingTabElementId);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNewTabElementId);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
std::unique_ptr<WebContentsInteractionTestUtil> existing_tab =
WebContentsInteractionTestUtil::ForExistingTabInBrowser(
browser(), kExistingTabElementId);
std::unique_ptr<WebContentsInteractionTestUtil> new_tab;
auto sequence =
ui::InteractionSequence::Builder()
.SetContext(browser()->window()->GetElementContext())
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
// Get the first tab and inject code to pop up a second window.
// Because the second window is created using a javascript: URL, it
// will not report a valid URL.
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kExistingTabElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence*, ui::TrackedElement*) {
new_tab =
WebContentsInteractionTestUtil::ForNextTabInBrowser(
browser(), kNewTabElementId);
// Cause a tab to come into being and do some stuff.
existing_tab->Evaluate(
"() => window.open('javascript:window.foo=1')");
}))
.Build())
// Verify that the element for the second tab is still created,
// despite it not having a URL.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetType(ui::InteractionSequence::StepType::kShown)
.SetElementID(kNewTabElementId)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
class WebContentsInteractionTestUtilInteractiveTest
: public InteractiveBrowserTest {
public:
WebContentsInteractionTestUtilInteractiveTest() = default;
~WebContentsInteractionTestUtilInteractiveTest() 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();
}
};
// TODO(crbug.com/1447298): flaky on Mac - see comments below for the likely
// culprit line.
#if BUILDFLAG(IS_MAC)
#define MAYBE_TrackWebContentsAcrossReplace \
DISABLED_TrackWebContentsAcrossReplace
#else
#define MAYBE_TrackWebContentsAcrossReplace TrackWebContentsAcrossReplace
#endif
IN_PROC_BROWSER_TEST_F(WebContentsInteractionTestUtilInteractiveTest,
MAYBE_TrackWebContentsAcrossReplace) {
const GURL url1 = embedded_test_server()->GetURL(kDocumentWithLinksURL);
const GURL url2 = embedded_test_server()->GetURL(kEmptyDocumentURL);
RunTestSequence(InstrumentTab(kWebContentsElementId),
NavigateWebContents(kWebContentsElementId, url1),
AddInstrumentedTab(kWebContentsElementId2, url2),
SelectTab(kTabStripElementId, 1), FlushEvents(),
// This has to be done on a fresh message loop.
Do(base::BindLambdaForTesting([&]() {
// Discard the first tab. This triggers a replacement. Note
// that because the active tab cannot be discarded, this
// line is guaranteed to discard the tab we want. (But if it
// did not, the following steps would fail.)
g_browser_process->GetTabManager()->DiscardTab(
mojom::LifecycleUnitDiscardReason::EXTERNAL);
})),
WaitForHide(kWebContentsElementId), FlushEvents(),
// This has to be done on a fresh message loop.
// For some reason, this does not reliably trigger page
// reload on Mac (see crbug.com/1447298).
SelectTab(kTabStripElementId, 0),
WaitForShow(kWebContentsElementId));
}