Table of Contents

Emulation Module

The Emulation module provides functionality for emulating device characteristics and media features in the browser.

Overview

The Emulation module allows you to:

  • Set user agent strings
  • Override forced colors mode theme (light/dark)
  • Override geolocation
  • Emulate timezone and locale settings
  • Override media features (e.g., prefers-color-scheme, prefers-reduced-motion)
  • Emulate network conditions (e.g., offline)
  • Override screen orientation (portrait/landscape)
  • Override screen settings (dimensions)
  • Override JavaScript enabled state
  • Override scrollbar type (classic/overlay)
  • Override text layout mode (mobile text autosizing)
  • Override touch capability (max touch points)
  • Override viewport meta tag handling

Accessing the Module

EmulationModule emulation = driver.Emulation;

Scoping

Every command in this module takes Contexts and UserContexts collections that limit the override to particular browsing contexts or user contexts. Leaving both empty means "not specified", and the two groups of commands treat that differently:

Behavior when unscoped Commands
Applies globally, becoming the default for new contexts setUserAgentOverride, setForcedColorsModeThemeOverride, setGeolocationOverride, setMediaFeaturesOverride, setNetworkConditions, setScrollbarTypeOverride, setTextLayoutModeOverride, setTouchOverride, setViewportMetaOverride
Rejected with invalid argument setLocaleOverride, setTimezoneOverride, setScreenSettingsOverride, setScreenOrientationOverride, setScriptingEnabled

For the second group the specification requires a scope, so add at least one browsing context or user context before executing the command.

The two scopes are mutually exclusive. A command that names both browsing contexts and user contexts is rejected with invalid argument, so scope each command one way or the other. The same holds outside this module wherever a command offers both, such as browsingContext.setViewport (whose BrowsingContextId and UserContexts cannot be combined), browsingContext.setBypassCSP, network.setExtraHeaders and script.addPreloadScript.

Timeout and Cancellation

All commands in this module accept optional timeoutOverride and CancellationToken parameters. Use timeoutOverride to set a per-command timeout (defaults to BiDiDriver.DefaultCommandTimeout when omitted). Use CancellationToken for cooperative cancellation. See the API Design Guide for details and examples.

Viewport Emulation

Viewport emulation (setting viewport dimensions, device pixel ratio, and common device presets) is provided by the BrowsingContext module, not the Emulation module. See the Browsing Context Module guide for details and code examples.

User Agent Override

Set Custom User Agent

SetUserAgentOverrideCommandParameters parameters = new SetUserAgentOverrideCommandParameters
{
    UserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1",
    Contexts = { contextId }
};

await driver.Emulation.SetUserAgentOverrideAsync(parameters);
Console.WriteLine("User agent set to iPhone");

Common User Agents

// Mobile Safari (iPhone)
string iPhoneUA = "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1";

// Mobile Chrome (Android)
string androidUA = "Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Mobile Safari/537.36";

// Desktop Safari (macOS)
string safariUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15";

// Desktop Firefox
string firefoxUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0";

await driver.Emulation.SetUserAgentOverrideAsync(
    new SetUserAgentOverrideCommandParameters
    {
        UserAgent = iPhoneUA,
        Contexts = { contextId }
    });

Clear User Agent Override

SetUserAgentOverrideCommandParameters parameters =
    SetUserAgentOverrideCommandParameters.ResetUserAgentOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetUserAgentOverrideAsync(parameters);
Console.WriteLine("User agent override cleared");

Forced Colors Mode Theme

The Emulation module provides SetForcedColorsModeThemeOverrideAsync to emulate forced colors mode (the forced-colors CSS media feature) with a light or dark theme. To emulate ordinary light or dark mode — the prefers-color-scheme media feature — use the media features override instead.

Emulate Forced Colors Mode

SetForcedColorsModeThemeOverrideCommandParameters parameters =
    new SetForcedColorsModeThemeOverrideCommandParameters
    {
        Theme = ForcedColorsModeTheme.Dark,
        Contexts = { contextId }
    };

