Skip to content

Commit

Permalink
Playwright
Browse files Browse the repository at this point in the history
  • Loading branch information
mattleibow committed Sep 11, 2024
1 parent 913db9b commit 92ab789
Show file tree
Hide file tree
Showing 45 changed files with 1,059 additions and 7 deletions.
16 changes: 15 additions & 1 deletion DeviceRunners.sln
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeviceRunners.UIAutomation.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeviceRunners.UIAutomation.Selenium.Tests", "test\DeviceRunners.UIAutomation.Selenium.Tests\DeviceRunners.UIAutomation.Selenium.Tests.csproj", "{21884F7C-DA99-4359-9D3B-93399046FF9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceTestingKitApp.WebApp", "sample\src\DeviceTestingKitApp.WebApp\DeviceTestingKitApp.WebApp.csproj", "{2A387F0F-3DBE-4A6C-BEB5-60CA6CE6F4B4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeviceTestingKitApp.WebApp", "sample\src\DeviceTestingKitApp.WebApp\DeviceTestingKitApp.WebApp.csproj", "{2A387F0F-3DBE-4A6C-BEB5-60CA6CE6F4B4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeviceTestingKitApp.UITests.XunitTests.Web", "sample\test\DeviceTestingKitApp.UITests.XunitTests.Web\DeviceTestingKitApp.UITests.XunitTests.Web.csproj", "{5F75C9B7-3838-4045-B7BD-963B338B14FD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeviceRunners.UIAutomation.Playwright", "src\DeviceRunners.UIAutomation.Playwright\DeviceRunners.UIAutomation.Playwright.csproj", "{A1FF7FA0-3842-4131-90B9-98D0D6B237C2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeviceRunners.UIAutomation.Playwright.Tests", "test\DeviceRunners.UIAutomation.Playwright.Tests\DeviceRunners.UIAutomation.Playwright.Tests.csproj", "{9F6C3958-DFDA-40C7-917B-2BC4984EEBCA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -209,6 +213,14 @@ Global
{5F75C9B7-3838-4045-B7BD-963B338B14FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5F75C9B7-3838-4045-B7BD-963B338B14FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5F75C9B7-3838-4045-B7BD-963B338B14FD}.Release|Any CPU.Build.0 = Release|Any CPU
{A1FF7FA0-3842-4131-90B9-98D0D6B237C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1FF7FA0-3842-4131-90B9-98D0D6B237C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1FF7FA0-3842-4131-90B9-98D0D6B237C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1FF7FA0-3842-4131-90B9-98D0D6B237C2}.Release|Any CPU.Build.0 = Release|Any CPU
{9F6C3958-DFDA-40C7-917B-2BC4984EEBCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9F6C3958-DFDA-40C7-917B-2BC4984EEBCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9F6C3958-DFDA-40C7-917B-2BC4984EEBCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9F6C3958-DFDA-40C7-917B-2BC4984EEBCA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -247,6 +259,8 @@ Global
{21884F7C-DA99-4359-9D3B-93399046FF9B} = {5073EAFD-FF7B-4C4E-84D2-3AA38A91DE73}
{2A387F0F-3DBE-4A6C-BEB5-60CA6CE6F4B4} = {7FD9E644-B760-47D7-A4FC-61199AFE9D62}
{5F75C9B7-3838-4045-B7BD-963B338B14FD} = {EE37724C-5FEF-46BB-AD80-CD002CC7FF54}
{A1FF7FA0-3842-4131-90B9-98D0D6B237C2} = {DBF1C119-C96C-4DD3-810B-52451603475C}
{9F6C3958-DFDA-40C7-917B-2BC4984EEBCA} = {5073EAFD-FF7B-4C4E-84D2-3AA38A91DE73}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2DABD41C-B083-429E-AE1E-971CFF8DC199}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

<ItemGroup>
<ProjectReference Include="..\..\..\src\DeviceRunners.UIAutomation.Appium\DeviceRunners.UIAutomation.Appium.csproj" />
<ProjectReference Include="..\..\..\src\DeviceRunners.UIAutomation.Playwright\DeviceRunners.UIAutomation.Playwright.csproj" />
<ProjectReference Include="..\..\..\src\DeviceRunners.UIAutomation.Selenium\DeviceRunners.UIAutomation.Selenium.csproj" />
<ProjectReference Include="..\..\..\src\DeviceRunners.UIAutomation\DeviceRunners.UIAutomation.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using DeviceRunners.UIAutomation.Appium;
using DeviceRunners.UIAutomation.Playwright;

using NUnit.Framework.Internal;
using Microsoft.Playwright;

namespace DeviceTestingKitApp.UITests.NUnitTests;

public class PlaywrightServerTests
{
public PlaywrightServerTests()
{
}

[Test]
public async Task IsReady()
{
using var playwright = await Playwright.CreateAsync();

await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Channel = "msedge" });

var page = await browser.NewPageAsync();

await page.GotoAsync("https://playwright.dev/dotnet");

await page.ScreenshotAsync(new()
{
Path = "screenshot.png"
});
}
}
62 changes: 62 additions & 0 deletions src/DeviceRunners.UIAutomation.Playwright/AppiumAutomatedApp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace DeviceRunners.UIAutomation.Playwright;

/// <summary>
/// This type represents an automated app that is driven by Playwright.
/// </summary>
public class PlaywrightAutomatedApp : IAutomatedApp
{
//private readonly PlaywrightAutomatedAppOptions _options;
//private readonly IPlaywrightDiagnosticLogger? _logger;

//public PlaywrightAutomatedApp(PlaywrightAutomationFramework playwright, PlaywrightAutomatedAppOptions options, IPlaywrightDiagnosticLogger? logger = null)
//{
// Framework = playwright;
// _options = options;
// _logger = logger;
// DriverManager = new PlaywrightDriverManager(Framework.ServiceManager, _options);
// Commands = new AutomatedAppCommandManager(this, options.Commands);
//}

//public PlaywrightAutomationFramework Framework { get; }

//public PlaywrightServiceManager ServiceManager => Framework.ServiceManager;

//public PlaywrightDriverManager DriverManager { get; }

//public PlaywrightDriver Driver => DriverManager.Driver;

public IAutomatedAppCommandManager Commands { get; }

Check warning on line 28 in src/DeviceRunners.UIAutomation.Playwright/AppiumAutomatedApp.cs

View workflow job for this annotation

GitHub Actions / Windows (Release)

Non-nullable property 'Commands' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

IReadOnlyList<IAutomatedAppElement> IContainsElements.FindElements(Action<IBy> by) => null;

Check warning on line 30 in src/DeviceRunners.UIAutomation.Playwright/AppiumAutomatedApp.cs

View workflow job for this annotation

GitHub Actions / Windows (Release)

Possible null reference return.
IAutomatedAppElement IContainsElements.FindElement(Action<IBy> by) => null;

Check warning on line 31 in src/DeviceRunners.UIAutomation.Playwright/AppiumAutomatedApp.cs

View workflow job for this annotation

GitHub Actions / Windows (Release)

Possible null reference return.

//public PlaywrightAutomatedAppElement FindElement(Action<IBy> by)
//{
// ArgumentNullException.ThrowIfNull(by);

// var playwrightBy = _options.ByFactory.Create(this);
// by(playwrightBy);

// var element = Driver.FindElement(playwrightBy.ToBy());

// return new PlaywrightAutomatedAppElement(this, element);
//}

//IAutomatedAppElement IContainsElements.FindElement(Action<IBy> by) =>
// FindElement(by);

//public IReadOnlyList<PlaywrightAutomatedAppElement> FindElements(Action<IBy> by)
//{
// ArgumentNullException.ThrowIfNull(by);

// var playwrightBy = _options.ByFactory.Create(this);
// by(playwrightBy);

// var elements = Driver.FindElements(playwrightBy.ToBy());

// return elements.Select(e => new PlaywrightAutomatedAppElement(this, e)).ToList();
//}

//IReadOnlyList<IAutomatedAppElement> IContainsElements.FindElements(Action<IBy> by) =>
// FindElements(by);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//using OpenQA.Selenium.Playwright;

//namespace DeviceRunners.UIAutomation.Playwright;

//public class PlaywrightAutomatedAppElement : IAutomatedAppElement
//{
// public PlaywrightAutomatedAppElement(PlaywrightAutomatedApp app, PlaywrightElement element)
// {
// App = app;
// PlaywrightElement = element;
// }

// public PlaywrightAutomatedApp App { get; }

// public PlaywrightElement PlaywrightElement { get; }

// IAutomatedApp IAutomatedAppElement.App => App;
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
////namespace DeviceRunners.UIAutomation.Playwright;

/////// <summary>
/////// This type is responsible for creating and managing Playwright automated app instances.
/////// </summary>
////public class PlaywrightAutomationFramework : IAutomationFramework
////{
//// private readonly PlaywrightServiceManagerOptions _options;
//// private readonly IReadOnlyList<PlaywrightAutomatedAppOptions> _apps;
//// private readonly IPlaywrightDiagnosticLogger? _logger;
//// private PlaywrightServiceManager? _serviceManager;
//// private bool _disposed;

//// public PlaywrightAutomationFramework(PlaywrightServiceManagerOptions options, IEnumerable<PlaywrightAutomatedAppOptions> apps, IPlaywrightDiagnosticLogger? logger = null)
//// {
//// _options = options;
//// _apps = apps.ToList();
//// _logger = logger;
//// }

//// public PlaywrightServiceManager ServiceManager
//// {
//// get
//// {
//// ObjectDisposedException.ThrowIf(_disposed, typeof(PlaywrightAutomationFramework));
//// return _serviceManager ??= new PlaywrightServiceManager(_options, _logger);
//// }
//// }

//// public IReadOnlyList<PlaywrightAutomatedAppOptions> AvailableApps => _apps;

//// public IAutomatedApp CreateApp(PlaywrightAutomatedAppOptions options)
//// {
//// return new PlaywrightAutomatedApp(this, options, _logger);
//// }

//// public void StartApp(PlaywrightAutomatedApp app)
//// {
//// app.DriverManager.StartDriver();
//// }

//// public void StopApp(PlaywrightAutomatedApp app)
//// {
//// app.DriverManager.ShutdownDriver();
//// }

//// public void RestartApp(PlaywrightAutomatedApp app)
//// {
//// app.DriverManager.RestartDriver();
//// }

//// IReadOnlyList<IAutomatedAppOptions> IAutomationFramework.AvailableApps => _apps;

//// IAutomatedApp IAutomationFramework.CreateApp(IAutomatedAppOptions options)
//// {
//// if (options is not PlaywrightAutomatedAppOptions playwrightOptions)
//// throw new ArgumentException($"Expected {nameof(PlaywrightAutomatedAppOptions)} but got {options.GetType().Name}", nameof(options));

//// return CreateApp(playwrightOptions);
//// }

//// void IAutomationFramework.StartApp(IAutomatedApp app) => StartApp(AsPlaywrightApp(app));

//// void IAutomationFramework.StopApp(IAutomatedApp app) => StopApp(AsPlaywrightApp(app));

//// void IAutomationFramework.RestartApp(IAutomatedApp app) => RestartApp(AsPlaywrightApp(app));

//// public void Dispose()
//// {
//// if (_disposed)
//// return;

//// _disposed = true;

//// _serviceManager?.Dispose();
//// }

//// private static PlaywrightAutomatedApp AsPlaywrightApp(IAutomatedApp app)
//// {
//// if (app is not PlaywrightAutomatedApp playwrightApp)
//// throw new ArgumentException($"Expected {nameof(PlaywrightAutomatedApp)} but got {app.GetType().Name}", nameof(app));
//// return playwrightApp;
//// }
////}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//namespace DeviceRunners.UIAutomation.Playwright;

///// <summary>
///// This type is responsible for building the options for the Playwright automation framework.
///// </summary>
//public class PlaywrightAutomationOptionsBuilder
//{
// private readonly List<IPlaywrightDiagnosticLogger> _loggers = [];
// private readonly Dictionary<string, PlaywrightAutomatedAppOptions> _apps = [];

// public PlaywrightAutomationOptionsBuilder UseServiceAddress(
// string hostAddress = PlaywrightServiceManagerOptions.DefaultHostAddress,
// int port = PlaywrightServiceManagerOptions.DefaultHostPort)
// {
// Options.HostAddress = hostAddress;
// Options.HostPort = port;

// return this;
// }

// public PlaywrightAutomationOptionsBuilder AddLogger(IPlaywrightDiagnosticLogger logger)
// {
// _loggers.Add(logger);

// return this;
// }

// public PlaywrightAutomationOptionsBuilder AddApp(string key, PlaywrightAutomatedAppOptions options)
// {
// if (_apps.TryGetValue(key, out var existing))
// throw new InvalidOperationException($"App with key '{key}' was already added: {existing}");

// _apps[key] = options;

// return this;
// }

// internal PlaywrightServiceManagerOptions Options { get; } = new();

// internal IReadOnlyCollection<PlaywrightAutomatedAppOptions> Apps => _apps.Values;

// internal IReadOnlyCollection<IPlaywrightDiagnosticLogger> Loggers => _loggers;
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//namespace DeviceRunners.UIAutomation.Playwright;

//public static partial class PlaywrightAutomationOptionsBuilderExtensions
//{
// public static PlaywrightAutomationOptionsBuilder AddAndroidApp(this PlaywrightAutomationOptionsBuilder builder, string key, Action<AndroidPlaywrightAutomatedAppOptionsBuilder> optionsAction)
// {
// var optionsBuilder = new AndroidPlaywrightAutomatedAppOptionsBuilder(key);

// optionsBuilder.AddDefaultPlaywrightCommands();
// optionsBuilder.AddDefaultAndroidPlaywrightCommands();

// optionsAction(optionsBuilder);

// builder.AddApp(key, optionsBuilder.Build());

// return builder;
// }
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Microsoft.Playwright;

namespace DeviceRunners.UIAutomation.Playwright;

public class EdgePlaywrightAutomatedAppOptions : PlaywrightAutomatedAppOptions
{
public EdgePlaywrightAutomatedAppOptions(string key, IPlaywrightBrowserLaunchOptions launchOptions, IReadOnlyList<IAutomatedAppCommand> commands)
: base(key, launchOptions, new PlaywrightDriverFactory(BrowserType.Chromium), new PlaywrightByFactory(), commands)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.Playwright;

namespace DeviceRunners.UIAutomation.Playwright;

public class EdgePlaywrightAutomatedAppOptionsBuilder : PlaywrightAutomatedAppOptionsBuilder
{
public EdgePlaywrightAutomatedAppOptionsBuilder(string key)
: base(key, BrowserType.Chromium)
{
LaunchOptions.GetOrAddBrowserTypeLaunchOptions().Channel = "msedge";
}

public override PlaywrightAutomatedAppOptions Build() =>
new EdgePlaywrightAutomatedAppOptions(Key, LaunchOptions, Commands);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Microsoft.Playwright;

namespace DeviceRunners.UIAutomation.Playwright;

public class PlaywrightDriverFactory : IPlaywrightDriverFactory
{
public PlaywrightDriverFactory(string browserType)
{
BrowserType = browserType;
}

public string BrowserType { get; }

public IBrowser CreateDriver(PlaywrightServiceManager playwright, PlaywrightAutomatedAppOptions options)
{
var type = playwright.Service[BrowserType];
var browserTask = type.LaunchAsync(options.LaunchOptions.GetBrowserTypeLaunchOptions());
var browser = browserTask.GetAwaiter().GetResult();
return browser;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//namespace DeviceRunners.UIAutomation.Playwright;

//public static class AutomationTestSuiteBuilderExtensions
//{
// public static AutomationTestSuiteBuilder AddPlaywright(this AutomationTestSuiteBuilder builder, Action<PlaywrightAutomationOptionsBuilder> optionsAction)
// {
// var optionsBuilder = new PlaywrightAutomationOptionsBuilder();

// optionsAction(optionsBuilder);

// var playwright = new PlaywrightAutomationFramework(
// optionsBuilder.Options,
// optionsBuilder.Apps,
// new CompositeLogger(optionsBuilder.Loggers));

// builder.AddAutomationFramework(playwright);

// return builder;
// }

// class CompositeLogger : IPlaywrightDiagnosticLogger
// {
// private readonly List<IPlaywrightDiagnosticLogger> _loggers;

// public CompositeLogger(IEnumerable<IPlaywrightDiagnosticLogger> loggers) =>
// _loggers = loggers.ToList();

// public void Log(string message)
// {
// foreach (var logger in _loggers)
// logger.Log(message);
// }
// }
//}
Loading

0 comments on commit 92ab789

Please sign in to comment.