Open notification to existing tab/window
This commit is contained in:
parent
37f32cce3a
commit
3a56b9f853
|
|
@ -25,6 +25,7 @@ const createSelf = () =>
|
||||||
clients: {
|
clients: {
|
||||||
claim: jest.fn(() => Promise.resolve()),
|
claim: jest.fn(() => Promise.resolve()),
|
||||||
openWindow: jest.fn(() => Promise.resolve()),
|
openWindow: jest.fn(() => Promise.resolve()),
|
||||||
|
matchAll: jest.fn(() => Promise.resolve([])),
|
||||||
},
|
},
|
||||||
}) as unknown as ServiceWorkerGlobalScope
|
}) as unknown as ServiceWorkerGlobalScope
|
||||||
|
|
||||||
|
|
@ -32,12 +33,16 @@ let originalWindow: Window & typeof globalThis
|
||||||
|
|
||||||
describe("service worker", () => {
|
describe("service worker", () => {
|
||||||
let self: ServiceWorkerGlobalScope
|
let self: ServiceWorkerGlobalScope
|
||||||
|
let controlledClients: Client[]
|
||||||
|
let uncontrolledClients: Client[]
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
originalWindow = global.window
|
originalWindow = global.window
|
||||||
global.window = undefined as any
|
global.window = undefined as any
|
||||||
indexedDB = new IDBFactory()
|
indexedDB = new IDBFactory()
|
||||||
self = createSelf()
|
self = createSelf()
|
||||||
|
controlledClients = []
|
||||||
|
uncontrolledClients = []
|
||||||
|
|
||||||
fetchMock.mockResponse((req) => {
|
fetchMock.mockResponse((req) => {
|
||||||
if (
|
if (
|
||||||
|
|
@ -94,28 +99,71 @@ describe("service worker", () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("closes notification on click", async () => {
|
describe("on notification click", () => {
|
||||||
initWorker(self)
|
test("the notification is closed", async () => {
|
||||||
|
initWorker(self)
|
||||||
|
|
||||||
const notificationHandler = getHandler("notificationclick")
|
const notificationHandler = getHandler("notificationclick")
|
||||||
|
|
||||||
const event = createNotificationEvent("tackupnow.com")
|
const event = createNotificationEvent("tackupnow.com")
|
||||||
notificationHandler(event)
|
notificationHandler(event)
|
||||||
await waitUntilCalls(event)
|
await waitUntilCalls(event)
|
||||||
|
|
||||||
expect(event.notification.close).toHaveBeenCalled()
|
expect(event.notification.close).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("opens tack up now on click", async () => {
|
test("opens tack up now if no clients match the destination", async () => {
|
||||||
initWorker(self)
|
initWorker(self)
|
||||||
|
|
||||||
const notificationHandler = getHandler("notificationclick")
|
const notificationHandler = getHandler("notificationclick")
|
||||||
|
|
||||||
const event = createNotificationEvent("tackupnow.com")
|
const event = createNotificationEvent("tackupnow.com")
|
||||||
notificationHandler(event)
|
notificationHandler(event)
|
||||||
await waitUntilCalls(event)
|
await waitUntilCalls(event)
|
||||||
|
|
||||||
expect(self.clients.openWindow).toHaveBeenCalledWith("tackupnow.com")
|
expect(self.clients.openWindow).toHaveBeenCalledWith("tackupnow.com")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("focuses first matching client", async () => {
|
||||||
|
addClient("https://tackupnow.com/place", "window", true)
|
||||||
|
const matchingClient = addClient(
|
||||||
|
"https://tackupnow.com/otherplace",
|
||||||
|
"window",
|
||||||
|
true
|
||||||
|
)
|
||||||
|
addClient("https://tackupnow.com/yetanotherotherplace", "window", true)
|
||||||
|
|
||||||
|
initWorker(self)
|
||||||
|
|
||||||
|
const notificationHandler = getHandler("notificationclick")
|
||||||
|
|
||||||
|
const event = createNotificationEvent("https://tackupnow.com/otherplace")
|
||||||
|
notificationHandler(event)
|
||||||
|
await waitUntilCalls(event)
|
||||||
|
|
||||||
|
expect(matchingClient.focus).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("focuses uncontrolled matching clients", async () => {
|
||||||
|
addClient("https://derpatious.world", "window", false)
|
||||||
|
addClient("https://tackupnow.com/1", "window", false)
|
||||||
|
const matchingClient = addClient(
|
||||||
|
"https://tackupnow.com/2",
|
||||||
|
"window",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
addClient("https://tackupnow.com/controlled", "window", true)
|
||||||
|
|
||||||
|
initWorker(self)
|
||||||
|
|
||||||
|
const notificationHandler = getHandler("notificationclick")
|
||||||
|
|
||||||
|
const event = createNotificationEvent("https://tackupnow.com/2")
|
||||||
|
notificationHandler(event)
|
||||||
|
await waitUntilCalls(event)
|
||||||
|
|
||||||
|
expect(matchingClient.focus).toHaveBeenCalled()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("claims on activate", async () => {
|
test("claims on activate", async () => {
|
||||||
|
|
@ -287,6 +335,31 @@ describe("service worker", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function addClient(url: string, type: ClientTypes, controlled: boolean) {
|
||||||
|
const matchAllMock = self.clients.matchAll as jest.Mock
|
||||||
|
|
||||||
|
matchAllMock.mockImplementation(
|
||||||
|
(args: { type: string; includeUncontrolled?: boolean }) => {
|
||||||
|
const clientList = args.includeUncontrolled
|
||||||
|
? [...uncontrolledClients, ...controlledClients]
|
||||||
|
: controlledClients
|
||||||
|
return clientList.filter((client) => client.type === args.type)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const clientList = controlled ? controlledClients : uncontrolledClients
|
||||||
|
|
||||||
|
const client: WindowClient = {
|
||||||
|
url,
|
||||||
|
type,
|
||||||
|
focus: jest.fn(() => Promise.resolve(client)) as WindowClient["focus"],
|
||||||
|
} as WindowClient
|
||||||
|
|
||||||
|
clientList.push(client)
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
function getHandler(eventName: string) {
|
function getHandler(eventName: string) {
|
||||||
expect(self.addEventListener).toHaveBeenCalledWith(
|
expect(self.addEventListener).toHaveBeenCalledWith(
|
||||||
eventName,
|
eventName,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,12 @@ export default function start(self: ServiceWorkerGlobalScope) {
|
||||||
})
|
})
|
||||||
|
|
||||||
self.addEventListener("push", function (event: PushEvent) {
|
self.addEventListener("push", function (event: PushEvent) {
|
||||||
const { title, body, badge, icon, destination } = event.data?.json()
|
const { title, body, badge, icon, destination } = event.data?.json() ?? {
|
||||||
|
title: "It was missing yo",
|
||||||
|
badge: "image/blah",
|
||||||
|
icon: "image/blah",
|
||||||
|
destination: "https://tackupnow.com",
|
||||||
|
}
|
||||||
|
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
self.registration.showNotification(title, {
|
self.registration.showNotification(title, {
|
||||||
|
|
@ -31,9 +36,25 @@ export default function start(self: ServiceWorkerGlobalScope) {
|
||||||
|
|
||||||
self.addEventListener("notificationclick", function (event) {
|
self.addEventListener("notificationclick", function (event) {
|
||||||
event.notification.close()
|
event.notification.close()
|
||||||
|
const destination = event.notification.data.destination
|
||||||
|
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
self.clients.openWindow(event.notification.data.destination)
|
(async () => {
|
||||||
|
const clients = await self.clients.matchAll({
|
||||||
|
type: "window",
|
||||||
|
includeUncontrolled: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const existingClient = clients.find(
|
||||||
|
(client) => client.url === event.notification.data.destination
|
||||||
|
)
|
||||||
|
|
||||||
|
if (existingClient === undefined) {
|
||||||
|
await self.clients.openWindow(destination)
|
||||||
|
} else {
|
||||||
|
await existingClient.focus()
|
||||||
|
}
|
||||||
|
})()
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue