How to run multi-tab automation on Firefox using NodeJS

by Ben Malka

As a part of the agile software development practice, the growing demand for fast and reliable CI/CD processes has led to the growing demand to speed up the automation testing processes. And as far as Web Development goes, what better way to make your E2E tests run faster than running parallel tests on different tabs?

In Chrome, this form of parallelization is supported out of the box when using Puppeteer, which uses the native ChromeDevtoolsProtocol, but in Firefox, not so much. Microsoft’s Playwright library supports this partially, but since it uses a custom build of Firefox beta, it limits your choices for other browser versions.

In this article, I’m suggesting a full NodeJS solution to the problem using an Express server and Foxdriver, a Firefox High-Level API Node library which I contribute to myself.

The solution briefly explained

The solution I’m about to show uses very simple Vanilla JS code, that runs on the browser itself, which will help us manage our multi-tab environment. Basically, we will be using the Foxdriver library to launch our browser, browse to a locally hosted page (On the Express Server), and execute several JS commands on that “TabManager” page. The running JS code on that page will launch new tabs using the and window.close JavaScript APIs, which will be later retrieved, again, by the Foxdriver library.

Setting up the Express Server

As mentioned above, the Express Server is being used to serve a locally hosted page, which will manage our newly opened tabs. In addition, new tabs will first browse to a different locally hosted path, that will set a unique id to each page for later retrieval and recognition by the Foxdriver.

In order to make it easier for you to follow along, this is my folder structure:

│ ├───index.js
│ └───static
│ └───index.html

Let’s set up the “TabManager” page. In express-server/static/index.html I’m placing a single Javascript block to initiate our managing APIs:


As can be seen, we have two functions on the page: createTab(tabId: String) and closeTab(tabId: String). createTab will launch a new tab by using and will place the returning window object in the tabs Map under the tabId key. This will be used later for closing the tab.

Another thing to notice is the browsing location. As mentioned above, the unique id of the tab is being placed in the browsing URL (i.e. http://localhost:5325/firefox/somerandomid ) to be later viewed by the Foxdriver

Now let’s set up the server. In express-server/index.js we first need to serve the static “TabManager” HTML file. In addition, we need to set the /firefox/:tabId path

If you are somewhat familiar with Express servers, you might find it odd that I’m exporting the app and not starting listening to a port. The reason for this will be revealed in the next section when we will discuss the Foxdriver code.

Making it work

Here we will view the Foxdriver code for linking it all together. First, let’s view the startServer function, it will be placed in the index.js file in the root directory

Now, the reason for exporting the app makes sense. The startServer function will return the randomly generated port for later use.

For our main part, let’s see the use of this function together with the Foxdriver APIs

So far I have launched a browser with one tab, browsed to http://localhost/, and retrieved the page’s title. I’ve added the custom pref “dom.disable_open_during_load” to enable popups which will be very handy in a second.

The openTab function is where the magic happens. Inside, I’m using the evaluateJSAsync API of the Foxdriver’s tab object to run a JS command on the “TabManager” page. Then, I’m using the browser.listTabs() API, again, from Foxdriver, to list all of the currently opened tabs on the Firefox browser. And by looking at each tab's URL I’m able to retrieve the correct one back to the user.

And the code addition in the anonymous async function

Few final notes

2. Due to performance reasons, I’d highly recommend not using more than 5 tabs open at the same time. But, with that being said, you can always use machines with more memory.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store