Skip to content

Commit 26ed97f

Browse files
ngwalkerewingjmAlex Bance
authored
feat: multiple users per alias (#88)
Allows multiple users to be configured for a given alias which will result in tests being evenly distributed across the users. This is required for running large numbers of tests in parallel to avoid API limit issues. Co-authored-by: Max Ewing <max.ewing@outlook.com> Co-authored-by: Alex Bance <alex.bance@capgemini.com>
1 parent 1ece3d6 commit 26ed97f

File tree

6 files changed

+57
-5
lines changed

6 files changed

+57
-5
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ users: # mandatory
7474
7575
The URL, driversPath, usernames, passwords, and application user details will be set from environment variable (if found). Otherwise, the value from the config file will be used. The browserOptions node supports anything in the EasyRepro `BrowserOptions` class.
7676

77+
Tests will be distributed evenly between the users if multiple users are configured with the same alias. This can be helpful when you run a large number of tests in parallel and are encountering errors due to user-level platform API limits.
78+
7779
#### User profiles
7880

7981
Setting the `useProfiles` property to true causes the solution to create and use a unique [profile](https://support.google.com/chrome/answer/2364824?co=GENIE.Platform%3DDesktop&hl=en) for each user listed in the config file. This currently only works in Chrome & Firefox and attempting to use it with Edge or IE will cause an exception to be thrown. By using profiles test runs for the same user will not be required to re-authenticate, this saves time during test runs. [To take full advantage of this you will need to have the "Stay Signed In" prompt enabled.](https://docs.microsoft.com/en-us/azure/active-directory/fundamentals/keep-me-signed-in)

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Configuration/TestConfiguration.cs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Configuration;
66
using System.Linq;
7+
using System.Threading;
78
using YamlDotNet.Serialization;
89

910
/// <summary>
@@ -18,6 +19,9 @@ public class TestConfiguration
1819

1920
private const string GetUserException = "Unable to retrieve user configuration. Please ensure a user with the given alias exists in the config.";
2021

22+
private readonly object userEnumeratorsLock = new object();
23+
private readonly ThreadLocal<UserConfiguration> currentUser = new ThreadLocal<UserConfiguration>();
24+
private Dictionary<string, IEnumerator<UserConfiguration>> userEnumerators;
2125
private string profilesBasePath;
2226

2327
/// <summary>
@@ -64,6 +68,28 @@ public TestConfiguration()
6468
[YamlMember(Alias = "applicationUser")]
6569
public ClientCredentials ApplicationUser { get; set; }
6670

71+
[YamlIgnore]
72+
private Dictionary<string, IEnumerator<UserConfiguration>> UserEnumerators
73+
{
74+
get
75+
{
76+
lock (this.userEnumeratorsLock)
77+
{
78+
if (this.userEnumerators == null)
79+
{
80+
this.userEnumerators = this.Users
81+
.Select(user => user.Alias)
82+
.Distinct()
83+
.ToDictionary(
84+
alias => alias,
85+
alias => this.Users.Where(u => u.Alias == alias).GetEnumerator());
86+
}
87+
}
88+
89+
return this.userEnumerators;
90+
}
91+
}
92+
6793
/// <summary>
6894
/// Gets the target URL.
6995
/// </summary>
@@ -77,17 +103,36 @@ public Uri GetTestUrl()
77103
/// Retrieves the configuration for a user.
78104
/// </summary>
79105
/// <param name="userAlias">The alias of the user.</param>
106+
/// <param name="useCurrentUser">Indicates whether to return the current user or get the next available.</param>
80107
/// <returns>The user configuration.</returns>
81-
public UserConfiguration GetUser(string userAlias)
108+
public UserConfiguration GetUser(string userAlias, bool useCurrentUser = true)
82109
{
110+
if (useCurrentUser && this.currentUser.Value != null)
111+
{
112+
return this.currentUser.Value;
113+
}
114+
83115
try
84116
{
85-
return this.Users.First(u => u.Alias == userAlias);
117+
lock (this.userEnumeratorsLock)
118+
{
119+
var aliasEnumerator = this.UserEnumerators[userAlias];
120+
if (!aliasEnumerator.MoveNext())
121+
{
122+
this.UserEnumerators[userAlias] = this.Users.Where(u => u.Alias == userAlias).GetEnumerator();
123+
aliasEnumerator = this.UserEnumerators[userAlias];
124+
aliasEnumerator.MoveNext();
125+
}
126+
127+
this.currentUser.Value = aliasEnumerator.Current;
128+
}
86129
}
87130
catch (Exception ex)
88131
{
89132
throw new ConfigurationErrorsException($"{GetUserException} User: {userAlias}", ex);
90133
}
134+
135+
return this.currentUser.Value;
91136
}
92137
}
93138
}

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/LoginSteps.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ public static void Login(IWebDriver driver, Uri orgUrl, string username, string
5454
/// <param name="appName">The name of the app.</param>
5555
/// <param name="userAlias">The alias of the user.</param>
5656
[Given("I am logged in to the '(.*)' app as '(.*)'")]
57-
public void GivenIAmLoggedInToTheAppAs(string appName, string userAlias)
57+
public static void GivenIAmLoggedInToTheAppAs(string appName, string userAlias)
5858
{
59-
var user = TestConfig.GetUser(userAlias);
59+
var user = TestConfig.GetUser(userAlias, useCurrentUser: false);
6060

6161
if (TestConfig.UseProfiles && TestConfig.BrowserOptions.BrowserType.SupportsProfiles())
6262
{

bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Capgemini.PowerApps.SpecFlowBindings.UiTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
<PackageReference Include="Microsoft.CrmSdk.XrmTooling.CoreAssembly" Version="9.1.0.64" />
3939
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
4040
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
41-
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="87.0.4280.8800" />
41+
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="91.0.4472.10100" />
4242
<PackageReference Include="SpecFlow" Version="3.5.14" />
4343
<PackageReference Include="SpecFlow.MsTest" Version="3.5.14" />
4444
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.5.14" />

bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/power-apps-bindings.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,8 @@ users:
1515
- username: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_USERNAME
1616
password: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_PASSWORD
1717
alias: an admin
18+
- username: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_USERNAME2
19+
password: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_PASSWORD2
20+
alias: an admin
1821
- username: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_USERNAME
1922
alias: an aliased user

templates/include-build-and-test-steps.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ jobs:
9292
POWERAPPS_SPECFLOW_BINDINGS_TEST_CLIENTSECRET: $(Application User Client Secret)
9393
POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_USERNAME: $(User ADO Integration Username)
9494
POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_PASSWORD: $(User ADO Integration Password)
95+
POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_USERNAME2: $(Extra Admin User Username)
96+
POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_PASSWORD2: $(Extra Admin User Password)
9597
POWERAPPS_SPECFLOW_BINDINGS_TEST_URL: $(URL)
9698
- task: SonarCloudAnalyze@1
9799
displayName: Analyse with SonarCloud

0 commit comments

Comments
 (0)