How to Perform Puppeteer Browser Automation With Node.js
Ini Arthur
Posted On: March 7, 2025
2360 Views
14 Min Read
As modern browser automation is on the rise, test automation frameworks (or libraries) such as Puppeteer have become a go-to choice for automating web browsers.
Puppeteer browser automation enables control of headless Chrome or Chromium through its API, leveraging the Chrome DevTools Protocol. Built for Node.js, Puppeteer uses asynchronous JavaScript to automate tasks like web scraping, UI testing, and browser interactions.
In this blog, we look at how to use Puppeteer for browser automation with Node.js.
TABLE OF CONTENTS
What Is Puppeteer?
Puppeteer is a modern JavaScript browser automation library with a high-level API for controlling Chrome or Chromium-based browsers. It is built on the Chrome DevTools and WebDriver BiDi Protocols and supports browsers such as Chrome, Chromium and Firefox.
Like many browser automation libraries, Puppeteer runs headless mode (i.e., without a visible browser window) by default but can be configured to display a browser window.
Puppeteer runs in web browsers and Chrome extensions and can be used to perform some of the following tasks:
- Automating form submission and UI testing
- Taking screenshots and generating PDFs of web pages
- Testing Chrome extensions
- Performing web scraping
Setting Up Puppeteer and Node.js
Here are some prerequisites to follow for running Puppeteer browser tests with Node.js:
- Install the Node.js library on your local machine. You can choose the Long Term Support (LTS) or the latest version of Node.js.
- Install Node.js package manager like npm or yarn. If you have previously installed Node.js, npm is included by default.
- Create a new project repository and navigate into the folder using the following command:
- Install Puppeteer by running the following command via the terminal:
- You can follow the below steps to run Puppeteer browser tests in headless mode:
- Install Puppeteer within your project directory.
- Set the headless property to true in the JSON object argument of the puppeteer.launch() method in your test script.
- Execute automation script like so: node < filename > . Puppeteer will launch and work in the background without a visible browser window.
- Navigate to the LambdaTest eCommerce Playground.
- Hover on the Mega Menu dropdown on the navigation bar.
- Click Apple on the dropdown menu and wait until it navigates to the product category page.
- Pick out the image link, name and price of each product on the category page.
- Return a list of extracted data in JSON format.
- Go to the LambdaTest Selenium Playground website.
- Click on the Ajax Form Submit link and wait for Navigation to the new page.
- Select form input and fill in the text “John Doe”.
- Select the textarea and fill with the text “I need customer support”.
- Click the Submit button to submit the form.
- Navigate to the LambdaTest homepage and wait for the page to complete loading.
- Take the screenshot using Puppeteer.
- Generate PDF using Puppeteer.
- Close Puppeteer browser.
- Puppeteer: https://pptr.dev/
Once you have Node.js installed, run the below command to check its version:
1 |
node -v |
Run the below command to check the npm version:
1 |
npm -v |
1 |
mkdir run-puppeteer-on-browser && cd run-puppeteer-on-browser |
1 |
npm i puppeteer |
The node_modules folder, package.json, and package-lock.json files will become visible within your project’s repository. A suitable version of Chrome browser is automatically downloaded during the Puppeteer installation.

