Bromine.Playwright.Extensions 1.0.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package Bromine.Playwright.Extensions --version 1.0.2
                    
NuGet\Install-Package Bromine.Playwright.Extensions -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Bromine.Playwright.Extensions" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bromine.Playwright.Extensions" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Bromine.Playwright.Extensions" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Bromine.Playwright.Extensions --version 1.0.2
                    
#r "nuget: Bromine.Playwright.Extensions, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Bromine.Playwright.Extensions@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Bromine.Playwright.Extensions&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Bromine.Playwright.Extensions&version=1.0.2
                    
Install as a Cake Tool

Bromine.Playwright.Extensions

NuGet NuGet Downloads CI codecov GitHub Release License: MIT

A Playwright extensions library for .NET providing fluent assertions (.Should()), browser/context builder patterns, and enhanced Page/Locator extension methods for E2E test automation.

Installation

dotnet add package Bromine.Playwright.Extensions

Or add to your .csproj:

<PackageReference Include="Bromine.Playwright.Extensions" Version="1.0.0" />

Features

1. Fluent Assertions — .Should()

Chain-able, readable assertions for ILocator, IPage, and IAPIResponse.

using Bromine.Playwright.Extensions.Assertions;

// Locator assertions
await page.Locator("#submit-btn").Should().BeVisibleAsync();
await page.Locator("#submit-btn").Should().BeInteractableAsync();   // visible + enabled
await page.Locator(".error").Should().HaveTextAsync("Invalid input");
await page.Locator(".error").Should().ContainTextAsync("Invalid");
await page.Locator("input[name='email']").Should().HaveValueAsync("test@test.com");
await page.Locator(".items").Should().HaveCountAsync(5);
await page.Locator("#checkbox").Should().BeCheckedAsync();
await page.Locator("#field").Should().HaveAttributeAsync("disabled", "true");
await page.Locator(".alert").Should().BeVisibleWithTextAsync("Success");

// Negated assertions
await page.Locator(".spinner").Should().Not.BeVisibleAsync();
await page.Locator("#old-element").Should().Not.HaveTextAsync("deprecated");

// Page assertions
await page.Should().HaveTitleAsync("Dashboard");
await page.Should().HaveUrlContainingAsync("/dashboard");
await page.Should().HaveUrlMatchingAsync(@"\/users\/\d+");

// Response assertions
var response = await request.GetAsync("/api/users");
await response.Should().BeOkAsync();
response.Should().HaveStatus(200);
response.Should().HaveHeader("Content-Type", "application/json");
await response.Should().BodyContainsAsync("\"success\":true");

// Custom timeout per assertion chain
await page.Locator("#slow-loader").Should().WithTimeout(30_000).BeVisibleAsync();

2. Browser Builder Pattern

Create Playwright browser instances with a fluent API — no more remembering option class hierarchies.

using Bromine.Playwright.Extensions.Builders;

// Simple headless Chromium
var (playwright, browser) = await PlaywrightBrowserBuilder.Create()
    .WithChromium()
    .Headless()
    .BuildAsync();

// Headed Firefox with slow motion for debugging
var result = await PlaywrightBrowserBuilder.Create()
    .WithFirefox()
    .Headed()
    .WithSlowMotion(100)
    .WithTimeout(15_000)
    .BuildAsync();

// Chrome channel with proxy
var result = await PlaywrightBrowserBuilder.Create()
    .WithChromium()
    .WithChannel("chrome")
    .Headless()
    .WithProxy("http://proxy.company.com:8080")
    .BuildAsync();

// Clean disposal
await using var browserSession = await PlaywrightBrowserBuilder.Create()
    .WithChromium()
    .Headless()
    .BuildAsync();
// browserSession.Browser and browserSession.Playwright are available
// both get cleaned up on DisposeAsync

3. Browser Context Builder

Build IBrowserContext instances with all options via fluent API.

using Bromine.Playwright.Extensions.Builders;

// Desktop context with full options
var context = await BrowserContextBuilder.For(browser)
    .WithViewport(1920, 1080)
    .BypassCSP()
    .WithPermissions("clipboard-read", "clipboard-write")
    .WithHttpCredentials("admin", "password123")
    .WithLocale("en-US")
    .WithTimezone("Europe/Athens")
    .AcceptDownloads()
    .WithDefaultTimeout(30_000)
    .WithDefaultNavigationTimeout(30_000)
    .BuildAsync();

// Mobile emulation
var mobileContext = await BrowserContextBuilder.For(browser)
    .WithDevice(playwright.Devices["iPhone 13"])
    .AsMobile()
    .WithPermissions("geolocation")
    .WithGeolocation(37.9838f, 23.7275f)  // Athens
    .BuildAsync();

