Reputation: 21
I'm working on unit testing a custom element written with lit-element using open-wc. We are using Karma, Mocha, Sinon and Chai. I'm trying to test the element's constructor:
constructor() {
super();
window.addEventListener(
'click',
this._handleClickOutside
);
}
For reference, the functions used in the test are:
_handleClickOutside = () => {
console.log('calling real')
this.active = false;
};
disconnectedCallback() {
window.removeEventListener('click', this._handleClickOutside);
}
In order to test this, I need to stub this._handleClickOutside and check that when a click event is dispatched, this._handleClickOutside is called. Here is my attempt at a test, where el is my custom element that has been initialized with open-wc:
it('should add event listener to window for click', () => {
const clickEvent = new Event('click');
el.disconnectedCallback();
const spy = sinon.stub(el, '_handleClickOutside').callsFake( () => {console.log('calling fake')});
el._handleClickOutside = spy;
el.constructor();
window.dispatchEvent(clickEvent);
expect(spy.callCount).to.equal(1);
});
I understand that when I use open-wc to create the element the constructor will be called before my stub is in place so I've tried to remove the event listener with el.disconnectedCallback(), stub the function, and then call the constructor again to add the event listener using the stub. However, this is still calling the real function.
I've been able to get the test to work by using an anonymous function in the constructor that calls this._handleClickOutside but I don't see a way to remove the event listener in the disconnected callback if I do it this way. Wondering why calling the constructor again when the stub is in place isn't working to stub the function.
Upvotes: 2
Views: 2169
Reputation: 102587
You need to do some refactoring. You need to change _handleClickOutside
class property method to the class prototype method. So that you can stub it before instancing the element.
E.g.
element.ts
:
class MyElement {
active = true;
constructor() {
this._handleClickOutside = this._handleClickOutside.bind(this);
window.addEventListener("click", this._handleClickOutside);
}
_handleClickOutside() {
console.log("calling real");
this.active = false;
}
}
export default MyElement;
element.test.ts
:
import MyElement from "./element";
import sinon from "sinon";
import { expect } from "chai";
// You don't need to setup jsdom in browser test environment
// jsdom start
import jsdom from "jsdom";
const html = '<!doctype html><html><head><meta charset="utf-8">' + "</head><body></body></html>";
const url = "http://localhost";
const document = new jsdom.JSDOM(html, { url });
const window = document.window;
(global as any).document = window.document;
(global as any).window = window;
// jsdom end
describe("MyElement", () => {
afterEach(() => {
sinon.restore();
});
describe("#construtor", () => {
it("should pass", () => {
const addEventListenerStub = sinon.stub(window, "addEventListener");
const handleClickOutsideStub = sinon.stub(MyElement.prototype, "_handleClickOutside").callsFake(() => {
console.log("calling fake");
});
new MyElement();
addEventListenerStub.yield();
sinon.assert.calledWithExactly(addEventListenerStub, "click", sinon.match.func);
sinon.assert.calledOnce(handleClickOutsideStub);
});
});
describe("#_handleClickOutside", () => {
it("should pass", () => {
const el = new MyElement();
el._handleClickOutside();
expect(el.active).to.be.false;
});
});
});
Unit test result with 100% coverage:
MyElement
#construtor
calling fake
✓ should pass
#_handleClickOutside
calling real
✓ should pass
2 passing (11ms)
-----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
element.test.ts | 100 | 100 | 100 | 100 | |
element.ts | 100 | 100 | 100 | 100 | |
-----------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/59567755
Upvotes: 1