who_need_help/assets/js/service_worker.test.mjs

157 lines
3.6 KiB
JavaScript

import assert from "node:assert/strict"
import {after, before, beforeEach, test} from "node:test"
const originalSelf = globalThis.self
const listeners = new Map()
const shownNotifications = []
let windowClients = []
let openedWindow = null
before(async () => {
globalThis.self = {
addEventListener(type, handler) {
listeners.set(type, handler)
},
location: {origin: "https://whoneedhelp.com"},
registration: {
async showNotification(title, options) {
shownNotifications.push({title, options})
}
},
clients: {
async matchAll(options) {
assert.deepEqual(options, {type: "window", includeUncontrolled: true})
return windowClients
},
async openWindow(url) {
openedWindow = url
return {url}
}
}
}
await import("../../priv/static/sw.js")
})
beforeEach(() => {
shownNotifications.length = 0
windowClients = []
openedWindow = null
})
after(() => {
globalThis.self = originalSelf
})
test("push keeps a safe same-origin path in the operating-system notification", async () => {
const event = pushEvent({
title: "Request accepted",
body: "Open the private request chat.",
path: "/requests/7f84fc06-0fae-4e9d-b266-041ca87678d1?section=chat",
tag: "request-update"
})
listeners.get("push")(event)
await event.completion
assert.deepEqual(shownNotifications, [
{
title: "Request accepted",
options: {
body: "Open the private request chat.",
icon: "/images/pwa-192.png",
badge: "/images/favicon-48.png",
tag: "request-update",
data: {
path: "/requests/7f84fc06-0fae-4e9d-b266-041ca87678d1?section=chat"
}
}
}
])
})
test("push rejects an external path instead of leaving the application origin", async () => {
const event = pushEvent({
title: "Unsafe target",
path: "//attacker.example/redirect"
})
listeners.get("push")(event)
await event.completion
assert.equal(shownNotifications[0].options.data.path, "/notifications")
})
test("notification click navigates and focuses an existing application window", async () => {
const navigation = []
let focused = false
windowClients = [
{
url: "https://whoneedhelp.com/notifications?section=settings",
async navigate(url) {
navigation.push(url)
},
async focus() {
focused = true
}
},
{url: "https://example.com/"}
]
const event = clickEvent("/requests/request-id")
listeners.get("notificationclick")(event)
await event.completion
assert.equal(event.closed, true)
assert.deepEqual(navigation, ["https://whoneedhelp.com/requests/request-id"])
assert.equal(focused, true)
assert.equal(openedWindow, null)
})
test("notification click opens the application when no application window exists", async () => {
windowClients = [{url: "https://example.com/"}]
const event = clickEvent("/notifications")
listeners.get("notificationclick")(event)
await event.completion
assert.equal(event.closed, true)
assert.equal(openedWindow, "https://whoneedhelp.com/notifications")
})
function pushEvent(payload) {
return waitableEvent({
data: {
json() {
return payload
}
}
})
}
function clickEvent(path) {
const event = waitableEvent({
closed: false,
notification: {
data: {path},
close() {
event.closed = true
}
}
})
return event
}
function waitableEvent(properties) {
return {
...properties,
completion: Promise.resolve(),
waitUntil(completion) {
this.completion = completion
}
}
}