browserTest function

  1. @isTest
Future<void> browserTest(
  1. String description,
  2. Future<void> callback(
    1. Browser browser
    ), {
  3. BrowserType? browserType,
  4. bool? headless,
  5. List<String>? args,
  6. String? baseUrl,
  7. Duration? timeout,
  8. Duration? launchTimeout,
  9. Map<String, dynamic>? extraCapabilities,
  10. ProxyConfiguration? proxy,
  11. Device? device,
  12. bool useAsync = true,
})

Defines an individual browser test case using the package:test framework.

Creates an isolated browser test with automatic setup and teardown.

Sets up a test with a browser instance launched according to the provided configuration overrides or global defaults. After the test completes, the browser is automatically closed.

void main() async {
  // Configure default settings (e.g., default browser, headless mode)
  await testBootstrap(BrowserConfig(browserName: 'chromium', headless: true));

  // Simple test using defaults
  browserTest('user can log in', (browser) async {
    await browser.visit('/login');
    // ... assertions ...
  });

  // Test overriding the browser type and headless mode
  browserTest('run firefox visibly', (browser) async {
     await browser.visit('/');
    // ... assertions ...
  }, browserType: firefox, headless: false);
}

The description identifies the test in output. The callback receives the configured browser instance. Optional parameters like browserType, headless, baseUrl, timeout, device, etc., allow overriding the global configuration set by testBootstrap specifically for this test. The useAsync flag determines whether to use async or sync WebDriver API.

Implementation

@isTest
Future<void> browserTest(
  String description,
  Future<void> Function(Browser browser) callback, {
  BrowserType? browserType,
  bool? headless,
  List<String>? args,
  String? baseUrl,
  Duration? timeout, // Operation timeout for BrowserConfig
  Duration? launchTimeout, // Timeout for the launch itself
  Map<String, dynamic>? extraCapabilities, // Use core.Map prefix
  ProxyConfiguration? proxy,
  Device? device,
  bool useAsync = true,
}) async {
  tearDown(() async {
    print("Tearing down browser test: $description");
    await DriverManager.stopAll();
  });
  final skipReason = _headfulSkipReason(headless);
  test(description, () async {
    bool configOverridden = false;
    // Apply configuration overrides for this test if provided
    // Check if *any* override relevant to config needs pushing
    if (browserType != null ||
        headless != null ||
        baseUrl != null ||
        timeout != null ||
        proxy != null) {
      TestBootstrap.pushConfigOverride(
        // Pass browserName only if browserType is also explicitly provided
        browserName: browserType?.name,
        headless: headless,
        baseUrl: baseUrl,
        timeout: timeout,
        // This sets the default operation timeout
        proxy: proxy,
      );
      configOverridden = true;
    }

    try {
      // Ensure no stale WebDriver servers remain from prior tests
      await DriverManager.stopAll();
      // Use the potentially overridden currentConfig for launch decisions
      final config = TestBootstrap.currentConfig;
      // Determine BrowserType based on explicit param or potentially overridden config
      final type =
          browserType ?? TestBootstrap.browserTypes[config.browserName];
      if (type == null) {
        throw ArgumentError(
          'Could not find BrowserType for ${config.browserName}. Ensure testBootstrap is configured correctly and the browser name is supported.',
        );
      }

      // Create Launch Options: These are specific instructions for *this* launch,
      // potentially differing from the overridden `currentConfig` (e.g., args).
      final launchOptions = BrowserLaunchOptions(
        // Use the explicitly passed headless value for launch,
        // falling back to the *effective* current config's headless value
        headless: headless ?? config.headless,
        args: args,
        // Base URL for the browser instance comes from effective config,
        // but BrowserLaunchOptions could override if needed.
        baseUrl: config.baseUrl,
        timeout: launchTimeout,
        // Specific timeout for the launch process
        extraCapabilities: extraCapabilities,
        // Proxy for the browser instance comes from effective config
        proxy: config.proxy,
        device: device, // Pass the device parameter for emulation
        // channel, executablePath, env, slowMo could be added here if needed
      );

      // Launch the browser using the determined type and options
      // BrowserType.launch now handles creating the final BrowserConfig for runtime
      // and selects async/sync implementation based on useAsync
      final browser = await type.launch(launchOptions, useAsync: useAsync);

      // The launched 'browser' instance now contains a BrowserConfig reflecting
      // the state after launch (including overrides applied via pushConfigOverride).

      try {
        await callback(browser);
      } finally {
        // Browser interface uses FutureOr, so 'await' works for both sync/async quit
        await browser.quit();
      }
    } finally {
      // Always restore the original configuration if we pushed an override
      if (configOverridden) {
        TestBootstrap.popConfigOverride();
      }
    }
  }, skip: skipReason);
}