Back to Repositories

Testing Chat Message Component Implementation in OpenHands

This test suite evaluates the ChatMessage component’s functionality in the OpenHands project, focusing on message rendering, clipboard operations, and markdown support. The tests verify both user and assistant message types while ensuring proper handling of code syntax highlighting and interactive features.

Test Coverage Overview

The test suite provides comprehensive coverage of the ChatMessage component’s core features:

  • Message rendering for both user and assistant types
  • Clipboard functionality with hover states
  • Code syntax highlighting and markdown support
  • Error handling for clipboard operations
  • Custom component rendering capabilities
  • Styling verification for inline code elements

Implementation Analysis

The testing approach utilizes React Testing Library with Jest and Vitest for component testing. The implementation employs user-event simulation for interactive features and leverages test IDs for reliable element selection. The suite demonstrates modern React testing patterns with async/await operations and proper component isolation.

Technical Details

  • Testing Framework: Jest/Vitest
  • UI Testing Library: @testing-library/react
  • User Interaction: @testing-library/user-event
  • Component Rendering: JSX with TypeScript
  • Assertion Methods: expect, toBeInTheDocument, toBeVisible
  • Test Organization: describe/it blocks

Best Practices Demonstrated

The test suite exemplifies several testing best practices including isolation of test cases, proper async testing patterns, and comprehensive error handling verification. It maintains a clear structure with descriptive test names and appropriate use of skip/todo annotations for incomplete features.

  • Isolated test cases
  • Proper async/await usage
  • Meaningful test descriptions
  • Comprehensive error scenarios
  • Component isolation

all-hands-ai/openhands

frontend/__tests__/components/chat-message.test.tsx

            
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, test } from "vitest";
import { ChatMessage } from "#/components/features/chat/chat-message";

describe("ChatMessage", () => {
  it("should render a user message", () => {
    render(<ChatMessage type="user" message="Hello, World!" />);
    expect(screen.getByTestId("user-message")).toBeInTheDocument();
    expect(screen.getByText("Hello, World!")).toBeInTheDocument();
  });

  it("should render an assistant message", () => {
    render(<ChatMessage type="assistant" message="Hello, World!" />);
    expect(screen.getByTestId("assistant-message")).toBeInTheDocument();
    expect(screen.getByText("Hello, World!")).toBeInTheDocument();
  });

  it.skip("should support code syntax highlighting", () => {
    const code = "```js
console.log('Hello, World!')
```";
    render(<ChatMessage type="user" message={code} />);

    // SyntaxHighlighter breaks the code blocks into "tokens"
    expect(screen.getByText("console")).toBeInTheDocument();
    expect(screen.getByText("log")).toBeInTheDocument();
    expect(screen.getByText("'Hello, World!'")).toBeInTheDocument();
  });

  it.todo("should support markdown content");

  it("should render the copy to clipboard button when the user hovers over the message", async () => {
    const user = userEvent.setup();
    render(<ChatMessage type="user" message="Hello, World!" />);
    const message = screen.getByText("Hello, World!");

    expect(screen.getByTestId("copy-to-clipboard")).not.toBeVisible();

    await user.hover(message);

    expect(screen.getByTestId("copy-to-clipboard")).toBeVisible();
  });

  it("should copy content to clipboard", async () => {
    const user = userEvent.setup();
    render(<ChatMessage type="user" message="Hello, World!" />);
    const copyToClipboardButton = screen.getByTestId("copy-to-clipboard");

    await user.click(copyToClipboardButton);

    expect(navigator.clipboard.readText()).resolves.toBe("Hello, World!");
  });

  // BUG: vi.useFakeTimers() seems to break the tests
  it.todo(
    "should display a checkmark for 200ms and disable the button after copying content to clipboard",
  );

  it("should display an error toast if copying content to clipboard fails", async () => {});

  test.todo("push a toast after successfully copying content to clipboard");

  it("should render a component passed as a prop", () => {
    function Component() {
      return <div data-testid="custom-component">Custom Component</div>;
    }
    render(
      <ChatMessage type="user" message="Hello, World">
        <Component />
      </ChatMessage>,
    );
    expect(screen.getByTestId("custom-component")).toBeInTheDocument();
  });

  it("should apply correct styles to inline code", () => {
    render(<ChatMessage type="assistant" message="Here is some `inline code` text" />);
    const codeElement = screen.getByText("inline code");

    expect(codeElement.tagName.toLowerCase()).toBe("code");
    expect(codeElement.closest("article")).not.toBeNull();
  });
});