test: add unit tests for usePwaInstall hook
This commit is contained in:
138
src/hooks/__tests__/usePwaInstall.test.ts
Normal file
138
src/hooks/__tests__/usePwaInstall.test.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* @vitest-environment jsdom
|
||||||
|
*/
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { renderHook, act } from '@testing-library/react';
|
||||||
|
|
||||||
|
// We need to control matchMedia before the module loads
|
||||||
|
let standaloneMatch = false;
|
||||||
|
const matchMediaMock = vi.fn().mockImplementation((query: string) => ({
|
||||||
|
matches: query === '(display-mode: standalone)' ? standaloneMatch : false,
|
||||||
|
media: query,
|
||||||
|
addEventListener: vi.fn(),
|
||||||
|
removeEventListener: vi.fn(),
|
||||||
|
addListener: vi.fn(),
|
||||||
|
removeListener: vi.fn(),
|
||||||
|
onchange: null,
|
||||||
|
dispatchEvent: vi.fn(),
|
||||||
|
}));
|
||||||
|
Object.defineProperty(window, 'matchMedia', { value: matchMediaMock, writable: true });
|
||||||
|
|
||||||
|
describe('usePwaInstall', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
standaloneMatch = false;
|
||||||
|
vi.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('initially canInstall is false and isInstalled is false', async () => {
|
||||||
|
const { usePwaInstall } = await import('../usePwaInstall');
|
||||||
|
const { result } = renderHook(() => usePwaInstall());
|
||||||
|
expect(result.current.canInstall).toBe(false);
|
||||||
|
expect(result.current.isInstalled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets canInstall to true when beforeinstallprompt fires', async () => {
|
||||||
|
const { usePwaInstall } = await import('../usePwaInstall');
|
||||||
|
const { result } = renderHook(() => usePwaInstall());
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
const event = new Event('beforeinstallprompt', { cancelable: true });
|
||||||
|
Object.assign(event, {
|
||||||
|
prompt: vi.fn().mockResolvedValue(undefined),
|
||||||
|
userChoice: Promise.resolve({ outcome: 'accepted' }),
|
||||||
|
});
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.canInstall).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('install() calls prompt and returns true on accepted', async () => {
|
||||||
|
const { usePwaInstall } = await import('../usePwaInstall');
|
||||||
|
const { result } = renderHook(() => usePwaInstall());
|
||||||
|
|
||||||
|
const promptMock = vi.fn().mockResolvedValue(undefined);
|
||||||
|
await act(async () => {
|
||||||
|
const event = new Event('beforeinstallprompt', { cancelable: true });
|
||||||
|
Object.assign(event, {
|
||||||
|
prompt: promptMock,
|
||||||
|
userChoice: Promise.resolve({ outcome: 'accepted' as const }),
|
||||||
|
});
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
let accepted: boolean | undefined;
|
||||||
|
await act(async () => {
|
||||||
|
accepted = await result.current.install();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(promptMock).toHaveBeenCalled();
|
||||||
|
expect(accepted).toBe(true);
|
||||||
|
expect(result.current.canInstall).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('install() returns false on dismissed', async () => {
|
||||||
|
const { usePwaInstall } = await import('../usePwaInstall');
|
||||||
|
const { result } = renderHook(() => usePwaInstall());
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
const event = new Event('beforeinstallprompt', { cancelable: true });
|
||||||
|
Object.assign(event, {
|
||||||
|
prompt: vi.fn().mockResolvedValue(undefined),
|
||||||
|
userChoice: Promise.resolve({ outcome: 'dismissed' as const }),
|
||||||
|
});
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
let accepted: boolean | undefined;
|
||||||
|
await act(async () => {
|
||||||
|
accepted = await result.current.install();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(accepted).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('install() returns false when no deferred prompt', async () => {
|
||||||
|
const { usePwaInstall } = await import('../usePwaInstall');
|
||||||
|
const { result } = renderHook(() => usePwaInstall());
|
||||||
|
|
||||||
|
let accepted: boolean | undefined;
|
||||||
|
await act(async () => {
|
||||||
|
accepted = await result.current.install();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(accepted).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets isInstalled on appinstalled event', async () => {
|
||||||
|
const { usePwaInstall } = await import('../usePwaInstall');
|
||||||
|
const { result } = renderHook(() => usePwaInstall());
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
window.dispatchEvent(new Event('appinstalled'));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isInstalled).toBe(true);
|
||||||
|
expect(result.current.canInstall).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cleans up event listeners on unmount', async () => {
|
||||||
|
const addSpy = vi.spyOn(window, 'addEventListener');
|
||||||
|
const removeSpy = vi.spyOn(window, 'removeEventListener');
|
||||||
|
|
||||||
|
const { usePwaInstall } = await import('../usePwaInstall');
|
||||||
|
const { unmount } = renderHook(() => usePwaInstall());
|
||||||
|
|
||||||
|
expect(addSpy).toHaveBeenCalledWith('beforeinstallprompt', expect.any(Function));
|
||||||
|
expect(addSpy).toHaveBeenCalledWith('appinstalled', expect.any(Function));
|
||||||
|
|
||||||
|
unmount();
|
||||||
|
|
||||||
|
expect(removeSpy).toHaveBeenCalledWith('beforeinstallprompt', expect.any(Function));
|
||||||
|
expect(removeSpy).toHaveBeenCalledWith('appinstalled', expect.any(Function));
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user