browserTest function
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);
}