@akamfoad/qrcode
Version:
The library is generating QR codes as SVG, HTML5 Canvas, PNG and JPG files, or text.
390 lines (349 loc) • 13.1 kB
text/typescript
import { beforeEach, describe, expect, it, vi } from 'vitest';
import QRCodeCanvas from '../QRCodeCanvas';
global.ImageData = function (bytes, width, height) {
this.bytes = bytes;
this.width = width;
this.height = height;
};
const MockCanvasRenderingContext2D = class {
putImageData: () => void;
drawImage: () => void;
imageSmoothingEnabled;
constructor() {
this.putImageData = vi.fn();
this.drawImage = vi.fn();
this.imageSmoothingEnabled = true;
}
};
HTMLCanvasElement.prototype.getContext = vi.fn(function () {
this.mockedContext = new MockCanvasRenderingContext2D();
return this.mockedContext;
});
describe('QRCodeCanvas', () => {
describe('constructor', () => {
it('should use default params if nothing is provided', () => {
// @ts-expect-error testing undefined case
const qrCode = new QRCodeCanvas();
expect(qrCode.value).toBeUndefined();
expect(qrCode.padding).toEqual(1);
expect(qrCode.level).toEqual('L');
expect(qrCode.typeNumber).toEqual(0);
expect(qrCode.errorsEnabled).toBeFalsy();
expect(qrCode.invert).toBeFalsy();
expect(qrCode.fgColor).toEqual('#000');
expect(qrCode.bgColor).toEqual('#FFF');
expect(qrCode.scale).toEqual(10);
expect(qrCode.size).toBeNull();
});
it('should default params for not specified params', () => {
const qrCode = new QRCodeCanvas('test 42', { level: 'Q', size: 100 });
expect(qrCode.value).toEqual('test 42');
expect(qrCode.padding).toEqual(1);
expect(qrCode.level).toEqual('Q');
expect(qrCode.typeNumber).toEqual(0);
expect(qrCode.errorsEnabled).toBeFalsy();
expect(qrCode.invert).toBeFalsy();
expect(qrCode.fgColor).toEqual('#000');
expect(qrCode.bgColor).toEqual('#FFF');
expect(qrCode.scale).toEqual(10);
expect(qrCode.size).toEqual(100);
});
it('should use specified params', () => {
const qrCode = new QRCodeCanvas('test 84', {
level: 'H',
padding: 0,
typeNumber: 20,
invert: true,
errorsEnabled: true,
fgColor: '#AAAA',
bgColor: '#FFF0',
scale: 11,
size: 100,
});
expect(qrCode.value).toEqual('test 84');
expect(qrCode.padding).toEqual(0);
expect(qrCode.level).toEqual('H');
expect(qrCode.typeNumber).toEqual(20);
expect(qrCode.errorsEnabled).toBeTruthy();
expect(qrCode.invert).toBeTruthy();
expect(qrCode.fgColor).toEqual('#AAAA');
expect(qrCode.bgColor).toEqual('#FFF0');
expect(qrCode.scale).toEqual(11);
expect(qrCode.size).toEqual(100);
});
});
describe('_clearCache', () => {
it('should clear qrCodeData and qrCodeText', () => {
const qrCode = new QRCodeCanvas('test');
qrCode.qrCodeData = [1, 2, 3, 4];
qrCode._clearCache();
expect(qrCode.qrCodeData).toBeNull();
});
});
describe('_getCanvasSize', () => {
it('should return null if dataSize is empty', () => {
const qrCode = new QRCodeCanvas('test');
qrCode.getDataSize = vi.fn(() => null);
expect(qrCode._getCanvasSize()).toBeNull();
qrCode.getDataSize = vi.fn(() => 0);
expect(qrCode._getCanvasSize()).toBeNull();
});
it('should return size if it is specified', () => {
const qrCode = new QRCodeCanvas('test', { size: 42 });
qrCode.getDataSize = vi.fn(() => 21);
expect(qrCode._getCanvasSize()).toEqual(42);
});
it('should return scaled dataSize if size is not specified', () => {
const qrCode = new QRCodeCanvas('test', { scale: 5 });
qrCode.getDataSize = vi.fn(() => 21);
expect(qrCode._getCanvasSize()).toEqual(105);
});
it('should return dataSize if size and scale are not specified', () => {
const qrCode = new QRCodeCanvas('test', { scale: 0 });
qrCode.getDataSize = vi.fn(() => 21);
expect(qrCode._getCanvasSize()).toEqual(21);
});
});
describe('_getImageSource', () => {
it('should return promise if string is passed', () => {
const qrCode = new QRCodeCanvas('test');
expect(qrCode._getImageSource({ source: 'foo-bar.png' })).toBeInstanceOf(
Promise,
);
});
it('should return Image if Image is passed', () => {
const qrCode = new QRCodeCanvas('test');
const img = new Image();
img.src = 'https://some-url.com/foo.png';
expect(qrCode._getImageSource({ source: img })).toEqual(img);
});
it('should return Canvas if Canvas is passed', () => {
const qrCode = new QRCodeCanvas('test');
const canvas = document.createElement('canvas');
expect(qrCode._getImageSource({ source: canvas })).toEqual(canvas);
});
it('should return null if source has wrong type', () => {
const qrCode = new QRCodeCanvas('test');
expect(qrCode._getImageSource({ source: true })).toEqual(null);
expect(qrCode._getImageSource({ source: 42 })).toEqual(null);
expect(qrCode._getImageSource({ source: undefined })).toEqual(null);
expect(qrCode._getImageSource({ source: null })).toEqual(null);
});
});
describe('_drawImage', () => {
let qrCode;
let imageConfig;
const qrCodeCanvasContext = {
drawImage: vi.fn(),
};
const pixelSize = 10;
beforeEach(() => {
qrCode = new QRCodeCanvas('test');
imageConfig = {
source: new Image(),
width: 10,
height: 12,
x: 1,
y: 2,
border: null,
};
qrCode._getImageConfig = vi.fn(() => imageConfig);
});
it('should return null if imageConfig is not provided', () => {
imageConfig = null;
expect(qrCode._drawImage(qrCodeCanvasContext, pixelSize)).toBeNull();
});
it('should return promise if source is a promise', () => {
const img = new Image();
const promise = Promise.resolve(img);
imageConfig.source = promise;
expect(qrCode._drawImage(qrCodeCanvasContext, pixelSize)).toEqual(
promise,
);
expect(qrCodeCanvasContext.drawImage).not.toHaveBeenCalled();
return promise.then(() => {
expect(qrCodeCanvasContext.drawImage).toHaveBeenCalledWith(
img,
10,
20,
100,
120,
);
});
});
it('should return true if source is an Image', () => {
const img = new Image();
imageConfig.source = img;
expect(qrCode._drawImage(qrCodeCanvasContext, pixelSize)).toEqual(true);
expect(qrCodeCanvasContext.drawImage).toHaveBeenCalledWith(
img,
10,
20,
100,
120,
);
});
it('should return true if source is a canvas', () => {
const canvas = document.createElement('canvas');
imageConfig.source = canvas;
expect(qrCode._drawImage(qrCodeCanvasContext, pixelSize)).toEqual(true);
expect(qrCodeCanvasContext.drawImage).toHaveBeenCalledWith(
canvas,
10,
20,
100,
120,
);
});
});
describe('getCanvas', () => {
it('should return result of call draw()', () => {
const qrCode = new QRCodeCanvas('test');
qrCode.draw = vi.fn(() => null);
expect(qrCode.getCanvas()).toBeNull();
const canvas = document.createElement('canvas');
qrCode.draw = vi.fn(() => canvas);
expect(qrCode.getCanvas()).toBe(canvas);
qrCode.draw = vi.fn(() => Promise.resolve(canvas));
expect(qrCode.getCanvas()).toBeInstanceOf(Promise);
});
});
describe('draw', () => {
it('should return null if dataSize is empty', () => {
const qrCode = new QRCodeCanvas('test');
qrCode.getDataSize = vi.fn(() => null);
expect(qrCode.draw()).toBeNull();
});
it('should return null if data are empty', () => {
const qrCode = new QRCodeCanvas('test');
qrCode.getData = vi.fn(() => null);
expect(qrCode.draw()).toBeNull();
});
it('should draw QR code and return new canvas', () => {
const qrCode = new QRCodeCanvas('test', {
bgColor: '#05060700',
fgColor: '#01020304',
scale: 8,
});
qrCode.getData = vi.fn(() => [
[true, true, true, true, true],
[true, false, false, false, true],
[true, false, true, false, true],
[true, false, false, false, true],
[true, true, true, true, true],
]);
const qrCodeCanvas = qrCode.draw();
expect(qrCodeCanvas).toBeInstanceOf(HTMLCanvasElement);
expect(qrCode.canvasContext.putImageData).toHaveBeenCalledTimes(1);
const putImageDataArgs = qrCode.canvasContext.putImageData.mock.calls[0];
const imageData = putImageDataArgs[0];
expect(imageData).toBeInstanceOf(ImageData);
expect(imageData.bytes).toBeInstanceOf(Uint8ClampedArray);
const expectUint8ClampedArray = new Uint8ClampedArray(5 * 5 * 4);
expectUint8ClampedArray.set(
[
1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3,
4, 5, 6, 7, 0, 5, 6, 7, 0, 5, 6, 7, 0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6,
7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 0, 5,
6, 7, 0, 5, 6, 7, 0, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4,
1, 2, 3, 4, 1, 2, 3, 4,
],
0,
);
expect(imageData.bytes).toEqual(expectUint8ClampedArray);
expect(imageData.width).toEqual(5);
expect(imageData.height).toEqual(5);
expect(qrCode.canvas.width).toEqual(5);
expect(qrCode.canvas.height).toEqual(5);
expect(putImageDataArgs[1]).toEqual(0);
expect(putImageDataArgs[2]).toEqual(0);
expect(qrCodeCanvas.mockedContext.imageSmoothingEnabled).toEqual(false);
expect(qrCodeCanvas.width).toEqual(40);
expect(qrCodeCanvas.height).toEqual(40);
expect(qrCodeCanvas.mockedContext.drawImage).toHaveBeenCalledWith(
qrCode.canvas,
0,
0,
40,
40,
);
});
it('should draw QR code and return provided canvas', () => {
const qrCode = new QRCodeCanvas('test', {
bgColor: '#05060700',
fgColor: '#01020304',
scale: 8,
});
qrCode.getData = vi.fn(() => [
[true, true, true, true, true],
[true, false, false, false, true],
[true, false, true, false, true],
[true, false, false, false, true],
[true, true, true, true, true],
]);
const canvas = document.createElement('canvas');
const qrCodeCanvas = qrCode.draw(canvas);
expect(qrCodeCanvas).toBeInstanceOf(HTMLCanvasElement);
expect(qrCodeCanvas).toBe(canvas);
expect(qrCode.canvasContext.putImageData).toHaveBeenCalledTimes(1);
const putImageDataArgs = qrCode.canvasContext.putImageData.mock.calls[0];
const imageData = putImageDataArgs[0];
expect(imageData).toBeInstanceOf(ImageData);
expect(imageData.bytes).toBeInstanceOf(Uint8ClampedArray);
const expectUint8ClampedArray = new Uint8ClampedArray(5 * 5 * 4);
expectUint8ClampedArray.set(
[
1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3,
4, 5, 6, 7, 0, 5, 6, 7, 0, 5, 6, 7, 0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6,
7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 0, 5,
6, 7, 0, 5, 6, 7, 0, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4,
1, 2, 3, 4, 1, 2, 3, 4,
],
0,
);
expect(imageData.bytes).toEqual(expectUint8ClampedArray);
expect(imageData.width).toEqual(5);
expect(imageData.height).toEqual(5);
expect(qrCode.canvas.width).toEqual(5);
expect(qrCode.canvas.height).toEqual(5);
expect(putImageDataArgs[1]).toEqual(0);
expect(putImageDataArgs[2]).toEqual(0);
expect(qrCodeCanvas.mockedContext.imageSmoothingEnabled).toEqual(false);
expect(qrCodeCanvas.width).toEqual(40);
expect(qrCodeCanvas.height).toEqual(40);
expect(qrCodeCanvas.mockedContext.drawImage).toHaveBeenCalledWith(
qrCode.canvas,
0,
0,
40,
40,
);
});
});
describe('toDataURL', () => {
let canvas;
beforeEach(() => {
HTMLCanvasElement.prototype.toDataURL = vi.fn(() => 'data:url');
canvas = document.createElement('canvas');
});
it('should return null if canvas is not drawn', () => {
const qrCode = new QRCodeCanvas('test');
qrCode.draw = vi.fn(() => null);
expect(qrCode.toDataUrl()).toBeNull();
});
it('should return promise if draws returns a promise', () => {
const qrCode = new QRCodeCanvas('test');
qrCode.draw = vi.fn(() => Promise.resolve(canvas));
const result = qrCode.toDataUrl();
expect(result).toBeInstanceOf(Promise);
return result.then((url) => {
expect(url).toEqual('data:url');
});
});
it('should return string if draws returns a canvas', () => {
const qrCode = new QRCodeCanvas('test');
qrCode.draw = vi.fn(() => canvas);
expect(qrCode.toDataUrl()).toEqual('data:url');
});
});
});