score:1

Accepted answer

There isn't much around needing to somehow wait for the promise to fulfill before ending the test. There are two main ways of doing it from your code that I can see.

  1. independently test that onClick and your promise methods. So check that onClick calls the correct function, but spying on setTextWithPromise, triggering a click and asserting that setTextWithPromise was called. Then you can also get the component instance and call that method which returns the promise you can attach a handler and assert it did the right thing.

  2. expose a callback prop that you can pass in that is called when the promise resolves.

score:67

Updated answer: using async / await leads to cleaner code. Old code below.

I've successfully solved this problem by combining the following elements:

  • Mock out the promise and make it resolve immediately
  • Make the test asynchronous by marking the test function async
  • After simulating the click, wait until the next macrotask to give the promise time to resolve

In your example, that might look like this:

// Mock the promise we're testing
global.doSomethingWithAPromise = () => Promise.resolve();

// Note that our test is an async function
it('displays the promise text after click of the button', async () => {
    wrapper.find('#promiseBtn').simulate('click');
    await tick();
    expect(wrapper.find('#promiseText').text()).toEqual('there is text!');
});

// Helper function returns a promise that resolves after all other promise mocks,
// even if they are chained like Promise.resolve().then(...)
// Technically: this is designed to resolve on the next macrotask
function tick() {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  })
}

Enzyme's update() is neither sufficient nor needed when using this method, because Promises never resolve in the same tick they are created -- by design. For a very detailed explanation of what is going on here, see this question.

Original answer: same logic but slightly less pretty. Use Node's setImmediate to defer the test until the next tick, which is when the promise will resolve. Then call Jest's done to finish the test asynchronously.

global.doSomethingWithAPromise = () => Promise.resolve({});

it('displays the promise text after click of the button', (done) => {
    wrapper.find('#promiseBtn').simulate('click');

  setImmediate( () => {
    expect(wrapper.find('#promiseText').text()).toEqual('there is text!');
    done();
  })
});

This isn't as nice because you'll get big nested callbacks if you have to wait for more than one promise.


Related Query

More Query from same tag