await driver.Emulation.SetForcedColorsModeThemeOverrideAsync(parameters);
Console.WriteLine("Forced colors mode enabled with a dark theme");

EvaluateResult result = await driver.Script.EvaluateAsync(
    new EvaluateCommandParameters(
        "window.matchMedia('(forced-colors: active)').matches",
        new ContextTarget(contextId),
        true));

if (result is EvaluateResultSuccess success)
{
    bool isForcedColors = success.Result.As<BooleanRemoteValue>().Value;
    Console.WriteLine($"Forced colors active: {isForcedColors}");
}

Clear Forced Colors Override

// Use reset property of command parameters to clear the override
SetForcedColorsModeThemeOverrideCommandParameters parameters =
    SetForcedColorsModeThemeOverrideCommandParameters.ResetForcedColorsModeThemeOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetForcedColorsModeThemeOverrideAsync(parameters);
Console.WriteLine("Forced colors override cleared");

Geolocation Override

Set Location

SetGeolocationOverrideCoordinatesCommandParameters parameters =
    new SetGeolocationOverrideCoordinatesCommandParameters
    {
        Coordinates = new GeolocationCoordinates(37.7749, -122.4194) { Accuracy = 100 },
        Contexts = { contextId }
    };

await driver.Emulation.SetGeolocationOverrideAsync(parameters);
Console.WriteLine("Geolocation set to San Francisco");

// Verify location in page
EvaluateResult result = await driver.Script.EvaluateAsync(
    new EvaluateCommandParameters(
        @"new Promise((resolve) => {
            navigator.geolocation.getCurrentPosition(
                (pos) => resolve({
                    lat: pos.coords.latitude,
                    lng: pos.coords.longitude
                })
            );
        })",
        new ContextTarget(contextId),
        true));

if (result is EvaluateResultSuccess success &&
    success.Result.As<KeyValuePairCollectionRemoteValue>().Value is RemoteValueDictionary location)
{
    Console.WriteLine($"Browser location: {location["lat"].As<NumberRemoteValue>().Value}, {location["lng"].As<NumberRemoteValue>().Value}");
}

Common Locations

// New York
await driver.Emulation.SetGeolocationOverrideAsync(
    new SetGeolocationOverrideCoordinatesCommandParameters
    {
        Coordinates = new GeolocationCoordinates(40.7128, -74.0060) { Accuracy = 100 },
        Contexts = { contextId }
    });

// London
await driver.Emulation.SetGeolocationOverrideAsync(
    new SetGeolocationOverrideCoordinatesCommandParameters
    {
        Coordinates = new GeolocationCoordinates(51.5074, -0.1278) { Accuracy = 100 },
        Contexts = { contextId }
    });

// Tokyo
await driver.Emulation.SetGeolocationOverrideAsync(
    new SetGeolocationOverrideCoordinatesCommandParameters
    {
        Coordinates = new GeolocationCoordinates(35.6762, 139.6503) { Accuracy = 100 },
        Contexts = { contextId }
    });

Clear Geolocation Override

SetGeolocationOverrideCommandParameters parameters =
    SetGeolocationOverrideCommandParameters.ResetGeolocationOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetGeolocationOverrideAsync(parameters);
Console.WriteLine("Geolocation override cleared");

Simulate a Position Error

To make the page's location requests fail as if the device could not determine its position, send the error form of the override, SetGeolocationOverrideErrorCommandParameters. Its Error is a GeolocationPositionError, whose Type is always positionUnavailable, the only error the specification defines. The page's getCurrentPosition and watchPosition calls then receive the POSITION_UNAVAILABLE error (code 2):

SetGeolocationOverrideErrorCommandParameters parameters = new SetGeolocationOverrideErrorCommandParameters();
parameters.Contexts.Add(contextId);

await driver.Emulation.SetGeolocationOverrideAsync(parameters);

