Skip to content
Source
Sign inSign up

markdownMedia.test.ts

290 lines10.6 KB

1import { EnvironmentId, ThreadId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3​
4import { resolveMarkdownMediaPreview } from "./markdownMedia";
5import { resolveMarkdownLinkPresentation } from "./markdownLinks";
6​
7const input = {
8 environmentId: EnvironmentId.make("environment-1"),
9 threadId: ThreadId.make("thread-1"),
10 workspaceRoot: "/repo",
11};
12​
13describe("resolveMarkdownMediaPreview", () => {
14 it("decodes remote filenames once without changing the authored URL", () => {
15 const href = "https://cdn.example.com/clip%20one%2520%2Emp4?signature=a%2fb#t=2";
16 expect(resolveMarkdownMediaPreview(href, input)).toMatchObject({
17 kind: "video",
18 source: {
19 uri: href,
20 actionsSource: {
21 name: "clip one%20.mp4",
22 mimeType: "video/mp4",
23 reference: { kind: "url", url: href },
24 },
25 },
26 });
27 });
28​
29 it("provides extensionless image actions only for image embeds", () => {
30 const href = "https://cdn.example.com/render?id=42";
31 expect(resolveMarkdownMediaPreview(href, input)).toBeNull();
32 expect(resolveMarkdownMediaPreview(href, { ...input, imageEmbed: true })).toMatchObject({
33 kind: "image",
34 source: { actionsSource: { reference: { kind: "url", url: href }, mimeType: "image/*" } },
35 });
36 });
37​
38 it.each([
39 ["/tmp/frame%23one.png", "/tmp/frame#one.png"],
40 ["/tmp/frame%3Fone.png", "/tmp/frame?one.png"],
41 ["/tmp/frame%2523one.png", "/tmp/frame%23one.png"],
42 ["file://server/share/frame.png", "\\\\server\\share\\frame.png", "\\\\server\\share"],
43 ["\\\\server\\share\\frame.png", "\\\\server\\share\\frame.png", "\\\\server\\share"],
44 ])("keeps encoded filename and UNC semantics for %s", (href, path, workspaceRoot = "/repo") => {
45 expect(resolveMarkdownMediaPreview(href, { ...input, workspaceRoot })).toMatchObject({
46 kind: "image",
47 source: {
48 resource: { path },
49 actionsSource: { reference: { kind: "file", path } },
50 },
51 });
52 });
53​
54 it("serves a linked T3 attachment file in place like any other host path", () => {
55 const path = "/home/demo/.t3/userdata/attachments/11111111-1111-4111-8111-111111111111-mp4.mp4";
56 expect(resolveMarkdownMediaPreview(path, input)).toMatchObject({
57 kind: "video",
58 source: {
59 resource: { _tag: "media-file", threadId: input.threadId, path },
60 actionsSource: { resource: { _tag: "media-file", path } },
61 },
62 });
63 });
64​
65 it("preserves a signed PDF URL and its authored name", () => {
66 const href = "//cdn.example.com/report%20one.pdf?signature=a%2fb#page=2";
67 expect(resolveMarkdownMediaPreview(href, input)).toMatchObject({
68 kind: "pdf",
69 source: { uri: `https:${href}`, name: "report one.pdf", mimeType: "application/pdf" },
70 });
71 });
72​
73 it.each(["./image.png", "./report.pdf", "./sound.mp3"])(
74 "uses the draft workspace when no thread exists: %s",
75 (href) => {
76 expect(
77 resolveMarkdownMediaPreview(href, { ...input, threadId: undefined })?.source,
78 ).toMatchObject({
79 resource: { _tag: "draft-workspace-file", cwd: "/repo", path: `/repo/${href}` },
80 });
81 },
82 );
83​
84 it("keeps captured host media unavailable while allowing direct media", () => {
85 const captured = { ...input, captured: true };
86 for (const href of ["./image.png", "./clip.mp4", "./report.pdf", "./sound.mp3"]) {
87 expect(resolveMarkdownMediaPreview(href, captured)).toBeNull();
88 }
89 expect(resolveMarkdownMediaPreview("https://cdn.example.com/report.pdf", captured)?.kind).toBe(
90 "pdf",
91 );
92 });
93​
94 it.each([["/tmp/sound%23one.mp3", "audio", "audio/mpeg", "/tmp/sound#one.mp3"]])(
95 "opens an authored embed through the host: %s",
96 (href, kind, mimeType, path) => {
97 expect(resolveMarkdownMediaPreview(href, { ...input, imageEmbed: true })).toMatchObject({
98 kind,
99 source: {
100 kind: "document",
101 name: path.split("/").at(-1),
102 mimeType,
103 environmentId: input.environmentId,
104 resource: { _tag: "media-file", threadId: input.threadId, path },
105 },
106 });
107 },
108 );
109​
110 it.each([
111 ["sound%20one.mp3", "audio"],
112 ["report%20one.csv", "document"],
113 ["report.tsv", "document"],
114 ["report.docx", "document"],
115 ["report.xlsx", "document"],
116 ["report.pptx", "document"],
117 ["report.unknown", "image"],
118 ])("preserves direct signed embeds: %s", (name, kind) => {
119 const href = `https://cdn.example.com/${name}?signature=a%2fb`;
120 expect(resolveMarkdownMediaPreview(href, { ...input, imageEmbed: true })).toMatchObject({
121 kind,
122 source: { uri: href, name: decodeURIComponent(name) },
123 });
124 });
125​
126 it.each([
127 "https://docs.example.com/guide.html",
128 "https://example.com/index.php?topic=files",
129 "https://example.com/report.unknown",
130 "https://example.com/main.tf",
131 "https://example.com/schema.prisma",
132 "https://example.com/chart.php?id=1",
133 "http://example.com/chart.php?id=1",
134 "//example.com/chart.php?id=1",
135 "https://example.com/chart?format=png",
136 "http://example.com/chart?format=png",
137 "//example.com/chart?format=png",
138 ])("keeps ordinary remote links in the browser: %s", (href) => {
139 expect(resolveMarkdownMediaPreview(href, input)).toBeNull();
140 expect(resolveMarkdownMediaPreview(href, { ...input, imageEmbed: true })).toMatchObject({
141 kind: "image",
142 source: { uri: href.startsWith("//") ? `https:${href}` : href },
143 });
144 });
145​
146 it.each([
147 "javascript:report.csv",
148 "data:text/csv,a,b",
149 "ftp://server/report.csv",
150 "~/report.csv",
151 ])("rejects blocked document links and embeds: %s", (href) => {
152 expect(resolveMarkdownMediaPreview(href, input)).toBeNull();
153 expect(resolveMarkdownMediaPreview(href, { ...input, imageEmbed: true })).toBeNull();
154 });
155​
156 it.each([
157 "src/main.ts",
158 "src/main.tsx",
159 "report.txt",
160 "report.md",
161 "report.html",
162 "package.json",
163 "report.csv",
164 "report.tsv",
165 "LICENSE",
166 "Dockerfile",
167 "main.tf",
168 "schema.prisma",
169 "index.astro",
170 "main.zig",
171 "Containerfile",
172 ])("routes text and table links and embeds to the file screen: %s", (name) => {
173 for (const href of [name, "./" + name, "/tmp/" + name, "file:///tmp/" + name]) {
174 for (const imageEmbed of [false, true]) {
175 expect(resolveMarkdownMediaPreview(href, { ...input, imageEmbed })).toMatchObject({
176 kind: "file",
177 source: { name: name.split("/").at(-1) },
178 });
179 }
180 expect(resolveMarkdownLinkPresentation(href)).toMatchObject({ kind: "file" });
181 }
182 });
183​
184 it.each(["doc", "docx", "xls", "xlsx", "ppt", "pptx", "unknown", "zip"])(
185 "keeps main's source reader for host %s links and embeds",
186 (extension) => {
187 const name = `report.${extension}`;
188 for (const href of [name, `./${name}`, `/tmp/${name}`, `file:///tmp/${name}`]) {
189 for (const imageEmbed of [false, true]) {
190 expect(resolveMarkdownMediaPreview(href, { ...input, imageEmbed })).toMatchObject({
191 kind: "file",
192 source: {
193 name,
194 mimeType: "text/plain",
195 },
196 });
197 expect(
198 resolveMarkdownMediaPreview(href, {
199 ...input,
200 threadId: undefined,
201 imageEmbed,
202 })?.kind,
203 ).toBe("file");
204 expect(resolveMarkdownLinkPresentation(href)).toMatchObject({ kind: "file" });
205 }
206 }
207 },
208 );
209​
210 it.each(["report.csv", "report.tsv", "report.docx", "report.xlsx", "report.unknown"])(
211 "preserves draft, captured, and encoded document paths: %s",
212 (name) => {
213 const href = `/tmp/one%23two%2520.${name.split(".").at(-1)}`;
214 const preview = resolveMarkdownMediaPreview(href, { ...input, imageEmbed: true });
215 expect(preview?.source).toMatchObject({
216 name: `one#two%20.${name.split(".").at(-1)}`,
217 resource: { path: decodeURIComponent(href) },
218 });
219 expect(
220 resolveMarkdownMediaPreview(href, { ...input, threadId: undefined })?.source,
221 ).toMatchObject({ resource: { _tag: "draft-workspace-file", cwd: "/repo" } });
222 expect(resolveMarkdownMediaPreview(href, { ...input, captured: true })).toBeNull();
223 expect(
224 resolveMarkdownMediaPreview(href, { ...input, threadId: undefined, workspaceRoot: null }),
225 ).toBeNull();
226 },
227 );
228​
229 it.each([
230 ["src/main.ts:42", "/repo/src/main.ts", 42],
231 ["src/main.ts#L42C3", "/repo/src/main.ts", 42],
232 ["report.csv:142:3", "/repo/report.csv", 142],
233 ["/tmp/report.pdf:12#page=2", "/tmp/report.pdf", 12],
234 ["file:///tmp/sound.mp3#L12", "/tmp/sound.mp3", 12],
235 ["/tmp/frame%23one.png:12", "/tmp/frame#one.png", 12],
236 ["/tmp/frame%3Fone.png:12:3", "/tmp/frame?one.png", 12],
237 ["/tmp/frame%2523one.png:12", "/tmp/frame%23one.png", 12],
238 ["./clip.mp4#L12C3", "/repo/./clip.mp4", 12],
239 ["src/main.ts%3A42%3A3", "/repo/src/main.ts", 42],
240 ])("keeps positioned links on the file route: %s", (href, path, line) => {
241 expect(resolveMarkdownMediaPreview(href, input)).toBeNull();
242 const presentation = resolveMarkdownLinkPresentation(href);
243 expect(presentation).toMatchObject({ kind: "file", line });
244 if (presentation.kind === "file") {
245 expect(
246 presentation.path.startsWith("/") ? presentation.path : "/repo/" + presentation.path,
247 ).toBe(path);
248 }
249 });
250​
251 it.each(["mp3", "wav", "m4a", "ogg", "oga", "aac", "aiff", "flac", "opus"])(
252 "resolves audio embeds as audio rather than images: %s",
253 (extension) => {
254 expect(
255 resolveMarkdownMediaPreview("/tmp/sound." + extension, { ...input, imageEmbed: true }),
256 ).toMatchObject({
257 kind: "audio",
258 source: { kind: "document", resource: { _tag: "media-file" } },
259 });
260 },
261 );
262​
263 it("does not authorize local files without thread or draft context", () => {
264 expect(
265 resolveMarkdownMediaPreview("/tmp/report.pdf", {
266 ...input,
267 threadId: undefined,
268 workspaceRoot: null,
269 }),
270 ).toBeNull();
271 expect(resolveMarkdownMediaPreview("report.pdf", { ...input, workspaceRoot: null })).toBeNull();
272 });
273​
274 it("decodes document filenames once and preserves image and video embeds", () => {
275 expect(resolveMarkdownMediaPreview("/tmp/report%23one%2520.pdf", input)).toMatchObject({
276 kind: "pdf",
277 source: {
278 name: "report#one%20.pdf",
279 mimeType: "application/pdf",
280 resource: { path: "/tmp/report#one%20.pdf" },
281 },
282 });
283 expect(
284 resolveMarkdownMediaPreview("/tmp/image.png", { ...input, imageEmbed: true })?.kind,
285 ).toBe("image");
286 expect(resolveMarkdownMediaPreview("/tmp/clip.mp4", { ...input, imageEmbed: true })?.kind).toBe(
287 "video",
288 );
289 });
290});