Skip to content
File

Blob: tests/worker/protocol/dav/html-listings.workers.test.ts

typescript133 lines
1import { describe, expect, it } from "vitest";
2 
3import { createDavFixture, davRequest } from "@tests/worker/helpers/dav";
4import { fetchWorker } from "@tests/worker/helpers/http";
5import { event, uid as calendarUid } from "@tests/worker/protocol/caldav/helpers";
6import { uid as addressUid, vcard } from "@tests/worker/protocol/carddav/helpers";
7 
8function encoded(segment: string): string {
9 return encodeURIComponent(segment);
10}
11 
12describe("DAV browser HTML listings", () => {
13 it("renders scoped root discovery listings", async () => {
14 const fixture = await createDavFixture(["files.readonly"]);
15 
16 const response = await fetchWorker(davRequest(fixture, "/"));
17 expect(response.status).toBe(200);
18 expect(response.headers.get("content-type")).toContain("text/html");
19 expect(response.headers.get("cache-control")).toBe("no-store");
20 expect(response.headers.get("content-security-policy")).toContain("default-src 'none'");
21 
22 const html = await response.text();
23 expect(html).toContain("WebDAV file storage");
24 expect(html).toContain("/files/");
25 expect(html).not.toContain("/calendars/");
26 expect(html).not.toContain("/addressbooks/");
27 
28 expect(await fetchWorker(davRequest(fixture, "/calendars/"))).toHaveProperty("status", 403);
29 expect(await fetchWorker(davRequest(fixture, "/addressbooks/"))).toHaveProperty("status", 403);
30 
31 const head = await fetchWorker(davRequest(fixture, "/", { method: "HEAD" }));
32 expect(head.status).toBe(200);
33 expect(head.headers.get("content-type")).toContain("text/html");
34 await expect(head.text()).resolves.toBe("");
35 });
36 
37 it("renders file collection listings and keeps file GET unchanged", async () => {
38 const fixture = await createDavFixture(["files.full"]);
39 const directory = `browser-${crypto.randomUUID()}`;
40 const fileName = `budget <Q2> & notes-${crypto.randomUUID()}.txt`;
41 const fileHref = `/files/${directory}/${encoded(fileName)}`;
42 
43 expect(await fetchWorker(davRequest(fixture, `/files/${directory}/`, { method: "MKCOL" }))).toHaveProperty(
44 "status",
45 201,
46 );
47 expect(
48 await fetchWorker(
49 davRequest(fixture, fileHref, {
50 method: "PUT",
51 headers: { "content-type": "text/plain" },
52 body: "listing body",
53 }),
54 ),
55 ).toHaveProperty("status", 201);
56 
57 const listing = await fetchWorker(davRequest(fixture, `/files/${directory}/`));
58 expect(listing.status).toBe(200);
59 expect(listing.headers.get("content-type")).toContain("text/html");
60 const html = await listing.text();
61 expect(html).toContain("Parent directory");
62 expect(html).toContain("budget &lt;Q2&gt; &amp; notes");
63 expect(html).not.toContain(fileName);
64 
65 const file = await fetchWorker(davRequest(fixture, fileHref));
66 expect(file.status).toBe(200);
67 expect(file.headers.get("content-type")).toBe("text/plain");
68 await expect(file.text()).resolves.toBe("listing body");
69 });
70 
71 it("renders calendar listings and keeps calendar object GET unchanged", async () => {
72 const fixture = await createDavFixture(["caldav.full"]);
73 const href = "/calendars/default/listing.ics";
74 const body = event({ uid: calendarUid(), summary: "Planning <Q2> & Sync" });
75 
76 expect(
77 await fetchWorker(
78 davRequest(fixture, href, {
79 method: "PUT",
80 headers: { "content-type": "text/calendar; charset=utf-8" },
81 body,
82 }),
83 ),
84 ).toHaveProperty("status", 201);
85 
86 const home = await fetchWorker(davRequest(fixture, "/calendars/"));
87 expect(home.status).toBe(200);
88 expect(await home.text()).toContain("/calendars/default/");
89 
90 const collection = await fetchWorker(davRequest(fixture, "/calendars/default/"));
91 expect(collection.status).toBe(200);
92 const html = await collection.text();
93 expect(html).toContain("listing.ics");
94 expect(html).toContain("VEVENT - Planning &lt;Q2&gt; &amp; Sync");
95 
96 const object = await fetchWorker(davRequest(fixture, href));
97 expect(object.status).toBe(200);
98 expect(object.headers.get("content-type")).toBe("text/calendar; charset=utf-8");
99 await expect(object.text()).resolves.toBe(body);
100 });
101 
102 it("renders address book listings and keeps vCard object GET unchanged", async () => {
103 const fixture = await createDavFixture(["carddav.full"]);
104 const href = "/addressbooks/default/listing.vcf";
105 const body = vcard({ uid: addressUid(), fn: "Alice <Ops> & Co", email: "alice@example.test" });
106 
107 expect(
108 await fetchWorker(
109 davRequest(fixture, href, {
110 method: "PUT",
111 headers: { "content-type": "text/vcard; charset=utf-8" },
112 body,
113 }),
114 ),
115 ).toHaveProperty("status", 201);
116 
117 const home = await fetchWorker(davRequest(fixture, "/addressbooks/"));
118 expect(home.status).toBe(200);
119 expect(await home.text()).toContain("/addressbooks/default/");
120 
121 const collection = await fetchWorker(davRequest(fixture, "/addressbooks/default/"));
122 expect(collection.status).toBe(200);
123 const html = await collection.text();
124 expect(html).toContain("listing.vcf");
125 expect(html).toContain("Alice &lt;Ops&gt; &amp; Co - alice@example.test");
126 
127 const object = await fetchWorker(davRequest(fixture, href));
128 expect(object.status).toBe(200);
129 expect(object.headers.get("content-type")).toBe("text/vcard; charset=utf-8");
130 await expect(object.text()).resolves.toBe(body);
131 });
132});