import React from "react"; import { render } from "@testing-library/react"; import { describe, it, expect, vi } from "vitest"; // ─── Mock react-native ─────────────────────────────────────────────────────── // jsdom doesn't have react-native, so we provide lightweight stand-ins. vi.mock("react-native", () => { const React = require("react"); const View = React.forwardRef( ( { children, style, testID, accessibilityLabel, accessibilityRole, ...rest }: any, ref: any, ) => React.createElement( "div", { ref, style, "data-testid": testID, "aria-label": accessibilityLabel, role: accessibilityRole, ...rest, }, children, ), ); View.displayName = "View"; const Text = React.forwardRef(({ children, style, ...rest }: any, ref: any) => React.createElement("span", { ref, style, ...rest }, children), ); Text.displayName = "Text"; const AnimatedValue = class { _value: number; constructor(value: number) { this._value = value; } interpolate({ outputRange }: any) { return outputRange[0]; } }; const AnimatedView = React.forwardRef( ({ children, style, ...rest }: any, ref: any) => React.createElement("div", { ref, style, ...rest }, children), ); AnimatedView.displayName = "Animated.View"; const timing = (_value: any, _config: any) => ({ start: vi.fn(), stop: vi.fn(), }); const sequence = (animations: any[]) => ({ start: vi.fn(), stop: vi.fn(), }); const loop = (animation: any) => ({ start: vi.fn(), stop: vi.fn(), }); const parallel = (animations: any[]) => ({ start: vi.fn(), stop: vi.fn(), }); const delay = (_ms: number) => ({ start: vi.fn(), stop: vi.fn(), }); const Animated = { Value: AnimatedValue, View: AnimatedView, timing, sequence, loop, parallel, delay, }; const StyleSheet = { create: (styles: any) => styles, flatten: (style: any) => Array.isArray(style) ? Object.assign({}, ...style) : style || {}, }; return { View, Text, Animated, StyleSheet, }; }); // ─── Mock the Markdown component (B1 owns it, may not exist yet) ───────────── vi.mock("../../Markdown", () => ({ CopilotMarkdown: ({ content }: { content: string }) => { const React = require("react"); return React.createElement( "div", { "data-testid": "copilot-markdown" }, content, ); }, })); // ─── Imports under test (after mocks) ──────────────────────────────────────── import { AssistantMessage } from "../AssistantMessage"; import { UserMessage } from "../UserMessage"; import { TypingIndicator } from "../TypingIndicator"; // ─── Tests ─────────────────────────────────────────────────────────────────── describe("AssistantMessage", () => { it("renders content via CopilotMarkdown", () => { const { getByTestId } = render( , ); const markdown = getByTestId("copilot-markdown"); expect(markdown).toBeTruthy(); expect(markdown.textContent).toBe("Hello from the assistant"); }); it("shows TypingIndicator when isLoading is true", () => { const { getByLabelText, queryByTestId } = render( , ); // Typing indicator should be present expect(getByLabelText("Typing indicator")).toBeTruthy(); // Markdown should NOT render during loading expect(queryByTestId("copilot-markdown")).toBeNull(); }); it("displays a timestamp when provided", () => { const date = new Date(2025, 0, 15, 14, 30); // Jan 15 2025, 2:30 PM const { container } = render( , ); expect(container.textContent).toContain("2:30 PM"); }); it("does not display timestamp when not provided", () => { const { container } = render(); // Should only contain the message text (via markdown mock) expect(container.textContent).toBe("No timestamp"); }); }); describe("UserMessage", () => { it("renders plain text content", () => { const { container } = render(); expect(container.textContent).toContain("Hello from the user"); }); it("displays a timestamp when provided", () => { const date = new Date(2025, 5, 20, 9, 5); // Jun 20 2025, 9:05 AM const { container } = render( , ); expect(container.textContent).toContain("9:05 AM"); }); it("does not display timestamp when not provided", () => { const { container } = render(); expect(container.textContent).toBe("Just text"); }); }); describe("TypingIndicator", () => { it("renders three animated dots", () => { const { getByLabelText } = render(); const indicator = getByLabelText("Typing indicator"); expect(indicator).toBeTruthy(); // Three Animated.View dots inside the container expect(indicator.children.length).toBe(3); }); });