// The page's location request now fails with POSITION_UNAVAILABLE.
EvaluateResult result = await driver.Script.EvaluateAsync(
    new EvaluateCommandParameters(
        @"new Promise((resolve) => {
            navigator.geolocation.getCurrentPosition(
                () => resolve('position received'),
                (error) => resolve('error code ' + error.code)
            );
        })",
        new ContextTarget(contextId),
        true));

if (result is EvaluateResultSuccess success)
{
    Console.WriteLine(success.Result.As<StringRemoteValue>().Value); // "error code 2"
}

Timezone Emulation

Set Timezone

SetTimeZoneOverrideCommandParameters parameters = new SetTimeZoneOverrideCommandParameters
{
    TimeZone = "America/Los_Angeles",
    Contexts = { contextId }
};

await driver.Emulation.SetTimeZoneOverrideAsync(parameters);
Console.WriteLine("Timezone set to Pacific Time");

// Verify timezone
EvaluateResult result = await driver.Script.EvaluateAsync(
    new EvaluateCommandParameters(
        "Intl.DateTimeFormat().resolvedOptions().timeZone",
        new ContextTarget(contextId),
        true));

if (result is EvaluateResultSuccess success)
{
    string timezone = success.Result.As<StringRemoteValue>().Value;
    Console.WriteLine($"Browser timezone: {timezone}");
}

Common Timezones

// Pacific Time (US West Coast)
await driver.Emulation.SetTimeZoneOverrideAsync(
    new SetTimeZoneOverrideCommandParameters
    {
        TimeZone = "America/Los_Angeles",
        Contexts = { contextId }
    });

// Eastern Time (US East Coast)
await driver.Emulation.SetTimeZoneOverrideAsync(
    new SetTimeZoneOverrideCommandParameters
    {
        TimeZone = "America/New_York",
        Contexts = { contextId }
    });

// UTC
await driver.Emulation.SetTimeZoneOverrideAsync(
    new SetTimeZoneOverrideCommandParameters
    {
        TimeZone = "UTC",
        Contexts = { contextId }
    });

// Tokyo
await driver.Emulation.SetTimeZoneOverrideAsync(
    new SetTimeZoneOverrideCommandParameters
    {
        TimeZone = "Asia/Tokyo",
        Contexts = { contextId }
    });

Clear Timezone Override

SetTimeZoneOverrideCommandParameters parameters =
    SetTimeZoneOverrideCommandParameters.ResetTimeZoneOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetTimeZoneOverrideAsync(parameters);
Console.WriteLine("Timezone override cleared");

Locale Emulation

Set Locale

SetLocaleOverrideCommandParameters parameters = new SetLocaleOverrideCommandParameters
{
    Locale = "fr-FR",
    Contexts = { contextId }
};

await driver.Emulation.SetLocaleOverrideAsync(parameters);
Console.WriteLine("Locale set to French");

// Verify locale
EvaluateResult result = await driver.Script.EvaluateAsync(
    new EvaluateCommandParameters(
        "navigator.language",
        new ContextTarget(contextId),
        true));

if (result is EvaluateResultSuccess success)
{
    string locale = success.Result.As<StringRemoteValue>().Value;
    Console.WriteLine($"Browser locale: {locale}");
}

Clear Locale Override

SetLocaleOverrideCommandParameters parameters =
    SetLocaleOverrideCommandParameters.ResetLocaleOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetLocaleOverrideAsync(parameters);
Console.WriteLine("Locale override cleared");

Network Conditions Emulation

The Emulation module provides SetNetworkConditionsAsync to emulate network conditions such as offline mode. This is useful for testing how your application behaves when the network is unavailable.

Emulate Offline

SetNetworkConditionsCommandParameters parameters = new SetNetworkConditionsCommandParameters
{
    NetworkConditions = new NetworkConditionsOffline(),
    Contexts = { contextId }
};

