Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>1.0.2</Version>
<Version>1.0.5</Version>
<Authors>NETWORG</Authors>
<Company>NETWORG</Company>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.PowerPlatform.Dataverse.Client;
using System;
using System.IO;
using System.Threading.Tasks;

namespace TALXIS.TestKit.Bindings.Configuration
{
public static class DataverseServiceClientFactory
{
public static ServiceClient CreateWithToken(string dataverseUrl,string accessToken)
{
return new ServiceClient(
new Uri(dataverseUrl),
(authority) => Task.FromResult(accessToken),
true,
null);
}

public static ServiceClient CreateWithClientCredentials(string dataverseUrl, ClientCredentials credentials)
{
string connectionString = $"AuthType=ClientSecret;Url={ExtractBaseUrl(dataverseUrl)};ClientId={credentials.ClientId};ClientSecret={credentials.ClientSecret};TenantId={credentials.TenantId};";

return new ServiceClient(dataverseConnectionString: connectionString);
}

public static string ExtractBaseUrl(string fullUrl)
{
if (string.IsNullOrWhiteSpace(fullUrl))
throw new ArgumentException("URL is empty", nameof(fullUrl));

var uri = new Uri(fullUrl);

return $"{uri.Scheme}://{uri.Host}";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections;
using System.Collections.Generic;

namespace TALXIS.TestKit.Bindings.Configuration
{
public class PersonaConfiguration
{
/// <summary>
/// Gets or sets the username of the pesrona.
/// </summary>
public string Username { get; set; }

/// <summary>
/// Gets or sets the password of the pesrona.
/// </summary>
public string Password { get; set; }

/// <summary>
/// Gets or sets the OTP token of the pesrona.
/// </summary>
public string OtpToken { get; set; }

/// <summary>
/// Gets or sets the alias of the pesrona (used to retrieve from configuration).
/// </summary>
public string Alias { get; set; }

/// <summary>
/// Gets or sets the Security Roles of the pesrona.
/// </summary>
public List<Guid> SecurityRoles { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.IO;

namespace TALXIS.TestKit.Bindings.Configuration
{
public class RoleAssignmentService
{
private readonly ServiceClient _serviceClient;

public RoleAssignmentService(ServiceClient serviceClient)
{
_serviceClient = serviceClient;
}

public void UpdateSecurityRoles(string user, List<Guid> roleIds)
{
ArgumentNullException.ThrowIfNull(user, nameof(user));

Guid? userIdNullable = GetUserIdByEmailOrUsername(user);

ArgumentNullException.ThrowIfNull(userIdNullable, nameof(userIdNullable));

Guid userId = userIdNullable.Value;

RemoveAllRolesFromUser(userId);

AssignRoles(userId, roleIds);
}

public void AssignRoles(Guid userId, List<Guid> roleIds)
{
ArgumentNullException.ThrowIfNull(userId, nameof(userId));
ArgumentNullException.ThrowIfNull(roleIds, nameof(roleIds));

var entityReferenceCollection = new EntityReferenceCollection();

foreach (var roleId in roleIds)
{
entityReferenceCollection.Add(new EntityReference("role", roleId));
}

_serviceClient.Associate(
"systemuser", userId,
new Relationship("systemuserroles_association"),
entityReferenceCollection
);
}

public void RemoveAllRolesFromUser(Guid userId)
{
var query = new QueryExpression("role")
{
ColumnSet = new ColumnSet("roleid"),
LinkEntities =
{
new LinkEntity
{
LinkFromEntityName = "role",
LinkFromAttributeName = "roleid",
LinkToEntityName = "systemuserroles",
LinkToAttributeName = "roleid",
JoinOperator = JoinOperator.Inner,
LinkCriteria =
{
Conditions =
{
new ConditionExpression("systemuserid", ConditionOperator.Equal, userId)
}
}
}
}
};

var result = _serviceClient.RetrieveMultiple(query);

if (result.Entities.Count == 0)
{
return;
}

var rolesToRemove = new EntityReferenceCollection();

foreach (var entity in result.Entities)
{
rolesToRemove.Add(new EntityReference("role", entity.Id));
}

_serviceClient.Disassociate(
"systemuser",
userId,
new Relationship("systemuserroles_association"),
rolesToRemove
);
}

private Guid? GetUserIdByEmailOrUsername(string user)
{
var query = new QueryExpression("systemuser")
{
ColumnSet = new ColumnSet("systemuserid", "domainname"),
Criteria = new FilterExpression(LogicalOperator.And)
{
Filters =
{
new FilterExpression(LogicalOperator.Or)
{
Conditions =
{
new ConditionExpression("domainname", ConditionOperator.Equal, user),
new ConditionExpression("internalemailaddress", ConditionOperator.Equal, user)
}
}
},
Conditions =
{
new ConditionExpression("isdisabled", ConditionOperator.Equal, false)
}
}
};

var result = _serviceClient.RetrieveMultiple(query);

if (result.Entities.Count == 0)
{
return null;
}

return result.Entities[0].Id;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
namespace TALXIS.TestKit.Bindings.Configuration
{
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.Configuration;

namespace TALXIS.TestKit.Bindings.Configuration
{
/// <summary>
/// Test configuration for PowerApps UI testing.
/// </summary>
public class TestConfiguration
{
private readonly IConfiguration configuration;
private static Dictionary<string, UserConfiguration> currentUsers = new Dictionary<string, UserConfiguration>();
private static Dictionary<string, PersonaConfiguration> currentPersona = new Dictionary<string, PersonaConfiguration>();

public TestConfiguration(IConfiguration configuration)
{
this.configuration = configuration;
this.BrowserOptions = configuration.GetSection("BrowserOptions").Get<BrowserOptionsWithProfileSupport>();
this.Users = configuration.GetSection("Users").Get<List<UserConfiguration>>();
this.Personas = configuration.GetSection("Personas").Get<List<PersonaConfiguration>>();
this.Url = configuration["Url"];
this.UseProfiles = configuration.GetValue<bool>("UseProfiles");
this.DeleteTestData = configuration.GetValue<bool>("DeleteTestData");
this.ProfilesBasePath = configuration["ProfilesBasePath"];
this.ApplicationUser = configuration.GetSection("applicationUser").Get<ClientCredentials>();

SpecifyUsersForPersonas(configuration.GetSection("Users").Get<List<UserConfiguration>>());

// TODO: Make this overrideable from config
//this.BrowserOptions.DriversPath
}
Expand All @@ -47,8 +51,7 @@ public TestConfiguration(IConfiguration configuration)
/// </summary>
public string ProfilesBasePath { get; set; }
public BrowserOptionsWithProfileSupport BrowserOptions { get; set; }
public List<UserConfiguration> Users { get; set; }

public List<PersonaConfiguration> Personas { get; set; }
public ClientCredentials ApplicationUser { get; set; }


Expand All @@ -61,20 +64,20 @@ public Uri GetTestUrl()
return new Uri(this.Url);
}

public UserConfiguration GetUser(string userAlias, bool useCurrentUser = true)
public PersonaConfiguration GetPersona(string userAlias, bool useCurrentUser = true)
{
if (useCurrentUser && currentUsers.ContainsKey(userAlias))
if (useCurrentUser && currentPersona.ContainsKey(userAlias))
{
return currentUsers[userAlias];
return currentPersona[userAlias];
}

var user = this.Users.Find(u => u.Alias == userAlias);
var user = this.Personas.Find(u => u.Alias == userAlias);
if (user == null)
{
throw new Exception("User not found");
}

currentUsers[userAlias] = user;
currentPersona[userAlias] = user;
return user;
}

Expand All @@ -83,7 +86,21 @@ public UserConfiguration GetUser(string userAlias, bool useCurrentUser = true)
/// </summary>
internal void Flush()
{
currentUsers.Clear();
currentPersona.Clear();
}

private void SpecifyUsersForPersonas(List<UserConfiguration> users)
{
foreach (var persona in Personas)
{
var matchingUser = users.FirstOrDefault(u => u.Username == persona.Username);

if (matchingUser != null)
{
persona.Password = matchingUser.Password;
persona.OtpToken = matchingUser.OtpToken;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace TALXIS.TestKit.Bindings.Configuration
using System.Collections;

namespace TALXIS.TestKit.Bindings.Configuration
{
/// <summary>
/// A user that tests can run as.
Expand All @@ -15,11 +17,6 @@ public class UserConfiguration
/// </summary>
public string Password { get; set; }

/// <summary>
/// Gets or sets the alias of the user (used to retrieve from configuration).
/// </summary>
public string Alias { get; set; }

/// <summary>
/// Gets or sets the OTP token of the user.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
using System.IO;
using System.Reflection;
using OpenQA.Selenium.Firefox;

/// <summary>
/// Reads test data from JSON files.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,18 @@ public void ScreenshotFailedScenario()
[AfterScenario]
public static void DisposeTempProfiles()
{
var basePath = string.IsNullOrEmpty(TestConfig.ProfilesBasePath) ? Path.GetTempPath() : TestConfig.ProfilesBasePath;
try
{
var basePath = string.IsNullOrEmpty(TestConfig.ProfilesBasePath) ? Path.GetTempPath() : TestConfig.ProfilesBasePath;

var tempProfilesDirectory = Path.Combine(basePath, "profiles", "TempProfiles");
var tempProfilesDirectory = Path.Combine(basePath, "profiles", "TempProfiles");

if (Directory.Exists(tempProfilesDirectory))
{
Directory.Delete(tempProfilesDirectory, true);
if (Directory.Exists(tempProfilesDirectory))
{
Directory.Delete(tempProfilesDirectory, true);
}
}
catch { }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static void BaseProfileSetup()
var webClient = new WebClient(userBrowserOptions);
using (var app = new XrmApp(webClient))
{
var user = TestConfig.Users.First(u => u.Username == username);
var user = TestConfig.Personas.First(u => u.Username == username);
app.OnlineLogin.Login(TestConfig.GetTestUrl(), user.Username.ToSecureString(), user.Password.ToSecureString());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public abstract class PowerAppsStepDefiner
/// <summary>
/// Gets access token used to authenticate as the application user configured for testing.
/// </summary>
protected static string AccessToken
internal static string AccessToken
{
get
{
Expand Down Expand Up @@ -193,7 +193,7 @@ protected static IDictionary<string, string> UserProfileDirectories
var profilesDirectory = Path.Combine(basePath, "profiles", "SaveProfiles");

Directory.CreateDirectory(profilesDirectory);
userProfilesDirectories = TestConfig.Users
userProfilesDirectories = TestConfig.Personas
.Where(u => !string.IsNullOrEmpty(u.Password))
.Select(u => u.Username)
.Distinct()
Expand Down
Loading