feat(chat): resize vertical du composer avec poignée haut-droit (modèle carnet ticket) — #193
This commit is contained in:
@ -78,7 +78,7 @@ describe("CustomAgentChatView", () => {
|
||||
__resetProjectCliInteractionActivityForTests();
|
||||
});
|
||||
|
||||
it("repaints compact attachment refs from legacy user prompt scrollback", async () => {
|
||||
it("repaints attachment labels from user prompt scrollback", async () => {
|
||||
const agent = {
|
||||
launchAgentChat: vi.fn(),
|
||||
reattachAgentChat: vi.fn(async (sessionId: string) => ({
|
||||
@ -119,68 +119,91 @@ describe("CustomAgentChatView", () => {
|
||||
);
|
||||
|
||||
expect(await screen.findByText("describe this")).toBeTruthy();
|
||||
expect(screen.getByText("[image 1] clipboard.png")).toBeTruthy();
|
||||
expect(screen.getByText("Fichier: clipboard.png")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("prefers canonical attachment refs from user prompt scrollback", async () => {
|
||||
it("resizes the composer vertically with mouse, keyboard, and double-click reset", async () => {
|
||||
const rectSpy = vi
|
||||
.spyOn(HTMLElement.prototype, "getBoundingClientRect")
|
||||
.mockReturnValue({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 600,
|
||||
height: 400,
|
||||
top: 0,
|
||||
right: 600,
|
||||
bottom: 400,
|
||||
left: 0,
|
||||
toJSON: () => {},
|
||||
} as DOMRect);
|
||||
const agent = {
|
||||
launchAgentChat: vi.fn(),
|
||||
reattachAgentChat: vi.fn(async (sessionId: string) => ({
|
||||
sessionId,
|
||||
scrollback: [
|
||||
{
|
||||
kind: "userPrompt" as const,
|
||||
text: "compare these",
|
||||
attachments: ["screenshot.png", "error.log"],
|
||||
attachmentRefs: [
|
||||
{
|
||||
reference: "[image 1]",
|
||||
filename: "screenshot.png",
|
||||
mime: "image/png",
|
||||
sizeBytes: 12,
|
||||
readablePath: "/repo/.ideai/attachments/screenshot.png",
|
||||
},
|
||||
{
|
||||
reference: "[file 1]",
|
||||
filename: "error.log",
|
||||
mime: "text/plain",
|
||||
sizeBytes: 34,
|
||||
readablePath: "/repo/.ideai/attachments/error.log",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
scrollback: [],
|
||||
})),
|
||||
sendAgentChat: vi.fn(async () => {}),
|
||||
cancelAgentChat: vi.fn(async () => {}),
|
||||
closeAgentChat: vi.fn(async () => {}),
|
||||
};
|
||||
|
||||
render(
|
||||
<DIProvider
|
||||
gateways={{
|
||||
agent,
|
||||
system: { pickFile: vi.fn(async () => null) },
|
||||
} as unknown as Gateways}
|
||||
>
|
||||
<CustomAgentChatView
|
||||
projectId="project-1"
|
||||
agentId="agent-1"
|
||||
agentName="Worker"
|
||||
profile={profile}
|
||||
cwd="/repo"
|
||||
nodeId="node-1"
|
||||
sessionId="chat-session-1"
|
||||
conversationId="conversation-1"
|
||||
onSessionId={vi.fn()}
|
||||
onConversationId={vi.fn()}
|
||||
/>
|
||||
</DIProvider>,
|
||||
);
|
||||
function Harness() {
|
||||
const [height, setHeight] = useState(64);
|
||||
return (
|
||||
<DIProvider
|
||||
gateways={{
|
||||
agent,
|
||||
system: { pickFile: vi.fn(async () => null) },
|
||||
} as unknown as Gateways}
|
||||
>
|
||||
<CustomAgentChatView
|
||||
projectId="project-1"
|
||||
agentId="agent-1"
|
||||
agentName="Worker"
|
||||
profile={profile}
|
||||
cwd="/repo"
|
||||
nodeId="node-1"
|
||||
sessionId="chat-session-1"
|
||||
conversationId="conversation-1"
|
||||
composerHeightPx={height}
|
||||
composerDefaultHeightPx={64}
|
||||
onComposerHeightPxChange={setHeight}
|
||||
onSessionId={vi.fn()}
|
||||
onConversationId={vi.fn()}
|
||||
/>
|
||||
</DIProvider>
|
||||
);
|
||||
}
|
||||
|
||||
expect(await screen.findByText("compare these")).toBeTruthy();
|
||||
expect(screen.getByText("[image 1] screenshot.png")).toBeTruthy();
|
||||
expect(screen.getByText("[file 1] error.log")).toBeTruthy();
|
||||
try {
|
||||
render(<Harness />);
|
||||
const composer = await screen.findByLabelText(/message CLI custom/) as HTMLTextAreaElement;
|
||||
const handle = screen.getByRole("button", {
|
||||
name: "redimensionner le composer node-1",
|
||||
});
|
||||
expect(composer.style.height).toBe("64px");
|
||||
expect(handle.style.cursor).toBe("ns-resize");
|
||||
|
||||
fireEvent.mouseDown(handle, { clientY: 200 });
|
||||
fireEvent.mouseMove(window, { clientY: 100 });
|
||||
fireEvent.mouseUp(window);
|
||||
await waitFor(() => expect(composer.style.height).toBe("164px"));
|
||||
|
||||
fireEvent.keyDown(handle, { key: "ArrowUp", shiftKey: true });
|
||||
await waitFor(() => expect(composer.style.height).toBe("176px"));
|
||||
fireEvent.keyDown(handle, { key: "ArrowDown", shiftKey: true });
|
||||
await waitFor(() => expect(composer.style.height).toBe("164px"));
|
||||
|
||||
fireEvent.mouseDown(handle, { clientY: 200 });
|
||||
fireEvent.mouseMove(window, { clientY: -200 });
|
||||
fireEvent.mouseUp(window);
|
||||
await waitFor(() => expect(composer.style.height).toBe("200px"));
|
||||
|
||||
fireEvent.doubleClick(handle);
|
||||
await waitFor(() => expect(composer.style.height).toBe("64px"));
|
||||
} finally {
|
||||
rectSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("detaches on unmount then reattaches with missed scrollback chunks", async () => {
|
||||
@ -895,7 +918,7 @@ describe("CustomAgentChatView", () => {
|
||||
});
|
||||
|
||||
expect(screen.getByText("Worker → QA")).toBeTruthy();
|
||||
expect(screen.getByText("Worker → QA").parentElement?.textContent).toContain("started");
|
||||
expect(screen.getByText("Worker → QA").parentElement?.textContent).toContain("En cours");
|
||||
expect(screen.queryByRole("status", { name: "Loading" })).toBeNull();
|
||||
|
||||
act(() => {
|
||||
@ -909,8 +932,8 @@ describe("CustomAgentChatView", () => {
|
||||
});
|
||||
|
||||
await screen.findByText("done");
|
||||
expect(screen.getAllByText("Worker ← QA")).toHaveLength(2);
|
||||
expect(screen.getAllByText("validate")).toHaveLength(2);
|
||||
expect(screen.getAllByText("Worker ← QA")).toHaveLength(1);
|
||||
expect(screen.getAllByText("validate")).toHaveLength(1);
|
||||
expect(screen.queryByText("Appel inter-agent")).toBeNull();
|
||||
expect(screen.queryByText("reply delivered")).toBeNull();
|
||||
expect(screen.queryByText("Tool")).toBeNull();
|
||||
@ -1072,7 +1095,7 @@ describe("CustomAgentChatView", () => {
|
||||
expect(screen.getAllByTestId("chat-tool-progress")).toHaveLength(1);
|
||||
expect(screen.queryByText("Downloading 10%")).toBeNull();
|
||||
expect(screen.getByText("Downloading 75%")).toBeTruthy();
|
||||
expect(screen.getByText("Tool").parentElement?.textContent).toContain("running");
|
||||
expect(screen.getByText("Tool").parentElement?.textContent).toContain("En cours");
|
||||
});
|
||||
|
||||
it("renders progress immediately when the chunk callback fires", async () => {
|
||||
@ -1149,6 +1172,97 @@ describe("CustomAgentChatView", () => {
|
||||
expect(screen.getAllByTestId("chat-tool-progress")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("upserts progress by progressId and keeps first emission chronological order", async () => {
|
||||
let emitChunk: ((chunk: unknown) => void) | null = null;
|
||||
const agent = {
|
||||
launchAgentChat: vi.fn(),
|
||||
reattachAgentChat: vi.fn(async (sessionId: string) => ({
|
||||
sessionId,
|
||||
scrollback: [],
|
||||
})),
|
||||
sendAgentChat: vi.fn(
|
||||
(_sessionId: string, _prompt: string, onChunk: (chunk: unknown) => void) =>
|
||||
new Promise<void>((resolve) => {
|
||||
emitChunk = (chunk: unknown) => {
|
||||
onChunk(chunk);
|
||||
if ((chunk as { kind?: string }).kind === "final") resolve();
|
||||
};
|
||||
}),
|
||||
),
|
||||
cancelAgentChat: vi.fn(async () => {}),
|
||||
closeAgentChat: vi.fn(async () => {}),
|
||||
};
|
||||
|
||||
render(
|
||||
<DIProvider
|
||||
gateways={{
|
||||
agent,
|
||||
system: { pickFile: vi.fn(async () => null) },
|
||||
} as unknown as Gateways}
|
||||
>
|
||||
<CustomAgentChatView
|
||||
projectId="project-1"
|
||||
agentId="agent-1"
|
||||
agentName="Worker"
|
||||
profile={profile}
|
||||
cwd="/repo"
|
||||
nodeId="node-1"
|
||||
sessionId="chat-session-1"
|
||||
conversationId="conversation-1"
|
||||
onSessionId={vi.fn()}
|
||||
onConversationId={vi.fn()}
|
||||
/>
|
||||
</DIProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(agent.reattachAgentChat).toHaveBeenCalledWith(
|
||||
"chat-session-1",
|
||||
expect.any(Function),
|
||||
),
|
||||
);
|
||||
|
||||
fireEvent.change(screen.getByLabelText(/message CLI custom/), {
|
||||
target: { value: "run tools" },
|
||||
});
|
||||
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||
await waitFor(() => expect(emitChunk).not.toBeNull());
|
||||
|
||||
const progress = (
|
||||
progressId: string,
|
||||
occurredAtMs: number,
|
||||
stage: "started" | "completed",
|
||||
text: string,
|
||||
) => ({
|
||||
kind: "progress" as const,
|
||||
progress: {
|
||||
progressId,
|
||||
occurredAtMs,
|
||||
source: "providerNative" as const,
|
||||
kind: "tool" as const,
|
||||
stage,
|
||||
label: "shell",
|
||||
toolName: "shell",
|
||||
text,
|
||||
},
|
||||
});
|
||||
|
||||
act(() => {
|
||||
emitChunk?.(progress("process-2", 20, "started", "Process 2 start"));
|
||||
emitChunk?.(progress("process-1", 10, "started", "Process 1 start"));
|
||||
emitChunk?.(progress("process-1", 10, "completed", "Process 1 done"));
|
||||
});
|
||||
|
||||
expect(screen.getAllByTestId("chat-tool-progress")).toHaveLength(2);
|
||||
expect(screen.queryByText("Process 1 start")).toBeNull();
|
||||
expect(screen.getByText("Process 1 done")).toBeTruthy();
|
||||
const first = screen.getByText("Process 1 done");
|
||||
const second = screen.getByText("Process 2 start");
|
||||
expect(
|
||||
Boolean(first.compareDocumentPosition(second) & Node.DOCUMENT_POSITION_FOLLOWING),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("moves completed entity progress from active map to turns", async () => {
|
||||
let emitChunk: ((chunk: unknown) => void) | null = null;
|
||||
const agent = {
|
||||
@ -1225,7 +1339,7 @@ describe("CustomAgentChatView", () => {
|
||||
expect(screen.getAllByTestId("chat-tool-progress")).toHaveLength(1);
|
||||
expect(screen.queryByText("Searching...")).toBeNull();
|
||||
expect(screen.getByText("Found results")).toBeTruthy();
|
||||
expect(screen.getByText("Tool").parentElement?.textContent).toContain("done");
|
||||
expect(screen.getByText("Tool").parentElement?.textContent).toContain("Terminé");
|
||||
|
||||
act(() => {
|
||||
emitChunk?.({ kind: "final", content: "done" });
|
||||
@ -1306,7 +1420,7 @@ describe("CustomAgentChatView", () => {
|
||||
});
|
||||
|
||||
expect(screen.queryByRole("status", { name: "Loading" })).toBeNull();
|
||||
expect(screen.getByText("Progress").parentElement?.textContent).toContain("running");
|
||||
expect(screen.getByText("Progress").parentElement?.textContent).toContain("En cours");
|
||||
|
||||
act(() => {
|
||||
emitChunk?.({ kind: "final", content: "done" });
|
||||
@ -1317,93 +1431,7 @@ describe("CustomAgentChatView", () => {
|
||||
expect(screen.queryByRole("status", { name: "Loading" })).toBeNull(),
|
||||
);
|
||||
expect(screen.getByText("Lecture du contexte disponible.")).toBeTruthy();
|
||||
expect(screen.getByText("Progress").parentElement?.textContent).toContain("done");
|
||||
});
|
||||
|
||||
it("updates message progress by progressId instead of appending running and done rows", async () => {
|
||||
let emitChunk: ((chunk: unknown) => void) | null = null;
|
||||
const agent = {
|
||||
launchAgentChat: vi.fn(),
|
||||
reattachAgentChat: vi.fn(async (sessionId: string) => ({
|
||||
sessionId,
|
||||
scrollback: [],
|
||||
})),
|
||||
sendAgentChat: vi.fn(
|
||||
(_sessionId: string, _prompt: string, onChunk: (chunk: unknown) => void) =>
|
||||
new Promise<void>((resolve) => {
|
||||
emitChunk = (chunk: unknown) => {
|
||||
onChunk(chunk);
|
||||
if ((chunk as { kind?: string }).kind === "final") resolve();
|
||||
};
|
||||
}),
|
||||
),
|
||||
cancelAgentChat: vi.fn(async () => {}),
|
||||
closeAgentChat: vi.fn(async () => {}),
|
||||
};
|
||||
|
||||
render(
|
||||
<DIProvider
|
||||
gateways={{
|
||||
agent,
|
||||
system: { pickFile: vi.fn(async () => null) },
|
||||
} as unknown as Gateways}
|
||||
>
|
||||
<CustomAgentChatView
|
||||
projectId="project-1"
|
||||
agentId="agent-1"
|
||||
agentName="Worker"
|
||||
profile={profile}
|
||||
cwd="/repo"
|
||||
nodeId="node-1"
|
||||
sessionId="chat-session-1"
|
||||
conversationId="conversation-1"
|
||||
onSessionId={vi.fn()}
|
||||
onConversationId={vi.fn()}
|
||||
/>
|
||||
</DIProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(agent.reattachAgentChat).toHaveBeenCalledWith(
|
||||
"chat-session-1",
|
||||
expect.any(Function),
|
||||
),
|
||||
);
|
||||
|
||||
fireEvent.change(screen.getByLabelText(/message CLI custom/), {
|
||||
target: { value: "ship progress ids" },
|
||||
});
|
||||
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||
await waitFor(() => expect(emitChunk).not.toBeNull());
|
||||
|
||||
const progress = (stage: "delta" | "completed", text: string) => ({
|
||||
kind: "progress" as const,
|
||||
progress: {
|
||||
progressId: "codex:message:item-1",
|
||||
source: "providerNative" as const,
|
||||
kind: "message" as const,
|
||||
stage,
|
||||
label: "Analyse",
|
||||
text,
|
||||
},
|
||||
});
|
||||
|
||||
act(() => {
|
||||
emitChunk?.(progress("delta", "Lecture du contexte disponible."));
|
||||
});
|
||||
|
||||
expect(screen.getAllByTestId("chat-progress-status")).toHaveLength(1);
|
||||
expect(screen.getByText("Lecture du contexte disponible.")).toBeTruthy();
|
||||
expect(screen.getByText("Progress").parentElement?.textContent).toContain("running");
|
||||
|
||||
act(() => {
|
||||
emitChunk?.(progress("completed", "Contexte lu."));
|
||||
});
|
||||
|
||||
expect(screen.getAllByTestId("chat-progress-status")).toHaveLength(1);
|
||||
expect(screen.queryByText("Lecture du contexte disponible.")).toBeNull();
|
||||
expect(screen.getByText("Contexte lu.")).toBeTruthy();
|
||||
expect(screen.getByText("Progress").parentElement?.textContent).toContain("done");
|
||||
expect(screen.getByText("Progress").parentElement?.textContent).toContain("Terminé");
|
||||
});
|
||||
|
||||
it("does not append late progress chunks after the final answer", async () => {
|
||||
@ -2123,7 +2151,8 @@ describe("CustomAgentChatView", () => {
|
||||
Object.defineProperty(file, "arrayBuffer", {
|
||||
value: vi.fn(async () => new Uint8Array([1, 2, 3]).buffer),
|
||||
});
|
||||
fireEvent.paste(screen.getByLabelText(/message CLI custom/), {
|
||||
const composer = screen.getByLabelText(/message CLI custom/) as HTMLTextAreaElement;
|
||||
fireEvent.paste(composer, {
|
||||
clipboardData: {
|
||||
items: [
|
||||
{
|
||||
@ -2138,12 +2167,14 @@ describe("CustomAgentChatView", () => {
|
||||
|
||||
expect(await screen.findByText("Fichier joint: clipboard.png")).toBeTruthy();
|
||||
expect(screen.getByTestId("attachment-preview-clipboard.png")).toBeTruthy();
|
||||
await waitFor(() => expect(composer.value).toBe("[Image 1]"));
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "retirer clipboard.png" }));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.queryByText("Fichier joint: clipboard.png")).toBeNull(),
|
||||
);
|
||||
expect(composer.value).toBe("");
|
||||
expect(
|
||||
(screen.getByRole("button", { name: "Envoyer" }) as HTMLButtonElement)
|
||||
.disabled,
|
||||
@ -2223,7 +2254,7 @@ describe("CustomAgentChatView", () => {
|
||||
await waitFor(() => expect(agent.sendAgentChat).toHaveBeenCalledTimes(1));
|
||||
expect(agent.sendAgentChat).toHaveBeenCalledWith(
|
||||
"chat-session-1",
|
||||
"",
|
||||
"[Image 1]",
|
||||
expect.any(Function),
|
||||
{
|
||||
attachments: [
|
||||
@ -2389,7 +2420,7 @@ describe("CustomAgentChatView", () => {
|
||||
await waitFor(() => expect(agent.sendAgentChat).toHaveBeenCalledTimes(1));
|
||||
expect(agent.sendAgentChat).toHaveBeenCalledWith(
|
||||
"chat-session-1",
|
||||
"",
|
||||
"[Image 1]",
|
||||
expect.any(Function),
|
||||
{
|
||||
attachments: [
|
||||
@ -2402,8 +2433,8 @@ describe("CustomAgentChatView", () => {
|
||||
],
|
||||
},
|
||||
);
|
||||
expect(screen.getByText("Pièce jointe")).toBeTruthy();
|
||||
expect(screen.getByText("[image 1] paste-image.png")).toBeTruthy();
|
||||
expect(screen.getByText("[Image 1]")).toBeTruthy();
|
||||
expect(screen.getByText("Fichier: paste-image.png")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("accepts pasted generic files exposed only through clipboardData.files", async () => {
|
||||
@ -2564,7 +2595,91 @@ describe("CustomAgentChatView", () => {
|
||||
},
|
||||
);
|
||||
expect(screen.getByText("prefix inserted")).toBeTruthy();
|
||||
expect(screen.getByText("[file 1] mixed.txt")).toBeTruthy();
|
||||
expect(screen.getByText("Fichier: mixed.txt")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("inserts pasted image tokens at the cursor and sends the tokenized projection", async () => {
|
||||
const agent = {
|
||||
launchAgentChat: vi.fn(),
|
||||
reattachAgentChat: vi.fn(async (sessionId: string) => ({
|
||||
sessionId,
|
||||
scrollback: [],
|
||||
})),
|
||||
sendAgentChat: vi.fn(async () => {}),
|
||||
cancelAgentChat: vi.fn(async () => {}),
|
||||
closeAgentChat: vi.fn(async () => {}),
|
||||
};
|
||||
|
||||
render(
|
||||
<DIProvider
|
||||
gateways={{
|
||||
agent,
|
||||
system: { pickFile: vi.fn(async () => null) },
|
||||
} as unknown as Gateways}
|
||||
>
|
||||
<CustomAgentChatView
|
||||
projectId="project-1"
|
||||
agentId="agent-1"
|
||||
agentName="Worker"
|
||||
profile={profile}
|
||||
cwd="/repo"
|
||||
nodeId="node-1"
|
||||
sessionId="chat-session-1"
|
||||
conversationId="conversation-1"
|
||||
onSessionId={vi.fn()}
|
||||
onConversationId={vi.fn()}
|
||||
/>
|
||||
</DIProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(agent.reattachAgentChat).toHaveBeenCalledWith(
|
||||
"chat-session-1",
|
||||
expect.any(Function),
|
||||
),
|
||||
);
|
||||
|
||||
const composer = screen.getByLabelText(/message CLI custom/) as HTMLTextAreaElement;
|
||||
fireEvent.change(composer, { target: { value: "voir maintenant" } });
|
||||
composer.setSelectionRange(5, 5);
|
||||
const file = new File(["ignored"], "cursor.png", { type: "image/png" });
|
||||
Object.defineProperty(file, "arrayBuffer", {
|
||||
value: vi.fn(async () => new Uint8Array([1, 2, 3]).buffer),
|
||||
});
|
||||
|
||||
fireEvent.paste(composer, {
|
||||
clipboardData: {
|
||||
items: [
|
||||
{
|
||||
kind: "file",
|
||||
type: "image/png",
|
||||
getAsFile: () => file,
|
||||
},
|
||||
],
|
||||
getData: () => "",
|
||||
},
|
||||
});
|
||||
|
||||
await screen.findByText("Fichier joint: cursor.png");
|
||||
await waitFor(() => expect(composer.value).toBe("voir [Image 1] maintenant"));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Envoyer" }));
|
||||
|
||||
await waitFor(() => expect(agent.sendAgentChat).toHaveBeenCalledTimes(1));
|
||||
expect(agent.sendAgentChat).toHaveBeenCalledWith(
|
||||
"chat-session-1",
|
||||
"voir [Image 1] maintenant",
|
||||
expect.any(Function),
|
||||
{
|
||||
attachments: [
|
||||
{
|
||||
filename: "cursor.png",
|
||||
contentBase64: "AQID",
|
||||
mime: "image/png",
|
||||
sourceKind: "clipboard",
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("deduplicates clipboard files by metadata across clipboard sources and repeated paste", async () => {
|
||||
@ -2826,13 +2941,13 @@ describe("CustomAgentChatView", () => {
|
||||
});
|
||||
await screen.findByText("Fichier joint: not-persisted.png");
|
||||
await waitFor(() =>
|
||||
expect(agent.saveChatDraft).toHaveBeenCalledWith(scope, "draft with file"),
|
||||
expect(agent.saveChatDraft).toHaveBeenCalledWith(scope, "draft with file[Image 1]"),
|
||||
);
|
||||
|
||||
first.unmount();
|
||||
renderView();
|
||||
|
||||
await screen.findByDisplayValue("draft with file");
|
||||
await screen.findByDisplayValue("draft with file[Image 1]");
|
||||
expect(screen.queryByText("Fichier joint: not-persisted.png")).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user