mirror of
https://github.com/multipleof4/ntfy.git
synced 2026-10-16 00:17:56 +00:00
More web tests
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import userManager from "./UserManager";
|
||||
import api from "./Api";
|
||||
|
||||
// Api.js talks to the server through the global fetch and looks up credentials via userManager.
|
||||
// (vi.mock is hoisted above the imports by vitest, so the import above receives the mock.)
|
||||
vi.mock("./UserManager", () => ({ default: { get: vi.fn() } }));
|
||||
|
||||
let fetchMock;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
fetchMock = vi.fn();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
userManager.get.mockResolvedValue(undefined); // anonymous by default
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
describe("Api.poll", () => {
|
||||
it("parses newline-delimited JSON and skips lines without an id", async () => {
|
||||
const body = [
|
||||
JSON.stringify({ id: "1", message: "a" }),
|
||||
JSON.stringify({ event: "keepalive" }), // no id -> skipped
|
||||
JSON.stringify({ id: "2", message: "b" }),
|
||||
].join("\n");
|
||||
fetchMock.mockResolvedValue(new Response(body));
|
||||
|
||||
const messages = await api.poll("https://ntfy.sh", "mytopic");
|
||||
|
||||
expect(messages.map((m) => m.id)).toEqual(["1", "2"]);
|
||||
});
|
||||
|
||||
it("uses the plain poll URL without a since cursor", async () => {
|
||||
fetchMock.mockResolvedValue(new Response(""));
|
||||
await api.poll("https://ntfy.sh", "mytopic");
|
||||
expect(fetchMock).toHaveBeenCalledWith("https://ntfy.sh/mytopic/json?poll=1", expect.anything());
|
||||
});
|
||||
|
||||
it("uses the since URL when a cursor is given", async () => {
|
||||
fetchMock.mockResolvedValue(new Response(""));
|
||||
await api.poll("https://ntfy.sh", "mytopic", 12345);
|
||||
expect(fetchMock).toHaveBeenCalledWith("https://ntfy.sh/mytopic/json?poll=1&since=12345", expect.anything());
|
||||
});
|
||||
});
|
||||
|
||||
describe("Api.publish", () => {
|
||||
it("PUTs the message body to the base URL", async () => {
|
||||
fetchMock.mockResolvedValue({ status: 200 });
|
||||
await api.publish("https://ntfy.sh", "mytopic", "Hello", { priority: 5, tags: ["warning"] });
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
"https://ntfy.sh",
|
||||
expect.objectContaining({
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ topic: "mytopic", message: "Hello", priority: 5, tags: ["warning"] }),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("attaches basic auth when the user has a password", async () => {
|
||||
userManager.get.mockResolvedValue({ username: "phil", password: "secret" });
|
||||
fetchMock.mockResolvedValue({ status: 200 });
|
||||
await api.publish("https://ntfy.sh", "mytopic", "Hi");
|
||||
const [, options] = fetchMock.mock.calls[0];
|
||||
expect(options.headers.Authorization).toBe(`Basic ${btoa("phil:secret")}`);
|
||||
});
|
||||
|
||||
it("attaches bearer auth when the user has a token", async () => {
|
||||
userManager.get.mockResolvedValue({ token: "tk_abc" });
|
||||
fetchMock.mockResolvedValue({ status: 200 });
|
||||
await api.publish("https://ntfy.sh", "mytopic", "Hi");
|
||||
const [, options] = fetchMock.mock.calls[0];
|
||||
expect(options.headers.Authorization).toBe("Bearer tk_abc");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Api.topicAuth", () => {
|
||||
it("returns true for a 2xx response", async () => {
|
||||
fetchMock.mockResolvedValue({ status: 200 });
|
||||
expect(await api.topicAuth("https://ntfy.sh", "mytopic")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for 401/403", async () => {
|
||||
fetchMock.mockResolvedValue({ status: 403 });
|
||||
expect(await api.topicAuth("https://ntfy.sh", "mytopic")).toBe(false);
|
||||
});
|
||||
|
||||
it("throws for any other status", async () => {
|
||||
fetchMock.mockResolvedValue({ status: 500 });
|
||||
await expect(api.topicAuth("https://ntfy.sh", "mytopic")).rejects.toThrow("Unexpected server response 500");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Api web push", () => {
|
||||
const pushSubscription = {
|
||||
endpoint: "https://push.example/abc",
|
||||
keys: { auth: "AUTH", p256dh: "P256" },
|
||||
};
|
||||
|
||||
it("updateWebPush POSTs endpoint, keys and topics to the web push URL", async () => {
|
||||
fetchMock.mockResolvedValue({ status: 200 });
|
||||
await api.updateWebPush(pushSubscription, ["topicA", "topicB"]);
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
"https://ntfy.sh/v1/webpush",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
body: JSON.stringify({ endpoint: "https://push.example/abc", auth: "AUTH", p256dh: "P256", topics: ["topicA", "topicB"] }),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("deleteWebPush DELETEs just the endpoint", async () => {
|
||||
fetchMock.mockResolvedValue({ status: 200 });
|
||||
await api.deleteWebPush(pushSubscription);
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
"https://ntfy.sh/v1/webpush",
|
||||
expect.objectContaining({
|
||||
method: "DELETE",
|
||||
body: JSON.stringify({ endpoint: "https://push.example/abc" }),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user