@egjs/axes
Version:
A module used to change the information of user action entered by various input devices such as touch screen or mouse into the logical virtual coordinates. You can easily create a UI that responds to user actions.
1,490 lines (1,368 loc) • 43.3 kB
JavaScript
import PanInputInjector from "inject-loader!../../src/inputType/PanInput.ts";
import Axes from "../../src/Axes.ts";
import { PanInput } from "../../src/inputType/PanInput.ts";
import { MoveKeyInput, KEY_RIGHT_ARROW } from "../../src/inputType/MoveKeyInput";
import { PinchInput } from "../../src/inputType/PinchInput.ts";
import { getDecimalPlace, roundNumber } from "../../src/utils";
import TestHelper from "./inputType/TestHelper";
describe("Axes", () => {
let el;
let input;
let inst;
let nestedInst;
let holdHandler;
let changeHandler;
let releaseHandler;
let finishHandler;
let animationStartHandler;
let animationEndHandler;
let preventedFn;
let notPreventedFn;
describe("Axes Test", () => {
beforeEach(() => {
inst = null;
});
afterEach(() => {
if (inst) {
inst.destroy();
inst = null;
}
});
it("should check a initialization empty value", () => {
// Given
// When
inst = new Axes();
// Then
const defaultOptions = {
easing: (x) => {
return 1 - Math.pow(1 - x, 3);
},
interruptable: true,
minimumDuration: 0,
maximumDuration: Infinity,
deceleration: 0.0006,
};
expect(inst).to.be.exist;
expect(defaultOptions.easing(0.5)).to.be.equal(inst.options.easing(0.5));
expect(defaultOptions.easing(0.3)).to.be.equal(inst.options.easing(0.3));
expect(defaultOptions.easing(0.1)).to.be.equal(inst.options.easing(0.1));
expect(defaultOptions.easing(0.7)).to.be.equal(inst.options.easing(0.7));
expect(defaultOptions.easing(0.9)).to.be.equal(inst.options.easing(0.9));
expect(defaultOptions.interruptable).to.be.equal(
inst.options.interruptable
);
expect(defaultOptions.minimumDuration).to.be.equal(
inst.options.minimumDuration
);
expect(defaultOptions.maximumDuration).to.be.equal(
inst.options.maximumDuration
);
expect(defaultOptions.deceleration).to.be.equal(
inst.options.deceleration
);
});
it("should check initialization status", () => {
// Given
// When
inst = new Axes(
{
x: {
range: [0, 100],
bounce: [30, 50],
circular: true,
},
otherX: {
range: [-100, 100],
bounce: 40,
circular: [false, true],
},
},
{
deceleration: 0.001,
},
{
x: 50,
otherX: 0,
}
);
// Then
expect(inst.axis.x.bounce).to.deep.equal([30, 50]);
expect(inst.axis.x.circular).to.deep.equal([true, true]);
expect(inst.axis.otherX.bounce).to.deep.equal([40, 40]);
expect(inst.axis.otherX.circular).to.deep.equal([false, true]);
expect(inst.get().x).to.equal(50);
expect(inst.get().otherX).to.equal(0);
});
it("should check startPos set correctly when initializing an instance", () => {
// Given
// When
changeHandler = sinon.spy();
inst = new Axes(
{
x: {
range: [0, 100],
bounce: [30, 50],
circular: true,
startPos: 100,
},
otherX: {
range: [-100, 100],
bounce: 40,
circular: [false, true],
},
},
{
deceleration: 0.001,
}
).on({
change: changeHandler,
});
// Then
expect(inst.get().x).to.equal(100);
expect(inst.get().otherX).to.equal(-100);
expect(changeHandler.called).to.be.false;
});
it("should check `holding` property changes whether the input is being held", (done) => {
// Given
el = sandbox();
input = new PanInput(el, {
inputType: ["touch"],
});
inst = new Axes(
{
x: {
range: [0, 100],
},
}
);
inst.connect("x", input);
// When
Simulator.gestures.pan(el, {
pos: [0, 0],
deltaX: 100,
duration: 1500,
easing: "linear",
});
// Then
setTimeout(() => {
expect(inst.holding).to.be.true;
}, 1000);
setTimeout(() => {
expect(inst.holding).to.be.false;
done();
}, 2000);
});
it("should check `holding` property changes whether the input is being held (with mixed input types) ", (done) => {
// Given
el = sandbox();
const moveKeyInput = new MoveKeyInput(el);
input = new PanInput(el, {
inputType: ["touch"],
});
inst = new Axes(
{
x: {
range: [0, 100],
},
}
);
inst.connect("x", input);
inst.connect("x", moveKeyInput);
// When
Simulator.gestures.pan(el, {
pos: [0, 0],
deltaX: 100,
duration: 1500,
easing: "linear",
});
TestHelper.key(el, "keydown", { keyCode: KEY_RIGHT_ARROW });
setTimeout(() => {
TestHelper.key(el, "keyup", { keyCode: KEY_RIGHT_ARROW });
}, 2500);
// Then
setTimeout(() => {
expect(inst.holding).to.be.true;
}, 1000);
setTimeout(() => {
expect(inst.holding).to.be.true;
}, 2000);
setTimeout(() => {
expect(inst.holding).to.be.false;
done();
}, 3000);
});
it("should check `setTo/setBy` method", () => {
// Given
inst = new Axes(
{
x: {
range: [0, 100],
bounce: [30, 50],
circular: true,
},
otherX: {
range: [-100, 100],
bounce: 40,
circular: [false, true],
},
},
{
deceleration: 0.001,
}
);
// When
const ret = inst.setTo({ x: 20 });
// Then
expect(inst.get()).to.be.eql({ x: 20, otherX: -100 });
expect(ret).to.be.equal(inst);
// When
inst.setBy({ x: 10 });
// Then
expect(inst.get()).to.be.eql({ x: 30, otherX: -100 });
});
it("should check `setTo` method (with duration)", () => {
// Given
const startHandler = sinon.spy();
changeHandler = sinon.spy();
const endHandler = sinon.spy(() => {
// Then
expect(startHandler.callCount).to.be.equal(1);
expect(changeHandler.called).to.be.true;
expect(inst.get()).to.be.eql({ x: 20, otherX: -100 });
done();
});
inst = new Axes(
{
x: {
range: [0, 100],
bounce: [30, 50],
circular: true,
},
otherX: {
range: [-100, 100],
bounce: 40,
circular: [false, true],
},
},
{
deceleration: 0.001,
}
).on({
animationStart: startHandler,
change: changeHandler,
animationEnd: endHandler,
});
// When
inst.setTo({ x: 20 }, 200);
});
it("should check `setBy` method (with duration)", () => {
// Given
inst = new Axes(
{
x: {
range: [0, 100],
bounce: [30, 50],
circular: true,
},
otherX: {
range: [-100, 100],
bounce: 40,
circular: [false, true],
},
},
{
deceleration: 0.001,
}
);
inst.setTo({ x: 50 });
expect(inst.get()).to.be.eql({ x: 50, otherX: -100 });
inst.on({
animationStart: startHandler,
change: changeHandler,
animationEnd: endHandler,
});
const startHandler = sinon.spy();
changeHandler = sinon.spy();
const endHandler = sinon.spy(() => {
// Then
expect(startHandler.callCount).to.be.equal(1);
expect(changeHandler.called).to.be.true;
expect(inst.get()).to.be.eql({ x: 40, otherX: -100 });
done();
});
// When
inst.setBy({ x: -10 }, 200);
});
it("should change Axes options via `setOptions` method", () => {
// Given
inst = new Axes(
{
x: {
range: [0, 100],
},
},
{
round: 10,
}
);
// When
inst.setTo({ x: 48 });
// Then
expect(inst.get()).to.be.eql({ x: 50 });
// When
inst.setOptions({
round: 1,
});
inst.setTo({ x: 48 });
// Then
expect(inst.get()).to.be.eql({ x: 48 });
});
it("should change options of each Axis via `setAxis` method", () => {
// Given
inst = new Axes(
{
x: {
range: [0, 100],
},
y: {
range: [0, 200],
},
},
);
// When
inst.setTo({ x: 150, y: 150 });
// Then
expect(inst.get()).to.be.eql({ x: 100, y: 150 });
// When
inst.setAxis({
x: {
range: [0, 200],
},
y: {
range: [0, 100],
},
});
inst.setTo({ x: 150, y: 200 });
// Then
expect(inst.get()).to.be.eql({ x: 150, y: 100 });
});
});
describe("Axes Test with InputType", () => {
beforeEach(() => {
inst = new Axes(
{
x: {
range: [0, 100],
bounce: [30, 50],
circular: true,
},
otherX: {
range: [-100, 100],
bounce: 40,
circular: [false, true],
},
},
{
deceleration: 0.001,
}
);
sandbox();
});
afterEach(() => {
if (inst) {
inst.destroy();
inst = null;
}
cleanup();
});
it("should check `connect` method", () => {
// Given
const input = new PanInput("#sandbox");
// When
let ret = inst.connect("x", input);
// Then
expect(input.axes).to.be.eql(["x"]);
expect(inst._inputs.length).to.be.equal(1);
expect(ret).to.be.equal(inst);
// When
ret = inst.connect(["x"], input);
// Then
expect(input.axes).to.be.eql(["x"]);
expect(inst._inputs.length).to.be.equal(1);
expect(ret).to.be.equal(inst);
// When
ret = inst.connect(["x", "y"], input);
// Then
expect(input.axes).to.be.eql(["x", "y"]);
expect(inst._inputs.length).to.be.equal(1);
expect(ret).to.be.equal(inst);
// When
ret = inst.connect("x y", input);
// Then
expect(input.axes).to.be.eql(["x", "y"]);
expect(inst._inputs.length).to.be.equal(1);
expect(ret).to.be.equal(inst);
input.destroy();
});
it("should check `disconnect` method", () => {
// Given
const input1 = new PanInput("#sandbox");
const input2 = new PanInput("#sandbox");
const input3 = new PanInput("#sandbox");
inst.connect("x", input1);
inst.connect("y", input2);
inst.connect("x y", input3);
// When
expect(inst._inputs.length).to.be.equal(3);
let ret = inst.disconnect(input1);
// Then
expect(inst._inputs.indexOf(input1)).to.be.equal(-1);
expect(inst._inputs.length).to.be.equal(2);
expect(ret).to.be.equal(inst);
// When
ret = inst.disconnect();
// Then
expect(inst._inputs).to.be.eql([]);
expect(ret).to.be.equal(inst);
expect(inst._inputs.length).to.be.equal(0);
// When no input, it should not make script error
ret = inst.disconnect(input1);
// Then
expect(ret).to.be.equal(inst);
input1.destroy();
input2.destroy();
input3.destroy();
});
it("should work with pan gesture when connecting PanInput after PinchInput disconnected.", (done) => {
// Given
const MOVE_HORIZONTALLY = {
pos: [0, 0],
deltaX: 100,
deltaY: 10,
duration: 200,
easing: "linear",
};
const MOVE_VERTICALLY = {
pos: [0, 0],
deltaX: 0,
deltaY: 100,
duration: 200,
easing: "linear",
};
const target = document.querySelector("#sandbox");
const pinchInput = new PinchInput(target, { inputType: ["touch"] });
const panInput = new PanInput(target, { inputType: ["touch"] });
inst.connect("x", pinchInput);
inst.disconnect(pinchInput);
inst.connect(["x", "otherX"], panInput);
const prevX = inst.get()["x"];
const prevY = inst.get()["otherX"];
// When
Simulator.gestures.pan(target, MOVE_HORIZONTALLY, () => {
Simulator.gestures.pan(target, MOVE_VERTICALLY, () => {
// Then
const currX = inst.get()["x"];
const currY = inst.get()["otherX"];
expect(currX).to.be.not.equal(prevX);
expect(currY).to.be.not.equal(prevY);
pinchInput.destroy();
panInput.destroy();
done();
});
});
});
});
describe("Nested Axes Test", () => {
beforeEach(() => {
inst = new Axes({
x: {
range: [0, 300],
bounce: [10, 10],
},
});
nestedInst = new Axes(
{
x: {
range: [0, 200],
bounce: [10, 10],
},
},
{
nested: true,
}
);
el = sandbox();
el.innerHTML = `<div id="parent"
style="width:300px; height:200px;"><div id="child" style="width:200px; height:200px;"></div></div>`;
});
afterEach(() => {
if (inst) {
inst.destroy();
inst = null;
}
if (nestedInst) {
nestedInst.destroy();
nestedInst = null;
}
cleanup();
});
it("should check only the state of the axes attached to the child element changes.", (done) => {
// Given
const parent = document.querySelector("#parent");
const child = document.querySelector("#child");
const parentInput = new PanInput(parent, { inputType: ["touch"] });
const childInput = new PanInput(child, { inputType: ["touch"] });
inst.connect(["x"], parentInput);
nestedInst.connect(["x"], childInput);
const parentPrevX = inst.get()["x"];
const childPrevX = nestedInst.get()["x"];
// When
Simulator.gestures.pan(
child,
{
pos: [0, 0],
deltaX: 100,
deltaY: 0,
duration: 200,
easing: "linear",
},
() => {
// Then
const parentCurrX = inst.get()["x"];
const childCurrX = nestedInst.get()["x"];
expect(parentPrevX).to.be.equal(parentCurrX);
expect(childPrevX).to.be.not.equal(childCurrX);
parentInput.destroy();
childInput.destroy();
done();
}
);
});
it("should check both state of the axes attached to the elements when child axes reaches the max range.", (done) => {
// Given
const parent = document.querySelector("#parent");
const child = document.querySelector("#child");
const parentInput = new PanInput(parent, { inputType: ["touch"] });
const childInput = new PanInput(child, { inputType: ["touch"] });
inst.connect(["x"], parentInput);
nestedInst.connect(["x"], childInput);
const parentPrevX = inst.get()["x"];
const childPrevX = nestedInst.get()["x"];
// When
Simulator.gestures.pan(
child,
{
pos: [0, 0],
deltaX: 300,
deltaY: 0,
duration: 200,
easing: "linear",
},
() => {
// Then
const parentCurrX = inst.get()["x"];
const childCurrX = nestedInst.get()["x"];
expect(parentPrevX).to.be.not.equal(parentCurrX);
expect(childPrevX).to.be.not.equal(childCurrX);
parentInput.destroy();
childInput.destroy();
done();
}
);
});
});
[20, 30, 40, 50].forEach((iOSEdgeSwipeThreshold) => {
describe(`Axes iOS Edge Test (iOSEdgeSwipeThreshold: ${iOSEdgeSwipeThreshold})`, () => {
beforeEach(() => {
inst = new Axes(
{
x: {
range: [0, 300],
bounce: 100,
},
y: {
range: [0, 400],
bounce: 100,
},
},
{
deceleration: 0.001,
}
);
el = sandbox();
el.innerHTML = `<div id="area"
style="position:relative; border:5px solid #444; width:300px; height:400px; color:#aaa; margin:0;box-sizing:content-box; z-index:9;"></div>`;
releaseHandler = sinon.spy();
finishHandler = sinon.spy();
const MockPanInputInjector = PanInputInjector({
"../const": {
IOS_EDGE_THRESHOLD: 30,
IS_IOS_SAFARI: true,
},
});
input = new MockPanInputInjector.PanInput(el, {
iOSEdgeSwipeThreshold,
inputKey: ["any"],
inputType: ["touch"],
});
inst
.on({
release: releaseHandler,
finish: finishHandler,
})
.connect(["x", "y"], input);
const touchTrigger = Simulator.events.touch.trigger;
Simulator.events.touch.originalTrigger = touchTrigger;
Simulator.events.touch.trigger = (touches, element, type) => {
if (type === "end") {
return;
}
touchTrigger.call(Simulator.events.touch, touches, element, type);
};
});
afterEach(() => {
Simulator.events.touch.trigger = Simulator.events.touch.originalTrigger;
if (inst) {
inst.destroy();
inst = null;
}
if (input) {
input.destroy();
input = null;
}
releaseHandler.resetHistory();
finishHandler.resetHistory();
cleanup();
});
it("should check release event occurs when swiping on ios safari edge. (left to right)", (done) => {
// Given, When
// clientX + => -
Simulator.gestures.pan(
el,
{
pos: [30, 30],
deltaX: -50,
deltaY: 10,
duration: 200,
easing: "linear",
},
() => {
// Then
// for test animation event
setTimeout(() => {
const releaseEvent = releaseHandler.getCall(0).args[0];
expect(releaseHandler.calledOnce).to.be.true;
// expect(releaseEvent.inputEvent.isFinal).to.be.false;
expect(releaseEvent.isTrusted).to.be.true;
const finishEvent = finishHandler.getCall(0).args[0];
expect(finishHandler.called).to.be.true;
expect(finishEvent.isTrusted).to.be.true;
done();
}, 500);
}
);
});
it("should check release event occurs when swiping a little on ios safari edge. (right to left)", (done) => {
// Given, When
// window.innerWidth => window.innerWidth - 15
Simulator.gestures.pan(
el,
{
pos: [window.innerWidth - iOSEdgeSwipeThreshold + 1, 30],
deltaX: -15,
deltaY: 0,
duration: 200,
easing: "linear",
},
() => {
// Then
// for test animation event
setTimeout(() => {
const releaseEvent = releaseHandler.getCall(0).args[0];
expect(releaseHandler.calledOnce).to.be.true;
// expect(releaseEvent.inputEvent.isFinal).to.be.false;
expect(releaseEvent.isTrusted).to.be.true;
const finishEvent = finishHandler.getCall(0).args[0];
expect(finishHandler.called).to.be.true;
expect(finishEvent.isTrusted).to.be.true;
done();
}, 500);
}
);
});
it("should check release event not occurs when swiping a lot on ios safari edge. (right to left)", (done) => {
// Given, When
// window.innerWidth => window.innerWidth - 30
Simulator.gestures.pan(
el,
{
pos: [window.innerWidth, 30],
deltaX: -60,
deltaY: 0,
duration: 200,
easing: "linear",
},
() => {
// Then
// for test animation event
setTimeout(() => {
expect(releaseHandler.callCount).to.be.equals(0);
done();
}, 500);
}
);
});
});
});
[["pointer"], ["touch", "mouse"], ["touch"]].forEach((type) => {
describe(`Axes Custom Event Test with interruptable(${type})`, () => {
beforeEach(() => {
if (type.indexOf("pointer") > -1) {
Simulator.setType("pointer");
} else {
Simulator.setType("touch");
}
inst = new Axes(
{
x: {
range: [0, 300],
bounce: 100,
},
y: {
range: [0, 400],
bounce: 100,
},
},
{
deceleration: 0.001,
interruptable: false,
}
);
el = sandbox();
el.innerHTML = `<div id="area"
style="position:relative; border:5px solid #444; width:300px; height:400px; color:#aaa; margin:0;box-sizing:content-box; z-index:9;"></div>`;
const self = inst;
preventedFn = () => {
expect(self.interruptManager._prevented).to.be.true;
};
notPreventedFn = () => {
expect(self.interruptManager._prevented).to.be.false;
};
input = new PanInput(el, {
inputType: type,
});
inst.connect(["x", "y"], input);
});
afterEach(() => {
if (inst) {
inst.destroy();
inst = null;
}
if (input) {
input.destroy();
input = null;
}
cleanup();
});
after(() => {
// Simulator type should be "touch" type after test complete.
// Otherwise this test will affect other simulation test.
Simulator.setType("touch");
});
const testNormalBehavior = (done) => {
// Given
holdHandler = sinon.spy();
changeHandler = sinon.spy();
releaseHandler = sinon.spy();
finishHandler = sinon.spy();
animationStartHandler = sinon.spy();
animationEndHandler = sinon.spy();
inst.off().on({
hold: holdHandler,
change: changeHandler,
release: releaseHandler,
finish: finishHandler,
animationStart: animationStartHandler,
animationEnd: animationEndHandler,
});
// When
Simulator.gestures.pan(
el,
{
pos: [0, 0],
deltaX: 100,
deltaY: 100,
duration: 1000,
easing: "linear",
},
() => {
// Then
// for test custom event
setTimeout(() => {
expect(holdHandler.calledOnce).to.be.true;
expect(changeHandler.called).to.be.true;
expect(releaseHandler.calledOnce).to.be.true;
expect(finishHandler.calledOnce).to.be.true;
expect(animationStartHandler.calledOnce).to.be.true;
expect(animationEndHandler.calledOnce).to.be.true;
done();
}, 1000);
}
);
};
it("should check normal behavior test after user invokes 'stop' from change event", (done) => {
// Given
let changeCount = 0;
let pos = 0;
changeHandler = sinon.spy((e) => {
++changeCount;
if (changeCount === 5) {
// current position
pos = e.pos;
// hold => change * 5 (stop) => finish
e.stop();
}
});
holdHandler = sinon.spy();
releaseHandler = sinon.spy();
finishHandler = sinon.spy();
animationStartHandler = sinon.spy();
animationEndHandler = sinon.spy();
inst.on({
hold: holdHandler,
change: changeHandler,
release: releaseHandler,
finish: finishHandler,
animationStart: animationStartHandler,
animationEnd: animationEndHandler,
});
// When
Simulator.gestures.pan(
el,
{
pos: [0, 0],
deltaX: 100,
deltaY: 100,
duration: 1000,
easing: "linear",
},
() => {
// Then
setTimeout(() => {
// The stopped position is the current position.
expect(pos).to.be.deep.equals(inst.get());
expect(holdHandler.calledOnce).to.be.true;
expect(changeCount).to.be.equals(5);
expect(changeHandler.callCount).to.be.equals(changeCount);
expect(finishHandler.calledOnce).to.be.true;
// not called events
expect(releaseHandler.callCount).to.be.equals(0);
expect(animationStartHandler.callCount).to.be.equals(0);
expect(animationEndHandler.callCount).to.be.equals(0);
testNormalBehavior(done);
}, 1000);
}
);
});
it("should check normal behavior test after user invokes 'stop' from change event(after animationStart)", (done) => {
// Given
holdHandler = sinon.spy();
let changeCount = 0;
let pos = 0;
releaseHandler = sinon.spy();
finishHandler = sinon.spy();
animationStartHandler = sinon.spy();
animationEndHandler = sinon.spy();
changeHandler = sinon.spy((e) => {
if (animationStartHandler.calledOnce) {
++changeCount;
if (changeCount === 5) {
// current position
pos = e.pos;
// hold => change * 5 (stop) => finish
e.stop();
}
}
});
inst.on({
hold: holdHandler,
change: changeHandler,
release: releaseHandler,
finish: finishHandler,
animationStart: animationStartHandler,
animationEnd: animationEndHandler,
});
// When
Simulator.gestures.pan(
el,
{
pos: [0, 0],
deltaX: 100,
deltaY: 100,
duration: 1000,
easing: "linear",
},
() => {
// Then
setTimeout(() => {
// The stopped position is the current position.
expect(pos).to.be.deep.equals(inst.get());
expect(holdHandler.called).to.be.true;
expect(changeCount).to.be.equals(5);
expect(releaseHandler.calledOnce).to.be.true;
expect(animationStartHandler.calledOnce).to.be.true;
expect(finishHandler.calledOnce).to.be.true;
// not called events
expect(animationEndHandler.callCount).to.be.equals(0);
testNormalBehavior(done);
}, 1000);
}
);
});
it("should check interrupt test when user's action is fast", (done) => {
holdHandler = sinon.spy(preventedFn);
changeHandler = sinon.spy(preventedFn);
releaseHandler = sinon.spy(preventedFn);
finishHandler = sinon.spy(notPreventedFn);
animationStartHandler = sinon.spy(preventedFn);
animationEndHandler = sinon.spy(notPreventedFn);
inst.on({
hold: holdHandler,
change: changeHandler,
release: releaseHandler,
finish: finishHandler,
animationStart: animationStartHandler,
animationEnd: animationEndHandler,
});
// when
Simulator.gestures.pan(
el,
{
pos: [0, 0],
deltaX: 100,
deltaY: 100,
duration: 1000,
easing: "linear",
},
() => {
// Then
// for test custom event
setTimeout(() => {
expect(holdHandler.calledOnce).to.be.true;
expect(changeHandler.called).to.be.true;
expect(releaseHandler.calledOnce).to.be.true;
expect(finishHandler.calledOnce).to.be.true;
expect(animationStartHandler.calledOnce).to.be.true;
expect(animationEndHandler.calledOnce).to.be.true;
done();
}, 1000);
}
);
});
});
describe(`Axes Custom Event Test(${type})`, () => {
beforeEach(() => {
if (type.indexOf("pointer") > -1) {
Simulator.setType("pointer");
} else {
Simulator.setType("touch");
}
inst = new Axes(
{
x: {
range: [0, 300],
bounce: 100,
},
y: {
range: [0, 400],
bounce: 100,
},
},
{
deceleration: 0.001,
}
);
el = sandbox();
el.innerHTML = `<div id="area"
style="position:relative; border:5px solid #444; width:300px; height:400px; color:#aaa; margin:0;box-sizing:content-box; z-index:9;"></div>`;
holdHandler = sinon.spy();
releaseHandler = sinon.spy();
finishHandler = sinon.spy();
animationStartHandler = sinon.spy();
animationEndHandler = sinon.spy();
input = new PanInput(el, {
inputType: type,
});
inst
.on({
hold: holdHandler,
release: releaseHandler,
finish: finishHandler,
animationStart: animationStartHandler,
animationEnd: animationEndHandler,
})
.connect(["x", "y"], input);
});
afterEach(() => {
if (inst) {
inst.destroy();
inst = null;
}
if (input) {
input.destroy();
input = null;
}
holdHandler.resetHistory();
releaseHandler.resetHistory();
finishHandler.resetHistory();
animationStartHandler.resetHistory();
animationEndHandler.resetHistory();
cleanup();
});
after(() => {
// Simulator type should be "touch" type after test complete.
// Otherwise this test will affect other simulation test.
Simulator.setType("touch");
});
it("should check slow movement test (no-velocity)", (done) => {
// Given
changeHandler = sinon.spy();
inst.on("change", changeHandler);
// When
Simulator.gestures.pan(
el,
{
pos: [30, 30],
deltaX: 10,
deltaY: 10,
duration: 3000,
easing: "linear",
},
() => {
// Then
const holdEvent = holdHandler.getCall(0).args[0];
expect(holdHandler.calledOnce).to.be.true;
expect(holdEvent.pos.x).to.be.equal(0);
expect(holdEvent.pos.y).to.be.equal(0);
// expect(holdEvent.inputEvent.isFirst).to.be.true;
expect(holdEvent.isTrusted).to.be.true;
expect(changeHandler.called).to.be.true;
for (let i = 0, len = changeHandler.callCount; i < len; i++) {
const changeEvent = changeHandler.getCall(i).args[0];
expect(changeEvent.holding).to.be.true;
expect(changeEvent.input).to.be.equal(input);
expect(changeEvent.isTrusted).to.be.true;
expect(changeEvent.inputEvent).to.be.not.equal(null);
}
const releaseEvent = releaseHandler.getCall(0).args[0];
expect(releaseHandler.calledOnce).to.be.true;
// expect(releaseEvent.inputEvent.isFinal).to.be.true;
expect(releaseEvent.input).to.be.equal(input);
expect(releaseEvent.isTrusted).to.be.true;
expect(inst.get()).to.be.eql({ x: 10, y: 10 });
const finishEvent = finishHandler.getCall(0).args[0];
expect(finishHandler.calledOnce).to.be.true;
expect(finishEvent.isTrusted).to.be.true;
expect(animationStartHandler.called).to.be.false;
expect(animationEndHandler.called).to.be.false;
done();
}
);
});
it("should check slow movement test (no-velocity), release outside", (done) => {
// Given
inst.on("change", (e) => {
if (animationStartHandler.called) {
expect(e.holding).to.be.false;
expect(inst.animationManager.getEventInfo().input).to.be.equal(
e.input
);
} else {
expect(e.holding).to.be.true;
}
expect(e.input).to.be.equal(input);
expect(e.inputEvent).to.be.not.equal(null);
expect(e.isTrusted).to.be.true;
});
inst.options.maximumDuration = 200;
// When
Simulator.gestures.pan(
el,
{
pos: [30, 30],
deltaX: -50,
deltaY: 10,
duration: 3000,
easing: "linear",
},
() => {
// Then
// for test animation event
setTimeout(() => {
const holdEvent = holdHandler.getCall(0).args[0];
expect(holdHandler.calledOnce).to.be.true;
expect(holdEvent.pos.x).to.be.equal(0);
expect(holdEvent.pos.y).to.be.equal(0);
// expect(holdEvent.inputEvent.isFirst).to.be.true;
expect(holdEvent.input).to.be.equal(input);
expect(holdEvent.isTrusted).to.be.true;
const releaseEvent = releaseHandler.getCall(0).args[0];
expect(releaseHandler.calledOnce).to.be.true;
// expect(releaseEvent.inputEvent.isFinal).to.be.true;
expect(releaseEvent.input).to.be.equal(input);
expect(releaseEvent.isTrusted).to.be.true;
const result = inst.get();
expect(result.x).to.be.equal(0);
expect(result.y).to.be.equal(10);
expect(releaseEvent.duration).to.be.equal(0);
expect(releaseEvent.depaPos).to.deep.equal(releaseEvent.destPos);
const animationStartEvent =
animationStartHandler.getCall(0).args[0];
expect(animationStartHandler.called).to.be.true;
expect(animationStartEvent.isTrusted).to.be.true;
const animationEndEvent = animationEndHandler.getCall(0).args[0];
expect(animationEndEvent.isTrusted).to.be.true;
const finishEvent = finishHandler.getCall(0).args[0];
expect(finishHandler.called).to.be.true;
expect(finishEvent.isTrusted).to.be.true;
done();
}, 500);
}
);
});
it("should check fast movement test (velocity)", (done) => {
// Given
inst.on("change", (e) => {
if (animationStartHandler.called) {
expect(e.holding).to.be.false;
expect(inst.animationManager.getEventInfo().input).to.be.equal(
e.input
);
} else {
expect(e.holding).to.be.true;
}
expect(e.input).to.be.equal(input);
expect(e.inputEvent).to.be.not.equal(null);
expect(e.isTrusted).to.be.true;
});
// When
Simulator.gestures.pan(
el,
{
pos: [0, 0],
deltaX: 100,
deltaY: 100,
duration: 500,
easing: "linear",
},
() => {
// Then
// for test animation event
setTimeout(() => {
expect(holdHandler.calledOnce).to.be.true;
const releaseEvent = releaseHandler.getCall(0).args[0];
expect(releaseHandler.calledOnce).to.be.true;
expect(animationStartHandler.calledOnce).to.be.true;
expect(animationEndHandler.calledOnce).to.be.true;
expect(finishHandler.calledOnce).to.be.true;
expect(releaseEvent.duration).to.not.equal(0);
expect(releaseEvent.depaPos).to.not.equal(releaseEvent.destPos);
done();
}, 2000);
}
);
});
it("should check movement test when stop method was called in 'animationStart' event", (done) => {
// Given
holdHandler = sinon.spy();
changeHandler = sinon.spy((e) => {
if (animationStartHandler.called) {
expect(e.holding).to.be.false;
} else {
expect(e.holding).to.be.true;
}
});
releaseHandler = sinon.spy();
finishHandler = sinon.spy();
animationStartHandler = sinon.spy((e) => {
e.stop();
setTimeout(() => {
e.done();
}, e.duration);
});
animationEndHandler = sinon.spy(notPreventedFn);
inst.on({
hold: holdHandler,
change: changeHandler,
release: releaseHandler,
finish: finishHandler,
animationStart: animationStartHandler,
animationEnd: animationEndHandler,
});
// When
Simulator.gestures.pan(
el,
{
pos: [30, 30],
deltaX: 100,
deltaY: 100,
duration: 500,
easing: "linear",
},
() => {
// Then
// for test custom event
setTimeout(() => {
expect(holdHandler.calledOnce).to.be.true;
expect(releaseHandler.calledOnce).to.be.true;
expect(animationStartHandler.calledOnce).to.be.true;
expect(animationEndHandler.calledOnce).to.be.true;
expect(finishHandler.calledOnce).to.be.true;
done();
}, 1000);
}
);
});
it("should fire animationEnd event with isTrusted-true flag if grab while animating.", (done) => {
// Given
// When
let destPos;
Simulator.gestures.pan(el, {
pos: [0, 0],
deltaX: 300,
deltaY: 50,
duration: 100,
easing: "linear",
});
// grab while animating
inst.on("animationStart", (e) => {
destPos = e.destPos;
Simulator.gestures.tap(el);
});
// Then
inst.on("animationEnd", (e) => {
setTimeout(() => {
const endPos = inst.get();
expect(e.isTrusted).to.be.true;
expect(endPos.x !== destPos.x || endPos.y !== destPos.y).to.be.true;
done();
});
});
});
});
});
describe("round final value of animation automatically by value range and dest pos", () => {
[
{ range: [0.12345, 100], destPos: 50.1234 }, // max decimal place at range[0]
{ range: [0, 100.123456789], destPos: 50.1234 }, // max decimal place at range[1]
{ range: [0, 100], destPos: 50.1234 }, // max decimal place at dest pos,
{ range: [0, 100], destPos: 50 }, // no decimal
{ range: [0, 1000], destPos: 445.17079889807155 }, // number to resolve flicking test failure.
{ range: [0, 1000], destPos: 240.79930795847713 }, // comparison value that works correctly.
].forEach((val) => {
it(`should round final value of animation by max decimal place(${val.range[0]}, ${val.range[1]}, ${val.destPos})`, async () => {
inst = new Axes({
x: {
range: val.range,
},
});
inst.setTo({ x: val.destPos }, 100);
await new Promise((res) => inst.on("finish", res));
expect(inst.get().x).to.be.equal(val.destPos);
});
});
});
describe("round option", () => {
[100, 10, 1, 0.1, 0.01, 0.001, 0.0001, 0.00001].forEach((round) => {
it(`should round value by option value of 1 unit (${round})`, async () => {
// Given, When
const dividables = await checkAnimatedValueIsRound(round);
// Then
const allDividable = dividables.every((val) => val);
expect(allDividable).to.be.equal(true);
});
});
[10, 5, 0.05, 0.002, 0.0005].forEach((round) => {
it(`should round value by option value of dividable unit (${round})`, async () => {
// Given, When
const dividables = await checkAnimatedValueIsRound(round);
// Then
const allDividable = dividables.every((val) => val);
expect(allDividable).to.be.equal(true);
});
});
[3, 7, 11, 0.03, 0.007].forEach((round) => {
it(`should round value by option value of undividable unit (${round})`, async () => {
// Given, When
const dividables = await checkAnimatedValueIsRound(round);
// Then
const allDividable = dividables.every((val) => val);
expect(allDividable).to.be.equal(true);
});
});
/**
* I found delta is zero although delta is not zero
* when 'round' is specified
*/
it("should fire change event with changed value / delta although round is specified", (done) => {
// Given
const result = [];
const delta = [];
inst = new Axes(
{
x: {
range: [0, 200],
},
},
{ round: 1 }
);
inst.on("change", (e) => {
result.push(e.pos.x);
delta.push(e.delta.x);
});
// When
const expectPos = [30, 60, 90];
expectPos.forEach((v) => inst.setBy({ x: 30 }));
// Then
setTimeout(() => {
expect(result.length).to.be.equal(expectPos.length);
delta.forEach((v) => expect(v).to.be.equal(30));
result.forEach((v, i) => expect(v).to.be.equal(expectPos[i]));
done();
}, 100);
});
const isDividable = (dividend, divisor) => {
// Round 'dividend / divsior' because it may has decimal error.
const result = roundNumber(dividend / divisor, 0.000001);
// Decimal place should be 0
return getDecimalPlace(result) === 0;
};
const checkAnimatedValueIsRound = async (round) => {
// Given
const dividables = [];
inst = new Axes(
{
x: {
range: [0, 200],
},
},
{ round }
).on({
animationStart: (e) => {
dividables.push(isDividable(e.destPos.x, round));
},
change: (e) => {
dividables.push(isDividable(e.pos.x, round));
},
finish: () => {
dividables.push(isDividable(inst.get().x, round));
},
});
// When
inst.setTo({ x: 100 }, 100);
await new Promise((res) => setTimeout(res, 150));
inst.destroy();
return dividables;
};
});
});