await driver.Emulation.SetNetworkConditionsAsync(parameters);
Console.WriteLine("Network set to offline");

Clear Network Conditions Override

SetNetworkConditionsCommandParameters parameters =
    SetNetworkConditionsCommandParameters.ResetNetworkConditions;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetNetworkConditionsAsync(parameters);
Console.WriteLine("Network conditions override cleared");

Screen Orientation Override

The Emulation module provides SetScreenOrientationOverrideAsync to emulate different screen orientations (portrait, landscape). This is useful for testing mobile device behavior.

Set Portrait Orientation

SetScreenOrientationOverrideCommandParameters parameters =
    new SetScreenOrientationOverrideCommandParameters
    {
        ScreenOrientation = new ScreenOrientation(
            ScreenOrientationNatural.Portrait,
            ScreenOrientationType.PortraitPrimary),
        Contexts = { contextId }
    };

await driver.Emulation.SetScreenOrientationOverrideAsync(parameters);
Console.WriteLine("Screen orientation set to portrait");

Set Landscape Orientation

SetScreenOrientationOverrideCommandParameters parameters =
    new SetScreenOrientationOverrideCommandParameters
    {
        ScreenOrientation = new ScreenOrientation(
            ScreenOrientationNatural.Landscape,
            ScreenOrientationType.LandscapePrimary),
        Contexts = { contextId }
    };

await driver.Emulation.SetScreenOrientationOverrideAsync(parameters);
Console.WriteLine("Screen orientation set to landscape");

Clear Screen Orientation Override

SetScreenOrientationOverrideCommandParameters parameters =
    SetScreenOrientationOverrideCommandParameters.ResetScreenOrientationOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetScreenOrientationOverrideAsync(parameters);
Console.WriteLine("Screen orientation override cleared");

Screen Settings Override

The Emulation module provides SetScreenSettingsOverrideAsync to emulate screen dimensions (width and height). This can be used to simulate different display sizes.

Set Screen Dimensions

SetScreenSettingsOverrideCommandParameters parameters =
    new SetScreenSettingsOverrideCommandParameters
    {
        ScreenArea = new ScreenArea { Width = 1920, Height = 1080 },
        Contexts = { contextId }
    };

await driver.Emulation.SetScreenSettingsOverrideAsync(parameters);
Console.WriteLine("Screen dimensions set to 1920x1080");

Clear Screen Settings Override

SetScreenSettingsOverrideCommandParameters parameters =
    SetScreenSettingsOverrideCommandParameters.ResetScreenSettingsOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetScreenSettingsOverrideAsync(parameters);
Console.WriteLine("Screen settings override cleared");

JavaScript Override

The Emulation module provides SetScriptingEnabledAsync to disable JavaScript for specified contexts. Note that this is primarily useful for simulating the case where JavaScript is disabled; the browser typically has JavaScript enabled by default.

Disable JavaScript

SetScriptingEnabledCommandParameters parameters = new SetScriptingEnabledCommandParameters
{
    IsScriptingEnabled = false,
    Contexts = { contextId }
};

await driver.Emulation.SetScriptingEnabledAsync(parameters);
Console.WriteLine("JavaScript disabled");

Clear Scripting Override

SetScriptingEnabledCommandParameters parameters =
    SetScriptingEnabledCommandParameters.ResetScriptingEnabled;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetScriptingEnabledAsync(parameters);
Console.WriteLine("Scripting override cleared");

Scrollbar Type Override

The Emulation module provides SetScrollbarTypeOverrideAsync to emulate different scrollbar types (classic or overlay). This is useful for testing how your application handles different scrollbar behaviors across platforms.

Set Overlay Scrollbars

SetScrollbarTypeOverrideCommandParameters parameters =
    new SetScrollbarTypeOverrideCommandParameters
    {
        ScrollbarType = ScrollbarType.Overlay,
        Contexts = { contextId }
    };

await driver.Emulation.SetScrollbarTypeOverrideAsync(parameters);
Console.WriteLine("Scrollbar type set to overlay");

