feat: add PWA manifest and service worker routes with coverage
This commit is contained in:
32
src/app.js
32
src/app.js
@@ -20,6 +20,14 @@ function html(status, markup) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function text(status, body, contentType) {
|
||||||
|
return {
|
||||||
|
status,
|
||||||
|
headers: { "content-type": contentType || "text/plain; charset=utf-8" },
|
||||||
|
body,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function parseJSON(rawBody) {
|
function parseJSON(rawBody) {
|
||||||
if (!rawBody) {
|
if (!rawBody) {
|
||||||
return {};
|
return {};
|
||||||
@@ -112,6 +120,30 @@ function buildApp({ config }) {
|
|||||||
return json(200, { ok: true });
|
return json(200, { ok: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (method === "GET" && path === "/manifest.webmanifest") {
|
||||||
|
return text(
|
||||||
|
200,
|
||||||
|
JSON.stringify({
|
||||||
|
name: "X Article to Audio",
|
||||||
|
short_name: "XArtAudio",
|
||||||
|
start_url: "/",
|
||||||
|
display: "standalone",
|
||||||
|
background_color: "#1d232a",
|
||||||
|
theme_color: "#1d232a",
|
||||||
|
icons: [],
|
||||||
|
}),
|
||||||
|
"application/manifest+json; charset=utf-8",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === "GET" && path === "/sw.js") {
|
||||||
|
return text(
|
||||||
|
200,
|
||||||
|
"self.addEventListener('install', () => self.skipWaiting()); self.addEventListener('activate', () => self.clients.claim());",
|
||||||
|
"application/javascript; charset=utf-8",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (method === "GET" && path === "/") {
|
if (method === "GET" && path === "/") {
|
||||||
return html(200, renderHomePage({
|
return html(200, renderHomePage({
|
||||||
authenticated: Boolean(userId),
|
authenticated: Boolean(userId),
|
||||||
|
|||||||
@@ -16,12 +16,19 @@ function layout({ title, content }) {
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>${escapeHtml(title)}</title>
|
<title>${escapeHtml(title)}</title>
|
||||||
|
<meta name="theme-color" content="#1d232a" />
|
||||||
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/dist/full.css" rel="stylesheet" type="text/css" />
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/dist/full.css" rel="stylesheet" type="text/css" />
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-base-200">
|
<body class="min-h-screen bg-base-200">
|
||||||
<main class="max-w-md mx-auto p-4 space-y-4">
|
<main class="max-w-md mx-auto p-4 space-y-4">
|
||||||
${content}
|
${content}
|
||||||
</main>
|
</main>
|
||||||
|
<script>
|
||||||
|
if ("serviceWorker" in navigator) {
|
||||||
|
navigator.serviceWorker.register("/sw.js").catch(() => {});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,21 @@ test("rejects invalid X webhook signature", () => {
|
|||||||
assert.equal(response.status, 401);
|
assert.equal(response.status, 401);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("serves pwa manifest route", () => {
|
||||||
|
const app = createApp();
|
||||||
|
const response = app.handleRequest({
|
||||||
|
method: "GET",
|
||||||
|
path: "/manifest.webmanifest",
|
||||||
|
headers: {},
|
||||||
|
rawBody: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(response.status, 200);
|
||||||
|
assert.match(response.headers["content-type"], /application\/manifest\+json/);
|
||||||
|
const body = JSON.parse(response.body);
|
||||||
|
assert.equal(body.short_name, "XArtAudio");
|
||||||
|
});
|
||||||
|
|
||||||
test("X webhook returns not_article response and no charge", () => {
|
test("X webhook returns not_article response and no charge", () => {
|
||||||
const app = createApp();
|
const app = createApp();
|
||||||
postJSON(app, "/api/webhooks/polar", { userId: "u1", credits: 5, eventId: "evt1" }, "polar-secret");
|
postJSON(app, "/api/webhooks/polar", { userId: "u1", credits: 5, eventId: "evt1" }, "polar-secret");
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ test("layout includes daisyui stylesheet and mobile-first wrapper", () => {
|
|||||||
const html = layout({ title: "t", content: "x" });
|
const html = layout({ title: "t", content: "x" });
|
||||||
assert.match(html, /daisyui@5/);
|
assert.match(html, /daisyui@5/);
|
||||||
assert.match(html, /max-w-md mx-auto p-4/);
|
assert.match(html, /max-w-md mx-auto p-4/);
|
||||||
|
assert.match(html, /manifest.webmanifest/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("home page renders jobs list and wallet credits", () => {
|
test("home page renders jobs list and wallet credits", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user