Things I learned after writing tests for JS and HTML page

2019-01-12 · 3 min read

In recent weeks, I want to learn about writing tests using Jest, Mocha or other stuffs. Unfortunately, I don't have any React apps that want to test these days but I have chances to figure out how to test HTML page with pure JavaScript.

I found most tutorials are using Jest with React or other JavaScript frameworks. Is it really possible to test HTML page with Jest? Yes!

How to start

View the demo here for my sample HTML page. Just a simple page to fetch JSON, show a list based on it and a button to show/hide translations.

In your root folder, create a package.json like this, and run npm install in console.

{
    "scripts": {
        "test": "jest --watch",
        "coverage": "jest --coverage"
    },
    "devDependencies": {
        "jest": "^23.6.0"
    }
}

When you finished, you can start testing your app! Create a file <YOUR-FILENAME>.spec.js and start testing like:

const fs = require('fs');
const path = require('path');
const html = fs.readFileSync(path.resolve(__dirname, '../index.html'), 'utf8');

jest
    .dontMock('fs');

describe('button', function () {
    beforeEach(() => {
        document.documentElement.innerHTML = html.toString();
    });

    afterEach(() => {
        // restore the original func after test
        jest.resetModules();
    });

    it('button exists', function () {
        expect(document.getElementById('disable')).toBeTruthy();
    });
});

Save it and run npm test!

View my GitHub here for codes

What I learned

1. Difference between testing HTML and React page

In React, you can use Enzyme to shallow the component and get state for testing. But in HTML and JavaScript page, the only thing you can test is the classes, content and function output. When you are used to test by states, it might be not so convenient to test HTML page.

As pure JS did not export anything for Jest to test, you also need to add this at the end:

if (typeof exports !== 'undefined') {
    module.exports = {
        getItem,
        setItems,
        triggerItem
    };
}

Then Jest can import/export functions for testing.

For HTML, you cannot directly import it like React component. You need to add this snippet before tests, to import the whole HTML:

const fs = require('fs');
const path = require('path');
const html = fs.readFileSync(path.resolve(__dirname, '../index.html'), 'utf8');

jest.dontMock('fs');

or write inline HTML inside tests. For example:

const html = document.createElement('div');
html.innerHTML = `<div class="lang-grid" id="language">This is a sample</div>`;
document.body.appendChild(div);

2. Special way to test asynchronous code

It is still easy to test basic thing, like checking the menu button will appear in mobile view using Jest. Like:

it('menu button exists', function () {
    expect(document.getElementById('menu-btn')).toBeTruthy();
});

But for asynchronous code, like Promise, you need to use different approach.

The most important thing is to add done() in each test.

it('get same items from json', function (done) {
    fetch.mockResponse(JSON.stringify(json))
    const {getItem} = require('../scripts/main.js');

    getItem().then(res => {
        expect(res).toEqual([{
            "phase": "Entschuldigung!",
            "trans": "Excuse me. [as in may I have your attention]."
        },
        {
            "phase": "Sprechen Sie Englisch?",
            "trans": "Do you speak English?"
        }])

        expect(res.length).toEqual(2);
        done();
    })
    .catch(err => console.log(err))
});

Just like what Jest documentation said, it is important to add done() in the test. Otherwise, it may have wrong results.

After you added done(), it will wait till your async call to be resolved and get the expected result.


3. Check coverage using Jest

Jest has built-in coverage function, you can call it using jest --coverage. Then you can see your reports in coverage/lcov-report/index.html. The chart is very useful and inform you which code have not tested.

Coverage tool

Why it is not 100% in branches? Because I skipped the test of exporting module statement at the end.

I have not use coverage tool before, that's why I am motivated when I saw my code changed from red to green!

Is testing fun?

Well, it may not be fun, but surely is satisfactory when I saw my code changed from RED to GREEN.

Do you have any suggestions to my flow? Or any ideas on testing? Feel free to drop me a line here :)



Written by Yuki Cheung

Hey there, I am Yuki! I'm a front-end software engineer who's passionate about both coding and gaming.

When I'm not crafting code, you'll often find me immersed in the worlds of Fire Emblem and The Legend of Zelda—those series are my absolute favorites! Lately, I've been delving into the fascinating realm of AI-related topics too.