imnew
imnew

Reputation: 79

Writing tests with Jest for vanilla JS

I followed Dev Eds course on creating a todo list, but now I want to write some tests to test the code. I want to use Jest but I'm not sure how to write a test to make sure that a todo is created, and deleted.

I've added the app.js file below (there is are html/css files as well). My attempt at writing a test is under the app.js file.

//Selectors
const todoInput = document.querySelector('.todo-input');
const todoButton = document.querySelector('.todo-button');
const todoList = document.querySelector('.todo-list');
const filterOption = document.querySelector('.filter-todo');

//Event Listeners
todoButton.addEventListener('click', addTodo);
todoList.addEventListener('click', deleteCheck);
filterOption.addEventListener('change', filterTodo);

//Functions
function addTodo(event){
    //console.log(event.target);
    // prevent form from submitting
    event.preventDefault();
    const todoDiv = document.createElement("div");
    todoDiv.classList.add("todo");
    //create LI
    const newTodo = document.createElement('li');
    newTodo.innerText = todoInput.value;
    newTodo.classList.add('todo-item');
    todoDiv.appendChild(newTodo);
    // Checkmark button
    const completedButton = document.createElement('button');
    completedButton.innerHTML = '<i class="fas fa-check"></i>'
    completedButton.classList.add("complete-btn");
    todoDiv.appendChild(completedButton);
    // Delete button
    const deleteButton = document.createElement('button');
    deleteButton.innerHTML = '<i class="fas fa-trash"></i>'
    deleteButton.classList.add("delete-btn");
    todoDiv.appendChild(deleteButton);
    // Append to list
    todoList.appendChild(todoDiv);
    // clear todo input value
    todoInput.value = "";
}

function deleteCheck(e){
    //console.log(e.target);
    const item = e.target;
    if(item.classList[0] === 'delete-btn'){
        const todo = item.parentElement;
        // animation
        todo.classList.add("fall");
        todo.addEventListener("transitionend", function() {
            todo.remove();
        });
    }
    // check mark
    if(item.classList[0] === "complete-btn"){
        const todo = item.parentElement;
        todo.classList.toggle('completed');
    }
}

function filterTodo(e) {
    const todos = todoList.childNodes;
    todos.forEach(function(todo) {
      switch (e.target.value) {
        case "all":
          todo.style.display = "flex";
          break;
        case "completed":
          if (todo.classList.contains("completed")) {
            todo.style.display = "flex";
          } else {
            todo.style.display = "none";
          }
          break;
        case "uncompleted":
          if (!todo.classList.contains("completed")) {
            todo.style.display = "flex";
          } else {
            todo.style.display = "none";
          }
      }
    });
  }

The first test I created was to check if a todo is created

const todo = require('./app')

test('add a todo', () => {
    expect(todo("buy milk".toBe("buy milk")))
})

But the test failed.

Upvotes: 2

Views: 3562

Answers (1)

Ringolds
Ringolds

Reputation: 236

First you need to decide whether you're testing the UI or the system/engine the UI wraps around. Besides the missing ) after todo("buy milk", you are trying to test the engine not the UI.

In your case you should look into splitting the actual to do engine code from the UI layer. Then you can test CRUD of the to do.

Lets take a basic example:

    const Todo = function() {
        let list = [];
        let items = {};
        return {
            create: function(task) { 
                let id = Date.now();
                items[id] = { id, task };
                list.push(id);
                return id;
            },
            read: function(id) {
                return items[id];
            },
            readAll: function() {
                return list.map(id => items[id]);
            },
            delete: function(id) {
                list = list.filter(itemId => itemId !== id);
                delete items[id];
                return true;
            }
        }
    }

This is a testable todo engine and tests would look like this (using mocha and chai):


const { expect } = require("chai");
const Todo = require("./Todo.js"); // replace with your file

describe("Todo", function() {
    const todo = Todo();
    it("should create a todo item and return an ID", function() {
        let id = todo.create("My first todo");
        expect(id).to.be.a("integer");
    });
    it("should create a todo item with text 'My second todo'", function() {
        let id = todo.create("My second todo");
        let text = todo.read(id);
        expect(text).to.equal("My second todo");
    });
    it("should return an array with two todo items", function() {
        let items = todo.readAll();
        // you can also test for exact strings
        expect(items.length).to.equal(2);
    });
    it("should delete first item", function() {
        let items = todo.readAll();
        let id = items[0].id;
        todo.delete(id);
        items = todo.readAll();
        expect(items.length).to.equal(1);
    });
});

When you get to front-end testing, you will use a tool like Cypress or Puppeteer

Upvotes: 3

Related Questions