Files
UnrealEngine/Engine/Source/Developer/AutomationDriver/Private/DriverSequence.cpp
2025-05-18 13:04:45 +08:00

1924 lines
55 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DriverSequence.h"
#include "IStepExecutor.h"
#include "IDriverSequence.h"
#include "IElementLocator.h"
#include "IApplicationElement.h"
#include "IApplicationElement.h"
#include "AutomatedApplication.h"
#include "AutomationDriverLogging.h"
#include "AutomationDriver.h"
#include "DriverConfiguration.h"
#include "StepExecutor.h"
#include "WaitUntil.h"
#include "LocateBy.h"
#include "GenericPlatform/ICursor.h"
#include "Misc/Timespan.h"
#include "InputCoreTypes.h"
#include "GenericPlatform/GenericApplicationMessageHandler.h"
#include "Framework/Application/SlateApplication.h"
class FStep
{
public:
static FStepResult Done()
{
return FStepResult(FStepResult::EState::DONE, FTimespan::FromSeconds(0.01));
}
static FStepResult Done(double Seconds)
{
return FStepResult(FStepResult::EState::DONE, FTimespan::FromSeconds(Seconds));
}
static FStepResult Done(const FTimespan& Value)
{
return FStepResult(FStepResult::EState::DONE, Value);
}
static FStepResult Wait(double Seconds)
{
return FStepResult(FStepResult::EState::REPEAT, FTimespan::FromSeconds(Seconds));
}
static FStepResult Wait(const FTimespan& Value)
{
return FStepResult(FStepResult::EState::REPEAT, Value);
}
static FStepResult Failed()
{
return FStepResult(FStepResult::EState::FAILED, FTimespan::MinValue());
}
};
class FActionSequenceExtensions
{
public:
static bool InterpreteCharacter(TCHAR Character, int32* OutKeyCode, int32* OutCharCode)
{
bool bSuccess = false;
FKey Key = FInputKeyManager::Get().GetKeyFromCodes(0, Character);
if (!Key.IsValid())
{
if (Character == TEXT('\n'))
{
// Treat line feed characters as a simulated Enter key press
Key = EKeys::Enter;
}
else if (Character == TEXT('\t'))
{
Key = EKeys::Tab;
}
}
if (Key.IsValid())
{
const uint32* KeyCodePtr;
const uint32* CharCodePtr;
FInputKeyManager::Get().GetCodesFromKey(Key, KeyCodePtr, CharCodePtr);
*OutKeyCode = (KeyCodePtr == nullptr) ? 0 : *KeyCodePtr;
*OutCharCode = (CharCodePtr == nullptr) ? Character : *CharCodePtr;
bSuccess = true;
}
else if (Character != TEXT('\r')) // skip processing any carriage returns
{
*OutKeyCode = 0;
*OutCharCode = Character;
bSuccess = true;
}
return bSuccess;
}
static FStepResult LocateElement(const TSharedRef<FAsyncAutomationDriver, ESPMode::ThreadSafe>& AsyncDriver, const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, const FTimespan& TotalProcessTime, TSharedPtr<IApplicationElement>& OutElement)
{
TArray<TSharedRef<IApplicationElement>> Elements;
ElementLocator->Locate(Elements);
if (Elements.Num() > 1)
{
FAutomationDriverLogging::TooManyElementsFound(Elements);
return FStep::Failed();
}
if (Elements.Num() == 0)
{
if (TotalProcessTime >= AsyncDriver->GetConfiguration()->ImplicitWait)
{
FAutomationDriverLogging::CannotFindElement(ElementLocator);
return FStep::Failed();
}
return FStep::Wait(1);
}
OutElement = Elements[0];
return FStep::Done();
}
static FStepResult LocateVisibleElement(const TSharedRef<FAsyncAutomationDriver, ESPMode::ThreadSafe>& AsyncDriver, const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, const FTimespan& TotalProcessTime, TSharedPtr<IApplicationElement>& OutElement)
{
TSharedPtr<IApplicationElement> Element;
FStepResult Result = LocateElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (!Element->IsVisible())
{
if (TotalProcessTime >= AsyncDriver->GetConfiguration()->ImplicitWait)
{
FAutomationDriverLogging::ElementNotVisible(ElementLocator);
return FStep::Failed();
}
return FStep::Wait(1);
}
OutElement = Element;
return FStep::Done();
}
static FStepResult LocateVisibleInteractableElement(const TSharedRef<FAsyncAutomationDriver, ESPMode::ThreadSafe>& AsyncDriver, const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, const FTimespan& TotalProcessTime, TSharedPtr<IApplicationElement>& OutElement)
{
TSharedPtr<IApplicationElement> Element;
FStepResult Result = LocateVisibleElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (!Element->IsInteractable())
{
if (TotalProcessTime >= AsyncDriver->GetConfiguration()->ImplicitWait)
{
FAutomationDriverLogging::ElementNotInteractable(ElementLocator);
return FStep::Failed();
}
return FStep::Wait(1);
}
OutElement = Element;
return FStep::Done();
}
};
class FAsyncActionSequence
: public IAsyncActionSequence
, public TSharedFromThis<FAsyncActionSequence, ESPMode::ThreadSafe>
{
public:
enum class EElementAnchor : uint8
{
TOP_LEFT_CORNER,
CENTER,
};
virtual ~FAsyncActionSequence()
{ }
bool IsExecuting() override
{
return StepsExecutor->IsExecuting();
}
virtual IAsyncActionSequence& Wait(FTimespan Timespan) override
{
InternalWait(Until::Lambda([Timespan](const FTimespan& TotalWaitTime) {
check(IsInGameThread());
if (TotalWaitTime > Timespan)
{
return FDriverWaitResponse::Passed();
}
return FDriverWaitResponse::Wait(Timespan);
}));
return *this;
}
virtual IAsyncActionSequence& Wait(const FDriverWaitDelegate& Delegate) override
{
InternalWait(Delegate);
return *this;
}
virtual IAsyncActionSequence& MoveToElement(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, int32 XOffset, int32 YOffset) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, XOffset, YOffset);
return *this;
}
virtual IAsyncActionSequence& MoveToElement(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
return *this;
}
virtual IAsyncActionSequence& MoveByOffset(int32 XOffset, int32 YOffset) override
{
InternalMoveByOffset(XOffset, YOffset);
return *this;
}
virtual IAsyncActionSequence& ScrollBy(float Delta) override
{
InternalScrollBy(Delta);
return *this;
}
virtual IAsyncActionSequence& ScrollBy(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, float Delta) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalScrollBy(Delta);
return *this;
}
virtual IAsyncActionSequence& ScrollToBeginning(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalScrollToBeginning(ElementLocator, 999999);
return *this;
}
virtual IAsyncActionSequence& ScrollToBeginning(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, float Amount) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalScrollToBeginning(ElementLocator, FMath::Abs(Amount));
return *this;
}
virtual IAsyncActionSequence& ScrollToBeginningUntil(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalScrollUntil(By::Cursor(), ElementLocator, 1);
return *this;
}
virtual IAsyncActionSequence& ScrollToBeginningUntil(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ScrollableElementLocator, const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalMoveToElement(ScrollableElementLocator, EElementAnchor::CENTER, 0, 0);
InternalScrollUntil(ScrollableElementLocator, ElementLocator, 1);
return *this;
}
virtual IAsyncActionSequence& ScrollToEnd(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalScrollToEnd(ElementLocator, -999999);
return *this;
}
virtual IAsyncActionSequence& ScrollToEnd(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, float Amount) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalScrollToEnd(ElementLocator, FMath::Abs(Amount) * -1);
return *this;
}
virtual IAsyncActionSequence& ScrollToEndUntil(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalScrollUntil(By::Cursor(), ElementLocator, -1);
return *this;
}
virtual IAsyncActionSequence& ScrollToEndUntil(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ScrollableElementLocator, const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalMoveToElement(ScrollableElementLocator, EElementAnchor::CENTER, 0, 0);
InternalScrollUntil(ScrollableElementLocator, ElementLocator, -1);
return *this;
}
virtual IAsyncActionSequence& Click(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalClick(ElementLocator, EMouseButtons::Left);
return *this;
}
virtual IAsyncActionSequence& Click(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalClick(ElementLocator, MouseButton);
return *this;
}
virtual IAsyncActionSequence& Click(EMouseButtons::Type MouseButton) override
{
InternalClick(By::Cursor(), MouseButton);
return *this;
}
virtual IAsyncActionSequence& Click() override
{
Click(EMouseButtons::Left);
return *this;
}
virtual IAsyncActionSequence& DoubleClick(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalDoubleClick(ElementLocator, EMouseButtons::Left);
return *this;
}
virtual IAsyncActionSequence& DoubleClick(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalDoubleClick(ElementLocator, MouseButton);
return *this;
}
virtual IAsyncActionSequence& DoubleClick(EMouseButtons::Type MouseButton) override
{
InternalDoubleClick(By::Cursor(), MouseButton);
return *this;
}
virtual IAsyncActionSequence& DoubleClick() override
{
InternalDoubleClick(By::Cursor(), EMouseButtons::Left);
return *this;
}
virtual IAsyncActionSequence& Type(const TCHAR* Text) override
{
Type(FString(Text));
return *this;
}
virtual IAsyncActionSequence& Type(FString Text) override
{
FString Temp;
for (TCHAR Char : Text.GetCharArray())
{
if (Char == TCHAR())
{
break;
}
Type(Char);
}
return *this;
}
virtual IAsyncActionSequence& Type(FKey Key) override
{
const uint32* KeyCodePtr;
const uint32* CharCodePtr;
FInputKeyManager::Get().GetCodesFromKey(Key, KeyCodePtr, CharCodePtr);
int32 KeyCode = (KeyCodePtr == nullptr) ? 0 : *KeyCodePtr;
int32 CharCode = (CharCodePtr == nullptr) ? 0 : *CharCodePtr;
InternalSendKey(KeyCode, CharCode);
return *this;
}
virtual IAsyncActionSequence& Type(TCHAR Character) override
{
int32 KeyCode;
int32 CharCode;
if (FActionSequenceExtensions::InterpreteCharacter(Character, &KeyCode, &CharCode))
{
InternalSendKey(KeyCode, CharCode);
}
return *this;
}
virtual IAsyncActionSequence& Type(const TArray<FKey>& Keys) override
{
for (const FKey& Key: Keys)
{
InternalSendKey(Key, 0);
}
return *this;
}
virtual IAsyncActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, const TCHAR* Text) override
{
InternalEnsureFocus(ElementLocator);
Type(Text);
return *this;
}
virtual IAsyncActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FString Text) override
{
InternalEnsureFocus(ElementLocator);
Type(MoveTemp(Text));
return *this;
}
virtual IAsyncActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key) override
{
InternalEnsureFocus(ElementLocator);
Type(Key);
return *this;
}
virtual IAsyncActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, TCHAR Character) override
{
InternalEnsureFocus(ElementLocator);
Type(Character);
return *this;
}
virtual IAsyncActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, const TArray<FKey>& Keys) override
{
InternalEnsureFocus(ElementLocator);
Type(Keys);
return *this;
}
virtual IAsyncActionSequence& TypeChord(FKey Key1, FKey Key2) override
{
Press(Key1);
Press(Key2);
Release(Key2);
Release(Key1);
return *this;
}
virtual IAsyncActionSequence& TypeChord(FKey Key1, TCHAR Character) override
{
Press(Key1);
Press(Character);
Release(Character);
Release(Key1);
return *this;
}
virtual IAsyncActionSequence& TypeChord(FKey Key1, FKey Key2, FKey Key3) override
{
Press(Key1);
Press(Key2);
Press(Key3);
Release(Key3);
Release(Key2);
Release(Key1);
return *this;
}
virtual IAsyncActionSequence& TypeChord(FKey Key1, FKey Key2, TCHAR Character) override
{
Press(Key1);
Press(Key2);
Press(Character);
Release(Character);
Release(Key2);
Release(Key1);
return *this;
}
virtual IAsyncActionSequence& TypeChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2) override
{
InternalEnsureFocus(ElementLocator);
TypeChord(Key1, Key2);
return *this;
}
virtual IAsyncActionSequence& TypeChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, TCHAR Character) override
{
InternalEnsureFocus(ElementLocator);
TypeChord(Key1, Character);
return *this;
}
virtual IAsyncActionSequence& TypeChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, FKey Key3) override
{
InternalEnsureFocus(ElementLocator);
TypeChord(Key1, Key2, Key3);
return *this;
}
virtual IAsyncActionSequence& TypeChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, TCHAR Character) override
{
InternalEnsureFocus(ElementLocator);
TypeChord(Key1, Key2, Character);
return *this;
}
virtual IAsyncActionSequence& Press(TCHAR Character) override
{
InternalPress(Character);
return *this;
}
virtual IAsyncActionSequence& Press(FKey Key) override
{
InternalPress(Key, 0);
return *this;
}
virtual IAsyncActionSequence& Press(EMouseButtons::Type MouseButton) override
{
InternalPress(By::Cursor(), MouseButton);
return *this;
}
virtual IAsyncActionSequence& Press(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, TCHAR Character) override
{
InternalEnsureFocus(ElementLocator);
InternalPress(Character);
return *this;
}
virtual IAsyncActionSequence& Press(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key) override
{
InternalEnsureFocus(ElementLocator);
InternalPress(Key, 0);
return *this;
}
virtual IAsyncActionSequence& Press(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalPress(ElementLocator, MouseButton);
return *this;
}
virtual IAsyncActionSequence& PressChord(FKey Key1, FKey Key2) override
{
InternalPress(Key1, 0);
InternalPress(Key2, 0);
return *this;
}
virtual IAsyncActionSequence& PressChord(FKey Key1, TCHAR Character) override
{
InternalPress(Key1, 0);
InternalPress(Character);
return *this;
}
virtual IAsyncActionSequence& PressChord(FKey Key1, FKey Key2, FKey Key3) override
{
InternalPress(Key1, 0);
InternalPress(Key2, 0);
InternalPress(Key3, 0);
return *this;
}
virtual IAsyncActionSequence& PressChord(FKey Key1, FKey Key2, TCHAR Character) override
{
InternalPress(Key1, 0);
InternalPress(Key2, 0);
InternalPress(Character);
return *this;
}
virtual IAsyncActionSequence& PressChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2) override
{
InternalEnsureFocus(ElementLocator);
InternalPress(Key1, 0);
InternalPress(Key2, 0);
return *this;
}
virtual IAsyncActionSequence& PressChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, TCHAR Character) override
{
InternalEnsureFocus(ElementLocator);
InternalPress(Key1, 0);
InternalPress(Character);
return *this;
}
virtual IAsyncActionSequence& PressChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, FKey Key3) override
{
InternalEnsureFocus(ElementLocator);
InternalPress(Key1, 0);
InternalPress(Key2, 0);
InternalPress(Key3, 0);
return *this;
}
virtual IAsyncActionSequence& PressChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, TCHAR Character) override
{
InternalEnsureFocus(ElementLocator);
InternalPress(Key1, 0);
InternalPress(Key2, 0);
InternalPress(Character);
return *this;
}
virtual IAsyncActionSequence& Release(TCHAR Character) override
{
InternalRelease(Character);
return *this;
}
virtual IAsyncActionSequence& Release(FKey Key) override
{
InternalRelease(Key, 0);
return *this;
}
virtual IAsyncActionSequence& Release(EMouseButtons::Type MouseButton) override
{
InternalRelease(MouseButton);
return *this;
}
virtual IAsyncActionSequence& Release(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, TCHAR Character) override
{
InternalEnsureFocus(ElementLocator);
InternalRelease(Character);
return *this;
}
virtual IAsyncActionSequence& Release(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key) override
{
InternalEnsureFocus(ElementLocator);
InternalRelease(Key, 0);
return *this;
}
virtual IAsyncActionSequence& Release(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton) override
{
InternalMoveToElement(ElementLocator, EElementAnchor::CENTER, 0, 0);
InternalRelease(MouseButton);
return *this;
}
virtual IAsyncActionSequence& ReleaseChord(FKey Key1, FKey Key2) override
{
InternalRelease(Key2, 0);
InternalRelease(Key1, 0);
return *this;
}
virtual IAsyncActionSequence& ReleaseChord(FKey Key1, TCHAR Character) override
{
InternalRelease(Character);
InternalRelease(Key1, 0);
return *this;
}
virtual IAsyncActionSequence& ReleaseChord(FKey Key1, FKey Key2, FKey Key3) override
{
InternalRelease(Key3, 0);
InternalRelease(Key2, 0);
InternalRelease(Key1, 0);
return *this;
}
virtual IAsyncActionSequence& ReleaseChord(FKey Key1, FKey Key2, TCHAR Character) override
{
InternalRelease(Character);
InternalRelease(Key2, 0);
InternalRelease(Key1, 0);
return *this;
}
virtual IAsyncActionSequence& ReleaseChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2) override
{
InternalEnsureFocus(ElementLocator);
InternalRelease(Key2, 0);
InternalRelease(Key1, 0);
return *this;
}
virtual IAsyncActionSequence& ReleaseChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, TCHAR Character) override
{
InternalEnsureFocus(ElementLocator);
InternalRelease(Character);
InternalRelease(Key1, 0);
return *this;
}
virtual IAsyncActionSequence& ReleaseChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, FKey Key3) override
{
InternalEnsureFocus(ElementLocator);
InternalRelease(Key3, 0);
InternalRelease(Key2, 0);
InternalRelease(Key1, 0);
return *this;
}
virtual IAsyncActionSequence& ReleaseChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, TCHAR Character) override
{
InternalEnsureFocus(ElementLocator);
InternalRelease(Character);
InternalRelease(Key2, 0);
InternalRelease(Key1, 0);
return *this;
}
virtual IAsyncActionSequence& Focus(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
InternalFocus(ElementLocator);
return *this;
}
virtual IAsyncActionSequence& Focus(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, uint32 UserFocus) override
{
InternalFocus(ElementLocator, UserFocus);
return *this;
}
TAsyncResult<bool> Perform()
{
if (!Application->IsHandlingMessages())
{
return false;
}
if (!AsyncDriver->PinActionSequence(SharedThis(this)))
{
return false;
}
TWeakPtr<FAsyncActionSequence, ESPMode::ThreadSafe> LocalWeakThis(SharedThis(this));
StepsExecutor->OnCompleted([LocalWeakThis]()
{
if (TSharedPtr<FAsyncActionSequence, ESPMode::ThreadSafe> Sequence = LocalWeakThis.Pin())
{
Sequence->AsyncDriver->UnpinActionSequence(Sequence);
}
});
return StepsExecutor->Execute();
}
private:
FAsyncActionSequence(
const TSharedRef<FAsyncAutomationDriver, ESPMode::ThreadSafe>& InAsyncDriver,
const TSharedRef<FAutomatedApplication, ESPMode::ThreadSafe>& InApplication,
const TSharedRef<IStepExecutor, ESPMode::ThreadSafe>& InStepsExecutor)
: AsyncDriver(InAsyncDriver)
, Application(InApplication)
, StepsExecutor(InStepsExecutor)
{ }
void InternalWait(const FDriverWaitDelegate& Delegate)
{
StepsExecutor->Add([this, Delegate](const FTimespan& TotalProcessTime) -> FStepResult {
if (Delegate.IsBound())
{
FDriverWaitResponse Response = Delegate.Execute(TotalProcessTime);
if (Response.State == FDriverWaitResponse::EState::WAIT)
{
return FStep::Wait(Response.NextWait);
}
else if (Response.State == FDriverWaitResponse::EState::FAILED)
{
return FStep::Failed();
}
}
return FStep::Done();
});
}
void InternalMoveToElement(const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EElementAnchor Anchor, float XOffset, float YOffset)
{
StepsExecutor->Add([this, ElementLocator, Anchor, XOffset, YOffset](const FTimespan& EnsureElementTotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateElement(AsyncDriver, ElementLocator, EnsureElementTotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (!Element->IsVisible())
{
const TSharedPtr<IApplicationElement> ParentElement = Element->GetScrollableParent();
if (ParentElement.IsValid())
{
const TSharedRef<IElementLocator, ESPMode::ThreadSafe> ParentElementLocator = ParentElement->CreateLocator();
StepsExecutor->InsertNext([this, ElementLocator, ParentElementLocator, Anchor, XOffset, YOffset](const FTimespan& ScrollToElementTotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> ScrollableParentElement;
FStepResult ParentResult = FActionSequenceExtensions::LocateElement(AsyncDriver, ParentElementLocator, ScrollToElementTotalProcessTime, ScrollableParentElement);
if (ParentResult.State != FStepResult::EState::DONE)
{
return ParentResult;
}
TSharedPtr<IApplicationElement> DesiredElement;
FStepResult ChildResult = FActionSequenceExtensions::LocateElement(AsyncDriver, ElementLocator, ScrollToElementTotalProcessTime, DesiredElement);
if (ChildResult.State != FStepResult::EState::DONE)
{
return ChildResult;
}
const FVector2D Position = ScrollableParentElement->GetAbsolutePosition();
FVector2D CursorPosition(Position.X + XOffset, Position.Y + YOffset);
if (Anchor == EElementAnchor::CENTER)
{
const FVector2D Size = ScrollableParentElement->GetSize();
CursorPosition.X += Size.X / 2;
CursorPosition.Y += Size.Y / 2;
}
else if (Anchor == EElementAnchor::TOP_LEFT_CORNER)
{
// do nothing
}
else
{
check(TEXT("Unknown MoveToElement Anchor specified"));
return FStep::Failed();
}
Application->Cursor->SetPosition(CursorPosition.X, CursorPosition.Y);
Application->GetRealMessageHandler()->OnMouseMove();
const FVector2D ChildLocation = DesiredElement->GetAbsolutePosition();
EOrientation ScrollOrientation;
if (ScrollableParentElement->IsScrollable(ScrollOrientation))
{
if (ScrollOrientation == Orient_Horizontal)
{
if (ChildLocation.X < CursorPosition.X)
{
InternalScrollUntil(ParentElementLocator, ElementLocator, 1);
}
else if (ChildLocation.X > CursorPosition.X)
{
InternalScrollUntil(ParentElementLocator, ElementLocator, -1);
}
}
else if (ScrollOrientation == Orient_Vertical)
{
if (ChildLocation.Y < CursorPosition.Y)
{
InternalScrollUntil(ParentElementLocator, ElementLocator, 1);
}
else if (ChildLocation.Y > CursorPosition.Y)
{
InternalScrollUntil(ParentElementLocator, ElementLocator, -1);
}
}
}
return FStep::Done();
});
}
}
return FStep::Done();
});
// Move the cursor to the desired element, even if not visible
StepsExecutor->Add([this, ElementLocator, Anchor, XOffset, YOffset](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
const FVector2D Position = Element->GetAbsolutePosition();
FVector2D CursorPosition(Position.X + XOffset, Position.Y + YOffset);
if (Anchor == EElementAnchor::CENTER)
{
const FVector2D Size = Element->GetSize();
CursorPosition.X += Size.X / 2;
CursorPosition.Y += Size.Y / 2;
}
else if (Anchor == EElementAnchor::TOP_LEFT_CORNER)
{
// do nothing
}
else
{
check(TEXT("Unknown MoveToElement Anchor specified"));
return FStep::Failed();
}
Application->Cursor->SetPosition(CursorPosition.X, CursorPosition.Y);
Application->GetRealMessageHandler()->OnMouseMove();
return FStep::Done();
});
}
void InternalMoveByOffset(float XOffset, float YOffset)
{
StepsExecutor->Add([this, XOffset, YOffset](const FTimespan& TotalProcessTime) -> FStepResult {
FVector2D CurrentPosition = Application->Cursor->GetPosition();
Application->Cursor->SetPosition(CurrentPosition.X + XOffset, CurrentPosition.Y + YOffset);
Application->GetRealMessageHandler()->OnMouseMove();
return FStep::Done();
});
}
void InternalScrollBy(float Delta)
{
StepsExecutor->Add([this, Delta](const FTimespan& TotalProcessTime) -> FStepResult {
Application->GetRealMessageHandler()->OnMouseWheel(Delta);
return FStep::Done();
});
}
void InternalScrollToBeginning(const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, float Delta)
{
StepsExecutor->Add([this, ElementLocator, Delta](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateVisibleElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (Element->IsScrolledToBeginning())
{
return FStep::Done();
}
Application->GetRealMessageHandler()->OnMouseWheel(Delta);
return FStep::Wait(FTimespan::Zero());
});
}
void InternalScrollToEnd(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, float Delta)
{
StepsExecutor->Add([this, ElementLocator, Delta](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateVisibleElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (Element->IsScrolledToEnd())
{
return FStep::Done();
}
Application->GetRealMessageHandler()->OnMouseWheel(Delta);
return FStep::Wait(FTimespan::Zero());
});
}
void InternalScrollUntil(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ScrollableElementLocator, const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, float Delta)
{
const TFunction<FStepResult(const FTimespan&)> Step = [this, ScrollableElementLocator, ElementLocator, Delta](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateVisibleElement(AsyncDriver, ElementLocator, FTimespan::Zero(), Element);
if (Result.State == FStepResult::EState::DONE)
{
return Result;
}
TSharedPtr<IApplicationElement> ScrollableElement;
Result = FActionSequenceExtensions::LocateVisibleElement(AsyncDriver, ScrollableElementLocator, TotalProcessTime, ScrollableElement);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (Delta < 0.0f && ScrollableElement->IsScrolledToEnd())
{
return FStep::Failed();
}
else if (!(Delta < 0.0f) && ScrollableElement->IsScrolledToBeginning())
{
return FStep::Failed();
}
Application->GetRealMessageHandler()->OnMouseWheel(Delta);
return FStep::Wait(FTimespan::Zero());
};
// We add a small wait after moving the element into view in order to give the UI enough time to fully process the last wheel event
// Otherwise, we occasionally have element scroll out from under the cursor on accident when performing follow up click
const TFunction<FStepResult(const FTimespan&)> Wait = [this](const FTimespan& TotalProcessTime) -> FStepResult {
if (TotalProcessTime < FTimespan::FromSeconds(0.5))
{
return FStep::Wait(FTimespan::FromSeconds(0.5));
}
return FStep::Done();
};
if (StepsExecutor->IsExecuting())
{
StepsExecutor->InsertNext(Wait);
StepsExecutor->InsertNext(Step);
}
else
{
StepsExecutor->Add(Step);
StepsExecutor->Add(Wait);
}
}
void InternalClick(const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton)
{
InternalPress(ElementLocator, MouseButton);
InternalRelease(MouseButton);
}
void InternalDoubleClick(const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton)
{
InternalActivateWindow(ElementLocator);
// Send mouse down event
StepsExecutor->Add([this, ElementLocator, MouseButton](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateVisibleInteractableElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (!Element->IsHovered())
{
if (TotalProcessTime >= AsyncDriver->GetConfiguration()->ImplicitWait)
{
FAutomationDriverLogging::CannotClickUnhoveredElement(ElementLocator);
return FStep::Failed();
}
return FStep::Wait(1);
}
const TSharedPtr<FGenericWindow> Window = Element->GetWindow();
if (!Window.IsValid())
{
FAutomationDriverLogging::ElementHasNoWindow(ElementLocator);
return FStep::Failed();
}
Application->GetRealMessageHandler()->OnMouseDown(Window.ToSharedRef(), MouseButton);
AsyncDriver->TrackPress(MouseButton);
TWeakPtr<FGenericWindow> WeakWindow(Window);
// Send double click event
StepsExecutor->InsertNext([this, WeakWindow, MouseButton](const FTimespan& DoubleClickEventTotalProcessTime) -> FStepResult {
const TSharedPtr<FGenericWindow> TargetWindow = WeakWindow.Pin();
if (TargetWindow.IsValid())
{
Application->GetRealMessageHandler()->OnMouseDoubleClick(TargetWindow.ToSharedRef(), MouseButton);
// Send mouse up event
StepsExecutor->InsertNext([this, MouseButton](const FTimespan& SecondMouseUpEventTotalProcessTime) -> FStepResult {
Application->GetRealMessageHandler()->OnMouseUp(MouseButton);
AsyncDriver->TrackRelease(MouseButton);
return FStep::Done(0);
});
}
return FStep::Done(0);
});
// Send mouse up event
StepsExecutor->InsertNext([this, MouseButton](const FTimespan& FirstMouseUpEventTotalProcessTime) -> FStepResult {
Application->GetRealMessageHandler()->OnMouseUp(MouseButton);
AsyncDriver->TrackRelease(MouseButton);
return FStep::Done(0);
});
return FStep::Done(0);
});
}
void InternalSendKey(FKey Key, TCHAR Char)
{
const uint32* KeyCodePtr;
const uint32* CharCodePtr;
FInputKeyManager::Get().GetCodesFromKey(Key, KeyCodePtr, CharCodePtr);
int32 KeyCode = (KeyCodePtr == nullptr) ? 0 : *KeyCodePtr;
int32 CharCode = (CharCodePtr == nullptr) ? Char : *CharCodePtr;
InternalSendKey(KeyCode, CharCode);
}
void InternalSendKey(int32 KeyCode, int32 CharCode)
{
InternalPress(KeyCode, CharCode);
InternalRelease(KeyCode, CharCode);
}
void InternalActivateWindow(const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator)
{
// Activate the window
StepsExecutor->Add([this, ElementLocator](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
const TSharedPtr<FGenericWindow> Window = Element->GetWindow();
if (!Window.IsValid())
{
FAutomationDriverLogging::ElementHasNoWindow(ElementLocator);
return FStep::Failed();
}
TSharedPtr<SWindow> ActiveWindow = FSlateApplication::Get().GetActiveTopLevelWindow();
if (!ActiveWindow.IsValid() || ActiveWindow->GetNativeWindow() != Window)
{
Window->SetWindowFocus();
}
return FStep::Done();
});
}
void InternalPress(TCHAR Character)
{
int32 KeyCode;
int32 CharCode;
if (FActionSequenceExtensions::InterpreteCharacter(Character, &KeyCode, &CharCode))
{
InternalPress(KeyCode, CharCode);
}
}
void InternalPress(FKey Key, TCHAR Character)
{
const uint32* KeyCodePtr;
const uint32* CharCodePtr;
FInputKeyManager::Get().GetCodesFromKey(Key, KeyCodePtr, CharCodePtr);
int32 KeyCode = (KeyCodePtr == nullptr) ? 0 : *KeyCodePtr;
int32 CharCode = (CharCodePtr == nullptr) ? Character : *CharCodePtr;
InternalPress(KeyCode, CharCode);
}
void InternalPress(int32 KeyCode, int32 CharCode)
{
// Send key down event
StepsExecutor->Add([this, KeyCode, CharCode](const FTimespan& TotalProcessTime) -> FStepResult {
if (!AsyncDriver->IsPressed(KeyCode, CharCode))
{
Application->GetRealMessageHandler()->OnKeyDown(KeyCode, CharCode, false);
AsyncDriver->TrackPress(KeyCode, CharCode);
}
return FStep::Done();
});
if (CharCode != 0)
{
InternalCharKey(CharCode, false);
}
}
void InternalPress(const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton)
{
InternalActivateWindow(ElementLocator);
// Send mouse down event
StepsExecutor->Add([this, ElementLocator, MouseButton](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateVisibleInteractableElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (!Element->IsHovered())
{
if (TotalProcessTime >= AsyncDriver->GetConfiguration()->ImplicitWait)
{
FAutomationDriverLogging::CannotClickUnhoveredElement(ElementLocator);
return FStep::Failed();
}
return FStep::Wait(1);
}
const TSharedPtr<FGenericWindow> Window = Element->GetWindow();
if (!Window.IsValid())
{
FAutomationDriverLogging::ElementHasNoWindow(ElementLocator);
return FStep::Failed();
}
if (!AsyncDriver->IsPressed(MouseButton))
{
Application->GetRealMessageHandler()->OnMouseDown(Window.ToSharedRef(), MouseButton);
AsyncDriver->TrackPress(MouseButton);
}
return FStep::Done();
});
}
void InternalCharKey(TCHAR Character, bool bIsRepeat)
{
// Send key char event
StepsExecutor->Add([this, Character, bIsRepeat](const FTimespan& TotalProcessTime) -> FStepResult {
TCHAR FinalCharacter = AsyncDriver->ProcessCharacterForControlCodes(Character);
Application->GetRealMessageHandler()->OnKeyChar(FinalCharacter, bIsRepeat);
return FStep::Done();
});
}
void InternalRelease(TCHAR Character)
{
int32 KeyCode;
int32 CharCode;
if (FActionSequenceExtensions::InterpreteCharacter(Character, &KeyCode, &CharCode))
{
InternalRelease(KeyCode, CharCode);
}
}
void InternalRelease(FKey Key, TCHAR Char)
{
const uint32* KeyCodePtr;
const uint32* CharCodePtr;
FInputKeyManager::Get().GetCodesFromKey(Key, KeyCodePtr, CharCodePtr);
int32 KeyCode = (KeyCodePtr == nullptr) ? 0 : *KeyCodePtr;
int32 CharCode = (CharCodePtr == nullptr) ? Char : *CharCodePtr;
InternalRelease(KeyCode, CharCode);
}
void InternalRelease(int32 KeyCode, int32 CharCode)
{
// Send key up event
StepsExecutor->Add([this, KeyCode, CharCode](const FTimespan& TotalProcessTime) -> FStepResult {
if (AsyncDriver->IsPressed(KeyCode, CharCode))
{
Application->GetRealMessageHandler()->OnKeyUp(KeyCode, CharCode, false);
AsyncDriver->TrackRelease(KeyCode, CharCode);
}
return FStep::Done();
});
}
void InternalRelease(EMouseButtons::Type MouseButton)
{
// Send mouse up event
StepsExecutor->Add([this, MouseButton](const FTimespan& TotalProcessTime) -> FStepResult {
if (AsyncDriver->IsPressed(MouseButton))
{
Application->GetRealMessageHandler()->OnMouseUp(MouseButton);
AsyncDriver->TrackRelease(MouseButton);
}
return FStep::Done();
});
}
void InternalEnsureFocus(const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator)
{
StepsExecutor->Add([this, ElementLocator](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateVisibleElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (!Element->HasFocusedDescendants())
{
Element->Focus();
}
return FStep::Done();
});
}
void InternalFocus(const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator)
{
StepsExecutor->Add([this, ElementLocator](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateVisibleElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (!Element->IsFocused())
{
Element->Focus();
}
return FStep::Done();
});
}
void InternalFocus(const TSharedPtr<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, uint32 UserIndex)
{
StepsExecutor->Add([this, ElementLocator, UserIndex](const FTimespan& TotalProcessTime) -> FStepResult {
TSharedPtr<IApplicationElement> Element;
FStepResult Result = FActionSequenceExtensions::LocateVisibleElement(AsyncDriver, ElementLocator, TotalProcessTime, Element);
if (Result.State != FStepResult::EState::DONE)
{
return Result;
}
if (!Element->IsFocused())
{
Element->Focus(UserIndex);
}
return FStep::Done();
});
}
private:
const TSharedRef<FAsyncAutomationDriver, ESPMode::ThreadSafe> AsyncDriver;
const TSharedRef<FAutomatedApplication, ESPMode::ThreadSafe> Application;
const TSharedRef<IStepExecutor, ESPMode::ThreadSafe> StepsExecutor;
friend FAsyncActionSequenceFactory;
};
TSharedRef<FAsyncActionSequence, ESPMode::ThreadSafe> FAsyncActionSequenceFactory::Create(
const TSharedRef<FAsyncAutomationDriver, ESPMode::ThreadSafe>& AsyncDriver,
const TSharedRef<FAutomatedApplication, ESPMode::ThreadSafe>& Application)
{
return MakeShareable(new FAsyncActionSequence(
AsyncDriver,
Application,
FStepExecutorFactory::Create(AsyncDriver->GetConfiguration(), Application)));
}
class FAsyncDriverSequence
: public IAsyncDriverSequence
{
public:
virtual ~FAsyncDriverSequence()
{ }
virtual IAsyncActionSequence& Actions()
{
return *ActionSequence;
}
virtual TAsyncResult<bool> Perform()
{
return ActionSequence->Perform();
}
FAsyncDriverSequence(
const TSharedRef<FAsyncActionSequence, ESPMode::ThreadSafe>& InActionSequence)
: ActionSequence(InActionSequence)
{ }
private:
const TSharedRef<FAsyncActionSequence, ESPMode::ThreadSafe> ActionSequence;
friend FAsyncDriverSequenceFactory;
};
TSharedRef<IAsyncDriverSequence, ESPMode::ThreadSafe> FAsyncDriverSequenceFactory::Create(
const TSharedRef<FAsyncActionSequence, ESPMode::ThreadSafe>& ActionSequence)
{
return MakeShareable(new FAsyncDriverSequence(
ActionSequence));
}
class FActionSequence
: public IActionSequence
, public TSharedFromThis<FActionSequence, ESPMode::ThreadSafe>
{
public:
virtual ~FActionSequence()
{ }
virtual IActionSequence& Wait(FTimespan Timespan) override
{
ActionSequence->Wait(Timespan);
return *this;
}
virtual IActionSequence& Wait(const FDriverWaitDelegate& Delegate) override
{
ActionSequence->Wait(Delegate);
return *this;
}
virtual IActionSequence& MoveToElement(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, int32 XOffset, int32 YOffset) override
{
ActionSequence->MoveToElement(ElementLocator, XOffset, YOffset);
return *this;
}
virtual IActionSequence& MoveToElement(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->MoveToElement(ElementLocator);
return *this;
}
virtual IActionSequence& MoveByOffset(int32 XOffset, int32 YOffset) override
{
ActionSequence->MoveByOffset(XOffset, YOffset);
return *this;
}
virtual IActionSequence& ScrollBy(float Delta) override
{
ActionSequence->ScrollBy(Delta);
return *this;
}
virtual IActionSequence& ScrollBy(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, float Delta) override
{
ActionSequence->ScrollBy(ElementLocator, Delta);
return *this;
}
virtual IActionSequence& ScrollToBeginning(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->ScrollToBeginning(ElementLocator);
return *this;
}
virtual IActionSequence& ScrollToBeginning(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, float Amount) override
{
ActionSequence->ScrollToBeginning(ElementLocator, Amount);
return *this;
}
virtual IActionSequence& ScrollToBeginningUntil(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->ScrollToBeginningUntil(ElementLocator);
return *this;
}
virtual IActionSequence& ScrollToBeginningUntil(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ScrollableElementLocator, const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->ScrollToBeginningUntil(ScrollableElementLocator, ElementLocator);
return *this;
}
virtual IActionSequence& ScrollToEnd(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->ScrollToEnd(ElementLocator);
return *this;
}
virtual IActionSequence& ScrollToEnd(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, float Amount) override
{
ActionSequence->ScrollToEnd(ElementLocator, Amount);
return *this;
}
virtual IActionSequence& ScrollToEndUntil(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->ScrollToEndUntil(ElementLocator);
return *this;
}
virtual IActionSequence& ScrollToEndUntil(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ScrollableElementLocator, const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->ScrollToEndUntil(ScrollableElementLocator, ElementLocator);
return *this;
}
virtual IActionSequence& Click(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->Click(ElementLocator);
return *this;
}
virtual IActionSequence& Click(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton) override
{
ActionSequence->Click(ElementLocator, MouseButton);
return *this;
}
virtual IActionSequence& Click(EMouseButtons::Type MouseButton) override
{
ActionSequence->Click(MouseButton);
return *this;
}
virtual IActionSequence& Click() override
{
ActionSequence->Click();
return *this;
}
virtual IActionSequence& DoubleClick(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->DoubleClick(ElementLocator);
return *this;
}
virtual IActionSequence& DoubleClick(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton) override
{
ActionSequence->DoubleClick(ElementLocator, MouseButton);
return *this;
}
virtual IActionSequence& DoubleClick(EMouseButtons::Type MouseButton) override
{
ActionSequence->DoubleClick(MouseButton);
return *this;
}
virtual IActionSequence& DoubleClick() override
{
ActionSequence->DoubleClick();
return *this;
}
virtual IActionSequence& Type(const TCHAR* Text) override
{
ActionSequence->Type(Text);
return *this;
}
virtual IActionSequence& Type(FString Text) override
{
ActionSequence->Type(MoveTemp(Text));
return *this;
}
virtual IActionSequence& Type(FKey Key) override
{
ActionSequence->Type(Key);
return *this;
}
virtual IActionSequence& Type(TCHAR Character) override
{
ActionSequence->Type(Character);
return *this;
}
virtual IActionSequence& Type(const TArray<FKey>& Keys) override
{
ActionSequence->Type(Keys);
return *this;
}
virtual IActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, const TCHAR* Text) override
{
ActionSequence->Type(ElementLocator, Text);
return *this;
}
virtual IActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FString Text) override
{
ActionSequence->Type(ElementLocator, MoveTemp(Text));
return *this;
}
virtual IActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key) override
{
ActionSequence->Type(ElementLocator, Key);
return *this;
}
virtual IActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, TCHAR Character) override
{
ActionSequence->Type(ElementLocator, Character);
return *this;
}
virtual IActionSequence& Type(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, const TArray<FKey>& Keys) override
{
ActionSequence->Type(ElementLocator, Keys);
return *this;
}
virtual IActionSequence& TypeChord(FKey Key1, FKey Key2) override
{
ActionSequence->TypeChord(Key1, Key2);
return *this;
}
virtual IActionSequence& TypeChord(FKey Key1, TCHAR Character) override
{
ActionSequence->TypeChord(Key1, Character);
return *this;
}
virtual IActionSequence& TypeChord(FKey Key1, FKey Key2, FKey Key3) override
{
ActionSequence->TypeChord(Key1, Key2, Key3);
return *this;
}
virtual IActionSequence& TypeChord(FKey Key1, FKey Key2, TCHAR Character) override
{
ActionSequence->TypeChord(Key1, Key2, Character);
return *this;
}
virtual IActionSequence& TypeChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2) override
{
ActionSequence->TypeChord(ElementLocator, Key1, Key2);
return *this;
}
virtual IActionSequence& TypeChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, TCHAR Character) override
{
ActionSequence->TypeChord(ElementLocator, Key1, Character);
return *this;
}
virtual IActionSequence& TypeChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, FKey Key3) override
{
ActionSequence->TypeChord(ElementLocator, Key1, Key2, Key3);
return *this;
}
virtual IActionSequence& TypeChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, TCHAR Character) override
{
ActionSequence->TypeChord(ElementLocator, Key1, Key2, Character);
return *this;
}
virtual IActionSequence& Press(TCHAR Character) override
{
ActionSequence->Press(Character);
return *this;
}
virtual IActionSequence& Press(FKey Key) override
{
ActionSequence->Press(Key);
return *this;
}
virtual IActionSequence& Press(EMouseButtons::Type MouseButton) override
{
ActionSequence->Press(MouseButton);
return *this;
}
virtual IActionSequence& Press(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, TCHAR Character) override
{
ActionSequence->Press(ElementLocator, Character);
return *this;
}
virtual IActionSequence& Press(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key) override
{
ActionSequence->Press(ElementLocator, Key);
return *this;
}
virtual IActionSequence& Press(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton) override
{
ActionSequence->Press(ElementLocator, MouseButton);
return *this;
}
virtual IActionSequence& PressChord(FKey Key1, FKey Key2) override
{
ActionSequence->PressChord(Key1, Key2);
return *this;
}
virtual IActionSequence& PressChord(FKey Key1, TCHAR Character) override
{
ActionSequence->PressChord(Key1, Character);
return *this;
}
virtual IActionSequence& PressChord(FKey Key1, FKey Key2, FKey Key3) override
{
ActionSequence->PressChord(Key1, Key2, Key3);
return *this;
}
virtual IActionSequence& PressChord(FKey Key1, FKey Key2, TCHAR Character) override
{
ActionSequence->PressChord(Key1, Key2, Character);
return *this;
}
virtual IActionSequence& PressChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2) override
{
ActionSequence->PressChord(ElementLocator, Key1, Key2);
return *this;
}
virtual IActionSequence& PressChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, TCHAR Character) override
{
ActionSequence->PressChord(ElementLocator, Key1, Character);
return *this;
}
virtual IActionSequence& PressChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, FKey Key3) override
{
ActionSequence->PressChord(ElementLocator, Key1, Key2, Key3);
return *this;
}
virtual IActionSequence& PressChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, TCHAR Character) override
{
ActionSequence->PressChord(ElementLocator, Key1, Key2, Character);
return *this;
}
virtual IActionSequence& Release(TCHAR Character) override
{
ActionSequence->Release(Character);
return *this;
}
virtual IActionSequence& Release(FKey Key) override
{
ActionSequence->Release(Key);
return *this;
}
virtual IActionSequence& Release(EMouseButtons::Type MouseButton) override
{
ActionSequence->Release(MouseButton);
return *this;
}
virtual IActionSequence& Release(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, TCHAR Character) override
{
ActionSequence->Release(ElementLocator, Character);
return *this;
}
virtual IActionSequence& Release(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key) override
{
ActionSequence->Release(ElementLocator, Key);
return *this;
}
virtual IActionSequence& Release(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, EMouseButtons::Type MouseButton) override
{
ActionSequence->Release(ElementLocator, MouseButton);
return *this;
}
virtual IActionSequence& ReleaseChord(FKey Key1, FKey Key2) override
{
ActionSequence->ReleaseChord(Key1, Key2);
return *this;
}
virtual IActionSequence& ReleaseChord(FKey Key1, TCHAR Character) override
{
ActionSequence->ReleaseChord(Key1, Character);
return *this;
}
virtual IActionSequence& ReleaseChord(FKey Key1, FKey Key2, FKey Key3) override
{
ActionSequence->ReleaseChord(Key1, Key2, Key3);
return *this;
}
virtual IActionSequence& ReleaseChord(FKey Key1, FKey Key2, TCHAR Character) override
{
ActionSequence->ReleaseChord(Key1, Key2, Character);
return *this;
}
virtual IActionSequence& ReleaseChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2) override
{
ActionSequence->ReleaseChord(ElementLocator, Key1, Key2);
return *this;
}
virtual IActionSequence& ReleaseChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, TCHAR Character) override
{
ActionSequence->ReleaseChord(ElementLocator, Key1, Character);
return *this;
}
virtual IActionSequence& ReleaseChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, FKey Key3) override
{
ActionSequence->ReleaseChord(ElementLocator, Key1, Key2, Key3);
return *this;
}
virtual IActionSequence& ReleaseChord(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, FKey Key1, FKey Key2, TCHAR Character) override
{
ActionSequence->ReleaseChord(ElementLocator, Key1, Key2, Character);
return *this;
}
virtual IActionSequence& Focus(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator) override
{
ActionSequence->Focus(ElementLocator);
return *this;
}
virtual IActionSequence& Focus(const TSharedRef<IElementLocator, ESPMode::ThreadSafe>& ElementLocator, uint32 UserFocus) override
{
ActionSequence->Focus(ElementLocator, UserFocus);
return *this;
}
bool Perform()
{
return ActionSequence->Perform().GetFuture().Get();
}
private:
FActionSequence(
const TSharedRef<FAutomationDriver, ESPMode::ThreadSafe>& InDriver,
const TSharedRef<FAsyncActionSequence, ESPMode::ThreadSafe>& InActionSequence)
: Driver(InDriver)
, ActionSequence(InActionSequence)
{ }
private:
const TSharedRef<FAutomationDriver, ESPMode::ThreadSafe> Driver;
const TSharedRef<FAsyncActionSequence, ESPMode::ThreadSafe> ActionSequence;
friend FActionSequenceFactory;
};
TSharedRef<FActionSequence, ESPMode::ThreadSafe> FActionSequenceFactory::Create(
const TSharedRef<FAutomationDriver, ESPMode::ThreadSafe>& Driver,
const TSharedRef<FAsyncAutomationDriver, ESPMode::ThreadSafe>& AsyncDriver,
const TSharedRef<FAutomatedApplication, ESPMode::ThreadSafe>& Application)
{
return MakeShareable(new FActionSequence(
Driver,
FAsyncActionSequenceFactory::Create(AsyncDriver, Application)));
}
class FDriverSequence
: public IDriverSequence
{
public:
virtual ~FDriverSequence()
{ }
virtual IActionSequence& Actions()
{
return *ActionSequence;
}
virtual bool Perform()
{
return ActionSequence->Perform();
}
FDriverSequence(
const TSharedRef<FActionSequence, ESPMode::ThreadSafe>& InActionSequence)
: ActionSequence(InActionSequence)
{ }
private:
const TSharedRef<FActionSequence, ESPMode::ThreadSafe> ActionSequence;
friend FDriverSequenceFactory;
};
TSharedRef<IDriverSequence, ESPMode::ThreadSafe> FDriverSequenceFactory::Create(
const TSharedRef<FActionSequence, ESPMode::ThreadSafe>& ActionSequence)
{
return MakeShareable(new FDriverSequence(
ActionSequence));
}