/** * @jest-environment jsdom */ import { FocusTimer } from '../focus-timer' jest.useFakeTimers() describe(FocusTimer, () => { const onFocusTimeEndCallback = jest.fn() const onFocusTimeStartCallback = jest.fn() beforeEach(() => { jest.clearAllMocks() jest.clearAllTimers() jest.restoreAllMocks() }) describe('with no initial interaction', () => { test('Report no time if has no interaction', (done) => { const timer = new FocusTimer() timer.on('focus_time.end', (time: number) => { expect(time).toEqual(0) done() }) jest.advanceTimersByTime(40000) }) }) test('works with default params', (done) => { const timer = new FocusTimer() timer.pulse() timer.on('focus_time.end', (time: number) => { expect(time).toBeGreaterThan(0) done() }) jest.advanceTimersByTime(40000) }) test('starts focus time when loaded', (done) => { const timer = new FocusTimer({ idleInterval: 3000 }) timer.pulse() timer.on('focus_time.end', (time: number) => { expect(time).toEqual(3000) done() }) jest.advanceTimersByTime(3000) }) test('computes focus time', (done) => { const dateStart = performance.now() const timer = new FocusTimer({ idleInterval: 3000 }) timer.pulse() timer.on('focus_time.end', (time: number) => { expect(time).toEqual(1000) done() }) jest.spyOn(performance, 'now').mockImplementationOnce(() => dateStart + 3100) jest.advanceTimersByTime(3100) }) test('tracks current page time', () => { const dateStart = performance.now() const timer = new FocusTimer() timer.pulse() jest.spyOn(performance, 'now').mockImplementationOnce(() => dateStart + 5_000) expect(timer.currentFocusTime).toBe(5_000) }) test('do not add current page time when unfocused', () => { const dateStart = performance.now() const timer = new FocusTimer({ idleInterval: 10_000 }) timer.onBlur() jest.spyOn(performance, 'now').mockImplementationOnce(() => dateStart + 5_000) // doesn't matter that time has elapsed when not focused expect(timer.currentFocusTime).toBe(0) }) describe('restart', () => { test('ends focus', (done) => { const dateStart = performance.now() const timer = new FocusTimer({ idleInterval: 3000 }) timer.pulse() timer.on('focus_time.end', (time: number) => { expect(time).toEqual(3100) done() }) jest.spyOn(performance, 'now').mockImplementationOnce(() => dateStart + 3100) timer.restart() }) }) describe('onVisibilityChange', () => { describe('when idling', () => { test('does not record a focus time on hidden', () => { const timer = new FocusTimer({ idleInterval: 3000 }) // Have to advance the timer to simulate going idle (focus timer starts as focused by default) jest.advanceTimersByTime(4000) timer.on('focus_time.end', onFocusTimeEndCallback) timer.onVisibilityChange('hidden') expect(onFocusTimeEndCallback).not.toBeCalled() }) test('starts a new focus time session on visible', () => { const timer = new FocusTimer({ idleInterval: 3000 }) // Have to advance the timer to simulate going idle (focus timer starts as focused by default) jest.advanceTimersByTime(4000) timer.on('focus_time.start', onFocusTimeStartCallback) timer.onVisibilityChange('visible') expect(onFocusTimeStartCallback).toBeCalledTimes(1) }) }) describe('when focused', () => { test('records the focus time on hidden', (done) => { const timer = new FocusTimer({ idleInterval: 3000 }) timer.pulse() timer.on('focus_time.end', (time: number) => { expect(time).toEqual(2000) done() }) jest.advanceTimersByTime(2000) timer.onVisibilityChange('hidden') }) test('does nothing on visible', () => { const timer = new FocusTimer({ idleInterval: 3000 }) timer.on('focus_time.end', onFocusTimeEndCallback) timer.on('focus_time.start', onFocusTimeStartCallback) timer.onVisibilityChange('visible') expect(onFocusTimeEndCallback).not.toBeCalled() expect(onFocusTimeStartCallback).not.toBeCalled() }) }) }) describe('pulse', () => { test('start focus timer when idling', () => { const timer = new FocusTimer({ idleInterval: 3000 }) // Have to advance the timer to simulate going idle (focus timer starts as focused by default) jest.advanceTimersByTime(4000) timer.on('focus_time.start', onFocusTimeStartCallback) timer.pulse() expect(onFocusTimeStartCallback).toBeCalledTimes(1) }) test('does nothing when focused', () => { const timer = new FocusTimer({ idleInterval: 3000 }) timer.on('focus_time.end', onFocusTimeEndCallback) timer.on('focus_time.start', onFocusTimeStartCallback) timer.pulse() expect(onFocusTimeEndCallback).not.toBeCalled() expect(onFocusTimeStartCallback).not.toBeCalled() }) }) describe('onBlur', () => { test('does nothing when idling', () => { const timer = new FocusTimer({ idleInterval: 3000 }) // Have to advance the timer to simulate going idle (focus timer starts as focused by default) jest.advanceTimersByTime(4000) timer.on('focus_time.end', onFocusTimeEndCallback) timer.on('focus_time.start', onFocusTimeStartCallback) timer.onBlur() expect(onFocusTimeEndCallback).not.toBeCalled() expect(onFocusTimeStartCallback).not.toBeCalled() }) test('records focus time when focused', (done) => { const timer = new FocusTimer({ idleInterval: 3000 }) timer.pulse() timer.on('focus_time.end', (time: number) => { expect(time).toEqual(2000) done() }) jest.advanceTimersByTime(2000) timer.onBlur() }) }) })