DEV Community

angrykoala
angrykoala

Posted on

I needed browser automation for testing, so I obviously wrote my own tool

Around a year ago, I decided to start using a browser automation tool to write some tests for my project front-end. While some degree of testing could be achieved by writing unit tests, mocks and jsdom, in the end, these tests couldn't reflect real interactions and properly detect errors.

Because these tests were going to be written by me and other developers (no QA team available!). I needed the tests to be as simple to write and maintain as possible to avoid wasting too much dev time and (more importantly) to avoid devs (and myself) stop writing tests due to laziness.

With this in mind I started searching for the correct browser automation tool for my case, with my priority being simplicity to write tests. During this process I tried several tools, in some cases using them for weeks:

All of these tools (and other I've been trying since) where, for me, either too difficult to setup, had little support for writing tests or relied in external (and usually paid) services.

So, as any sane developer would do, I decided to build my own tools. Of course, I wasn't going to build a headless browser from scratch, after testing a few of the above mentioned, I decided to go with Puppeteer as a base for my own tool. It was reliable, powerful, easy to setup and well-maintained. And so, I started building wendigo, a full wrapper on top of Puppeteer to make testing easier and add features as I needed.

Wendigo Logo

After a few weeks of development in my free time, the efforts started to pay off. Integration and E2E tests started to look cleaner on several projects at my company. Adding built-in assertion methods greatly simplified tests. Going from a test written in Puppeteer like this:

// Test a modal text with Puppeteer
await page.click(".my-btn");
await page.waitForSelector("#my-modal")
const modalText = await page.evaluate(() => {
    const modalElement = document.querySelector("#my-modal");
    return modalElement.textContent;
})
assert.strictEqual(modalText, "Button Clicked");
Enter fullscreen mode Exit fullscreen mode

To this:

// Test a modal text with Wendigo
await browser.click(".my-btn");
await browser.waitFor("#my-modal");
await browser.assert.text("#my-modal", "Button Clicked");
Enter fullscreen mode Exit fullscreen mode

The more complex the test, the bigger was the difference.

After more than a year on development. Wendigo already provides not only simple built-in assertions, but a big set of features:

  • Easy setup (npm install --save-dev wendigo)
  • Complex queries such as elementFromPoint(x,y), findByTextContaining(text).
  • One-line assertions for most common scenarios.
  • LocalStorage and Cookies interfaces and assertions.
  • Requests mocks. Allowing to emulate server errors, 3rd party requests, etc.
  • Plugin system to add extra modules or your own methods as needed.
  • Agnostic, it works on any Node.js based framework, testing toolchain or in Docker and CI.
  • Full access to underlying Puppeteer methods.

These have proven to be effective features for fast and reliable testing. Of course, there are some shortcoming on using Wendigo instead of other alternatives:

  • Only Chrome supported.
  • The abstraction layers provided by Wendigo may result on unexpected behaviors on some cases.
  • Node.js required for running and writing tests.

An example of how a test with Wendigo using mocha would look like:


const assert = require('assert');
const Wendigo = require('wendigo');

describe("My Tests", function() {
    this.timeout(5000); // Recommended for CI or slower machines
    let browser;

    beforeEach(async() => {
        browser = await Wendigo.createBrowser();
    });

    afterEach(async() => {
        await browser.close();
    });

    after(async() => {
        // After all tests finished, makes sure all browsers are closed
        await Wendigo.stop(); 
    });

    it("Page Title", async() => {
        await browser.open("http://localhost");
        await browser.assert.text("h1#main-title", "My Webpage");
        await browser.assert.title("My Webpage");
    });

    it("Open Menu", async() => {
        await browser.open("http://localhost");
        await browser.assert.not.visible(".menu");
        await browser.click(".btn.open-menu");
        await browser.assert.visible(".menu");
    });

    it("Initial Request To API Fails Gracefully", async() => {
        browser.requests.mock("http://localhost/api", {
          status: 500
        });
        await browser.open("http://localhost", {
            clearRequestMocks: false // Keeps the mock created before
        });

        // Show red text with error message
        await browser.assert.text(".error-message", "Error loading the API");
        await browser.assert.style(".error-message", "color", "red");
    });
});
Enter fullscreen mode Exit fullscreen mode

This tool is completely open-source on GitHub, tested and documented and still on active development. Currently I'm trying to get feedback on improvements, bugs or features to help other developers with their front-end and E2E testing.

GitHub logo angrykoala / wendigo

A proper monster for front-end automated testing

Wendigo

by @angrykoala
npm

A proper monster for front-end automated testing.

Wendigo (/wɛndɪɡo/) simplify your front-end and end-to-end automated testing using Puppeteer. Install it with npm install --save-dev wendigo.

WARNING: This documentation refers to Wendigo 2, if you are using previous versions of Wendigo, go here.

Consider the following example using just Puppeteer:

await page.click(".my-btn");
await page.waitForSelector("#my-modal")
const modalText = await page.evaluate(() => {
    const modalElement = document.querySelector("#my-modal");
    return modalElement.textContent;
})
assert.strictEqual(modalText, "Button Clicked");
Enter fullscreen mode Exit fullscreen mode

The same test can be written like this with Wendigo:

await browser.click(".my-btn");
await browser.waitFor("#my-modal");
await browser.assert.text("#my-modal", "Button Clicked");
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
david_j_eddy profile image
David J Eddy

Have you looked into Cypress.io? It is what I moved on to then Phantom when EOL. I'll take a look at what you got here and try it out.

Collapse
 
angrykoala profile image
angrykoala

I saw it, although never really worked with it. While it looks like a better alternative than the ones I listed, back when I learnt about cypress I already had wendigo going on, and the scopes seem slightly different, wendigo is just an agnostic browser automation tool while cypress provides a whole framework for testing + CI infraestructure, plus the actions and assertions provided (at least for what I saw in the docs, it may have changed) don't look nearly as varied as what wendigo already has (obviously by now I'm completely biased so take this answer with a grain of salt)

Collapse
 
itsasine profile image
ItsASine (Kayla)

On the one hand, it looks cool and I want to give it a shot.

On the other hand, I've been playing too much Fallout 76 lately, so I don't want to deal with wendigos.

Collapse
 
ben profile image
Ben Halpern • Edited

Seems like a lot of fun!

Build vs buy is never cut and dry and even though there’s a lot of good reasons to buy (or install for free), diy is great too!