这里说的什么");
expect(container).not.toHaveTextContent("@
");
});
it("hydrates workspace app mention icons without putting icon data in the href", async () => {
const iconUrl = "data:image/png;base64,weather";
const { container } = render(
);
await waitFor(() =>
expect(
container.querySelector('[data-agent-mention-kind="workspace-app"]')
).not.toBeNull()
);
const mention = container.querySelector(
'[data-agent-mention-kind="workspace-app"]'
);
expect(mention).toHaveTextContent("Weather");
expect(mention).toHaveAttribute(
"data-agent-mention-href",
"mention://workspace-app/weather?workspaceId=workspace-1"
);
expect(mention).toHaveAttribute("data-agent-mention-icon-url", iconUrl);
expect(mention?.querySelector("img")).toHaveAttribute("src", iconUrl);
});
it("hydrates session mention icons from Agent Target presentation metadata", async () => {
const iconUrl = "data:image/png;base64,gemini";
const href =
"mention://agent-session/session-1?agentTargetId=extension%3Agemini&workspaceId=workspace-1";
const { container } = render(
);
await waitFor(() =>
expect(
container.querySelector('[data-agent-mention-kind="session"] img')
).toHaveAttribute("src", iconUrl)
);
const mention = container.querySelector(
'[data-agent-mention-kind="session"]'
);
expect(mention).toHaveTextContent("Gemini session");
expect(mention).toHaveAttribute("data-agent-mention-href", href);
});
it("does not override mention wrapper width in readonly messages", async () => {
const { container } = render(
);
await waitFor(() =>
expect(
container.querySelector('[data-agent-mention-kind="workspace-app"]')
).not.toBeNull()
);
const editor = container.querySelector(".ProseMirror");
expect(editor).not.toHaveClass(
"[&_[data-agent-file-mention=true]]:max-w-full"
);
});
it("marks readonly messages that only contain one mention", async () => {
const { container, rerender } = render(
);
await waitFor(() =>
expect(
container.querySelector('[data-agent-mention-kind="workspace-app"]')
).not.toBeNull()
);
expect(container.firstElementChild).toHaveAttribute(
"data-agent-mention-only",
"true"
);
rerender(
);
expect(container.firstElementChild).not.toHaveAttribute(
"data-agent-mention-only"
);
});
it("hydrates app workspace-reference mention icons from workspace app icons", async () => {
const iconUrl = "data:image/png;base64,canvas";
const { container } = render(
);
await waitFor(() =>
expect(
container.querySelector(
'[data-agent-mention-kind="workspace-reference"]'
)
).not.toBeNull()
);
const mention = container.querySelector(
'[data-agent-mention-kind="workspace-reference"]'
);
expect(mention).toHaveTextContent("AI Canvas");
expect(mention).toHaveAttribute(
"data-agent-mention-href",
"mention://workspace-reference/ai-canvas?source=app&workspaceId=workspace-1"
);
expect(mention).toHaveAttribute("data-agent-mention-icon-url", iconUrl);
expect(mention?.querySelector("img")).toHaveAttribute("src", iconUrl);
});
it("renders workspace app factory markdown as a mention token", async () => {
const { container } = render(
);
await waitFor(() =>
expect(
container.querySelector(
'[data-agent-mention-kind="workspace-app-factory"]'
)
).not.toBeNull()
);
const mention = container.querySelector(
'[data-agent-mention-kind="workspace-app-factory"]'
);
expect(mention).toHaveTextContent("Create App");
expect(mention).toHaveAttribute(
"data-agent-mention-href",
"mention://workspace-app-factory/create"
);
expect(container).not.toHaveTextContent(
"mention://workspace-app-factory/create"
);
});
it("renders known skill triggers as skill tokens", async () => {
const { container } = render(
);
await waitFor(() =>
expect(
container.querySelector('[data-agent-skill-token="true"]')
).not.toBeNull()
);
const skillToken = container.querySelector(
'[data-agent-skill-token="true"]'
);
expect(skillToken).toHaveTextContent("/caveman");
expect(skillToken).toHaveAttribute("data-agent-skill-trigger", "/caveman");
expect(container).toHaveTextContent("/compact");
});
it("renders readonly agent tokens without invalid React DOM properties", () => {
const consoleError = vi
.spyOn(console, "error")
.mockImplementation(() => {});
const extensions = createAgentRichTextReadonlyExtensions();
for (const token of [
{
attrs: {
capability: "browser",
label: "Browser",
name: "browser",
trigger: "/browser"
},
dataAttribute: "data-agent-capability-token",
extension: createAgentCapabilityTokenExtension(),
type: "agentCapabilityToken"
},
{
attrs: {
label: "Caveman",
name: "caveman",
trigger: "/caveman"
},
dataAttribute: "data-agent-skill-token",
extension: createAgentSkillTokenExtension(),
type: "agentSkillToken"
}
]) {
const renderHTML = token.extension.config.renderHTML as
| ((props: { HTMLAttributes: Record }) => unknown)
| undefined;
const outputSpec = renderHTML?.({ HTMLAttributes: token.attrs });
const outputAttributes = Array.isArray(outputSpec)
? (outputSpec[1] as Record)
: {};
expect(outputAttributes.contentEditable).toBe("false");
expect(outputAttributes.contenteditable).toBeUndefined();
const content = renderToReactElement({
content: {
content: [
{
content: [{ attrs: token.attrs, type: token.type }],
type: "paragraph"
}
],
type: "doc"
},
extensions
});
const rendered = render(content);
expect(
rendered.container.querySelector(`[${token.dataAttribute}="true"]`)
).toHaveAttribute("contenteditable", "false");
rendered.unmount();
}
expect(consoleError.mock.calls.flat().join(" ")).not.toContain(
"Invalid DOM property `contenteditable`"
);
});
});