Clear Scrollbar Type Override

SetScrollbarTypeOverrideCommandParameters parameters =
    SetScrollbarTypeOverrideCommandParameters.ResetScrollbarTypeOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetScrollbarTypeOverrideAsync(parameters);
Console.WriteLine("Scrollbar type override cleared");

Text Layout Mode Override

The Emulation module provides SetTextLayoutModeOverrideAsync to emulate the text layout mode a mobile browser uses. The specification defines a single mode, TextLayoutMode.Mobile, which turns on text autosizing (also called font inflation): the browser enlarges blocks of text so they stay readable in a narrow viewport without the user zooming in. Pages usually control this behavior through the text-size-adjust CSS property, so the override is useful for testing how that property and your text sizing behave on mobile devices.

A browser that does not support text layout mode emulation rejects a Mobile override with an unsupported operation error, which surfaces as a WebDriverBiDiCommandException whose ErrorCode is ErrorCode.UnsupportedOperation. Clearing the override never raises that error, and returns the page to the browser's default text layout mode.

Set Mobile Text Layout Mode

SetTextLayoutModeOverrideCommandParameters parameters =
    new SetTextLayoutModeOverrideCommandParameters()
    {
        TextLayoutMode = TextLayoutMode.Mobile,
        Contexts = { contextId }
    };

await driver.Emulation.SetTextLayoutModeOverrideAsync(parameters);
Console.WriteLine("Text layout mode set to mobile");

Clear Text Layout Mode Override

SetTextLayoutModeOverrideCommandParameters parameters =
    SetTextLayoutModeOverrideCommandParameters.ResetTextLayoutModeOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetTextLayoutModeOverrideAsync(parameters);
Console.WriteLine("Text layout mode override cleared");

Touch Override

The Emulation module provides SetTouchOverrideAsync to emulate touch capability by setting the maximum number of touch points. This is useful for testing touch-enabled interfaces on devices that may not have native touch support.

Enable Touch Emulation

SetTouchOverrideCommandParameters parameters = new SetTouchOverrideCommandParameters
{
    MaxTouchPoints = 5,
    Contexts = { contextId }
};

await driver.Emulation.SetTouchOverrideAsync(parameters);
Console.WriteLine("Touch emulation enabled with 5 touch points");

Clear Touch Override

SetTouchOverrideCommandParameters parameters =
    SetTouchOverrideCommandParameters.ResetTouchOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetTouchOverrideAsync(parameters);
Console.WriteLine("Touch override cleared");

Media Features Override

The Emulation module provides SetMediaFeaturesOverrideAsync to override CSS media features such as prefers-color-scheme and prefers-reduced-motion, allowing you to test styles and behavior that respond to media queries without changing operating system settings.

Set Media Features

SetMediaFeaturesOverrideCommandParameters parameters = new SetMediaFeaturesOverrideCommandParameters
{
    Features = new MediaFeatures
    {
        PrefersColorScheme = PrefersColorSchemeMediaFeatureValue.Dark,
        PrefersReducedMotion = PrefersReducedMotionMediaFeatureValue.Reduce
    },
    Contexts = { contextId }
};

await driver.Emulation.SetMediaFeaturesOverrideAsync(parameters);
Console.WriteLine("Media features overridden: dark color scheme, reduced motion");

Clear Media Features Override

SetMediaFeaturesOverrideCommandParameters parameters =
    SetMediaFeaturesOverrideCommandParameters.ResetMediaFeaturesOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetMediaFeaturesOverrideAsync(parameters);
Console.WriteLine("Media features override cleared");

Viewport Meta Override

The Emulation module provides SetViewportMetaOverrideAsync to override how the browser honors the page's <meta name="viewport"> tag, which is useful when testing mobile layouts.

Set Viewport Meta Override

SetViewportMetaOverrideCommandParameters parameters = new SetViewportMetaOverrideCommandParameters
{
    IsViewportMetaOverridden = true,
    Contexts = { contextId }
};

