From f1bbdf270c8b52820fcfc3fe5fde088ff93be1c1 Mon Sep 17 00:00:00 2001 From: Grant Archibald <31553604+Grant-Archibald-MS@users.noreply.github.com> Date: Thu, 6 Jun 2024 17:20:47 -0700 Subject: [PATCH 1/3] Adding TestEngine.PlaywrightScript() #335 --- samples/playwrightscript/sample.csx | 16 +++ samples/playwrightscript/testPlan.fx.yaml | 29 ++++ src/PowerAppsTestEngine.sln | 14 ++ .../PlaywrightScriptsFunctionTests.cs | 89 ++++++++++++ .../PlaywrightScriptsModuleTests.cs | 89 ++++++++++++ .../Usings.cs | 1 + ...ngine.module.playwrightscript.tests.csproj | 31 +++++ .../PlaywrightScriptFunction.cs | 130 ++++++++++++++++++ .../PlaywrightScriptModule.cs | 36 +++++ .../testengine.module.playwrightscript.csproj | 28 ++++ 10 files changed, 463 insertions(+) create mode 100644 samples/playwrightscript/sample.csx create mode 100644 samples/playwrightscript/testPlan.fx.yaml create mode 100644 src/testengine.module.playwrightscript.tests/PlaywrightScriptsFunctionTests.cs create mode 100644 src/testengine.module.playwrightscript.tests/PlaywrightScriptsModuleTests.cs create mode 100644 src/testengine.module.playwrightscript.tests/Usings.cs create mode 100644 src/testengine.module.playwrightscript.tests/testengine.module.playwrightscript.tests.csproj create mode 100644 src/testengine.module.playwrightscript/PlaywrightScriptFunction.cs create mode 100644 src/testengine.module.playwrightscript/PlaywrightScriptModule.cs create mode 100644 src/testengine.module.playwrightscript/testengine.module.playwrightscript.csproj diff --git a/samples/playwrightscript/sample.csx b/samples/playwrightscript/sample.csx new file mode 100644 index 00000000..0b75ab9c --- /dev/null +++ b/samples/playwrightscript/sample.csx @@ -0,0 +1,16 @@ +#r "Microsoft.Playwright.dll" +#r "Microsoft.Extensions.Logging.dll" +using Microsoft.Playwright; +using Microsoft.Extensions.Logging; +using System.Linq; + +public class PlaywrightScript { + public static void Run(IBrowserContext context, ILogger logger) { + var page = context.Pages.First(); + foreach ( var frame in page.Frames ) { + if ( frame.Locator("button:has-text('Button')").CountAsync().Result > 0 ) { + frame.ClickAsync("button:has-text('Button')").Wait(); + } + } + } +} \ No newline at end of file diff --git a/samples/playwrightscript/testPlan.fx.yaml b/samples/playwrightscript/testPlan.fx.yaml new file mode 100644 index 00000000..13590997 --- /dev/null +++ b/samples/playwrightscript/testPlan.fx.yaml @@ -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 diff --git a/src/PowerAppsTestEngine.sln b/src/PowerAppsTestEngine.sln index 16efe04c..ba0007b4 100644 --- a/src/PowerAppsTestEngine.sln +++ b/src/PowerAppsTestEngine.sln @@ -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 @@ -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 @@ -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} diff --git a/src/testengine.module.playwrightscript.tests/PlaywrightScriptsFunctionTests.cs b/src/testengine.module.playwrightscript.tests/PlaywrightScriptsFunctionTests.cs new file mode 100644 index 00000000..8b95a842 --- /dev/null +++ b/src/testengine.module.playwrightscript.tests/PlaywrightScriptsFunctionTests.cs @@ -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 MockTestInfraFunctions; + private Mock MockTestState; + private Mock MockTestWebProvider; + private Mock MockSingleTestInstanceState; + private Mock MockFileSystem; + private Mock MockPage; + private PowerFxConfig TestConfig; + private NetworkRequestMock TestNetworkRequestMock; + private Mock MockLogger; + + public PlaywrightScriptsFunctionTests() + { + MockTestInfraFunctions = new Mock(MockBehavior.Strict); + MockTestState = new Mock(MockBehavior.Strict); + MockTestWebProvider = new Mock(); + MockSingleTestInstanceState = new Mock(MockBehavior.Strict); + MockFileSystem = new Mock(MockBehavior.Strict); + MockPage = new Mock(MockBehavior.Strict); + TestConfig = new PowerFxConfig(); + TestNetworkRequestMock = new NetworkRequestMock(); + MockLogger = new Mock(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(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + (Func)It.IsAny())); + + MockFileSystem.Setup(x => x.IsValidFilePath(file)).Returns(true); + MockFileSystem.Setup(x => x.ReadAllText(file)).Returns(code); + + MockTestInfraFunctions.Setup(x => x.GetContext()).Returns(new Mock().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(l => l == logLevel), + It.IsAny(), + It.Is((v, t) => v.ToString() == message), + It.IsAny(), + It.IsAny>()), Times.AtLeastOnce); + } + } +} \ No newline at end of file diff --git a/src/testengine.module.playwrightscript.tests/PlaywrightScriptsModuleTests.cs b/src/testengine.module.playwrightscript.tests/PlaywrightScriptsModuleTests.cs new file mode 100644 index 00000000..296de5fa --- /dev/null +++ b/src/testengine.module.playwrightscript.tests/PlaywrightScriptsModuleTests.cs @@ -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 MockTestInfraFunctions; + private Mock MockTestState; + private Mock MockTestWebProvider; + private Mock MockSingleTestInstanceState; + private Mock MockFileSystem; + private Mock MockPage; + private PowerFxConfig TestConfig; + private NetworkRequestMock TestNetworkRequestMock; + private Mock MockLogger; + + public PlaywrightScriptsModuleTests() + { + MockTestInfraFunctions = new Mock(MockBehavior.Strict); + MockTestState = new Mock(MockBehavior.Strict); + MockTestWebProvider = new Mock(); + MockSingleTestInstanceState = new Mock(MockBehavior.Strict); + MockFileSystem = new Mock(MockBehavior.Strict); + MockPage = new Mock(MockBehavior.Strict); + TestConfig = new PowerFxConfig(); + TestNetworkRequestMock = new NetworkRequestMock(); + MockLogger = new Mock(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(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + (Func)It.IsAny())); + + + // Act + module.RegisterPowerFxFunction(TestConfig, MockTestInfraFunctions.Object, MockTestWebProvider.Object, MockSingleTestInstanceState.Object, MockTestState.Object, MockFileSystem.Object); + + // Assert + MockLogger.Verify(l => l.Log(It.Is(l => l == LogLevel.Information), + It.IsAny(), + It.Is((v, t) => v.ToString() == "Registered PlaywrightScript()"), + It.IsAny(), + It.IsAny>()), 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 + } + } +} \ No newline at end of file diff --git a/src/testengine.module.playwrightscript.tests/Usings.cs b/src/testengine.module.playwrightscript.tests/Usings.cs new file mode 100644 index 00000000..8c927eb7 --- /dev/null +++ b/src/testengine.module.playwrightscript.tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/src/testengine.module.playwrightscript.tests/testengine.module.playwrightscript.tests.csproj b/src/testengine.module.playwrightscript.tests/testengine.module.playwrightscript.tests.csproj new file mode 100644 index 00000000..02f11e54 --- /dev/null +++ b/src/testengine.module.playwrightscript.tests/testengine.module.playwrightscript.tests.csproj @@ -0,0 +1,31 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/testengine.module.playwrightscript/PlaywrightScriptFunction.cs b/src/testengine.module.playwrightscript/PlaywrightScriptFunction.cs new file mode 100644 index 00000000..f5b1d01d --- /dev/null +++ b/src/testengine.module.playwrightscript/PlaywrightScriptFunction.cs @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using Microsoft.PowerFx.Types; +using Microsoft.PowerFx; +using Microsoft.PowerApps.TestEngine.TestInfra; +using Microsoft.Extensions.Logging; +using Microsoft.PowerApps.TestEngine.Config; +using Microsoft.PowerApps.TestEngine.System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using System.Reflection; +using Microsoft.PowerFx.Core.Utils; + +namespace testengine.module +{ + /// + /// This will execute CSharp Script (CSX) file passing IBrowserContext and ILogger + /// + public class PlaywrightScriptFunction : ReflectionFunction + { + private readonly ITestInfraFunctions _testInfraFunctions; + private readonly ITestState _testState; + private readonly ILogger _logger; + private readonly IFileSystem _filesystem; + + public PlaywrightScriptFunction(ITestInfraFunctions testInfraFunctions, ITestState testState, IFileSystem filesystem, ILogger logger) + : base(DPath.Root.Append(new DName("TestEngine")), "PlaywrightScript", FormulaType.Blank, FormulaType.String) + { + _testInfraFunctions = testInfraFunctions; + _testState = testState; + _logger = logger; + _filesystem = filesystem; + } + + public BlankValue Execute(StringValue file) + { + _logger.LogInformation("------------------------------\n\n" + + "Executing PlaywrightScript function."); + + if ( !_filesystem.IsValidFilePath(file.Value) ) + { + _logger.LogError("Invalid file"); + throw new ArgumentException("Invalid file"); + } + + _logger.LogDebug("Loading file"); + + var filename = GetFullFile(_testState, file.Value); + var script = _filesystem.ReadAllText(filename); + + byte[] assemblyBinaryContent; + + _logger.LogDebug("Compiling file"); + + ScriptOptions options = ScriptOptions.Default; + var roslynScript = CSharpScript.Create(script, options); + var compilation = roslynScript.GetCompilation(); + + compilation = compilation.WithOptions(compilation.Options + .WithOptimizationLevel(OptimizationLevel.Release) + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)); + + using (var assemblyStream = new MemoryStream()) + { + var result = compilation.Emit(assemblyStream); + if (!result.Success) + { + var errors = string.Join(Environment.NewLine, result.Diagnostics.Select(x => x)); + throw new Exception("Compilation errors: " + Environment.NewLine + errors); + } + + assemblyBinaryContent = assemblyStream.ToArray(); + } + + GC.Collect(); + + Assembly assembly = Assembly.Load(assemblyBinaryContent); + + _logger.LogDebug("Run script"); + Run(assembly); + + _logger.LogInformation("Successfully finished executing PlaywrightScript function."); + + return FormulaValue.NewBlank(); + } + + private string GetFullFile(ITestState testState, string filename) + { + if (!Path.IsPathRooted(filename)) + { + var testResultDirectory = Path.GetDirectoryName(testState.GetTestConfigFile().FullName); + filename = Path.Combine(testResultDirectory, filename); + } + return filename; + } + + private void Run(Assembly assembly) + { + //Execute the script + var types = assembly.GetTypes(); + + bool found = false; + foreach ( var scriptType in types ) + { + if ( scriptType.Name.Equals("PlaywrightScript") ) + { + found = true; + + var method = scriptType.GetMethod("Run", BindingFlags.Static | BindingFlags.Public); + + var context = _testInfraFunctions.GetContext(); + + if (method == null) + { + _logger.LogError("Static Run Method not found"); + } + + method?.Invoke(null, new object[] { context, _logger }); + } + } + + if ( !found ) { + _logger.LogError("PlaywrightScript class not found"); + } + } + } +} + diff --git a/src/testengine.module.playwrightscript/PlaywrightScriptModule.cs b/src/testengine.module.playwrightscript/PlaywrightScriptModule.cs new file mode 100644 index 00000000..2ac92656 --- /dev/null +++ b/src/testengine.module.playwrightscript/PlaywrightScriptModule.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using Microsoft.PowerFx; +using System.ComponentModel.Composition; +using Microsoft.Extensions.Logging; +using Microsoft.PowerApps.TestEngine.Config; +using Microsoft.PowerApps.TestEngine.Modules; +using Microsoft.PowerApps.TestEngine.Providers; +using Microsoft.PowerApps.TestEngine.System; +using Microsoft.PowerApps.TestEngine.TestInfra; +using Microsoft.Playwright; + +namespace testengine.module +{ + [Export(typeof(ITestEngineModule))] + public class PlaywrightScriptModule : ITestEngineModule + { + public void ExtendBrowserContextOptions(BrowserNewContextOptions options, TestSettings settings) + { + + } + + public void RegisterPowerFxFunction(PowerFxConfig config, ITestInfraFunctions testInfraFunctions, ITestWebProvider testWebProvider, ISingleTestInstanceState singleTestInstanceState, ITestState testState, IFileSystem fileSystem) + { + ILogger logger = singleTestInstanceState.GetLogger(); + config.AddFunction(new PlaywrightScriptFunction(testInfraFunctions, testState, fileSystem, logger)); + logger.LogInformation("Registered PlaywrightScript()"); + } + + public async Task RegisterNetworkRoute(ITestState state, ISingleTestInstanceState singleTestInstanceState, IFileSystem fileSystem, IPage Page, NetworkRequestMock mock) + { + await Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/testengine.module.playwrightscript/testengine.module.playwrightscript.csproj b/src/testengine.module.playwrightscript/testengine.module.playwrightscript.csproj new file mode 100644 index 00000000..bcf997d6 --- /dev/null +++ b/src/testengine.module.playwrightscript/testengine.module.playwrightscript.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + + + + + From 563ae00378738f781db5e8251560b26193648a1a Mon Sep 17 00:00:00 2001 From: Grant Archibald <31553604+Grant-Archibald-MS@users.noreply.github.com> Date: Fri, 7 Jun 2024 09:19:25 -0700 Subject: [PATCH 2/3] Adding TestEngine.PlaywrightScript docs --- docs/PowerFX/README.md | 1 + docs/PowerFX/TestEngine.PlaywrightScript.md | 44 +++++++++++++++++++++ samples/playwrightscript/README.md | 3 ++ samples/playwrightscript/sample.csx | 16 +++++++- 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 docs/PowerFX/TestEngine.PlaywrightScript.md create mode 100644 samples/playwrightscript/README.md diff --git a/docs/PowerFX/README.md b/docs/PowerFX/README.md index 76d6edc2..a3d3f14b 100644 --- a/docs/PowerFX/README.md +++ b/docs/PowerFX/README.md @@ -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 diff --git a/docs/PowerFX/TestEngine.PlaywrightScript.md b/docs/PowerFX/TestEngine.PlaywrightScript.md new file mode 100644 index 00000000..83b427d0 --- /dev/null +++ b/docs/PowerFX/TestEngine.PlaywrightScript.md @@ -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 ro 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 + } +} +``` diff --git a/samples/playwrightscript/README.md b/samples/playwrightscript/README.md new file mode 100644 index 00000000..c08feceb --- /dev/null +++ b/samples/playwrightscript/README.md @@ -0,0 +1,3 @@ +# Overview + +This sample assumes that you have the buttonclicker sample application in the target environment diff --git a/samples/playwrightscript/sample.csx b/samples/playwrightscript/sample.csx index 0b75ab9c..073d5c80 100644 --- a/samples/playwrightscript/sample.csx +++ b/samples/playwrightscript/sample.csx @@ -3,13 +3,25 @@ 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 ( frame.Locator("button:has-text('Button')").CountAsync().Result > 0 ) { - frame.ClickAsync("button:has-text('Button')").Wait(); + if ( await frame.Locator("button:has-text('Button')").CountAsync() > 0 ) { + await frame.ClickAsync("button:has-text('Button')"); } } } From bebfbad304609a2cf5e90c00bd6289c816454c08 Mon Sep 17 00:00:00 2001 From: Grant Archibald <31553604+Grant-Archibald-MS@users.noreply.github.com> Date: Fri, 7 Jun 2024 09:27:52 -0700 Subject: [PATCH 3/3] Update TestEngine.PlaywrightScript.md --- docs/PowerFX/TestEngine.PlaywrightScript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PowerFX/TestEngine.PlaywrightScript.md b/docs/PowerFX/TestEngine.PlaywrightScript.md index 83b427d0..6ce26c19 100644 --- a/docs/PowerFX/TestEngine.PlaywrightScript.md +++ b/docs/PowerFX/TestEngine.PlaywrightScript.md @@ -8,7 +8,7 @@ You can use the playwright inspector to record C# commands to build the C# scrip ## 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 ro allow projectless scripting of Playwright code. The Action assumes the following: +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