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

Adding TestEngine.PlaywrightScript() #336

Closed
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
1 change: 1 addition & 0 deletions docs/PowerFX/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ There are several specifically defined functions for the test framework.
- [Pause](./Pause.md)
- [Select](./Select.md)
- [SetProperty](./SetProperty.md)
- [TestEngine.PlaywrightScript](./TestEngine.PlaywrightScript.md)
- [Wait](./Wait.md)

## Naming
Expand Down
44 changes: 44 additions & 0 deletions docs/PowerFX/TestEngine.PlaywrightScript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# TestEngine.PlaywrightScript

`TestEngine.PlaywrightScript(csxFileName)`

The PlaywrightScript function provides a "no cliffs" extensibility for Test Engine providing the ability to execute CSharp Scripts (*.csx) files inside a Test Engine web provider based test that uses Playwright as web page test framework.

You can use the playwright inspector to record C# commands to build the C# script

## C# Script

This action takes advantage of [dotnet-script](https://github.com/dotnet-script/dotnet-script) and the underlying [Rosyln](https://github.com/dotnet/roslyn) compiler to allow projectless scripting of Playwright code. The Action assumes the following:

1. Any required .Net Assemblies are globally available or in the current folder and can be loaded using #r compiler directive
2. A public class named **PlaywrightScript** MUST exist
3. A method with **public static void Run(IBrowserContext context, ILogger logger)** MUST exist

## Sample Test

A sample [testPlan.fx.yaml](../../samples/playwrightscript/testPlan.fx.yaml) and [sample.csx](../../samples/playwrightscript/sample.csx) provide a demonstration of how this action can be integrated into a Test Engine test.

## Example

` TestEngine.PlaywrightScript("sample.csx")

Where sample could use template to include Playwright

```csharp
#r "Microsoft.Playwright.dll"
#r "Microsoft.Extensions.Logging.dll"
using Microsoft.Playwright;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading.Tasks;

public class PlaywrightScript {
public static void Run(IBrowserContext context, ILogger logger) {
Execute(context, logger).Wait();
}

public static async Task Execute(IBrowserContext context, ILogger logger) {
// Insert your code here
}
}
```
3 changes: 3 additions & 0 deletions samples/playwrightscript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Overview

This sample assumes that you have the buttonclicker sample application in the target environment
28 changes: 28 additions & 0 deletions samples/playwrightscript/sample.csx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#r "Microsoft.Playwright.dll"
#r "Microsoft.Extensions.Logging.dll"
using Microsoft.Playwright;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading.Tasks;

public class PlaywrightScript {
public static void Run(IBrowserContext context, ILogger logger) {
Execute(context, logger).Wait();
}

public static async Task Execute(IBrowserContext context, ILogger logger) {
var page = context.Pages.First();

if ( page.Url == "about:blank" ) {
var nextPage = context.Pages.Skip(1).First();
await page.CloseAsync();
page = nextPage;
}

foreach ( var frame in page.Frames ) {
if ( await frame.Locator("button:has-text('Button')").CountAsync() > 0 ) {
await frame.ClickAsync("button:has-text('Button')");
}
}
}
}
29 changes: 29 additions & 0 deletions samples/playwrightscript/testPlan.fx.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
testSuite:
testSuiteName: testPlan Template
testSuiteDescription: Playwright csx example
persona: User1
appLogicalName: new_buttonclicker_0a877
onTestSuiteComplete: Screenshot("playwrightaction_onTestSuiteComplete.png");

testCases:
- testCaseName: Run Script
testCaseDescription: CSX example
testSteps: |
= TestEngine.Pause();
TestEngine.PlaywrightScript("sample.csx");
TestEngine.Pause();

testSettings:
headless: false
locale: "en-US"
recordVideo: true
extensionModules:
enable: true
browserConfigurations:
- browser: Chromium

environmentVariables:
users:
- personaName: User1
emailKey: user1Email
passwordKey: user1Password
14 changes: 14 additions & 0 deletions src/PowerAppsTestEngine.sln
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "testengine.module.pause", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "testengine.module.pause.tests", "testengine.module.pause.tests\testengine.module.pause.tests.csproj", "{3D9F90F2-0937-486D-AA0B-BFE425354F4A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "testengine.module.playwrightscript", "testengine.module.playwrightscript\testengine.module.playwrightscript.csproj", "{FE05DDD2-73C6-44B4-9A1B-7DACD984A428}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "testengine.module.playwrightscript.tests", "testengine.module.playwrightscript.tests\testengine.module.playwrightscript.tests.csproj", "{6E2A1E7D-CDED-49D4-B6C3-BEDD828ED7DB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -100,6 +104,14 @@ Global
{3D9F90F2-0937-486D-AA0B-BFE425354F4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D9F90F2-0937-486D-AA0B-BFE425354F4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D9F90F2-0937-486D-AA0B-BFE425354F4A}.Release|Any CPU.Build.0 = Release|Any CPU
{FE05DDD2-73C6-44B4-9A1B-7DACD984A428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FE05DDD2-73C6-44B4-9A1B-7DACD984A428}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE05DDD2-73C6-44B4-9A1B-7DACD984A428}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE05DDD2-73C6-44B4-9A1B-7DACD984A428}.Release|Any CPU.Build.0 = Release|Any CPU
{6E2A1E7D-CDED-49D4-B6C3-BEDD828ED7DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E2A1E7D-CDED-49D4-B6C3-BEDD828ED7DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E2A1E7D-CDED-49D4-B6C3-BEDD828ED7DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E2A1E7D-CDED-49D4-B6C3-BEDD828ED7DB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -117,6 +129,8 @@ Global
{8AEAF6BD-38E3-4649-9221-6A67AD1E96EC} = {0B61ADD8-5EED-4A2C-99AA-B597EC3EE223}
{B3A02421-223D-4E80-A8CE-977B425A6EB2} = {ACAB614B-304F-48C0-B8B1-8D95F3A9FBC4}
{3D9F90F2-0937-486D-AA0B-BFE425354F4A} = {ACAB614B-304F-48C0-B8B1-8D95F3A9FBC4}
{FE05DDD2-73C6-44B4-9A1B-7DACD984A428} = {ACAB614B-304F-48C0-B8B1-8D95F3A9FBC4}
{6E2A1E7D-CDED-49D4-B6C3-BEDD828ED7DB} = {ACAB614B-304F-48C0-B8B1-8D95F3A9FBC4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7E7B2C01-DDE2-4C5A-96C3-AF474B074331}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using Microsoft.PowerApps.TestEngine.TestInfra;
using Microsoft.PowerFx;
using Microsoft.Playwright;
using Moq;
using Microsoft.PowerApps.TestEngine.Config;
using Microsoft.PowerApps.TestEngine.Providers;
using Microsoft.PowerApps.TestEngine.System;
using Microsoft.Extensions.Logging;
using Microsoft.PowerFx.Types;

namespace testengine.module.browserlocale.tests
{
public class PlaywrightScriptsFunctionTests
{
private Mock<ITestInfraFunctions> MockTestInfraFunctions;
private Mock<ITestState> MockTestState;
private Mock<ITestWebProvider> MockTestWebProvider;
private Mock<ISingleTestInstanceState> MockSingleTestInstanceState;
private Mock<IFileSystem> MockFileSystem;
private Mock<IPage> MockPage;
private PowerFxConfig TestConfig;
private NetworkRequestMock TestNetworkRequestMock;
private Mock<ILogger> MockLogger;

public PlaywrightScriptsFunctionTests()
{
MockTestInfraFunctions = new Mock<ITestInfraFunctions>(MockBehavior.Strict);
MockTestState = new Mock<ITestState>(MockBehavior.Strict);
MockTestWebProvider = new Mock<ITestWebProvider>();
MockSingleTestInstanceState = new Mock<ISingleTestInstanceState>(MockBehavior.Strict);
MockFileSystem = new Mock<IFileSystem>(MockBehavior.Strict);
MockPage = new Mock<IPage>(MockBehavior.Strict);
TestConfig = new PowerFxConfig();
TestNetworkRequestMock = new NetworkRequestMock();
MockLogger = new Mock<ILogger>(MockBehavior.Strict);
}

[Theory]
[InlineData(@"c:\test.csx", @"#r ""Microsoft.Playwright.dll""
#r ""Microsoft.Extensions.Logging.dll""
using Microsoft.Playwright;
using Microsoft.Extensions.Logging;

public class PlaywrightScript
{
public static void Run(IBrowserContext context, ILogger logger)
{
}
}")]
public void PlaywrightExecute(string file, string code)
{
// Arrange

var function = new PlaywrightScriptFunction(MockTestInfraFunctions.Object, MockTestState.Object, MockFileSystem.Object, MockLogger.Object);

MockLogger.Setup(x => x.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
(Func<It.IsAnyType, Exception, string>)It.IsAny<object>()));

MockFileSystem.Setup(x => x.IsValidFilePath(file)).Returns(true);
MockFileSystem.Setup(x => x.ReadAllText(file)).Returns(code);

MockTestInfraFunctions.Setup(x => x.GetContext()).Returns(new Mock<IBrowserContext>().Object);

// Act
function.Execute(StringValue.New(file));

// Assert
MockLogVerify(LogLevel.Information, "------------------------------\n\n" +
"Executing PlaywrightScript function.");

MockLogVerify(LogLevel.Debug, "Loading file");

MockLogVerify(LogLevel.Information, "Successfully finished executing PlaywrightScript function.");
}

private void MockLogVerify(LogLevel logLevel, string message)
{
MockLogger.Verify(l => l.Log(It.Is<LogLevel>(l => l == logLevel),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString() == message),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()), Times.AtLeastOnce);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using Microsoft.PowerApps.TestEngine.TestInfra;
using Microsoft.PowerFx;
using Microsoft.Playwright;
using Moq;
using Microsoft.PowerApps.TestEngine.Config;
using Microsoft.PowerApps.TestEngine.Providers;
using Microsoft.PowerApps.TestEngine.System;
using Microsoft.Extensions.Logging;

namespace testengine.module.browserlocale.tests
{
public class PlaywrightScriptsModuleTests
{
private Mock<ITestInfraFunctions> MockTestInfraFunctions;
private Mock<ITestState> MockTestState;
private Mock<ITestWebProvider> MockTestWebProvider;
private Mock<ISingleTestInstanceState> MockSingleTestInstanceState;
private Mock<IFileSystem> MockFileSystem;
private Mock<IPage> MockPage;
private PowerFxConfig TestConfig;
private NetworkRequestMock TestNetworkRequestMock;
private Mock<ILogger> MockLogger;

public PlaywrightScriptsModuleTests()
{
MockTestInfraFunctions = new Mock<ITestInfraFunctions>(MockBehavior.Strict);
MockTestState = new Mock<ITestState>(MockBehavior.Strict);
MockTestWebProvider = new Mock<ITestWebProvider>();
MockSingleTestInstanceState = new Mock<ISingleTestInstanceState>(MockBehavior.Strict);
MockFileSystem = new Mock<IFileSystem>(MockBehavior.Strict);
MockPage = new Mock<IPage>(MockBehavior.Strict);
TestConfig = new PowerFxConfig();
TestNetworkRequestMock = new NetworkRequestMock();
MockLogger = new Mock<ILogger>(MockBehavior.Strict);
}

[Fact]
public void ExtendBrowserContextOptionsLocaleUpdate()
{
// Arrange
var module = new PlaywrightScriptModule();
var options = new BrowserNewContextOptions();
var settings = new TestSettings() { };

// Act
module.ExtendBrowserContextOptions(options, settings);
}

[Fact]
public void RegisterPowerFxFunction()
{
// Arrange
var module = new PlaywrightScriptModule();

MockSingleTestInstanceState.Setup(x => x.GetLogger()).Returns(MockLogger.Object);

MockLogger.Setup(x => x.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
(Func<It.IsAnyType, Exception, string>)It.IsAny<object>()));


// Act
module.RegisterPowerFxFunction(TestConfig, MockTestInfraFunctions.Object, MockTestWebProvider.Object, MockSingleTestInstanceState.Object, MockTestState.Object, MockFileSystem.Object);

// Assert
MockLogger.Verify(l => l.Log(It.Is<LogLevel>(l => l == LogLevel.Information),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString() == "Registered PlaywrightScript()"),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()), Times.AtLeastOnce);
}

[Fact]
public async Task RegisterNetworkRoute()
{
// Arrange
var module = new PlaywrightScriptModule();


// Act
await module.RegisterNetworkRoute(MockTestState.Object, MockSingleTestInstanceState.Object, MockFileSystem.Object, MockPage.Object, TestNetworkRequestMock);

// Assert
}
}
}
1 change: 1 addition & 0 deletions src/testengine.module.playwrightscript.tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.PowerApps.TestEngine\Microsoft.PowerApps.TestEngine.csproj" />
<ProjectReference Include="..\testengine.module.playwrightscript\testengine.module.playwrightscript.csproj" />
</ItemGroup>
</Project>
Loading
Loading