await driver.Emulation.SetViewportMetaOverrideAsync(parameters);
Console.WriteLine("Viewport meta override enabled");

Clear Viewport Meta Override

SetViewportMetaOverrideCommandParameters parameters =
    SetViewportMetaOverrideCommandParameters.ResetViewportMetaOverride;
parameters.Contexts.Add(contextId);

await driver.Emulation.SetViewportMetaOverrideAsync(parameters);
Console.WriteLine("Viewport meta override cleared");

Common Patterns

Pattern: Mobile Device Emulation

public async Task EmulateMobileDevice(
    BiDiDriver driver,
    string contextId,
    string deviceName)
{
    switch (deviceName.ToLower())
    {
        case "iphone":
            await driver.BrowsingContext.SetViewportAsync(
                new SetViewportCommandParameters
                {
                    BrowsingContextId = contextId,
                    Viewport = new Viewport { Width = 390, Height = 844 },
                    DevicePixelRatio = 3.0
                });

            await driver.Emulation.SetUserAgentOverrideAsync(
                new SetUserAgentOverrideCommandParameters
                {
                    UserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1",
                    Contexts = { contextId }
                });
            break;

        case "android":
            await driver.BrowsingContext.SetViewportAsync(
                new SetViewportCommandParameters
                {
                    BrowsingContextId = contextId,
                    Viewport = new Viewport { Width = 412, Height = 915 },
                    DevicePixelRatio = 2.625
                });

            await driver.Emulation.SetUserAgentOverrideAsync(
                new SetUserAgentOverrideCommandParameters
                {
                    UserAgent = "Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Mobile Safari/537.36",
                    Contexts = { contextId }
                });
            break;

        case "tablet":
            await driver.BrowsingContext.SetViewportAsync(
                new SetViewportCommandParameters
                {
                    BrowsingContextId = contextId,
                    Viewport = new Viewport { Width = 1024, Height = 1366 },
                    DevicePixelRatio = 2.0
                });
            break;
    }

    Console.WriteLine($"Emulating {deviceName}");
}
// Usage
await EmulateMobileDevice(driver, contextId, "iphone");

Pattern: Responsive Testing

// Test at multiple viewport sizes
List<(int Width, int Height, string Name)> viewports = new()
{
    (320, 568, "Mobile Small"),
    (375, 667, "Mobile Medium"),
    (414, 896, "Mobile Large"),
    (768, 1024, "Tablet"),
    (1920, 1080, "Desktop")
};

foreach ((int Width, int Height, string Name) viewport in viewports)
{
    Console.WriteLine($"\nTesting {viewport.Name} ({viewport.Width}x{viewport.Height})");

    await driver.BrowsingContext.SetViewportAsync(
        new SetViewportCommandParameters
        {
            BrowsingContextId = contextId,
            Viewport = new Viewport { Width = (ulong)viewport.Width, Height = (ulong)viewport.Height },
            DevicePixelRatio = 1.0
        });

    // Take screenshot
    CaptureScreenshotCommandResult screenshot =
        await driver.BrowsingContext.CaptureScreenshotAsync(
            new CaptureScreenshotCommandParameters(contextId));

    byte[] imageBytes = Convert.FromBase64String(screenshot.Data);
    await File.WriteAllBytesAsync(
        $"screenshot-{viewport.Name.Replace(" ", "-")}.png",
        imageBytes);
}

Pattern: Dark Mode Testing

// Test both light and dark modes by overriding the prefers-color-scheme
// CSS media feature.
string[] colorSchemes = { "light", "dark" };

