This commit is contained in:
Matthew Bongiovi 2018-04-25 17:01:30 -07:00
Родитель 6b4e59e876
Коммит 9eef71b865
23 изменённых файлов: 1551 добавлений и 2 удалений

3
.gitignore поставляемый
Просмотреть файл

@ -9,6 +9,9 @@
*.userosscache
*.sln.docstates
# ALWAYS ignore the credential configuration, so nobody checks in their API keys
App.config
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

Просмотреть файл

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdfsUITestManager", "AdfsUITestManager\AdfsUITestManager.csproj", "{540AFC72-5649-4ECF-B4D4-1273CE070504}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{540AFC72-5649-4ECF-B4D4-1273CE070504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{540AFC72-5649-4ECF-B4D4-1273CE070504}.Debug|Any CPU.Build.0 = Debug|Any CPU
{540AFC72-5649-4ECF-B4D4-1273CE070504}.Release|Any CPU.ActiveCfg = Release|Any CPU
{540AFC72-5649-4ECF-B4D4-1273CE070504}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{540AFC72-5649-4ECF-B4D4-1273CE070504}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>AdfsUITestManager</RootNamespace>
<AssemblyName>AdfsUITestManager</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Selenium.WebDriverBackedSelenium, Version=3.11.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.WebDriverBackedSelenium.3.11.0\lib\net45\Selenium.WebDriverBackedSelenium.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WebDriver, Version=3.11.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.WebDriver.3.11.0\lib\net45\WebDriver.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="WebDriver.Support, Version=3.11.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.Support.3.11.0\lib\net45\WebDriver.Support.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="BrowserStackLogReader.cs" />
<Compile Include="DriverFactory.cs" />
<Compile Include="Tasks\ExternalAuthTasks.cs" />
<Compile Include="Tasks\FormsPageTasks.cs" />
<Compile Include="Manager.cs" />
<Compile Include="Tasks\OptionsPageTasks.cs" />
<Compile Include="Tasks\PaginatedFormsTasks.cs" />
<Compile Include="Tasks\RelyingPartyTasks.cs" />
<Compile Include="Tasks\TaskBase.cs" />
<Compile Include="TestCases.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScreenShotRemoteWebDriver.cs" />
<Compile Include="TaskConfiguration.cs" />
<Compile Include="TaskConfigurationFactory.cs" />
<Compile Include="Tasks\IdpPageTasks.cs" />
<Compile Include="TestContext.cs" />
<Compile Include="TestListConfiguration.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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,163 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;
namespace AdfsUITestManager
{
public enum TestStatus
{
Passed,
Failed
}
public class MarkTestData
{
public string status { get; set; }
}
public enum BrowserStackDataType
{
Build,
Session
}
class BrowserStackLogReader
{
public static NetworkCredential AuthCredential = new NetworkCredential( ConfigurationManager.AppSettings[ "BrowserStackUser" ], ConfigurationManager.AppSettings[ "BrowserStackKey" ] );
/// <summary>
/// BrowserStack API supports build and session queries for a given user.
/// This method retrieves all builds or sessions, and then searches for the ID of the matching session/build by name
/// </summary>
/// <param name="name"></param>
/// <param name="url"></param>
/// <param name="dataType"></param>
/// <returns></returns>
private static string GetIdFromName( string name, string url, BrowserStackDataType dataType )
{
WebResponse response = null;
StreamReader reader = null;
// BrowserStack doesn't allow hyphens in remote names, and removes them silently
name = name.Replace( '-', ' ' );
try
{
WebRequest request = WebRequest.Create( url );
request.Credentials = AuthCredential;
response = request.GetResponse();
Stream dataStream = response.GetResponseStream();
reader = new StreamReader( dataStream );
string responseFromServer = reader.ReadToEnd();
string data = "{\"data\": " + responseFromServer + "}";
var jsonData = JObject.Parse( data );
// Iterate through builds until you find one that matches our build name
for ( int i = 0; i < jsonData[ "data" ].Count(); i++ )
{
string automationElement = "automation_session";
if ( dataType == BrowserStackDataType.Build )
{
automationElement = "automation_build";
}
var nameRemote = ( string )jsonData[ "data" ][ i ][ automationElement ][ "name" ];
if ( nameRemote.Equals( name ) )
{
return ( string )jsonData[ "data" ][ i ][ automationElement ][ "hashed_id" ];
}
}
}
catch ( Exception e )
{
Console.WriteLine( "Error while trying to call BrowserStack." );
Console.WriteLine( e.Message );
Console.WriteLine( e.StackTrace );
}
finally
{
reader?.Close();
response?.Close();
}
return null;
}
/// <summary>
/// Locates the BrowserStack session ID for a given build and session name.
/// </summary>
/// <param name="buildName"></param>
/// <param name="sessionName"></param>
/// <returns></returns>
public static string GetSessionId(string buildName, string sessionName)
{
var buildId = GetIdFromName( buildName, $"https://api.browserstack.com/automate/builds.json", BrowserStackDataType.Build );
if ( string.IsNullOrEmpty( buildId ) )
{
Console.WriteLine("Error getting build ID. No matching build ID found.");
return null;
}
var sessionId = GetIdFromName( sessionName, $"https://api.browserstack.com/automate/builds/{buildId}/sessions.json", BrowserStackDataType.Session );
if ( string.IsNullOrEmpty( sessionId ) )
{
Console.WriteLine( "Error getting session ID. No matching session ID found." );
return null;
}
return sessionId;
}
/// <summary>
/// Marks a test corresponding to the given session ID with the test status supplied.
/// This method is used to mark tests as "pass" or "fail", based on what occurs in the ADFS environment.
/// </summary>
/// <param name="sessionId"></param>
/// <param name="status"></param>
public static void MarkTest( string sessionId, TestStatus status)
{
try
{
string url = $"https://api.browserstack.com/";
var handler = new HttpClientHandler { Credentials = AuthCredential };
HttpClient client = new HttpClient( handler );
client.BaseAddress = new Uri( url );
// Create the data to PUT
MarkTestData data = new MarkTestData();
data.status = status.ToString();
var output = JsonConvert.SerializeObject( data );
var inputMessage = new HttpRequestMessage
{
Content = new StringContent( output, Encoding.UTF8, "application/json" )
};
inputMessage.Headers.Accept.Add( new MediaTypeWithQualityHeaderValue( "application/json" ) );
HttpResponseMessage message = client.PutAsync( $"automate/sessions/{sessionId}.json", inputMessage.Content ).Result;
if ( message.IsSuccessStatusCode )
{
Console.WriteLine( "Test marked successfully" );
}
}
catch ( Exception e )
{
Console.WriteLine( $"Failed to mark test {sessionId} with status {status}." );
Console.WriteLine( e.Message );
Console.WriteLine( e.StackTrace );
}
}
}
}

Просмотреть файл

@ -0,0 +1,88 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using OpenQA.Selenium.Remote;
using System.Configuration;
namespace AdfsUITestManager
{
using System;
using OpenQA.Selenium;
/// <summary>
/// Class responsible for creating web drivers to be used for UI testing
/// </summary>
class DriverFactory
{
static readonly string RemoteDriverUri = ConfigurationManager.AppSettings[ "RemoteDriverUri" ];
public static IWebDriver GenerateDriver( DesiredCapabilities capability )
{
var driver = new RemoteWebDriver(
new Uri( RemoteDriverUri ), capability
);
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds( 10 );
return driver;
}
public static void AddCommonCapabilities( DesiredCapabilities capability )
{
var user = ConfigurationManager.AppSettings[ "BrowserStackUser" ];
var key = ConfigurationManager.AppSettings[ "BrowserStackKey" ];
var sessionName = $"AD FS {DateTime.UtcNow.ToFileTimeUtc()}";
var buildName = $"AD FS Build {DateTime.UtcNow.ToString("yyyy-MM-dd")}";
capability.SetCapability( "browserstack.local", "true" );
capability.SetCapability( "browserstack.user", user );
capability.SetCapability( "browserstack.key", key );
capability.SetCapability( "browserstack.debug", "true" );
capability.SetCapability( "resolution", "1024x768" );
capability.SetCapability( "build", buildName );
capability.SetCapability( "name", sessionName );
}
public static DesiredCapabilities GetChromeDriverCapabilities()
{
DesiredCapabilities capability = new DesiredCapabilities();
capability.SetCapability( "browser", "Chrome" );
capability.SetCapability( "browser_version", "62.0" );
capability.SetCapability( "os", "Windows" );
capability.SetCapability( "os_version", "10" );
capability.SetCapability( "ignore-certificate", true );
AddCommonCapabilities( capability );
return capability;
}
public static DesiredCapabilities GetFirefoxDriverCapabilities()
{
DesiredCapabilities capability = new DesiredCapabilities();
capability.SetCapability( "os", "Windows" );
capability.SetCapability( "os_version", "10" );
capability.SetCapability( "browser", "Firefox" );
capability.SetCapability( "browser_version", "58.0" );
capability.SetCapability( "acceptInsecureCerts", true );
AddCommonCapabilities( capability );
return capability;
}
public static DesiredCapabilities GetIEDriverCapabilities()
{
DesiredCapabilities capability = new DesiredCapabilities();
capability.SetCapability( "os", "Windows" );
capability.SetCapability( "os_version", "10" );
capability.SetCapability( "browser", "IE" );
capability.SetCapability( "browser_version", "11.0" );
AddCommonCapabilities( capability );
return capability;
}
}
}

Просмотреть файл

@ -0,0 +1,52 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Reflection;
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;
namespace AdfsUITestManager
{
class Manager
{
static void Main( string[] args )
{
// Contract:
// .\BrowserStackTestManager.exe
TestContext context = new TestContext();
InvokeTests( context.DriversPerTestCase, context.Configuration );
Console.WriteLine( "Done" );
}
private static void InvokeTests( Dictionary<MethodInfo, List<DesiredCapabilities>> driversPerTest, TaskConfiguration config )
{
foreach ( var test in driversPerTest.Keys )
{
foreach ( var driverData in driversPerTest[ test] )
{
IWebDriver driver = DriverFactory.GenerateDriver( driverData );
string sessionName = ( string )driverData.GetCapability( "name" );
string buildName = ( string )driverData.GetCapability( "build" );
try
{
Console.WriteLine( $"Executing test: {test.Name}" );
test.Invoke( null, new object[] { driver, config } );
BrowserStackLogReader.MarkTest(BrowserStackLogReader.GetSessionId( buildName, sessionName ), TestStatus.Passed);
}
catch ( Exception e )
{
Console.WriteLine("Exception thrown during test execution.");
Console.WriteLine( e.Message );
Console.WriteLine( e.StackTrace );
BrowserStackLogReader.MarkTest( BrowserStackLogReader.GetSessionId( buildName, sessionName ), TestStatus.Failed );
driver.Quit();
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,36 @@
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( "BrowserStackPaginatedUI" )]
[assembly: AssemblyDescription( "" )]
[assembly: AssemblyConfiguration( "" )]
[assembly: AssemblyCompany( "" )]
[assembly: AssemblyProduct( "BrowserStackPaginatedUI" )]
[assembly: AssemblyCopyright( "Copyright © 2018" )]
[assembly: AssemblyTrademark( "" )]
[assembly: AssemblyCulture( "" )]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible( false )]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid( "540afc72-5649-4ecf-b4d4-1273ce070504" )]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion( "1.0.0.0" )]
[assembly: AssemblyFileVersion( "1.0.0.0" )]

Просмотреть файл

@ -0,0 +1,25 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;
namespace AdfsUITestManager
{
public class ScreenShotRemoteWebDriver : RemoteWebDriver, ITakesScreenshot
{
public ScreenShotRemoteWebDriver( Uri uri, DesiredCapabilities dc )
: base( uri, dc )
{
}
public Screenshot GetScreenshot()
{
Response screenshotResponse = this.Execute( DriverCommand.Screenshot, null );
string base64 = screenshotResponse.Value.ToString();
return new Screenshot( base64 );
}
}
}

Просмотреть файл

@ -0,0 +1,255 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System.Configuration;
using System;
using System.Collections.Generic;
namespace AdfsUITestManager
{
public class TaskConfiguration : ConfigurationSection
{
[ConfigurationProperty( "farmData" )]
public FarmDataElement Farm
{
get
{
return ( FarmDataElement )this[ "farmData" ];
}
set
{ this[ "farmData" ] = value; }
}
[ConfigurationProperty( "userData" )]
public UserDataElement User
{
get
{
return ( UserDataElement )this[ "userData" ];
}
set
{ this[ "userData" ] = value; }
}
[ConfigurationProperty( "relyingPartyData" )]
public RelyingPartyDataElement RelyingPartyData
{
get
{
return ( RelyingPartyDataElement )this[ "relyingPartyData" ];
}
set
{ this[ "relyingPartyData" ] = value; }
}
[ConfigurationProperty( "screenshot" )]
public ScreenshotDataElement ScreenshotData
{
get
{
return ( ScreenshotDataElement )this[ "screenshot" ];
}
set
{ this[ "screenshot" ] = value; }
}
[ConfigurationProperty( "externalAuth" )]
public ExternalAuthDataElement ExternalAuth
{
get
{
return ( ExternalAuthDataElement )this[ "externalAuth" ];
}
set
{ this[ "externalAuth" ] = value; }
}
}
public class ExternalAuthDataElement : ConfigurationElement
{
[ConfigurationProperty( "challengeAnswers", IsRequired = true )]
public string AnswersString
{
get
{
return ( String )this[ "challengeAnswers" ];
}
}
public List<String> Answers
{
get
{
List<string> answers = new List<string>( AnswersString.Split( ',' ) );
return answers;
}
}
}
public class ScreenshotDataElement : ConfigurationElement
{
[ConfigurationProperty( "shouldScreenshot", IsRequired = true )]
public Boolean ShouldScreenshot
{
get
{
return ( Boolean )this[ "shouldScreenshot" ];
}
set
{
this[ "shouldScreenshot" ] = value;
}
}
}
public class RelyingPartyDataElement : ConfigurationElement
{
[ConfigurationProperty( "name", IsRequired = true )]
public String Name
{
get
{
return ( String )this[ "name" ];
}
set
{
this[ "name" ] = value;
}
}
[ConfigurationProperty( "wtrealm", IsRequired = true )]
public String Wtrealm
{
get
{
return ( String )this[ "wtrealm" ];
}
set
{
this[ "wtrealm" ] = value;
}
}
[ConfigurationProperty( "wreply", IsRequired = true )]
public String Wreply
{
get
{
return ( String )this[ "wreply" ];
}
set
{
this[ "wreply" ] = value;
}
}
}
public class FarmDataElement : ConfigurationElement
{
[ConfigurationProperty( "farmName", IsRequired = true )]
public String FarmName
{
get
{
return ( String )this[ "farmName" ];
}
set
{
this[ "farmName" ] = value;
}
}
}
public class UserDataElement : ConfigurationElement
{
[ConfigurationProperty( "misformattedUsername", IsRequired = true )]
public String MisformattedUsername
{
get
{
return ( String )this[ "misformattedUsername" ];
}
set
{
this[ "misformattedUsername" ] = value;
}
}
[ConfigurationProperty( "correctUsername", IsRequired = true )]
public String CorrectUsername
{
get
{
return ( String )this[ "correctUsername" ];
}
set
{
this[ "correctUsername" ] = value;
}
}
[ConfigurationProperty( "correctPassword", IsRequired = true )]
public String CorrectPassword
{
get
{
return ( String )this[ "correctPassword" ];
}
set
{
this[ "correctPassword" ] = value;
}
}
[ConfigurationProperty( "badPassword", IsRequired = true )]
public String BadPassword
{
get
{
return ( String )this[ "badPassword" ];
}
set
{
this[ "badPassword" ] = value;
}
}
[ConfigurationProperty( "externalAuthUsername", IsRequired = true )]
public String ExternalAuthUsername
{
get
{
return ( String )this[ "externalAuthUsername" ];
}
set
{
this[ "externalAuthUsername" ] = value;
}
}
[ConfigurationProperty( "adminUsername", IsRequired = true )]
public String AdminUsername
{
get
{
return ( String )this[ "adminUsername" ];
}
set
{
this[ "adminUsername" ] = value;
}
}
[ConfigurationProperty( "badUsername", IsRequired = true )]
public String BadUsername
{
get
{
return ( String )this[ "badUsername" ];
}
set
{
this[ "badUsername" ] = value;
}
}
}
}

Просмотреть файл

@ -0,0 +1,15 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace AdfsUITestManager
{
class TaskConfigurationFactory
{
public static TaskConfiguration GetConfiguration()
{
TaskConfiguration config = ( TaskConfiguration ) System.Configuration.ConfigurationManager.GetSection( "taskConfigurationGroup/taskConfiguration" );
return config;
}
}
}

Просмотреть файл

@ -0,0 +1,36 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using OpenQA.Selenium;
namespace AdfsUITestManager
{
/// <summary>
/// A class containing the tasks that can be performed on the External Authentication UI Page through AD FS
/// </summary>
class ExternalAuthTasks : TaskBase
{
/// <summary>
/// A task to enter an answer to a challenge question provided by an External Auth Provider
/// that prompts with challenge questions
///
/// Note: This External Auth Adapter is based on a test auth adapter used by the AD FS Product Group
///
/// </summary>
/// <param name="driver">A web driver</param>
/// <param name="config">Configuration data for this task</param>
public static void EnterChallengeAnswers( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
foreach ( string answer in config.ExternalAuth.Answers )
{
IWebElement challengeField = driver.FindElement( By.Id( "challengeQuestionInput" ) );
challengeField.SendKeys( $"{answer}" );
challengeField.SendKeys( Keys.Return );
LogAndScreenshot( driver, config );
}
}
}
}

Просмотреть файл

@ -0,0 +1,82 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using OpenQA.Selenium;
namespace AdfsUITestManager
{
/// <summary>
/// A class containing the tasks that can be performed on the Username/Password Page through AD FS
/// </summary>
class FormsPageTasks : TaskBase
{
// TODO: Add tasks to use 'Next' button instead of Return key
public static void EnterMisformattedUsername( IWebDriver driver, TaskConfiguration config )
{
IWebElement usernameField = driver.FindElement( By.Id( "userNameInput" ) );
usernameField.SendKeys( $"{config.User.MisformattedUsername}" );
usernameField.SendKeys( Keys.Return );
// TODO: Validate that the error message appears
LogAndScreenshot( driver, config );
}
public static void ClearUsername( IWebDriver driver, TaskConfiguration config )
{
IWebElement usernameField = driver.FindElement( By.Id( "userNameInput" ) );
usernameField.Clear();
}
public static void EnterBadUsername( IWebDriver driver, TaskConfiguration config )
{
IWebElement usernameField = driver.FindElement( By.Id( "userNameInput" ) );
usernameField.SendKeys( $"{config.User.BadUsername}" );
LogAndScreenshot( driver, config );
usernameField.SendKeys( Keys.Return );
// TODO: Validate that the error message appears
}
public static void EnterCorrectUsername( IWebDriver driver, TaskConfiguration config )
{
IWebElement usernameField = driver.FindElement( By.Id( "userNameInput" ) );
usernameField.SendKeys( $"{config.User.CorrectUsername}" );
LogAndScreenshot( driver, config );
usernameField.SendKeys( Keys.Return );
}
public static void EnterCorrectUsernameAndTab( IWebDriver driver, TaskConfiguration config )
{
IWebElement usernameField = driver.FindElement( By.Id( "userNameInput" ) );
usernameField.SendKeys( $"{config.User.CorrectUsername}" );
LogAndScreenshot( driver, config );
usernameField.SendKeys( Keys.Tab );
}
public static void EnterBadPassword( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
IWebElement passwordField = driver.FindElement( By.Id( "passwordInput" ) );
passwordField.SendKeys( $"{config.User.BadPassword}" );
passwordField.SendKeys( Keys.Return );
LogAndScreenshot( driver, config );
}
public static void EnterCorrectPassword( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
IWebElement passwordField = driver.FindElement( By.Id( "passwordInput" ) );
passwordField.SendKeys( $"{config.User.CorrectPassword}" );
passwordField.SendKeys( Keys.Return );
LogAndScreenshot( driver, config );
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using OpenQA.Selenium;
namespace AdfsUITestManager
{
/// <summary>
/// A class containing the tasks that can be performed on the IDP Initiated Sign On Page through AD FS
/// </summary>
class IdpPageTasks : TaskBase
{
public static void GoToIdpSignOnPage( IWebDriver driver, TaskConfiguration config )
{
string url = $"https://{config.Farm.FarmName}/adfs/ls/idpinitiatedsignon";
driver.Navigate().GoToUrl( url );
LogAndScreenshot( driver, config );
}
public static void GoToIdpSignOnPageWithLoginHint( IWebDriver driver, TaskConfiguration config )
{
driver.Navigate().GoToUrl( $"https://{config.Farm.FarmName}/adfs/ls/idpinitiatedsignon?login_hint={config.User.CorrectUsername}" );
LogAndScreenshot( driver, config );
}
public static void ClickSignInOnIdpPage( IWebDriver driver, TaskConfiguration config )
{
IWebElement signInButton = driver.FindElement( By.Id( "idp_SignInButton" ) );
signInButton.Click();
LogAndScreenshot( driver, config );
}
public static bool ValidateSignedIn( IWebDriver driver, TaskConfiguration config )
{
IWebElement signOutButton = driver.FindElement( By.Id( "idp_SignOutPanel" ) );
LogAndScreenshot( driver, config );
return signOutButton != null;
}
}
}

Просмотреть файл

@ -0,0 +1,52 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using OpenQA.Selenium;
namespace AdfsUITestManager
{
/// <summary>
/// A class containing the tasks that can be performed on the Primary/Secondary Credential Options Page through AD FS
/// </summary>
class OptionsPageTasks : TaskBase
{
public static void SelectFormsOnOptionPage( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
IWebElement passwordOption = driver.FindElement( By.Id( "FormsAuthentication" ) );
passwordOption.Click();
}
public static void SelectCertOnOptionPage( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
IWebElement certOption = driver.FindElement( By.Id( "CertificateAuthentication" ) );
certOption.Click();
LogAndScreenshot( driver, config );
}
public static void SelectExternalOnOptionPage( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
IWebElement externalOption = driver.FindElement( By.Id( "ExternalAuth" ) );
externalOption.Click();
LogAndScreenshot( driver, config );
}
public static void SignInWithOtherOptions( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
IWebElement otherOption = driver.FindElement( By.Id( "otherOptions" ) );
otherOption.Click();
LogAndScreenshot( driver, config );
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using OpenQA.Selenium;
namespace AdfsUITestManager
{
/// <summary>
/// A class containing the tasks that can be performed on the Paginated Login Page through AD FS
/// </summary>
class PaginatedFormsTasks : TaskBase
{
public static void PasswordPageBackNavigate( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
IWebElement backButton = driver.FindElement( By.Id( "backButton" ) );
backButton.Click();
LogAndScreenshot( driver, config );
}
public static void OptionsPageBackNavigate( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
IWebElement backButton = driver.FindElement( By.Id( "optionsBackButton" ) );
backButton.Click();
LogAndScreenshot( driver, config );
}
public static void UsernamePageForwardNavigate( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
IWebElement nextButton = driver.FindElement( By.Id( "nextButton" ) );
nextButton.Click();
LogAndScreenshot( driver, config );
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System;
using OpenQA.Selenium;
namespace AdfsUITestManager.Tasks
{
/// <summary>
/// A class containing the tasks that can be performed when requesting a Relying Party through AD FS
/// </summary>
class RelyingPartyTasks : TaskBase
{
public static void GoToRpSignIn( IWebDriver driver, TaskConfiguration config )
{
string url = $"https://{config.Farm.FarmName}/adfs/ls/?wa=wsignin1.0&wtrealm={config.RelyingPartyData.Wtrealm}&wreply={config.RelyingPartyData.Wreply}";
driver.Navigate().GoToUrl( url );
LogAndScreenshot( driver, config );
}
public static bool ValidateRpIsSignedIn( IWebDriver driver, TaskConfiguration config )
{
LogAndScreenshot( driver, config );
return string.Compare( driver.Url, config.RelyingPartyData.Wreply, StringComparison.InvariantCultureIgnoreCase ) == 0;
}
}
}

Просмотреть файл

@ -0,0 +1,22 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System;
using OpenQA.Selenium;
namespace AdfsUITestManager
{
class TaskBase
{
public static void LogAndScreenshot( IWebDriver driver, TaskConfiguration config )
{
Console.WriteLine( $"Current Page Title: {driver.Title}" );
if ( config.ScreenshotData.ShouldScreenshot )
{
Screenshot ss = ( ( ITakesScreenshot )driver ).GetScreenshot();
ss.SaveAsFile( $"{ DateTime.Now:yyyy - MM - dd_hh - mm - ss - fff}.png", ScreenshotImageFormat.Png );
}
}
}
}

Просмотреть файл

@ -0,0 +1,237 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using OpenQA.Selenium;
using System.Diagnostics;
namespace AdfsUITestManager
{
using AdfsUITestManager.Tasks;
public class TestCases
{
#region IDP Initiated Sign On Tests
/// <summary>
/// ENVIRONMENT REQUIREMENTS:
/// 1. Paginated UI feature enabled
/// 2. Multiple auth options available
/// </summary>
/// <param name="driver"></param>
static void BasicIdpSignOn_WithOptions_Password( IWebDriver driver, TaskConfiguration configuration )
{
// Perform UI Operations
IdpPageTasks.GoToIdpSignOnPage( driver, configuration );
IdpPageTasks.ClickSignInOnIdpPage( driver, configuration );
FormsPageTasks.EnterMisformattedUsername( driver, configuration );
FormsPageTasks.ClearUsername( driver, configuration );
FormsPageTasks.EnterCorrectUsername( driver, configuration );
OptionsPageTasks.SelectFormsOnOptionPage( driver, configuration );
FormsPageTasks.EnterBadPassword( driver, configuration );
OptionsPageTasks.SelectFormsOnOptionPage( driver, configuration );
FormsPageTasks.EnterCorrectPassword( driver, configuration );
// Perform test validation
var success = IdpPageTasks.ValidateSignedIn( driver, configuration );
Debug.Assert( success );
// Clean up
driver.Quit();
}
/// <summary>
/// ENVIRONMENT REQUIREMENTS:
/// 1. Paginated UI feature enabled
/// 2. Multiple auth options available (including certificate)
/// </summary>
/// <param name="driver"></param>
static void BasicIdpSignOn_WithOptions_CertThenForms( IWebDriver driver, TaskConfiguration configuration )
{
// Perform UI Operations
IdpPageTasks.GoToIdpSignOnPage( driver, configuration );
IdpPageTasks.ClickSignInOnIdpPage( driver, configuration );
FormsPageTasks.EnterMisformattedUsername( driver, configuration );
FormsPageTasks.EnterCorrectUsername( driver, configuration );
OptionsPageTasks.SelectCertOnOptionPage( driver, configuration );
OptionsPageTasks.SignInWithOtherOptions( driver, configuration );
OptionsPageTasks.SelectFormsOnOptionPage( driver, configuration );
FormsPageTasks.EnterCorrectPassword( driver, configuration );
// Perform test validation
var success = IdpPageTasks.ValidateSignedIn( driver, configuration );
Debug.Assert( success );
// Cleanup
driver.Quit();
}
/// <summary>
/// ENVIRONMENT REQUIREMENTS:
/// 1. Paginated UI feature enabled
/// 2. Multiple auth options available (including external)
/// 3. User configured for external auth
/// </summary>
/// <param name="driver"></param>
static void BasicIdpSignOn_WithOptions_External( IWebDriver driver, TaskConfiguration configuration )
{
// Perform UI Operations
IdpPageTasks.GoToIdpSignOnPage( driver, configuration );
IdpPageTasks.ClickSignInOnIdpPage( driver, configuration );
FormsPageTasks.EnterCorrectUsername( driver, configuration );
OptionsPageTasks.SelectExternalOnOptionPage( driver, configuration );
ExternalAuthTasks.EnterChallengeAnswers( driver, configuration );
// Perform test validation
var success = IdpPageTasks.ValidateSignedIn( driver, configuration );
Debug.Assert( success );
// Cleanup
driver.Quit();
}
/// <summary>
/// ENVIRONMENT REQUIREMENTS:
/// 1. Paginated UI feature enabled
/// 2. Multiple auth options available (including external)
/// 3. User configured for external auth
/// </summary>
/// <param name="driver"></param>
static void HintIdpSignOn_WithOptions_External( IWebDriver driver, TaskConfiguration configuration )
{
// Perform UI Operations
IdpPageTasks.GoToIdpSignOnPageWithLoginHint( driver, configuration );
IdpPageTasks.ClickSignInOnIdpPage( driver, configuration );
OptionsPageTasks.SelectExternalOnOptionPage( driver, configuration );
ExternalAuthTasks.EnterChallengeAnswers( driver, configuration );
// Perform test validation
var success = IdpPageTasks.ValidateSignedIn( driver, configuration );
Debug.Assert( success );
// Cleanup
driver.Quit();
}
/// <summary>
/// ENVIRONMENT REQUIREMENTS:
/// 1. Paginated UI feature enabled
/// 2. Multiple auth options available (including external)
/// </summary>
/// <param name="driver"></param>
static void HintIdpSignOn_WithOptions_Password_ChangeUser( IWebDriver driver, TaskConfiguration configuration )
{
// Perform UI Operations
IdpPageTasks.GoToIdpSignOnPageWithLoginHint( driver, configuration );
IdpPageTasks.ClickSignInOnIdpPage( driver, configuration );
PaginatedFormsTasks.OptionsPageBackNavigate( driver, configuration );
FormsPageTasks.ClearUsername( driver, configuration );
FormsPageTasks.EnterCorrectUsername( driver, configuration );
OptionsPageTasks.SelectFormsOnOptionPage( driver, configuration );
FormsPageTasks.EnterCorrectPassword( driver, configuration );
// Perform test validation
var success = IdpPageTasks.ValidateSignedIn( driver, configuration );
Debug.Assert( success );
// Cleanup
driver.Quit();
}
/// <summary>
/// ENVIRONMENT REQUIREMENTS:
/// 1. Paginated UI feature enabled
/// 2. Only forms auth options available
/// </summary>
/// <param name="driver"></param>
static void BasicIdpSignOn_NoOptions_Password( IWebDriver driver, TaskConfiguration configuration )
{
// Perform UI Operations
IdpPageTasks.GoToIdpSignOnPage( driver, configuration );
IdpPageTasks.ClickSignInOnIdpPage( driver, configuration );
FormsPageTasks.EnterMisformattedUsername( driver, configuration );
FormsPageTasks.EnterCorrectUsername( driver, configuration );
PaginatedFormsTasks.PasswordPageBackNavigate( driver, configuration );
PaginatedFormsTasks.UsernamePageForwardNavigate( driver, configuration );
FormsPageTasks.EnterCorrectPassword( driver, configuration );
// Perform test validation
var success = IdpPageTasks.ValidateSignedIn( driver, configuration );
Debug.Assert( success );
// Cleanup
driver.Quit();
}
/// <summary>
/// ENVIRONMENT REQUIREMENTS:
/// 1. Paginated UI feature disabled
/// </summary>
/// <param name="driver"></param>
static void BasicIdpSignOn_Password_Legacy( IWebDriver driver, TaskConfiguration configuration )
{
// Perform UI Operations
IdpPageTasks.GoToIdpSignOnPage( driver, configuration );
IdpPageTasks.ClickSignInOnIdpPage( driver, configuration );
FormsPageTasks.EnterCorrectUsernameAndTab( driver, configuration );
FormsPageTasks.EnterCorrectPassword( driver, configuration );
// Perform test validation
var success = IdpPageTasks.ValidateSignedIn( driver, configuration );
Debug.Assert(success);
// Clean up
driver.Quit();
}
/// <summary>
/// ENVIRONMENT REQUIREMENTS:
/// 1. Paginated UI feature disabled
/// </summary>
/// <param name="driver"></param>
static void BasicIdpSignOn_Password_Failure_Legacy( IWebDriver driver, TaskConfiguration configuration )
{
// Perform UI Operations
IdpPageTasks.GoToIdpSignOnPage( driver, configuration );
IdpPageTasks.ClickSignInOnIdpPage( driver, configuration );
FormsPageTasks.EnterCorrectUsername( driver, configuration );
FormsPageTasks.EnterBadPassword( driver, configuration );
// Perform test validation
var success = IdpPageTasks.ValidateSignedIn( driver, configuration );
Debug.Assert( success );
// Cleanup
driver.Quit();
}
#endregion
#region Basic RP Sign On Tests
/// <summary>
/// ENVIRONMENT REQUIREMENTS:
/// 1. Paginated UI feature enabled
/// 2. Multiple auth options available
/// 3. Relying party exists, and user has access to it
/// </summary>
/// <param name="driver"></param>
static void BasicRpSignOn_WithOptions_Password( IWebDriver driver, TaskConfiguration configuration )
{
// Perform UI Operations
RelyingPartyTasks.GoToRpSignIn( driver, configuration );
FormsPageTasks.EnterCorrectUsername( driver, configuration );
OptionsPageTasks.SelectFormsOnOptionPage( driver, configuration );
FormsPageTasks.EnterBadPassword( driver, configuration );
OptionsPageTasks.SelectFormsOnOptionPage( driver, configuration );
FormsPageTasks.EnterCorrectPassword( driver, configuration );
// Perform test validation
var success = RelyingPartyTasks.ValidateRpIsSignedIn( driver, configuration );
Debug.Assert( success );
// Clean up
driver.Quit();
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,71 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System.Collections.Generic;
using System.Reflection;
using OpenQA.Selenium.Remote;
namespace AdfsUITestManager
{
using System;
class TestContext
{
public List<MethodInfo> TestCases;
//public List<IWebDriver> BrowserDrivers;
public TaskConfiguration Configuration;
public Dictionary<MethodInfo, List<DesiredCapabilities>> DriversPerTestCase;
public TestContext()
{
TestListConfiguration testListConfig = ( TestListConfiguration )System.Configuration.ConfigurationManager.GetSection( "testListConfigurationGroup/testListConfiguration" );
List<string> testCases = new List<string>( testListConfig.TestData.TestIds );
this.TestCases = this.TestCases = ParseAndVerifyTestIds( testCases );
this.Configuration = TaskConfigurationFactory.GetConfiguration();
this.DriversPerTestCase = new Dictionary<MethodInfo, List<DesiredCapabilities>>();
foreach ( var testcase in this.TestCases )
{
this.DriversPerTestCase[testcase] = new List<DesiredCapabilities> { DriverFactory.GetChromeDriverCapabilities() };
}
}
private List<MethodInfo> ParseAndVerifyTestIds( List<string> testIdsRaw )
{
if ( testIdsRaw.Count == 0 )
{
throw new ArgumentNullException( $"Cannot find any test names in string {testIdsRaw}. Please check the string, and ensure that test names are comma-separated." );
}
List<MethodInfo> methods = new List<MethodInfo>();
Type type = typeof( TestCases );
foreach ( var testLine in testIdsRaw )
{
if ( string.IsNullOrEmpty( testLine ) )
{
// Skip blank lines
continue;
}
// Handle the case where tests are comma-separated
List<string> testIds = new List<string>( testLine.Split( ',' ) );
foreach ( var test in testIds )
{
MethodInfo methodInfo = type.GetMethod( test, BindingFlags.Static | BindingFlags.NonPublic );
if ( methodInfo == null )
{
throw new ArgumentNullException( $"Cannot find test name {test}. Please check BrowserStackTestManager.TestCases.cs to ensure your test case exists. Note: names are case-sensitive." );
}
methods.Add( methodInfo );
}
}
return methods;
}
}
}

Просмотреть файл

@ -0,0 +1,47 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Configuration;
namespace AdfsUITestManager
{
class TestListConfiguration : ConfigurationSection
{
[ConfigurationProperty( "testData" )]
public TestDataElement TestData
{
get
{
return ( TestDataElement )this[ "testData" ];
}
set
{ this[ "testData" ] = value; }
}
public class TestDataElement : ConfigurationElement
{
[ConfigurationProperty( "testIds", IsRequired = true )]
public string TestIdsString
{
get
{
return ( String )this[ "testIds" ];
}
}
public List<String> TestIds
{
get
{
List<string> answers = new List<string>( TestIdsString.Split( ',' ) );
return answers;
}
}
}
}
}

Просмотреть файл

Просмотреть файл

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net452" />
<package id="Selenium.Support" version="3.11.0" targetFramework="net452" />
<package id="Selenium.WebDriver" version="3.11.0" targetFramework="net452" />
<package id="Selenium.WebDriverBackedSelenium" version="3.11.0" targetFramework="net452" />
</packages>

132
README.md
Просмотреть файл

@ -1,5 +1,133 @@
# AD FS Automated User Interface Testing
# Contributing
## Overview
This project provides a set of automated UI tests that can be performed to validate the user interface of an AD FS deployment.
Note that in order to execute these tests, you must have either your own Selenium infrastructure, or a subscription to a remote Selenium infrastructure.
This project is set up to support BrowserStack as the remote Selenium infrastructure. You can use a free BrowserStack subscription for 100 hours of testing, after which you will need a paid subscription.
For more details, see [BrowserStack](https://www.browserstack.com)
## Requirements
1. Visual Studio
2. BrowserStack subscription, free trial, or custom Selenium infrastructure
3. Internet access from your AD FS Domain Controller
## Building the Project
1. Open the `AdfsUITestManager.sln` solution in Visual Studio
2. In the project directory containing `AdfsUITestManager.csproj`, add an application configuration (`App.config`) file. Your `App.config` file should contain the following:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="taskConfigurationGroup">
<section
name="taskConfiguration"
type="AdfsUITestManager.TaskConfiguration, AdfsUITestManager"
allowLocation="true"
allowDefinition="Everywhere" />
</sectionGroup>
<sectionGroup name="testListConfigurationGroup">
<section
name="testListConfiguration"
type="AdfsUITestManager.TestListConfiguration, AdfsUITestManager"
allowLocation="true"
allowDefinition="Everywhere" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<appSettings>
<add key="RemoteDriverUri" value="http://hub-cloud.browserstack.com/wd/hub/"/>
<add key="BrowserStackUser" value=<ENTER BROWSERSTACK USERNAME>/>
<add key="BrowserStackKey" value=<ENTER BROWSERTACK API KEY>/>
</appSettings>
<!-- Task Configuration -->
<taskConfigurationGroup>
<taskConfiguration>
<farmData farmName=<ENTER FARMNAME>/>
<userData correctUsername=<ENTER CORRECT USERNAME>
correctPassword=<ENTER CORRECT PASSWORD>
badPassword="badpassword"
externalAuthUsername=<ENTER A USERNAME REGISTERED FOR EXTERNAL AUTH>
adminUsername=<ENTER ADMIN USERNAME>
badUsername="badUser@farm.com"
misformattedUsername="wronguser"
/>
<relyingPartyData name=<ENTER RP NAME>
wtrealm=<ENTER WTREALM>
wreply=<ENTER WREPLY>
/>
<screenshot shouldScreenshot="true"/>
<externalAuth challengeAnswers=<ENTER ANSWERS, COMMA SEPARATED> />
</taskConfiguration>
</taskConfigurationGroup>
<!-- Test List Configuration -->
<testListConfigurationGroup>
<testListConfiguration>
<testData testIds="BasicRpSignOn_WithOptions_Password" />
</testListConfiguration>
</testListConfigurationGroup>
</configuration>
You can locate the BrowserStack settings [here](https://www.browserstack.com/automate/c-sharp)
3. Update the list of test IDs under the `<testListConfiguration>` element to include the comma-separated list of tests you wish to run against your environment.
The list of supported test cases is in `TestCases.cs`. If you wish to add more tests, please add them under `TestCases.cs`.
4. Build the project using Visual Studio
## Setting Up Your Environment
To execute the UI tests against your own AD FS environment, you must:
**Deploy BrowserStack Test Agent**
1. Install the [BrowserStack local testing agent](https://www.browserstack.com/browserstack-local/BrowserStackLocal-win32.zip) on your AD FS Domain Controller.
For more details on BrowserStack local testing, see [here](https://www.browserstack.com/local-testing)
2. Determine your BrowserStack Automate Access Key, under ["Settings" > "Automate"](https://www.browserstack.com/accounts/settings)
3. Execute the BrowerStackLocal agent by running the following in a command prompt on your AD FS Domain Controller:
`BrowserStackLocal.exe --key <key>`
## Running the Tests
1. Decide what test cases you want to run. Note that different test cases have different environment requirements. You should only run the tests that match your environment.
All test cases, along with the environment requirements, are listed in `TestCases.cs`.
2. Update the list of test IDs under the `<testListConfiguration>` element to include the comma-separated list of tests you wish to run against your environment.
3. Execute the test manager by running:
`.\AdfsUITestManager.exe`
## Validating the Results
You can find the BrowserStack results [here](https://www.browserstack.com/automate). These results will contain screenshots for all steps, along with a video you can review.
Each test case validates that the user scenario works correctly, but it does not validate the "look and feel" of any customizations, branding, etc. you have applied.
To ensure that the pages look and operate as you expect, we encourage you to manually examine the screenshots and video of the test runs.
## Submitting AD FS UI Bugs
If you find issues with the base AD FS User Interface, please open an [Issue](https://github.com/Microsoft/adfsUITests/issues) against this project. Please include the following information:
1. Your AD FS Build Number and Behavior Level (major and minor, if applicable)
2. A detailed description of the problem you see
3. Screenshots of the issue you see
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
@ -11,4 +139,4 @@ provided by the bot. You will only need to do this once across all repos using o
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.