diff --git a/packages/views/editor/extensions/file-card.test.tsx b/packages/views/editor/extensions/file-card.test.tsx new file mode 100644 index 000000000..7d0ab9904 --- /dev/null +++ b/packages/views/editor/extensions/file-card.test.tsx @@ -0,0 +1,105 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { render, screen, waitFor } from "@testing-library/react"; +import type { ReactElement } from "react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + +// Tiptap NodeView primitives can't be instantiated without a full editor. +// Stub the wrapper so FileCardView renders as a plain React component and +// the DOM can be inspected directly. +vi.mock("@tiptap/react", () => ({ + NodeViewWrapper: ({ children, ...rest }: any) =>
chart
", + originalContentType: "text/html", + }); + + const node = { + attrs: { + href: "/uploads/report.html", + filename: "report.html", + uploading: false, + }, + } as any; + + renderWithQuery(chart
"); + // The AttachmentCard chrome surfaces the filename as text inside its row. + // HtmlAttachmentPreview replaces the chrome entirely, so the filename + // must not appear as visible text. + expect(screen.queryByText("report.html")).toBeNull(); + }); +}); diff --git a/packages/views/editor/extensions/file-card.tsx b/packages/views/editor/extensions/file-card.tsx index cb42b5207..fe7285a5e 100644 --- a/packages/views/editor/extensions/file-card.tsx +++ b/packages/views/editor/extensions/file-card.tsx @@ -31,7 +31,7 @@ const FILE_CARD_MARKDOWN_RE = new RegExp( // React NodeView // --------------------------------------------------------------------------- -function FileCardView({ node }: NodeViewProps) { +export function FileCardView({ node }: NodeViewProps) { const href = (node.attrs.href as string) || ""; const filename = (node.attrs.filename as string) || ""; const uploading = node.attrs.uploading as boolean; diff --git a/packages/views/editor/readonly-content.test.tsx b/packages/views/editor/readonly-content.test.tsx index c87948b6e..9e30b6085 100644 --- a/packages/views/editor/readonly-content.test.tsx +++ b/packages/views/editor/readonly-content.test.tsx @@ -1,5 +1,17 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { fireEvent, render, waitFor } from "@testing-library/react"; +import type { ReactElement } from "react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + +const { getAttachmentTextContentMock } = vi.hoisted(() => ({ + getAttachmentTextContentMock: vi.fn(), +})); + +vi.mock("@multica/core/api", () => ({ + api: { getAttachmentTextContent: getAttachmentTextContentMock }, + PreviewTooLargeError: class extends Error {}, + PreviewUnsupportedError: class extends Error {}, +})); vi.mock("@multica/core/paths", () => ({ useWorkspacePaths: () => ({ @@ -253,3 +265,47 @@ describe("ReadonlyContent HTML block rendering", () => { ).not.toBeNull(); }); }); + +describe("ReadonlyContent file-card → AttachmentBlock HTML routing", () => { + // Regression pin for readonly-content.tsx:279. The `div data-type=fileCard` + // branch must render throughchart
", + originalContentType: "text/html", + }); + const attachment = { + id: "att-1", + url: "/uploads/report.html", + filename: "report.html", + content_type: "text/html", + size_bytes: 0, + } as any; + const { container, queryByText } = renderWithQuery( +chart
"); + // AttachmentCard chrome surfaces the filename as visible text in a + // row. HtmlAttachmentPreview replaces it entirely.
+ expect(queryByText("report.html")).toBeNull();
+ });
+});
diff --git a/packages/views/issues/components/comment-card.test.tsx b/packages/views/issues/components/comment-card.test.tsx
new file mode 100644
index 000000000..2a7b93dd9
--- /dev/null
+++ b/packages/views/issues/components/comment-card.test.tsx
@@ -0,0 +1,64 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { render, screen, waitFor } from "@testing-library/react";
+import type { ReactElement } from "react";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+
+const { getAttachmentTextContentMock } = vi.hoisted(() => ({
+ getAttachmentTextContentMock: vi.fn(),
+}));
+
+vi.mock("@multica/core/api", () => ({
+ api: {
+ getAttachmentTextContent: getAttachmentTextContentMock,
+ getAttachment: vi.fn(),
+ },
+ PreviewTooLargeError: class extends Error {},
+ PreviewUnsupportedError: class extends Error {},
+}));
+
+import { AttachmentList } from "./comment-card";
+
+function renderWithQuery(ui: ReactElement) {
+ const qc = new QueryClient({
+ defaultOptions: { queries: { retry: false, gcTime: 0 } },
+ });
+ return render( chart chart text;
+ // HtmlAttachmentPreview replaces the row entirely.
+ expect(screen.queryByText("report.html")).toBeNull();
+ });
+});
diff --git a/packages/views/issues/components/comment-card.tsx b/packages/views/issues/components/comment-card.tsx
index 83fc557b3..47f2a6905 100644
--- a/packages/views/issues/components/comment-card.tsx
+++ b/packages/views/issues/components/comment-card.tsx
@@ -121,7 +121,7 @@ function DeleteCommentDialog({
// Standalone attachment list — renders attachments not already in the markdown
// ---------------------------------------------------------------------------
-function AttachmentList({ attachments, content, className }: { attachments?: Attachment[]; content?: string; className?: string }) {
+export function AttachmentList({ attachments, content, className }: { attachments?: Attachment[]; content?: string; className?: string }) {
const download = useDownloadAttachment();
const preview = useAttachmentPreview();
if (!attachments?.length) return null;