import "fake-indexeddb/auto" import "core-js/stable/structured-clone" import initWorker from "./worker" import Dexie, { EntityTable } from "dexie" const createSelf = () => ({ addEventListener: jest.fn(), skipWaiting: jest.fn(() => Promise.resolve()), registration: { pushManager: { subscribe: jest.fn(() => Promise.resolve({ subscription: "HI" })), getSubscription: jest.fn(() => Promise.resolve({ subscription: "HI" })), }, }, clients: { claim: jest.fn(() => Promise.resolve()), }, }) as unknown as ServiceWorkerGlobalScope describe("service worker", () => { let self: ServiceWorkerGlobalScope beforeEach(() => { indexedDB = new IDBFactory() self = createSelf() // fetchMock.mockIf(/http:\/\/localhost\/api\/subscribe\/?/, (req) => { // return Promise.resolve( // req.method === "POST" // ? { // body: JSON.stringify({ subscriptionId: 123 }), // } // : req.method === "PUT" // ? { init: { status: 200 } } // : { init: { status: 405 } } // ) // }) fetchMock.mockResponse((req) => { return Promise.resolve( req.method === "POST" ? { body: JSON.stringify({ subscriptionId: 123 }), } : req.method === "PUT" ? { init: { status: 200 } } : { init: { status: 405 } } ) }) }) afterEach(() => { jest.clearAllMocks() }) xtest("displays push notifications", () => { initWorker(self) const pushHandler = getHandler("push") const pushEvent = createEvent({ data: "HI" }) pushHandler(pushEvent) }) test("claims on activate", async () => { initWorker(self) const activateHandler = getHandler("activate") const event = createEvent() activateHandler(event) await waitUntilCalls(event) expect(self.clients.claim).toHaveBeenCalled() }) test("skips waiting on install", async () => { initWorker(self) const installHandler = getHandler("install") const event = createEvent() installHandler(event) await waitUntilCalls(event) expect(self.skipWaiting).toHaveBeenCalled() }) test("puts push subscription if the subscription id is set", async () => { initWorker(self) await setSavedSubscription(5000) const changeHandler = getHandler("pushsubscriptionchange") const changeEvent = createEvent() changeHandler(changeEvent) await waitUntilCalls(changeEvent) expect(self.registration.pushManager.subscribe).toHaveBeenCalledWith({ userVisibleOnly: true, applicationServerKey: expect.any(Uint8Array), }) expect(fetch).toHaveBeenCalledWith( `${process.env.BASE_URL}/api/subscription/5000`, { method: "PUT", body: JSON.stringify({ subscription: "HI" }), } ) }) test("posts to subscriptions when there is no user set", async () => { initWorker(self) const changeHandler = getHandler("pushsubscriptionchange") const changeEvent = createEvent() changeHandler(changeEvent) await waitUntilCalls(changeEvent) expect(self.registration.pushManager.subscribe).toHaveBeenCalledWith({ userVisibleOnly: true, applicationServerKey: expect.any(Uint8Array), }) expect(fetch).toHaveBeenCalledWith( `${process.env.BASE_URL}/api/subscription/`, { method: "POST", body: JSON.stringify({ subscription: "HI" }), } ) }) test("subscription id is saved on first subscription change", async () => { initWorker(self) const changeHandler = getHandler("pushsubscriptionchange") const changeEvent = createEvent() changeHandler(changeEvent) await waitUntilCalls(changeEvent) expect(await getSavedSubscription()).toEqual({ id: 1, subscriptionId: 123 }) }) function getHandler(eventName: string) { expect(self.addEventListener).toHaveBeenCalledWith( eventName, expect.anything() ) const [, handler] = (self.addEventListener as jest.Mock).mock.calls.find( ([event]: [string]) => event === eventName ) return handler } }) async function getSavedSubscription() { const database = new Dexie("tack-up-now", { indexedDB }) as Dexie & { subscriptions: EntityTable<{ id: number; subscriptionId: number }, "id"> } database.version(1).stores({ subscriptions: "id++, subscriptionId&" }) return (await database.subscriptions.toArray())[0] } async function setSavedSubscription(subscriptionId: number) { const database = new Dexie("tack-up-now", { indexedDB }) as Dexie & { subscriptions: EntityTable<{ id: number; subscriptionId: number }, "id"> } database.version(1).stores({ subscriptions: "id++, subscriptionId&" }) await database.subscriptions.put({ id: 1, subscriptionId }) } function createPushEvent(data: string) { const pushFields = { data: { blob: () => new Blob([data], { type: "text/plain" }), json: () => JSON.parse(data), text: () => data, arrayBuffer: () => new TextEncoder().encode(data), }, } return createEvent(pushFields) } function createMessageEvent(message: any) { return createEvent({ data: message }) } function createEvent(properties?: object) { return { waitUntil: jest.fn(), ...properties, } } function waitUntilCalls(event: { waitUntil: jest.Mock> }) { return Promise.all(event.waitUntil.mock.calls.map(([promise]) => promise)) }