reblend-testing-library
Version:
Simple and complete Reblendjs testing utilities that encourage good testing practices.
230 lines (229 loc) • 6.88 kB
JavaScript
import { Reblend, useEffect, useEffectAfter, useRef } from 'reblendjs';
import { render, fireEvent } from '../';
const eventTypes = [{
type: 'Clipboard',
events: ['copy', 'paste'],
elementType: 'input'
}, {
type: 'Composition',
events: ['compositionEnd', 'compositionStart', 'compositionUpdate'],
elementType: 'input'
}, {
type: 'Keyboard',
events: ['keyDown', 'keyPress', 'keyUp'],
elementType: 'input',
init: {
keyCode: 13
}
}, {
type: 'Focus',
events: ['focus', 'blur'],
elementType: 'input'
}, {
type: 'Form',
events: ['focus', 'blur'],
elementType: 'input'
}, {
type: 'Focus',
events: ['input', 'invalid'],
elementType: 'input'
}, {
type: 'Focus',
events: ['submit'],
elementType: 'form'
}, {
type: 'Mouse',
events: ['click', 'contextMenu', 'doubleClick', 'drag', 'dragEnd', 'dragEnter', 'dragExit', 'dragLeave', 'dragOver', 'dragStart', 'drop', 'mouseDown', 'mouseEnter', 'mouseLeave', 'mouseMove', 'mouseOut', 'mouseOver', 'mouseUp'],
elementType: 'button'
}, {
type: 'Pointer',
events: ['pointerOver', 'pointerEnter', 'pointerDown', 'pointerMove', 'pointerUp', 'pointerCancel', 'pointerOut', 'pointerLeave', 'gotPointerCapture', 'lostPointerCapture'],
elementType: 'button'
}, {
type: 'Selection',
events: ['select'],
elementType: 'input'
}, {
type: 'Touch',
events: ['touchCancel', 'touchEnd', 'touchMove', 'touchStart'],
elementType: 'button'
}, {
type: 'UI',
events: ['scroll'],
elementType: 'div'
}, {
type: 'Wheel',
events: ['wheel'],
elementType: 'div'
}, {
type: 'Media',
events: ['abort', 'canPlay', 'canPlayThrough', 'durationChange', 'emptied', 'encrypted', 'ended', 'error', 'loadedData', 'loadedMetadata', 'loadStart', 'pause', 'play', 'playing', 'progress', 'rateChange', 'seeked', 'seeking', 'stalled', 'suspend', 'timeUpdate', 'volumeChange', 'waiting'],
elementType: 'video'
}, {
type: 'Image',
events: ['load', 'error'],
elementType: 'img'
}, {
type: 'Animation',
events: ['animationStart', 'animationEnd', 'animationIteration'],
elementType: 'div'
}, {
type: 'Transition',
events: ['transitionEnd'],
elementType: 'div'
}];
const UNSUPPORTED_EVENTS = [
// Clipboard events
'copy', 'paste',
// Composition events
'compositionStart', 'compositionUpdate', 'compositionEnd',
// Touch events
'touchCancel', 'touchEnd', 'touchMove', 'touchStart',
// Pointer events
'pointerOver', 'pointerEnter', 'pointerDown', 'pointerMove', 'pointerUp', 'pointerCancel', 'pointerOut', 'pointerLeave', 'gotPointerCapture', 'lostPointerCapture',
// Animation events
'animationStart', 'animationEnd', 'animationIteration',
// Transition events
'transitionEnd',
// Media events (only those that fail)
'encrypted', 'timeUpdate',
// doubleClick (synthetic event, not always mapped correctly)
'doubleClick',
// DragExit (not supported in JSDOM)
'dragExit'];
eventTypes.forEach(({
type,
events,
elementType,
init
}) => {
describe(`${type} Events`, () => {
events.forEach(eventName => {
const propName = `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`;
if (UNSUPPORTED_EVENTS.includes(eventName)) {
it.skip(`triggers ${propName} (unsupported in JSDOM)`, () => {});
return;
}
it(`triggers ${propName}`, async () => {
const ref = useRef();
const spy = jest.fn();
await render(Reblend.construct(elementType, {
[propName]: spy,
ref
}));
fireEvent[eventName](ref.current, init);
expect(spy).toHaveBeenCalledTimes(1);
});
});
});
});
eventTypes.forEach(({
type,
events,
elementType,
init
}) => {
describe(`Native ${type} Events`, () => {
events.forEach(eventName => {
let nativeEventName = eventName.toLowerCase();
if (nativeEventName === 'doubleclick') {
nativeEventName = 'dblclick';
}
if (UNSUPPORTED_EVENTS.includes(eventName)) {
it.skip(`triggers native ${nativeEventName} (unsupported in JSDOM)`, () => {});
return;
}
it(`triggers native ${nativeEventName}`, async () => {
const ref = useRef();
const spy = jest.fn();
const Element = elementType;
//@reblendComponent
const NativeEventElement = class
/* @Reblend: Transformed from function to class */
extends Reblend {
static ELEMENT_NAME = "NativeEventElement";
constructor() {
super();
}
async initState() {
useEffectAfter.bind(this)(() => {
const element = ref.current;
element.addEventListener(nativeEventName, spy);
return () => {
element.removeEventListener(nativeEventName, spy);
};
});
}
async initProps() {
this.props = {};
}
async html() {
return Reblend.construct.bind(this)(Element, {
ref: ref
});
}
};
await render(Reblend.construct.bind(this)(NativeEventElement, null));
fireEvent[eventName](ref.current, init);
expect(spy).toHaveBeenCalledTimes(1);
});
});
});
});
test('onChange works', async () => {
const handleChange = jest.fn();
const {
container: {
firstChild: input
}
} = await render(Reblend.construct.bind(this)("input", {
onChange: handleChange
}));
fireEvent.change(input, {
target: {
value: 'a'
}
});
expect(handleChange).toHaveBeenCalledTimes(1);
});
test('calling `fireEvent` directly works too', async () => {
const handleEvent = jest.fn();
const {
container: {
firstChild: button
}
} = await render(Reblend.construct.bind(this)("button", {
onClick: handleEvent
}));
fireEvent(button, new Event('MouseEvent', {
bubbles: true,
cancelable: true,
button: 0
}));
});
test('blur/focus dose not bubbles in reblendjs', async () => {
const handleBlur = jest.fn();
const handleBubbledBlur = jest.fn();
const handleFocus = jest.fn();
const handleBubbledFocus = jest.fn();
const {
container
} = await render(Reblend.construct.bind(this)("div", {
onBlur: handleBubbledBlur,
onFocus: handleBubbledFocus
}, Reblend.construct.bind(this)("button", {
onBlur: handleBlur,
onFocus: handleFocus
})));
const button = container.firstChild.firstChild;
fireEvent.focus(button);
expect(handleBlur).toHaveBeenCalledTimes(0);
expect(handleBubbledBlur).toHaveBeenCalledTimes(0);
expect(handleFocus).toHaveBeenCalledTimes(1);
expect(handleBubbledFocus).toHaveBeenCalledTimes(0);
fireEvent.blur(button);
expect(handleBlur).toHaveBeenCalledTimes(1);
expect(handleBubbledBlur).toHaveBeenCalledTimes(0);
expect(handleFocus).toHaveBeenCalledTimes(1);
expect(handleBubbledFocus).toHaveBeenCalledTimes(0);
});