mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
597 lines
21 KiB
TypeScript
597 lines
21 KiB
TypeScript
import { CATEGORIES, TOOLS, toolSection } from "@snapotter/shared";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
buildToolRequestDiscussionUrl,
|
|
matchTool,
|
|
scoreTool,
|
|
searchTools,
|
|
type ToolSearchItem,
|
|
} from "../../../apps/landing/src/lib/tool-search.js";
|
|
|
|
const jpgPng = {
|
|
id: "jpg-to-png",
|
|
haystack: "jpg to png convert jpg to png jpg jpeg png",
|
|
};
|
|
|
|
const tools: ToolSearchItem[] = [
|
|
{
|
|
id: "compress",
|
|
name: "Compress",
|
|
description: "Reduce image file size by quality or target size",
|
|
modality: "image",
|
|
category: "optimization",
|
|
url: "/tools/image/compress/",
|
|
icon: "Minimize2",
|
|
iconSvg: "",
|
|
color: "#10B981",
|
|
acceptedInputs: ["image/jpeg", "image/png"],
|
|
outputModality: "image",
|
|
keywords: ["image compression", "jpg size reducer", "optimize image"],
|
|
workflowAliases: ["optimize web", "reduce image size"],
|
|
},
|
|
{
|
|
id: "compress-pdf",
|
|
name: "Compress PDF",
|
|
description: "Reduce PDF file size while preserving readability",
|
|
modality: "document",
|
|
category: "pdf-optimize",
|
|
url: "/tools/pdf/compress-pdf/",
|
|
icon: "Minimize2",
|
|
iconSvg: "",
|
|
color: "#10B981",
|
|
acceptedInputs: ["application/pdf", "pdf"],
|
|
outputModality: "document",
|
|
keywords: ["pdf compression", "reduce pdf size", "compress document"],
|
|
workflowAliases: ["optimize pdf", "reduce pdf size"],
|
|
},
|
|
{
|
|
id: "convert-document",
|
|
name: "Convert Document",
|
|
description: "Convert PDF and document files between formats",
|
|
modality: "document",
|
|
category: "document-conversion",
|
|
url: "/tools/pdf/convert-document/",
|
|
icon: "FileOutput",
|
|
iconSvg: "",
|
|
color: "#3B82F6",
|
|
acceptedInputs: ["application/pdf", "pdf", "docx"],
|
|
outputModality: "document",
|
|
keywords: ["pdf converter", "document converter", "change document format"],
|
|
workflowAliases: [],
|
|
},
|
|
{
|
|
id: "inspect-pdf",
|
|
name: "Inspect PDF",
|
|
description: "Inspect PDF metadata and page information",
|
|
modality: "document",
|
|
category: "pdf-inspect",
|
|
url: "/tools/pdf/inspect-pdf/",
|
|
icon: "Info",
|
|
iconSvg: "",
|
|
color: "#6366F1",
|
|
acceptedInputs: ["application/pdf", "pdf"],
|
|
outputModality: "document",
|
|
keywords: ["pdf metadata", "document properties"],
|
|
workflowAliases: ["inspect document"],
|
|
},
|
|
{
|
|
id: "convert",
|
|
name: "Convert",
|
|
description: "Convert between image formats",
|
|
modality: "image",
|
|
category: "essentials",
|
|
url: "/tools/image/convert/",
|
|
icon: "FileOutput",
|
|
iconSvg: "",
|
|
color: "#3B82F6",
|
|
acceptedInputs: ["image/jpeg", "image/png", "jpg", "png", "webp"],
|
|
outputModality: "image",
|
|
keywords: ["jpg to png", "image converter"],
|
|
workflowAliases: [],
|
|
},
|
|
{
|
|
id: "remove-background",
|
|
name: "Remove Background",
|
|
description: "Remove image backgrounds and export transparent PNG files",
|
|
modality: "image",
|
|
category: "enhance",
|
|
url: "/tools/image/remove-background/",
|
|
icon: "Sparkles",
|
|
iconSvg: "",
|
|
color: "#F59E0B",
|
|
acceptedInputs: ["image/jpeg", "image/png"],
|
|
outputModality: "image",
|
|
keywords: ["remove bg", "cutout", "transparent background", "transparent png"],
|
|
workflowAliases: ["enhance ai"],
|
|
},
|
|
{
|
|
id: "trim-video",
|
|
name: "Trim Video",
|
|
description: "Cut video clips by start and end time",
|
|
modality: "video",
|
|
category: "video-edit",
|
|
url: "/tools/video/trim-video/",
|
|
icon: "Scissors",
|
|
iconSvg: "",
|
|
color: "#EF4444",
|
|
acceptedInputs: ["video/mp4", "mp4"],
|
|
outputModality: "video",
|
|
keywords: ["cut video", "shorten mp4"],
|
|
workflowAliases: [],
|
|
},
|
|
{
|
|
id: "inspect-psd",
|
|
name: "Inspect PSD",
|
|
description: "Inspect Photoshop document metadata and layers",
|
|
modality: "image",
|
|
category: "image-inspect",
|
|
url: "/tools/image/inspect-psd/",
|
|
icon: "Info",
|
|
iconSvg: "",
|
|
color: "#6366F1",
|
|
acceptedInputs: ["image/vnd.adobe.photoshop", "psd"],
|
|
outputModality: "image",
|
|
keywords: ["psd metadata", "photoshop layers"],
|
|
workflowAliases: ["inspect psd"],
|
|
},
|
|
];
|
|
|
|
function getToolFixture(id: string): ToolSearchItem {
|
|
const tool = tools.find((entry) => entry.id === id);
|
|
if (!tool) {
|
|
throw new Error(`Missing tool fixture: ${id}`);
|
|
}
|
|
return tool;
|
|
}
|
|
|
|
const realTools: ToolSearchItem[] = TOOLS.map((tool) => {
|
|
const category = CATEGORIES.find((entry) => entry.id === tool.category);
|
|
return {
|
|
id: tool.id,
|
|
name: tool.name,
|
|
description: tool.description,
|
|
modality: tool.modality,
|
|
category: tool.category,
|
|
url: `/tools/${toolSection(tool)}/${tool.id}/`,
|
|
icon: tool.icon,
|
|
iconSvg: "",
|
|
color: category?.color ?? "#6B7280",
|
|
acceptedInputs: tool.acceptedInputs,
|
|
outputModality: tool.outputModality,
|
|
keywords: tool.keywords,
|
|
workflowAliases: [],
|
|
};
|
|
});
|
|
|
|
const genericConverterIds = ["convert", "convert-audio", "convert-video", "convert-spreadsheet"];
|
|
const pdfConversionIds = new Set([
|
|
"pdf-to-word",
|
|
"pdf-to-jpg",
|
|
"pdf-to-png",
|
|
"pdf-to-tiff",
|
|
"pdf-to-image",
|
|
]);
|
|
const psdConversionIds = new Set(["psd-to-jpg", "psd-to-png", "psd-to-svg"]);
|
|
const toPdfConversionIds = new Set([
|
|
"jpg-to-pdf",
|
|
"png-to-pdf",
|
|
"heic-to-pdf",
|
|
"tiff-to-pdf",
|
|
"webp-to-pdf",
|
|
"gif-to-pdf",
|
|
"eps-to-pdf",
|
|
"word-to-pdf",
|
|
"excel-to-pdf",
|
|
"powerpoint-to-pdf",
|
|
"html-to-pdf",
|
|
"markdown-to-pdf",
|
|
"image-to-pdf",
|
|
]);
|
|
const fromPdfConversionIds = new Set(["pdf-to-word", "pdf-to-jpg", "pdf-to-png", "pdf-to-tiff"]);
|
|
const videoToAudioIds = new Set([
|
|
"extract-audio",
|
|
"mp4-to-mp3",
|
|
"mov-to-mp3",
|
|
"mkv-to-mp3",
|
|
"webm-to-mp3",
|
|
"avi-to-mp3",
|
|
"mp4-to-wav",
|
|
"mov-to-wav",
|
|
"mp4-to-ogg",
|
|
"convert-audio",
|
|
]);
|
|
|
|
describe("landing matchTool", () => {
|
|
it.each(["jpeg to png", "jpg2png", "jpg to png", "covert jpg to png"])("matches %s", (q) => {
|
|
expect(matchTool(q, jpgPng.haystack)).toBe(true);
|
|
});
|
|
|
|
it("typo within one edit still matches via fallback", () => {
|
|
expect(matchTool("jpge to png", jpgPng.haystack)).toBe(true);
|
|
});
|
|
|
|
it("rejects unrelated query", () => {
|
|
expect(matchTool("trim video", jpgPng.haystack)).toBe(false);
|
|
});
|
|
|
|
it("requires exact matches for known short file formats", () => {
|
|
expect(matchTool("jpg2png", "jpg2png")).toBe(true);
|
|
expect(matchTool("mp4 to mp3", "convert video mp4 mov mkv avi webm")).toBe(false);
|
|
expect(
|
|
matchTool("mp4 to mp3", "m4a to mp3 aac to mp3 wav to mp3 ogg to mp3 audio converter"),
|
|
).toBe(false);
|
|
expect(matchTool("mp4 to mp3", "mp4 to mp3 extract audio from mp4 as mp3")).toBe(true);
|
|
expect(matchTool("add text to image", "add text captions and labels to images")).toBe(true);
|
|
expect(matchTool("pdf to jpg", "convert image jpg png webp")).toBe(false);
|
|
expect(matchTool("pdf to jpg", "jpg to pdf convert jpg images to pdf")).toBe(false);
|
|
expect(matchTool("pdf to jpg", "pdf to jpg convert pdf pages to images")).toBe(true);
|
|
expect(matchTool("aviftopng", "png to avif convert png images to avif")).toBe(false);
|
|
expect(matchTool("aviftopng", "avif to png convert avif images to png")).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("landing scoreTool", () => {
|
|
it("scores exact or near-exact tool name matches with a reason", () => {
|
|
const result = scoreTool("remove background", getToolFixture("remove-background"));
|
|
expect(result.score).toBeGreaterThan(0);
|
|
expect(result.reason).toMatch(/name|keyword/i);
|
|
});
|
|
|
|
it("ranks conversion shorthand through normalized keywords", () => {
|
|
const result = searchTools(tools, { query: "jpg2png", modality: "all", limit: 5 });
|
|
expect(result.results[0]?.item.id).toBe("convert");
|
|
expect(result.results[0]?.score).toBeGreaterThan(0);
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
});
|
|
|
|
it("ranks PDF compression above generic image compression", () => {
|
|
const result = searchTools(tools, { query: "compress pdf", modality: "all", limit: 5 });
|
|
expect(result.results[0]?.item.id).toBe("compress-pdf");
|
|
});
|
|
|
|
it("preserves conversion intent when ranking PDF tools", () => {
|
|
const result = searchTools(tools, { query: "convert pdf", modality: "all", limit: 5 });
|
|
expect(result.results[0]?.item.id).toBe("convert-document");
|
|
});
|
|
|
|
it("preserves conversion intent when ranking document tools", () => {
|
|
const result = searchTools(tools, { query: "convert document", modality: "all", limit: 5 });
|
|
expect(result.results[0]?.item.id).toBe("convert-document");
|
|
});
|
|
|
|
it("keeps unsupported conversion searches away from unrelated confident first results", () => {
|
|
const result = searchTools(tools, { query: "convert psd", modality: "all", limit: 5 });
|
|
expect(result.results.map((entry) => entry.item.id)).not.toContain("inspect-psd");
|
|
expect(result.hasConfidentMatch).toBe(false);
|
|
});
|
|
|
|
it("understands common task aliases", () => {
|
|
const result = searchTools(tools, {
|
|
query: "remove bg transparent",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
expect(result.results[0]?.item.id).toBe("remove-background");
|
|
});
|
|
|
|
it("hard-filters by modality after scoring", () => {
|
|
const result = searchTools(tools, { query: "compress", modality: "document", limit: 5 });
|
|
expect(result.results.map((entry) => entry.item.id)).toEqual(["compress-pdf"]);
|
|
});
|
|
|
|
it("reports no confident match for unrelated searches", () => {
|
|
const result = searchTools(tools, {
|
|
query: "convert figma file to layered psd",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
expect(result.results).toHaveLength(0);
|
|
expect(result.hasConfidentMatch).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("landing searchTools with real catalog metadata", () => {
|
|
it("ranks PDF-specific conversion before generic converters for convert pdf", () => {
|
|
const result = searchTools(realTools, { query: "convert pdf", modality: "all", limit: 8 });
|
|
const firstId = result.results[0]?.item.id;
|
|
|
|
expect(firstId).toBeDefined();
|
|
expect(firstId?.startsWith("pdf-to-") || pdfConversionIds.has(firstId ?? "")).toBe(true);
|
|
expect(genericConverterIds).not.toContain(firstId);
|
|
});
|
|
|
|
it("ranks document conversion before generic converters for convert document", () => {
|
|
const result = searchTools(realTools, { query: "convert document", modality: "all", limit: 8 });
|
|
const rankedIds = result.results.map((entry) => entry.item.id);
|
|
|
|
expect(rankedIds[0]).toBe("convert-document");
|
|
for (const genericId of genericConverterIds) {
|
|
const genericIndex = rankedIds.indexOf(genericId);
|
|
if (genericIndex !== -1) {
|
|
expect(genericIndex).toBeGreaterThan(rankedIds.indexOf("convert-document"));
|
|
}
|
|
}
|
|
});
|
|
|
|
it("ranks PSD-specific conversion before generic converters for convert psd", () => {
|
|
const result = searchTools(realTools, { query: "convert psd", modality: "all", limit: 8 });
|
|
const firstId = result.results[0]?.item.id;
|
|
|
|
expect(firstId).toBeDefined();
|
|
expect(psdConversionIds.has(firstId ?? "")).toBe(true);
|
|
expect(genericConverterIds).not.toContain(firstId);
|
|
});
|
|
|
|
it("ranks destination-to-PDF tools before PDF source converters for convert to pdf", () => {
|
|
const result = searchTools(realTools, { query: "convert to pdf", modality: "all", limit: 8 });
|
|
const firstId = result.results[0]?.item.id;
|
|
|
|
expect(firstId).toBeDefined();
|
|
expect(firstId?.endsWith("-to-pdf") || toPdfConversionIds.has(firstId ?? "")).toBe(true);
|
|
expect(fromPdfConversionIds.has(firstId ?? "")).toBe(false);
|
|
expect(genericConverterIds).not.toContain(firstId);
|
|
});
|
|
|
|
it("keeps explicit PDF source direction for pdf to jpg", () => {
|
|
const result = searchTools(realTools, { query: "pdf to jpg", modality: "all", limit: 8 });
|
|
|
|
expect(result.results[0]?.item.id).toBe("pdf-to-jpg");
|
|
});
|
|
|
|
it("does not create confident results for same-format alias conversions", () => {
|
|
const result = searchTools(realTools, { query: "jpg to jpeg", modality: "all", limit: 8 });
|
|
|
|
expect(result.results).toHaveLength(0);
|
|
expect(result.hasConfidentMatch).toBe(false);
|
|
});
|
|
|
|
it("returns confident results for document extension conversions", () => {
|
|
const result = searchTools(realTools, { query: "doc to docx", modality: "all", limit: 8 });
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(result.results[0]?.item.id).toBe("convert-document");
|
|
});
|
|
|
|
it("returns confident results for presentation extension conversions", () => {
|
|
const result = searchTools(realTools, { query: "ppt to pptx", modality: "all", limit: 8 });
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(result.results[0]?.item.id).toBe("convert-presentation");
|
|
});
|
|
|
|
it("returns confident results for spreadsheet extension conversions", () => {
|
|
const result = searchTools(realTools, { query: "xls to xlsx", modality: "all", limit: 8 });
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(result.results[0]?.item.id).toBe("convert-spreadsheet");
|
|
});
|
|
|
|
it("returns confident results for broad video to audio conversion", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "convert video to audio",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(videoToAudioIds.has(result.results[0]?.item.id ?? "")).toBe(true);
|
|
});
|
|
|
|
it("returns confident results for MP4 to audio conversion", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "mp4 to audio",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(videoToAudioIds.has(result.results[0]?.item.id ?? "")).toBe(true);
|
|
});
|
|
|
|
it("treats audio from video as video to audio conversion", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "convert audio from video",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
const firstId = result.results[0]?.item.id;
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(videoToAudioIds.has(firstId ?? "")).toBe(true);
|
|
expect(firstId).not.toBe("video-to-webp");
|
|
expect(firstId).not.toBe("video-to-gif");
|
|
});
|
|
|
|
it("treats mp3 from mp4 as mp4 to mp3 conversion", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "convert mp3 from mp4",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
const rankedIds = result.results.map((entry) => entry.item.id);
|
|
const mp4ToMp3Index = rankedIds.indexOf("mp4-to-mp3");
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(["mp4-to-mp3", "extract-audio", "convert-audio"]).toContain(result.results[0]?.item.id);
|
|
expect(mp4ToMp3Index).not.toBe(-1);
|
|
for (const unrelatedId of ["mp4-to-wav", "mp4-to-ogg", "mp4-to-aac", "mp4-to-flac"]) {
|
|
const unrelatedIndex = rankedIds.indexOf(unrelatedId);
|
|
if (unrelatedIndex !== -1) {
|
|
expect(unrelatedIndex).toBeGreaterThan(mp4ToMp3Index);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("returns confident results for broad video to MP4 conversion", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "convert video to mp4",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
const firstId = result.results[0]?.item.id;
|
|
const rankedIds = result.results.map((entry) => entry.item.id);
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(firstId === "convert-video" || firstId?.endsWith("-to-mp4")).toBe(true);
|
|
expect(rankedIds).not.toContain("compress-video");
|
|
});
|
|
|
|
it("returns confident results for broad audio to MP3 conversion", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "convert audio to mp3",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
const firstId = result.results[0]?.item.id;
|
|
const rankedIds = result.results.map((entry) => entry.item.id);
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(firstId === "convert-audio" || firstId?.endsWith("-to-mp3")).toBe(true);
|
|
expect(rankedIds).not.toContain("transcribe-audio");
|
|
});
|
|
|
|
it("returns confident results for broad image to PDF conversion", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "convert images to pdf",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
const firstId = result.results[0]?.item.id;
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(firstId === "image-to-pdf" || firstId?.endsWith("-to-pdf")).toBe(true);
|
|
});
|
|
|
|
it("returns confident results for broad document to PDF conversion", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "convert documents to pdf",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
const firstId = result.results[0]?.item.id;
|
|
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(
|
|
firstId === "convert-document" ||
|
|
[
|
|
"word-to-pdf",
|
|
"excel-to-pdf",
|
|
"powerpoint-to-pdf",
|
|
"html-to-pdf",
|
|
"markdown-to-pdf",
|
|
].includes(firstId ?? ""),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("keeps broad image convert available for bmp to png", () => {
|
|
const result = searchTools(realTools, { query: "bmp to png", modality: "all", limit: 5 });
|
|
const rankedIds = result.results.map((entry) => entry.item.id);
|
|
|
|
expect(result.results[0]?.item.id).toBe("convert");
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
expect(rankedIds).not.toContain("ocr");
|
|
expect(rankedIds).not.toContain("colorize");
|
|
expect(rankedIds).not.toContain("optimize-for-web");
|
|
});
|
|
|
|
it("does not rank reversed AVIF conversion first for avif to png", () => {
|
|
const result = searchTools(realTools, { query: "avif to png", modality: "all", limit: 5 });
|
|
|
|
expect(result.results[0]?.item.id).toBe("convert");
|
|
expect(result.results[0]?.item.id).not.toBe("png-to-avif");
|
|
});
|
|
|
|
it("normalizes compact AVIF conversion aliases before direction ranking", () => {
|
|
const result = searchTools(realTools, { query: "avif2png", modality: "all", limit: 5 });
|
|
const rankedIds = result.results.map((entry) => entry.item.id);
|
|
|
|
expect(result.results[0]?.item.id).toBe("convert");
|
|
expect(result.results[0]?.item.id).not.toBe("png-to-avif");
|
|
expect(rankedIds).not.toContain("ocr");
|
|
expect(rankedIds).not.toContain("colorize");
|
|
expect(rankedIds).not.toContain("optimize-for-web");
|
|
});
|
|
|
|
it("does not treat remove background from image as conversion intent", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "remove background from image",
|
|
modality: "all",
|
|
limit: 5,
|
|
});
|
|
|
|
expect(result.results[0]?.item.id).toBe("remove-background");
|
|
expect(result.hasConfidentMatch).toBe(true);
|
|
});
|
|
|
|
it("does not treat add text to image as conversion intent", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "add text to image",
|
|
modality: "all",
|
|
limit: 8,
|
|
});
|
|
|
|
expect(result.results[0]?.item.id).toBe("text-overlay");
|
|
});
|
|
|
|
it("does not treat add watermark to pdf as conversion intent", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "add watermark to pdf",
|
|
modality: "all",
|
|
limit: 8,
|
|
});
|
|
|
|
expect(result.results[0]?.item.id).toBe("watermark-pdf");
|
|
});
|
|
|
|
it("does not treat extract pages from pdf as conversion intent", () => {
|
|
const result = searchTools(realTools, {
|
|
query: "extract pages from pdf",
|
|
modality: "all",
|
|
limit: 8,
|
|
});
|
|
|
|
expect(result.results[0]?.item.id).toBe("extract-pages");
|
|
});
|
|
});
|
|
|
|
describe("buildToolRequestDiscussionUrl", () => {
|
|
it("builds an Ideas discussion URL with encoded title and body", () => {
|
|
const url = new URL(buildToolRequestDiscussionUrl("convert figma file to layered psd"));
|
|
expect(url.origin + url.pathname).toBe(
|
|
"https://github.com/snapotter-hq/snapotter/discussions/new",
|
|
);
|
|
expect(url.searchParams.get("category")).toBe("ideas");
|
|
expect(url.searchParams.get("title")).toBe("Tool request: convert figma file to layered psd");
|
|
expect(url.searchParams.get("body")).toContain(
|
|
"I searched SnapOtter for:\n\n> convert figma file to layered psd",
|
|
);
|
|
expect(url.searchParams.get("body")).not.toContain("I searched SnapOtter for: `");
|
|
});
|
|
|
|
it("strips title newlines and collapses whitespace", () => {
|
|
const url = new URL(buildToolRequestDiscussionUrl(" convert\n\nfoo\tto bar "));
|
|
expect(url.searchParams.get("title")).toBe("Tool request: convert foo to bar");
|
|
});
|
|
|
|
it("truncates long title queries", () => {
|
|
const long = "x".repeat(200);
|
|
const url = new URL(buildToolRequestDiscussionUrl(long));
|
|
expect(url.searchParams.get("title")).toBe(`Tool request: ${"x".repeat(120)}`);
|
|
expect(url.searchParams.get("body")).toContain("x".repeat(200));
|
|
});
|
|
|
|
it("uses a generic title for empty queries", () => {
|
|
const url = new URL(buildToolRequestDiscussionUrl(" "));
|
|
expect(url.searchParams.get("title")).toBe("Tool request");
|
|
expect(url.searchParams.get("body")).not.toContain("I searched SnapOtter for:");
|
|
});
|
|
|
|
it("renders backtick queries without an inline code span", () => {
|
|
const url = new URL(buildToolRequestDiscussionUrl("convert `pdf` to docx"));
|
|
expect(url.searchParams.get("body")).toContain(
|
|
"I searched SnapOtter for:\n\n> convert `pdf` to docx",
|
|
);
|
|
expect(url.searchParams.get("body")).not.toContain("I searched SnapOtter for: `");
|
|
});
|
|
});
|