import React from "react";
import { render, screen, act } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { ThreadsProvider, useThreads } from "../threads-context";
function ThreadIdViewer() {
const { threadId, isThreadIdExplicit } = useThreads();
return (
<>
{threadId}
{String(isThreadIdExplicit)}
>
);
}
// Exposes setThreadId to the test so it can trigger the auto→explicit flip.
function ThreadIdController({ nextId }: { nextId: string }) {
const { setThreadId } = useThreads();
return (
);
}
describe("ThreadsProvider", () => {
it("updates threadId when explicit prop becomes available", () => {
const { rerender } = render(
,
);
expect(screen.getByTestId("threadId").textContent).toBe("mock-thread-id");
rerender(
,
);
expect(screen.getByTestId("threadId").textContent).toBe("customer-thread");
});
describe("isThreadIdExplicit", () => {
it("is false on first mount when no threadId prop is supplied", () => {
// Auto-minted UUID — the backend has never seen it, so downstream
// consumers (e.g. /connect) must NOT treat this as a real thread.
render(
,
);
expect(screen.getByTestId("threadId").textContent).toBe("mock-thread-id");
expect(screen.getByTestId("isExplicit").textContent).toBe("false");
});
it("is true when threadId prop is supplied on mount", () => {
render(
,
);
expect(screen.getByTestId("threadId").textContent).toBe(
"customer-thread",
);
expect(screen.getByTestId("isExplicit").textContent).toBe("true");
});
it("flips from false to true after setThreadId() is called", () => {
render(
,
);
expect(screen.getByTestId("isExplicit").textContent).toBe("false");
act(() => {
screen.getByTestId("setThread").click();
});
expect(screen.getByTestId("threadId").textContent).toBe(
"user-picked-thread",
);
expect(screen.getByTestId("isExplicit").textContent).toBe("true");
});
it("reverts to false when an explicit prop is removed and setThreadId was never called", () => {
// Current contract: explicitness via the `threadId` prop is prop-derived,
// so removing the prop returns the provider to its auto-minted state.
// Pinning this guards against an accidental "sticky explicit" regression.
const { rerender } = render(
,
);
expect(screen.getByTestId("isExplicit").textContent).toBe("true");
rerender(
,
);
expect(screen.getByTestId("threadId").textContent).toBe("mock-thread-id");
expect(screen.getByTestId("isExplicit").textContent).toBe("false");
});
it("stays true after prop is removed if setThreadId was called while prop was present", () => {
// Once the caller has touched setThreadId, explicitness is sticky —
// the internal "user picked a thread" flag outlives any prop churn.
const { rerender } = render(
,
);
act(() => {
screen.getByTestId("setThread").click();
});
rerender(
,
);
expect(screen.getByTestId("threadId").textContent).toBe(
"user-picked-thread",
);
expect(screen.getByTestId("isExplicit").textContent).toBe("true");
});
});
});