Embed selected native project structures directly in mailbox messages and drafts. - Persist native structure embeds in message metadata and expose their shared types. - Add composer selection, mailbox rendering, previews, and dashboard navigation for linked structures. - Cover metadata persistence and mailbox embed behavior with core and dashboard tests. Files changed: .changeset/fn-8292-mail-native-structure-embeds.md | 7 ++ docs/dashboard-guide.md | 1 + .../message-metadata-native-structures.test.ts | 25 +++++++ .../__tests__/postgres/message-store.pg.test.ts | 21 ++++++ packages/core/src/index.ts | 2 +- packages/core/src/types.ts | 32 +++++++++ packages/core/src/types/messages.ts | 14 ++++ packages/dashboard/app/components/MailboxModal.css | 29 ++++++++ packages/dashboard/app/components/MailboxModal.tsx | 13 +++- .../components/MailboxNativeStructureEmbeds.tsx | 37 ++++++++++ packages/dashboard/app/components/MailboxView.tsx | 14 +++- .../dashboard/app/components/MessageComposer.tsx | 65 ++++++++++++++++-- .../app/components/NativeStructurePreview.tsx | 6 +- .../__tests__/MailboxModal.cache.test.tsx | 8 +-- .../app/components/__tests__/MailboxModal.test.tsx | 9 +++ .../MailboxNativeStructureEmbeds.test.tsx | 42 ++++++++++++ .../app/components/__tests__/MailboxView.test.tsx | 9 +++ .../components/__tests__/MessageComposer.test.tsx | 25 +++++++ .../app/components/dashboard/MainContent.tsx | 78 +++++++++++++++++++++- .../MainContent.mailbox-view-task.test.tsx | 46 ++++++++++++- 20 files changed, 459 insertions(+), 24 deletions(-) Fusion-Task-Id: FN-8292 Fusion-Task-Lineage: ae6cfb9c-a764-407c-bd45-cfd879ccdd3e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
43 lines
2.2 KiB
TypeScript
43 lines
2.2 KiB
TypeScript
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import type { Message, NativeStructureEmbed } from "@fusion/core";
|
|
import { fetchNativeStructurePreview } from "../../api";
|
|
import { MailboxNativeStructureEmbeds } from "../MailboxNativeStructureEmbeds";
|
|
|
|
vi.mock("../../api", () => ({ fetchNativeStructurePreview: vi.fn() }));
|
|
const fetchPreview = vi.mocked(fetchNativeStructurePreview);
|
|
|
|
function message(nativeStructures?: NativeStructureEmbed[]): Pick<Message, "metadata"> {
|
|
return { metadata: nativeStructures ? { nativeStructures } : undefined };
|
|
}
|
|
|
|
describe("MailboxNativeStructureEmbeds", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("does not create an attachment shell without embeds", () => {
|
|
const { container } = render(<MailboxNativeStructureEmbeds message={message()} onOpen={vi.fn()} />);
|
|
expect(container).toBeEmptyDOMElement();
|
|
});
|
|
|
|
it("renders every persisted embed and forwards preview navigation", async () => {
|
|
const onOpen = vi.fn();
|
|
fetchPreview.mockResolvedValue({ available: true, kind: "mission", kindLabel: "Mission", title: "Launch mail", excerpt: "Review", openTarget: { view: "missions", id: "M-1" } });
|
|
render(<MailboxNativeStructureEmbeds message={message([
|
|
{ kind: "mission", id: "M-1", label: "Launch mail" },
|
|
{ kind: "goal", id: "G-1", label: "Ship" },
|
|
])} onOpen={onOpen} />);
|
|
|
|
await waitFor(() => expect(screen.getAllByTestId("native-structure-preview")).toHaveLength(2));
|
|
fireEvent.click(screen.getAllByRole("button", { name: /Open Mission/ })[0]);
|
|
expect(onOpen).toHaveBeenCalledWith({ kind: "mission", id: "M-1", projectId: undefined }, expect.objectContaining({ available: true }));
|
|
});
|
|
|
|
it("uses the captured label for unavailable targets", async () => {
|
|
fetchPreview.mockResolvedValue({ available: false, kind: "mission", id: "M-1", reason: "soft-deleted" });
|
|
render(<MailboxNativeStructureEmbeds message={message([{ kind: "mission", id: "M-1", label: "Launch mail" }])} onOpen={vi.fn()} />);
|
|
await waitFor(() => expect(screen.getByTestId("native-structure-preview-unavailable")).toHaveTextContent("Launch mail"));
|
|
});
|
|
});
|