paperclip/ui/src/components/MarkdownExternalLink.test.tsx

95 lines
3.7 KiB
TypeScript

// @vitest-environment node
import type { ReactNode } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { describe, expect, it, vi } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { ThemeProvider } from "../context/ThemeContext";
import { MarkdownBody, type MarkdownExternalReferenceMap } from "./MarkdownBody";
vi.mock("@/lib/router", () => ({
Link: ({
children,
to,
...props
}: { children: ReactNode; to: string } & React.ComponentProps<"a">) => (
<a href={to} {...props}>{children}</a>
),
}));
vi.mock("../api/issues", () => ({
issuesApi: { get: vi.fn() },
}));
function render(children: string, externalReferences?: MarkdownExternalReferenceMap) {
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
return renderToStaticMarkup(
<QueryClientProvider client={queryClient}>
<ThemeProvider>
<MarkdownBody externalReferences={externalReferences}>{children}</MarkdownBody>
</ThemeProvider>
</QueryClientProvider>,
);
}
describe("MarkdownBody external object decoration", () => {
const successReference: MarkdownExternalReferenceMap = {
"https://github.com/acme/web/pull/241": {
providerKey: "github",
objectType: "pull_request",
displayKey: "Github Pull Request",
iconKey: "github",
statusCategory: "succeeded",
statusIconKey: "git-merge",
liveness: "fresh",
statusLabel: "Merged",
displayTitle: "Add external refs",
},
};
it("decorates a known URL with the external status icon and metadata attributes", () => {
const html = render("Take a look: https://github.com/acme/web/pull/241", successReference);
expect(html).toContain('class="paperclip-markdown-external-ref"');
expect(html).toContain('data-external-link="resolved"');
expect(html).toContain('data-external-status="succeeded"');
expect(html).toContain('data-external-liveness="fresh"');
expect(html).toContain('aria-label="Github Pull Request Merged: Add external refs"');
expect(html).toContain("github.com/acme/web/pull/241");
});
it("matches a URL even when the user pasted it with different host case or trailing punctuation", () => {
const html = render("see HTTPS://Github.com/acme/web/pull/241#frag.", successReference);
expect(html).toContain('data-external-status="succeeded"');
});
it("renders an unresolved URL as a plain external link with no status affordance", () => {
const html = render("https://random.example.com/path");
expect(html).not.toContain("paperclip-markdown-external-ref");
expect(html).toContain('href="https://random.example.com/path"');
});
it("never decorates a URL that lives inside a fenced or inline code block", () => {
const html = render(
"```\nhttps://github.com/acme/web/pull/241\n```\n\nInline: `https://github.com/acme/web/pull/241`",
successReference,
);
// The fenced/inline literal should still be present as text but the
// decorated anchor should not appear since no `<a>` is created in code.
expect(html).not.toContain('class="paperclip-markdown-external-ref"');
});
it("shows liveness suffix in the aria-label when the object is stale or auth_required", () => {
const html = render("Auth-blocked: https://app.hubspot.com/leads/99", {
"https://app.hubspot.com/leads/99": {
providerKey: "hubspot",
objectType: "lead",
statusCategory: "auth_required",
liveness: "auth_required",
statusLabel: "Reconnect",
displayTitle: "Acme deal",
},
});
expect(html).toContain('aria-label="HubSpot Reconnect (Requires auth): Acme deal"');
});
});