2

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?

TotomInc
  • 613
  • 1
  • 7
  • 21

2 Answers2

3

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).

TotomInc
  • 613
  • 1
  • 7
  • 21
0

How about move image creation from imagePromise to ImageClass? After that you will have control on promise resolving / rejection

import test from "ava";
import { EventEmitter } from "events";

function imagePromise(img) {
    return new Promise((resolve, reject) => {
        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 => {
    class ImageClass extends EventEmitter {
        addEventListener(type, fn) {
            this.on(type, fn);
        }
    }

    const image = new ImageClass();

    process.nextTick(() => image.emit("load"));

    return imagePromise(image).then(result => {
        t.is(result, true);
    });
});
Krzysztof Safjanowski
  • 7,292
  • 3
  • 35
  • 47
  • I've made a rough example of how I wanted to do things, but behind this I have a loader class with sub-loaders classes (such as ImageLoader, AudioLoader, ...). But I finally managed to fix my test, I'm going to write it as an answer in a few mins. Thanks for the help. – TotomInc Feb 18 '18 at 14:26