Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored UI Tests #3039

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
44 changes: 26 additions & 18 deletions tests/E2E Tests/WebAppUiTests/B2CWebAppCallsWebApiLocally.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.Versioning;
using System.Threading;
using System.Text;
using System.Threading.Tasks;
using Azure.Identity;
using Microsoft.Identity.Lab.Api;
using Microsoft.Playwright;
using Xunit;
using Xunit.Abstractions;
using TC = Microsoft.Identity.Web.Test.Common.TestConstants;
using UITH = WebAppUiTests.UiTestHelpers;

namespace WebAppUiTests
#if !FROM_GITHUB_ACTION
Expand All @@ -26,6 +28,7 @@ public class B2CWebAppCallsWebApiLocally : IClassFixture<InstallPlaywrightBrowse
private const string KeyvaultPasswordName = "IdWeb-B2C-password";
private const string KeyvaultClientSecretName = "IdWeb-B2C-Client-ClientSecret";
private const string NameOfUser = "unknown";
private const uint NumProcessRetries = 3;
private const uint TodoListClientPort = 5000;
private const uint TodoListServicePort = 44332;
private const string TraceClassName = "B2CWebAppCallsWebApiLocally";
Expand All @@ -46,7 +49,7 @@ public async Task Susi_B2C_LocalAccount_TodoAppFucntionsCorrectly()
{
// Web app and api environmental variable setup.
DefaultAzureCredential azureCred = new();
string clientSecret = await UiTestHelpers.GetValueFromKeyvaultWitDefaultCreds(_keyvaultUri, KeyvaultClientSecretName, azureCred);
string clientSecret = await UITH.GetValueFromKeyvaultWitDefaultCreds(_keyvaultUri, KeyvaultClientSecretName, azureCred);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest keeping UiTestHelpers naming. UITH is not a common acronym, so it's not immediately obvious what it represents - makes code less readable. Plus with IntelliSense auto-complete, the name length is not a huge issue.

var serviceEnvVars = new Dictionary<string, string>
{
{"ASPNETCORE_ENVIRONMENT", "Development" },
Expand All @@ -60,8 +63,8 @@ public async Task Susi_B2C_LocalAccount_TodoAppFucntionsCorrectly()
};

// Get email and password from keyvault.
string email = await UiTestHelpers.GetValueFromKeyvaultWitDefaultCreds(_keyvaultUri, KeyvaultEmailName, azureCred);
string password = await UiTestHelpers.GetValueFromKeyvaultWitDefaultCreds(_keyvaultUri, KeyvaultPasswordName, azureCred);
string email = await UITH.GetValueFromKeyvaultWitDefaultCreds(_keyvaultUri, KeyvaultEmailName, azureCred);
string password = await UITH.GetValueFromKeyvaultWitDefaultCreds(_keyvaultUri, KeyvaultPasswordName, azureCred);

// Playwright setup. To see browser UI, set 'Headless = false'.
const string TraceFileName = TraceClassName + "_TodoAppFunctionsCorrectly";
Expand All @@ -70,20 +73,28 @@ public async Task Susi_B2C_LocalAccount_TodoAppFucntionsCorrectly()
IBrowserContext context = await browser.NewContextAsync(new BrowserNewContextOptions { IgnoreHTTPSErrors = true });
await context.Tracing.StartAsync(new() { Screenshots = true, Snapshots = true, Sources = true });

Process? serviceProcess= null;
Process? clientProcess = null;

Dictionary<string, Process>? processes = null;

try
{
// Start the web app and api processes.
// The delay before starting client prevents transient devbox issue where the client fails to load the first time after rebuilding.
serviceProcess = UiTestHelpers.StartProcessLocally(_testAssemblyPath, _devAppPath + TC.s_todoListServicePath, TC.s_todoListServiceExe, serviceEnvVars);
var serviceProcess = new ProcessStartOptions(_testAssemblyPath, _devAppPath + TC.s_todoListServicePath, TC.s_todoListServiceExe, serviceEnvVars);
await Task.Delay(3000);
clientProcess = UiTestHelpers.StartProcessLocally(_testAssemblyPath, _devAppPath + TC.s_todoListClientPath, TC.s_todoListClientExe, clientEnvVars);
var clientProcess = new ProcessStartOptions(_testAssemblyPath, _devAppPath + TC.s_todoListClientPath, TC.s_todoListClientExe, clientEnvVars);

bool processesAreRunning = UITH.StartAndVerifyProcessesAreRunning(new List<ProcessStartOptions>() { serviceProcess, clientProcess }, out processes, NumProcessRetries);

if (!UiTestHelpers.ProcessesAreAlive(new List<Process>() { clientProcess, serviceProcess }))
if (!processesAreRunning)
{
Assert.Fail(TC.WebAppCrashedString);
_output.WriteLine($"Process not started after {NumProcessRetries} attempts.");
StringBuilder runningProcesses = new StringBuilder();
foreach (var process in processes)
{
runningProcesses.AppendLine(CultureInfo.InvariantCulture, $"Is {process.Key} running: {UITH.ProcessIsAlive(process.Value)}");
}
Assert.Fail(TC.WebAppCrashedString + " " + runningProcesses.ToString());
}

// Navigate to web app the retry logic ensures the web app has time to start up to establish a connection.
Expand All @@ -103,7 +114,7 @@ public async Task Susi_B2C_LocalAccount_TodoAppFucntionsCorrectly()
if (InitialConnectionRetryCount == 0) { throw ex; }
}
}
LabResponse labResponse = await LabUserHelper.GetB2CLocalAccountAsync().ConfigureAwait(false);
LabResponse labResponse = await LabUserHelper.GetB2CLocalAccountAsync();