// With HAR + Video recording + Tracing
var debugContext = await BrowserContextBuilder.For(browser)
    .WithViewport(1920, 1080)
    .WithHarRecording("./traces/network.har", omitContent: true)
    .WithVideoRecording("./traces/videos", width: 1920, height: 1080)
    .WithTracing(screenshots: true, snapshots: true, sources: true)
    .BuildAsync();

// With offline mode & storage state
var offlineContext = await BrowserContextBuilder.For(browser)
    .Offline()
    .WithStorageState("./auth-state.json")
    .BuildAsync();

4. Page Extensions

Enhanced IPage extension methods for common operations.

using Bromine.Playwright.Extensions.Extensions;

// Navigation
await page.NavigateAndWaitAsync("https://example.com");
await page.NavigateAndWaitForDomAsync("https://example.com");
await page.ReloadAndWaitAsync();
await page.WaitForUrlContainingAsync("/dashboard");
await page.WaitForStableStateAsync();

// Cookies
var cookie = await page.GetCookieByNameAsync("session_id");
await page.SetCookieAsync("my_cookie", "value123", domain: ".example.com");
await page.ClearCookiesAsync();

// Screenshots
var bytes = await page.FullPageScreenshotAsync();
await page.FullPageScreenshotAsync("/path/to/screenshot.png");
var base64 = await page.ScreenshotToBase64Async();

// Safe interactions
bool clicked = await page.TryClickAsync(".optional-banner-dismiss");
string? text = await page.GetVisibleTextAsync(".user-greeting");

// Scrolling
await page.ScrollToBottomAsync();
await page.ScrollToTopAsync();

// Downloads
string filePath = await page.ClickAndDownloadAsync("#export-btn", "./downloads");

5. Global Configuration

Set default timeouts once at test startup.

using Bromine.Playwright.Extensions.Configuration;

// In your [BeforeTestRun] or setup
PlaywrightDefaults.AssertionTimeout = 10_000;    // 10s for assertions
PlaywrightDefaults.NavigationTimeout = 60_000;   // 60s for navigation
PlaywrightDefaults.ActionTimeout = 15_000;       // 15s for actions
PlaywrightDefaults.DefaultRetryCount = 5;        // 5 retry attempts
PlaywrightDefaults.RetryDelayMs = 1_000;         // 1s between retries

// Reset to defaults
PlaywrightDefaults.Reset();

Static Analysis — Unawaited Assertion Detection

This package ships with a built-in Roslyn analyzer (BROE101) that produces a compile error if you forget to await a fluent assertion chain.

// ❌ BROE101: This fluent assertion chain is not awaited and will never execute.
locator.Should().BeVisibleAsync();

// ✅ Correct
await locator.Should().BeVisibleAsync();

No configuration needed — the analyzer is bundled in the NuGet package and activates automatically.


Running Tests

# Build
dotnet build -c Release

# Install Playwright browsers
pwsh tests/bin/Release/net8.0/playwright.ps1 install chromium --with-deps

# Run tests
dotnet test tests/Bromine.Playwright.Extensions.Tests.csproj -c Release --no-build

Tests run headed locally and headless in CI (detected via the CI environment variable).


CI / CD

Pipelines

Workflow Trigger Description
CI Push / PR to main Build → Install browsers → Run tests
Publish Tag push (v*) Run tests → Pack → Push to nuget.org → GitHub Release
Update Playwright Weekly (Monday 8 AM UTC) Check for new Playwright version → Update → Test → Open PR

Publishing a New Version

git tag v1.0.1
git push origin v1.0.1

The tag version (1.0.1) becomes the NuGet package version automatically. Tests must pass before the package is published.

Setup

Add NUGET_API_KEY as a repository secret (Settings → Secrets → Actions). Get your key from nuget.org/account/apikeys.


Building & Packing Locally

Build

dotnet build -c Release

Pack

dotnet pack src/Bromine.Playwright.Extensions.csproj -c Release -o ./nupkgs

The .nupkg includes the library DLL, XML docs, and the bundled Roslyn analyzer.

Install Locally in Another Project

# Add the local folder as a NuGet source (once)
dotnet nuget add source /path/to/nupkgs --name LocalBromine

# Install
dotnet add package Bromine.Playwright.Extensions --version 1.0.0

Requirements

  • .NET 8.0+
  • Microsoft.Playwright 1.56.0+

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.4 4,913 5/5/2026
1.0.3 180 4/15/2026
1.0.2 141 4/9/2026
1.0.1 104 4/7/2026
1.0.0 125 4/3/2026