mirror of
https://github.com/directus/directus.git
synced 2026-01-26 14:58:30 -05:00
Throttle idle event listeners (#16555)
* throttle idle event listeners * export timeout duration to use it in test directly
This commit is contained in:
95
app/src/idle.test.ts
Normal file
95
app/src/idle.test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { afterEach, beforeEach, describe, expect, SpyInstance, test, vi } from 'vitest';
|
||||
import { DefineComponent, defineComponent, h, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
import { time as timeoutDuration } from './idle';
|
||||
|
||||
vi.mock('lodash', () => ({
|
||||
throttle: vi.fn((fn, _wait) => fn),
|
||||
}));
|
||||
|
||||
describe('idle', () => {
|
||||
let testComponent: DefineComponent<any>;
|
||||
let idleTrackerEmitSpy: SpyInstance;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const { idleTracker, startIdleTracking, stopIdleTracking } = await import('./idle');
|
||||
|
||||
testComponent = defineComponent({
|
||||
setup() {
|
||||
onMounted(() => startIdleTracking());
|
||||
onUnmounted(() => stopIdleTracking());
|
||||
},
|
||||
render: () => h('div'),
|
||||
});
|
||||
|
||||
idleTrackerEmitSpy = vi.spyOn(idleTracker, 'emit');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
|
||||
// Ensure the internal visible & idle variables in the imported idle
|
||||
// are reset before every test
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
test('should emit "hide"/"show" when document visibility changes', () => {
|
||||
mount(testComponent);
|
||||
|
||||
// mock document visibility state
|
||||
Object.defineProperty(document, 'visibilityState', { value: 'hidden', configurable: true });
|
||||
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
|
||||
expect(idleTrackerEmitSpy).toHaveBeenCalledWith('hide');
|
||||
|
||||
// mock document visibility state
|
||||
Object.defineProperty(document, 'visibilityState', { value: 'visible', configurable: true });
|
||||
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
|
||||
expect(idleTrackerEmitSpy).toHaveBeenCalledWith('show');
|
||||
});
|
||||
|
||||
test('should not emit "idle" before the timeout has passed', () => {
|
||||
mount(testComponent);
|
||||
|
||||
document.dispatchEvent(new PointerEvent('pointerdown'));
|
||||
|
||||
// advance less than the idle timeout duration
|
||||
vi.advanceTimersByTime(1000);
|
||||
|
||||
expect(idleTrackerEmitSpy).not.toHaveBeenCalledWith('idle');
|
||||
});
|
||||
|
||||
test('should emit "idle" after the timeout has passed', () => {
|
||||
mount(testComponent);
|
||||
|
||||
document.dispatchEvent(new PointerEvent('pointerdown'));
|
||||
|
||||
// advance past the idle timeout duration (added 1000 just in case there's timing issues)
|
||||
vi.advanceTimersByTime(timeoutDuration + 1000);
|
||||
|
||||
expect(idleTrackerEmitSpy).toHaveBeenCalledWith('idle');
|
||||
});
|
||||
|
||||
test('should emit "active" after being idle', () => {
|
||||
mount(testComponent);
|
||||
|
||||
document.dispatchEvent(new PointerEvent('pointerdown'));
|
||||
|
||||
// advance past the idle timeout duration (added 1000 just in case there's timing issues)
|
||||
vi.advanceTimersByTime(timeoutDuration + 1000);
|
||||
|
||||
// stop the current idle state
|
||||
document.dispatchEvent(new PointerEvent('pointerdown'));
|
||||
|
||||
// advance past the throttle duration (500)
|
||||
vi.advanceTimersByTime(1000);
|
||||
|
||||
expect(idleTrackerEmitSpy).toHaveBeenCalledWith('active');
|
||||
});
|
||||
});
|
||||
@@ -1,20 +1,23 @@
|
||||
import { throttle } from 'lodash';
|
||||
import mitt from 'mitt';
|
||||
|
||||
const events = ['pointermove', 'pointerdown', 'keydown'];
|
||||
const time = 5 * 60 * 1000; // 5 min in ms
|
||||
export const time = 5 * 60 * 1000; // 5 min in ms
|
||||
|
||||
let timeout: NodeJS.Timeout;
|
||||
let timeout: number | null;
|
||||
|
||||
let visible = true;
|
||||
let idle = false;
|
||||
|
||||
export const idleTracker = mitt();
|
||||
|
||||
const throttledOnIdleEvents = throttle(onIdleEvents, 500);
|
||||
|
||||
export function startIdleTracking(): void {
|
||||
document.addEventListener('visibilitychange', onVisibilityChange);
|
||||
|
||||
for (const event of events) {
|
||||
document.addEventListener(event, onIdleEvents);
|
||||
document.addEventListener(event, throttledOnIdleEvents);
|
||||
}
|
||||
|
||||
resetTimeout();
|
||||
@@ -24,7 +27,7 @@ export function stopIdleTracking(): void {
|
||||
document.removeEventListener('visibilitychange', onVisibilityChange);
|
||||
|
||||
for (const event of events) {
|
||||
document.removeEventListener(event, onIdleEvents);
|
||||
document.removeEventListener(event, throttledOnIdleEvents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +54,11 @@ function onVisibilityChange() {
|
||||
|
||||
function resetTimeout() {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
window.clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
timeout = window.setTimeout(() => {
|
||||
idle = true;
|
||||
idleTracker.emit('idle');
|
||||
}, time);
|
||||
|
||||
Reference in New Issue
Block a user