使用 jsdom、sinon、mocha 和 chai 测试图像加载

问题描述 投票:0回答:2

谁能帮我测试以下功能

function onload(cb){
  const image = 'http://placehold.it/350x150'
  const img = new Image()
  img.src = image
  img.onload = () => {
    cb()
  }
}

在我的测试文件中

Image
可以通过jsdom获得。我希望我可以测试
cb
是否被调用Once

javascript unit-testing jsdom sinon-chai
2个回答
6
投票

选项1:在JSDOM中主动加载外部资源

使用

resources: 'usable'
初始化 JSDOM 通常对于最新的 API 就足够了 (
v10+
):

const jsdom = new JSDOM('<!doctype html><html><body></body></html>',{
    resources: 'usable'
});
const { window } = jsdom;

它需要

canvas
canvascanvas-prebuilt)模块,如JSDOM文档中所述。


选项 2:模拟 window.Image 来控制它

使用最新的

canvas
模块,您可以模拟
window.Image
来触发
load
事件。

首先为

window.Image
类创建一个模拟:

import CanvasImage from 'canvas/lib/image';
import fs from 'fs';
import path from 'path';
import { URL } from 'url';
import request from 'request';
// Where to fetch assets on the file system when path are provided.
const ASSETS_DIRECTORY = path.join(__dirname,'.');

const WindowImage = function() {    
    // Reimplemented the following class:
    // https://github.com/tmpvar/jsdom/blob/master/lib/jsdom/living/nodes/HTMLImageElement-impl.js
    // https://github.com/Automattic/node-canvas#imagesrcbuffer
    let source;
    let image;
    let onload;
    let onerror;
    return {
        set src (value) {
            // TODO Throw errors to the Image.onerror handler.
            const onDataLoaded = function (data) {
                image = new CanvasImage();
                image.onload = () => {
                    if (onload) {
                        onload(image);
                    }
                };
                image.onerror = err => {
                    if (onerror) {
                        onerror(err);
                    }
                };
                image.src = data;
                source = value;
            };
            // URL or path?
            let url;
            try {
                url = new URL(value);
            } catch (e) {}
            // Fetch the data.
            if (url) {
                request(url.href, (error, response, body) => {
                    if (response && response.statusCode !== undefined && response.statusCode !== 200) {
                        throw new Error("Status code: " + response.statusCode);
                    }
                    onDataLoaded(body);
                });
            } else {
                // Assume it is a file path: try a local file read.
                fs.readFile(path.join(ASSETS_DIRECTORY,value),function (err,data) {
                    if (err) {
                        throw err;
                    }
                    onDataLoaded(data);
                });
            }
        },
        set onload (handler) {
            onload = handler;
        },
        set onerror (handler) {
            onerror = handler;
        },
        get src () {
            return source;
        },
        // TODO Should allows to modify height and width
        // + add natural height and width
        // Cf. https://github.com/tmpvar/jsdom/blob/master/lib/jsdom/living/nodes/HTMLImageElement-impl.js#L51
        get width () {
            return image && image.width;
        },
        get height () {
            return image && image.height;
        }
    };
};

然后你可以在测试中通过重新定义 JSDOM 使用的来模拟

window.Image

window.Image = WindowImage;

这并不是很优雅,但它可以让您控制图像数据的加载方式,因为

canvas.Image.src
通常可以获取任何缓冲区。


0
投票

使用Vitest:

onload.ts:

export function onload(cb: CallableFunction) {
  const image = 'http://placehold.it/350x150';
  const img = new Image();
  img.src = image;
  img.onload = () => {
    cb();
  };
}

onload.test.ts:

// @vitest-environment jsdom

import { describe, expect, it, vi, afterEach, beforeEach } from 'vitest';
import { onload } from './onload.ts';

describe(`fn onload`, () => {
  const event = {} as Event;
  let img: HTMLImageElement;

  beforeEach(() => {
    vi.spyOn(window, 'Image').mockImplementation(() => {
      img = {} as HTMLImageElement;
      return img;
    });
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('should call the callback function once on image load', () => {
    expect.assertions(1);

    const cb = vi.fn();

    onload(cb);
    img.onload?.(event);

    expect(cb).toHaveBeenCalledOnce();
  });
});
© www.soinside.com 2019 - 2024. All rights reserved.