// Initial sign in
_output.WriteLine("Starting web app sign-in flow.");
Expand Down Expand Up @@ -138,7 +149,7 @@ public async Task Susi_B2C_LocalAccount_TodoAppFucntionsCorrectly()
_output.WriteLine("Starting web app create new todo flow.");
await page.GetByRole(AriaRole.Link, new() { Name = "Create New" }).ClickAsync();
ILocator titleEntryBox = page.GetByLabel("Title");
await UiTestHelpers.FillEntryBox(titleEntryBox, TC.TodoTitle1);
await UITH.FillEntryBox(titleEntryBox, TC.TodoTitle1);
await Assertions.Expect(page.GetByRole(AriaRole.Cell, new() { Name = TC.TodoTitle1 })).ToBeVisibleAsync(_assertVisibleOptions);
_output.WriteLine("Web app create new todo flow successful.");

Expand All @@ -164,14 +175,11 @@ public async Task Susi_B2C_LocalAccount_TodoAppFucntionsCorrectly()
}
finally
{
// Add the following to make sure all processes and their children are stopped.
Queue<Process> processes = new Queue<Process>();
if (serviceProcess != null) { processes.Enqueue(serviceProcess); }
if (clientProcess != null) { processes.Enqueue(clientProcess); }
UiTestHelpers.KillProcessTrees(processes);
// Make sure all application processes and their children are stopped.
UITH.EndProcesses(processes);

// Stop tracing and export it into a zip archive.
string path = UiTestHelpers.GetTracePath(_testAssemblyPath, TraceFileName);
string path = UITH.GetTracePath(_testAssemblyPath, TraceFileName);
await context.Tracing.StopAsync(new() { Path = path });
_output.WriteLine($"Trace data for {TraceFileName} recorded to {path}.");

Expand Down
27 changes: 9 additions & 18 deletions tests/E2E Tests/WebAppUiTests/TestingWebAppLocally.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Xunit;
using Xunit.Abstractions;
using TC = Microsoft.Identity.Web.Test.Common.TestConstants;
using UITH = WebAppUiTests.UiTestHelpers;

namespace WebAppUiTests;

Expand Down Expand Up @@ -39,11 +40,11 @@ public TestingWebAppLocally(ITestOutputHelper output)
[SupportedOSPlatform("windows")]
public async Task ChallengeUser_MicrosoftIdFlow_LocalApp_ValidEmailPassword()
{
LabResponse labResponse = await LabUserHelper.GetDefaultUserAsync().ConfigureAwait(false);
LabResponse labResponse = await LabUserHelper.GetDefaultUserAsync();

var clientEnvVars = new Dictionary<string, string>();

await ExecuteWebAppCallsGraphFlow(labResponse.User.Upn, labResponse.User.GetOrFetchPassword(), clientEnvVars, TraceFileClassName).ConfigureAwait(false);
await ExecuteWebAppCallsGraphFlow(labResponse.User.Upn, labResponse.User.GetOrFetchPassword(), clientEnvVars, TraceFileClassName);
}

[Theory]
Expand All @@ -61,7 +62,7 @@ public async Task ChallengeUser_MicrosoftIdFlow_LocalApp_ValidEmailWithCiamPassw
{"AzureAd__Instance", "" }
};

