Allan Mogusu
Allan Mogusu

Reputation: 240

How to test vanilla js code that manpulates DOM with jest

// get submit button and add event listener to it
const submitBtn = document.getElementById("submit");
if(submitBtn){
submitBtn.addEventListener('click', loginFunction)
}
//call back function
function loginFunction(e){
    e.preventDefault();
    //  the data to post
    const data = {
        email: document.getElementById("email").value,
        password: document.getElementById("password").value,
    };

    //  post the data to db via fetch
    fetch("https://store-manager-api-app-v2.herokuapp.com/api/v2/auth/login",{
    headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin':'*',
        'Access-Control-Request-Method': '*'
    },
    method:"POST",
    mode: "cors",
    body: JSON.stringify(data)

    }).then(function(response){return response.json()})
    .then(function(response){
        localStorage.setItem('token', response.token);
        if (response.Message === "User logged in successfully!"){
            // redirect to index page
            document.getElementById("notify").innerHTML =`<div class="isa_success">
                               <i class="fa fa-check"></i>
     ${response.Message}
    </div>`;
        window.location.assign('../HTML/index.html')
        }
        else{
            let notify = document.getElementById("notify");
            notify.innerHTML =`<div class="isa_info">
                        <i class="fa fa-info-circle"></i>
                        ${response.Message}
                         </div>`
        }

    })
}

This is my login.js file that listens to a submit button and then performs a fetch to login the user.

Below is my login.test.js file..this is the test for the login.js, but it ain't working. I've tried changing await Promise.resolve.then() to jest.UseFakeTimers() but it ain't working. Anyone have an idea why it is not working, and a possible solution?

describe('login',() => {
    let fetchMock;
    let assignMock;

    beforeEach(() => {
        document.body.innerHTML +=`
        <div id="notify">
          </div>
        <form id="signin">
          <input type="email" id="email" value="[email protected]">
          <input type="password"  id="password" value ="test1234">
          <input type="submit" id="submit">
        </form>`;
        fetchMock = jest.spyOn(global,'fetch');
        fetchMock.mockImplementation(() =>Promise.resolve ({
            json: () => Promise.resolve({Message:"User logged in successfully!"})
        }));
        assignMock = jest.spyOn(window.location , "assign");
        assignMock.mockImplementation(() =>{});
        require('../UI/js/login');
    });
    afterEach(() => {
        fetchMock.mockRestore();
        assignMock.mockRestore();
        jest.resetModules()
    });
    it('fetch data and change the content of #notify', async function() {
        document.getElementById('submit').click();
        expect(fetchMock).toHaveBeenCalledTimes(1);
        const fetchArgs = fetchMock.mock.calls[0];
        expect(fetchArgs[0]).toBe('https://store-manager-api-app-v2.herokuapp.com/api/v2/auth/login');
        expect(fetchArgs[1]).toEqual({
            method: "POST",
            headers: {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin':'*',
                'Access-Control-Request-Method': '*'
            },
            mode: "cors",
            body: JSON.stringify({
                email: '[email protected]',
                password: "test1234"
            })

        });
     await Promise.resolve().then();
     expect(document.getElementById('notify').innerHTML).toBe(`<div class="isa_success">
<i class="fa fa-check"></i>
    User logged in successfully!
     </div>`);
       expect(assignMock).toHaveBeenCalledTimes(1);
       expect(assignMock.mock.calls[0][0]).toBe("../HTML/index.html");
    });


});

This is the error i'm getting:

Error: expect(received).toBe(expected) // Object.is equality

Expected: "<div class=\"isa_success\">
<i class=\"fa fa-check\"></i>
    User logged in successfully!
     </div>"
Received: "
          "

Difference:

- Expected
+ Received

- <div class="isa_success">
- <i class="fa fa-check"></i>
-     User logged in successfully!
-      </div>
+ 
+            <Click to see difference>


      45 |         });
      46 |      await Promise.resolve().then();
    > 47 |      expect(document.getElementById('notify').innerHTML).toBe(`<div class="isa_success">
         |                                                          ^
      48 | <i class="fa fa-check"></i>
      49 |     User logged in successfully!
      50 |      </div>`);

      at Object.toBe (__tests__/login.test.js:47:58)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
      at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
      at step (__tests__/login.test.js:3:191)
      at __tests__/login.test.js:3:361

Upvotes: 2

Views: 2791

Answers (1)

Allan Mogusu
Allan Mogusu

Reputation: 240

The solution was rather easy. I just had to add an await Promise.resolve.then() line to make it work:

    describe('login',() => {
        let fetchMock;
        let assignMock;

        beforeEach(() => {
            document.body.innerHTML +=`
            <div id="notify">
              </div>
            <form id="signin">
              <input type="email" id="email" value="[email protected]">
              <input type="password"  id="password" value ="test1234">
              <input type="submit" id="submit">
            </form>`;
            fetchMock = jest.spyOn(global,'fetch');
            fetchMock.mockImplementation(() =>Promise.resolve ({
                json: () => Promise.resolve({Message:"User logged in successfully!"})
            }));
            assignMock = jest.spyOn(window.location , "assign");
            assignMock.mockImplementation(() =>{});
            require('../UI/js/login');
        });
        afterEach(() => {
            fetchMock.mockRestore();
            assignMock.mockRestore();
            jest.resetModules()
        });
        it('fetch data and change the content of #notify', async function() {
            document.getElementById('submit').click();
            expect(fetchMock).toHaveBeenCalledTimes(1);
            const fetchArgs = fetchMock.mock.calls[0];
            expect(fetchArgs[0]).toBe('https://store-manager-api-app-v2.herokuapp.com/api/v2/auth/login');
            expect(fetchArgs[1]).toEqual({
                method: "POST",
                headers: {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin':'*',
                    'Access-Control-Request-Method': '*'
                },
                mode: "cors",
                body: JSON.stringify({
                    email: '[email protected]',
                    password: "test1234"
                })

            });
         await Promise.resolve().then();
         await Promise.resolve().then(); //on adding this the test passes
         expect(document.getElementById('notify').innerHTML).toBe(`<div class="isa_success">
    <i class="fa fa-check"></i>
        User logged in successfully!
         </div>`);
           expect(assignMock).toHaveBeenCalledTimes(1);
           expect(assignMock.mock.calls[0][0]).toBe("../HTML/index.html");
        });


});

Upvotes: 2

Related Questions