UNPKG

@rosskevin/ifvisible

Version:

Cross-browser, lightweight way to check if user is looking at the page or interacting with it. (wrapper around HTML5 visibility api)

341 lines (288 loc) 9.03 kB
import { vi } from 'vitest' import { FireableEvent, Status } from '../EventBus.js' import { IfVisible } from '../IfVisible.js' function expectIt(ifv: IfVisible, status: Status) { expect(ifv.getStatus()).toEqual(status) } function expectActive(ifv: IfVisible) { expectIt(ifv, 'active') } function expectIdle(ifv: IfVisible) { expectIt(ifv, 'idle') } function expectHidden(ifv: IfVisible) { expectIt(ifv, 'hidden') } describe('IfVisible', () => { let ifv: IfVisible beforeEach(() => { vi.useFakeTimers() vi.setSystemTime(new Date('2022-06-16')) ifv = new IfVisible(window, document) }) afterEach(() => { vi.useRealTimers() }) describe('when instantiating', () => { it('is `active`', () => { expectActive(ifv) }) it('is `idle` after initial idleTime (30)', () => { expectActive(ifv) // 10 total (of 30) vi.advanceTimersByTime(10000) expectActive(ifv) // 30 total (of 30) vi.advanceTimersByTime(20000) expectIdle(ifv) }) }) describe('methods', () => { describe('idle', () => { it('setup is initally `active`', () => { expectActive(ifv) }) it('is `idle`', () => { ifv.idle() expectIdle(ifv) }) it(`fires status change 'idle'`, () => { let newStatus: Status | undefined ifv.on('statusChanged', (data) => (newStatus = data?.status)) ifv.idle() expect(newStatus).toEqual('idle') }) it(`fires 'idle'`, () => { const spy = vi.fn() ifv.on('idle', spy) ifv.idle() expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalledTimes(1) }) }) describe('blur', () => { it('setup is initally `active`', () => { expectActive(ifv) }) it('is `hidden`', () => { ifv.blur() expectHidden(ifv) }) it(`fires status change 'hidden'`, () => { let newStatus: Status | undefined ifv.on('statusChanged', (data) => (newStatus = data?.status)) ifv.blur() expect(newStatus).toEqual('hidden') }) it(`fires 'blur'`, () => { const spy = vi.fn() ifv.on('blur', spy) ifv.blur() expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalledTimes(1) }) }) describe('focus', () => { beforeEach(() => { expectActive(ifv) vi.advanceTimersByTime(30000) expectIdle(ifv) }) it('setup is initally `idle`', () => { expectIdle(ifv) }) it('is `active`', () => { ifv.focus() expectActive(ifv) }) it(`fires status change 'active'`, () => { let newStatus: Status | undefined ifv.on('statusChanged', (data) => (newStatus = data?.status)) ifv.focus() expect(newStatus).toEqual('active') }) ;['focus', 'wakeup'].forEach((name) => { it(`fires '${name}'`, () => { const spy = vi.fn() ifv.on(name as FireableEvent, spy) ifv.focus() expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalledTimes(1) }) }) }) describe('wakeup', () => { beforeEach(() => { expectActive(ifv) vi.advanceTimersByTime(30000) expectIdle(ifv) }) it('setup is initally `idle`', () => { expectIdle(ifv) }) it('is `active`', () => { ifv.wakeup() expectActive(ifv) }) it(`fires status change 'active'`, () => { let newStatus: Status | undefined ifv.on('statusChanged', (data) => (newStatus = data?.status)) ifv.wakeup() expect(newStatus).toEqual('active') }) it(`fires 'wakeup'`, () => { const spy = vi.fn() ifv.on('wakeup', spy) ifv.wakeup() expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalledTimes(1) }) }) describe('now', () => { it('when active is true', () => { expectActive(ifv) expect(ifv.now()).toEqual(true) expect(ifv.now('active')).toEqual(true) expect(ifv.now('hidden')).toEqual(false) expect(ifv.now('idle')).toEqual(false) }) it('when idle is false', () => { ifv.idle() expect(ifv.now()).toEqual(false) expect(ifv.now('active')).toEqual(false) expect(ifv.now('hidden')).toEqual(false) expect(ifv.now('idle')).toEqual(true) }) it('when hidden is false', () => { ifv.blur() expect(ifv.now()).toEqual(false) expect(ifv.now('active')).toEqual(false) expect(ifv.now('hidden')).toEqual(true) expect(ifv.now('idle')).toEqual(false) }) }) describe('onEvery', () => { it(`fires callback repeatedly when 'active'`, () => { const spy = vi.fn() ifv.onEvery(0.5, spy) expectActive(ifv) expect(spy).not.toHaveBeenCalled() // 1 total (of 3) vi.advanceTimersByTime(1000) expectActive(ifv) expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalledTimes(2) // 2 total (of 3) vi.advanceTimersByTime(1000) expectActive(ifv) expect(spy).toHaveBeenCalledTimes(4) // 3 total (of 3) vi.advanceTimersByTime(1000) expectActive(ifv) expect(spy).toHaveBeenCalledTimes(6) }) it(`does not continue to fire callback when 'hidden' after blur()`, () => { const spy = vi.fn() ifv.onEvery(0.5, spy) expectActive(ifv) expect(spy).not.toHaveBeenCalled() // 1 total (of 3) vi.advanceTimersByTime(1000) expectActive(ifv) expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalledTimes(2) // blur it and check ifv.blur() expectHidden(ifv) expect(spy).toHaveBeenCalledTimes(2) // advance time and check again to be sure expectHidden(ifv) expect(spy).toHaveBeenCalledTimes(2) }) it(`does not continue to fire callback when 'idle' after idle()`, () => { const spy = vi.fn() ifv.onEvery(0.5, spy) expectActive(ifv) expect(spy).not.toHaveBeenCalled() // 1 total (of 3) vi.advanceTimersByTime(1000) expectActive(ifv) expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalledTimes(2) // idle it and check ifv.idle() expectIdle(ifv) expect(spy).toHaveBeenCalledTimes(2) // advance time and check again to be sure expectIdle(ifv) expect(spy).toHaveBeenCalledTimes(2) }) it(`stops firing when 'idle' after timeout`, () => { const spy = vi.fn() ifv.onEvery(1, spy) expectActive(ifv) expect(spy).not.toHaveBeenCalled() // 1 total (of 60) vi.advanceTimersByTime(1000) expectActive(ifv) expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalledTimes(1) // 60 total (of 60) and 30s default timeout vi.advanceTimersByTime(59000) expectIdle(ifv) expect(spy).toHaveBeenCalledTimes(30 - 1) // it's always -1, not sure why based on original code, but not a big deal to me at least }) it(`restarts firing when waking after event`, () => { const spy = vi.fn() ifv.onEvery(1, spy) expectActive(ifv) expect(spy).not.toHaveBeenCalled() // 1 total (of 60) vi.advanceTimersByTime(30000) expectIdle(ifv) expect(spy).toHaveBeenCalledTimes(30 - 1) // it's always -1, not sure why based on original code, but not a big deal to me at least // // now, let's wake this up and check to see it resumes // expectIdle(ifv) document.dispatchEvent(new window.Event('mousemove')) expectActive(ifv) expect(spy).toHaveBeenCalledTimes(30 - 1) // same as above, we haven't moved time. // see if it reinitiates vi.advanceTimersByTime(1000) expectActive(ifv) expect(spy).toHaveBeenCalledTimes(30) }) }) }) describe('DOM events', () => { beforeEach(() => { expectActive(ifv) vi.advanceTimersByTime(30000) expectIdle(ifv) }) it('setup is initally `idle`', () => { expectIdle(ifv) }) describe('document', () => { // all doc events ;['mousemove', 'mousedown', 'keyup', 'touchstart'].forEach((name) => { it(`is active after ${name} event`, () => { expectIdle(ifv) document.dispatchEvent(new window.Event(name)) expectActive(ifv) }) }) }) describe('window', () => { // all doc events ;['scroll'].forEach((name) => { it(`is active after ${name} event`, () => { expectIdle(ifv) window.dispatchEvent(new window.Event(name)) expectActive(ifv) }) }) }) }) })