await ExecuteWebAppCallsGraphFlow("[email protected]", LabUserHelper.FetchUserPassword("msidlabciam6"), clientEnvVars, TraceFileClassNameCiam).ConfigureAwait(false);
await ExecuteWebAppCallsGraphFlow("[email protected]", LabUserHelper.FetchUserPassword("msidlabciam6"), clientEnvVars, TraceFileClassNameCiam);
}

private async Task ExecuteWebAppCallsGraphFlow(string upn, string credential, Dictionary<string, string>? clientEnvVars, string traceFileClassName)
Expand All @@ -76,9 +77,9 @@ private async Task ExecuteWebAppCallsGraphFlow(string upn, string credential, Di

try
{
process = UiTestHelpers.StartProcessLocally(_uiTestAssemblyLocation, _devAppPath, _devAppExecutable, clientEnvVars);
process = UITH.StartProcessLocally(_uiTestAssemblyLocation, _devAppPath, _devAppExecutable, clientEnvVars);

if (!UiTestHelpers.ProcessIsAlive(process))
if (!UITH.ProcessIsAlive(process))
{ Assert.Fail(TC.WebAppCrashedString); }

IPage page = await browser.NewPageAsync();
Expand All @@ -104,7 +105,7 @@ private async Task ExecuteWebAppCallsGraphFlow(string upn, string credential, Di
// Act
Trace.WriteLine("Starting Playwright automation: web app sign-in & call Graph.");
string email = upn;
await UiTestHelpers.FirstLogin_MicrosoftIdFlow_ValidEmailPassword(page, email, credential, _output);
await UITH.FirstLogin_MicrosoftIdFlow_ValidEmailPassword(page, email, credential, _output);

// Assert
await Assertions.Expect(page.GetByText("Welcome")).ToBeVisibleAsync(_assertVisibleOptions);
Expand All @@ -120,21 +121,11 @@ private async Task ExecuteWebAppCallsGraphFlow(string upn, string credential, Di
Queue<Process> processes = new();
if (process != null)
{ processes.Enqueue(process); }

#if WINDOWS
UiTestHelpers.KillProcessTrees(processes);
#else
while (processes.Count > 0)
{
Process p = processes.Dequeue();
p.Kill();
p.WaitForExit();
}
#endif
UITH.KillProcessTrees(processes);

// Cleanup Playwright
// Stop tracing and export it into a zip archive.
string path = UiTestHelpers.GetTracePath(_uiTestAssemblyLocation, TraceFileName);
string path = UITH.GetTracePath(_uiTestAssemblyLocation, TraceFileName);
await context.Tracing.StopAsync(new() { Path = path });
_output.WriteLine($"Trace data for {TraceFileName} recorded to {path}.");
await browser.DisposeAsync();
Expand Down
Loading
Loading