Testing Dom onclick event with Jest

hi house, can someone help me on how to write a jest test to call “onclick” event that was attarched to an html button, whereby, once that button is clicked it will trigger another function

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>My TODO App</title>

    <link rel="stylesheet" href="style.css" type="text/css">

</head>

<body>

    <header id="navbar">      

        <h2>Welcome</h2>

        <div id="search">

            <input id="searchTodo" type="text" placeholder="Search" onkeyup="searchTodo()">

        </div>

</header>

    <div class="container">

        <div id="control" class="controls">

            <input type="text" placeholder="Your Todo" id="todo" maxlength = "20"> <br>

            <button class="button" id="add" onclick="addTodo()"> Add</button>

        </div>

    

        <div id="list1">

            <ul id="list">

            </ul>

        </div>

    </div>

    <script src="newLogic.js">

    </script>

</body>

</html>
const allTodos = {}

let currentID = 0;

Object.keys(localStorage).map(key => allTodos[key] = localStorage[key]);

const todoNodes = []

Object.keys(allTodos).map(item => {

    // CREATE li Element

    const li = document.createElement('li');

    const div = document.createElement('div');

    const checkbox = document.createElement('input');

    const label = document.createElement('label');

    const editButton = document.createElement('button');

    const deleteButton = document.createElement('button');

    const p = document.createElement('p');

    p.textContent = item;

    editButton.setAttribute('onclick', `editTodo('${item}')`);

    deleteButton.setAttribute('onclick', `deleteTodo('${item}')`);

    editButton.setAttribute('class', 'edit');

    deleteButton.setAttribute('class', 'edit');

    editButton.innerHTML = 'Edit';

    deleteButton.innerHTML = 'Delete';

    label.textContent = allTodos[item];

    checkbox.setAttribute('id', item);

    checkbox.setAttribute('class', 'check');

    checkbox.setAttribute('type', 'checkbox');

//ADD li ELEMENT to the DOM

    div.appendChild(checkbox);

    div.appendChild(label);

    div.appendChild(editButton);

    div.appendChild(deleteButton);

    div.appendChild(p);

    li.appendChild(div);

    todoNodes.push(li);

});

function display(){

    const toDisplayHere = document.querySelector('#list');

    todoNodes.forEach(todo => toDisplayHere.appendChild(todo));

}

function addTodo(){

    let todoValue = document.querySelector('#todo').value;

    const id = new Date().toLocaleString('en-GB', { timeZone: 'UTC' });

    if(!todoValue){

        const errPara = document.createElement('p');

        errPara.setAttribute('class', 'error')

        errPara.textContent =' Your Todo input cannot be empty';

        const divContrl = document.querySelector('#control');

        const addButton = document.querySelector('#add');

        divContrl.insertBefore(errPara, addButton )

    }

    localStorage.setItem(id, todoValue);

    document.querySelector("#todo").value = "";

    setTimeout(() =>{

        location.reload();

    },1000);

}

function deleteTodo(id){

    localStorage.removeItem(id);

    location.reload();

}

function editTodo(id){

    currentID = id;

    document.querySelector('#todo').value = allTodos[id];

    document.querySelector('#add').innerHTML = 'SAVE';

    document.querySelector("#add").setAttribute('onclick', 'updateTodo()')

}

function updateTodo(){

    let newValue = document.querySelector('#todo').value;

    localStorage[currentID] = newValue;

    document.querySelector("#todo").value = "";

    location.reload();

}

function searchTodo(){

    const toDisplayHere = document.querySelector('#list');

    const param = document.querySelector('#searchTodo').value.toLowerCase();

    toDisplayHere.innerHTML = "";

    

    todoNodes.forEach(todo => {

        const todoValue = todo.children[0].children[1].innerHTML;

        if(todoValue.toLowerCase().includes(param)){

            toDisplayHere.appendChild(todo);

        }

    });

}

display();

module.exports = {addTodo, searchTodo, updateTodo, editTodo, deleteTodo };
// #region for jest setup

const jsdom = require('jsdom');

const path = require('path');

const fs = require('fs');

const {addTodo, searchTodo, updateTodo, editTodo, deleteTodo } = require('./newLogic');

const indexFile = fs.readFileSync(path.join(__dirname, "./index.html"), {

    encoding: 'utf-8'

});

let document;

let window;

let virtualDOM;

beforeEach(() => {

    const resourceLoader = new jsdom.ResourceLoader({

        userAgent: 'jsDom'

    });

    virtualDOM = new jsdom.JSDOM(indexFile, {

        runScripts: "dangerously",

        resources: resourceLoader

    });

    window = virtualDOM.window;

    document = window.document;

})

// #End region

const {getByText} = require('@testing-library/dom');

const userEvent = require("@testing-library/user-event").default;

describe("myTodo-App", () =>{

    

    test(' Your Todo input cannot be empty', () => {

        const addBtn = getByText(document, /Add/);

        addBtn.onclick  = jest.fn();

        userEvent.click(getByText("Check"));

        expect(getByTestId("checkbox")).toHaveAttribute("checked", true);

        userEvent.click(getByText(document, /Add/));

        expect(addTodo).toHaveBeenCalledTimes(1);

        expect(addBtn).toHaveBeenCalledWith(addTodo)

    });

});

@olayodepossible

Good attempt at using Jest. However, could you setup a codesandbox for this particular issue you have?

From what I can see, I think you’ve mistaken how to use testing-library/dom
I’d suggest taking a look at the apis/examples again

I am grateful for the response, am just starting up, kindly help me out.
here is the codeSandbox link: https://codesandbox.io/s/amazing-vaughan-9qg3u?fontsize=14&hidenavigation=1&theme=dark

I await your effort to unblock please.

Hi @olayodepossible,

Thanks for the sandbox. However, i’m unsure if you have worked with Jest before. From the sandbox, you didn’t install Jest, jsdom or the testing-library dependencies. You also didn’t write a script in your package.json to execute your test.

I believe you do not necessarily need jsdom, try to break your UI into sections and test them separately. Try to follow the example from testing-library.com in my previous response to you.

Also, the paths to your index.html and style.css are incorrect. AddTodo() button doesn’t work on the UI.

I’d suggest trying to simplify your index.js file and extract certain functions into separate files.

Take a look at Jest’s documentation on how to create and run tests.

I’d suggest trying to make Jest work first before even thinking about making assertions on your code.

For example to test if it’s working just write a simple test

test('should work', ()=> {
  const thisIsTrue = true
  expect(thisIsTrue).toBe(true)
})

If you see in your console that Jest shows that the above test passes, you can be sure your Jest is working properly. Only from there then try to integrate testing-library.

Hope that helps

1 Like