Skip to content

Commit

Permalink
[dev-v5] [Playwrights Tests] Add fundamentals for Playwright testing (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
dvoituron authored Nov 22, 2024
1 parent 1be48a3 commit d9b39fa
Show file tree
Hide file tree
Showing 19 changed files with 496 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ dotnet_diagnostic.CA2200.severity = warning
dotnet_diagnostic.CA2201.severity = warning

# CA2208: Instantiate argument exceptions correctly
dotnet_diagnostic.CA2208.severity = warning
dotnet_diagnostic.CA2208.severity = none

# CA2245: Do not assign a property to itself
dotnet_diagnostic.CA2245.severity = warning
Expand Down
20 changes: 19 additions & 1 deletion .github/workflows/build-core-lib.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ on:

env:
PROJECTS: "./src/Core/Microsoft.FluentUI.AspNetCore.Components.csproj"
TESTS: "./tests/Core/Microsoft.FluentUI.AspNetCore.Components.Tests.csproj"
TESTS: "./tests/Core/Components.Tests.csproj"
INTEGRATION_TEST: "./tests/Integration/Components.IntegrationTests.csproj"
MIN_COVERAGE: "98"

jobs:
Expand Down Expand Up @@ -143,6 +144,23 @@ jobs:
with:
path: CoverageReports/SummaryGithub.md

# Integration Tests

- name: Build Integration Tests
run: dotnet build ${{ env.INTEGRATION_TEST }} --verbosity normal --configuration Release
working-directory: ${{ github.workspace }}

- name: Install Playwright
if: success() || failure()
shell: pwsh
working-directory: ${{ github.workspace }}
run: |
./tests/Integration/bin/Release/net9.0/playwright.ps1 install
- name: Run Integration Tests
run: dotnet test ${{ env.INTEGRATION_TEST }} --logger:trx --results-directory ./TestResults --verbosity normal --configuration Release
working-directory: ${{ github.workspace }}

analyze:
name: Analyze
runs-on: ubuntu-latest
Expand Down
4 changes: 3 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="Microsoft.Playwright" Version="1.48.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<!-- Shared dependencies -->
<PackageVersion Include="Markdig.Signed" Version="0.34.0" />
<PackageVersion Include="Meziantou.Analyzer" Version="2.0.162" />
Expand Down Expand Up @@ -51,4 +53,4 @@
<PackageVersion Include="System.Text.Encodings.Web" Version="$(RuntimeVersion9)" />
<PackageVersion Include="System.Text.Json" Version="$(RuntimeVersion9)" />
</ItemGroup>
</Project>
</Project>
20 changes: 13 additions & 7 deletions Microsoft.FluentUI-v5.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{0A73C6AE-F69
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A7EC98D2-21E3-4967-8C5A-D62E640305EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.FluentUI.AspNetCore.Components.Tests", "tests\Core\Microsoft.FluentUI.AspNetCore.Components.Tests.csproj", "{3A9FE7FC-69CB-4698-A676-39A3A13399B3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Components.Tests", "tests\Core\Components.Tests.csproj", "{3A9FE7FC-69CB-4698-A676-39A3A13399B3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F273876F-7528-42B3-BFE8-7CFF8ED1E2A2}"
EndProject
Expand All @@ -37,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentUI.Demo.SampleData",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentUI.Demo.SampleApi", "examples\Tools\FluentUI.Demo.SampleApi\FluentUI.Demo.SampleApi.csproj", "{E67B08B6-AEE4-4281-8700-1C87A5A3C11E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Components.IntegrationTests", "tests\Integration\Components.IntegrationTests.csproj", "{F380FA22-53D8-4381-B89B-4047AF544D53}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -85,24 +87,28 @@ Global
{E67B08B6-AEE4-4281-8700-1C87A5A3C11E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E67B08B6-AEE4-4281-8700-1C87A5A3C11E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E67B08B6-AEE4-4281-8700-1C87A5A3C11E}.Release|Any CPU.Build.0 = Release|Any CPU
{F380FA22-53D8-4381-B89B-4047AF544D53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F380FA22-53D8-4381-B89B-4047AF544D53}.Release|Any CPU.ActiveCfg = Release|AnyCPU
{F380FA22-53D8-4381-B89B-4047AF544D53}.Release|Any CPU.Build.0 = Release|AnyCPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B6B58D33-2D25-42BA-9096-702F9ABEF9EE} = {62586417-1901-4732-8188-AE8BADC6AE17}
{7D2833AF-6BA7-481A-B625-4C162D8D4756} = {B6B58D33-2D25-42BA-9096-702F9ABEF9EE}
{7D2833AF-6BA7-481A-B625-4C162D8D4756} = {62586417-1901-4732-8188-AE8BADC6AE17}
{3C30B391-7436-4349-A6CE-19EAFA6804FA} = {B6B58D33-2D25-42BA-9096-702F9ABEF9EE}
{3A9FE7FC-69CB-4698-A676-39A3A13399B3} = {A7EC98D2-21E3-4967-8C5A-D62E640305EB}
{2A11833A-E392-484A-99DF-A870C0692302} = {F273876F-7528-42B3-BFE8-7CFF8ED1E2A2}
{AEBC6690-5247-4C1E-ADAF-0BBFAC97D606} = {2A11833A-E392-484A-99DF-A870C0692302}
{0252FA12-8398-4A68-8C80-8DFBDF3021FD} = {2A11833A-E392-484A-99DF-A870C0692302}
{958BF092-4CF2-470C-B058-9244496B234F} = {B98A7516-E9B2-4301-B6A3-33656BF4F4D9}
{AEBC6690-5247-4C1E-ADAF-0BBFAC97D606} = {B6B58D33-2D25-42BA-9096-702F9ABEF9EE}
{0252FA12-8398-4A68-8C80-8DFBDF3021FD} = {F273876F-7528-42B3-BFE8-7CFF8ED1E2A2}
{958BF092-4CF2-470C-B058-9244496B234F} = {2A11833A-E392-484A-99DF-A870C0692302}
{B98A7516-E9B2-4301-B6A3-33656BF4F4D9} = {F273876F-7528-42B3-BFE8-7CFF8ED1E2A2}
{2462C5CC-81BC-47AF-85B8-5FADD9E47ADF} = {B98A7516-E9B2-4301-B6A3-33656BF4F4D9}
{2462C5CC-81BC-47AF-85B8-5FADD9E47ADF} = {2A11833A-E392-484A-99DF-A870C0692302}
{C78BB9AD-B8CF-417B-990E-02410447649F} = {B98A7516-E9B2-4301-B6A3-33656BF4F4D9}
{32466925-47C6-420F-B869-5F922162C3A7} = {B98A7516-E9B2-4301-B6A3-33656BF4F4D9}
{32466925-47C6-420F-B869-5F922162C3A7} = {F273876F-7528-42B3-BFE8-7CFF8ED1E2A2}
{E67B08B6-AEE4-4281-8700-1C87A5A3C11E} = {B98A7516-E9B2-4301-B6A3-33656BF4F4D9}
{F380FA22-53D8-4381-B89B-4047AF544D53} = {A7EC98D2-21E3-4967-8C5A-D62E640305EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {44D95FF7-AEBE-41FB-9D40-CF1E09ADC6BC}
Expand Down
3 changes: 3 additions & 0 deletions spelling.dic
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
ansi
appsettings
blazor
brotli
cref
csproj
datalist
elementreference
evenodd
gzip
javascript
microsoft
noattribute
nonfile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
<LangVersion>latest</LangVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IsPackable>false</IsPackable>
<AssemblyName>Microsoft.FluentUI.AspNetCore.Components.Tests</AssemblyName>
<RootNamespace>Microsoft.FluentUI.AspNetCore.Components.Tests</RootNamespace>
</PropertyGroup>

<ItemGroup>
Expand Down
31 changes: 31 additions & 0 deletions tests/Integration/Components.IntegrationTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IsPackable>false</IsPackable>
<StartupObject>Microsoft.FluentUI.AspNetCore.Components.IntegrationTests.WebServer.Program</StartupObject>
<AssemblyName>Microsoft.FluentUI.AspNetCore.Components.IntegrationTests</AssemblyName>
<RootNamespace>Microsoft.FluentUI.AspNetCore.Components.IntegrationTests</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="bunit" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.Playwright" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Core\Microsoft.FluentUI.AspNetCore.Components.csproj" />
</ItemGroup>

</Project>
16 changes: 16 additions & 0 deletions tests/Integration/Components/Button/Examples/DefaultPage.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@page "/button/default"

<div>
Current count: @Value
</div>
<FluentButton Appearance="ButtonAppearance.Primary" OnClick="@Increment">Increment</FluentButton>

@code
{
private int Value = 0;

private void Increment()
{
Value++;
}
}
42 changes: 42 additions & 0 deletions tests/Integration/Components/Button/FluentButtonTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using Microsoft.FluentUI.AspNetCore.Components.IntegrationTests.WebServer;
using Microsoft.Playwright;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.FluentUI.AspNetCore.Components.IntegrationTests.Components.Button;

[Collection(StartServerCollection.Name)]
public class FluentButtonTests : FluentPlaywrightBaseTest
{
public FluentButtonTests(ITestOutputHelper output, StartServerFixture server)
: base(output, server)
{
}

[Fact]
public async Task FluentButton_IncrementCounter()
{
// Arrange
var page = await WaitOpenPageAsync($"/button/default", openDevTools: false);

// Act
await page.ClickAsync("fluent-button");
await Task.Delay(100); // Wait for page to render

// Assert
var content = await page.ContentAsync();
await page.ScreenshotAsync(new()
{
Path = $"{Server.ScreenshotsFolder}FluentButton_IncrementCounter.png"
});

Trace.WriteLine(content);

Assert.Contains("Current count: 1", content);
}
}

77 changes: 77 additions & 0 deletions tests/Integration/Components/FluentPlaywrightBaseTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using Microsoft.FluentUI.AspNetCore.Components.IntegrationTests.WebServer;
using Microsoft.Playwright;
using Xunit.Abstractions;

namespace Microsoft.FluentUI.AspNetCore.Components.IntegrationTests.Components;

#pragma warning disable CS0612 // Type or member is obsolete

public abstract class FluentPlaywrightBaseTest : IAsyncDisposable, IDisposable
{
private IPlaywright? _playwright;
private IBrowser? _browser;

/// <summary>
/// Constructor for the FluentPlaywrightBaseTest
/// </summary>
/// <param name="output"></param>
/// <param name="server"></param>
protected FluentPlaywrightBaseTest(ITestOutputHelper output, StartServerFixture server)
{
Trace = output;
Server = server;
}

/// <summary>
/// Output helper for logging
/// </summary>
public virtual ITestOutputHelper Trace { get; set; }

/// <summary>
/// Server fixture for starting the server
/// </summary>
protected virtual StartServerFixture Server { get; set; }

public async Task<IPage> WaitOpenPageAsync(string url, bool openDevTools = false)
{
_playwright = await Playwright.Playwright.CreateAsync();
_browser = await _playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions()
{
Devtools = openDevTools
});

var page = await _browser.NewPageAsync();
page.Console += (_, msg) => Trace.WriteLine(msg.Text);

await page.GotoAsync($"{Server.BaseUrl}{url}");
await page.WaitForConsoleMessageAsync(new PageWaitForConsoleMessageOptions()
{
Predicate = msg => msg.Text.Contains("WebSocket connected"),
Timeout = 1000
});
await Task.Delay(100); // Wait for page to render

return page;
}

public void Dispose()
{
Task.Run(async () => await DisposeAsync());
}

public async ValueTask DisposeAsync()
{
if (_browser != null)
{
await _browser.CloseAsync();
}

_playwright?.Dispose();
}
}

#pragma warning restore CS0612 // Type or member is obsolete
12 changes: 12 additions & 0 deletions tests/Integration/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"Microsoft.FluentUI.AspNetCore.Components.IntegrationTests": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:65511;http://localhost:65512"
}
}
}
64 changes: 64 additions & 0 deletions tests/Integration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Integration tests using Playwright for .NET

