File size: 3,358 Bytes
f31a721
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { renderHook } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { useReasoningContent } from "./useReasoningContent";

const expectEmptyState = (result: {
  current: ReturnType<typeof useReasoningContent>;
}) => {
  expect(result.current.reasoningContent).toBe("");
  expect(result.current.mainContent).toBe("");
  expect(result.current.isGenerating).toBe(false);
};

describe("useReasoningContent hook", () => {
  describe("parsing reasoning content from markdown markers", () => {
    it("should extract reasoning content between start and end markers", () => {
      const { result } = renderHook(() =>
        useReasoningContent(
          "<think>Let me think about this</think>\nHere is the answer.",
        ),
      );

      expect(result.current.reasoningContent).toBe("Let me think about this");
      expect(result.current.mainContent).toBe("\nHere is the answer.");
      expect(result.current.isGenerating).toBe(false);
    });

    it("should handle empty text", () => {
      const { result } = renderHook(() => useReasoningContent(""));
      expectEmptyState(result);
    });

    it("should return text as main content when no markers present", () => {
      const { result } = renderHook(() =>
        useReasoningContent("This is a normal response."),
      );

      expect(result.current.reasoningContent).toBe("");
      expect(result.current.mainContent).toBe("This is a normal response.");
      expect(result.current.isGenerating).toBe(false);
    });

    it("should detect generating state when end marker is missing", () => {
      const { result } = renderHook(() =>
        useReasoningContent("<think>I'm still thinking"),
      );

      expect(result.current.reasoningContent).toBe("I'm still thinking");
      expect(result.current.mainContent).toBe("");
      expect(result.current.isGenerating).toBe(true);
    });

    it("should handle whitespace-only content", () => {
      const { result } = renderHook(() => useReasoningContent("   "));
      expectEmptyState(result);
    });

    it("should handle null/undefined content gracefully", () => {
      const { result } = renderHook(() =>
        useReasoningContent(null as unknown as string),
      );
      expectEmptyState(result);
    });
  });

  describe("UI state management for reasoning section", () => {
    it("should provide correct isGenerating state for accordion title", () => {
      const streamingState = renderHook(() =>
        useReasoningContent("<think>Currently thinking..."),
      );

      expect(streamingState.result.current.isGenerating).toBe(true);

      const completedState = renderHook(() =>
        useReasoningContent(
          "<think>Thought process completed</think>\nHere is the answer.",
        ),
      );

      expect(completedState.result.current.isGenerating).toBe(false);
    });

    it("should handle transition from streaming to completed state", () => {
      const initial = renderHook(() =>
        useReasoningContent("<think>Building response..."),
      );
      expect(initial.result.current.isGenerating).toBe(true);

      const transitioned = renderHook(() =>
        useReasoningContent(
          "<think>Response built.</think>\nFinal answer here.",
        ),
      );
      expect(transitioned.result.current.isGenerating).toBe(false);
    });
  });
});