Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a Testcafe test to assert a loading spinner is visible after making a fetch request

I have the following scenario:

  • Load page
  • Expect spinner is hidden
  • Type username Click search
  • Expect spinner display
  • After a few seconds delay, expect spinner to hide
  • Assert the right user details are displayed

Here is the working demo enter image description here

I have mocked the network request in my test spec, but I am unable to understand how to assert spinner is visible after I click the search button

Here is my test spec:

    import {Selector, RequestMock} from "testcafe";
   import mockUser from "../mocks/mockUser.json";

var apiMocks = RequestMock()
  .onRequestTo(/\/api\/users/)
  .respond(mockUser, 200, {
    'access-control-allow-credentials': "*",
    'access-control-allow-origin': "*"
  })
fixture `When a user is searched`
  .page(`http://localhost:3000/`)
  .requestHooks(apiMocks);

test("Should fetch user details", async t => {
  const spinnerEl = Selector("[data-test-id='spinner']");

  await t.expect(spinnerEl.exists).notOk();

  await t
    .typeText("[data-test-id='txt-search']", "foo")
    .click("[data-test-id='btn-search']");
   // This line does not work
  // await t.expect(spinnerEl.exists).ok();
  await t.expect(Selector("[data-test-id='username']").innerText).eql("Foo Bar");
  await t.expect(Selector("[data-test-id='userid']").innerText).eql("foo");
})

I am new to TestCafe, could someone help me with this.

Thanks!

like image 205
Nikhil Avatar asked Nov 08 '19 16:11

Nikhil


1 Answers

It is difficult to check whether the described spinner element is shown due to the following:

  1. It is displayed only during a short period of time. This does not allow TestCafe to check it in time. Using mocks makes the spinner appear only for milliseconds.
  2. TestCafe waits for all requests and does not perform any actions until XHR requests are completed. This means that assertions will not start until your request is finished.

However, it's still possible to work around the issue. You can use MutationObserver and TestCafe ClientFunctions mechanism.

You can create your element observer using the ClientFunction. The observer will watch for the app element changes. If the spinner element appears the observer will be notified and set the window.spinnerWasShown variable to true.

After the button is clicked, you can check that the windows.spinnerWasShown variable is set to true.

Here is the full example:

import { Selector, RequestMock, ClientFunction } from "testcafe";
import mockUser from "../mocks/mockUser.json";

var apiMocks = RequestMock()
    .onRequestTo(/\/api.github.com\/users/)
    .respond(mockUser, 200, {
        'access-control-allow-credentials': "*",
        'access-control-allow-origin':      "*"
    });

fixture`When a user is searched`
    .page(`http://localhost:3000/`)
    .requestHooks(apiMocks);


const spinnerWasShown = ClientFunction(() => window.spinnerWasShown);


const observeSpinner = ClientFunction(() => {
    var appEl = document.querySelector('.app');

    const config = { attributes: true, childList: true };

    const callback = function(mutationsList) {
        for(let mutation of mutationsList) {
            if (mutation.type === 'childList') {
                for (var i =0; i < mutation.addedNodes.length; i++ )
                    window.spinnerWasShown = window.spinnerWasShown || mutation.addedNodes[i].className.indexOf('spinner') > -1;
            }
        }
    };

    const observer = new MutationObserver(callback);

    observer.observe(appEl, config);
});

test("Should fetch user details", async t => {
    const spinnerEl = Selector("[data-test-id='spinner']");

    await t.expect(spinnerEl.exists).notOk();

    await t.typeText("[data-test-id='txt-search']", "foo");

    await observeSpinner();

    await t.click("[data-test-id='btn-search']");

    await t.expect(spinnerWasShown()).eql(true);

    await t.expect(spinnerEl.exists).notOk();

    await t.expect(Selector("[data-test-id='username']").innerText).eql("Foo Bar");
    await t.expect(Selector("[data-test-id='userid']").innerText).eql("foo");
});
like image 175
Alex Kamaev Avatar answered Oct 19 '22 09:10

Alex Kamaev