blob: 99c3f9e709ea7853b230033baea05f722aab8a25 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/interaction/interaction_sequence.h"
#include <optional>
#include <sstream>
#include "base/functional/callback_forward.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_test_util.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/base/interaction/expect_call_in_scope.h"
#include "ui/base/interaction/interaction_sequence_test_util.h"
#include "ui/base/interaction/interactive_test_internal.h"
namespace ui {
namespace {
constexpr char kElementName1[] = "Element1";
constexpr char kElementName2[] = "Element2";
constexpr char kStepDescription[] = "Step description.";
constexpr char kStepDescription2[] = "Step description 2.";
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestIdentifier1);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestIdentifier2);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestIdentifier3);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kCustomEventType1);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kCustomEventType2);
const ElementContext kTestContext1(1);
const ElementContext kTestContext2(2);
const ElementContext kTestContext3(3);
} // namespace
class InteractionSequenceTestBase : public testing::Test {
public:
InteractionSequenceTestBase() = default;
~InteractionSequenceTestBase() override = default;
// Returns what step start mode should be used, null for default.
virtual std::optional<InteractionSequence::StepStartMode> GetMode() const {
return std::nullopt;
}
InteractionSequence::Builder Builder() {
InteractionSequence::Builder builder;
if (const auto mode = GetMode()) {
builder.SetDefaultStepStartMode(*mode);
}
return builder;
}
private:
base::test::SingleThreadTaskEnvironment task_environment;
};
class InteractionSequenceTest
: public InteractionSequenceTestBase,
public testing::WithParamInterface<InteractionSequence::StepStartMode> {
public:
InteractionSequenceTest() = default;
~InteractionSequenceTest() override = default;
std::optional<InteractionSequence::StepStartMode> GetMode() const override {
return GetParam();
}
};
INSTANTIATE_TEST_SUITE_P(
,
InteractionSequenceTest,
testing::Values(InteractionSequence::StepStartMode::kImmediate,
InteractionSequence::StepStartMode::kAsynchronous),
[](const testing::TestParamInfo<InteractionSequence::StepStartMode>& mode) {
std::ostringstream oss;
oss << mode.param;
return oss.str();
});
TEST_P(InteractionSequenceTest, ConstructAndDestructContext) {
auto sequence =
Builder()
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence.reset();
}
TEST_P(InteractionSequenceTest, ConstructAndDestructWithWithInitialElement) {
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.AddStep(InteractionSequence::WithInitialElement(&element))
.Build();
sequence.reset();
}
TEST_P(InteractionSequenceTest, StartAndDestruct) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence.reset());
}
TEST_P(InteractionSequenceTest, StartFailsIfWithInitialElementNotVisible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
StartFailsIfWithInitialElementNotVisibleIdentifierOnly) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(true)
.SetMustRemainVisible(true)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest, AbortIfWithInitialElementHiddenBeforeStart) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElementPtr element =
std::make_unique<test::TestElement>(kTestIdentifier1, kTestContext1);
element->Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(element.get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element->identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
element.reset();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(1, nullptr, kTestIdentifier1,
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::
kElementHiddenBeforeSequenceStart)),
sequence->Start());
}
TEST_P(InteractionSequenceTest,
AbortIfWithInitialElementHiddenBeforeStartIdentifierOnly) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElementPtr element =
std::make_unique<test::TestElement>(kTestIdentifier1, kTestContext1);
element->Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element->context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element->identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(true)
.SetMustRemainVisible(true)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element->identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
element.reset();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest, HideWithInitialElementAborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element.Hide());
}
TEST_P(InteractionSequenceTest,
HideWithInitialElementDoesNotAbortIfMustRemainVisibleIsFalse) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(true)
.SetMustRemainVisible(false)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetMustBeVisibleAtStart(false)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
element.Hide();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence.reset());
}
// This tests a case where the element is hidden on the first step and there is
// an explicit step transition.
TEST_P(InteractionSequenceTest, TransitionOnElementHiddenFirstStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetTransitionOnlyOnEvent(true))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element.Hide());
}
// Now that we're fairly confident that sequences can complete, try all the
// different ways to construct and add steps.
TEST_P(InteractionSequenceTest, TestStepBuilderConstructAndAdd) {
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kCustomEvent);
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, start1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, end1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, start2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, end2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, start3);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, end3);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, start4);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, end4);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
InteractionSequence::StepBuilder step1;
step1.SetElementID(element1.identifier());
step1.SetStartCallback(start1.Get());
step1.SetEndCallback(end1.Get());
InteractionSequence::StepBuilder step2;
step2.SetElementID(element1.identifier());
step2.SetType(InteractionSequence::StepType::kActivated);
step2.SetStartCallback(start2.Get());
step2.SetEndCallback(end2.Get());
InteractionSequence::StepBuilder step3;
step3.SetElementID(element2.identifier());
step3.SetStartCallback(start3.Get());
step3.SetEndCallback(end3.Get());
InteractionSequence::StepBuilder step4;
step4.SetElementID(element2.identifier());
step4.SetType(InteractionSequence::StepType::kCustomEvent, kCustomEvent);
// Test move and assign for step builder.
InteractionSequence::StepBuilder step1_move_constructed(std::move(step1));
InteractionSequence::StepBuilder step2_move_assigned;
step2_move_assigned = std::move(step2);
InteractionSequence::StepBuilder step4_move_constructed_then_modified(
std::move(step4));
step4_move_constructed_then_modified.SetStartCallback(start4.Get());
step4_move_constructed_then_modified.SetEndCallback(end4.Get());
InteractionSequence::Builder builder;
builder.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(step1_move_constructed)
.AddStep(step2_move_assigned)
.AddStep(std::move(step3))
.AddStep(std::move(step4_move_constructed_then_modified))
.AddStep(std::move(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)));
// Test move and assign for builder.
InteractionSequence::Builder builder2(std::move(builder));
InteractionSequence::Builder builder3;
builder3 = std::move(builder2);
auto sequence = builder3.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(start1, Run, sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(end1, Run, start2, Run, element1.Activate());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(end2, Run, start3, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(end3, Run, start4, Run,
element2.SendCustomEvent(kCustomEvent));
EXPECT_ASYNC_CALLS_IN_SCOPE_2(end4, Run, completed, Run, element2.Hide());
}
TEST_P(InteractionSequenceTest, TransitionOnActivated) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element), completed,
Run, element.Activate());
}
TEST_P(InteractionSequenceTest, TransitionOnCustomEventSameId) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element), completed,
Run,
element.SendCustomEvent(kCustomEventType1));
}
TEST_P(InteractionSequenceTest, TransitionOnCustomEventDifferentId) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
// Non-matching ID should not trigger the step, even if the event type
// matches.
element.SendCustomEvent(kCustomEventType1);
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element2), completed,
Run,
element2.SendCustomEvent(kCustomEventType1));
}
TEST_P(InteractionSequenceTest, TransitionOnCustomEventAnyElement) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step2.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step, Run(sequence.get(), &element),
element.SendCustomEvent(kCustomEventType1));
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2, Run(sequence.get(), &element2),
completed, Run,
element2.SendCustomEvent(kCustomEventType1));
}
TEST_P(InteractionSequenceTest, TransitionOnCustomEventMultipleEvents) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType2)
.SetStartCallback(step2.Get())
.Build())
.Build();
sequence->Start();
// This is the wrong event type so won't cause a transition.
element.SendCustomEvent(kCustomEventType2);
EXPECT_ASYNC_CALL_IN_SCOPE(step, Run(sequence.get(), &element),
element.SendCustomEvent(kCustomEventType1));
// This is the wrong event type so won't cause a transition.
element2.SendCustomEvent(kCustomEventType1);
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2, Run(sequence.get(), &element2),
completed, Run,
element2.SendCustomEvent(kCustomEventType2));
}
TEST_P(InteractionSequenceTest, TransitionOnCustomEventFailsIfMustBeVisible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetMustBeVisibleAtStart(true)
.SetStartCallback(step.Get())
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
2, nullptr, element2.identifier(),
InteractionSequence::StepType::kCustomEvent,
InteractionSequence::AbortedReason::kElementNotVisibleAtStartOfStep)),
sequence->Start());
}
TEST_P(InteractionSequenceTest, TransitionOnElementShown) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element2), completed,
Run, element2.Show());
}
TEST_P(InteractionSequenceTest, TransitionFailsOnElementShownIfMustBeVisible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetDescription(kStepDescription)
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step.Get())
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
2, nullptr, element2.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::kElementNotVisibleAtStartOfStep,
std::string(kStepDescription))),
sequence->Start());
}
TEST_P(InteractionSequenceTest, TransitionOnSameElementHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustRemainVisible(false)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
element2.Show();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), nullptr), completed,
Run, element2.Hide());
}
TEST_P(InteractionSequenceTest, TransitionOnOtherElementHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), nullptr), completed,
Run, element2.Hide());
}
TEST_P(InteractionSequenceTest, TransitionOnOtherElementAlreadyHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetStartCallback(step.Get())
.Build())
.Build();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), testing::_),
completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
FailOnOtherElementAlreadyHiddenIfMustBeVisible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetMustBeVisibleAtStart(true)
.SetStartCallback(step.Get())
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
2, nullptr, element2.identifier(),
InteractionSequence::StepType::kHidden,
InteractionSequence::AbortedReason::kElementNotVisibleAtStartOfStep)),
sequence->Start());
}
TEST_P(InteractionSequenceTest,
FailIfFirstElementBecomesHiddenBeforeActivation) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element1.Hide());
}
TEST_P(InteractionSequenceTest,
FailIfSecondElementBecomesHiddenBeforeActivation) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element2.Hide());
}
TEST_P(InteractionSequenceTest,
FailIfFirstElementBecomesHiddenBeforeCustomEvent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
1, &element1, element1.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::kElementHiddenDuringStep,
testing::_,
testing::ElementsAre(testing::Optional(test::SequenceAbortedMatcher(
2, testing::_, element1.identifier(),
InteractionSequence::StepType::kCustomEvent,
InteractionSequence::AbortedReason::kSequenceDestroyed))))),
element1.Hide());
}
TEST_P(InteractionSequenceTest, NoInitialElementTransitionsOnActivation) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetMustBeVisibleAtStart(false)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element), completed,
Run, element.Activate());
}
TEST_P(InteractionSequenceTest, NoInitialElementTransitionsOnCustomEvent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element.context())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element), completed,
Run,
element.SendCustomEvent(kCustomEventType1));
}
TEST_P(InteractionSequenceTest, NoInitialElementTransitionsOnShown) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element(kTestIdentifier1, kTestContext1);
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element), completed,
Run, element.Show());
}
TEST_P(InteractionSequenceTest, StepEndCallbackCalled) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step_end);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step_start.Get())
.SetEndCallback(step_end.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step_start, Run(sequence.get(), &element),
step_end, Run(&element), completed, Run,
element.Activate());
}
TEST_P(InteractionSequenceTest, StepEndCallbackCalledForInitialStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element, step_start.Get(), step_end.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2.Get())
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step_start, Run, sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step_end, Run(&element), step2,
Run(sequence.get(), &element), completed, Run,
element.Activate());
}
TEST_P(InteractionSequenceTest, MultipleStepsComplete) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1_start.Get())
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step1_end, Run, step2_start, Run,
element2.Activate());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2_end, Run, completed, Run,
element3.Show());
}
TEST_P(InteractionSequenceTest, MultipleStepsWithImmediateTransition) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
element3.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1_start.Get())
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run, element2.Show());
// Since element3 is already visible, we skip straight to the end.
{
testing::InSequence in_sequence;
EXPECT_CALL(step1_end, Run).Times(1);
EXPECT_CALL(step2_start, Run).Times(1);
EXPECT_CALL(step2_end, Run).Times(1);
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Activate());
}
element2.Activate();
}
TEST_P(InteractionSequenceTest, CancelMidSequenceWhenViewHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1_start.Get())
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetDescription(kStepDescription)
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
// Specify that this element must remain visible:
.SetMustRemainVisible(true)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step1_end, Run, step2_start, Run,
element2.Activate());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(
step2_end, Run, aborted,
Run(test::SequenceAbortedMatcher(
3, testing::_, element2.identifier(),
InteractionSequence::StepType::kActivated,
InteractionSequence::AbortedReason::kElementHiddenDuringStep,
std::string(kStepDescription))),
element2.Hide());
}
TEST_P(InteractionSequenceTest, DontCancelIfViewDoesNotNeedToRemainVisible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1_start.Get())
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
// Specify that this element need not remain visible:
.SetMustRemainVisible(false)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step1_end, Run, step2_start, Run,
element2.Activate());
element2.Hide();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2_end, Run, completed, Run,
element3.Show());
}
TEST_P(InteractionSequenceTest,
MultipleSequencesInDifferentContextsOneCompletes) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step.Get())
.Build())
.Build();
auto sequence2 =
Builder()
.SetAbortedCallback(aborted2.Get())
.SetCompletedCallback(completed2.Get())
.AddStep(InteractionSequence::WithInitialElement(&element2))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2.Get())
.Build())
.Build();
sequence->Start();
sequence2->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element1), completed,
Run, element1.Activate());
EXPECT_ASYNC_CALL_IN_SCOPE(aborted2, Run, element2.Hide());
}
TEST_P(InteractionSequenceTest,
MultipleSequencesInDifferentContextsBothComplete) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step.Get())
.Build())
.Build();
auto sequence2 =
Builder()
.SetAbortedCallback(aborted2.Get())
.SetCompletedCallback(completed2.Get())
.AddStep(InteractionSequence::WithInitialElement(&element2))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2.Get())
.Build())
.Build();
sequence->Start();
sequence2->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element1), completed,
Run, element1.Activate());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2, Run(sequence2.get(), &element2),
completed2, Run, element2.Activate());
}
// These tests verify that events sent during callbacks (as might be used by an
// interactive UI test powered by an InteractionSequence) do not break the
// sequence.
TEST_P(InteractionSequenceTest, ShowDuringCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.Show();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step2_start.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_end, Run, step2_start, Run, completed,
Run, element1.Activate());
}
TEST_P(InteractionSequenceTest, HideDuringCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.Hide();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetStartCallback(step2_start.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_end, Run, step2_start, Run, completed,
Run, element1.Activate());
}
TEST_P(InteractionSequenceTest, ActivateDuringCallbackDifferentView) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.Activate();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_end, Run, step2_start, Run, completed,
Run, element1.Activate());
}
TEST_P(InteractionSequenceTest, ActivateDuringCallbackSameView) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.Activate();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_end, Run, step2_start, Run, completed,
Run, element2.Show());
}
TEST_P(InteractionSequenceTest, CustomEventDuringCallbackDifferentView) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.SendCustomEvent(kCustomEventType1);
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step2_start.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_end, Run, step2_start, Run, completed,
Run, element1.Activate());
}
TEST_P(InteractionSequenceTest, CustomEventDuringCallbackSameView) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.SendCustomEvent(kCustomEventType1);
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step2_start.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_end, Run, step2_start, Run, completed,
Run, element2.Show());
}
TEST_P(InteractionSequenceTest, ElementHiddenDuringElementShownCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto hide_element = [&](InteractionSequence*, TrackedElement*) {
element2.Hide();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(
base::BindLambdaForTesting(std::move(hide_element)))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
TEST_P(InteractionSequenceTest, HideAfterActivateDoesntAbort) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
element3.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.Activate();
element2.Hide();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
TEST_P(InteractionSequenceTest, HideAfterCustomEventDoesntAbort) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
element3.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.SendCustomEvent(kCustomEventType1);
element2.Hide();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
TEST_P(InteractionSequenceTest, HideUnnamedElementAfterCustomEventDoesntAbort) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
element3.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.SendCustomEvent(kCustomEventType1);
element2.Hide();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
TEST_P(InteractionSequenceTest, HideDuringStepStartedCallbackAborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.Hide();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element2.Show());
}
TEST_P(InteractionSequenceTest, HideDuringStepEndedCallbackAborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](TrackedElement*) { element2.Hide(); };
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element1, InteractionSequence::StepStartCallback(),
base::BindLambdaForTesting(std::move(callback))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(true)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
HideDuringStepStartedCallbackBeforeCustomEventAborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.Hide();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(
base::BindLambdaForTesting(std::move(callback)))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element2.Show());
}
TEST_P(InteractionSequenceTest,
HideDuringStepEndedCallbackBeforeCustomEventAborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](TrackedElement*) { element2.Hide(); };
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element1, InteractionSequence::StepStartCallback(),
base::BindLambdaForTesting(std::move(callback))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(true)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest, ElementHiddenDuringFinalStepStart) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](InteractionSequence*, TrackedElement*) {
element2.Hide();
};
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting(callback))
.SetEndCallback(step_end.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step_end, Run(nullptr), completed, Run,
element2.Show());
}
TEST_P(InteractionSequenceTest, ElementHiddenDuringFinalStepEnd) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](TrackedElement*) { element2.Hide(); };
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustRemainVisible(false)
.SetEndCallback(base::BindLambdaForTesting(callback))
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
TEST_P(InteractionSequenceTest, ElementHiddenDuringStepEndDuringAbort) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto callback = [&](TrackedElement*) { element2.Hide(); };
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetEndCallback(base::BindLambdaForTesting(callback))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetDescription(kStepDescription)
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
element2.Show();
// First parameter will be null because during the delete the step end
// callback will hide the element, which happens before the abort callback is
// called.
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
3, nullptr, element2.identifier(),
InteractionSequence::StepType::kActivated,
InteractionSequence::AbortedReason::kSequenceDestroyed,
std::string(kStepDescription))),
sequence.reset());
}
TEST_P(InteractionSequenceTest,
SequenceDestroyedDuringInitialStepStartCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
auto callback = [&](InteractionSequence*, TrackedElement*) {
sequence.reset();
};
sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element1, base::BindLambdaForTesting(callback), step1_end.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step1_end, Run, aborted, Run,
sequence->Start());
}
TEST_P(InteractionSequenceTest, SequenceDestroyedDuringInitialStepEndCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
auto callback = [&](TrackedElement*) { sequence.reset(); };
sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element1, InteractionSequence::StepStartCallback(),
base::BindLambdaForTesting(callback)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element1.Activate());
}
TEST_P(InteractionSequenceTest, SequenceDestroyedDuringInitialStepAbort) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
auto callback = [&](const InteractionSequence::AbortedData&) {
sequence.reset();
};
sequence =
Builder()
.SetAbortedCallback(base::BindLambdaForTesting(callback))
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element1, step1_start.Get(), step1_end.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run, sequence->Start());
EXPECT_ASYNC_CALL_IN_SCOPE(step1_end, Run, element1.Hide());
EXPECT_FALSE(sequence);
}
TEST_P(InteractionSequenceTest, SequenceDestroyedDuringMidSequenceStepStart) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
auto callback = [&](InteractionSequence*, TrackedElement*) {
sequence.reset();
};
sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(base::BindLambdaForTesting(callback))
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step1_end, Run, aborted, Run, element2.Show());
EXPECT_FALSE(sequence);
}
TEST_P(InteractionSequenceTest, SequenceDestroyedDuringMidSequenceStepEnd) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
auto callback = [&](TrackedElement*) { sequence.reset(); };
sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1_start.Get())
.SetEndCallback(base::BindLambdaForTesting(callback))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run, element2.Show());
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element2.Activate());
EXPECT_FALSE(sequence);
}
TEST_P(InteractionSequenceTest, SequenceDestroyedDuringMidSequenceAbort) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
auto callback = [&](const InteractionSequence::AbortedData&) {
sequence.reset();
};
sequence =
Builder()
.SetAbortedCallback(base::BindLambdaForTesting(callback))
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1_start.Get())
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run, element2.Show());
EXPECT_ASYNC_CALL_IN_SCOPE(step1_end, Run, element2.Hide());
EXPECT_FALSE(sequence);
}
TEST_P(InteractionSequenceTest, SequenceDestroyedDuringFinalStepEnd) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
auto callback = [&](TrackedElement*) { sequence.reset(); };
sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1_start.Get())
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.SetEndCallback(base::BindLambdaForTesting(callback))
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_end, Run, step2_start, Run, completed,
Run, element2.Activate());
EXPECT_FALSE(sequence);
}
TEST_P(InteractionSequenceTest, SequenceDestroyedDuringCompleted) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
auto callback = [&]() { sequence.reset(); };
sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(base::BindLambdaForTesting(callback))
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1_start.Get())
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get())
.SetEndCallback(step2_end.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_end, Run, step2_start, Run, step2_end,
Run, element2.Activate());
EXPECT_FALSE(sequence);
}
TEST_P(InteractionSequenceTest, SimulateTestTimeout) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
auto delete_sequence =
base::BindLambdaForTesting([&]() { sequence.reset(); });
sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetStartCallback(base::BindOnce(
[](base::OnceClosure cb) {
base::SingleThreadTaskRunner::GetCurrentDefault()
->PostTask(FROM_HERE, std::move(cb));
},
std::move(delete_sequence))))
.AddStep(
InteractionSequence::StepBuilder().SetElementID(kTestIdentifier2))
.Build();
// Should indicate step that was queued and waiting, not the step that
// succeeded.
EXPECT_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
2, nullptr, kTestIdentifier2, InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::kSequenceDestroyed)),
sequence->RunSynchronouslyForTesting());
}
// Transition during step callback tests for show and hide events.
// These are tricky to get right, so all of the variations must be tested.
TEST_P(InteractionSequenceTest, HideDuringStepTransitionSameElement) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetStartCallback(base::BindLambdaForTesting(
[&]() { element1.Hide(); })))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kHidden)
.SetTransitionOnlyOnEvent(true))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
HideDuringStepTransitionSameElementVisibilityBlinks) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetStartCallback(base::BindLambdaForTesting([&]() {
element1.Hide();
element1.Show();
})))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kHidden)
.SetTransitionOnlyOnEvent(true))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
HideDuringStepTransitionDifferentElementSameID) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext1);
element1.Show();
element2.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetStartCallback(base::BindLambdaForTesting(
[&]() { element2.Hide(); })))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kHidden)
.SetTransitionOnlyOnEvent(true))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(
InteractionSequenceTest,
HideDuringStepTransitionDifferentElementSameIDSameElementVisibilityBlinks) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext1);
element1.Show();
element2.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetStartCallback(base::BindLambdaForTesting([&]() {
element2.Hide();
element2.Show();
})))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kHidden)
.SetTransitionOnlyOnEvent(true))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest, HideDuringStepTransitionDifferentID) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetStartCallback(base::BindLambdaForTesting(
[&]() { element2.Hide(); })))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier2)
.SetType(InteractionSequence::StepType::kHidden)
.SetTransitionOnlyOnEvent(true))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
HideDuringStepTransitionDifferentIDVisibilityBlinks) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetStartCallback(base::BindLambdaForTesting([&]() {
element2.Hide();
element2.Show();
})))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier2)
.SetType(InteractionSequence::StepType::kHidden)
.SetTransitionOnlyOnEvent(true))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
ShowDuringStepTransitionSameElementTransitionOnlyOnEvent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetStartCallback(base::BindLambdaForTesting([&]() {
element1.Hide();
element1.Show();
})))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kShown)
.SetTransitionOnlyOnEvent(true))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
ShowDuringStepTransitionSameElementDoesNotNeedToRemainVisible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting([&]() {
element1.Hide();
element1.Show();
element1.Hide();
})))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kShown)
.SetMustRemainVisible(false))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
ShowDuringStepTransitionSameIDTransitionOnlyOnEvent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting(
[&]() { element2.Show(); })))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kShown)
.SetTransitionOnlyOnEvent(true))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
ShowDuringStepTransitionSameIDDoesNotNeedToRemainVisible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting([&]() {
element2.Show();
element2.Hide();
})))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kShown)
.SetMustRemainVisible(false))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
ShowDuringStepTransitionDifferentIDTransitionOnlyOnEvent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting(
[&]() { element2.Show(); })))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier2)
.SetType(InteractionSequence::StepType::kShown)
.SetTransitionOnlyOnEvent(true))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
ShowDuringStepTransitionDifferentIDDoesNotNeedToRemainVisible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetMustRemainVisible(false)
.SetStartCallback(base::BindLambdaForTesting([&]() {
element2.Show();
element2.Hide();
})))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier2)
.SetType(InteractionSequence::StepType::kShown)
.SetMustRemainVisible(false))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
ShowDuringStepTransitionDoesNotAbortAfterTrigger) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetStartCallback(base::BindLambdaForTesting([&]() {
element2.Show();
element1.Hide();
})))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier2)
.SetType(InteractionSequence::StepType::kShown)
.SetMustRemainVisible(false))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
// Bait-and-switch tests - verify that when an element must start visible and
// there are multiple such elements, it's okay if any of them receive the
// following event.
TEST_P(InteractionSequenceTest, BaitAndSwitchActivation) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
sequence->Start();
element3.Show();
element2.Hide();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element3.Activate());
}
TEST_P(InteractionSequenceTest, BaitAndSwitchActivationFails) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, {
// By hiding before showing the other element, there are no visible
// elements.
element2.Hide();
element3.Show();
element3.Activate();
});
}
TEST_P(InteractionSequenceTest, BaitAndSwitchActivationDuringStepTransition) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step_start =
base::BindLambdaForTesting([&](InteractionSequence*, TrackedElement*) {
element3.Show();
element2.Hide();
element3.Activate();
});
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element1, std::move(step_start)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
BaitAndSwitchActivationDuringStepTransitionEventuallyConsistent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step_start =
base::BindLambdaForTesting([&](InteractionSequence*, TrackedElement*) {
// By hiding before showing the other element, there are no visible
// elements. However, because the system becomes consistent before the
// end of the callback and the event is triggered, we are allowed to
// proceed. This is technically incorrect behavior but adds a bit of
// forgiveness into the system if visibility of elements is updated in
// arbitrary order.
element2.Hide();
element3.Show();
element3.Activate();
});
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element1, std::move(step_start)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest, BaitAndSwitchCustomEvent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
sequence->Start();
element3.Show();
element2.Hide();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run,
element3.SendCustomEvent(kCustomEventType1));
}
TEST_P(InteractionSequenceTest, BaitAndSwitchCustomEventFails) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
sequence->Start();
// By hiding before showing the other element, there are no visible elements.
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, {
element2.Hide();
element3.Show();
element3.SendCustomEvent(kCustomEventType1);
});
}
TEST_P(InteractionSequenceTest, BaitAndSwitchCustomEventDuringStepTransition) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step_start =
base::BindLambdaForTesting([&](InteractionSequence*, TrackedElement*) {
element3.Show();
element2.Hide();
element3.SendCustomEvent(kCustomEventType1);
});
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element1, std::move(step_start)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
BaitAndSwitchCustomEventDuringStepTransitionEventuallyConsistent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step_start =
base::BindLambdaForTesting([&](InteractionSequence*, TrackedElement*) {
// By hiding before showing the other element, there are no visible
// elements. However, because the system becomes consistent before the
// end of the callback and the event is triggered, we are allowed to
// proceed. This is technically incorrect behavior but adds a bit of
// forgiveness into the system if visibility of elements is updated in
// arbitrary order.
element2.Hide();
element3.Show();
element3.SendCustomEvent(kCustomEventType1);
});
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(
&element1, std::move(step_start)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
// Test step default values:
TEST_P(InteractionSequenceTest,
MustBeVisibleAtStart_DefaultsToTrueForActivated) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
auto sequence =
Builder()
.SetContext(element1.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetEndCallback(step2_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(
step1_end, Run, step2_end, Run, aborted,
Run(test::SequenceAbortedMatcher(
3, nullptr, element3.identifier(),
InteractionSequence::StepType::kActivated,
InteractionSequence::AbortedReason::kElementNotVisibleAtStartOfStep)),
element1.Show());
}
TEST_P(InteractionSequenceTest,
MustBeVisibleAtStart_DefaultsToTrueForCustomEventIfElementIdSet) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
auto sequence =
Builder()
.SetContext(element1.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetEndCallback(step2_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(
step1_end, Run, step2_end, Run, aborted,
Run(test::SequenceAbortedMatcher(
3, nullptr, element3.identifier(),
InteractionSequence::StepType::kCustomEvent,
InteractionSequence::AbortedReason::kElementNotVisibleAtStartOfStep)),
element1.Show());
}
TEST_P(InteractionSequenceTest,
MustBeVisibleAtStart_DefaultsToFalseForCustomEventWithUnnamedElement) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step1_end);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepEndCallback, step2_end);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
auto sequence =
Builder()
.SetContext(element1.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetEndCallback(step1_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetEndCallback(step2_end.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_end, Run, element1.Show());
element3.Show();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2_end, Run, completed, Run,
element3.SendCustomEvent(kCustomEventType1));
}
TEST_P(InteractionSequenceTest,
MustRemainVisible_DefaultsBasedOnCurrentAndNextStep_Activation) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step3);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step4);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step5);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetContext(element1.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
// Shown followed by hidden defaults to must_remain_visible = false.
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetStartCallback(step2.Get())
.Build())
// Activated step defaults to false.
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetMustBeVisibleAtStart(false)
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step3.Get())
.Build())
// Shown followed by activated defaults to true.
// (We will fail the sequence on this step.)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step4.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step5.Get())
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
EXPECT_ASYNC_CALL_IN_SCOPE(step2, Run, element1.Hide());
element2.Show();
EXPECT_ASYNC_CALL_IN_SCOPE(step3, Run, element2.Activate());
EXPECT_ASYNC_CALL_IN_SCOPE(step4, Run, element3.Show());
// Fail step four.
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
4, &element3, element3.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::kElementHiddenDuringStep)),
element3.Hide());
}
TEST_P(InteractionSequenceTest,
MustRemainVisible_DefaultsBasedOnCurrentAndNextStep_CustomEvents) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step3);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step4);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step5);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetContext(element1.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
// Shown followed by hidden defaults to must_remain_visible = false.
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetStartCallback(step2.Get())
.Build())
// Custom event step defaults to false.
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetMustBeVisibleAtStart(false)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step3.Get())
.Build())
// Shown followed by activated defaults to true.
// (We will fail the sequence on this step.)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step4.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType2)
.SetStartCallback(step5.Get())
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
EXPECT_ASYNC_CALL_IN_SCOPE(step2, Run, element1.Hide());
element2.Show();
EXPECT_ASYNC_CALL_IN_SCOPE(step3, Run,
element2.SendCustomEvent(kCustomEventType1));
EXPECT_ASYNC_CALL_IN_SCOPE(step4, Run, element3.Show());
// Fail step four.
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
4, &element3, element3.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::kElementHiddenDuringStep)),
element3.Hide());
}
TEST_P(InteractionSequenceTest,
MustRemainVisible_DefaultsBasedOnCurrentAndNextStep_Reshow) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step3);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step4);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetContext(element1.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
// Shown followed by reshow defaults to must_remain_visible = false.
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetTransitionOnlyOnEvent(true)
.SetStartCallback(step2.Get())
.Build())
// Shown followed by different element defaults to true.
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step3.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetTransitionOnlyOnEvent(true)
.SetStartCallback(step4.Get())
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
element1.Hide();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2, Run, step3, Run, element1.Show());
// Break the sequence at step 3.
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
3, testing::_, element1.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::kElementHiddenDuringStep)),
element1.Hide());
}
// SetTransitionOnlyOnEvent tests:
TEST_P(InteractionSequenceTest,
SetTransitionOnlyOnEvent_TransitionsOnDifferentElementShown) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step_start);
// Two elements have the same identifier, but only the first is visible.
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetTransitionOnlyOnEvent(true)
.SetStartCallback(step_start.Get())
.Build())
.Build();
sequence->Start();
// Fail step four.
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step_start, Run, completed, Run,
element2.Show());
}
TEST_P(InteractionSequenceTest,
SetTransitionOnlyOnEvent_TransitionsOnSameElementShown) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetContext(element1.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(true)
.SetMustRemainVisible(false)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetTransitionOnlyOnEvent(true)
.SetStartCallback(step_start.Get())
.Build())
.Build();
sequence->Start();
element1.Hide();
// Fail step four.
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step_start, Run, completed, Run,
element1.Show());
}
TEST_P(InteractionSequenceTest,
SetTransitionOnlyOnEvent_TransitionsOnElementHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetTransitionOnlyOnEvent(true)
.SetStartCallback(step_start.Get())
.Build())
.Build();
sequence->Start();
element2.Show();
// Fail step four.
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step_start, Run, completed, Run,
element2.Hide());
}
// Named element tests:
TEST_P(InteractionSequenceTest, CanNameElementInAnyContext) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(
InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(base::BindLambdaForTesting(
[&element2](InteractionSequence* seq, TrackedElement*) {
seq->NameElement(&element2, kElementName1);
}))
.SetContext(InteractionSequence::ContextMode::kAny)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.SetContext(InteractionSequence::ContextMode::kAny)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementShown_NamedBeforeSequenceStarts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->NameElement(&element2, kElementName1);
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementShown_NamedDuringStepCallback_SameIdentifier) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext1);
auto step1_start = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
EXPECT_EQ(&element1, element);
sequence->NameElement(&element2, kElementName1);
});
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->NameElement(&element2, kElementName1);
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element2), completed,
Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementShown_NamedDuringStepCallback_DifferentIdentifiers) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
auto step1_start = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
EXPECT_EQ(&element1, element);
sequence->NameElement(&element2, kElementName1);
});
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->NameElement(&element2, kElementName1);
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step, Run(sequence.get(), &element2), completed,
Run, sequence->Start());
}
TEST_P(InteractionSequenceTest, NameElement_ElementShown_FirstElementNamed) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->NameElement(&element1, kElementName1);
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementShown_MultipleNamedElements) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1_start);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2_start);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step1_start.Get())
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName2)
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step2_start.Get())
.Build())
.Build();
sequence->NameElement(&element1, kElementName1);
sequence->NameElement(&element2, kElementName2);
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_start, Run(sequence.get(), &element1),
step2_start, Run(sequence.get(), &element2),
completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementShown_DisappearsBeforeSequenceStart) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->NameElement(&element1, kElementName1);
element1.Hide();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
1, nullptr, element1.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::kElementNotVisibleAtStartOfStep)),
sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementShown_DisappearsBeforeStepAbortsTheSequence) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->NameElement(&element2, kElementName1);
sequence->Start();
element2.Hide();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
3, nullptr, element2.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::kElementNotVisibleAtStartOfStep)),
element1.Activate());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementShown_RespectsMustRemainVisibleFalse) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(step.Get())
.SetMustRemainVisible(false)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->NameElement(&element2, kElementName1);
EXPECT_ASYNC_CALL_IN_SCOPE(step, Run, sequence->Start());
element2.Hide();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element1.Activate());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementActivated_NamedBeforeSequenceStarts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->NameElement(&element1, kElementName1);
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element1.Activate());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementActivated_NamedBeforeSequenceStarts_AbortsIfHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->NameElement(&element1, kElementName1);
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element1.Hide());
}
TEST_P(InteractionSequenceTest, NameElement_ElementActivated_NamedDuringStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Activate());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementActivated_NamedDuringStep_AbortsIfHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element2.Hide());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementActivated_NamedAndActivatedDuringStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
element2.Activate();
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementActivated_NamedAndHiddenDuringStep_Aborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
element2.Hide();
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementActivated_NamedActivatedAndHiddenDuringStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
element2.Activate();
element2.Hide();
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_CustomEvent_NamedBeforeSequenceStarts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->NameElement(&element1, kElementName1);
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run,
element1.SendCustomEvent(kCustomEventType1));
}
TEST_P(InteractionSequenceTest,
NameElement_CustomEvent_NamedBeforeSequenceStarts_AbortsIfHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->NameElement(&element1, kElementName1);
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element1.Hide());
}
TEST_P(InteractionSequenceTest, NameElement_CustomEvent_NamedDuringStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run,
element2.SendCustomEvent(kCustomEventType1));
}
TEST_P(InteractionSequenceTest,
NameElement_CustomEvent_NamedDuringStep_AbortsIfHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element2.Hide());
}
TEST_P(InteractionSequenceTest,
NameElement_CustomEvent_NamedAndActivatedDuringStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
element2.SendCustomEvent(kCustomEventType1);
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_CustomEvent_NamedAndHiddenDuringStep_Aborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
element2.Hide();
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_CustomEvent_NamedActivatedAndHiddenDuringStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
element2.SendCustomEvent(kCustomEventType1);
element2.Hide();
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementHidden_NamedBeforeSequenceAndHiddenBeforeSequence) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kHidden)
.Build())
.Build();
sequence->NameElement(&element2, kElementName1);
element2.Hide();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementHidden_NamedBeforeSequenceAndHiddenDuringSequence) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kHidden)
.Build())
.Build();
sequence->NameElement(&element2, kElementName1);
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Hide());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementHidden_NamedDuringCallbackAndHiddenDuringSequence) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kHidden)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Hide());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementHidden_NamedAndHiddenDuringCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto step = base::BindLambdaForTesting(
[&](InteractionSequence* sequence, TrackedElement* element) {
sequence->NameElement(&element2, kElementName1);
element2.Hide();
});
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1,
std::move(step)))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kHidden)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest, NameElement_ElementHidden_NoElementExplicitly) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kHidden)
.Build())
.Build();
sequence->NameElement(nullptr, kElementName1);
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementShown_NoElementExplicitly_Aborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
sequence->NameElement(nullptr, kElementName1);
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_ElementActivated_NoElementExplicitly_Aborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->NameElement(nullptr, kElementName1);
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
NameElement_TwoSequencesWithSameElementWithDifferentNames) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
element2.Show();
auto sequence1 =
Builder()
.SetAbortedCallback(aborted1.Get())
.SetCompletedCallback(completed1.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
auto sequence2 =
Builder()
.SetAbortedCallback(aborted2.Get())
.SetCompletedCallback(completed2.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName2)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence1->NameElement(&element2, kElementName1);
sequence2->NameElement(&element2, kElementName2);
sequence1->Start();
sequence2->Start();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(completed1, Run, completed2, Run,
element2.Activate());
}
// RunSynchronouslyForTesting() tests:
TEST_P(InteractionSequenceTest,
RunSynchronouslyForTesting_SequenceAbortsDuringStart) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
std::unique_ptr<InteractionSequence> sequence;
sequence = Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(aborted, Run, sequence->RunSynchronouslyForTesting());
}
TEST_P(InteractionSequenceTest,
RunSynchronouslyForTesting_SequenceCompletesDuringStart) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
sequence = Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(true)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
TEST_P(InteractionSequenceTest,
RunSynchronouslyForTesting_SequenceAbortsDuringStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustRemainVisible(true)
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence*, TrackedElement*) {
base::SingleThreadTaskRunner::GetCurrentDefault()
->PostTask(FROM_HERE,
base::BindLambdaForTesting(
[&]() { element1.Hide(); }));
}))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(aborted, Run, sequence->RunSynchronouslyForTesting());
}
TEST_P(InteractionSequenceTest,
RunSynchronouslyForTesting_SequenceCompletesDuringStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
std::unique_ptr<InteractionSequence> sequence;
sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence*, TrackedElement*) {
base::SingleThreadTaskRunner::GetCurrentDefault()
->PostTask(FROM_HERE,
base::BindLambdaForTesting(
[&]() { element2.Show(); }));
}))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
TEST_P(InteractionSequenceTest, AddStepWithUnBuildStepBuilder) {
auto sequence =
Builder()
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier1)
.SetType(InteractionSequence::StepType::kActivated))
.Build();
sequence.reset();
}
// Element shown in any context tests.
TEST_P(InteractionSequenceTest, StartsOnElementShownInAnyContext) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext2);
element.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(InteractionSequence::ContextMode::kAny)
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest, TransitionsOnElementShownInAnyContext) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element1.Show();
element2.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetContext(InteractionSequence::ContextMode::kAny)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
ElementShownInAnyContextMustBeVisibleAtStartFirstStepAborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext2);
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(InteractionSequence::ContextMode::kAny)
.SetMustBeVisibleAtStart(true)
.SetType(InteractionSequence::StepType::kShown)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest,
ElementShownInAnyContextMustBeVisibleAtStartLaterStepAborts) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(true)
.SetContext(InteractionSequence::ContextMode::kAny)
.Build())
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, sequence->Start());
}
TEST_P(InteractionSequenceTest, ElementShownInAnyContextTransitionOnEvent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetTransitionOnlyOnEvent(true)
.SetContext(InteractionSequence::ContextMode::kAny)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
TEST_P(InteractionSequenceTest, ElementShownInAnyContextAssignNameAndActivate) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* sequence,
TrackedElement* element) {
sequence->NameElement(element, kElementName1);
}))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
element2.Show();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Activate());
}
TEST_P(InteractionSequenceTest,
ElementShownInAnyContextAssignNameAndSendCustomEvent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* sequence,
TrackedElement* element) {
sequence->NameElement(element, kElementName1);
}))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->Start();
element2.Show();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run,
element2.SendCustomEvent(kCustomEventType1));
}
TEST_P(InteractionSequenceTest,
ElementShownInAnyContextActivateDuringCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* sequence,
TrackedElement* element) {
sequence->NameElement(element, kElementName1);
element2.Activate();
}))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
TEST_P(InteractionSequenceTest,
ElementShownInAnyContextSendCustomEventDuringCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* sequence,
TrackedElement* element) {
sequence->NameElement(element, kElementName1);
element2.SendCustomEvent(kCustomEventType1);
}))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
TEST_P(InteractionSequenceTest,
ElementShownInAnyContextActivateAndHideDuringCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* sequence,
TrackedElement* element) {
sequence->NameElement(element, kElementName1);
element2.Activate();
element2.Hide();
}))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kActivated)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
TEST_P(InteractionSequenceTest,
ElementShownInAnyContextSendCustomEventAndHideDuringCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element1.Show();
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(kTestContext1)
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kShown)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* sequence,
TrackedElement* element) {
sequence->NameElement(element, kElementName1);
element2.SendCustomEvent(kCustomEventType1);
element2.Hide();
}))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
// Elements destroyed by external code during callbacks.
TEST_P(InteractionSequenceTest, DestroyElementDuringShow) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
auto subscription =
ElementTracker::GetElementTracker()->AddElementShownCallback(
element1.identifier(), element1.context(),
base::BindLambdaForTesting(
[&](TrackedElement*) { element1.Hide(); }));
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetContext(element1.context())
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kShown)
.SetElementID(element1.identifier())
.Build())
.Build();
sequence->Start();
// This show will be pre-empted by the initial subscription we added which
// hides the element.
element1.Show();
// Remove the callback that hides the element.
subscription = ElementTracker::Subscription();
// Now the sequence should work.
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element1.Show());
}
TEST_P(InteractionSequenceTest, DestroyElementDuringActivate) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
auto subscription =
ElementTracker::GetElementTracker()->AddElementActivatedCallback(
element1.identifier(), element1.context(),
base::BindLambdaForTesting(
[&](TrackedElement*) { element1.Hide(); }));
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetContext(element1.context())
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kShown)
.SetElementID(element1.identifier())
.SetMustBeVisibleAtStart(false)
.SetMustRemainVisible(false)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kActivated)
.SetElementID(element1.identifier())
.SetMustBeVisibleAtStart(false)
.Build())
.Build();
sequence->Start();
element1.Show();
// This activate will be pre-empted by the initial subscription we added which
// hides the element.
element1.Activate();
// Remove the callback that hides the element.
subscription = ElementTracker::Subscription();
// Now the sequence should work.
element1.Show();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element1.Activate());
}
TEST_P(InteractionSequenceTest, DestroyedElementDuringNestedEvents) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
std::unique_ptr<InteractionSequence> sequence =
Builder()
.SetContext(element1.context())
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kShown)
.SetElementID(element1.identifier())
.SetMustBeVisibleAtStart(false)
.SetMustRemainVisible(false)
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kActivated)
.SetElementID(element1.identifier())
.SetMustBeVisibleAtStart(false)
.Build())
.Build();
// The first step will register its listener first.
sequence->Start();
// Now we can register a couple of chained listeners.
auto subscription2 =
ElementTracker::GetElementTracker()->AddElementActivatedCallback(
element1.identifier(), element1.context(),
base::BindLambdaForTesting(
[&](TrackedElement*) { element1.Hide(); }));
auto subscription =
ElementTracker::GetElementTracker()->AddElementShownCallback(
element1.identifier(), element1.context(),
base::BindLambdaForTesting(
[&](TrackedElement*) { element1.Activate(); }));
// This will transition the first step, and activate the second callback, but
// it will be pre-empted by the second subscription.
element1.Show();
// Remove the callback that hides the element.
subscription2 = ElementTracker::Subscription();
// Now the sequence should work.
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element1.Show());
}
TEST_P(InteractionSequenceTest, StepStartEndConvenienceMethods) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(TrackedElement*)>,
step1_start);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step1_end);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step2_start);
test::TestElement element(kTestIdentifier1, kTestContext1);
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kShown)
.SetMustBeVisibleAtStart(false)
.SetStartCallback(step1_start.Get())
.SetEndCallback(step1_end.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2_start.Get()))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1_start, Run(&element), element.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1_end, Run, step2_start, Run, completed,
Run, element.Activate());
}
// Fail for testing tests.
TEST_P(InteractionSequenceTest, FailForTestingBetweenSteps) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element.context())
.AddStep(InteractionSequence::StepBuilder().SetElementID(
element.identifier()))
.AddStep(InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kActivated)
.SetElementID(element.identifier()))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
1, &element, element.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::kFailedForTesting)),
sequence->FailForTesting());
}
TEST_P(InteractionSequenceTest, FailForTestingOnLastStepCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.SetContext(element.context())
.AddStep(InteractionSequence::StepBuilder().SetElementID(
element.identifier()))
.AddStep(
InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kActivated)
.SetElementID(element.identifier())
.SetStartCallback(base::BindOnce(
[](ui::InteractionSequence* seq, ui::TrackedElement* el) {
seq->FailForTesting();
})))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
2, &element, element.identifier(),
InteractionSequence::StepType::kActivated,
InteractionSequence::AbortedReason::kFailedForTesting)),
element.Activate());
}
// Context-switching and ContextMode tests.
TEST_P(InteractionSequenceTest, ExplicitContextChange) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
element.Show();
element2.Show();
auto sequence = Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(element.context())
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetContext(element2.context())
.SetStartCallback(step2.Get()))
.Build();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(step1, Run(sequence.get(), &element), step2,
Run(sequence.get(), &element2), completed, Run,
sequence->Start());
}
TEST_P(InteractionSequenceTest, InheritContextFromPreviousStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
element.Show();
element2.Show();
auto sequence =
Builder()
.SetContext(element2.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(element.context()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(
InteractionSequence::ContextMode::kFromPreviousStep)
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetContext(element2.context()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetContext(
InteractionSequence::ContextMode::kFromPreviousStep)
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step2.Get()))
.Build();
sequence->Start();
element2.Activate();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, element.Activate());
element.Activate();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2, Run, completed, Run,
element2.Activate());
}
TEST_P(InteractionSequenceTest, InheritContextFromNamedElement) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
constexpr char kOtherElementName[] = "Other Element";
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
test::TestElement element3(kTestIdentifier2, kTestContext2);
element.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(element.context())
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* seq, TrackedElement*) {
seq->NameElement(&element2, kOtherElementName);
})))
.AddStep(InteractionSequence::StepBuilder().SetElementName(
kOtherElementName))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kOtherElementName)
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(step.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetContext(
InteractionSequence::ContextMode::kFromPreviousStep))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step, Run, element2.Activate());
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element3.Show());
}
TEST_P(InteractionSequenceTest,
InheritContextFromNamedElement_TransitionOnEventDuringCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
constexpr char kOtherElementName[] = "Other Element";
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
test::TestElement element3(kTestIdentifier2, kTestContext2);
element.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(element.context())
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* seq, TrackedElement*) {
seq->NameElement(&element2, kOtherElementName);
})))
.AddStep(InteractionSequence::StepBuilder().SetElementName(
kOtherElementName))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kOtherElementName)
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(base::BindLambdaForTesting(
[&]() { element3.Show(); })))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetContext(
InteractionSequence::ContextMode::kFromPreviousStep)
.SetTransitionOnlyOnEvent(true))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Activate());
}
TEST_P(InteractionSequenceTest, AnyContext_TransitionOnEventDuringCallback) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
constexpr char kOtherElementName[] = "Other Element";
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
test::TestElement element3(kTestIdentifier2, kTestContext3);
element.Show();
element2.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(element.context())
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* seq, TrackedElement*) {
seq->NameElement(&element2, kOtherElementName);
})))
.AddStep(InteractionSequence::StepBuilder().SetElementName(
kOtherElementName))
.AddStep(InteractionSequence::StepBuilder()
.SetElementName(kOtherElementName)
.SetType(InteractionSequence::StepType::kActivated)
.SetStartCallback(base::BindLambdaForTesting(
[&]() { element3.Show(); })))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetContext(InteractionSequence::ContextMode::kAny)
.SetTransitionOnlyOnEvent(true))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Activate());
}
// AbortData tests.
TEST_P(InteractionSequenceTest, BuildAbortDataForTimeout) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element.Show();
// The same values should be returned if the sequence times out waiting for
// the first step vs. during the first step start callback. Note that
// `element` will only be set if the step is currently in progress.
auto check_aborted_data =
[](const InteractionSequence* seq, int step_index, ElementIdentifier id,
const TrackedElement* element, const char* description) {
const auto data = seq->BuildAbortedData(
InteractionSequence::AbortedReason::kSequenceTimedOut);
EXPECT_EQ(InteractionSequence::AbortedReason::kSequenceTimedOut,
data.aborted_reason);
EXPECT_EQ(step_index, data.step_index);
EXPECT_EQ(InteractionSequence::StepType::kShown, data.step_type);
EXPECT_EQ(description, data.step_description);
EXPECT_EQ(id, data.element_id);
EXPECT_EQ(element, data.element.get());
};
auto sequence =
Builder()
.SetContext(element.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetDescription(kStepDescription)
.SetStartCallback(base::BindLambdaForTesting(
[&](InteractionSequence* seq, TrackedElement* el) {
// Verify that the correct values are returned if a
// timeout happens during a step start callback.
// Note how the element is set in this case, as a
// step is currently executing.
check_aborted_data(seq, 1, el->identifier(), el,
kStepDescription);
step.Get().Run(seq, el);
})))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetDescription(kStepDescription2))
.Build();
// Verify that the correct values are returned if a timeout happens before the
// first step.
check_aborted_data(sequence.get(), 1, element.identifier(), nullptr,
kStepDescription);
// Verify that the correct values are returned if a timeout happens between
// steps.
EXPECT_ASYNC_CALL_IN_SCOPE(step, Run, sequence->Start());
check_aborted_data(sequence.get(), 2, element2.identifier(), nullptr,
kStepDescription2);
// Complete the sequence.
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Show());
}
// ContextMode::kAny tests.
TEST_P(InteractionSequenceTest, ActivatedInAnyContext) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step3);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
test::TestElement duplicate(kTestIdentifier1, kTestContext2);
element2.Show();
duplicate.Show();
auto sequence =
Builder()
.SetContext(element.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetContext(
InteractionSequence::ContextMode::kFromPreviousStep)
.SetStartCallback(step2.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(InteractionSequence::ContextMode::kAny)
.SetTransitionOnlyOnEvent(true))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(step3.Get())
.SetMustBeVisibleAtStart(true))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, element2.Activate());
EXPECT_ASYNC_CALL_IN_SCOPE(step2, Run, duplicate.Activate());
element.Show();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step3, Run, completed, Run, element.Activate());
}
TEST_P(InteractionSequenceTest, ActivatedInAnyContextDuringStepTransition) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
element2.Show();
auto sequence =
Builder()
.SetContext(element.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(base::BindLambdaForTesting(
[&element2]() { element2.Activate(); }))
.SetTransitionOnlyOnEvent(true))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetContext(InteractionSequence::ContextMode::kAny))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element.Show());
}
TEST_P(InteractionSequenceTest, EventInAnyContext) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step3);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
test::TestElement duplicate(kTestIdentifier1, kTestContext2);
element.Show();
element2.Show();
duplicate.Show();
auto sequence =
Builder()
.SetContext(element.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetContext(
InteractionSequence::ContextMode::kFromPreviousStep)
.SetStartCallback(step2.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType2)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(step3.Get())
.SetMustBeVisibleAtStart(true))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run,
element2.SendCustomEvent(kCustomEventType1));
EXPECT_ASYNC_CALL_IN_SCOPE(step2, Run,
duplicate.SendCustomEvent(kCustomEventType1));
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step3, Run, completed, Run,
element.SendCustomEvent(kCustomEventType2));
}
TEST_P(InteractionSequenceTest, EventInAnyContextDuringStepTransition) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
element2.Show();
auto sequence =
Builder()
.SetContext(element.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetContext(InteractionSequence::ContextMode::kAny)
.SetStartCallback(base::BindLambdaForTesting([&element2]() {
element2.SendCustomEvent(kCustomEventType1);
}))
.SetTransitionOnlyOnEvent(true))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetContext(InteractionSequence::ContextMode::kAny))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element.Show());
}
TEST_P(InteractionSequenceTest,
ActivatedInAnyContextMustBeVisibleFailsIfLastElementIsHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element2.Show();
auto sequence =
Builder()
.SetContext(element.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder().SetElementID(
element.identifier()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetContext(InteractionSequence::ContextMode::kAny))
.Build();
sequence->Start();
element.Show();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted, Run, element2.Hide());
}
TEST_P(
InteractionSequenceTest,
ActivatedInAnyContextMustBeVisibleSucceedsIfLastElementInDefaultContextIsHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext2);
element2.Show();
auto sequence =
Builder()
.SetContext(element.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetMustRemainVisible(false))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kActivated)
.SetContext(InteractionSequence::ContextMode::kAny))
.Build();
sequence->Start();
element.Show();
element.Hide();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Activate());
}
TEST_P(InteractionSequenceTest,
HiddenInAnyContextTransitionsOnNoElementsPresent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
auto sequence =
Builder()
.SetContext(element.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder().SetElementID(
element.identifier()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(kTestIdentifier2)
.SetType(InteractionSequence::StepType::kHidden)
.SetContext(InteractionSequence::ContextMode::kAny))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element.Show());
}
TEST_P(InteractionSequenceTest, HiddenInAnyContextTransitionsOnElementHidden) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
element.Show();
element2.Show();
auto sequence =
Builder()
.SetContext(element.context())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder().SetElementID(
element.identifier()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetContext(InteractionSequence::ContextMode::kAny))
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element2.Hide());
}
TEST_P(InteractionSequenceTest, HiddenInAnyContextTransitionOnlyOnEvent) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element(kTestIdentifier1, kTestContext2);
auto sequence =
Builder()
.SetContext(kTestContext1)
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kHidden)
.SetContext(InteractionSequence::ContextMode::kAny)
.SetMustBeVisibleAtStart(false)
.SetTransitionOnlyOnEvent(true))
.Build();
sequence->Start();
element.Show();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element.Hide());
}
// Subsequence tests.
class InteractionSequenceSubsequenceTest
: public InteractionSequenceTestBase,
public testing::WithParamInterface<
std::tuple<InteractionSequence::StepStartMode,
InteractionSequence::SubsequenceMode>> {
public:
InteractionSequenceSubsequenceTest() = default;
~InteractionSequenceSubsequenceTest() override = default;
static auto DoNotRun() {
return base::BindOnce([](const InteractionSequence*,
const TrackedElement*) { return false; });
}
std::optional<InteractionSequence::StepStartMode> GetMode() const override {
return std::get<InteractionSequence::StepStartMode>(GetParam());
}
InteractionSequence::SubsequenceMode GetSubsequenceMode() const {
return std::get<InteractionSequence::SubsequenceMode>(GetParam());
}
};
TEST_P(InteractionSequenceSubsequenceTest, SubsequenceRuns) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, before);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, after);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(before.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(subsequence.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(after.Get()))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(before, Run, sequence->Start());
EXPECT_ASYNC_CALL_IN_SCOPE(subsequence, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(after, Run, completed, Run, element3.Show());
}
TEST_P(InteractionSequenceSubsequenceTest,
SubsequenceWithFailedConditionSkips) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, before);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, after);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(before.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
// This subsequence will not run.
.AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(subsequence1.Get()))),
DoNotRun())
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(after.Get()))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(before, Run, sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence2, Run, after, Run, completed, Run,
element3.Show());
}
TEST_P(InteractionSequenceSubsequenceTest, SubsequenceDoesNotRun) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, before);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, after);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(before.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(subsequence.Get()))),
DoNotRun()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(after.Get()))
.Build();
switch (GetSubsequenceMode()) {
case InteractionSequence::SubsequenceMode::kAll:
case InteractionSequence::SubsequenceMode::kAtMostOne:
// These will skip the test and then succeed.
EXPECT_ASYNC_CALL_IN_SCOPE(before, Run, sequence->Start());
element2.Show();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(after, Run, completed, Run,
element3.Show());
break;
case InteractionSequence::SubsequenceMode::kAtLeastOne:
case InteractionSequence::SubsequenceMode::kExactlyOne:
// These will fail.
EXPECT_ASYNC_CALLS_IN_SCOPE_2(before, Run, aborted, Run,
sequence->Start());
break;
}
}
TEST_P(InteractionSequenceSubsequenceTest, TwoEligibleSubsequences) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, before);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, after);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(before.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(subsequence1.Get()))))
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(after.Get()))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(before, Run, sequence->Start());
switch (GetSubsequenceMode()) {
case InteractionSequence::SubsequenceMode::kAll:
// Both sequences must complete before the sequence proceeds.
EXPECT_ASYNC_CALL_IN_SCOPE(subsequence1, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence2, Run, after, Run, completed,
Run, element3.Show());
break;
case InteractionSequence::SubsequenceMode::kAtMostOne:
case InteractionSequence::SubsequenceMode::kAtLeastOne:
case InteractionSequence::SubsequenceMode::kExactlyOne:
// Only the first sequence will run, or the second will abort as soon as
// the first completes.
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence1, Run, after, Run, completed,
Run, element2.Show());
break;
}
}
TEST_P(InteractionSequenceSubsequenceTest,
MultipleEligibleSubsequencesSomeIneligible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence3);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step1.Get())
.SetMustRemainVisible(false))
.AddStep(InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
// This subsequence will not run.
.AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(subsequence1.Get()))),
DoNotRun())
// The following two subsequences are identical and will
// call the same callback.
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(subsequence2.Get()))))
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(subsequence2.Get()))))
// This subsequence is unique.
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetType(InteractionSequence::StepType::kHidden)
.SetElementID(element1.identifier())
.SetStartCallback(subsequence3.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(step2.Get()))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
element2.Show();
switch (GetSubsequenceMode()) {
case InteractionSequence::SubsequenceMode::kAll:
// All three enabled sequences must complete before the sequence proceeds.
EXPECT_CALL(subsequence2, Run).Times(2);
element3.Show();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence3, Run, step2, Run, completed,
Run, element1.Hide());
break;
case InteractionSequence::SubsequenceMode::kAtMostOne:
case InteractionSequence::SubsequenceMode::kAtLeastOne:
case InteractionSequence::SubsequenceMode::kExactlyOne:
// In these cases, either only one subsequence will run, or only one
// will complete. Therefore, there should only be one subsequence
// callback.
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence2, Run, step2, Run, completed,
Run, element3.Show());
element1.Hide();
break;
}
}
TEST_P(InteractionSequenceSubsequenceTest, SubsequenceWithMultipleSteps) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step1.Get()))
.AddStep(
InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(std::move(
Builder()
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(subsequence1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step2.Get()))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
EXPECT_ASYNC_CALL_IN_SCOPE(subsequence1, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence2, Run, step2, Run, completed, Run,
element3.Show());
}
TEST_P(InteractionSequenceSubsequenceTest, SubsequenceFailsAtStart) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, before);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, after);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(before.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetMustBeVisibleAtStart(true)
.SetStartCallback(subsequence1.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(after.Get()))
.Build();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(
before, Run, aborted,
Run(test::SequenceAbortedMatcher(
2, nullptr, ElementIdentifier(),
InteractionSequence::StepType::kSubsequence,
InteractionSequence::AbortedReason::kSubsequenceFailed, testing::_,
testing::ElementsAre(testing::Optional(test::SequenceAbortedMatcher(
1, nullptr, element2.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::
kElementNotVisibleAtStartOfStep))))),
sequence->Start());
}
TEST_P(InteractionSequenceSubsequenceTest, SubsequenceFailsInMiddle) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step1.Get()))
.AddStep(
InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(std::move(
Builder()
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(subsequence1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetMustBeVisibleAtStart(true)
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step2.Get()))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(
subsequence1, Run, aborted,
Run(test::SequenceAbortedMatcher(
2, nullptr, ElementIdentifier(),
InteractionSequence::StepType::kSubsequence,
InteractionSequence::AbortedReason::kSubsequenceFailed, testing::_,
testing::ElementsAre(testing::Optional(test::SequenceAbortedMatcher(
2, nullptr, element3.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::
kElementNotVisibleAtStartOfStep))))),
element2.Show());
}
TEST_P(InteractionSequenceSubsequenceTest, FirstFailsSecondSucceeds) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetMustBeVisibleAtStart(true)
.SetStartCallback(subsequence1.Get()))))
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step2.Get()))
.Build();
switch (GetSubsequenceMode()) {
case InteractionSequence::SubsequenceMode::kAll:
case InteractionSequence::SubsequenceMode::kAtMostOne:
case InteractionSequence::SubsequenceMode::kExactlyOne:
// The first failure should scuttle this attempt.
EXPECT_ASYNC_CALLS_IN_SCOPE_2(
step1, Run, aborted,
Run(test::SequenceAbortedMatcher(
2, nullptr, ElementIdentifier(),
InteractionSequence::StepType::kSubsequence,
InteractionSequence::AbortedReason::kSubsequenceFailed,
testing::_,
testing::ElementsAre(
testing::Optional(test::SequenceAbortedMatcher(
1, nullptr, element2.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::
kElementNotVisibleAtStartOfStep)),
testing::Eq(std::nullopt)))),
sequence->Start());
break;
case InteractionSequence::SubsequenceMode::kAtLeastOne:
// As long as one of the sequences succeeds, the step succeeds.
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence2, Run, step2, Run, completed,
Run, element3.Show());
break;
}
}
TEST_P(InteractionSequenceSubsequenceTest, FirstSucceedsSecondFails) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(subsequence1.Get()))))
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetMustBeVisibleAtStart(true)
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step2.Get()))
.Build();
switch (GetSubsequenceMode()) {
case InteractionSequence::SubsequenceMode::kAll:
// The first failure should scuttle this attempt.
EXPECT_ASYNC_CALLS_IN_SCOPE_2(
step1, Run, aborted,
Run(test::SequenceAbortedMatcher(
2, nullptr, ElementIdentifier(),
InteractionSequence::StepType::kSubsequence,
InteractionSequence::AbortedReason::kSubsequenceFailed,
testing::_,
testing::ElementsAre(
testing::Eq(std::nullopt),
testing::Optional(test::SequenceAbortedMatcher(
1, nullptr, element3.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::
kElementNotVisibleAtStartOfStep))))),
sequence->Start());
break;
case InteractionSequence::SubsequenceMode::kAtMostOne:
case InteractionSequence::SubsequenceMode::kExactlyOne:
case InteractionSequence::SubsequenceMode::kAtLeastOne:
// As long as one of the sequences succeeds, the step succeeds.
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence1, Run, step2, Run, completed,
Run, element2.Show());
break;
}
}
TEST_P(InteractionSequenceSubsequenceTest, ElementPassedToCondition) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, before);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, after);
UNCALLED_MOCK_CALLBACK(InteractionSequence::SubsequenceCondition, condition);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
element2.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(before.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(subsequence.Get()))),
condition.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(after.Get()))
.Build();
EXPECT_CALL(condition, Run(sequence.get(), &element2))
.WillOnce(testing::Return(true));
EXPECT_ASYNC_CALL_IN_SCOPE(before, Run, sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence, Run, after, Run, completed, Run,
element3.Show());
}
TEST_P(InteractionSequenceSubsequenceTest,
ElementNotPassedToConditionIfNotVisible) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, before);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, after);
UNCALLED_MOCK_CALLBACK(InteractionSequence::SubsequenceCondition, condition);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(before.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(subsequence.Get()))),
condition.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(after.Get()))
.Build();
EXPECT_CALL(condition, Run(sequence.get(), nullptr))
.WillOnce(testing::Return(true));
EXPECT_ASYNC_CALL_IN_SCOPE(before, Run, sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence, Run, after, Run, completed, Run,
element3.Show());
}
TEST_P(InteractionSequenceSubsequenceTest, TriggerDuringPreviousStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, after);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(base::BindLambdaForTesting(
[&element1](InteractionSequence*, TrackedElement*) {
element1.SendCustomEvent(kCustomEventType1);
})))
.AddStep(InteractionSequence::StepBuilder().AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(subsequence.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(after.Get()))
.Build();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence, Run, after, Run, completed, Run,
sequence->Start());
}
TEST_P(InteractionSequenceSubsequenceTest, SubsequenceAsFirstStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder().AddSubsequence(std::move(
Builder().AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(subsequence.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(step2.Get()))
.Build();
EXPECT_ASYNC_CALLS_IN_SCOPE_2(subsequence, Run, step1, Run,
sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2, Run, completed, Run, element2.Show());
}
TEST_P(InteractionSequenceSubsequenceTest, NestedSubsequenceAsFirstStep) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder().AddSubsequence(std::move(
Builder()
.AddStep(InteractionSequence::StepBuilder().AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(subsequence1.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(step2.Get()))
.Build();
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence2, Run, subsequence1, Run, step1,
Run, sequence->Start());
EXPECT_ASYNC_CALLS_IN_SCOPE_2(step2, Run, completed, Run, element2.Show());
}
TEST_P(InteractionSequenceSubsequenceTest,
NestedSubsequencesTriggeredByEventBeforeStarted) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(base::BindLambdaForTesting(
[&element1](InteractionSequence*, TrackedElement*) {
element1.SendCustomEvent(kCustomEventType1);
})))
.AddStep(InteractionSequence::StepBuilder().AddSubsequence(std::move(
Builder()
.AddStep(InteractionSequence::StepBuilder().AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetType(
InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(subsequence1.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step2.Get()))
.Build();
testing::InSequence in_sequence;
EXPECT_CALL(subsequence2, Run);
EXPECT_CALL(subsequence1, Run);
EXPECT_CALL(step2, Run);
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
TEST_P(InteractionSequenceSubsequenceTest, SubsequenceInSameContext) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step3);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
test::TestElement element3(kTestIdentifier2, kTestContext2);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetContext(element2.context())
.SetStartCallback(step2.Get()))
.AddStep(InteractionSequence::StepBuilder().AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetContext(
InteractionSequence::ContextMode::kFromPreviousStep)
.SetStartCallback(subsequence1.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step3.Get()))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
EXPECT_ASYNC_CALL_IN_SCOPE(step2, Run, element2.Show());
EXPECT_ASYNC_CALLS_IN_SCOPE_3(subsequence1, Run, step3, Run, completed, Run,
element3.Show());
}
TEST_P(InteractionSequenceSubsequenceTest, NestedSubsequenceInSameContext) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step2);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step3);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier1, kTestContext2);
test::TestElement element3(kTestIdentifier2, kTestContext2);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step1.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetContext(element2.context())
.SetStartCallback(step2.Get()))
.AddStep(InteractionSequence::StepBuilder().AddSubsequence(std::move(
Builder()
.AddStep(InteractionSequence::StepBuilder().AddSubsequence(
std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetContext(InteractionSequence::ContextMode::
kFromPreviousStep)
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(subsequence1.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(step3.Get()))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(step1, Run, sequence->Start());
EXPECT_ASYNC_CALL_IN_SCOPE(step2, Run, element2.Show());
EXPECT_CALL(subsequence2, Run);
EXPECT_CALL(subsequence1, Run);
EXPECT_CALL(step3, Run);
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, element3.Show());
}
TEST_P(InteractionSequenceSubsequenceTest, SequenceDeletedDuringSubsequences) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, before);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, after);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence1);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, subsequence2);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(before.Get()))
.AddStep(InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(subsequence1.Get()))))
.AddSubsequence(std::move(Builder().AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(subsequence2.Get())))))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(after.Get()))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(before, Run, sequence->Start());
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(
2, nullptr, ElementIdentifier(),
InteractionSequence::StepType::kSubsequence,
InteractionSequence::AbortedReason::kSequenceDestroyed, testing::_,
// TODO(dfried): Should kill and capture where each subsequence
// aborted.
testing::IsEmpty())),
sequence.reset());
}
TEST_P(InteractionSequenceSubsequenceTest, NamedElements) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
element2.Show();
element3.Show();
auto sequence =
Builder()
.SetCompletedCallback(completed.Get())
.SetAbortedCallback(aborted.Get())
.SetContext(element1.context())
// Name a single element.
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element1.identifier())
.SetStartCallback(base::BindLambdaForTesting(
[&element2](InteractionSequence* seq,
TrackedElement* el) {
seq->NameElement(&element2, kElementName1);
})))
// Run one or more subsequences which refer to the named element
// above, and which [re-]assign conflicting names.
.AddStep(
InteractionSequence::StepBuilder()
.SetSubsequenceMode(GetSubsequenceMode())
.AddSubsequence(std::move(
Builder()
.AddStep(
InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetStartCallback(base::BindLambdaForTesting(
[&element3](InteractionSequence* seq,
TrackedElement* el) {
// This assigns a second name.
seq->NameElement(&element3,
kElementName2);
})))
// Verify that the element is named as above, then
// reassign the first name.
.AddStep(
InteractionSequence::StepBuilder()
.SetElementName(kElementName2)
.SetStartCallback(base::BindLambdaForTesting(
[&element1, &element3](
InteractionSequence* seq,
TrackedElement* el) {
EXPECT_EQ(&element3, el);
seq->NameElement(&element1,
kElementName1);
})))
// Verify the reassignmed name.
.AddStep(
InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetStartCallback(base::BindLambdaForTesting(
[&element1](TrackedElement* el) {
EXPECT_EQ(&element1, el);
})))))
.AddSubsequence(std::move(
Builder()
.AddStep(
InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(base::BindLambdaForTesting(
[&element2](InteractionSequence* seq,
TrackedElement* el) {
// This assigns a second name.
seq->NameElement(&element2,
kElementName2);
})))
// Verify that the element is named above. Then
// reassign the original name.
.AddStep(
InteractionSequence::StepBuilder()
.SetElementName(kElementName2)
.SetStartCallback(base::BindLambdaForTesting(
[&element2, &element3](
InteractionSequence* seq,
TrackedElement* el) {
EXPECT_EQ(&element2, el);
seq->NameElement(&element3,
kElementName1);
})))
// Verify the reassignmed name.
.AddStep(
InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetStartCallback(base::BindLambdaForTesting(
[&element3](TrackedElement* el) {
EXPECT_EQ(&element3, el);
}))))))
// None of the (possibly conflicting) names changed in subsequences
// should be present here.
.AddStep(
InteractionSequence::StepBuilder()
.SetElementName(kElementName1)
.SetStartCallback(base::BindLambdaForTesting(
[&element2](InteractionSequence* seq,
TrackedElement* el) {
EXPECT_EQ(&element2, el)
<< "Original named element should be unchanged.";
EXPECT_FALSE(
base::Contains(seq->named_elements_, kElementName2))
<< "Element named in subsequence should not be "
"present here.";
})))
.Build();
EXPECT_ASYNC_CALL_IN_SCOPE(completed, Run, sequence->Start());
}
INSTANTIATE_TEST_SUITE_P(
,
InteractionSequenceSubsequenceTest,
testing::Combine(
testing::Values(InteractionSequence::StepStartMode::kImmediate,
InteractionSequence::StepStartMode::kAsynchronous),
testing::Values(InteractionSequence::SubsequenceMode::kAtMostOne,
InteractionSequence::SubsequenceMode::kExactlyOne,
InteractionSequence::SubsequenceMode::kAtLeastOne,
InteractionSequence::SubsequenceMode::kAll)),
[](const testing::TestParamInfo<
std::tuple<InteractionSequence::StepStartMode,
InteractionSequence::SubsequenceMode>>& modes) {
std::ostringstream oss;
oss << std::get<InteractionSequence::StepStartMode>(modes.param) << "_"
<< std::get<InteractionSequence::SubsequenceMode>(modes.param);
return oss.str();
});
class InteractionSequenceAsyncTest : public InteractionSequenceTestBase {
public:
InteractionSequenceAsyncTest() = default;
~InteractionSequenceAsyncTest() override = default;
std::optional<InteractionSequence::StepStartMode> GetMode() const override {
return InteractionSequence::StepStartMode::kAsynchronous;
}
};
TEST_F(InteractionSequenceAsyncTest, AbortOnHideBetweenTriggerAndStart) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element(kTestIdentifier1, kTestContext1);
element.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element.identifier())
.SetType(InteractionSequence::StepType::kCustomEvent,
kCustomEventType1)
.SetStartCallback(step.Get())
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(aborted,
Run(test::SequenceAbortedMatcher(
2, testing::_, element.identifier(),
InteractionSequence::StepType::kCustomEvent,
InteractionSequence::AbortedReason::
kElementHiddenBetweenTriggerAndStepStart)),
{
// This triggers step 2.
element.SendCustomEvent(kCustomEventType1);
// This kills step 2 before the step callback can
// run.
element.Hide();
});
}
TEST_F(InteractionSequenceAsyncTest, AbortOnHideBetweenTriggerAndStage) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed);
UNCALLED_MOCK_CALLBACK(InteractionSequence::StepStartCallback, step);
test::TestElement element1(kTestIdentifier1, kTestContext1);
test::TestElement element2(kTestIdentifier2, kTestContext1);
test::TestElement element3(kTestIdentifier3, kTestContext1);
element1.Show();
auto sequence =
Builder()
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
.AddStep(InteractionSequence::WithInitialElement(&element1))
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element2.identifier())
.SetStartCallback(base::BindLambdaForTesting([&]() {
element3.Show();
element3.Hide();
}))
.Build())
.AddStep(InteractionSequence::StepBuilder()
.SetElementID(element3.identifier())
.SetStartCallback(step.Get())
.SetTransitionOnlyOnEvent(true)
.Build())
.Build();
sequence->Start();
EXPECT_ASYNC_CALL_IN_SCOPE(
aborted,
Run(test::SequenceAbortedMatcher(3, testing::_, element3.identifier(),
InteractionSequence::StepType::kShown,
InteractionSequence::AbortedReason::
kSubsequentStepTriggerInvalidated)),
{
// This triggers step 2.
element2.Show();
});
}
} // namespace ui