Search code examples
javascriptunit-testingava

Ava testing image-loading with promise: promise returned by test never resolved


I want to test my promise function to load an image, the piece of code looks like this:

function imagePromise() {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.addEventListener('load', () => resolve(true));
    img.addEventListener('error', () => reject(false));
    img.src = 'http://78.media.tumblr.com/tumblr_lr9mx5Axur1qlyuwso1_1280.jpg';
  });
}

When I consume my promise in the browser, it works fine:

imagePromise().then((result) => console.log(result));
// fulfill the promise and log the result: true

However, when I'm testing my promise using ava and browser-env, ava returns an error:

Promise returned by test never resolved

My test file looks like this:

import test from 'ava';

function imagePromise() {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.addEventListener('load', () => resolve(true));
    img.addEventListener('error', () => reject(false));
    img.src = 'http://78.media.tumblr.com/tumblr_lr9mx5Axur1qlyuwso1_1280.jpg';
  });
}

test('load image with promise', (t) => {
  return imagePromise().then((result) => {
    t.is(result, true);
  });
});

Note that I have both tried addEventListener and onload/onerror methods due to browser compatibility, and browser-env have been configured to work with ava. Is there something I'm missing?


Solution

  • I finally fixed my tests, using browser-env (which itself uses jsdom). I've made a small repo for people interested:

    My first mistake was I forgot to install canvas-prebuilt, which allow jsdom to manipulate images and canvas elements.

    // install browser-env and canvas-prebuilt
    // (https://github.com/jsdom/jsdom#loading-subresources)
    yarn add browser-env canvas-prebuilt -D
    

    My second mistake that I forgot to put a special config on the browser-env declaration for the tests, in the test/helpers/setup-browser-env.js file:

    require('browser-env')(['window', 'document', 'Image'], {
      resources: 'usable',
    });
    

    Then we can run our image-loading promises tests, from test/test.js:

    import test from 'ava';
    
    function imagePromise() {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.addEventListener('load', () => resolve(true));
        img.addEventListener('error', () => reject(false));
        // an heavy 4k wallpaper to check if promise is resolved after a long time
        img.src = 'https://www.wallpaperup.com/wallpaper/download/991808/8000/5224';
      });
    }
    
    test('load image with promise', (t) => {
      return imagePromise().then((result) => {
        t.is(result, true);
      });
    });
    

    To run the tests, you can exec ava but I highly recommend to add --verbose flag as it helps to debug with --watch flag (kind of a live-reload for your tests).