## Running the tests

First, make sure you have the Playwright for .NET package installed. If you don't, follow the instructions in the next section.

- Build the **InegrationTests** project to generate the test assembly in the Debug folder: `dotnet build`.
- Next, install the **Playwright browsers**, running the `playwright.ps1` script.

```powershell
dotnet build
./bin/Debug/net9.0/playwright.ps1 install
```

> **Note**: `dotnet build` needs to be run from the `Integration` folder.
More details on https://playwright.dev/dotnet/docs/intro

## Web Server for testing

The tests are running against a local web server.
This server is automatically started and stopped by the tests (see `StartServerFixture.cs`).

The server is started to listen on `http://localhost:5050`.

To start the server automatically, you need to include the test in a specific collection of tests:
```csharp
[Collection(StartServerCollection.Name)]
public class FluentButtonTests : FluentPlaywrightBaseTest
{
public FluentButtonTests(ITestOutputHelper output, StartServerFixture server)
: base(output, server)
{
}

[Fact]
public async Task FluentButton_IncrementCounter()
{
// Arrange
var page = await WaitOpenPageAsync($"/button/default");

// Act
await page.ClickAsync("fluent-button");
await Task.Delay(100); // Wait for page to render
// Assert
var content = await page.ContentAsync();
Assert.Contains("Current count: 1", content);
}
}
```

> ⚠️ **Notes:**
>
> If you interrupt a test abruptly (for example, by pressing the Stop button on the Test Explorer)
> it is possible that the `StartServerFixture.DisposeAsync` procedure will not be called.
> In this case, the **web server remains running**.
> This can block the next compilation of the code.
> ```
> Could not copy "...\apphost.exe" to "bin\Debug\Microsoft.FluentUI.AspNetCore.Components.IntegrationTests.exe".
> Exceeded retry count of 10. Failed.
> The file is locked by: "Microsoft.FluentUI.AspNetCore.Components.IntegrationTests"
> ```
> You can kill it manually using the Task Manager (filtering on ‘IntegrationTests’).
14 changes: 14 additions & 0 deletions tests/Integration/WebServer/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<HeadOutlet @rendermode="InteractiveServer" />
</head>

<body>
<Routes @rendermode="InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
Loading

0 comments on commit d9b39fa

Please sign in to comment.