diff --git a/change/@ni-nimble-blazor-0f03cea0-79ac-44ba-983f-26623b2691f9.json b/change/@ni-nimble-blazor-0f03cea0-79ac-44ba-983f-26623b2691f9.json
new file mode 100644
index 0000000000..8ee73aa7e1
--- /dev/null
+++ b/change/@ni-nimble-blazor-0f03cea0-79ac-44ba-983f-26623b2691f9.json
@@ -0,0 +1,7 @@
+{
+ "type": "none",
+ "comment": "Acceptance/e2e tests for Nimble Blazor using Playwright (first pass)",
+ "packageName": "@ni/nimble-blazor",
+ "email": "20709258+msmithNI@users.noreply.github.com",
+ "dependentChangeType": "none"
+}
diff --git a/package-lock.json b/package-lock.json
index d3f75a1f1c..ffbd9501c0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -29981,6 +29981,7 @@
"packages/nimble-blazor": {
"name": "@ni/nimble-blazor",
"version": "11.8.30",
+ "hasInstallScript": true,
"license": "MIT",
"devDependencies": {
"@microsoft/fast-web-utilities": "^6.0.0",
@@ -29990,6 +29991,7 @@
"@rollup/plugin-node-resolve": "^15.0.1",
"cross-env": "^7.0.3",
"glob": "^8.1.0",
+ "playwright": "^1.30.0",
"rollup": "^3.10.1"
}
},
@@ -34058,6 +34060,7 @@
"@rollup/plugin-node-resolve": "^15.0.1",
"cross-env": "^7.0.3",
"glob": "^8.1.0",
+ "playwright": "^1.30.0",
"rollup": "^3.10.1"
},
"dependencies": {
diff --git a/packages/nimble-blazor/.gitignore b/packages/nimble-blazor/.gitignore
index a61f755857..1932c22e06 100644
--- a/packages/nimble-blazor/.gitignore
+++ b/packages/nimble-blazor/.gitignore
@@ -2,6 +2,7 @@
NimbleBlazor/wwwroot/nimble-*/
NimbleBlazor/wwwroot/NimbleBlazor.HybridWorkaround.js
NimbleBlazor/Components/Icons/
+build/generate-playwright-version-properties/dist/
artifacts/
bin/
obj/
diff --git a/packages/nimble-blazor/CONTRIBUTING.md b/packages/nimble-blazor/CONTRIBUTING.md
index c9559ab4a8..a7aaa659bd 100644
--- a/packages/nimble-blazor/CONTRIBUTING.md
+++ b/packages/nimble-blazor/CONTRIBUTING.md
@@ -14,7 +14,7 @@ For Nimble Blazor development on Windows, the suggested tools to install are:
- (Optional) Enable IIS (see "Enabling IIS", below)
- ASP.NET Core Runtime 6.0.4xx: Choose "Hosting Bundle" under ASP.NET Core Runtime, on the [.NET 6.0 Download Page](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
-In Visual Studio, run either the `Demo.Server` or `Demo.Projects` to see the Blazor demo apps.
+In Visual Studio, run either the `Demo.Server` or `Demo.Client` project to see the Blazor demo apps.
### Mac / Visual Studio Code
Install [Visual Studio Code](https://code.visualstudio.com/), and install the suggested extensions that appear once you open the NimbleBlazor project folders.
@@ -75,10 +75,26 @@ The C# code for a property supporting 2-way binding will look like this:
## Testing
-### Automated
+### Automated Unit Tests
+
+Test Project: `NimbleBlazor.Tests`
Testing the Nimble Blazor components is possible through the use of xUnit and bUnit. Each Nimble Blazor component should have a corresponding test file.
+### Automated Acceptance Tests
+
+Test Project: `NimbleBlazor.Tests.Acceptance`
+
+In order to fully test the Nimble Blazor components, consider writing new automated acceptance tests for new/modified components. Any component which requires custom JS code in `NimbleBlazor.lib.module.js` should generally have corresponding acceptance tests, because the bUnit tests in `NimbleBlazor.Tests` are unable to exercise/test that JavaScript code.
+
+The `NimbleBlazor.Tests.Acceptance` project starts a local Blazor.Server app which serves Razor pages that host the Nimble components. Then, xUnit-based acceptance tests start a Chromium instance using [Playwright](https://playwright.dev/), load those Razor pages, and interact with them.
+
+To add a new acceptance test:
+- Add a new Razor file that uses that component in the `Pages` subfolder, with the name `[ComponentName][FunctionalityUnderTest].razor`, e.g. `DialogOpenAndClose.razor`. Add any necessary code to initialize the component in a `@code` section in the same file. If you'll interact with the component as the test runs, you may need to add other Nimble components like buttons to trigger new actions on your component under test.
+- In the `Tests` subfolder, add a new class `[ComponentName]Tests.cs` if it doesn't already exist. Add a new test method in that class. Load your Razor file with the `NewPageForRouteAsync(routeName)` method. Using the Playwright APIs, interact with the components on the page, and make assertions about the state of the component under test.
+
+See the existing acceptance tests for examples of using the Playwright APIs. Additionally, see [Getting Started with Playwright Tests (Skyline End2EndTests)](https://dev.azure.com/ni/DevCentral/_git/Skyline?path=/End2EndTests/Getting%20Started%20with%20Playwright%20Tests.md&_a=preview) and the [Playwright .NET docs on writing tests](https://playwright.dev/dotnet/docs/writing-tests).
+
### Example App / Manual Testing
Each Nimble Blazor component should also be showcased in the `Demo` example projects. Simple component examples can be added directly in the `ComponentsDemo.razor` file (in the `Demo.Shared` project). The example project is very similar to the Nimble Angular example-client-app, and component demos can be adapted from that Angular app. Things to keep in mind that are specific to Blazor:
diff --git a/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor b/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor
index 27300948f8..1ae1e4a02a 100644
--- a/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor
+++ b/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor
@@ -5,7 +5,7 @@
+
+ An error has occurred. This application may no longer respond until reloaded.
+
+
+ An unhandled exception has occurred. See browser dev tools for details.
+
+ Reload
+ 🗙
+
+
+
+
+
diff --git a/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/PlaywrightFixture.cs b/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/PlaywrightFixture.cs
new file mode 100644
index 0000000000..25ff2dccef
--- /dev/null
+++ b/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/PlaywrightFixture.cs
@@ -0,0 +1,45 @@
+using Microsoft.Playwright;
+using Xunit;
+using PlaywrightProgram = Microsoft.Playwright.Program;
+
+namespace NimbleBlazor.Tests.Acceptance;
+
+///
+/// Fixture to handle Playwright initialization for acceptance tests.
+///
+public class PlaywrightFixture : IAsyncLifetime
+{
+ private IBrowser? _browser;
+ private IPlaywright? _playwright;
+ public IBrowserContext? BrowserContext { get; private set; }
+
+ public async Task InitializeAsync()
+ {
+ _playwright = await Playwright.CreateAsync();
+ _browser = await _playwright.Chromium.LaunchAsync(
+ new BrowserTypeLaunchOptions()
+ {
+#if DEBUG
+ Headless = false,
+ SlowMo = 1000
+#endif
+ });
+ BrowserContext = await _browser.NewContextAsync(new BrowserNewContextOptions { IgnoreHTTPSErrors = true });
+#if DEBUG
+ BrowserContext.SetDefaultTimeout(30000);
+#endif
+ }
+
+ public async Task DisposeAsync()
+ {
+ if (BrowserContext != null)
+ {
+ await BrowserContext.DisposeAsync();
+ }
+ if (_browser != null)
+ {
+ await _browser.DisposeAsync();
+ }
+ _playwright?.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/Program.cs b/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/Program.cs
new file mode 100644
index 0000000000..a4ce042610
--- /dev/null
+++ b/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/Program.cs
@@ -0,0 +1,21 @@
+namespace NimbleBlazor.Tests.Acceptance
+{
+ ///
+ /// Main entry point which spins up the web server and allows loading the Razor fixtures/pages in a browser
+ /// without running a specific test.
+ ///
+ public static class Program
+ {
+ public static void Main(string[] arguments)
+ {
+ var builder = WebApplication.CreateBuilder(arguments);
+
+ var startup = new Startup(builder.Configuration);
+ startup.ConfigureServices(builder.Services);
+ var app = builder.Build();
+ startup.Configure(app);
+
+ app.Run();
+ }
+ }
+}
diff --git a/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/Properties/launchSettings.json b/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/Properties/launchSettings.json
new file mode 100644
index 0000000000..129a55f7fa
--- /dev/null
+++ b/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:44651"
+ }
+ },
+ "profiles": {
+ "NimbleBlazor.Acceptance.Tests": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5202",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/Shared/MainLayout.razor b/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/Shared/MainLayout.razor
new file mode 100644
index 0000000000..708f84232b
--- /dev/null
+++ b/packages/nimble-blazor/Tests/NimbleBlazor.Tests.Acceptance/Shared/MainLayout.razor
@@ -0,0 +1,20 @@
+@using Microsoft.AspNetCore.Components
+@using NimbleBlazor
+@namespace NimbleBlazor.Tests.Acceptance.Shared
+@inherits LayoutComponentBase
+
+Nimble Blazor tests
+
+