UNPKG

@networkpro/web

Version:

This package powers the official web presence of Network Pro Strategies — a research- and infrastructure-focused technology initiative working across cybersecurity, digital systems, and privacy.

170 lines (149 loc) 4.8 kB
/* ========================================================================== tests/unit/client/lib/registerServiceWorker.test.js Copyright © 2025-2026 Network Pro Strategies (Network Pro™) SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later This file is part of Network Pro. ========================================================================== */ import { goto } from '$app/navigation'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; vi.mock('$app/navigation', () => ({ goto: vi.fn().mockResolvedValue(undefined), })); /** * Build a minimal SvelteKit navigation object for eligibility tests. * * @param {Record<string, unknown>} [overrides] * @returns {Record<string, unknown>} */ function createNavigation(overrides = {}) { return { cancel: vi.fn(), from: { params: {}, route: { id: '/' }, url: new URL('https://netwk.pro/'), }, to: { params: {}, route: { id: '/about' }, url: new URL('https://netwk.pro/about'), }, type: 'link', willUnload: false, ...overrides, }; } describe('service worker navigation lifecycle', () => { beforeEach(() => { vi.resetModules(); vi.restoreAllMocks(); window.__DISABLE_SW__ = false; vi.mocked(goto).mockResolvedValue(undefined); }); afterEach(() => { vi.clearAllTimers(); vi.useRealTimers(); }); it('intercepts only owned link and goto route navigations', async () => { const { shouldInterceptServiceWorkerNavigation } = await import('$lib/registerServiceWorker.js'); expect(shouldInterceptServiceWorkerNavigation(createNavigation())).toBe( true, ); expect( shouldInterceptServiceWorkerNavigation( createNavigation({ type: 'goto' }), ), ).toBe(true); const excludedNavigations = [ createNavigation({ type: 'form' }), createNavigation({ type: 'popstate' }), createNavigation({ type: 'leave', willUnload: true }), createNavigation({ to: { params: {}, route: { id: null }, url: new URL('https://example.com/'), }, willUnload: true, }), createNavigation({ from: { params: {}, route: { id: '/' }, url: new URL('https://netwk.pro/#first'), }, to: { params: {}, route: { id: '/' }, url: new URL('https://netwk.pro/#second'), }, }), ]; excludedNavigations.forEach((navigation) => { expect(shouldInterceptServiceWorkerNavigation(navigation)).toBe(false); }); }); it('requests activation only when an update is waiting', async () => { const waitingWorker = { postMessage: vi.fn((_message, transferredPorts) => { transferredPorts[0].postMessage({ type: 'SOLE_CLIENT_ACTIVATION_RESULT', willActivate: false, }); }), }; const registration = new EventTarget(); Object.assign(registration, { installing: null, scope: 'https://netwk.pro/', waiting: waitingWorker, }); const serviceWorkerContainer = new EventTarget(); Object.assign(serviceWorkerContainer, { controller: {}, register: vi.fn().mockResolvedValue(registration), }); Object.defineProperty(navigator, 'serviceWorker', { configurable: true, value: serviceWorkerContainer, }); vi.spyOn(document, 'readyState', 'get').mockReturnValue('complete'); const { activateWaitingServiceWorkerForNavigation, registerServiceWorker, shouldInterceptServiceWorkerNavigation, } = await import('$lib/registerServiceWorker.js'); registerServiceWorker(); await vi.waitFor(() => { expect(serviceWorkerContainer.register).toHaveBeenCalled(); }); const destination = new URL('/about', window.location.origin); registration.waiting = null; expect(activateWaitingServiceWorkerForNavigation(destination)).toBe(false); registration.waiting = waitingWorker; expect(activateWaitingServiceWorkerForNavigation(destination)).toBe(true); const [message, transferredPorts] = waitingWorker.postMessage.mock.calls[0]; expect(message).toEqual({ type: 'ACTIVATE_IF_SOLE_CLIENT' }); expect(transferredPorts).toHaveLength(1); await vi.waitFor(() => { expect(goto).toHaveBeenCalledWith(destination.href); }); expect( shouldInterceptServiceWorkerNavigation( createNavigation({ from: { params: {}, route: { id: '/' }, url: new URL('/', window.location.origin), }, to: { params: {}, route: { id: '/about' }, url: destination, }, }), ), ).toBe(false); }); });