# Multi-Browser EM SK1E Essai avec Playwright pour .NET

<datetime class="hidden">2025-11-27T14:00</datetime>

<!--category-- Playwright, E2E Testing, xUnit, Testing, Multi-Browser -->
Playwright est la solution officielle de Microsoft' pour l’automatisation des navigateurs modernes , offrant un support multilatéral-browser MSC3ChromeM SK4Firefox+,Safari*) avec une seule APIMST7 construite MST8dans le domaine du débugging des traces+MST9 et de l’émulation mobile+Mst10 Ce guide couvre tout, depuis l’installation jusqu’aux modèles d’essais avancésMst11 en comparant avec PuppeteerSharp afin de vous aider à choisir l’outil appropriéM st12 Si vous n’avez qu’à utiliser Chrome et que vous voulez une configuration plus simpleM ST13 envisagez [Écarpe de marionnettes](/blog/puppeteersharp-e2e-testing) au lieu - mais si vous avez besoin d’une analyse transversale exhaustive-essais de navigateurM SK2 Le playwright est la voie à suivre

## Introduction

Si vous' avez lu mon article sur [Écarpe de marionnettes](/blog/puppeteersharp-e2e-testing), vous ' sachez que je'je suis un fan de l’E moderneM SK3outils d’essai qui ne permettent pas à 't de vous détruire les cheveuxMSC5 PuppeteerSharp est brillant pour ChromeMST6seulement à l’essaisMSP7 mais qu’il en va si vous avez besoin de tester sur plusieurs navigateursMSS8 QueMSSS9est là où [Playwright](https://playwright.dev/) arrive en.

Playwright est Microsoft' la réponse au problème de l’automatisation du navigateur , et ils' ont appris à partir de tout ce qui a Ã©tÃ© prÃ©cÃ©demment Ã lâ€TMÃ©gard de . Câ€TMest ce que fait ' comme PuppeteerSharpM SK5 son frère plus ambitieux |- c'est tout ce que le puppeteer fait | , mais il ajoute des supports Firefox et Safari |

Dans cet article, je vous montrerai comment utiliser Playwright pour .NET afin de tester à travers ChromeM SK3 Firefox, et SafariMSC5 avec des exemples de code réels et des modèles pratiques que vous pouvez utiliser aujourd’hui .

[TOC]

## Pourquoi Playwright sur PuppeteerSharp?

Ne me trompez pas - [Écarpe de marionnettes](/blog/puppeteersharp-e2e-testing) est excellent si vous n’avez besoin que de Chrome. Mais ici's quand Playwright a sensM SK2

### Le problème du navigateur multi-

Vos utilisateurs n’utilisent pas tous le Chrome' Tous les utilisateurs utilisent

- **Chrome/Edge**:  ~65% d'utilisateurs
- **Safari**:  ~20% d’utilisateurs
- **Firefox**: ~5% d'utilisateurs ( mais souvent une traduction différente)

Un bug qui n'apparaît que dans Safari peut vous perdre 20% de vos utilisateurs potentiels. Playwright vous permet d'essayer les trois avec la même API

### Améliorer l'expérience des développeurs

```mermaid
graph TB
    subgraph "PuppeteerSharp"
        PS[Chrome Only]
        PS --> PS1[Fast setup]
        PS --> PS2[Simple API]
        PS --> PS3[Chrome DevTools]
    end

    subgraph "Playwright"
        PW[Multi-Browser]
        PW --> PW1[Chrome, Firefox, Safari]
        PW --> PW2[Auto-waiting built-in]
        PW --> PW3[Trace viewer]
        PW --> PW4[Codegen tool]
        PW --> PW5[Better error messages]
    end

    style PW stroke:#00aa00,stroke-width:3px
    style PW1 stroke:#0066cc,stroke-width:2px
    style PW3 stroke:#0066cc,stroke-width:2px
    style PW4 stroke:#0066cc,stroke-width:2px
```

### Avantages clés

1. **Multi-Support du navigateur** - Test ChromeM SK1 Firefox, Safari avec code identique
2. **Better auto-attendre** - Plus fiable à l’extérieur de la boîte
3. **Vérificateur des traces** - Enregistrez les traces complètes des essais de débugging
4. **Codegéne** - Gérer le code d'essai en interagissant avec votre site
5. **Interception du réseau** - Plus puissant que PuppeteerSharp
6. **Caractéristiques modernes du Web** - Amélioration de l'appui pour le DOM-shadow, iframesMSC2 etcM SK3

## Établissement d'un dramaturge pour .NET

Installer le [Microsoft.Playwright](https://www.nuget.org/packages/Microsoft.Playwright) NuGet package:

```bash
dotnet add package Microsoft.Playwright
dotnet add package Microsoft.Playwright.NUnit  # Or use xUnit
```

Ensuite installez les navigateurs (cette téléchargement [Chromium](https://www.chromium.org/), [Firefox](https://www.mozilla.org/firefox/), et [WebKit](https://webkit.org/)):

```bash
pwsh bin/Debug/net9.0/playwright.ps1 install
```

Ou sur Linux/Mac:

```bash
playwright install
```

**Note:** Le premier navigateur installe les téléchargements autour de 400MBM SK1 Les mises à jour subséquentes sont beaucoup plus petites.

Voici la configuration de mon projet d’essai'

```xml
<PackageReference Include="Microsoft.Playwright" Version="1.48.0" />
<PackageReference Include="Microsoft.Playwright.NUnit" Version="1.48.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
```

I'm utilisant [xUnit](https://xunit.net/), mais Playwright fonctionne aussi bien avec [NUnit](https://nunit.org/) ou [Essai MS](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest). En fait, Le dramaturge a un [Intégration NUnit](https://playwright.dev/dotnet/docs/test-runners#nunit) avec des aides supplémentaires.

## Création d'une classe d'essai de base

Il s'agit d'une approche similaire à [Écarpe de marionnettes](/blog/puppeteersharp-e2e-testing#creating-a-base-test-class), mais avec plusieurs supports de navigateurM SK1browser:

### Structure de la classe

```csharp
using Microsoft.Playwright;
using Xunit.Abstractions;

namespace Mostlylucid.Test.E2E;

public abstract class PlaywrightTestBase : IAsyncLifetime
{
    protected IPlaywright Playwright = null!;
    protected IBrowser Browser = null!;
    protected IBrowserContext Context = null!;
    protected IPage Page = null!;

    protected readonly ITestOutputHelper Output;
    protected const string BaseUrl = "http://localhost:8080";

    // Override this in derived classes to test different browsers
    protected virtual BrowserType BrowserType => BrowserType.Chromium;

    protected PlaywrightTestBase(ITestOutputHelper output)
    {
        Output = output;
    }

    public async Task InitializeAsync()
    {
        // Create Playwright instance
        Playwright = await Microsoft.Playwright.Playwright.CreateAsync();

        // Launch the specified browser
        Browser = await LaunchBrowserAsync();

        // Create a new context (like an incognito window)
        Context = await Browser.NewContextAsync(new BrowserNewContextOptions
        {
            ViewportSize = new ViewportSize { Width = 1400, Height = 900 },
            IgnoreHTTPSErrors = true
        });

        // Create a new page
        Page = await Context.NewPageAsync();

        // Set default timeout
        Page.SetDefaultTimeout(30000);
    }

    private async Task<IBrowser> LaunchBrowserAsync()
    {
        var options = new BrowserTypeLaunchOptions
        {
            Headless = true, // Set to false for debugging
        };

        return BrowserType switch
        {
            BrowserType.Chromium => await Playwright.Chromium.LaunchAsync(options),
            BrowserType.Firefox => await Playwright.Firefox.LaunchAsync(options),
            BrowserType.Webkit => await Playwright.Webkit.LaunchAsync(options),
            _ => throw new ArgumentException($"Unknown browser type: {BrowserType}")
        };
    }

    public async Task DisposeAsync()
    {
        await Page?.CloseAsync()!;
        await Context?.CloseAsync()!;
        await Browser?.CloseAsync()!;
        Playwright?.Dispose();
    }
}

public enum BrowserType
{
    Chromium,
    Firefox,
    Webkit
}
```

### Pourquoi les contextes du navigateur?

Veuillez noter que nous avons créé `Context` avant la création d'un `Page`. C’est un concept de dramaturge que PuppeteerSharp ne possède pas.

- **Contexte** = Séance de navigateur isolé (comme mode incognito)
- **Pages** = Un onglet dans ce contexte

Les contextes vous permettent:

- Tester avec différentes sessions d'utilisateur simultanément
- Établir des cookies différents, permissions, ou locale par contexte
- Isoler complètement les essais (même dans la même instance de navigateur)

### Méthodes d'aide

```csharp
// Navigation with better waiting
protected async Task NavigateAsync(string path)
{
    var url = path.StartsWith("http") ? path : $"{BaseUrl}{path}";
    await Page.GotoAsync(url, new PageGotoOptions
    {
        WaitUntil = WaitUntilState.NetworkIdle
    });
}

// Playwright has better built-in waiting, but these are still useful
protected async Task<IElementHandle?> WaitForSelectorAsync(string selector)
{
    try
    {
        return await Page.WaitForSelectorAsync(selector, new PageWaitForSelectorOptions
        {
            State = WaitForSelectorState.Visible
        });
    }
    catch (TimeoutException)
    {
        return null;
    }
}

// Improved element operations with auto-waiting
protected async Task<bool> ElementExistsAsync(string selector)
{
    return await Page.Locator(selector).CountAsync() > 0;
}

protected async Task<string?> GetTextContentAsync(string selector)
{
    var locator = Page.Locator(selector);
    return await locator.TextContentAsync();
}

protected async Task TypeAsync(string selector, string text)
{
    await Page.Locator(selector).FillAsync(text);
}

protected async Task ClickAsync(string selector)
{
    await Page.Locator(selector).ClickAsync();
}
```

### Locateurs - Playwright 's Secret Weapon

Veuillez noter que `Page.Locator()` appels? Ceci diffère de PuppeteerSharp's `QuerySelector`. Les locateurs sontM SK1

- **Lazy** - Ils ne demandent pas de DOM jusqu’à ce que vous les utilisez
- **En essayant de nouveau** - Ils attendent et essaient automatiquement les opérations
- **Strict** - Il y a des erreurs si plusieurs éléments correspondent à ceux-ci.

```csharp
// PuppeteerSharp way (manual waiting)
await Page.WaitForSelectorAsync("#button");
await Page.ClickAsync("#button");

// Playwright way (auto-waiting)
await Page.Locator("#button").ClickAsync(); // Waits automatically!
```

## Multi-Examen du navigateur

Vous pouvez faire le même test sur tous les navigateurs.

### Utilisation de la théorie xUnit pour les tests multi-Browser

```csharp
public class CrossBrowserTests : PlaywrightTestBase
{
    public CrossBrowserTests(ITestOutputHelper output) : base(output) { }

    [Theory]
    [InlineData(BrowserType.Chromium)]
    [InlineData(BrowserType.Firefox)]
    [InlineData(BrowserType.Webkit)]
    public async Task HomePage_LoadsCorrectly_InAllBrowsers(BrowserType browserType)
    {
        // Override the browser type for this test
        BrowserType = browserType;
        await InitializeAsync();

        // Arrange & Act
        await NavigateAsync("/");

        // Assert
        var title = await Page.TitleAsync();
        Assert.Contains("Mostlylucid", title);

        Output.WriteLine($"✓ Test passed in {browserType}");
    }
}
```

### Browser-Classes d'essai spécifiques

Ou créer des classes d’essai distinctes pour chaque navigateur:

```csharp
public class ChromiumTests : PlaywrightTestBase
{
    protected override BrowserType BrowserType => BrowserType.Chromium;

    public ChromiumTests(ITestOutputHelper output) : base(output) { }

    [Fact]
    public async Task FilterBar_WorksInChrome()
    {
        await NavigateAsync("/blog");

        await Page.Locator("#LanguageDropDown button").ClickAsync();
        await Page.Locator("#LanguageDropDown li:has-text('Spanish')").ClickAsync();

        var url = Page.Url;
        Assert.Contains("language=es", url);
    }
}

public class FirefoxTests : PlaywrightTestBase
{
    protected override BrowserType BrowserType => BrowserType.Firefox;

    public FirefoxTests(ITestOutputHelper output) : base(output) { }

    [Fact]
    public async Task FilterBar_WorksInFirefox()
    {
        // Same test, different browser!
        await NavigateAsync("/blog");

        await Page.Locator("#LanguageDropDown button").ClickAsync();
        await Page.Locator("#LanguageDropDown li:has-text('Spanish')").ClickAsync();

        var url = Page.Url;
        Assert.Contains("language=es", url);
    }
}

public class SafariTests : PlaywrightTestBase
{
    protected override BrowserType BrowserType => BrowserType.Webkit;

    public SafariTests(ITestOutputHelper output) : base(output) { }

    [Fact]
    public async Task FilterBar_WorksInSafari()
    {
        await NavigateAsync("/blog");

        await Page.Locator("#LanguageDropDown button").ClickAsync();
        await Page.Locator("#LanguageDropDown li:has-text('Spanish')").ClickAsync();

        var url = Page.Url;
        Assert.Contains("language=es", url);
    }
}
```

## Tests de rédaction - La façon d’écrire

Let's voir des exemples réels montrant les avantages de Playwright's

### Tester HTMX avec Auto-Waiting

```csharp
[Fact]
public async Task SortingDropdown_UpdatesContentViaHTMX()
{
    // Arrange
    await NavigateAsync("/blog");

    // Get first post title before sorting
    var firstPostBefore = await Page.Locator("article h2 a").First.TextContentAsync();
    Output.WriteLine($"First post before: {firstPostBefore}");

    // Act - Change sort order
    await Page.Locator("#orderSelect").SelectOptionAsync("date_asc");

    // Playwright automatically waits for HTMX to update the DOM!
    await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);

    // Assert - Content should have changed
    var firstPostAfter = await Page.Locator("article h2 a").First.TextContentAsync();
    Output.WriteLine($"First post after: {firstPostAfter}");

    var selectValue = await Page.Locator("#orderSelect").InputValueAsync();
    Assert.Equal("date_asc", selectValue);
}
```

### Évaluation des interactions Alpine.js

Méthodes de manipulation [Alpine.js](https://alpinejs.dev/) réactivité et animations sans fil:

```csharp
[Fact]
public async Task AlpineDropdown_OpensAndCloses()
{
    await NavigateAsync("/blog");

    // Click to open dropdown (Alpine.js controlled)
    await Page.Locator("#LanguageDropDown button").ClickAsync();

    // Wait for Alpine.js animation
    await Page.WaitForSelectorAsync("#LanguageDropDown div[x-show]", new()
    {
        State = WaitForSelectorState.Visible
    });

    // Check dropdown is visible
    var isVisible = await Page.Locator("#LanguageDropDown div[x-show]").IsVisibleAsync();
    Assert.True(isVisible);

    // Click outside to close
    await Page.Locator("body").ClickAsync(new LocatorClickOptions
    {
        Position = new Position { X = 0, Y = 0 }
    });

    // Verify closed
    await Page.WaitForSelectorAsync("#LanguageDropDown div[x-show]", new()
    {
        State = WaitForSelectorState.Hidden
    });

    isVisible = await Page.Locator("#LanguageDropDown div[x-show]").IsVisibleAsync();
    Assert.False(isVisible);
}
```

### Évaluation des formulaires avec validation

```csharp
[Fact]
public async Task CommentForm_ValidatesRequiredFields()
{
    await NavigateAsync("/blog/some-post");

    // Try to submit without filling fields
    await Page.Locator("#comment-submit").ClickAsync();

    // Check HTML5 validation messages
    var nameInput = Page.Locator("#comment-name");
    var validationMessage = await nameInput.EvaluateAsync<string>("el => el.validationMessage");

    Assert.NotEmpty(validationMessage);
    Output.WriteLine($"Validation message: {validationMessage}");

    // Fill form properly
    await nameInput.FillAsync("Test User");
    await Page.Locator("#comment-email").FillAsync("test@example.com");
    await Page.Locator("#comment-content").FillAsync("Great article!");

    // Submit
    await Page.Locator("#comment-submit").ClickAsync();

    // Wait for success
    await Page.Locator(".comment-success").WaitForAsync();
}
```

## Caractéristiques avancées du dramaturge

### Interception du réseau - Plus puissant que PuppeteerSharp

```csharp
[Fact]
public async Task PageLoadsWithoutImages_ToTestPerformance()
{
    // Block all image requests
    await Page.RouteAsync("**/*.{png,jpg,jpeg,gif,webp}", route => route.AbortAsync());

    await NavigateAsync("/blog");

    // Page should still function without images
    var title = await Page.Locator("h1").TextContentAsync();
    Assert.NotEmpty(title);
}

[Fact]
public async Task MockAPIResponse_ForTesting()
{
    // Intercept API calls and return mock data
    await Page.RouteAsync("**/api/posts", route => route.FulfillAsync(new()
    {
        Status = 200,
        ContentType = "application/json",
        Body = System.Text.Json.JsonSerializer.Serialize(new
        {
            posts = new[]
            {
                new { id = 1, title = "Mock Post 1" },
                new { id = 2, title = "Mock Post 2" }
            }
        })
    }));

    await NavigateAsync("/blog");

    // Should show mock data
    var posts = await Page.Locator(".post-title").CountAsync();
    Assert.Equal(2, posts);
}
```

### Trace Viewer - Des essais de débugging ont échoué

```csharp
public async Task InitializeAsync()
{
    Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
    Browser = await LaunchBrowserAsync();

    // Start tracing
    Context = await Browser.NewContextAsync();
    await Context.Tracing.StartAsync(new()
    {
        Screenshots = true,
        Snapshots = true,
        Sources = true
    });

    Page = await Context.NewPageAsync();
}

public async Task DisposeAsync()
{
    // Save trace on test completion
    var tracePath = Path.Combine("traces", $"{TestContext.TestName}.zip");
    await Context.Tracing.StopAsync(new()
    {
        Path = tracePath
    });

    // View trace with: playwright show-trace traces/TestName.zip

    await Page?.CloseAsync()!;
    await Context?.CloseAsync()!;
    await Browser?.CloseAsync()!;
    Playwright?.Dispose();
}
```

Ensuite, voir la trace:

```bash
playwright show-trace traces/TestName.zip
```

Ceci ouvre une interface montrant:

- Toutes les mesures que votre test a prises
- Screenshots à chaque étape
- Demandes de réseau
- Logs de la console
- DOM snapshots

Il'est absolument brillant pour le débugging.

### Screenshots sur le défaut

```csharp
public async Task DisposeAsync()
{
    // Take screenshot if test failed
    if (TestContext.CurrentTestOutcome != TestOutcome.Passed)
    {
        var screenshot = await Page.ScreenshotAsync();
        File.WriteAllBytes($"failure-{TestContext.TestName}.png", screenshot);
        Output.WriteLine($"Screenshot saved: failure-{TestContext.TestName}.png");
    }

    await Page?.CloseAsync()!;
    await Context?.CloseAsync()!;
    await Browser?.CloseAsync()!;
    Playwright?.Dispose();
}
```

### Emulation mobile

```csharp
[Fact]
public async Task BlogPage_WorksOnMobile()
{
    // Create context with mobile emulation
    var iPhone = Playwright.Devices["iPhone 13"];
    await using var context = await Browser.NewContextAsync(iPhone);
    await using var page = await context.NewPageAsync();

    await page.GotoAsync($"{BaseUrl}/blog");

    // Check mobile menu is visible
    var mobileMenu = page.Locator(".mobile-menu");
    await Expect(mobileMenu).ToBeVisibleAsync();

    // Desktop menu should be hidden
    var desktopMenu = page.Locator(".desktop-menu");
    await Expect(desktopMenu).Not.ToBeVisibleAsync();
}
```

L'auteur joue avec [descripteurs d'appareils](https://playwright.dev/dotnet/docs/emulation#devices) pour:

- iPhone 13,
- iPad, iPad Pro
- Samsung Galaxy, Pixel
- Et beaucoup plus

### Évaluation du mode foncé

```csharp
[Fact]
public async Task DarkMode_TogglesCorrectly()
{
    // Start with dark color scheme
    await using var context = await Browser.NewContextAsync(new()
    {
        ColorScheme = ColorScheme.Dark
    });
    await using var page = await context.NewPageAsync();

    await page.GotoAsync($"{BaseUrl}");

    // Check dark mode is active
    var isDark = await page.EvaluateAsync<bool>(
        "() => window.matchMedia('(prefers-color-scheme: dark)').matches"
    );
    Assert.True(isDark);

    // Check background color reflects dark mode
    var bgColor = await page.Locator("body").EvaluateAsync<string>(
        "el => getComputedStyle(el).backgroundColor"
    );
    Assert.Contains("rgb(0, 0, 0)", bgColor); // Dark background
}
```

## Playwright vs PuppeteerSharp - Side by Side

Voici' est le même test dans les deux bibliothèques pour montrer les différencesM SK1

### Version PuppeteerSharp

```csharp
[Fact]
public async Task FilterTest_PuppeteerSharp()
{
    await Page.GoToAsync("http://localhost:8080/blog");

    // Manual waiting required
    await Page.WaitForSelectorAsync("#LanguageDropDown button");
    await Page.ClickAsync("#LanguageDropDown button");

    // Wait for dropdown animation
    await Task.Delay(300);

    // Click Spanish option
    await Page.WaitForSelectorAsync("#LanguageDropDown li:nth-child(2) a");
    await Page.ClickAsync("#LanguageDropDown li:nth-child(2) a");

    // Wait for navigation
    await Task.Delay(500);

    // Check URL
    var url = Page.Url;
    Assert.Contains("language=", url);
}
```

### Version de Playwright

```csharp
[Fact]
public async Task FilterTest_Playwright()
{
    await Page.GotoAsync("http://localhost:8080/blog");

    // Auto-waiting built in
    await Page.Locator("#LanguageDropDown button").ClickAsync();

    // Click Spanish option (waits automatically for visibility)
    await Page.Locator("#LanguageDropDown li:has-text('Spanish')").ClickAsync();

    // Check URL (waits automatically for navigation)
    await Expect(Page).ToHaveURLAsync(new Regex(".*language=.*"));
}
```

Avis:

- Aucun manuel `WaitForSelectorAsync` d'un État membre
- Non `Task.Delay` d'un État membre
- Assertions plus propres avec `Expect`
- Plus lisible avec `has-text` Détermination de l'échantillon

## Production de PDF avec Playwright

Comme PuppeteerSharp, Le Playwright peut générer des PDFs . L’API est presque identique:

```csharp
public async Task<byte[]> GeneratePdfAsync(string url)
{
    await using var browser = await Playwright.Chromium.LaunchAsync();
    await using var page = await browser.NewPageAsync();
    await page.GotoAsync(url);

    return await page.PdfAsync(new()
    {
        Format = "A4",
        PrintBackground = true,
        Margin = new()
        {
            Top = "20mm",
            Right = "20mm",
            Bottom = "20mm",
            Left = "20mm"
        }
    });
}
```

Le même [PDF de l'article PuppeteerSharp](/blog/puppeteersharp-e2e-testing#pdf-generation-gotchas) Appliquez ici aussi.

## Quand choisir le dramaturge

```mermaid
graph TD
    A[Need E2E Testing?] --> B{Multi-browser required?}
    B -->|Yes| C[Playwright]
    B -->|No| D{Chrome only?}
    D -->|Yes| E[PuppeteerSharp or Playwright]
    D -->|No| C

    C --> F[Benefits]
    F --> F1[Test all major browsers]
    F --> F2[Better debugging tools]
    F --> F3[More reliable waiting]
    F --> F4[Mobile emulation]

    E --> G[PuppeteerSharp Benefits]
    G --> G1[Simpler setup]
    G --> G2[Smaller overhead]
    G --> G3[Chromium focused]

    style C stroke:#00aa00,stroke-width:3px
    style E stroke:#0066cc,stroke-width:2px
```

**Choisissez Playwright lorsque:**

- Vous avez besoin de tests multi-browser
- Vous voulez des outils de débugging meilleurs (trace viewer)
- Vous testez des applications Web modernes complexes
- Vous avez besoin d'une emulation mobile
- Vous voulez des tests plus fiables hors de la boîte

**Choisissez PuppeteerSharp lorsque:**

- Chrome/L'Edge seulement est bon
- Vous voulez une configuration plus simple
- Vous' êtes déjà familiers avec Puppeteer
- Vous avez besoin de moins de ressources supplémentaires

## Fonctionnement dans CI/CD

### Exemple d'actions de GitHub

Le playwright travaille sans fil dans les pipelines de CI/CDM SK1C'est ici [Actions de GitHub](https://github.com/features/actions) flux de travail:

```yaml
name: Playwright Tests

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3

    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '9.0.x'

    - name: Install dependencies
      run: dotnet restore

    - name: Build
      run: dotnet build --no-restore

    - name: Install Playwright browsers
      run: pwsh Mostlylucid.Test/bin/Debug/net9.0/playwright.ps1 install --with-deps

    - name: Start application
      run: |
        dotnet run --project Mostlylucid/Mostlylucid.csproj &
        echo $! > app.pid

    - name: Wait for application
      run: |
        timeout 60 bash -c 'until curl -f http://localhost:8080/health; do sleep 2; done'

    - name: Run Playwright tests
      run: |
        dotnet test Mostlylucid.Test/Mostlylucid.Test.csproj \
          --filter "Category=E2E" \
          --logger "console;verbosity=detailed"

    - name: Upload test results
      if: always()
      uses: actions/upload-artifact@v3
      with:
        name: playwright-results
        path: |
          **/test-results/
          **/traces/

    - name: Stop application
      if: always()
      run: kill $(cat app.pid) || true
```

### Soutien du docker

Exécuter les tests de Playwright dans [Docker](https://www.docker.com/) Il faut installer des dépendances de système:

```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build

# Install Playwright dependencies
RUN apt-get update && apt-get install -y \
    libnss3 \
    libnspr4 \
    libatk1.0-0 \
    libatk-bridge2.0-0 \
    libcups2 \
    libdrm2 \
    libxkbcommon0 \
    libxcomposite1 \
    libxdamage1 \
    libxfixes3 \
    libxrandr2 \
    libgbm1 \
    libasound2

WORKDIR /app
COPY . .

RUN dotnet restore
RUN dotnet build
RUN pwsh Mostlylucid.Test/bin/Debug/net9.0/playwright.ps1 install

CMD ["dotnet", "test"]
```

## Comparaison du rendement

De mes essais sur ce blog:

| Caractéristique | PuppeteerSharp MSC2 Playwright MSC3
|---------|---------------|------------|
| **La vitesse d'essai Chrome** |  ~2.5s
| **Multi-browser** | ❌ ≥| ≤✓ (adds ~1s par navigateur
| **Auto-attendant la fiabilité** | Bonne (manuelle) | Excellente MSC4automatiqueMSc5 |
| **Mémoire par navigateur** |  ~150MB
| **Complexité d'installation** | simples | modérées |
| **Outils de débugging** | Chrome DevTools | Trace Viewer
| **Émulation mobile** | Basic | Excellent |

Playwright est légèrement plus lente et utilise un peu plus de mémoire, mais les avantages liés à la fiabilité et au débugging sont habituellement supérieurs à ceux qui y sont associés.

## Gotchas communs

### 1. Mode strict

Playwright'les locateurs sont stricts par défaut:

```csharp
// This errors if multiple buttons exist
await Page.Locator("button").ClickAsync();

// Be specific
await Page.Locator("button#submit").ClickAsync();

// Or use .First if you really want the first match
await Page.Locator("button").First.ClickAsync();
```

### 2. Installation du navigateur

Don' ne pas oublier de courir `playwright install` après l’ajout du paquet. Les navigateurs ne sont pas inclus dans le paquet NuGet'

### 3. Contexte vs Page

Rappeler les contextes isolant les tests:

```csharp
// Bad - state leaks between tests
await Page.GotoAsync("/login");
// Do stuff...
await Page.GotoAsync("/dashboard");
// Previous login state might still exist

// Good - fresh context per test
await using var context = await Browser.NewContextAsync();
await using var page = await context.NewPageAsync();
// Completely isolated
```

### 4. Différences de la trousse Web

Safari (WebKit) peut se comporter différemment

```csharp
// May work in Chrome but fail in WebKit
await Page.Locator(".dropdown").Hover();
await Page.Locator(".dropdown-item").ClickAsync();

// More reliable across browsers
await Page.Locator(".dropdown").ClickAsync();
await Page.WaitForSelectorAsync(".dropdown-item");
await Page.Locator(".dropdown-item").ClickAsync();
```

## Conclusion

Playwright représente l’évolution de l’automatisation des navigateurs pour les développeurs .NETM SK1 Si vous avez besoin d’un support multilatéral -browser [Écarpe de marionnettes](/blog/puppeteersharp-e2e-testing), mais les avantages sont substantielsM SK1

- Tester tous les principaux navigateurs avec un code identique
- Une plus grande fiabilité grâce à l'attente automatique
- Excellente débugging avec trace viewer
- Emulation mobile et tablette hors de la boîte
- Une plus grande résilience aux problèmes liés au calendrier

**Ma recommandation:**

- **Nouveaux projets nécessitant des tests croisés de navigateur**: Démarrer par Playwright
- **Chrome-projets seulement**: PuppeteerSharp est plus simple
- **Projets existants de PuppeteerSharp**: Migrant seulement si vous avez besoin d’un navigateur multi-
- **Applications Web modernes complexes**: PlaywrightThe tooling of ' is worth the overhead

Le seul lecteur de trace m’a sauvé des heures de débugging. Être en mesure de répéter un essai manquant et de voir exactement ce que le navigateur a vu à chaque étape est inestimable.

Donnez à Playwright un coup d’oeil sur votre prochain projet - vousM SK1 vous serez agréablement surpris de la facilité qu’il permet de faire le croisement

## Autres lectures

- [Playwright pour la documentation .NET](https://playwright.dev/dotnet/)
- [Playwright API Référence](https://playwright.dev/dotnet/docs/api/class-playwright)
- [PuppeteerSharp vs Playwright](/blog/puppeteersharp-e2e-testing)
- [Guide de l'aperçu des traces](https://playwright.dev/dotnet/docs/trace-viewer)
- [Contexte du navigateur expliqué](https://playwright.dev/dotnet/docs/browser-contexts)