如何在 Jest 中使用 requestAnimationFrame 测试延迟事件处理程序?
How to test a delayed event handler with requestAnimationFrame in Jest?
我有一个函数应该在不久的将来执行,我想开玩笑地测试它,但不知道如何正确地执行此操作。实际上,我正在使用 React + testing react library 但这并没有改变我想的任何东西。
这是函数本身:
let start;
const holdClick = (el, delayedClickHandler, time = 0) => {
if (time === 0) return delayedClickHandler();
let counter = 0;
function countToExecute() {
if (counter === time) delayedClickHandler();
else {
start = requestAnimationFrame(countToExecute);
counter += 1;
}
}
start = window.requestAnimationFrame(countToExecute);
el.ontouchend = () => {
window.cancelAnimationFrame(start);
};
};
我的组件
function Component() {
const [clicked, setClicked] = useState(false);
return (
<span
// data attribute must be changed to "active" after 30 ticks
data-testid={clicked ? 'active' : 'static'}
onTouchStart={e => holdClick(e.target, () => setClicked(true), 30)}
>
click me
</span>
);
}
还有我的测试
test('execute delayed click', () => {
beforeEach(() => {
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => cb());
});
const { getByTestId } = render(<App />);
const el = getByTestId('static');
fireEvent.click(el);
jest.useFakeTimers();
setTimeout(() => {
expect(el).toHaveAttribute('data-testid', 'active');
}, 10000);
jest.runAllTimers();
});
我的猜测是问题出在 requestAnimationFrame
但正如我之前提到的,我几乎不知道有什么方法可以解决这个问题。我试图添加 jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => cb());
但它没有改变任何东西。 所以我想知道是否有不需要麻烦的 window
对象模拟的解决方案。
首先,您的回调是在 onTouchStart
道具上设置的,这意味着您需要触发 touchStart
事件才能触发它。
fireEvent.touchStart(el);
其次,在您的 countToExecute
函数中,counter
变量应在 调用 requestAnimationFrame
之前增加 ,否则 counter
永远不会增加。这是因为实际 requestAnimationFrame
会在每次重绘(每个“tick”)时调用其回调,但由于它在测试中被模拟,因此回调会立即被调用。
function countToExecute() {
if (counter === time) {
delayedClickHandler();
} else {
counter += 1;
start = requestAnimationFrame(countToExecute);
}
}
您的测试应该如下所示:
test('execute delayed click', () => {
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => cb());
const { getByTestId } = render(<MyComponent />);
const el = getByTestId('static');
fireEvent.touchStart(el);
expect(el).toHaveAttribute('data-testid', 'active');
});
我有一个函数应该在不久的将来执行,我想开玩笑地测试它,但不知道如何正确地执行此操作。实际上,我正在使用 React + testing react library 但这并没有改变我想的任何东西。
这是函数本身:
let start;
const holdClick = (el, delayedClickHandler, time = 0) => {
if (time === 0) return delayedClickHandler();
let counter = 0;
function countToExecute() {
if (counter === time) delayedClickHandler();
else {
start = requestAnimationFrame(countToExecute);
counter += 1;
}
}
start = window.requestAnimationFrame(countToExecute);
el.ontouchend = () => {
window.cancelAnimationFrame(start);
};
};
我的组件
function Component() {
const [clicked, setClicked] = useState(false);
return (
<span
// data attribute must be changed to "active" after 30 ticks
data-testid={clicked ? 'active' : 'static'}
onTouchStart={e => holdClick(e.target, () => setClicked(true), 30)}
>
click me
</span>
);
}
还有我的测试
test('execute delayed click', () => {
beforeEach(() => {
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => cb());
});
const { getByTestId } = render(<App />);
const el = getByTestId('static');
fireEvent.click(el);
jest.useFakeTimers();
setTimeout(() => {
expect(el).toHaveAttribute('data-testid', 'active');
}, 10000);
jest.runAllTimers();
});
我的猜测是问题出在 requestAnimationFrame
但正如我之前提到的,我几乎不知道有什么方法可以解决这个问题。我试图添加 jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => cb());
但它没有改变任何东西。 所以我想知道是否有不需要麻烦的 window
对象模拟的解决方案。
首先,您的回调是在 onTouchStart
道具上设置的,这意味着您需要触发 touchStart
事件才能触发它。
fireEvent.touchStart(el);
其次,在您的 countToExecute
函数中,counter
变量应在 调用 requestAnimationFrame
之前增加 ,否则 counter
永远不会增加。这是因为实际 requestAnimationFrame
会在每次重绘(每个“tick”)时调用其回调,但由于它在测试中被模拟,因此回调会立即被调用。
function countToExecute() {
if (counter === time) {
delayedClickHandler();
} else {
counter += 1;
start = requestAnimationFrame(countToExecute);
}
}
您的测试应该如下所示:
test('execute delayed click', () => {
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => cb());
const { getByTestId } = render(<MyComponent />);
const el = getByTestId('static');
fireEvent.touchStart(el);
expect(el).toHaveAttribute('data-testid', 'active');
});