Hello GitHub
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A43517B5-8BE3-4312-913F-004978C34444}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Calculator.UIAutomationLibrary</RootNamespace>
|
||||
<AssemblyName>Calculator.UIAutomationLibrary</AssemblyName>
|
||||
<TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
|
||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
||||
<NuGetTargetMoniker>.NETCore,Version=v5.0</NuGetTargetMoniker>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.17763.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.17134.0</TargetPlatformMinVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CalculatorAppLauncher.cs" />
|
||||
<Compile Include="Components\App\CalculatorAppLfm.cs" />
|
||||
<Compile Include="Components\App\CalculatorAppPom.cs" />
|
||||
<Compile Include="Components\ContentDialogLfm.cs" />
|
||||
<Compile Include="Components\Pages\CalculatorBasePom.cs" />
|
||||
<Compile Include="Components\Pages\AboutFlyoutLfm.cs" />
|
||||
<Compile Include="Components\Pages\AboutFlyoutPom.cs" />
|
||||
<Compile Include="Components\Pages\DateCalculatorLfm.cs" />
|
||||
<Compile Include="Components\Pages\DateCalculatorPom.cs" />
|
||||
<Compile Include="Components\Pages\UnitConverterLfm.cs" />
|
||||
<Compile Include="Components\Pages\UnitConverterPom.cs" />
|
||||
<Compile Include="Components\Shared\HistoryLfm.cs" />
|
||||
<Compile Include="Components\Shared\HistoryPom.cs" />
|
||||
<Compile Include="Components\Shared\ICanFocusWithClicks.cs" />
|
||||
<Compile Include="Components\Shared\MemoryLfm.cs" />
|
||||
<Compile Include="Components\Shared\MemoryPom.cs" />
|
||||
<Compile Include="Components\Pages\NavBarLfm.cs" />
|
||||
<Compile Include="Components\Pages\NavBarPom.cs" />
|
||||
<Compile Include="Components\Shared\NumberPadPom.cs" />
|
||||
<Compile Include="Components\Pages\ProgrammerCalculatorLfm.cs" />
|
||||
<Compile Include="Components\Pages\ProgrammerCalculatorPom.cs" />
|
||||
<Compile Include="Components\Pages\ScientificCalculatorLfm.cs" />
|
||||
<Compile Include="Components\Pages\ScientificCalculatorPom.cs" />
|
||||
<Compile Include="Components\Pages\StandardCalculatorLfm.cs" />
|
||||
<Compile Include="Components\Pages\StandardCalculatorPom.cs" />
|
||||
<Compile Include="Components\Pages\MainPageLfm.cs" />
|
||||
<Compile Include="Components\Pages\MainPagePom.cs" />
|
||||
<Compile Include="Utilities\Impersonator.cs" />
|
||||
<Compile Include="Utilities\PerfTestConstants.cs" />
|
||||
<Compile Include="Tests\BasicCalculationTest.cs" />
|
||||
<Compile Include="Tests\ScientificCalculationTest.cs" />
|
||||
<Compile Include="Utilities\EtwHelper.cs" />
|
||||
<Compile Include="Utilities\InstallHelper.cs" />
|
||||
<Compile Include="Components\ContentDialogPom.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Utilities\Constants.cs" />
|
||||
<Compile Include="Utilities\NativeMethods.cs" />
|
||||
<Compile Include="Utilities\ServiceHelper.cs" />
|
||||
<Compile Include="Utilities\UIObjectExtensions.cs" />
|
||||
<Compile Include="Utilities\Utilities.cs" />
|
||||
<Compile Include="Utilities\WindowHelper.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="project.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '15.0' ">
|
||||
<VisualStudioVersion>15.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!--additional imports-->
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Etw.Managed;
|
||||
using Microsoft.OneCoreUap.Test.AppModel;
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Calculator.UIAutomationLibrary.Components;
|
||||
using WEX.Logging.Interop;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that can open and close the Calculator app.
|
||||
/// </summary>
|
||||
public static class CalculatorAppLauncher
|
||||
{
|
||||
public const string CoreWindowClassName = "Windows.UI.Core.CoreWindow";
|
||||
|
||||
// This doesn't actually work right now becaue popup will disappear
|
||||
// Bug 13713223: ContentDialog/Popup does not show up in the UIA tree when Windows.Current.Content has an AutomationName set.
|
||||
// public static readonly UICondition TopLevelWindowUICondition = UICondition.CreateFromId(Constants.TopLevelWindowAutomationId);
|
||||
public static readonly UICondition CoreWindowUICondition = UICondition.CreateFromClassName(CoreWindowClassName)
|
||||
.AndWith(UICondition.CreateFromName(Constants.AppWindowName));
|
||||
|
||||
/// <summary>
|
||||
/// Launch the app
|
||||
/// </summary>
|
||||
public static CalculatorAppLfm Launch()
|
||||
{
|
||||
Log.Comment("Launching Calculator and waiting for first page load...");
|
||||
|
||||
// Need to set this for the MITALite Tap~ methods to work on high DPI screens.
|
||||
UAPApp.SetTestDPIAwareness();
|
||||
|
||||
// We want to be able to see any element in the tree
|
||||
Context.RawContext.Activate();
|
||||
|
||||
// Set default wait timeout.
|
||||
MS.Internal.Mita.Foundation.Waiters.Waiter.DefaultTimeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
// Enable Mita internal logging.
|
||||
MS.Internal.Mita.Foundation.Utilities.Log.OutImplementation = (s, a) => { Log.Comment($"- [MitaLite] { string.Format(s, a) }"); };
|
||||
|
||||
using (EtwWaiter appLaunchWaiter = new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.AppLaunchEndETWEventName))
|
||||
{
|
||||
var viewDescriptor = NavigationHelper.LaunchApplication(Constants.PackageAppUserModelId);
|
||||
appLaunchWaiter.Wait(TimeSpan.FromSeconds(30));
|
||||
|
||||
Window calculatorWindow = new Window(UIObject.Root.Descendants.Find(CoreWindowUICondition));
|
||||
Debug.Assert(calculatorWindow.ClassName == CoreWindowClassName);
|
||||
|
||||
// Move our window to the foreground.
|
||||
WindowHelper.SetAsForeground(calculatorWindow.GetTopLevelWindow());
|
||||
|
||||
return new CalculatorAppLfm(new CalculatorAppPom(calculatorWindow), viewDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Microsoft.OneCoreUap.Test.AppModel;
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class CalculatorAppLfm
|
||||
{
|
||||
private readonly IViewDescriptor viewDescriptor;
|
||||
|
||||
public CalculatorAppLfm(CalculatorAppPom objectModel, IViewDescriptor viewDescriptor)
|
||||
{
|
||||
this.ObjectModel = objectModel;
|
||||
this.viewDescriptor = viewDescriptor;
|
||||
}
|
||||
|
||||
public CalculatorAppPom ObjectModel { get; }
|
||||
|
||||
public MainPageLfm MainPageLfm
|
||||
{
|
||||
get
|
||||
{
|
||||
return new MainPageLfm(this.ObjectModel.MainPagePom);
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
// ObjectModel is essentially the window ui object.
|
||||
if (this.viewDescriptor != null)
|
||||
{
|
||||
NavigationHelper.CloseApplication(this.viewDescriptor.AUMID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class CalculatorAppPom : UIObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="CalculatorAppPom"/> class.
|
||||
/// </summary>
|
||||
/// <param name="uiObject">UIObject for the calculator app window.</param>
|
||||
public CalculatorAppPom(UIObject uiObject)
|
||||
: base(uiObject)
|
||||
{
|
||||
}
|
||||
public MainPagePom MainPagePom
|
||||
{
|
||||
get
|
||||
{
|
||||
return new MainPagePom(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class ContentDialogLfm
|
||||
{
|
||||
public ContentDialogLfm(ContentDialogPom objectModel)
|
||||
{
|
||||
this.ObjectModel = objectModel;
|
||||
}
|
||||
|
||||
public ContentDialogPom ObjectModel { get; }
|
||||
|
||||
public void InvokePrimary()
|
||||
{
|
||||
this.ObjectModel.PrimaryButton.Invoke();
|
||||
}
|
||||
public void InvokeSecondary()
|
||||
{
|
||||
this.ObjectModel.SecondaryButton.Invoke();
|
||||
}
|
||||
|
||||
public void InvokeClose()
|
||||
{
|
||||
this.ObjectModel.CloseButton.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class ContentDialogPom : UIObject
|
||||
{
|
||||
private static readonly UICondition textScrollViewerCondition = UICondition.CreateFromId("ContentScrollViewer");
|
||||
private static readonly UICondition titleTextBlockCondition = UICondition.CreateFromClassName("TextBlock");
|
||||
|
||||
private static readonly UICondition primaryButtonCondition =
|
||||
UICondition.CreateFromClassName("Button")
|
||||
.AndWith(UICondition.CreateFromId("PrimaryButton"));
|
||||
private static readonly UICondition secondaryButtonCondition =
|
||||
UICondition.CreateFromClassName("Button")
|
||||
.AndWith(UICondition.CreateFromId("SecondaryButton"));
|
||||
private static readonly UICondition closeButtonCondition =
|
||||
UICondition.CreateFromClassName("Button")
|
||||
.AndWith(UICondition.CreateFromId("CloseButton"));
|
||||
|
||||
public ContentDialogPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public Button PrimaryButton
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Button(this.Children.Find(primaryButtonCondition));
|
||||
}
|
||||
}
|
||||
|
||||
public Button SecondaryButton
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Button(this.Children.Find(secondaryButtonCondition));
|
||||
}
|
||||
}
|
||||
|
||||
public Button CloseButton
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Button(this.Children.Find(closeButtonCondition));
|
||||
}
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get
|
||||
{
|
||||
var scrollViewer = this.Children.Find(textScrollViewerCondition);
|
||||
var textBlock = scrollViewer.Children.Find(titleTextBlockCondition);
|
||||
return textBlock.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class AboutFlyoutLfm : ICanFocusWithClicks
|
||||
{
|
||||
private const string FlyoutId = "FlyoutNav";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AboutFlyoutLfm" /> class.
|
||||
/// </summary>
|
||||
/// <param name="objectModel">The AboutFlyoutPom that represents the About flyout panel.</param>
|
||||
public AboutFlyoutLfm(AboutFlyoutPom objectModel)
|
||||
{
|
||||
this.ObjectModel = objectModel;
|
||||
}
|
||||
|
||||
public AboutFlyoutPom ObjectModel { get; }
|
||||
|
||||
public void FocusWithClicks()
|
||||
{
|
||||
this.ObjectModel.Title.DoubleClick();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
this.ObjectModel.SendKeys("{ESC}");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class AboutFlyoutPom : UIObject
|
||||
{
|
||||
private const string TitleId = "Header";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AboutFlyoutPom" /> class.
|
||||
/// </summary>
|
||||
/// <param name="uiObject">The UIObject that is the root of the navigation menu.</param>
|
||||
public AboutFlyoutPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public UIObject Title
|
||||
{
|
||||
get
|
||||
{
|
||||
return new UIObject(this.Descendants.Find(TitleId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Display section of the calculator modes.
|
||||
/// </summary>
|
||||
public class CalculatorBasePom : UIObject
|
||||
{
|
||||
private const string ExpressionContainerId = "CalculatorExpression";
|
||||
private const string NormalOutputId = "normalOutput";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StandardCalculatorPom" /> class.
|
||||
/// </summary>
|
||||
/// <param name="uiObject">The UIObject that is the root of the standard calculator.</param>
|
||||
public CalculatorBasePom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public TextBlock Expression
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TextBlock(this.Descendants.Find(ExpressionContainerId));
|
||||
}
|
||||
}
|
||||
|
||||
public UIEventWaiter GetExpressionChangedWaiter()
|
||||
{
|
||||
return new PropertyChangedEventWaiter(this.Expression, UIProperty.Get("Name"));
|
||||
}
|
||||
|
||||
public TextBlock Display
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TextBlock(this.Descendants.Find(NormalOutputId));
|
||||
}
|
||||
}
|
||||
|
||||
public UIEventWaiter GetDisplayChangedWaiter()
|
||||
{
|
||||
return new PropertyChangedEventWaiter(this.Display, UIProperty.Get("Name"));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class DateCalculatorLfm
|
||||
{
|
||||
public DateCalculatorLfm(DateCalculatorPom dateCalculatorPom)
|
||||
{
|
||||
this.ObjectModel = dateCalculatorPom;
|
||||
}
|
||||
|
||||
public DateCalculatorPom ObjectModel { get; }
|
||||
|
||||
public void EnsureDateDifferenceMode()
|
||||
{
|
||||
this.OpenModeSelector();
|
||||
this.ObjectModel.ModeSelector.AllItems[0].Select();
|
||||
}
|
||||
|
||||
public void EnsureAddSubtractMode()
|
||||
{
|
||||
this.OpenModeSelector();
|
||||
this.ObjectModel.ModeSelector.AllItems[1].Select();
|
||||
}
|
||||
|
||||
private void OpenModeSelector()
|
||||
{
|
||||
using (var waiter = this.ObjectModel.ModeSelector.GetExpandedWaiter())
|
||||
{
|
||||
this.ObjectModel.ModeSelector.Expand();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class DateCalculatorPom : UIObject
|
||||
{
|
||||
private const string ModeSelectorId = "DateCalculationOption";
|
||||
|
||||
public DateCalculatorPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public ComboBox ModeSelector
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ComboBox(this.Descendants.Find(ModeSelectorId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,255 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Etw.Managed;
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class MainPageLfm : ICanFocusWithClicks
|
||||
{
|
||||
public MainPageLfm(MainPagePom objectModel)
|
||||
{
|
||||
this.ObjectModel = objectModel;
|
||||
}
|
||||
|
||||
public MainPagePom ObjectModel { get; }
|
||||
|
||||
public NavBarLfm OpenNavBar()
|
||||
{
|
||||
using (EtwWaiter waiter = this.ObjectModel.GetNavBarOpenedWaiter())
|
||||
{
|
||||
this.ObjectModel.NavButton.Invoke();
|
||||
waiter.Wait();
|
||||
}
|
||||
|
||||
return new NavBarLfm(this.ObjectModel.NavBarPom);
|
||||
}
|
||||
|
||||
public void CloseNavBar()
|
||||
{
|
||||
this.ObjectModel.NavBarPom.CloseButton.Invoke();
|
||||
}
|
||||
|
||||
public void FocusWithClicks()
|
||||
{
|
||||
this.ObjectModel.Header.DoubleClick();
|
||||
}
|
||||
|
||||
public StandardCalculatorLfm NavigateToStandardCalculator()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectStandard();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new StandardCalculatorLfm(this.ObjectModel.StandardCalculatorPom);
|
||||
}
|
||||
|
||||
public ScientificCalculatorLfm NavigateToScientificCalculator()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectScientific();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new ScientificCalculatorLfm(this.ObjectModel.ScientificCalculatorPom);
|
||||
}
|
||||
|
||||
public ProgrammerCalculatorLfm NavigateToProgrammerCalculator()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectProgrammer();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new ProgrammerCalculatorLfm(this.ObjectModel.ProgrammerCalculatorPom);
|
||||
}
|
||||
|
||||
public DateCalculatorLfm NavigateToDateCalculator()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectDate();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new DateCalculatorLfm(this.ObjectModel.DateCalculatorPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToCurrencyConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectCurrency();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToVolumeConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectVolume();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToLengthConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectLength();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToWeightConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectWeight();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToTemperatureConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectTemperature();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToEnergyConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectEnergy();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToAreaConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectArea();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToSpeedConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectSpeed();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToTimeConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectTime();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToPowerConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectPower();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToDataConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectData();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToPressureConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectPressure();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public UnitConverterLfm NavigateToAngleConverter()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (var waiter = this.ObjectModel.GetModeChangedWaiter())
|
||||
{
|
||||
navBar.SelectAngle();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
return new UnitConverterLfm(this.ObjectModel.UnitConverterPom);
|
||||
}
|
||||
|
||||
public AboutFlyoutLfm OpenAboutFlyout()
|
||||
{
|
||||
var navBar = this.OpenNavBar();
|
||||
using (EtwWaiter waiter = new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.AboutFlyoutOpenedETWEventName))
|
||||
{
|
||||
navBar.SelectAbout();
|
||||
waiter.Wait();
|
||||
}
|
||||
|
||||
return new AboutFlyoutLfm(this.ObjectModel.AboutFlyoutPom);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Etw.Managed;
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Physical Object Model for the app window.
|
||||
/// POM is the implementation model of the app.
|
||||
/// See following references to POM:
|
||||
/// * https://blogs.msdn.microsoft.com/wltester/2011/11/14/object-model-design/
|
||||
/// * https://blogs.msdn.microsoft.com/micahel/2005/06/03/how-do-i-invoke-thee-let-me-count-the-ways-the-physical-object-model/
|
||||
/// See https://en.wikipedia.org/wiki/Model-based_testing for model-based testing.
|
||||
/// </summary>
|
||||
public class MainPagePom : UIObject
|
||||
{
|
||||
private const string NavButtonId = "TogglePaneButton";
|
||||
private const string SplitViewPaneRootId = "PaneRoot";
|
||||
private const string NavBarFlyoutId = "FlyoutNav";
|
||||
private const string HeaderId = "Header";
|
||||
private const string AboutPageFlyoutId = "AboutPageFlyout";
|
||||
|
||||
public MainPagePom(UIObject uiObject)
|
||||
: base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public Button NavButton => new Button(this.Descendants.Find(UICondition.CreateFromId(NavButtonId)));
|
||||
|
||||
public UIObject Header => new UIObject(this.Descendants.Find(HeaderId));
|
||||
|
||||
public NavBarPom NavBarPom => new NavBarPom(this.Children.Find(SplitViewPaneRootId));
|
||||
|
||||
public EtwWaiter GetNavBarOpenedWaiter() => new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.NavBarOpenedETWEventName);
|
||||
|
||||
public StandardCalculatorPom StandardCalculatorPom => new StandardCalculatorPom(this);
|
||||
|
||||
public ScientificCalculatorPom ScientificCalculatorPom => new ScientificCalculatorPom(this);
|
||||
|
||||
public ProgrammerCalculatorPom ProgrammerCalculatorPom => new ProgrammerCalculatorPom(this);
|
||||
|
||||
public DateCalculatorPom DateCalculatorPom => new DateCalculatorPom(this);
|
||||
|
||||
public UnitConverterPom UnitConverterPom => new UnitConverterPom(this);
|
||||
|
||||
public AboutFlyoutPom AboutFlyoutPom => new AboutFlyoutPom(this.Descendants.Find(AboutPageFlyoutId));
|
||||
|
||||
public EtwWaiter GetModeChangedWaiter() => new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.AppModeChangeEndETWEventName);
|
||||
}
|
||||
}
|
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Windows.Automation;
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
using MS.Internal.Mita.Foundation.Patterns;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the NavBar of the Calculator app.
|
||||
/// </summary>
|
||||
public class NavBarLfm : ICanFocusWithClicks
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NavBarLfm" /> class.
|
||||
/// </summary>
|
||||
/// <param name="objectModel">The NavBarPom that represents the NavBar.</param>
|
||||
public NavBarLfm(NavBarPom objectModel)
|
||||
{
|
||||
this.ObjectModel = objectModel;
|
||||
}
|
||||
|
||||
public NavBarPom ObjectModel { get; }
|
||||
|
||||
public void SelectStandard()
|
||||
{
|
||||
SelectItem(this.ObjectModel.StandardMenuItem);
|
||||
}
|
||||
|
||||
public void SelectScientific()
|
||||
{
|
||||
SelectItem(this.ObjectModel.ScientificMenuItem);
|
||||
}
|
||||
|
||||
public void SelectProgrammer()
|
||||
{
|
||||
SelectItem(this.ObjectModel.ProgrammerMenuItem);
|
||||
}
|
||||
|
||||
public void SelectDate()
|
||||
{
|
||||
SelectItem(this.ObjectModel.DateMenuItem);
|
||||
}
|
||||
|
||||
public void SelectCurrency()
|
||||
{
|
||||
SelectItem(this.ObjectModel.CurrencyMenuItem);
|
||||
}
|
||||
|
||||
public void SelectVolume()
|
||||
{
|
||||
SelectItem(this.ObjectModel.VolumeMenuItem);
|
||||
}
|
||||
|
||||
public void SelectLength()
|
||||
{
|
||||
SelectItem(this.ObjectModel.LengthMenuItem);
|
||||
}
|
||||
|
||||
public void SelectWeight()
|
||||
{
|
||||
SelectItem(this.ObjectModel.WeightMenuItem);
|
||||
}
|
||||
|
||||
public void SelectTemperature()
|
||||
{
|
||||
SelectItem(this.ObjectModel.TemperatureMenuItem);
|
||||
}
|
||||
|
||||
public void SelectEnergy()
|
||||
{
|
||||
SelectItem(this.ObjectModel.EnergyMenuItem);
|
||||
}
|
||||
|
||||
public void SelectArea()
|
||||
{
|
||||
SelectItem(this.ObjectModel.AreaMenuItem);
|
||||
}
|
||||
|
||||
public void SelectSpeed()
|
||||
{
|
||||
SelectItem(this.ObjectModel.SpeedMenuItem);
|
||||
}
|
||||
|
||||
public void SelectTime()
|
||||
{
|
||||
SelectItem(this.ObjectModel.TimeMenuItem);
|
||||
}
|
||||
|
||||
public void SelectPower()
|
||||
{
|
||||
SelectItem(this.ObjectModel.PowerMenuItem);
|
||||
}
|
||||
|
||||
public void SelectData()
|
||||
{
|
||||
SelectItem(this.ObjectModel.DataMenuItem);
|
||||
}
|
||||
|
||||
public void SelectPressure()
|
||||
{
|
||||
SelectItem(this.ObjectModel.PressureMenuItem);
|
||||
}
|
||||
|
||||
public void SelectAngle()
|
||||
{
|
||||
SelectItem(this.ObjectModel.AngleMenuItem);
|
||||
}
|
||||
|
||||
public void SelectAbout()
|
||||
{
|
||||
this.ObjectModel.AboutButton.Invoke();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
this.ObjectModel.CloseButton.Invoke();
|
||||
}
|
||||
|
||||
public void FocusWithClicks()
|
||||
{
|
||||
// To focus (for AccSpot) without changing anything, click to the right of the close button.
|
||||
Button button = this.ObjectModel.CloseButton;
|
||||
int xPos = button.BoundingRectangle.Width + Constants.ClickMargin;
|
||||
int yPos = button.BoundingRectangle.Height / 2;
|
||||
button.DoubleClick(PointerButtons.Primary, xPos, yPos);
|
||||
}
|
||||
|
||||
private void SelectItem(ListViewItem item)
|
||||
{
|
||||
if (item.IsSelected)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Select();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Linq;
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the navigation menu.
|
||||
/// </summary>
|
||||
public class NavBarPom : UIObject
|
||||
{
|
||||
private const string StandardId = "Standard";
|
||||
private const string ScientificId = "Scientific";
|
||||
private const string ProgrammerId = "Programmer";
|
||||
private const string DateId = "Date";
|
||||
private const string CurrencyId = "Currency";
|
||||
private const string VolumeId = "Volume";
|
||||
private const string LengthId = "Length";
|
||||
private const string WeightId = "Weight";
|
||||
private const string TemperatureId = "Temperature";
|
||||
private const string EnergyId = "Energy";
|
||||
private const string AreaId = "Area";
|
||||
private const string SpeedId = "Speed";
|
||||
private const string TimeId = "Time";
|
||||
private const string PowerId = "Power";
|
||||
private const string DataId = "Data";
|
||||
private const string PressureId = "Pressure";
|
||||
private const string AngleId = "Angle";
|
||||
private const string AboutId = "AboutButton";
|
||||
private const string CloseId = "TogglePaneButton";
|
||||
private const string FlyoutListViewId = "MenuItemsHost";
|
||||
private const string ConverterSectionId = "Converter";
|
||||
private const string ConverterTextKey = "ConverterModeText";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NavBarPom" /> class.
|
||||
/// </summary>
|
||||
/// <param name="uiObject">The UIObject that is the root of the navigation menu.</param>
|
||||
public NavBarPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public ListViewItem StandardMenuItem => ScrollAndGetItem(StandardId);
|
||||
|
||||
public ListViewItem ScientificMenuItem => ScrollAndGetItem(ScientificId);
|
||||
|
||||
public ListViewItem ProgrammerMenuItem => ScrollAndGetItem(ProgrammerId);
|
||||
|
||||
public ListViewItem DateMenuItem => ScrollAndGetItem(DateId);
|
||||
|
||||
public ListViewItem CurrencyMenuItem => ScrollAndGetItem(CurrencyId);
|
||||
|
||||
public ListViewItem VolumeMenuItem => ScrollAndGetItem(VolumeId);
|
||||
|
||||
public ListViewItem LengthMenuItem => ScrollAndGetItem(LengthId);
|
||||
|
||||
public ListViewItem WeightMenuItem => ScrollAndGetItem(WeightId);
|
||||
|
||||
public ListViewItem TemperatureMenuItem => ScrollAndGetItem(TemperatureId);
|
||||
|
||||
public ListViewItem EnergyMenuItem => ScrollAndGetItem(EnergyId);
|
||||
|
||||
public ListViewItem AreaMenuItem => ScrollAndGetItem(AreaId);
|
||||
|
||||
public ListViewItem SpeedMenuItem => ScrollAndGetItem(SpeedId);
|
||||
|
||||
public ListViewItem TimeMenuItem => ScrollAndGetItem(TimeId);
|
||||
|
||||
public ListViewItem PowerMenuItem => ScrollAndGetItem(PowerId);
|
||||
|
||||
public ListViewItem DataMenuItem => ScrollAndGetItem(DataId);
|
||||
|
||||
public ListViewItem PressureMenuItem => ScrollAndGetItem(PressureId);
|
||||
|
||||
public ListViewItem AngleMenuItem => ScrollAndGetItem(AngleId);
|
||||
|
||||
public Button AboutButton => new Button(this.Descendants.Find(AboutId));
|
||||
|
||||
public Button CloseButton => new Button(this.Parent.Children.Find(CloseId));
|
||||
|
||||
public ListView ModeListView => new ListView(this.Descendants.Find(FlyoutListViewId));
|
||||
|
||||
private ListViewItem ScrollAndGetItem(string id)
|
||||
{
|
||||
ListViewItem item;
|
||||
var res = this.ModeListView.AllItems.TryFind(id, out item);
|
||||
|
||||
item.ScrollIntoView();
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class ProgrammerCalculatorLfm
|
||||
{
|
||||
public ProgrammerCalculatorLfm(ProgrammerCalculatorPom programmerCalculatorPom)
|
||||
{
|
||||
this.ObjectModel = programmerCalculatorPom;
|
||||
}
|
||||
|
||||
public ProgrammerCalculatorPom ObjectModel { get; }
|
||||
|
||||
public void EnsureFullKeypad()
|
||||
{
|
||||
if (!this.ObjectModel.FullKeypadButton.IsSelected)
|
||||
{
|
||||
this.ObjectModel.FullKeypadButton.Select();
|
||||
}
|
||||
}
|
||||
|
||||
public void EnsureBitTogglingKeypad()
|
||||
{
|
||||
if (!this.ObjectModel.BitFlipKeypadButton.IsSelected)
|
||||
{
|
||||
this.ObjectModel.BitFlipKeypadButton.Select();
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeBitLength()
|
||||
{
|
||||
this.ObjectModel.GetCurrentBitLengthButton().Invoke();
|
||||
}
|
||||
|
||||
public MemoryLfm OpenMemory()
|
||||
{
|
||||
MemoryLfm lfm = new MemoryLfm(this.ObjectModel.MemoryControls);
|
||||
lfm.OpenBody();
|
||||
return lfm;
|
||||
}
|
||||
|
||||
public void FiveMemorySet()
|
||||
{
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
this.ObjectModel.NumberPad.FiveButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
this.ObjectModel.MemoryControls.SetButton.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class ProgrammerCalculatorPom : UIObject
|
||||
{
|
||||
private const string FullKeypadButtonId = "fullKeypad";
|
||||
private const string BitFlipKeypadButtonId = "bitFlip";
|
||||
private const string CalculatorResultsId = "CalculatorResults";
|
||||
private const string NumberPadId = "NumberPad";
|
||||
|
||||
private readonly string[] BitLengthButtonIds =
|
||||
{
|
||||
"qwordButton",
|
||||
"dwordButton",
|
||||
"wordButton",
|
||||
"byteButton"
|
||||
};
|
||||
|
||||
public ProgrammerCalculatorPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public NumberPadPom NumberPad => new NumberPadPom(this.Descendants.Find(NumberPadId));
|
||||
|
||||
public MemoryPom MemoryControls => new MemoryPom(this);
|
||||
|
||||
public RadioButton FullKeypadButton => new RadioButton(this.Descendants.Find(FullKeypadButtonId));
|
||||
|
||||
public RadioButton BitFlipKeypadButton => new RadioButton(this.Descendants.Find(BitFlipKeypadButtonId));
|
||||
|
||||
public TextBlock Display => new TextBlock(this.Descendants.Find(CalculatorResultsId));
|
||||
|
||||
public UIEventWaiter GetDisplayChangedWaiter() => this.Display.GetNameChangedWaiter();
|
||||
|
||||
public Button GetCurrentBitLengthButton()
|
||||
{
|
||||
// There are four bit length buttons, with only one visible at a time.
|
||||
UIObject button = null;
|
||||
foreach (var buttonId in this.BitLengthButtonIds)
|
||||
{
|
||||
if (this.Descendants.TryFind(buttonId, out button))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Button(button);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using WEX.Logging.Interop;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Standard calculator view.
|
||||
/// </summary>
|
||||
public class ScientificCalculatorLfm
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScientificCalculatorLfm" /> class.
|
||||
/// </summary>
|
||||
/// <param name="uiObject">The UIObject that is the root of the scientific Calculator.</param>
|
||||
public ScientificCalculatorLfm(ScientificCalculatorPom objectModel)
|
||||
{
|
||||
this.ObjectModel = objectModel;
|
||||
}
|
||||
|
||||
public ScientificCalculatorPom ObjectModel { get; }
|
||||
|
||||
public void Press1()
|
||||
{
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking 1");
|
||||
this.ObjectModel.OneButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void Press2()
|
||||
{
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking 2");
|
||||
this.ObjectModel.NumberPad.TwoButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void Press3()
|
||||
{
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking 3");
|
||||
this.ObjectModel.ThreeButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void Press4()
|
||||
{
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking 4");
|
||||
this.ObjectModel.FourButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void PressSqrt()
|
||||
{
|
||||
// When invoking sqrt, both the expression changes.
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetExpressionChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking sqrt");
|
||||
this.ObjectModel.SqrtButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void PressMinus()
|
||||
{
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetExpressionChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking minus");
|
||||
this.ObjectModel.MinusButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void PressPlus()
|
||||
{
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetExpressionChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking plus");
|
||||
this.ObjectModel.PlusButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void PressEquals()
|
||||
{
|
||||
// When invoking equals, both the display and the expression change.
|
||||
using (UIEventWaiter expressionWaiter = this.ObjectModel.GetExpressionChangedWaiter())
|
||||
using (UIEventWaiter displayWaiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking equals");
|
||||
this.ObjectModel.EqualButton.Invoke();
|
||||
expressionWaiter.TryWait();
|
||||
displayWaiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnePlusTwoEnter()
|
||||
{
|
||||
Press1();
|
||||
PressPlus();
|
||||
Press2();
|
||||
PressEquals();
|
||||
}
|
||||
|
||||
public void MemorySet() => this.ObjectModel.MemoryControls.SetButton.Invoke();
|
||||
|
||||
public MemoryLfm OpenMemory()
|
||||
{
|
||||
MemoryLfm lfm = new MemoryLfm(this.ObjectModel.MemoryControls);
|
||||
lfm.OpenBody();
|
||||
return lfm;
|
||||
}
|
||||
|
||||
public HistoryLfm OpenHistory()
|
||||
{
|
||||
HistoryLfm lfm = new HistoryLfm(this.ObjectModel.HistoryControls);
|
||||
lfm.OpenBody();
|
||||
return lfm;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Scientific Calculator
|
||||
/// </summary>
|
||||
public class ScientificCalculatorPom : CalculatorBasePom
|
||||
{
|
||||
private const string NumberPadId = "NumberPad";
|
||||
private const string StandardOperatorsId = "StandardOperators";
|
||||
private const string DisplayControlsId = "DisplayControls";
|
||||
private const string ScientificFunctionsId = "ScientificFunctions";
|
||||
private const string OneButtonId = "num1Button";
|
||||
private const string ThreeButtonId = "num3Button";
|
||||
private const string FourButtonId = "num4Button";
|
||||
private const string SqrtButtonId = "squareRootButton";
|
||||
private const string MinusButtonId = "minusButton";
|
||||
private const string PlusButtonId = "plusButton";
|
||||
private const string EqualButtonId = "equalButton";
|
||||
private const string ClearButtonId = "clearButton";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScientificCalculatorPom" /> class.
|
||||
/// </summary>
|
||||
/// <param name="uiObject">The UIObject that is the root of the scientific calculator.</param>
|
||||
public ScientificCalculatorPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public UIObject StandardOperatorsGroup => this.Descendants.Find(StandardOperatorsId);
|
||||
|
||||
public UIObject DisplayControlsGroup => this.Descendants.Find(DisplayControlsId);
|
||||
|
||||
public UIObject ScientificFunctionsGroup => this.Descendants.Find(ScientificFunctionsId);
|
||||
|
||||
public Button OneButton => this.NumberPad.OneButton;
|
||||
|
||||
public Button ThreeButton => this.NumberPad.ThreeButton;
|
||||
|
||||
public Button FourButton => this.NumberPad.FourButton;
|
||||
|
||||
public Button SqrtButton => new Button(this.ScientificFunctionsGroup.Children.Find(SqrtButtonId));
|
||||
|
||||
public Button MinusButton => new Button(this.StandardOperatorsGroup.Children.Find(MinusButtonId));
|
||||
|
||||
public Button PlusButton => new Button(this.StandardOperatorsGroup.Children.Find(PlusButtonId));
|
||||
|
||||
public Button EqualButton => new Button(this.StandardOperatorsGroup.Children.Find(EqualButtonId));
|
||||
|
||||
public Button ClearButton => new Button(this.DisplayControlsGroup.Children.Find(ClearButtonId));
|
||||
|
||||
public NumberPadPom NumberPad => new NumberPadPom(this.Descendants.Find(NumberPadId));
|
||||
|
||||
public HistoryPom HistoryControls => new HistoryPom(this);
|
||||
|
||||
public MemoryPom MemoryControls => new MemoryPom(this);
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
using WEX.Logging.Interop;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Standard calculator view.
|
||||
/// </summary>
|
||||
public class StandardCalculatorLfm
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StandardCalculatorLfm" /> class.
|
||||
/// </summary>
|
||||
/// <param name="uiObject">The UIObject that is the root of the Standard Calculator.</param>
|
||||
public StandardCalculatorLfm(StandardCalculatorPom objectModel)
|
||||
{
|
||||
this.ObjectModel = objectModel;
|
||||
}
|
||||
|
||||
public StandardCalculatorPom ObjectModel { get; }
|
||||
|
||||
public void OnePlusTwoEnter()
|
||||
{
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking 1");
|
||||
this.ObjectModel.NumPad.OneButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetExpressionChangedWaiter())
|
||||
{
|
||||
Log.Comment("Pressing +");
|
||||
this.ObjectModel.SendKeys("{ADD}");
|
||||
// PropertyChangeWaiter is unreliable for the first name changed notification
|
||||
// Bug 17624996: PropertyChanged event not fired when Name is updated for the first time for a control with custom automation peer.
|
||||
waiter.TryWait();
|
||||
}
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
Log.Comment("Pressing 2");
|
||||
this.ObjectModel.SendKeys("2");
|
||||
waiter.TryWait();
|
||||
}
|
||||
// When pressing enter, both the display and the expression change.
|
||||
using (UIEventWaiter expressionWaiter = this.ObjectModel.GetExpressionChangedWaiter())
|
||||
using (UIEventWaiter displayWaiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
Log.Comment("Invoking equals");
|
||||
this.ObjectModel.EqualButton.Invoke();
|
||||
expressionWaiter.TryWait();
|
||||
displayWaiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
Log.Comment("Pressing escape");
|
||||
this.ObjectModel.ClearButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearFiveMemorySet()
|
||||
{
|
||||
this.Clear();
|
||||
using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
this.ObjectModel.NumPad.FiveButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
|
||||
this.ObjectModel.MemoryControls.SetButton.Invoke();
|
||||
}
|
||||
|
||||
public MemoryLfm OpenMemory()
|
||||
{
|
||||
MemoryLfm lfm = new MemoryLfm(this.ObjectModel.MemoryControls);
|
||||
lfm.OpenBody();
|
||||
return lfm;
|
||||
}
|
||||
|
||||
public HistoryLfm OpenHistory()
|
||||
{
|
||||
HistoryLfm lfm = new HistoryLfm(this.ObjectModel.HistoryControls);
|
||||
lfm.OpenBody();
|
||||
return lfm;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Standard Calculator
|
||||
/// </summary>
|
||||
public class StandardCalculatorPom : CalculatorBasePom
|
||||
{
|
||||
private const string CalculatorResultsId = "CalculatorResults";
|
||||
private const string ExpressionContainerId = "CalculatorExpression";
|
||||
private const string NumberPadId = "NumberPad";
|
||||
private const string StandardOperatorsId = "StandardOperators";
|
||||
private const string DisplayControlsId = "DisplayControls";
|
||||
private const string EqualButtonId = "equalButton";
|
||||
private const string ClearButtonId = "clearButton";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StandardCalculatorPom" /> class.
|
||||
/// </summary>
|
||||
/// <param name="uiObject">The UIObject that is the root of the standard calculator.</param>
|
||||
public StandardCalculatorPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public NumberPadPom NumPad => new NumberPadPom(this.Descendants.Find(NumberPadId));
|
||||
|
||||
public MemoryPom MemoryControls => new MemoryPom(this);
|
||||
|
||||
public HistoryPom HistoryControls => new HistoryPom(this);
|
||||
|
||||
public UIObject StandardOperatorsGroup => this.Descendants.Find(StandardOperatorsId);
|
||||
|
||||
public UIObject DisplayControlsGroup => this.Descendants.Find(DisplayControlsId);
|
||||
|
||||
public Button EqualButton => new Button(this.StandardOperatorsGroup.Children.Find(EqualButtonId));
|
||||
|
||||
public Button ClearButton => new Button(this.DisplayControlsGroup.Children.Find(ClearButtonId));
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class UnitConverterLfm
|
||||
{
|
||||
public UnitConverterLfm(UnitConverterPom unitConverterPom)
|
||||
{
|
||||
this.ObjectModel = unitConverterPom;
|
||||
}
|
||||
|
||||
public UnitConverterPom ObjectModel { get; }
|
||||
|
||||
public void Eight()
|
||||
{
|
||||
using (var waiter = this.ObjectModel.GetDisplayChangedWaiter())
|
||||
{
|
||||
this.ObjectModel.NumberPad.EightButton.Invoke();
|
||||
waiter.TryWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class UnitConverterPom : UIObject
|
||||
{
|
||||
private const string DisplayId = "Value1";
|
||||
private const string NumberPadId = "numberPad";
|
||||
|
||||
public UnitConverterPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public NumberPadPom NumberPad => new NumberPadPom(this.Descendants.Find(NumberPadId));
|
||||
|
||||
public TextBlock Display => new TextBlock(this.Descendants.Find(DisplayId));
|
||||
|
||||
public PropertyChangedEventWaiter GetDisplayChangedWaiter() => this.Display.GetNameChangedWaiter();
|
||||
|
||||
public ElementAddedWaiter GetDisplayElementAddedWaiter() => new ElementAddedWaiter(this, Scope.Descendants, DisplayId);
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Etw.Managed;
|
||||
using MS.Internal.Mita.Foundation;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class HistoryLfm : ICanFocusWithClicks
|
||||
{
|
||||
public HistoryLfm(HistoryPom historyPom)
|
||||
{
|
||||
this.ObjectModel = historyPom;
|
||||
}
|
||||
|
||||
public HistoryPom ObjectModel { get; }
|
||||
|
||||
public void FocusWithClicks()
|
||||
{
|
||||
// On the Programming calc, the default click location can land on the first memory item, dismissing the flyout.
|
||||
// Instead, click just below, in the gutter to the left of the trash can.
|
||||
var body = this.ObjectModel.Body;
|
||||
int height = body.BoundingRectangle.Height;
|
||||
body.DoubleClick(PointerButtons.Primary, Constants.ClickMargin, height + Constants.ClickMargin);
|
||||
}
|
||||
|
||||
public void OpenBody()
|
||||
{
|
||||
using (EtwWaiter waiter = new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.HistoryBodyOpenedETWEventName))
|
||||
{
|
||||
// Only one exists at a given time.
|
||||
if (this.ObjectModel.IsHistoryButtonVisible)
|
||||
{
|
||||
if (!this.ObjectModel.IsBodyOpen)
|
||||
{
|
||||
this.ObjectModel.HistoryButton.Invoke();
|
||||
waiter.Wait();
|
||||
}
|
||||
}
|
||||
else if (!this.ObjectModel.HistoryPivot.IsSelected)
|
||||
{
|
||||
this.ObjectModel.HistoryPivot.Click();
|
||||
waiter.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class HistoryPom : UIObject
|
||||
{
|
||||
private const string HistoryButtonId = "HistoryButton";
|
||||
private const string HistoryPivotId = "HistoryLabel";
|
||||
private const string BodyId = "HistoryListView";
|
||||
|
||||
public HistoryPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public Button HistoryButton => new Button(this.Descendants.Find(HistoryButtonId));
|
||||
|
||||
public bool IsHistoryButtonVisible => this.DoesDescendantExist(HistoryButtonId);
|
||||
|
||||
public TabItem HistoryPivot => new TabItem(this.Descendants.Find(HistoryPivotId));
|
||||
|
||||
public UIObject Body => this.Descendants.Find(BodyId);
|
||||
|
||||
public bool IsBodyOpen => this.DoesDescendantExist(BodyId);
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public interface ICanFocusWithClicks
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets focus on an object by clicking on it, without causing further action.
|
||||
/// Supports AccSpot scans by raising click events.
|
||||
/// </summary>
|
||||
void FocusWithClicks();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using Etw.Managed;
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
using WEX.Logging.Interop;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class MemoryLfm : ICanFocusWithClicks
|
||||
{
|
||||
public MemoryLfm(MemoryPom memoryPom)
|
||||
{
|
||||
this.ObjectModel = memoryPom;
|
||||
}
|
||||
|
||||
public MemoryPom ObjectModel { get; }
|
||||
|
||||
public void FocusWithClicks()
|
||||
{
|
||||
// On the Programming calc, the default click location can land on the first memory item, dismissing the flyout.
|
||||
// Instead, click just below, in the gutter to the left of the trash can.
|
||||
var body = this.ObjectModel.Body;
|
||||
int height = body.BoundingRectangle.Height;
|
||||
body.DoubleClick(PointerButtons.Primary, Constants.ClickMargin, height + Constants.ClickMargin);
|
||||
}
|
||||
|
||||
public void OpenBody()
|
||||
{
|
||||
using (EtwWaiter waiter = new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.MemoryBodyOpenedETWEventName))
|
||||
{
|
||||
// Only one exists at a given time
|
||||
if (this.ObjectModel.IsMemoryButtonVisible)
|
||||
{
|
||||
if (!this.ObjectModel.IsBodyOpen)
|
||||
{
|
||||
this.ObjectModel.MemoryButton.Invoke();
|
||||
waiter.Wait();
|
||||
}
|
||||
}
|
||||
else if (!this.ObjectModel.MemoryPivot.IsSelected)
|
||||
{
|
||||
this.ObjectModel.MemoryPivot.Click();
|
||||
waiter.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class MemoryPom : UIObject
|
||||
{
|
||||
private const string ClearMemoryButtonId = "ClearMemoryButton";
|
||||
private const string RecallButtonId = "MemRecall";
|
||||
private const string PlusButtonId = "MemPlus";
|
||||
private const string MinusButtonId = "MemMinus";
|
||||
private const string SetButtonId = "memButton";
|
||||
private const string MemoryButtonId = "MemoryButton";
|
||||
private const string MemoryPivotId = "MemoryLabel";
|
||||
private const string BodyId = "MemoryListView";
|
||||
|
||||
public MemoryPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public Button ClearButton => new Button(this.Descendants.Find(ClearMemoryButtonId));
|
||||
|
||||
public Button RecallButton => new Button(this.Descendants.Find(RecallButtonId));
|
||||
|
||||
public Button PlusButton => new Button(this.Descendants.Find(PlusButtonId));
|
||||
|
||||
public Button MinusButton => new Button(this.Descendants.Find(MinusButtonId));
|
||||
|
||||
public Button SetButton => new Button(this.Descendants.Find(SetButtonId));
|
||||
|
||||
public Button MemoryButton => new Button(this.Descendants.Find(MemoryButtonId));
|
||||
|
||||
public bool IsMemoryButtonVisible => this.DoesDescendantExist(MemoryButtonId);
|
||||
|
||||
public TabItem MemoryPivot => new TabItem(this.Descendants.Find(MemoryPivotId));
|
||||
|
||||
public UIObject Body => this.Descendants.Find(BodyId);
|
||||
|
||||
public bool IsBodyOpen => this.DoesDescendantExist(BodyId);
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Components
|
||||
{
|
||||
public class NumberPadPom : UIObject
|
||||
{
|
||||
private const string OneButtonId = "num1Button";
|
||||
private const string TwoButtonId = "num2Button";
|
||||
private const string ThreeButtonId = "num3Button";
|
||||
private const string FourButtonId = "num4Button";
|
||||
private const string FiveButtonId = "num5Button";
|
||||
private const string SixButtonId = "num6Button";
|
||||
private const string SevenButtonId = "num7Button";
|
||||
private const string EightButtonId = "num8Button";
|
||||
private const string NineButtonId = "num9Button";
|
||||
private const string ZeroButtonId = "num0Button";
|
||||
private const string DecimalButtonId = "decimalSeparatorButton";
|
||||
|
||||
public NumberPadPom(UIObject uiObject) : base(uiObject)
|
||||
{
|
||||
}
|
||||
|
||||
public Button ZeroButton => new Button(this.Children.Find(ZeroButtonId));
|
||||
|
||||
public Button OneButton => new Button(this.Children.Find(OneButtonId));
|
||||
|
||||
public Button TwoButton => new Button(this.Children.Find(TwoButtonId));
|
||||
|
||||
public Button ThreeButton => new Button(this.Children.Find(ThreeButtonId));
|
||||
|
||||
public Button FourButton => new Button(this.Children.Find(FourButtonId));
|
||||
|
||||
public Button FiveButton => new Button(this.Children.Find(FiveButtonId));
|
||||
|
||||
public Button SixButton => new Button(this.Children.Find(SixButtonId));
|
||||
|
||||
public Button SevenButton => new Button(this.Children.Find(SevenButtonId));
|
||||
|
||||
public Button EightButton => new Button(this.Children.Find(EightButtonId));
|
||||
|
||||
public Button NineButton => new Button(this.Children.Find(NineButtonId));
|
||||
|
||||
public Button DecimalButton => new Button(this.Children.Find(DecimalButtonId));
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Calculator.UIAutomationLibrary")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("D6913DAD-1C3B-4229-915F-8301A57970FC")]
|
||||
|
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Etw.Managed;
|
||||
using Calculator.UIAutomationLibrary.Components;
|
||||
using WEX.TestExecution;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Tests
|
||||
{
|
||||
public static class BasicCalculationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// This test uses LFMs to add and then delete an alarm.
|
||||
/// </summary>
|
||||
/// <param name="calculatorAppLfm">The LFM for the alarms app window.</param>
|
||||
public static void CalculateOnePlusTwo(this CalculatorAppLfm calculatorAppLfm)
|
||||
{
|
||||
var mainPage = calculatorAppLfm.MainPageLfm;
|
||||
|
||||
var standardCalculator = mainPage.NavigateToStandardCalculator();
|
||||
|
||||
standardCalculator.OnePlusTwoEnter();
|
||||
|
||||
Verify.AreEqual("\u202D3\u202C", standardCalculator.ObjectModel.Display.Name, "Ensure display value is 3");
|
||||
|
||||
standardCalculator.Clear();
|
||||
|
||||
Verify.AreEqual("\u202D0\u202C", standardCalculator.ObjectModel.Display.Name, "Ensure display value is 0");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Calculator.UIAutomationLibrary.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using WEX.TestExecution;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary.Tests
|
||||
{
|
||||
public static class ScientificCalculationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// This test uses LFMs to calculate the sqrt(4) - 2.
|
||||
/// </summary>
|
||||
/// <param name="calculatorAppLfm">The LFM for the calculator app window.</param>
|
||||
public static void CalculateSqrt4Minus2(this CalculatorAppLfm calculatorAppLfm)
|
||||
{
|
||||
var mainPage = calculatorAppLfm.MainPageLfm;
|
||||
|
||||
var scientificCalculator = mainPage.NavigateToScientificCalculator();
|
||||
|
||||
scientificCalculator.Press4();
|
||||
scientificCalculator.PressSqrt();
|
||||
scientificCalculator.PressMinus();
|
||||
scientificCalculator.Press3();
|
||||
scientificCalculator.PressPlus();
|
||||
scientificCalculator.Press1();
|
||||
scientificCalculator.PressEquals();
|
||||
|
||||
Verify.AreEqual("\u202D0\u202C", scientificCalculator.ObjectModel.Display.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
100
internal/Calculator.UIAutomationLibrary/Utilities/Constants.cs
Normal file
100
internal/Calculator.UIAutomationLibrary/Utilities/Constants.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
public class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to the certificate file.
|
||||
/// </summary>
|
||||
public const string CertificateFileName = @"Calculator.cer";
|
||||
|
||||
/// <summary>
|
||||
/// The path to the appxbundle file.
|
||||
/// </summary>
|
||||
public const string PackageFileName = @"Calculator.appxbundle";
|
||||
|
||||
/// <summary>
|
||||
/// The path to the appxbundle file.
|
||||
/// </summary>
|
||||
public const string VCLibsPackageFileName = @"Microsoft.VCLibs.appx";
|
||||
|
||||
/// <summary>
|
||||
/// The path to the appxbundle file.
|
||||
/// </summary>
|
||||
public const string WinUIPackageFileName = @"Microsoft.UI.Xaml.appx";
|
||||
|
||||
/// <summary>
|
||||
/// Name of the CoreWindow.
|
||||
/// </summary>
|
||||
public const string AppWindowName = "Calculator";
|
||||
|
||||
/// <summary>
|
||||
/// Name of the process executable.
|
||||
/// </summary>
|
||||
public const string ProcessName = "Calculator.exe";
|
||||
|
||||
/// <summary>
|
||||
/// The package name.
|
||||
/// </summary>
|
||||
public const string PackageName = "Microsoft.WindowsCalculator";
|
||||
|
||||
/// <summary>
|
||||
/// The package family name for the app to test.
|
||||
/// </summary>
|
||||
public const string PackageFamilyName = PackageName + "_8wekyb3d8bbwe";
|
||||
|
||||
/// <summary>
|
||||
/// The package App User Model Id.
|
||||
/// </summary>
|
||||
public const string PackageAppUserModelId = PackageFamilyName + "!App";
|
||||
|
||||
/// <summary>
|
||||
/// AutomationId for the top level UI element.
|
||||
/// </summary>
|
||||
public const string TopLevelWindowAutomationId = "CalculatorWindow";
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the first page is loaded.
|
||||
/// </summary>
|
||||
public const string AppLaunchEndETWEventName = "AppLaunchEnd";
|
||||
|
||||
/// <summary>
|
||||
/// App Provider GUID for ETW Events
|
||||
/// </summary>
|
||||
public static readonly Guid CalculatorETWProviderGUID = new Guid("0905CA09-610E-401E-B650-2F212980B9E0");
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when a calculator mode change is complete.
|
||||
/// </summary>
|
||||
public const string AppModeChangeEndETWEventName = "ModeChangeEnd";
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the History panel is opened by flyout or by changing pivot tabs.
|
||||
/// </summary>
|
||||
public const string HistoryBodyOpenedETWEventName = "HistoryBodyOpened";
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the Memory panel is opened by flyout or by changing pivot tabs.
|
||||
/// </summary>
|
||||
public const string MemoryBodyOpenedETWEventName = "MemoryBodyOpened";
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the About flyout control is loaded.
|
||||
/// </summary>
|
||||
public const string AboutFlyoutOpenedETWEventName = "AboutFlyoutOpened";
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the Nav Bar control is opened.
|
||||
/// </summary>
|
||||
public const string NavBarOpenedETWEventName = "NavBarOpened";
|
||||
|
||||
/// <summary>
|
||||
/// Margin used to click in the gutter beneath the History and Memory lists
|
||||
/// </summary>
|
||||
public const int ClickMargin = 10;
|
||||
}
|
||||
}
|
101
internal/Calculator.UIAutomationLibrary/Utilities/EtwHelper.cs
Normal file
101
internal/Calculator.UIAutomationLibrary/Utilities/EtwHelper.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.OneCoreUap.Test.AppModel;
|
||||
using WEX.Logging.Interop;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
public class EtwHelper
|
||||
{
|
||||
private static bool etwServiceWasInstalled = false;
|
||||
|
||||
/// <summary>
|
||||
/// Installs and starts the Etw.Service so that Tests may utilize Etw Waiters.
|
||||
/// Wex.Services.exe is part of TAEF.
|
||||
/// </summary>
|
||||
public static void InstallAndStartETWService()
|
||||
{
|
||||
etwServiceWasInstalled = ServiceHelper.IsInstalled("Etw.Service");
|
||||
if (!etwServiceWasInstalled)
|
||||
{
|
||||
string wexServices = Path.Combine(TAEFHelper.GetTestDeploymentDirectory(), "Wex.Services.exe");
|
||||
if (!File.Exists(wexServices))
|
||||
{
|
||||
throw new FileNotFoundException(wexServices);
|
||||
}
|
||||
|
||||
Log.Comment("Attempting to install Etw.Service...");
|
||||
var startInfo = new ProcessStartInfo();
|
||||
startInfo.FileName = wexServices;
|
||||
startInfo.Arguments = "/install:Etw.Service";
|
||||
|
||||
var process = new Process();
|
||||
process.StartInfo = startInfo;
|
||||
if (process.Start())
|
||||
{
|
||||
process.WaitForExit();
|
||||
Log.Comment("Completed installation of Etw.Service");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("ETW service was not able to be installed. Process didn't start.");
|
||||
}
|
||||
}
|
||||
|
||||
Log.Comment("Attempting to start Etw.Service...");
|
||||
ServiceHelper.Start("Etw.Service");
|
||||
Log.Comment("Etw.Service started");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the Etw.Service.
|
||||
/// </summary>
|
||||
public static void StopAndRemoveETWService()
|
||||
{
|
||||
if (ServiceHelper.IsInstalled("Etw.Service"))
|
||||
{
|
||||
Log.Comment("Attempting to stop Etw.Service...");
|
||||
ServiceHelper.Stop("Etw.Service");
|
||||
Log.Comment("Etw.Service stopped");
|
||||
|
||||
// if we installed the Etw.Service as part of this test we should also remove it.
|
||||
// This prevents cases where TDP is upgraded but the service tregistration is referencing the old
|
||||
// location that no longer exists.
|
||||
if (!etwServiceWasInstalled)
|
||||
{
|
||||
|
||||
string wexServices = Path.Combine(TAEFHelper.GetTestDeploymentDirectory(), "Wex.Services.exe");
|
||||
if (!File.Exists(wexServices))
|
||||
{
|
||||
throw new FileNotFoundException(wexServices);
|
||||
}
|
||||
|
||||
Log.Comment("Attempting to remove Etw.Service...");
|
||||
var startInfo = new ProcessStartInfo();
|
||||
startInfo.FileName = wexServices;
|
||||
startInfo.Arguments = "/remove:Etw.Service";
|
||||
|
||||
var process = new Process();
|
||||
process.StartInfo = startInfo;
|
||||
if (process.Start())
|
||||
{
|
||||
process.WaitForExit();
|
||||
Log.Comment("Completed removal of Etw.Service");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("ETW service could not be removed. Process didn't start.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
public static class Impersonater
|
||||
{
|
||||
public static void RunAs(RunAsUser user, Action action)
|
||||
{
|
||||
IntPtr errorInfo;
|
||||
SafeAccessTokenHandle restrictedToken;
|
||||
GetRunAsUserToken getRunAsUserToken = ResolveGetRunAsUserTokenMethod();
|
||||
Marshal.ThrowExceptionForHR(getRunAsUserToken(user, out restrictedToken, out errorInfo), errorInfo);
|
||||
|
||||
WindowsIdentity.RunImpersonated(restrictedToken, action);
|
||||
}
|
||||
|
||||
public static void RunAs<T>(RunAsUser user, Func<T> function)
|
||||
{
|
||||
IntPtr errorInfo;
|
||||
SafeAccessTokenHandle restrictedToken;
|
||||
GetRunAsUserToken getRunAsUserToken = ResolveGetRunAsUserTokenMethod();
|
||||
Marshal.ThrowExceptionForHR(getRunAsUserToken(user, out restrictedToken, out errorInfo), errorInfo);
|
||||
|
||||
WindowsIdentity.RunImpersonated(restrictedToken, function);
|
||||
}
|
||||
|
||||
// From: https://microsoft.visualstudio.com/EngSys/_git/validation.taef?path=%2Fsrc%2FTAEF%2FCommon%2FPublished%2FRunAsFromSystem.h&version=GBdevelop
|
||||
public enum RunAsUser
|
||||
{
|
||||
ElevatedUser,
|
||||
User,
|
||||
RestrictedUser,
|
||||
LowIL,
|
||||
InteractiveUser,
|
||||
};
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate int GetRunAsUserToken(RunAsUser user, out SafeAccessTokenHandle token, out IntPtr errorInfo);
|
||||
|
||||
// GetRunAsUserToken is defined in a namespace so we have to do some tricks to use P/Invoke.
|
||||
// We got the actual exported method name by running dumpbin /exports TE.Common.dll
|
||||
private static GetRunAsUserToken ResolveGetRunAsUserTokenMethod()
|
||||
{
|
||||
IntPtr teCommonDll = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
teCommonDll = LoadLibrary(@"TE.Common.dll");
|
||||
|
||||
IntPtr x64GetRunAsUserToken = GetProcAddress(teCommonDll, "?GetRunAsUserToken@TestExecution@WEX@@YAJW4RunAsUser@12@PEAPEAXPEAPEAUIErrorInfo@@@Z");
|
||||
if (x64GetRunAsUserToken != IntPtr.Zero)
|
||||
{
|
||||
return Marshal.GetDelegateForFunctionPointer<GetRunAsUserToken>(x64GetRunAsUserToken);
|
||||
}
|
||||
IntPtr x86GetRunAsUserToken = GetProcAddress(teCommonDll, "?GetRunAsUserToken@TestExecution@WEX@@YGJW4RunAsUser@12@PAPAXPAPAUIErrorInfo@@@Z");
|
||||
if (x86GetRunAsUserToken != IntPtr.Zero)
|
||||
{
|
||||
return Marshal.GetDelegateForFunctionPointer<GetRunAsUserToken>(x86GetRunAsUserToken);
|
||||
}
|
||||
IntPtr armGetRunAsUserToken = GetProcAddress(teCommonDll, "?GetRunAsUserToken@TestExecution@WEX@@YAJW4RunAsUser@12@PAPAXPAPAUIErrorInfo@@@Z");
|
||||
if (armGetRunAsUserToken != IntPtr.Zero)
|
||||
{
|
||||
return Marshal.GetDelegateForFunctionPointer<GetRunAsUserToken>(armGetRunAsUserToken);
|
||||
}
|
||||
IntPtr arm64GetRunAsUserToken = GetProcAddress(teCommonDll, "?GetRunAsUserToken@TestExecution@WEX@@YAJW4RunAsUser@12@PEAPEAXPEAPEAUIErrorInfo@@@Z");
|
||||
if (arm64GetRunAsUserToken != IntPtr.Zero)
|
||||
{
|
||||
return Marshal.GetDelegateForFunctionPointer<GetRunAsUserToken>(arm64GetRunAsUserToken);
|
||||
}
|
||||
|
||||
throw new Exception("Unable to find the GetRunAsUserToken function in DLL 'TE.Common.dll'");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (teCommonDll != IntPtr.Zero)
|
||||
{
|
||||
FreeLibrary(teCommonDll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr LoadLibrary(string dllToLoad);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool FreeLibrary(IntPtr hModule);
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using Microsoft.OneCoreUap.Test.AppModel;
|
||||
using WEX.Logging.Interop;
|
||||
using Windows.Foundation;
|
||||
using Windows.Management.Deployment;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
public class InstallHelper
|
||||
{
|
||||
public static void InstallCertFile(string certFilePath)
|
||||
{
|
||||
// Ensure cert exists.
|
||||
if (!File.Exists(certFilePath))
|
||||
{
|
||||
throw new FileNotFoundException(certFilePath);
|
||||
}
|
||||
|
||||
// For some reason, attempting to use CertificateHelper.InstallFromSignedPackage() to install
|
||||
// the certificate associated with the appx package fails with the error:
|
||||
// "A certificate chain could not be built to a trusted root authority."
|
||||
// The reason is that the cert needs to be installed in 'StoreName.TrustedPeople',
|
||||
// but DeploymentHelper.AddPackage() attempts to install it in 'StoreName.Root'.
|
||||
// Therefore, the cert has been installed manually beforehand.
|
||||
Log.Comment($"Starting install of certificate at {certFilePath}");
|
||||
|
||||
X509Certificate2 certificate = new X509Certificate2(certFilePath);
|
||||
X509Store trustedPeopleStore = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
|
||||
trustedPeopleStore.Open(OpenFlags.MaxAllowed);
|
||||
trustedPeopleStore.Add(certificate);
|
||||
|
||||
Log.Comment($"Completed install of certificate");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upgrades the appx/appxbundle from the path if needed.
|
||||
/// </summary>
|
||||
public static void InstallPackage(string appxFilePath, params string[] dependencyAppxFilePaths)
|
||||
{
|
||||
// Ensure the files exist.
|
||||
if (!File.Exists(appxFilePath))
|
||||
{
|
||||
throw new FileNotFoundException(appxFilePath);
|
||||
}
|
||||
foreach (var path in dependencyAppxFilePaths.Where(p => !File.Exists(p)))
|
||||
{
|
||||
throw new FileNotFoundException(path);
|
||||
}
|
||||
|
||||
Log.Comment($"Starting install of app package at {appxFilePath}");
|
||||
|
||||
PackageManager packageManager = new PackageManager();
|
||||
var deploymentOperation = packageManager.AddPackageAsync(
|
||||
new Uri(appxFilePath),
|
||||
dependencyAppxFilePaths.Select(d => new Uri(d)),
|
||||
DeploymentOptions.ForceApplicationShutdown | DeploymentOptions.ForceTargetApplicationShutdown | DeploymentOptions.ForceUpdateFromAnyVersion);
|
||||
|
||||
WaitForDeploymentOperation(deploymentOperation);
|
||||
|
||||
Log.Comment("Completed install of app package");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for a deployment operation to complete
|
||||
/// </summary>
|
||||
private static void WaitForDeploymentOperation(IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> operation)
|
||||
{
|
||||
ManualResetEvent isCompletedEvent = new ManualResetEvent(false);
|
||||
operation.Completed = (IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> asyncInfo, AsyncStatus asyncStatus) =>
|
||||
{
|
||||
isCompletedEvent.Set();
|
||||
};
|
||||
|
||||
isCompletedEvent.WaitOne();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
internal static class NativeMethods
|
||||
{
|
||||
internal const int GW_OWNER = 4;
|
||||
|
||||
internal delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam);
|
||||
|
||||
[DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)]
|
||||
internal static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr extraData);
|
||||
|
||||
[DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)]
|
||||
internal static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
|
||||
|
||||
[DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)]
|
||||
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||
|
||||
[DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)]
|
||||
internal static extern int GetWindowTextLength(IntPtr hWnd);
|
||||
|
||||
[DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)]
|
||||
internal static extern bool IsWindowVisible(IntPtr hWnd);
|
||||
|
||||
[DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)]
|
||||
internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)]
|
||||
internal static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);
|
||||
|
||||
[DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)]
|
||||
internal static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool StartService(IntPtr hService, int dwNumServiceArgs, string[] lpServiceArgVectors);
|
||||
|
||||
[DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool ControlService(IntPtr hService, SERVICE_CONTROL dwControl, ref SERVICE_STATUS lpServiceStatus);
|
||||
|
||||
[DllImport("api-ms-win-service-management-l1-1-0.dll")]
|
||||
internal static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
|
||||
|
||||
[DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool CloseServiceHandle(IntPtr hSCObject);
|
||||
|
||||
[DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
|
||||
|
||||
[DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true)]
|
||||
internal static extern bool QueryServiceStatus(IntPtr hService, ref SERVICE_STATUS dwServiceStatus);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct SERVICE_STATUS
|
||||
{
|
||||
public int dwServiceType;
|
||||
public int dwCurrentState;
|
||||
public int dwControlsAccepted;
|
||||
public int dwWin32ExitCode;
|
||||
public int dwServiceSpecificExitCode;
|
||||
public int dwCheckPoint;
|
||||
public int dwWaitHint;
|
||||
}
|
||||
|
||||
internal static uint STANDARD_RIGHTS_REQUIRED = 0xF0000;
|
||||
|
||||
internal static uint SC_MANAGER_CONNECT = 0x0001;
|
||||
internal static uint SC_MANAGER_CREATE_SERVICE = 0x0002;
|
||||
internal static uint SC_MANAGER_ENUMERATE_SERVICE = 0x0004;
|
||||
internal static uint SC_MANAGER_LOCK = 0x0008;
|
||||
internal static uint SC_MANAGER_QUERY_LOCK_STATUS = 0x0010;
|
||||
internal static uint SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020;
|
||||
internal static uint SC_MANAGER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
|
||||
SC_MANAGER_CONNECT |
|
||||
SC_MANAGER_CREATE_SERVICE |
|
||||
SC_MANAGER_ENUMERATE_SERVICE |
|
||||
SC_MANAGER_LOCK |
|
||||
SC_MANAGER_QUERY_LOCK_STATUS |
|
||||
SC_MANAGER_MODIFY_BOOT_CONFIG);
|
||||
|
||||
internal static uint SERVICE_QUERY_CONFIG = 0x0001;
|
||||
internal static uint SERVICE_CHANGE_CONFIG = 0x0002;
|
||||
internal static uint SERVICE_QUERY_STATUS = 0x0004;
|
||||
internal static uint SERVICE_ENUMERATE_DEPENDENTS = 0x0008;
|
||||
internal static uint SERVICE_START = 0x0010;
|
||||
internal static uint SERVICE_STOP = 0x0020;
|
||||
internal static uint SERVICE_PAUSE_CONTINUE = 0x0040;
|
||||
internal static uint SERVICE_INTERROGATE = 0x0080;
|
||||
internal static uint SERVICE_USER_DEFINED_CONTROL = 0x0100;
|
||||
internal static uint SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
|
||||
SERVICE_QUERY_CONFIG |
|
||||
SERVICE_CHANGE_CONFIG |
|
||||
SERVICE_QUERY_STATUS |
|
||||
SERVICE_ENUMERATE_DEPENDENTS |
|
||||
SERVICE_START |
|
||||
SERVICE_STOP |
|
||||
SERVICE_PAUSE_CONTINUE |
|
||||
SERVICE_INTERROGATE |
|
||||
SERVICE_USER_DEFINED_CONTROL);
|
||||
|
||||
[Flags]
|
||||
internal enum SERVICE_CONTROL : uint
|
||||
{
|
||||
STOP = 0x00000001,
|
||||
PAUSE = 0x00000002,
|
||||
CONTINUE = 0x00000003,
|
||||
INTERROGATE = 0x00000004,
|
||||
SHUTDOWN = 0x00000005,
|
||||
PARAMCHANGE = 0x00000006,
|
||||
NETBINDADD = 0x00000007,
|
||||
NETBINDREMOVE = 0x00000008,
|
||||
NETBINDENABLE = 0x00000009,
|
||||
NETBINDDISABLE = 0x0000000A,
|
||||
DEVICEEVENT = 0x0000000B,
|
||||
HARDWAREPROFILECHANGE = 0x0000000C,
|
||||
POWEREVENT = 0x0000000D,
|
||||
SESSIONCHANGE = 0x0000000E
|
||||
}
|
||||
|
||||
internal enum SERVICE_STATE : int
|
||||
{
|
||||
SERVICE_STOPPED = 0x00000001,
|
||||
SERVICE_START_PENDING = 0x00000002,
|
||||
SERVICE_STOP_PENDING = 0x00000003,
|
||||
SERVICE_RUNNING = 0x00000004,
|
||||
SERVICE_CONTINUE_PENDING = 0x00000005,
|
||||
SERVICE_PAUSE_PENDING = 0x00000006,
|
||||
SERVICE_PAUSED = 0x00000007
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
public class PerfConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Path where the regions, wprprofiles and wpaprofiles will be deployed to by the spkg.
|
||||
/// </summary>
|
||||
public const string ConfigDirectory = @"Config\";
|
||||
|
||||
/// <summary>
|
||||
/// Our FunGates source, where we can view test results.
|
||||
/// </summary>
|
||||
public const string FunGatesSource =
|
||||
#if DEBUG
|
||||
"TestSite";
|
||||
#else
|
||||
"Utility Apps Performance Tests";
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The Windows Performance Recorder profile. These strings must have the config directory prefix.
|
||||
/// For use with the WPRProfileFile test attribute.
|
||||
/// </summary>
|
||||
public const string AppLifecycleWPRProfile = ConfigDirectory + "AppLifecycle.Profile.wprp";
|
||||
|
||||
/// <summary>
|
||||
/// The regions of interest file that contains the events we are interested in measuring.
|
||||
/// </summary>
|
||||
public const string AppLifecycleRegions = ConfigDirectory + "AppLifecycle.regions.xml";
|
||||
|
||||
/// <summary>
|
||||
/// These are uses with the DataSource test property to specify iteration info.
|
||||
/// </summary>
|
||||
public const string AppLifecycleInterationsSource = "Table:" + ConfigDirectory + "AppLifecycle.Iterations.xml#PerformanceConfigurations";
|
||||
}
|
||||
}
|
@@ -0,0 +1,156 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
public class ServiceHelper
|
||||
{
|
||||
public static void Start(string serviceName, int timeoutInMilliSeconds = 30000)
|
||||
{
|
||||
IntPtr hService = IntPtr.Zero;
|
||||
IntPtr hSCManager = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
hSCManager = NativeMethods.OpenSCManager(null, null, NativeMethods.SC_MANAGER_ALL_ACCESS);
|
||||
if (IntPtr.Zero == hSCManager)
|
||||
{
|
||||
throw new Exception($"Start: Cannot Open OpenSCManager, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
hService = NativeMethods.OpenService(hSCManager, serviceName, NativeMethods.SERVICE_ALL_ACCESS);
|
||||
if (IntPtr.Zero == hService)
|
||||
{
|
||||
throw new Exception($"Start: Cannot Open Service, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
NativeMethods.SERVICE_STATUS serviceStatus = new NativeMethods.SERVICE_STATUS();
|
||||
if (!NativeMethods.QueryServiceStatus(hService, ref serviceStatus))
|
||||
{
|
||||
throw new Exception($"Start: Unable to query status of Service, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
if (serviceStatus.dwCurrentState != (int)NativeMethods.SERVICE_STATE.SERVICE_RUNNING &&
|
||||
serviceStatus.dwCurrentState != (int)NativeMethods.SERVICE_STATE.SERVICE_START_PENDING)
|
||||
{
|
||||
if (!NativeMethods.StartService(hService, 0, null))
|
||||
{
|
||||
throw new Exception($"Start: Service cannot be started, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
}
|
||||
|
||||
WaitForStatus(hService, NativeMethods.SERVICE_STATE.SERVICE_RUNNING, TimeSpan.FromMilliseconds(timeoutInMilliSeconds));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (IntPtr.Zero != hService)
|
||||
{
|
||||
NativeMethods.CloseServiceHandle(hService);
|
||||
}
|
||||
if (IntPtr.Zero != hSCManager)
|
||||
{
|
||||
NativeMethods.CloseServiceHandle(hSCManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop(string serviceName, int timeoutInMilliSeconds = 30000)
|
||||
{
|
||||
IntPtr hSCManager = IntPtr.Zero;
|
||||
IntPtr hService = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
hSCManager = NativeMethods.OpenSCManager(null, null, NativeMethods.SC_MANAGER_ALL_ACCESS);
|
||||
if (IntPtr.Zero == hSCManager)
|
||||
{
|
||||
throw new Exception($"Stop: Cannot Open OpenSCManager, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
hService = NativeMethods.OpenService(hSCManager, serviceName, NativeMethods.SERVICE_ALL_ACCESS);
|
||||
if (IntPtr.Zero == hService)
|
||||
{
|
||||
throw new Exception($"Stop: Cannot Open Service, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
NativeMethods.SERVICE_STATUS serviceStatus = new NativeMethods.SERVICE_STATUS();
|
||||
if (!NativeMethods.QueryServiceStatus(hService, ref serviceStatus))
|
||||
{
|
||||
throw new Exception($"Stop: Unable to query status of Service, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
if (serviceStatus.dwCurrentState != (int)NativeMethods.SERVICE_STATE.SERVICE_STOPPED &&
|
||||
serviceStatus.dwCurrentState != (int)NativeMethods.SERVICE_STATE.SERVICE_STOP_PENDING)
|
||||
{
|
||||
if (!NativeMethods.ControlService(hService, NativeMethods.SERVICE_CONTROL.STOP, ref serviceStatus))
|
||||
{
|
||||
throw new Exception($"Stop: Service cannot be stopped, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
}
|
||||
|
||||
WaitForStatus(hService, NativeMethods.SERVICE_STATE.SERVICE_STOPPED, TimeSpan.FromMilliseconds(timeoutInMilliSeconds));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (IntPtr.Zero != hService)
|
||||
{
|
||||
NativeMethods.CloseServiceHandle(hService);
|
||||
}
|
||||
|
||||
if (IntPtr.Zero != hSCManager)
|
||||
{
|
||||
NativeMethods.CloseServiceHandle(hSCManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsInstalled(string svcName)
|
||||
{
|
||||
IntPtr sc_handle = NativeMethods.OpenSCManager(null, null, NativeMethods.SC_MANAGER_ALL_ACCESS);
|
||||
if (sc_handle == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"IsInstalled: Cannot open service manager, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
|
||||
bool bResult = false;
|
||||
IntPtr sv_handle = NativeMethods.OpenService(sc_handle, svcName, NativeMethods.SERVICE_QUERY_CONFIG);
|
||||
if (sv_handle.ToInt64() != 0)
|
||||
{
|
||||
bResult = true;
|
||||
NativeMethods.CloseServiceHandle(sv_handle);
|
||||
}
|
||||
NativeMethods.CloseServiceHandle(sc_handle);
|
||||
return bResult;
|
||||
}
|
||||
|
||||
private static void WaitForStatus(IntPtr hService, NativeMethods.SERVICE_STATE desiredStatus, TimeSpan timeout)
|
||||
{
|
||||
Stopwatch swLoop = new Stopwatch();
|
||||
swLoop.Start();
|
||||
|
||||
NativeMethods.SERVICE_STATUS serviceStatus = new NativeMethods.SERVICE_STATUS();
|
||||
|
||||
do
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
if (!NativeMethods.QueryServiceStatus(hService, ref serviceStatus))
|
||||
{
|
||||
throw new Exception($"WaitForStatus: Unable to query status of service, {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
}
|
||||
while (serviceStatus.dwCurrentState != (int)desiredStatus && (swLoop.ElapsedMilliseconds <= timeout.TotalMilliseconds));
|
||||
|
||||
if (serviceStatus.dwCurrentState != (int)desiredStatus)
|
||||
{
|
||||
throw new Exception($"WaitForStatus: Service failed to reach desired state: {desiredStatus}, current state: {serviceStatus.dwCurrentState}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Linq;
|
||||
using System.Windows.Automation;
|
||||
using MS.Internal.Mita.Foundation;
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
using MS.Internal.Mita.Foundation.Waiters;
|
||||
using WEX.Logging.Interop;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
public static class UIObjectExtensions
|
||||
{
|
||||
private const string NamePropertyName = "Name";
|
||||
private static UICondition CoreWindowCondition = UICondition.CreateFromClassName("Windows.UI.Core.CoreWindow");
|
||||
|
||||
public static Window GetParentCoreWindow(this UIObject uiObject)
|
||||
{
|
||||
if (uiObject.Matches(CoreWindowCondition))
|
||||
{
|
||||
return new Window(uiObject);
|
||||
}
|
||||
|
||||
return new Window(uiObject.Ancestors.Find(CoreWindowCondition));
|
||||
}
|
||||
|
||||
public static Window GetTopLevelWindow(this Window window)
|
||||
{
|
||||
var node = window;
|
||||
while (node != null && node.Parent != null && node.Parent.ControlType == ControlType.Window)
|
||||
{
|
||||
node = new Window(node.Parent);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public static void VerifyParentTreeStructure(this UIObject uiObject)
|
||||
{
|
||||
var node = uiObject;
|
||||
while (node != null && node.Parent != null)
|
||||
{
|
||||
if (!node.Parent.Children.Contains(node))
|
||||
{
|
||||
Log.Comment($"- [VerifyingTree] {node} specifies {node.Parent} as parent but is not part of its children.");
|
||||
}
|
||||
if (!node.Parent.Descendants.Contains(node))
|
||||
{
|
||||
Log.Comment($"- [VerifyingTree] {node} specifies {node.Parent} as parent but is not part of its descendants.");
|
||||
}
|
||||
node = node.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DoesDescendantExist(this UIObject uiObject, string automationId)
|
||||
{
|
||||
UIObject temp;
|
||||
return uiObject.Descendants.TryFind(automationId, out temp);
|
||||
}
|
||||
|
||||
public static PropertyChangedEventWaiter GetNameChangedWaiter(this TextBlock textBlock)
|
||||
{
|
||||
return new PropertyChangedEventWaiter(textBlock, UIProperty.Get(NamePropertyName));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using WEX.Logging.Interop;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
public class Utilities
|
||||
{
|
||||
public static void KillExistingCalculatorProcesses()
|
||||
{
|
||||
Log.Comment("Killing any existing Calculator processes");
|
||||
|
||||
foreach (var process in Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Constants.ProcessName)))
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
Log.Comment($"Killed {process.ProcessName}, Id: {process.Id}");
|
||||
}
|
||||
catch (Exception) when (process.HasExited)
|
||||
{
|
||||
Log.Comment($"{process.ProcessName}, Id: {process.Id} already exited.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using MS.Internal.Mita.Foundation.Controls;
|
||||
using WEX.Logging.Interop;
|
||||
|
||||
namespace Calculator.UIAutomationLibrary
|
||||
{
|
||||
public class WindowHelper
|
||||
{
|
||||
public static void SetAsForeground(Window window)
|
||||
{
|
||||
Log.Comment($"Set window {window.NativeWindowHandle} as the foreground window.");
|
||||
|
||||
NativeMethods.SetForegroundWindow(window.NativeWindowHandle);
|
||||
}
|
||||
}
|
||||
}
|
12
internal/Calculator.UIAutomationLibrary/project.json
Normal file
12
internal/Calculator.UIAutomationLibrary/project.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"AppModel.TestHelper": "2018.3.22",
|
||||
"EtwProcessor.Managed": "10.34.181220007",
|
||||
"MITALite": "1.0.180128001",
|
||||
"Taef.Managed": "10.34.181220007",
|
||||
"Test.Net.Redist": "2.0.1"
|
||||
},
|
||||
"frameworks": {
|
||||
"netcore50": {}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user