Below is the provided solution for unit testing:
index.ts
:
export default class MyClass {
constructor() {
this.render();
}
render() {
const el: HTMLInputElement = document.createElement('input') as HTMLInputElement;
const link: HTMLAnchorElement = document.createElement('a') as HTMLAnchorElement;
const container: HTMLBodyElement = document.querySelector('body') as HTMLBodyElement;
link.innerHTML = 'Click Me!';
link.setAttribute('href', '#');
link.setAttribute('target', '_blank');
el.setAttribute('type', 'file');
container.appendChild(el);
container.appendChild(link);
el.addEventListener('change', (event) => {
if ('files' in el) {
const availFile: File = el.files![0];
const blob = new Blob([availFile], { type: availFile.type });
const objectURL = window.URL.createObjectURL(blob);
link.setAttribute('href', objectURL);
}
});
}
}
index.test.ts
:
import MyClass from './index';
describe('testing as first', () => {
let createElement;
let querySelector;
let createObjectURL;
beforeEach(() => {
createElement = document.createElement;
querySelector = document.querySelector;
createObjectURL = window.URL.createObjectURL;
});
afterEach(() => {
jest.restoreAllMocks();
document.createElement = createElement;
document.querySelector = querySelector;
window.URL.createObjectURL = createObjectURL;
});
it('should call render method', () => {
jest.spyOn(MyClass.prototype, 'render').mockReturnValueOnce();
const myClass = new MyClass();
expect(myClass.render).toBeCalledTimes(1);
});
it('should render element and handle change event if input has files', () => {
const myClass = new MyClass();
const mAvailFile = new Blob(['I am file'], { type: 'text/html' });
const mInput = { setAttribute: jest.fn(), addEventListener: jest.fn(), files: [mAvailFile] };
const mLink = { setAttribute: jest.fn(), innerHTML: '' };
const mContainer = { appendChild: jest.fn() };
document.createElement = jest.fn().mockImplementation((tagName) => {
switch (tagName) {
case 'input':
return mInput;
case 'a':
return mLink;
}
});
document.querySelector = jest.fn().mockReturnValueOnce(mContainer);
mInput.addEventListener.mockImplementationOnce((event, callback) => {
callback();
});
const mObjectURL = 'blob:https://www.google.com/6e165b50-979b-43f6-b685-7163413f0faf';
window.URL.createObjectURL = jest.fn().mockReturnValueOnce(mObjectURL);
myClass.render();
expect(document.createElement).toBeCalledTimes(2);
expect(document.querySelector).toBeCalledWith('body');
expect(mLink.innerHTML).toBe('Click Me!');
expect(mLink.setAttribute.mock.calls[0]).toEqual(['href', '#']);
expect(mLink.setAttribute.mock.calls[1]).toEqual(['target', '_blank']);
expect(mInput.setAttribute).toBeCalledWith('type', 'file');
expect(mContainer.appendChild.mock.calls[0]).toEqual([mInput]);
expect(mContainer.appendChild.mock.calls[1]).toEqual([mLink]);
expect(mInput.addEventListener).toBeCalledWith('change', expect.any(Function));
expect(window.URL.createObjectURL).toBeCalledWith(new Blob([mAvailFile], { type: mAvailFile.type }));
expect(mLink.setAttribute.mock.calls[2]).toEqual(['href', mObjectURL]);
});
it('should render element', () => {
const myClass = new MyClass();
const mInput = { setAttribute: jest.fn(), addEventListener: jest.fn() };
const mLink = { setAttribute: jest.fn(), innerHTML: '' };
const mContainer = { appendChild: jest.fn() };
document.createElement = jest.fn().mockImplementation((tagName) => {
switch (tagName) {
case 'input':
return mInput;
case 'a':
return mLink;
}
});
document.querySelector = jest.fn().mockReturnValueOnce(mContainer);
mInput.addEventListener.mockImplementationOnce((event, callback) => {
callback();
});
myClass.render();
expect(document.createElement).toBeCalledTimes(2);
expect(document.querySelector).toBeCalledWith('body');
expect(mLink.innerHTML).toBe('Click Me!');
expect(mLink.setAttribute.mock.calls[0]).toEqual(['href', '#']);
expect(mLink.setAttribute.mock.calls[1]).toEqual(['target', '_blank']);
expect(mInput.setAttribute).toBeCalledWith('type', 'file');
expect(mContainer.appendChild.mock.calls[0]).toEqual([mInput]);
expect(mContainer.appendChild.mock.calls[1]).toEqual([mLink]);
expect(mInput.addEventListener).toBeCalledWith('change', expect.any(Function));
});
});
Unit tests completed with full coverage at 100%:
PASS src/stackoverflow/59833555/index.test.ts (14.002s)
testing as first
✓ should call render method (6ms)
✓ should render element and handle change event if input has files (19ms)
✓ should render element (13ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 16.304s, estimated 17s
Original source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59833555