Running Puppeteer Tests on GitLab CI

Pradap Pandiyan
4 min readFeb 15, 2025

--

In this article, we’ll walk through setting up GitLab CI to run Puppeteer tests in headless mode using a lightweight Alpine-based Node.js image. We’ll also include a sample page object for the SauceDemo login page.

Prerequisites

  • A GitLab repository with a Node.js project.
  • Basic knowledge of Puppeteer and GitLab CI/CD.
  • A test script (using Puppeteer) and a Page Object Model (POM) file for better maintainability.

GitLab CI Configuration

We will use the node:16-alpine Docker image to run our tests. Alpine images are lightweight and fast, which is ideal for CI environments. Because Puppeteer normally downloads its own version of Chromium, we can save build time by preventing that download and instead using the system-installed Chromium.

Create a .gitlab-ci.yml file in your project root with the following content:

image: node:16-alpine
variables:
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: "true"
PUPPETEER_EXECUTABLE_PATH: "/usr/bin/chromium-browser"
stages:
- test
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
test:
stage: test
before_script:
- apk update
- apk add --no-cache chromium nss freetype harfbuzz ca-certificates ttf-freefont
- npm install
script:
- npm test

Explanation

Image & Variables:

  • We use the node:16-alpine image for a small footprint.
  • PUPPETEER_SKIP_CHROMIUM_DOWNLOAD is set to "true" to skip Chromium download during installation.
  • PUPPETEER_EXECUTABLE_PATH tells Puppeteer where Chromium is installed on Alpine.

Stages & Cache:

  • We define a single test stage.
  • The cache section caches node_modules to speed up builds.

Before Script:

  • apk update updates the Alpine package lists.
  • apk add --no-cache chromium nss freetype harfbuzz ca-certificates ttf-freefont installs Chromium and its necessary dependencies.
  • npm install installs your project dependencies.
  • Script:
  • The npm test command runs your test suite. Ensure your package.json is set up accordingly (for example, with a script "test": "node runTests.js").

Creating a Page Object

We use the Page Object Model (POM) to encapsulate page-specific logic. Below is a sample page object for the SauceDemo login page. Create a file at pages/loginPage.js:

// pages/loginPage.js
class LoginPage {
/**
* @param {puppeteer.Page} page - The Puppeteer page instance.
*/
constructor(page) {
this.page = page;
this.usernameInput = '#user-name';
this.passwordInput = '#password';
this.loginButton = '#login-button';
}
async navigate() {
await this.page.goto('https://www.saucedemo.com/', { waitUntil: 'networkidle2' });
}
async login(username, password) {
await this.page.type(this.usernameInput, username);
await this.page.type(this.passwordInput, password);
await this.page.click(this.loginButton);
}
}
module.exports = LoginPage;

How It Works

  • Constructor:
    The constructor receives the Puppeteer page object and initializes selectors for the username input, password input, and login button.
  • navigate():
    Opens the SauceDemo site and waits until the network is idle.
  • login(username, password):
    Fills in the username and password, then clicks the login button.

Putting It All Together

  1. Develop Your Tests:
    Write your Puppeteer tests using the page object. For example, create a test file (e.g., tests/loginTest.js) that uses the LoginPage:
// tests/loginTest.js
const { launchBrowser } = require('../helpers/browser');
const LoginPage = require('../pages/loginPage');

(async () => {
const browser = await launchBrowser();
const page = await browser.newPage();

try {
// Create an instance of LoginPage and perform test steps.
const loginPage = new LoginPage(page);
await loginPage.navigate();
await loginPage.login('standard_user', 'secret_sauce');


// Verify the presence of the inventory list as a successful login indicator.
await page.waitForSelector('.inventory_list', { timeout: 5000 });
console.log('Login successful and inventory page loaded.');
} catch (error) {
console.error('Test failed:', error);
process.exit(1);
} finally {
await browser.close();
}
})();
  1. Configure Browser Helper:
    Ensure you have a helper file (helpers/browser.js) that launches Puppeteer. A simple version might look like:
// helpers/browser.js
const puppeteer = require('puppeteer');

async function launchBrowser() {
return await puppeteer.launch({
headless: true, // Disable headless mode
devtools: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-setuid-sandbox'

],
defaultViewport: null
});
}

module.exports = { launchBrowser };

I have added a single test for the Saucelabs website to validate the tests.

I have added the logs for the pipeline of the automation run.

Conclusion

By following this guide, you’ve set up GitLab CI to run Puppeteer tests using the lightweight node:16-alpine image. This configuration leverages system-installed Chromium, caches dependencies for faster builds, and runs tests in headless mode. The sample page object for the SauceDemo login page illustrates how to encapsulate page-specific actions, making your tests cleaner and more maintainable.

Now, every push to your repository will trigger the CI pipeline, ensuring that your Puppeteer tests run automatically in GitLab CI

I have created a project on Gitlab and added the code here.

Here is the test execution link on Gitlab CI here.

Feel free to hit clap if you like the content. Happy Automation Testing :) Cheers. 👏

--

--

Pradap Pandiyan
Pradap Pandiyan

Written by Pradap Pandiyan

I’m a passionate QA Engineer. I’m a motovlogger, content creator, off-roader and freelancer. Buy me a coffee here https://www.buymeacoffee.com/pradappandiyan

Responses (1)