Run Puppeteer browser tests across 50+ real environments. Try LambdaTest Today!
Using Puppeteer With Chrome
Puppeteer is built to work seamlessly and out of the box with Chromium-based browsers like Chrome.
You can leverage its browser automation capabilities without much configuration. It supports running on other Chromium browsers, and the executable path can be specified.
Implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import puppeteer from 'puppeteer'; (async () => { // Launches Puppeteer with default Chrome browser const browser = await puppeteer.launch(); // Creates a page instance const page = await browser.newPage(); // Navigates to the website await page.goto('https://developer.chrome.com/'); // Logs the page title console.log(await page.title()); // Closes the browser await browser.close(); })(); |
Our sample test script is wrapped in an Immediately Invoked Function Expression (IIFE), which means it will run immediately after being defined.
Code Walkthrough:
A new Chrome browser instance is created using the await puppeteer.launch() method, which will run in headless mode. The await browser.newPage() method creates a new browser page in the default browser context.
A URL string is passed to the await page.goto() method for navigation while the console.log(await page.title()) logs the page title to the console. The await browser.close() method closes the browser instance.
Test Execution:
Run the below command to execute the test script via the terminal:
1 |
node index.js |
To launch the Chrome browser in headed mode, an object, {headless: ‘false’}, with a key-value pair headless and ‘false’, is passed as an argument to the puppeteer.launch() method.
In some scenarios, you may wish to use a different version of Chrome. For that, when creating a browser instance, you can simply pass in the executable’s path to use another Chrome browser.
Also, always ensure that you provide the correct path to the Chrome browser’s executable file. On Linux, it can be found at /usr/bin/google-chrome.
Google Chrome might be the most popular Chromium-based browser, but there are others, such as Microsoft Edge, Brave, and Opera.
Since the Chromium engine powers these browsers, they are excellent for Puppeteer browser automation based on the Chrome DevTools Protocol. When pointing to a Chromium-based browser executable file, you can easily substitute one for another.
Using Puppeteer With Firefox
When launching Puppeteer with Firefox, the WebDriver BiDi is activated by default. It is a cross-browser automation protocol that allows bidirectional communication.
To use the Firefox browser with Puppeteer, you’ll have to install Firefox because it is not downloaded automatically like the Chromium browser.
You can install Firefox by running the below-given command:
1 |
npx puppeteer browsers install firefox |
Running Puppeteer browser tests in Firefox is similar to running the JavaScript automation library in a Chromium-based browser.
The only tweak is that you pass in an object, {browser: ‘firefox’}, with a key-value pair browser, ‘firefox’, to the puppeteer.launch() method.
Using Puppeteer Without Chromium
There is little difference between puppeteer and puppeteer-core libraries. When installed, the puppeteer downloads a Chrome browser. However, puppeteer-core does not download Chrome during installation.
The puppeteer-core library is best suited for managing browsers independently. It uses an executable file path or connection link to a remote browser.
To run Puppeteer without Chromium, install the puppeteer-core library:
1 |
npm i puppeteer-core |
The above command will install the puppeteer-core library without installing Chromium, allowing you to use any other existing browser.
The import statement when using the puppeteer-core library slightly changes. Below is how to import and use the puppeteer-core library in your automation test script:
1 |
import puppeteer from 'puppeteer-core'; |
All methods and functions remain the same as in the puppeteer library.
Using Puppeteer in Headless Mode
Puppeteer allows you to run tests with a graphical user interface (headed mode) or in the background with no window tab opened and no interaction (headless mode).
Unless Puppeteer’s default configuration is tweaked, it will run in headless mode. The use cases for running Puppeteer in headless mode include web scraping, network monitoring, performance testing, handling JavaScript execution etc.
Performing Puppeteer Browser Testing on LambdaTest
While performing Puppeteer browser testing on a local grid, you may encounter challenges such as limited resources and browser configuration issues.
Moreover, configuring different browser versions and ensuring compatibility can also be time-consuming. Local machines may struggle with parallel test execution, leading to slower test runs and limited scalability. To overcome these challenges, you can use cloud-based testing platforms such as LambdaTest.
LambdaTest is an AI-native test execution platform that lets you run Puppeteer browser testing at scale on a test automation cloud of real desktop browsers. You can also run parallel tests, reducing test execution time by multiple folds.
To get started, refer to this documentation on Puppeteer browser testing on LambdaTest.
Let’s set up a remote browser connection with Puppeteer on LambdaTest. For that, you need to configure your Username and Access Key from your LambdaTest Account Settings.
Implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
'use strict'; import { connect } from 'puppeteer'; export async function remoteBrowserPage() { const capabilities = { 'browserName': 'Chrome', 'browserVersion': 'latest', 'LT:Options': { 'platform': 'Windows 10', 'build': 'puppeteer-build-1', 'name': 'My first Puppeteer test', 'resolution': '1366x768', 'user': process.env.LT_USERNAME || "Your Username", 'accessKey': process.env.LT_ACCESS_KEY || "Your Access Key", 'network': true } }; let browser; let page; try { browser = await connect({ browserWSEndpoint: `wss://cdp.lambdatest.com/puppeteer?capabilities=${encodeURIComponent(JSON.stringify(capabilities))}`, }); page = await browser.newPage(); await page.setViewport({ width: 1024, height: 768, deviceScaleFactor: 1, }); } catch (e) { if (browser) { const page = await browser.newPage(); await page.evaluate(_ => { }, `lambdatest_action: ${JSON.stringify({ action: 'setTestStatus', arguments: { status: 'failed', remark: "Test Failed" } })}`) await browser.close(); } console.log("Error - ", e); } return {page, browser}; } |

Code Walkthrough:
Create a file connector.js file in the root of your project directory. This file will hold the LambdaTest capabilities needed for the project and a function that returns the Puppeteer browser and page objects.
Use the LambdaTest Automation Capabilities Generator to generate the capabilities needed to make a remote connection. The capabilities object has keys such as browserName, browserVersion, and LT:Options.
Create and export an async function remoteBrowserPage() function, which will return page and browser objects. Add the capabilities object variable to the async function. Then, create a try/catch code block to initiate the remote connection.
The connect() function instantiates a browser instance by passing in the browser WebSocket endpoint argument. The endpoint also contains the capabilities variable generated earlier.
Create a page instance using the browser.newPage() method. Use the catch block to catch any exceptions thrown in the try block and also set the LambdaTest test status to failed.
Return a promise of the browser and page instances from the function.
The remoteBrowserPage function is exported from the module as a name function and would be imported and used where necessary in the project.
Perform Web Scraping With Puppeteer
A common use case for Puppeteer is web scraping, which is the process of extracting useful data from websites or API endpoints.
We’ll showcase how Puppeteer can be used to scrape certain data from LambdaTest eCommerce Playground.
Test Scenario:
|
Implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
'use strict'; import { remoteBrowserPage } from './connector.js'; (async () => { try { // console.log("Navigating to LambdaTest eCommerce Playground"); let {page, browser} = await remoteBrowserPage(); // Navigate the page to a URL await page.goto('https://ecommerce-playground.lambdatest.io/'); // show dropdown menu await page.evaluate(() => { const element = document.querySelector('ul.mz-sub-menu-28.dropdown-menu.mega-menu-content.full-width'); if (element) { element.classList.add('show'); // Add the "show" class } }); // click on the apple category await page.click('a.nav-link.icon-left.text[title="Apple"]'); // wait for element to load await page.waitForSelector('div.carousel-item.active img.lazy-load'); // scroll down to load images await page.evaluate(async () => { await new Promise((resolve) => { let totalHeight = 0; // Scroll distance per interval const distance = 50; const timer = setInterval(() => { const scrollHeight = document.body.scrollHeight; window.scrollBy(0, distance); totalHeight += distance; if (totalHeight >= scrollHeight) { clearInterval(timer); resolve(); } // Scroll every 100ms }, 100); }); }); // Extract all src attribute values const imgSrcs = await page.evaluate(() => { const imgElements = document.querySelectorAll('div.carousel-item.active img.lazy-load'); return Array.from(imgElements).map(img => img.src); // Extract src attribute from each img element }); // Wait for the elements to load await page.waitForSelector('div.caption h4.title'); await page.waitForSelector('div.caption div.price'); // Extract innerTexts from title elements const titles = await page.evaluate(() => { const titleElements = document.querySelectorAll('div.caption h4.title'); return Array.from(titleElements).map(title => title.innerText.trim()); }); // Extract innerTexts from price elements const prices = await page.evaluate(() => { const priceElements = document.querySelectorAll('div.caption div.price'); return Array.from(priceElements).map(price => price.innerText.trim()); }); // console.log('Titles:', titles); // console.log('Prices:', prices); // Combine the lists into a structured object const combined = imgSrcs.map((src, index) => ({ image: src, title: titles[index], price: prices[index] })); // Print the combined result console.log(combined); // set test status to passed await page.evaluate(_ => { }, `lambdatest_action: ${JSON.stringify({ action: 'setTestStatus', arguments: { status: 'passed', remark: "Test Passed" } })}`) console.log("Closing browser"); await browser.close(); } catch (e) { console.log("Error - ", e); } })(); |
Code Walkthrough:
First, import the remoteBrowserPage() function from the connector.js module. Extract the page and browser instances from the remoteBrowserPage() function. Use the page.goto() function to navigate to the website to be scrapped.
The page.evaluate() method allows JavaScript code to be executed within the context of the loaded web page. The document.querySelector() method traverses the DOM to find the element that matches the CSS selector passed in as an argument.
If the specified element is found, the CSS class show is added to make it visible in the DOM tree. For example, this would make the “Mega Menu” element visible for more interaction with Puppeteer.
After the “Mega Menu” becomes visible, use the page.click() method to click on the category of choice, “Apple,” passing in the needed CSS selector. The page.waitForSelector() method is used to await elements with the CSS class div.carousel-item.active img.lazy-load to complete loading in the DOM.
The LambdaTest eCommerce Playground implements lazy image loading, which means that some images will not load until they are within the viewport. We use the page.evaluate() method to execute JavaScript code for scrolling down the web page.
A Promise object, await new Promise((resolve) => { … }), is created to manage the scrolling process and resolves when the bottom of the page is reached. The setInterval() function executes the scrolling logic at given time intervals.
Scrolling stops when the totalHeight is greater than or equal to the scrollHeight. The promise is resolved, and the scrolling process is terminated.
To extract the image links, names and prices of products on the page.evaluate() method is used. The document.querySelectorAll() method returns all elements that match the specific CSS selector for the image links, names and product prices. Use the Array.from().map() methods chain to create a list of retrieved attribute values.
Use the JavaScript map() method to create a list of JSON objects, each having product image links, names and prices. Set the LamdaTest test status as Test Passed.
Test Execution:
To execute the Puppeteer web scraping test, run the command below in your terminal:
1 |
node webScraper.js |
Visit the LambdaTest Web Automation dashboard to view the test execution result:
Automate Form Submission With Puppeteer
Puppeteer can be used for automation of the form submission process. Form submission is a very common procedure and this is how it is automated using Puppeteer’s capabilities.
Test Scenario:
|
Implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import { remoteBrowserPage } from "./connector.js"; (async () => { // Launch the browser and open a new blank page let {page, browser} = await remoteBrowserPage(); console.log("starting navigation"); // Navigate the page to a URL await page.goto('https://www.lambdatest.com/selenium-playground/'); // select Ajax Submit form Element await page.evaluate(() => { const element = document.querySelector('li.pt-10 > a[href="https://www.lambdatest.com/selenium-playground/ajax-form-submit-demo"]'); element.click(); }); // wait for navigation await page.waitForNavigation(); // select input and fill with text await page.type('input#title', 'John Doe'); // select textarea and fill with text await page.locator('textarea#description').fill('I need customer support'); // click submit button await page.locator('.btn').click(); console.log('Form submitted'); // set test status to passed await page.evaluate(_ => { }, `lambdatest_action: ${JSON.stringify({ action: 'setTestStatus', arguments: { status: 'passed', remark: "Test Passed" } })}`); await browser.close(); console.log("closing browser"); })(); |
Code Walkthrough:
Extract page and browser instances from the remoteBrowserPage() function from the connector.js module. The page and browser instances have different methods that will be used to automate our form-filling process.
The page instance goto() method takes a URL argument to which it navigates, in this case, the LambdaTest Selenium Playground website.
To pick out the anchor tag element with the “Ajax Submit Form”, we use the document.querySelector() method, which returns the first element that matches the CSS selector. After the element is selected, it is clicked to trigger navigation to a new page.
The document.querySelector() and element.click() methods are both executed with the page.evaluate() method. The method allows developers to run JavaScript code within the context of the current web page. Next, we wait for navigation to be completed using the page.waitForNavigation() method.
We use the page.type() method to select the input element with ID “title” and type in the string “John Doe”. The page.locator() method is used to pick out the textarea element with ID “description” while the fill() method fills out the identified element with the provided string value “I need customer support”.
Finally, the page.locator() method selects the element with the CSS selector .btn, and the click() method completes the form submission.
Test Execution:
Run the following command to execute the test:
1 |
node fillForm.js |
Visit the LambdaTest Web Automation dashboard to view the test execution result:
Take Screenshots and Generate PDFs With Puppeteer
With Puppeteer, taking screenshots and generating PDFs is pretty simple. Let’s see how it’s done!
Test Scenario:
|
Implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import { remoteBrowserPage } from "./connector.js"; (async () => { let { page, browser } = await remoteBrowserPage(); console.log('Starting navigation'); // Navigate the page to a URL await page.goto('https://www.lambdatest.com'); // Take screenshot await page.screenshot({ path: '../lambdatest.png' }); console.log('Screenshot taken!') // Generate PDF await page.pdf({ path: '../lambdatest.pdf' }); console.log('PDF generated!') // set test status to passed await page.evaluate(_ => { }, `lambdatest_action: ${JSON.stringify({ action: 'setTestStatus', arguments: { status: 'passed', remark: "Test Passed" } })}`); await browser.close(); console.log('Browser closed.'); })(); |
Code Walkthrough:
The page.goto() method navigates to the LambdaTest homepage, as provided. Alternatively, you can use the page.screenshot() method to take a screenshot of the web page in context.
The page.screenshot() method takes a path object as an argument, which indicates where the screenshot will be saved. In this case, the screenshot is saved to the project’s root directory.
Generating a PDF follows a similar procedure to the above. The page.pdf() method will create a PDF document from the web page in context. Ensure to provide a path object like this {path: lambdatest.pdf}. A PDF document is generated and saved in the root directory of the project.
Test Execution:
Run the following command to execute the test:
1 |
node screenshotPdfGenerator.js |
Visit the LambdaTest Web Automation dashboard to view the test execution result:
Here is the LambdaTest homepage screenshot captured by Puppeteer after executing the script:
You can also check newly created files lambdatest.png and lambdatest.pdf, in the project’s root directory.
Conclusion
Puppeteer is a JavaScript browser automation library that leverages the Chrome DevTools Protocol to drive its automation capabilities. Although Puppeteer supports Chromium-based browsers by default, its support has been extended to Firefox since v23.0.0.
Firefox with Puppeteer does not automatically download, so users will have to download it. When using Firefox alongside a Chromium-based browser, a few feature inconsistencies and constraints may be noticed.
Despite the few cross-browser limitations users may experience with Puppeteer, it remains a valuable library for web scraping, UI testing, and PDF generation, amongst others. The automation library also offers users the ability to customize Puppeteer in browsers with its robust configuration capabilities.
Frequently Asked Questions (FAQs)
Is Puppeteer better than Selenium?
Puppeteer is faster and more lightweight than Selenium but supports fewer browsers. Selenium is more versatile, supporting multiple languages and browsers. The choice depends on the use case—Puppeteer for Chrome automation and Selenium for broader compatibility.
Is Puppeteer only for Chrome?
Initially, Puppeteer was built for Chrome and Chromium. However, Puppeteer now supports Firefox in experimental mode. It does not natively support other browsers like Safari or Edge.
Is Puppeteer owned by Google?
Yes, Puppeteer is developed and maintained by Google.
Citations
Got Questions? Drop them on LambdaTest Community. Visit now