Jest: не вызывается фиктивная функция window.location.assign .. это связано с уловкой Promise ()? - PullRequest
0 голосов
/ 07 января 2019

Я тестирую следующий файл

Тесты / клиент / blog.spec.js

    import axios from 'axios';

    import API_BASE from './config';   // '/api/v1/blog/'

    const deletePost = id => {
      console.log('ID: ', id);
      axios.delete(`${API_BASE}/blog/${id}`, {
          headers: { 'Content-type': 'application/json' },
          data: null, // data null is necessary to pass the headers
        })
        .then((result) => {
          console.log('AXIOS RESOLVED: ', result);
          window.location.assign('/admin?cache=false');
          console.log('CALLED window.location.assign with /admin?cache=false');
        })
        .catch((e) => {
          console.log('AXIOS REJECTED: ', e);
          window.location.assign('/admin?cache=true');
          console.log('CALLED window.location.assign with /admin?cache=true');
        });
      };

    const setupDeletePostHandler = () => {
      const links = Array.prototype.slice.call(document.querySelectorAll('.delete-post'), 0);
      if (links.length > 0) {
        links.forEach(el => {
          el.addEventListener('click', e => {
            e.preventDefault();
            e.currentTarget.style.pointerEvents = 'none';
            e.currentTarget.querySelector('i').classList.remove('is-hidden');
            const id = el.dataset.postId;
            return deletePost(id);
          });
        });
      }
    };

    const pageReady = page => {
      switch (page) {
        case 'admin-index':
          setupDeletePostHandler();
          break;
        default:
          break;
      }
    };

    export default {
      pageReady,
    };

со следующими характеристиками:

Тесты / клиент / blog.spec.js

    import Blog from '../../src/client/js/blog.js';

    import mockAxios from "axios";

    jest.mock('axios');

    describe('client/blog', () => {

      beforeAll(() => {
        jest.spyOn(window.location, 'assign').mockImplementation(() => {});
      });

      afterEach(() => {
        mockAxios.delete.mockClear();
      });

      afterAll(() => {
        window.location.assign.mockRestore();
      });

      it('set the DeletePostHandler', async function () {
        // WHEN
        const post = '<div class="posts"><div class="post">' +
          '<p>Today should be a great day to be alive!</p>' +
          '<div class="is-hidden">' +
          '<a id="link_1" class="delete-post" href="/admin/edit-post/" data-post-id="">delete<i class="is-hidden"></i></a>' +
          '</div></div>';
        document.body.innerHTML = post;
        Blog.pageReady('admin-index');
        // WHEN
        await document.querySelector('#link_1').click();
        // THEN
        expect(mockAxios.delete).toHaveBeenCalledTimes(1);
        expect(window.location.assign).toHaveBeenCalled();
        console.log('CALLS: ', window.location.assign.mock.calls);
        expect(window.location.assign).toHaveBeenCalledWith('/admin?cache=false');
      });

    });

это (сбой, вот консоль:

__ издевается __ / axios.js

    export default {
      delete: jest.fn((url) => {
        if (url === '/api/v1/blog/1') {
            return Promise.resolve({
              data: {},
              status: 200,
              statusText: 'OK',
              headers: {}
            });
        } else {
          return Promise.reject({
            data: {},
            status: 400,
            statusText: 'Error',
            headers: {}
          });
        }
      })
    };

console.log

    $ yarn test-client
    yarn run v1.9.4
    $ jest tests/client/*.js
     FAIL  tests/client/blog.spec.js
      client/blog
        ✕ set the DeletePostHandler (47ms)

      ● client/blog › set the DeletePostHandler

        expect(jest.fn()).toHaveBeenCalled()

        Expected mock function to have been called, but it was not called.

          33 |     // THEN
          34 |     expect(mockAxios.delete).toHaveBeenCalledTimes(1);
        > 35 |     expect(window.location.assign).toHaveBeenCalled();
             |                                    ^
          36 |     console.log('CALLS: ', window.location.assign.mock.calls);
          37 |     expect(window.location.assign).toHaveBeenCalledWith('/admin?cache=false');
          38 |   });

          at Object.toHaveBeenCalled (tests/client/blog.spec.js:35:36)
          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/client/blog.spec.js:22:191)
          at tests/client/blog.spec.js:22:361

      console.log src/client/js/blog.js:7
        ID:

      console.log src/client/js/blog.js:18
        AXIOS REJECTED:  { data: {}, status: 400, statusText: 'Error', headers: {} }

      console.log src/client/js/blog.js:20
        CALLED window.location.assign with /admin?cache=true

Странно ... в журнале показано, что при отклонении запроса Axios должна быть вызвана фиктивная функция ... но это не

Примечание. Когда я тестирую сценарий с разрешением запроса Axios, макет window.location.assign правильно вызывается ...

1 Ответ

0 голосов
/ 07 января 2019

Я сталкивался с подобными проблемами в прошлом.

Выпуск

catch не выполнено к тому времени, когда expect(window.location.assign).toHaveBeenCalled() запускается и завершается ошибкой.

Подробнее

click() на самом деле ничего не возвращает, поэтому await нечего ждать.

По моему опыту, вызов await допускает один цикл очереди PromiseJobs, поэтому then запускается и тест проходит, если запрос Axios разрешается.

catch, кажется, занимает два цикла очереди PromiseJobs, поэтому он не был выполнен тестом, продолжается после await и завершается ошибкой в ​​утверждении.

*

Решение * 1027 Решение состоит в том, чтобы убедиться, что catch был запущен до подтверждения. В идеале это можно сделать, вернув Promise и await в тесте, как вы пытаетесь это сделать. Сложность этого теста в том, что click на самом деле ничего не возвращает, поэтому ждать Promise не приходится. Для подобных ситуаций, когда невозможно await, фактический Promise хороший обходной путь - await разрешенное Promise для требуемого количества циклов в очереди PromiseJobs. Каждый раз, когда Promise равен await, остальная часть теста, по существу, ставится в очередь в конце PromiseJobs и позволяет выполнить все, что уже находится в очереди, перед продолжением теста. В этом случае ожидание двух циклов очереди PromiseJobs даст catch возможность запуска: document.querySelector('#link_1').click(); // remove "await" since nothing is returned await Promise.resolve().then(); // wait two cycles of the PromiseJobs queue // THEN expect(mockAxios.delete).toHaveBeenCalledTimes(1); expect(window.location.assign).toHaveBeenCalled(); // SUCCESS

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...