foreach (string scheme in colorSchemes)
{
    Console.WriteLine($"\nTesting {scheme} mode");

    PrefersColorSchemeMediaFeatureValue colorScheme = scheme == "dark" ? PrefersColorSchemeMediaFeatureValue.Dark : PrefersColorSchemeMediaFeatureValue.Light;
    await driver.Emulation.SetMediaFeaturesOverrideAsync(
        new SetMediaFeaturesOverrideCommandParameters
        {
            Features = new MediaFeatures
            {
                PrefersColorScheme = colorScheme
            },
            Contexts = { contextId }
        });

    // Navigate to page
    await driver.BrowsingContext.NavigateAsync(
        new NavigateCommandParameters(contextId, "https://example.com")
        { Wait = ReadinessState.Complete });

    // Verify color scheme applied
    EvaluateResult result = await driver.Script.EvaluateAsync(
        new EvaluateCommandParameters(
            "getComputedStyle(document.body).backgroundColor",
            new ContextTarget(contextId),
            true));

    if (result is EvaluateResultSuccess success)
    {
        string bgColor = success.Result.As<StringRemoteValue>().Value;
        Console.WriteLine($"Background color: {bgColor}");
    }

    // Take screenshot
    CaptureScreenshotCommandResult screenshot =
        await driver.BrowsingContext.CaptureScreenshotAsync(
            new CaptureScreenshotCommandParameters(contextId));

    byte[] imageBytes = Convert.FromBase64String(screenshot.Data);
    await File.WriteAllBytesAsync($"screenshot-{scheme}.png", imageBytes);
}

Pattern: Location-Based Testing

// Test application behavior in different locations
Dictionary<string, (double Lat, double Lng)> locations = new()
{
    { "New York", (40.7128, -74.0060) },
    { "London", (51.5074, -0.1278) },
    { "Tokyo", (35.6762, 139.6503) },
    { "Sydney", (-33.8688, 151.2093) }
};

foreach (KeyValuePair<string, (double Lat, double Lng)> location in locations)
{
    Console.WriteLine($"\nTesting from {location.Key}");

    await driver.Emulation.SetGeolocationOverrideAsync(
        new SetGeolocationOverrideCoordinatesCommandParameters
        {
            Coordinates = new GeolocationCoordinates(location.Value.Lat, location.Value.Lng) { Accuracy = 100 },
            Contexts = { contextId }
        });

    // Navigate to location-aware page
    await driver.BrowsingContext.NavigateAsync(
        new NavigateCommandParameters(contextId, "https://example.com/location")
        { Wait = ReadinessState.Complete });

    // Check location detection
    EvaluateResult result = await driver.Script.EvaluateAsync(
        new EvaluateCommandParameters(
            "document.querySelector('#detected-location')?.textContent",
            new ContextTarget(contextId),
            true));

    if (result is EvaluateResultSuccess success)
    {
        string? detectedLocation = success.Result.As<StringRemoteValue>().Value;
        Console.WriteLine($"Detected: {detectedLocation}");
    }
}

Best Practices

  1. Reset between tests: Clear emulation settings between test cases
  2. Match device characteristics: Set viewport, user agent, and pixel ratio together
  3. Test real devices: Emulation is useful but test on real devices when possible
  4. Verify settings: Check that emulation applied correctly
  5. Consider performance: Some emulations may affect performance

Limitations

  • Emulation is not perfect - some device-specific behaviors may not be replicated
  • Hardware features (camera, sensors) cannot be fully emulated
  • Performance characteristics differ from real devices
  • Some browser features may detect emulation

Common Issues

Viewport Not Changing

Problem: Viewport size doesn't change after setting.

Solution:

  • Ensure the browsing context is valid
  • Try refreshing the page after setting viewport
  • Check that the page doesn't override viewport settings

User Agent Not Applied

Problem: Websites detect wrong device despite user agent override.

Solution:

  • Set user agent before navigation
  • Also set viewport and device pixel ratio
  • Some sites use other detection methods (touch events, etc.)

Geolocation Permission Denied

Problem: Page can't access geolocation even after setting.

Solution:

  • Grant geolocation permission through browser settings
  • Use the Permissions module to grant access
  • Check browser console for permission errors

Next Steps