Claude
Skills
Sign in
Back

dotnet-uno-testing

Included with Lifetime
$97 forever

Testing Uno Platform apps. Playwright for WASM, platform-specific patterns, runtime heads.

General

What this skill does


# dotnet-uno-testing

Testing Uno Platform applications across target heads (WASM, Desktop, Mobile). Covers Playwright-based browser automation for Uno WASM apps, platform-specific testing patterns for different runtime heads, and test infrastructure for cross-platform Uno projects.

**Version assumptions:** .NET 8.0+ baseline, Uno Platform 5.x+, Playwright 1.40+ for WASM testing. Uno Platform uses single-project structure with multiple target frameworks.

**Out of scope:** Shared UI testing patterns (page object model, selectors, wait strategies) are in [skill:dotnet-ui-testing-core]. Playwright fundamentals (installation, CI caching, trace viewer) are covered by [skill:dotnet-playwright]. Test project scaffolding is owned by [skill:dotnet-add-testing].

**Prerequisites:** Uno Platform application with WASM head configured. For WASM testing: Playwright browsers installed (see [skill:dotnet-playwright]). For mobile testing: platform SDKs configured (Android SDK, Xcode).

Cross-references: [skill:dotnet-ui-testing-core] for page object model and selector strategies, [skill:dotnet-playwright] for Playwright installation, CI caching, and trace viewer, [skill:dotnet-uno-platform] for Uno Extensions, MVUX, Toolkit, and theme guidance, [skill:dotnet-uno-targets] for per-target deployment and platform-specific gotchas.

---

## Uno Testing Strategy by Head

Uno Platform apps run on multiple heads (WASM, Desktop/Skia, iOS, Android, Windows). Each head has different testing tools and trade-offs.

| Head | Testing Approach | Tool | Speed | Fidelity |
|------|-----------------|------|-------|----------|
| **WASM** | Browser automation | Playwright | Medium | High -- real browser rendering |
| **Desktop (Skia/GTK, WPF)** | UI automation | Appium / WinAppDriver | Medium | High -- real desktop rendering |
| **iOS** | Simulator automation | Appium + XCUITest | Slow | Highest -- real iOS rendering |
| **Android** | Emulator automation | Appium + UIAutomator2 | Slow | Highest -- real Android rendering |
| **Unit (shared logic)** | In-memory | xUnit (no UI) | Fast | N/A -- logic only |

**Recommended priority:** Test shared business logic with unit tests first. Use Playwright against the WASM head for UI verification -- it is the fastest UI testing path with the broadest coverage. Add platform-specific Appium tests only for behaviors that differ between heads.

---

## Playwright for Uno WASM

The WASM head renders Uno apps in a browser, making Playwright the natural choice for UI testing.

### Test Infrastructure

```csharp
// NuGet: Microsoft.Playwright
public class UnoWasmFixture : IAsyncLifetime
{
    public IPlaywright Playwright { get; private set; } = null!;
    public IBrowser Browser { get; private set; } = null!;
    public IPage Page { get; private set; } = null!;

    private Process? _serverProcess;

    public async ValueTask InitializeAsync()
    {
        // Start the WASM app (dotnet run or serve the published output)
        _serverProcess = await StartWasmServerAsync();

        Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
        Browser = await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
        {
            Headless = true
        });
        Page = await Browser.NewPageAsync();

        // Wait for Uno WASM app to fully load
        await Page.GotoAsync("http://localhost:5000");
        await WaitForUnoAppReadyAsync();
    }

    public async ValueTask DisposeAsync()
    {
        await Page.CloseAsync();
        await Browser.CloseAsync();
        Playwright.Dispose();

        _serverProcess?.Kill(entireProcessTree: true);
        _serverProcess?.Dispose();
    }

    private async Task WaitForUnoAppReadyAsync()
    {
        // Uno WASM apps show a loading splash; wait for the app root to appear
        await Page.WaitForSelectorAsync(
            "[data-testid='app-root'], #uno-body",
            new() { State = WaitForSelectorState.Visible, Timeout = 30_000 });

        // Additional wait for Uno runtime initialization
        await Page.WaitForFunctionAsync(
            "() => document.querySelector('#uno-body')?.children.length > 0",
            null,
            new() { Timeout = 15_000 });
    }

    private static async Task<Process> StartWasmServerAsync()
    {
        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "dotnet",
                Arguments = "run --project src/MyApp/MyApp.Wasm.csproj --no-build",
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true
            }
        };
        process.Start();

        // Wait for server to be ready by probing the health endpoint
        using var httpClient = new HttpClient();
        var deadline = DateTime.UtcNow.AddSeconds(30);
        while (DateTime.UtcNow < deadline)
        {
            try
            {
                var resp = await httpClient.GetAsync("http://localhost:5000");
                if (resp.IsSuccessStatusCode) break;
            }
            catch (HttpRequestException)
            {
                // Server not ready yet
            }
            await Task.Delay(500);
        }
        return process;
    }
}
```

