= ({ onAudioReady }) => (
);
render(
{}}
audioRecorder={CustomRecorder}
/>
,
);
const recorder = screen.queryByTestId("custom-recorder");
if (recorder) {
expect(recorder.textContent).toContain("Custom Recorder");
}
});
});
describe("multiple custom components", () => {
it("should render multiple custom components together", () => {
const CustomTextArea = React.forwardRef(
(props, ref) => (
),
);
const CustomSendButton: React.FC = (props) => (
);
render(
,
);
expect(screen.getByTestId("multi-textarea")).toBeDefined();
expect(screen.getByTestId("multi-send")).toBeDefined();
});
});
});
// ============================================================================
// 4. RECURSIVE DRILL-DOWN TESTS (Input has no sub-slots, but test nested props)
// ============================================================================
describe("4. Nested Props and Complex Configurations", () => {
describe("complex textArea configuration", () => {
it("should support complex props configuration", () => {
render(
,
);
const textarea = screen.queryByTestId("complex-ta");
if (textarea) {
expect(textarea.getAttribute("placeholder")).toBe(
"Complex placeholder",
);
}
});
});
describe("complex sendButton configuration", () => {
it("should support complex props on sendButton", () => {
render(
,
);
const btn = screen.queryByTestId("complex-send-btn");
if (btn) {
expect(btn.getAttribute("aria-label")).toBe("Send your message");
expect(btn.getAttribute("title")).toBe("Click to send");
}
});
});
});
// ============================================================================
// 5. CLASSNAME OVERRIDE TESTS
// ============================================================================
describe("5. className Override with Tailwind", () => {
describe("className prop in object slots", () => {
it("should allow className prop in textArea object slot", () => {
const { container } = render(
,
);
expect(
container.querySelector(".textarea-class-override"),
).toBeDefined();
});
it("should allow className prop in sendButton object slot", () => {
const { container } = render(
,
);
expect(container.querySelector(".send-class-override")).toBeDefined();
});
});
describe("string slot vs className prop equivalence", () => {
it("string slot should set className on textArea", () => {
const { container } = render(
,
);
expect(container.querySelector(".string-class-textarea")).toBeDefined();
});
it("string slot should set className on sendButton", () => {
const { container } = render(
,
);
expect(container.querySelector(".string-class-send")).toBeDefined();
});
});
describe("tailwind utility classes", () => {
it("should apply tailwind focus utilities to textArea", () => {
const { container } = render(
,
);
const el = container.querySelector(".focus\\:ring-2");
expect(el).toBeDefined();
});
it("should apply tailwind hover utilities to sendButton", () => {
const { container } = render(
,
);
const el = container.querySelector(".hover\\:bg-blue-600");
expect(el).toBeDefined();
});
});
});
// ============================================================================
// 6. CHILDREN RENDER FUNCTION TESTS
// ============================================================================
describe("6. Children Render Function", () => {
it("should support children render function for custom layout", () => {
render(
{({ textArea, sendButton }) => (
)}
,
);
expect(screen.getByTestId("custom-input-layout")).toBeDefined();
});
it("children render function should receive all slot elements", () => {
const receivedKeys: string[] = [];
render(
{(slots) => {
receivedKeys.push(...Object.keys(slots));
return Rendered
;
}}
,
);
expect(screen.getByTestId("slots-check")).toBeDefined();
// Should receive textArea, sendButton, etc.
});
it("children render function allows complete layout control", () => {
render(
{} }]}>
{({ textArea, sendButton, addMenuButton }) => (
{addMenuButton}
{textArea}
{sendButton}
)}
,
);
expect(screen.getByTestId("full-control-layout")).toBeDefined();
expect(document.querySelector(".toolbar")).toBeDefined();
expect(document.querySelector(".main")).toBeDefined();
expect(document.querySelector(".actions")).toBeDefined();
});
});
// ============================================================================
// 7. POSITIONING PROP TESTS
// ============================================================================
describe("7. Positioning Prop", () => {
it("should render static positioning by default", () => {
const { container } = render(
,
);
// By default (static), should NOT have absolute positioning classes
const inputContainer = container.querySelector(
'[data-testid="input-container"]',
) as HTMLElement;
expect(inputContainer).not.toBeNull();
expect(inputContainer.classList.contains("cpk:absolute")).toBe(false);
});
it("should render absolute positioning when positioning='absolute'", () => {
const { container } = render(
,
);
const inputContainer = container.querySelector(
'[data-testid="absolute-input"]',
) as HTMLElement;
expect(inputContainer).not.toBeNull();
expect(inputContainer.classList.contains("cpk:absolute")).toBe(true);
expect(inputContainer.classList.contains("cpk:bottom-0")).toBe(true);
});
it("should apply keyboard height transform", () => {
const { container } = render(
,
);
const inputContainer = container.querySelector(
'[data-testid="keyboard-input"]',
) as HTMLElement;
expect(inputContainer).not.toBeNull();
expect(inputContainer.style.transform).toBe("translateY(-300px)");
});
it("should forward containerRef", () => {
let containerRef: HTMLDivElement | null = null;
const RefCapture = () => {
const ref = React.useRef(null);
React.useEffect(() => {
containerRef = ref.current;
}, []);
return (
);
};
render();
expect(containerRef).not.toBeNull();
});
});
// ============================================================================
// 8. DISCLAIMER SLOT TESTS
// ============================================================================
describe("8. Disclaimer Slot", () => {
it("should render disclaimer when showDisclaimer=true", () => {
render(
,
);
// Look for disclaimer text (from labels)
expect(
screen.queryByText(/AI-generated responses/i) ||
document.querySelector('[class*="text-muted-foreground"]'),
).toBeDefined();
});
it("should hide disclaimer when showDisclaimer=false", () => {
const { container } = render(
,
);
// Disclaimer should not be rendered
const disclaimer = container.querySelector(
'[class*="text-center"][class*="text-xs"]',
);
expect(disclaimer).toBeNull();
});
it("should show disclaimer by default with absolute positioning", () => {
const { container } = render(
,
);
// Disclaimer should be present with absolute positioning (default showDisclaimer=true)
const disclaimer = container.querySelector(
'[class*="text-center"][class*="text-xs"]',
);
expect(disclaimer).not.toBeNull();
});
it("should hide disclaimer by default with static positioning", () => {
const { container } = render(
,
);
// Disclaimer should NOT be present with static positioning (default showDisclaimer=false)
const disclaimer = container.querySelector(
'[class*="text-center"][class*="text-xs"]',
);
expect(disclaimer).toBeNull();
});
it("should apply tailwind class to disclaimer", () => {
const { container } = render(
,
);
const disclaimer = container.querySelector(".text-red-500");
if (disclaimer) {
expect(disclaimer.classList.contains("italic")).toBe(true);
}
});
it("should render custom disclaimer component", () => {
const CustomDisclaimer: React.FC = () => (
Custom Disclaimer Content
);
render(
,
);
const disclaimer = screen.queryByTestId("custom-disclaimer");
if (disclaimer) {
expect(disclaimer.textContent).toContain("Custom Disclaimer");
}
});
});
});