Heuristic:DevExpress Testcafe MacOS Browser Launch Serialization
| Knowledge Sources | |
|---|---|
| Domains | Platform_Compatibility, Browser_Automation |
| Last Updated | 2026-02-12 03:30 GMT |
Overview
On macOS, browser launches must be serialized with a 500ms delay between each to prevent concurrent launch failures.
Description
macOS does not allow starting multiple instances of the same application simultaneously. The `BrowserStarter` class uses an `OperationsQueue` to serialize browser opening operations on macOS, with a 500ms post-operation delay between launches. On other platforms (Linux, Windows), browsers are launched concurrently without delay. This is particularly relevant when running tests with concurrency > 1, where multiple browser instances need to be opened for parallel test execution.
Usage
Be aware of this behavior when running concurrent tests on macOS development machines. Test startup time on macOS scales linearly with concurrency factor (each additional browser adds ~500ms+ to startup). On CI servers running Linux, this overhead does not apply. If macOS startup seems slow with high concurrency, this is expected behavior, not a bug.
The Insight (Rule of Thumb)
- Action: No configuration needed; serialization is automatic on macOS. For faster test startup on macOS, consider using headless mode or testing on Linux.
- Value: 500ms delay between browser launches on macOS.
- Trade-off: Serialized launch adds startup latency on macOS but prevents launch failures from concurrent open attempts. With concurrency of 4, expect ~2s extra startup time on macOS vs near-instant on Linux.
Reasoning
macOS's application model restricts concurrent launches of the same app bundle. Attempting to open multiple Chrome or Firefox instances simultaneously on macOS can cause "application is already opening" errors or duplicate instance failures. The 500ms delay provides sufficient time for the OS to register each browser launch before the next one begins. This was discovered through empirical testing and is documented in the source code comment.
Code Evidence
From `src/browser/provider/utils/browser-starter.js:3-37`:
const POST_OPERATION_DELAY = 500;
class OperationsQueue {
constructor () {
this.chainPromise = Promise.resolve();
}
executeOperation (operation) {
const operationPromise = this.chainPromise.then(operation);
this.chainPromise = operationPromise.then(() => delay(POST_OPERATION_DELAY));
return operationPromise;
}
}
export default class BrowserStarter {
constructor () {
// NOTE: You can't start multiple instances of the same app at the same time on macOS.
// That's why a queue of opening requests is needed.
this.macOSBrowserOpeningQueue = new OperationsQueue();
}
async startBrowser (...openArgs) {
const openBrowserOperation = () => browserTools.open(...openArgs);
if (OS.mac)
await this.macOSBrowserOpeningQueue.executeOperation(openBrowserOperation);
else
await openBrowserOperation();
}
}