Skip to content

Commit 3292dfa

Browse files
NAME-ASHWANIYADAVrakshityadav1868
authored andcommitted
Add unit tests for inactivity tracking and auto-saving (sugarlabs#7645)
* test: add unit tests for idle watcher * chore: retrigger CI * test: add double-init regression test and jest.restoreAllMocks cleanup
1 parent 756ae13 commit 3292dfa

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

js/activity/__tests__/idle-watcher.test.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ describe("setupActivityIdleWatcher", () => {
5656

5757
afterEach(() => {
5858
jest.clearAllMocks();
59+
jest.restoreAllMocks();
5960
jest.useRealTimers();
6061
delete global.createjs;
6162
delete global.debugLog;
@@ -162,6 +163,58 @@ describe("setupActivityIdleWatcher", () => {
162163

163164
expect(mockActivity.isAppIdle).toBe(false);
164165
expect(global.createjs.Ticker.framerate).toBe(60);
166+
expect(mockActivity.stageDirty).toBe(true);
167+
});
168+
169+
it("does not accumulate listeners or intervals when called twice", () => {
170+
setupActivityIdleWatcher(mockActivity);
171+
mockActivity._initIdleWatcher();
172+
173+
const firstListenerCount = mockActivity.addEventListener.mock.calls.length;
174+
const firstInterval = mockActivity._idleWatcherInterval;
175+
176+
const clearIntervalSpy = jest.spyOn(global, "clearInterval");
177+
178+
// Call _initIdleWatcher again (double init)
179+
mockActivity._initIdleWatcher();
180+
181+
// The old interval should have been cleared by _stopIdleWatcher
182+
expect(clearIntervalSpy).toHaveBeenCalledWith(firstInterval);
183+
184+
// Listeners should be re-added, not accumulated
185+
// The second init should have removed old listeners then added new ones
186+
expect(mockActivity.removeEventListener).toHaveBeenCalledWith(
187+
window,
188+
"mousemove",
189+
expect.any(Function)
190+
);
191+
expect(mockActivity.removeEventListener).toHaveBeenCalledWith(
192+
window,
193+
"mousedown",
194+
expect.any(Function)
195+
);
196+
expect(mockActivity.removeEventListener).toHaveBeenCalledWith(
197+
window,
198+
"keydown",
199+
expect.any(Function)
200+
);
201+
expect(mockActivity.removeEventListener).toHaveBeenCalledWith(
202+
window,
203+
"touchstart",
204+
expect.any(Function)
205+
);
206+
expect(mockActivity.removeEventListener).toHaveBeenCalledWith(
207+
window,
208+
"wheel",
209+
expect.any(Function)
210+
);
211+
212+
// New interval should be a different handle
213+
expect(mockActivity._idleWatcherInterval).not.toBe(firstInterval);
214+
215+
// addEventListener should have been called same number of times each init
216+
// (5 listeners per init, so 10 total)
217+
expect(mockActivity.addEventListener).toHaveBeenCalledTimes(firstListenerCount * 2);
165218
});
166219
});
167220

@@ -240,6 +293,17 @@ describe("setupActivityIdleWatcher", () => {
240293
operation: "autoSave"
241294
});
242295
});
296+
297+
it("does not throw when saveLocally is null", () => {
298+
setupActivityIdleWatcher(mockActivity);
299+
mockActivity.saveLocally = null;
300+
mockActivity._initAutoSave();
301+
302+
jest.advanceTimersByTime(5 * 60 * 1000);
303+
304+
// Should not crash and ErrorHandler should not be called
305+
expect(global.ErrorHandler.recoverable).not.toHaveBeenCalled();
306+
});
243307
});
244308

245309
describe("_stopAutoSave", () => {

0 commit comments

Comments
 (0)