From 1af1cf546c0e5d9cc9954bfcc43fa76a02ba5a57 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Jun 2026 13:42:01 +0000 Subject: [PATCH] Add unit tests for useInterval composable Test the setInterval lifecycle composable that registers/clears an interval on component mount/unmount. - fn is not invoked before mount - fn fires after the specified delay once mounted - fn fires repeatedly on each tick - fn is not called before the delay elapses - interval is cleared on unmount (no further calls) - unmounting mid-interval prevents any call - table-driven cases validate count for short/long delays Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shell/composables/useInterval.test.ts | 106 ++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 shell/composables/useInterval.test.ts diff --git a/shell/composables/useInterval.test.ts b/shell/composables/useInterval.test.ts new file mode 100644 index 00000000000..b2ecdabd414 --- /dev/null +++ b/shell/composables/useInterval.test.ts @@ -0,0 +1,106 @@ +import { defineComponent } from 'vue'; +import { mount } from '@vue/test-utils'; +import { useInterval } from './useInterval'; + +function createHarness(fn: () => void, delay: number) { + return defineComponent({ + setup() { + useInterval(fn, delay); + }, + template: '
', + }); +} + +describe('useInterval', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('does not call fn before the component is mounted', () => { + const fn = jest.fn(); + + createHarness(fn, 1000); + + expect(fn).not.toHaveBeenCalled(); + }); + + it('calls fn after the specified delay once mounted', () => { + const fn = jest.fn(); + + mount(createHarness(fn, 1000)); + + expect(fn).not.toHaveBeenCalled(); + jest.advanceTimersByTime(1000); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it('calls fn repeatedly on each interval tick', () => { + const fn = jest.fn(); + + mount(createHarness(fn, 500)); + + jest.advanceTimersByTime(1500); + expect(fn).toHaveBeenCalledTimes(3); + }); + + it('does not call fn before the delay elapses', () => { + const fn = jest.fn(); + + mount(createHarness(fn, 200)); + + jest.advanceTimersByTime(199); + expect(fn).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(1); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it('clears the interval on unmount so fn is not called afterwards', () => { + const fn = jest.fn(); + const wrapper = mount(createHarness(fn, 1000)); + + jest.advanceTimersByTime(1000); + expect(fn).toHaveBeenCalledTimes(1); + + wrapper.unmount(); + jest.advanceTimersByTime(2000); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it('stops calling fn immediately when unmounted mid-interval', () => { + const fn = jest.fn(); + const wrapper = mount(createHarness(fn, 1000)); + + jest.advanceTimersByTime(500); + wrapper.unmount(); + + jest.advanceTimersByTime(1000); + expect(fn).not.toHaveBeenCalled(); + }); + + it.each([ + { + desc: 'short delay (100 ms) fires correct count', + delay: 100, + time: 350, + calls: 3, + }, + { + desc: 'long delay (2000 ms) fires correct count', + delay: 2000, + time: 5000, + calls: 2, + }, + ])('$desc', ({ delay, time, calls }) => { + const fn = jest.fn(); + + mount(createHarness(fn, delay)); + jest.advanceTimersByTime(time); + + expect(fn).toHaveBeenCalledTimes(calls); + }); +});