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

OSOE-93: Add extension methods #162

Merged
merged 11 commits into from
May 30, 2022
2 changes: 1 addition & 1 deletion Lombiq.Tests.UI.Shortcuts/Manifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Name = "Shortcuts - Lombiq UI Testing Toolbox",
Author = "Lombiq Technologies",
Website = "https://github.com/Lombiq/UI-Testing-Toolbox",
Version = "2.1.2-alpha"
Version = "3.0.1-alpha"
)]

[assembly: Feature(
Expand Down
46 changes: 46 additions & 0 deletions Lombiq.Tests.UI/Extensions/FailureDumpUITestContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Lombiq.Tests.UI.Services;
using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace Lombiq.Tests.UI.Extensions;

public static class FailureDumpUITestContextExtensions
{
/// <summary>
/// Appends stream as file content to be collected on failure dump.
/// </summary>
/// <param name="context"><see cref="UITestContext"/> instance.</param>
/// <param name="fileName">The name of the file.</param>
/// <param name="action">Gets called in failure dump collection.</param>
public static void AppendFailureDump(
this UITestContext context,
string fileName,
Func<UITestContext, Task<Stream>> action) =>
context.FailureDumpContainer.Add(
fileName,
() => action(context));

/// <summary>
/// Appends string as file content to be collected on failure dump.
/// </summary>
/// <param name="context"><see cref="UITestContext"/> instance.</param>
/// <param name="fileName">The name of the file.</param>
/// <param name="content">
/// File content. Can be a composite format string <see cref="string.Format(string, object?[])"/>.
/// </param>
/// <param name="args">An object array that contains zero or more objects to format.</param>
public static void AppendFailureDump(
this UITestContext context,
string fileName,
string content,
params object[] args) =>
context.FailureDumpContainer.Add(
fileName,
() => Task.FromResult(
new MemoryStream(
Encoding.UTF8.GetBytes(
string.Format(CultureInfo.InvariantCulture, content, args))) as Stream));
}
10 changes: 10 additions & 0 deletions Lombiq.Tests.UI/Services/UITestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

Expand Down Expand Up @@ -76,6 +77,15 @@ public class UITestContext
Justification = "Deliberately modifiable by consumer code.")]
public Dictionary<string, object> CustomContext { get; } = new();

/// <summary>
/// Gets a dictionary storing some custom data for collecting in failure dump.
/// </summary>
[SuppressMessage(
"Design",
"MA0016:Prefer return collection abstraction instead of implementation",
Justification = "Deliberately modifiable by consumer code.")]
sarahelsaig marked this conversation as resolved.
Show resolved Hide resolved
public Dictionary<string, Func<Task<Stream>>> FailureDumpContainer { get; } = new();

/// <summary>
/// Gets or sets the current tenant name when testing multi-tenancy. When testing sites with multi-tenancy you
/// should set the value to the tenant in question so methods (e.g. <see cref="TypedRouteUITestContextExtensions"/>)
Expand Down
47 changes: 45 additions & 2 deletions Lombiq.Tests.UI/Services/UITestExecutionSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Selenium.Axe;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -58,6 +59,7 @@ public UITestExecutionSession(UITestManifest testManifest, OrchardCoreUITestExec
public async Task<bool> ExecuteAsync(int retryCount, string dumpRootPath)
{
var startTime = DateTime.UtcNow;
Dictionary<string, Func<Task<Stream>>> failureDumpContainer = null;

_testOutputHelper.WriteLineTimestampedAndDebug("Starting execution of {0}.", _testManifest.Name);

Expand Down Expand Up @@ -105,6 +107,9 @@ public async Task<bool> ExecuteAsync(int retryCount, string dumpRootPath)

_context ??= await CreateContextAsync();

_context.FailureDumpContainer.Clear();
failureDumpContainer = _context.FailureDumpContainer;

_context.SetDefaultBrowserSize();

await _testManifest.TestAsync(_context);
Expand All @@ -119,7 +124,7 @@ public async Task<bool> ExecuteAsync(int retryCount, string dumpRootPath)

if (ex is SetupFailedFastException) throw;

await CreateFailureDumpAsync(ex, dumpRootPath, retryCount);
await CreateFailureDumpAsync(ex, dumpRootPath, retryCount, failureDumpContainer);

if (retryCount == _configuration.MaxRetryCount)
{
Expand Down Expand Up @@ -168,6 +173,8 @@ private async ValueTask ShutdownAsync()
_context.Scope?.Dispose();

DirectoryHelper.SafelyDeleteDirectoryIfExists(DirectoryPaths.GetTempSubDirectoryPath(_context.Id));

_context.FailureDumpContainer.Clear();
}

_sqlServerManager?.Dispose();
Expand Down Expand Up @@ -209,7 +216,11 @@ private Exception PrepareAndLogException(Exception ex)
return ex;
}

private async Task CreateFailureDumpAsync(Exception ex, string dumpRootPath, int retryCount)
private async Task CreateFailureDumpAsync(
Exception ex,
string dumpRootPath,
int retryCount,
Dictionary<string, Func<Task<Stream>>> failureDumpContainer)
{
var dumpContainerPath = Path.Combine(dumpRootPath, $"Attempt {retryCount.ToTechnicalString()}");
var debugInformationPath = Path.Combine(dumpContainerPath, "DebugInformation");
Expand Down Expand Up @@ -255,6 +266,14 @@ await File.WriteAllLinesAsync(
if (_dumpConfiguration.CaptureAppSnapshot) await CaptureAppSnapshotAsync(dumpContainerPath);

CaptureMarkupValidationResults(ex, debugInformationPath);

if (failureDumpContainer != null)
{
foreach (var toDump in failureDumpContainer)
{
await SaveFailureDumpFromContextAsync(debugInformationPath, toDump.Key, toDump.Value);
}
}
}
catch (Exception dumpException)
{
Expand All @@ -267,6 +286,30 @@ await File.WriteAllLinesAsync(
}
}

private async Task SaveFailureDumpFromContextAsync(
string debugInformationPath,
string dumpRelativePath,
Func<Task<Stream>> dumpAction)
{
try
{
using var dumpStream = await dumpAction();
string filePath = Path.Combine(debugInformationPath, dumpRelativePath);
FileSystemHelper.EnsureDirectoryExists(Path.GetDirectoryName(filePath));

using var dumpFile = File.Open(
filePath,
FileMode.Create,
FileAccess.Write);
await dumpStream.CopyToAsync(dumpFile);
}
catch (Exception dumpException)
{
_testOutputHelper.WriteLineTimestampedAndDebug(
$"Saving dump({dumpRelativePath}) of the test from context failed with the following exception: {dumpException}");
}
}

private async Task SaveTestOutputAsync(string debugInformationPath)
{
try
Expand Down
4 changes: 4 additions & 0 deletions Lombiq.Tests.UI/Services/WebDriverFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ ChromeDriver CreateDriverInner(ChromeDriverService service)
// https://developers.google.com/web/tools/puppeteer/troubleshooting#tips for more information.
chromeConfig.Options.AddArgument("disable-dev-shm-usage");

// Disabling hardware acceleration to avoid hardware dependent issues in rendering and visual validation.
chromeConfig.Options.AddArgument("disable-accelerated-2d-canvas");
chromeConfig.Options.AddArgument("disable-gpu");

if (configuration.Headless) chromeConfig.Options.AddArgument("headless");

configuration.BrowserOptionsConfigurator?.Invoke(chromeConfig.Options);
Expand Down