vanillajs-browser-helpers
Version:
Collection of convenience code snippets (helpers) that aims to make it a little easier to work with vanilla JS in the browser
188 lines (131 loc) • 4.76 kB
text/typescript
import { triggerEvent } from './assets/helpers';
import eventOptionsSupported from '../eventOptionsSupported';
import once, { OnceEventListenerOptions } from '../once';
describe('"once"', () => {
function suite(elm?: HTMLElement | Window) {
const cb = jest.fn();
const evt = 'test';
const evts = [1, 2, 3].map((n) => evt + n);
const _once = (...args: [
string | string[],
EventListenerOrEventListenerObject,
OnceEventListenerOptions?
]) => elm ? once(elm, ...args) : once(...args);
const target = elm || document;
beforeAll(() => {
cb.mockReset();
});
beforeEach(() => {
cb.mockReset();
});
it('Returns a function that unbinds the event handler', () => {
const unbind = _once(evt, cb);
unbind();
triggerEvent(evt);
expect(cb).toHaveBeenCalledTimes(0);
});
describe('Binds event with "addEventListener"', () => {
let spy: jest.SpyInstance;
beforeAll(() => {
spy = jest.spyOn(target, 'addEventListener');
});
beforeEach(() => {
// we need to do the check to get the right number of calls,
// since the check uses "addEventListener"
eventOptionsSupported();
spy.mockClear();
});
afterAll(() => spy.mockRestore());
it.each(
['', '_', '-', '.', ':']
)('with separator: "%s"', (separator) => {
let e = evt;
if (separator) { e += separator + 'part'; }
const unbind = _once(e, cb);
expect(spy).toHaveBeenCalledTimes(1);
unbind();
});
it('For each event name in a list', () => {
const unbind = _once(evts, cb);
expect(spy).toHaveBeenCalledTimes(3);
unbind();
});
describe('When EventTarget is not supported, third argument in "addEventListener" is', () => {
const anyFunc = expect.any(Function);
let supportSpy;
beforeEach(() => {
supportSpy = jest.spyOn(document, 'addEventListener')
.mockImplementation(() => { throw new Error('nope'); });
const supported = eventOptionsSupported(true);
expect(supported).toBe(false);
// Since we are re-mocking the "document.addEventListener" above,
// we only "reset" when the target is the document, instead of restoring
target === document
? supportSpy.mockReset()
: supportSpy.mockRestore();
});
afterEach(() => {
eventOptionsSupported(true);
});
it('`false` when no options are given', () => {
const unbind = _once(evt, cb);
expect(spy).toHaveBeenCalledWith(evt, anyFunc, false);
unbind();
});
it.each([undefined, false])('`false` when "capture" is: %s', (capture) => {
const unbind = _once(evt, cb, { capture });
expect(spy).toHaveBeenCalledWith(evt, anyFunc, false);
unbind();
});
it('`true` when "capture" is true', () => {
const unbind = _once(evt, cb, { capture: true });
expect(spy).toHaveBeenCalledWith(evt, anyFunc, true);
unbind();
});
});
});
describe('Triggers event only once', () => {
it('Trigger given event only once', () => {
const unbind = _once(evt, cb);
triggerEvent(evt, target);
triggerEvent(evt, target);
expect(cb).toHaveBeenCalledTimes(1);
unbind();
});
it('Trigger given event the first time the "when" option is fulfilled', () => {
const when = jest.fn()
.mockReturnValue(true)
.mockReturnValueOnce(false);
const unbind = _once(evt, cb, { when });
triggerEvent(evt, target);
expect(cb).toHaveBeenCalledTimes(0);
expect(when).toHaveBeenCalledTimes(1);
triggerEvent(evt, target);
expect(cb).toHaveBeenCalledTimes(1);
expect(when).toHaveBeenCalledTimes(2);
triggerEvent(evt, target);
expect(cb).toHaveBeenCalledTimes(1);
expect(when).toHaveBeenCalledTimes(2);
unbind();
});
it('Multiple events handlers are triggered only once', () => {
const unbind = _once(evts, cb);
evts.forEach((e) => triggerEvent(e, target));
expect(cb).toHaveBeenCalledTimes(3);
cb.mockClear();
evts.forEach((e) => triggerEvent(e, target));
expect(cb).toHaveBeenCalledTimes(0);
unbind();
});
});
}
describe('With no Element given (falls back to Document)', () => {
suite();
});
describe('With a given HTML element', () => {
suite(document.body);
});
describe('With Window', () => {
suite(window);
});
});