Bromine.Playwright.Extensions 1.0.4

dotnet add package Bromine.Playwright.Extensions --version 1.0.4
                    
NuGet\Install-Package Bromine.Playwright.Extensions -Version 1.0.4
                    
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.4" />
                    
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.4" />
                    
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.4
                    
#r "nuget: Bromine.Playwright.Extensions, 1.0.4"
                    
#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.4
                    
#: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.4
                    
Install as a Cake Addin
#tool nuget:?package=Bromine.Playwright.Extensions&version=1.0.4
                    
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()
    .HaveCountAsync(1)
    .HaveTextAsync("Submit");

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

// Chaining multiple assertions
await page.Locator("#submit-btn").Should()
    .BeVisibleAsync()
    .Not.BeEnabledAsync();

// "because" messages for better failure diagnostics
await page.Locator("#submit-btn").Should()
    .BeVisibleAsync(because: "the form should be loaded")
    .BeEnabledAsync(because: "the user has filled all required fields");

// Page assertions
await page.Should().HaveTitleAsync("Dashboard");
await page.Should().HaveURLAsync("https://example.com/dashboard");

// Response assertions — fully chainable, same pattern as locator assertions
var response = await request.GetAsync("/api/users");
await response.Should()
    .BeOKAsync()
    .HaveStatusAsync(200)
    .HaveHeaderValueAsync("Content-Type", "application/json")
    .BodyContainsAsync("Alice");

// Negated
await response.Should().Not.BeOKAsync();

// With because messages
await response.Should()
    .BeOKAsync(because: "the users endpoint should be healthy")
    .BodyContainsAsync("Alice", because: "Alice should be in the user list");

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  (default: 5s)
PlaywrightDefaults.NavigationTimeout = 60_000;   // 60s for navigation (default: 30s)
PlaywrightDefaults.ActionTimeout = 15_000;       // 15s for actions    (default: 15s)
PlaywrightDefaults.DefaultRetryCount = 3;        // 3 retry attempts   (default: 3)
PlaywrightDefaults.RetryDelayMs = 500;           // 500ms between retries (default: 500ms)

// 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,770 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