### WASM UI Tests

```csharp
public class UnoNavigationTests : IClassFixture<UnoWasmFixture>
{
    private readonly IPage _page;

    public UnoNavigationTests(UnoWasmFixture fixture)
    {
        _page = fixture.Page;
    }

    [Fact]
    public async Task MainPage_LoadsSuccessfully()
    {
        // Uno WASM renders XAML controls as HTML elements
        // Use AutomationProperties.AutomationId for selectors
        var title = _page.Locator("[data-testid='main-title']");

        await Expect(title).ToBeVisibleAsync();
        await Expect(title).ToHaveTextAsync("Welcome");
    }

    [Fact]
    public async Task Navigation_ClickSettings_ShowsSettingsPage()
    {
        await _page.ClickAsync("[data-testid='nav-settings']");

        var settingsHeader = _page.Locator("[data-testid='settings-header']");
        await Expect(settingsHeader).ToBeVisibleAsync();
        await Expect(settingsHeader).ToHaveTextAsync("Settings");
    }
}
```

### Form Interaction Tests

```csharp
[Fact]
public async Task LoginForm_SubmitValid_NavigatesToDashboard()
{
    await _page.FillAsync("[data-testid='username-input']", "testuser");
    await _page.FillAsync("[data-testid='password-input']", "P@ssw0rd!");
    await _page.ClickAsync("[data-testid='login-button']");

    // Wait for navigation after login
    var dashboard = _page.Locator("[data-testid='dashboard-title']");
    await Expect(dashboard).ToBeVisibleAsync(
        new() { Timeout = 10_000 });
}

[Fact]
public async Task TodoList_AddItem_AppearsInList()
{
    await _page.FillAsync("[data-testid='todo-input']", "Buy groceries");
    await _page.ClickAsync("[data-testid='add-todo-btn']");

    var items = _page.Locator("[data-testid='todo-item']");
    await Expect(items).ToHaveCountAsync(1);
    await Expect(items.First).ToContainTextAsync("Buy groceries");
}
```

---

## Platform-Specific Testing

### AutomationProperties for Cross-Platform Selectors

Uno maps `AutomationProperties.AutomationId` to each platform's native identifier:

```xml
<!-- Uno XAML -- works across all heads -->
<Page x:Class="MyApp.Views.LoginPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <StackPanel>
        <TextBox AutomationProperties.AutomationId="username-input"
                 PlaceholderText="Username" />

        <PasswordBox AutomationProperties.AutomationId="password-input"
                     PlaceholderText="Password" />

        <Button AutomationProperties.AutomationId="login-button"
                Content="Log In" />

        <TextBlock AutomationProperties.AutomationId="error-message"
                   Foreground="
Files: 1
Size: 13.8 KB
Complexity: 16/100
Category: General

Related in General