@tambo-ai/react 0.43.1 → 0.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/__tests__/use-suggestions.test.js +55 -27
- package/dist/hooks/__tests__/use-suggestions.test.js.map +1 -1
- package/dist/hooks/__tests__/use-tambo-stream-status.test.js +82 -39
- package/dist/hooks/__tests__/use-tambo-stream-status.test.js.map +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -3
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/use-component-state.d.ts.map +1 -1
- package/dist/hooks/use-component-state.js +2 -2
- package/dist/hooks/use-component-state.js.map +1 -1
- package/dist/hooks/use-current-message.d.ts +12 -27
- package/dist/hooks/use-current-message.d.ts.map +1 -1
- package/dist/hooks/use-current-message.js +16 -36
- package/dist/hooks/use-current-message.js.map +1 -1
- package/dist/hooks/use-tambo-stream-status.js +1 -1
- package/dist/hooks/use-tambo-stream-status.js.map +1 -1
- package/dist/hooks/use-thread-input.js +3 -2
- package/dist/hooks/use-thread-input.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/providers/__tests__/tambo-prop-stream-provider.test.js +79 -9
- package/dist/providers/__tests__/tambo-prop-stream-provider.test.js.map +1 -1
- package/dist/providers/__tests__/tambo-thread-provider.test.js +51 -7
- package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +2 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/tambo-provider.d.ts +2 -2
- package/dist/providers/tambo-provider.d.ts.map +1 -1
- package/dist/providers/tambo-provider.js.map +1 -1
- package/dist/providers/tambo-stubs.d.ts.map +1 -1
- package/dist/providers/tambo-stubs.js +6 -6
- package/dist/providers/tambo-stubs.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts +29 -11
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +128 -71
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/dist/util/generate-component.d.ts.map +1 -1
- package/dist/util/generate-component.js +6 -2
- package/dist/util/generate-component.js.map +1 -1
- package/esm/hooks/__tests__/use-suggestions.test.js +55 -27
- package/esm/hooks/__tests__/use-suggestions.test.js.map +1 -1
- package/esm/hooks/__tests__/use-tambo-stream-status.test.js +82 -39
- package/esm/hooks/__tests__/use-tambo-stream-status.test.js.map +1 -1
- package/esm/hooks/index.d.ts +2 -2
- package/esm/hooks/index.d.ts.map +1 -1
- package/esm/hooks/index.js +2 -2
- package/esm/hooks/index.js.map +1 -1
- package/esm/hooks/use-component-state.d.ts.map +1 -1
- package/esm/hooks/use-component-state.js +3 -3
- package/esm/hooks/use-component-state.js.map +1 -1
- package/esm/hooks/use-current-message.d.ts +12 -27
- package/esm/hooks/use-current-message.d.ts.map +1 -1
- package/esm/hooks/use-current-message.js +15 -34
- package/esm/hooks/use-current-message.js.map +1 -1
- package/esm/hooks/use-tambo-stream-status.js +2 -2
- package/esm/hooks/use-tambo-stream-status.js.map +1 -1
- package/esm/hooks/use-thread-input.js +4 -3
- package/esm/hooks/use-thread-input.js.map +1 -1
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +2 -2
- package/esm/index.js.map +1 -1
- package/esm/providers/__tests__/tambo-prop-stream-provider.test.js +80 -10
- package/esm/providers/__tests__/tambo-prop-stream-provider.test.js.map +1 -1
- package/esm/providers/__tests__/tambo-thread-provider.test.js +51 -7
- package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/esm/providers/index.d.ts +1 -1
- package/esm/providers/index.d.ts.map +1 -1
- package/esm/providers/index.js +1 -1
- package/esm/providers/index.js.map +1 -1
- package/esm/providers/tambo-provider.d.ts +2 -2
- package/esm/providers/tambo-provider.d.ts.map +1 -1
- package/esm/providers/tambo-provider.js.map +1 -1
- package/esm/providers/tambo-stubs.d.ts.map +1 -1
- package/esm/providers/tambo-stubs.js +7 -7
- package/esm/providers/tambo-stubs.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts +29 -11
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +125 -70
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/esm/util/generate-component.d.ts.map +1 -1
- package/esm/util/generate-component.js +6 -2
- package/esm/util/generate-component.js.map +1 -1
- package/package.json +11 -11
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { QueryClient } from "@tanstack/react-query";
|
|
2
2
|
import { act, renderHook } from "@testing-library/react";
|
|
3
|
+
import { GenerationStage, } from "../../model/generate-component-response";
|
|
3
4
|
import { useTamboClient, useTamboQueryClient, } from "../../providers/tambo-client-provider";
|
|
4
5
|
import { useTambo } from "../../providers/tambo-provider";
|
|
5
6
|
import { useTamboThread, } from "../../providers/tambo-thread-provider";
|
|
@@ -36,6 +37,42 @@ describe("useTamboSuggestions", () => {
|
|
|
36
37
|
detailedSuggestion: "Test suggestion 2",
|
|
37
38
|
},
|
|
38
39
|
];
|
|
40
|
+
// Helper function to create mock TamboThreadMessage
|
|
41
|
+
const createMockMessage = (overrides = {}) => ({
|
|
42
|
+
id: "test-message-id",
|
|
43
|
+
componentState: {},
|
|
44
|
+
content: [{ type: "text", text: "Test message" }],
|
|
45
|
+
createdAt: new Date().toISOString(),
|
|
46
|
+
role: "assistant",
|
|
47
|
+
threadId: "test-thread-id",
|
|
48
|
+
...overrides,
|
|
49
|
+
});
|
|
50
|
+
// Helper function to create mock TamboThread
|
|
51
|
+
const createMockThread = (overrides = {}) => ({
|
|
52
|
+
id: "test-thread-id",
|
|
53
|
+
projectId: "test-project-id",
|
|
54
|
+
createdAt: new Date().toISOString(),
|
|
55
|
+
updatedAt: new Date().toISOString(),
|
|
56
|
+
messages: [],
|
|
57
|
+
...overrides,
|
|
58
|
+
});
|
|
59
|
+
// Helper function to create mock CombinedTamboThreadContextProps
|
|
60
|
+
const createMockThreadContext = (overrides = {}) => ({
|
|
61
|
+
thread: createMockThread(),
|
|
62
|
+
switchCurrentThread: jest.fn(),
|
|
63
|
+
startNewThread: jest.fn(),
|
|
64
|
+
updateThreadName: jest.fn(),
|
|
65
|
+
generateThreadName: jest.fn(),
|
|
66
|
+
addThreadMessage: jest.fn(),
|
|
67
|
+
updateThreadMessage: jest.fn(),
|
|
68
|
+
cancel: jest.fn(),
|
|
69
|
+
streaming: false,
|
|
70
|
+
sendThreadMessage: jest.fn(),
|
|
71
|
+
generationStage: GenerationStage.IDLE,
|
|
72
|
+
generationStatusMessage: "",
|
|
73
|
+
isIdle: true,
|
|
74
|
+
...overrides,
|
|
75
|
+
});
|
|
39
76
|
beforeEach(() => {
|
|
40
77
|
jest.mocked(useTamboQueryClient).mockReturnValue(new QueryClient());
|
|
41
78
|
jest.mocked(useTamboMutation).mockImplementation(({ mutationFn }) => ({
|
|
@@ -51,18 +88,15 @@ describe("useTamboSuggestions", () => {
|
|
|
51
88
|
jest.mocked(useTambo).mockReturnValue({
|
|
52
89
|
sendThreadMessage: jest.fn(),
|
|
53
90
|
});
|
|
54
|
-
jest.mocked(useTamboThread).mockReturnValue({
|
|
55
|
-
thread: {
|
|
56
|
-
id: "test-thread-id",
|
|
91
|
+
jest.mocked(useTamboThread).mockReturnValue(createMockThreadContext({
|
|
92
|
+
thread: createMockThread({
|
|
57
93
|
messages: [
|
|
58
|
-
{
|
|
59
|
-
id: "test-message-id",
|
|
94
|
+
createMockMessage({
|
|
60
95
|
role: "hydra",
|
|
61
|
-
|
|
62
|
-
},
|
|
96
|
+
}),
|
|
63
97
|
],
|
|
64
|
-
},
|
|
65
|
-
});
|
|
98
|
+
}),
|
|
99
|
+
}));
|
|
66
100
|
jest.mocked(useTamboThreadInput).mockReturnValue({
|
|
67
101
|
setValue: jest.fn(),
|
|
68
102
|
value: "",
|
|
@@ -93,18 +127,15 @@ describe("useTamboSuggestions", () => {
|
|
|
93
127
|
},
|
|
94
128
|
},
|
|
95
129
|
});
|
|
96
|
-
jest.mocked(useTamboThread).mockReturnValue({
|
|
97
|
-
thread: {
|
|
98
|
-
id: "test-thread-id",
|
|
130
|
+
jest.mocked(useTamboThread).mockReturnValue(createMockThreadContext({
|
|
131
|
+
thread: createMockThread({
|
|
99
132
|
messages: [
|
|
100
|
-
{
|
|
101
|
-
id: "test-message-id",
|
|
133
|
+
createMockMessage({
|
|
102
134
|
role: "assistant",
|
|
103
|
-
|
|
104
|
-
},
|
|
135
|
+
}),
|
|
105
136
|
],
|
|
106
|
-
},
|
|
107
|
-
});
|
|
137
|
+
}),
|
|
138
|
+
}));
|
|
108
139
|
// Mock the query result to return the mock suggestions
|
|
109
140
|
jest.mocked(useTamboQuery).mockReturnValue({
|
|
110
141
|
data: mockSuggestions,
|
|
@@ -134,18 +165,15 @@ describe("useTamboSuggestions", () => {
|
|
|
134
165
|
},
|
|
135
166
|
});
|
|
136
167
|
// Mock the thread to have a non-Tambo message
|
|
137
|
-
jest.mocked(useTamboThread).mockReturnValue({
|
|
138
|
-
thread: {
|
|
139
|
-
id: "test-thread-id",
|
|
168
|
+
jest.mocked(useTamboThread).mockReturnValue(createMockThreadContext({
|
|
169
|
+
thread: createMockThread({
|
|
140
170
|
messages: [
|
|
141
|
-
{
|
|
142
|
-
id: "test-message-id",
|
|
171
|
+
createMockMessage({
|
|
143
172
|
role: "user",
|
|
144
|
-
|
|
145
|
-
},
|
|
173
|
+
}),
|
|
146
174
|
],
|
|
147
|
-
},
|
|
148
|
-
});
|
|
175
|
+
}),
|
|
176
|
+
}));
|
|
149
177
|
const { result } = renderHook(() => useTamboSuggestions());
|
|
150
178
|
// Wait for the effect to run
|
|
151
179
|
await act(async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-suggestions.test.js","sourceRoot":"","sources":["../../../src/hooks/__tests__/use-suggestions.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient, UseQueryResult } from \"@tanstack/react-query\";\nimport { act, renderHook } from \"@testing-library/react\";\nimport { TamboThreadMessage } from \"../../model/generate-component-response\";\nimport {\n useTamboClient,\n useTamboQueryClient,\n} from \"../../providers/tambo-client-provider\";\nimport { TamboContextProps, useTambo } from \"../../providers/tambo-provider\";\nimport {\n TamboThreadContextProps,\n useTamboThread,\n} from \"../../providers/tambo-thread-provider\";\nimport { PartialTamboAI } from \"../../testing/types\";\nimport { useTamboMutation, useTamboQuery } from \"../react-query-hooks\";\nimport { useTamboSuggestions } from \"../use-suggestions\";\nimport { useTamboThreadInput, UseThreadInput } from \"../use-thread-input\";\n\n// Mock the required providers\njest.mock(\"../../providers/tambo-client-provider\", () => ({\n useTamboClient: jest.fn(),\n useTamboQueryClient: jest.fn(),\n}));\n\njest.mock(\"../../providers/tambo-provider\", () => ({ useTambo: jest.fn() }));\n\njest.mock(\"../../providers/tambo-thread-provider\", () => ({\n useTamboThread: jest.fn(),\n}));\n\njest.mock(\"../use-thread-input\", () => ({ useTamboThreadInput: jest.fn() }));\n\n// Mock the react-query-hooks\njest.mock(\"../react-query-hooks\", () => ({\n useTamboQuery: jest.fn(),\n useTamboMutation: jest.fn(),\n}));\n\ndescribe(\"useTamboSuggestions\", () => {\n const mockSuggestions: TamboAI.Beta.Threads.Suggestion[] = [\n {\n id: \"suggestion-1\",\n messageId: \"test-message-id\",\n title: \"Test Suggestion 1\",\n detailedSuggestion: \"Test suggestion 1\",\n },\n {\n id: \"suggestion-2\",\n messageId: \"test-message-id\",\n title: \"Test Suggestion 2\",\n detailedSuggestion: \"Test suggestion 2\",\n },\n ];\n\n beforeEach(() => {\n jest.mocked(useTamboQueryClient).mockReturnValue(new QueryClient());\n jest.mocked(useTamboMutation).mockImplementation(\n ({ mutationFn }) =>\n ({\n mutateAsync: mutationFn,\n isLoading: false,\n isError: false,\n error: null,\n }) as any,\n );\n // Setup default mock implementations\n jest.mocked(useTamboClient).mockReturnValue({\n beta: { threads: { suggestions: { generate: jest.fn() } } },\n } satisfies PartialTamboAI as any);\n jest.mocked(useTambo).mockReturnValue({\n sendThreadMessage: jest.fn(),\n } satisfies Partial<TamboContextProps> as any);\n jest.mocked(useTamboThread).mockReturnValue({\n thread: {\n id: \"test-thread-id\",\n messages: [\n {\n id: \"test-message-id\",\n role: \"hydra\",\n content: [{ type: \"text\", text: \"Test message\" }],\n },\n ],\n },\n } as TamboThreadContextProps);\n jest.mocked(useTamboThreadInput).mockReturnValue({\n setValue: jest.fn(),\n value: \"\",\n submit: jest.fn(),\n } satisfies Partial<UseThreadInput> as any);\n // Default query mock returns empty array\n jest.mocked(useTamboQuery).mockReturnValue({\n data: [],\n isLoading: false,\n isError: false,\n error: null,\n } satisfies Partial<UseQueryResult<unknown, unknown>> as any);\n });\n\n it(\"should initialize with empty suggestions and no selected suggestion\", () => {\n const { result } = renderHook(() => useTamboSuggestions());\n\n expect(result.current.suggestions).toEqual([]);\n expect(result.current.selectedSuggestionId).toBeNull();\n });\n\n it(\"should generate suggestions when latest message is from Tambo\", async () => {\n const mockGenerate = jest.fn().mockResolvedValue(mockSuggestions);\n jest.mocked(useTamboClient).mockReturnValue({\n beta: {\n threads: {\n suggestions: {\n generate: mockGenerate,\n list: jest.fn(),\n },\n },\n },\n } satisfies PartialTamboAI as any);\n jest.mocked(useTamboThread).mockReturnValue({\n thread: {\n id: \"test-thread-id\",\n messages: [\n {\n id: \"test-message-id\",\n role: \"assistant\",\n content: [{ type: \"text\", text: \"Test message\" }],\n } as TamboThreadMessage,\n ],\n },\n } as TamboThreadContextProps);\n\n // Mock the query result to return the mock suggestions\n jest.mocked(useTamboQuery).mockReturnValue({\n data: mockSuggestions,\n isLoading: false,\n isError: false,\n error: null,\n } as UseQueryResult<unknown, unknown>);\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n // Wait for the effect to run\n await act(async () => {\n await new Promise((resolve) => setTimeout(resolve, 0));\n });\n\n // Since we're mocking useTamboQuery to return the suggestions directly,\n // the generate function won't be called, so we don't need to check that\n expect(result.current.suggestions).toEqual(mockSuggestions);\n });\n\n it(\"should not generate suggestions when latest message is not from Tambo\", async () => {\n const mockGenerate = jest.fn();\n jest.mocked(useTamboClient).mockReturnValue({\n beta: {\n threads: {\n suggestions: {\n generate: mockGenerate,\n list: jest.fn(),\n },\n },\n },\n } satisfies PartialTamboAI as any);\n // Mock the thread to have a non-Tambo message\n jest.mocked(useTamboThread).mockReturnValue({\n thread: {\n id: \"test-thread-id\",\n messages: [\n {\n id: \"test-message-id\",\n role: \"user\",\n content: [{ type: \"text\", text: \"Test message\" }],\n } as TamboThreadMessage,\n ],\n },\n } as TamboThreadContextProps);\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n // Wait for the effect to run\n await act(async () => {\n await new Promise((resolve) => setTimeout(resolve, 0));\n });\n\n expect(mockGenerate).not.toHaveBeenCalled();\n expect(result.current.suggestions).toEqual([]);\n });\n\n it(\"should accept a suggestion and update input value\", async () => {\n const mockSetValue = jest.fn();\n jest.mocked(useTamboThreadInput).mockReturnValue({\n setValue: mockSetValue,\n value: \"\",\n submit: jest.fn(),\n } satisfies Partial<UseThreadInput> as any);\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n await act(async () => {\n await result.current.accept({\n suggestion: mockSuggestions[0],\n shouldSubmit: false,\n });\n });\n\n expect(mockSetValue).toHaveBeenCalledWith(\"Test suggestion 1\");\n expect(result.current.selectedSuggestionId).toBe(\"suggestion-1\");\n });\n\n it(\"should accept a suggestion and submit it\", async () => {\n const mockSendThreadMessage = jest.fn();\n jest.mocked(useTambo).mockReturnValue({\n sendThreadMessage: mockSendThreadMessage,\n } satisfies Partial<TamboContextProps> as any);\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n await act(async () => {\n await result.current.accept({\n suggestion: mockSuggestions[0],\n shouldSubmit: true,\n });\n });\n\n expect(mockSendThreadMessage).toHaveBeenCalledWith(\"Test suggestion 1\", {\n threadId: \"test-thread-id\",\n });\n expect(result.current.selectedSuggestionId).toBe(\"suggestion-1\");\n });\n\n it(\"should throw error when accepting invalid suggestion\", async () => {\n const invalidSuggestion = {\n id: \"invalid-suggestion\",\n messageId: \"test-message-id\",\n title: \"Invalid Suggestion\",\n detailedSuggestion: \"\", // Empty suggestion should fail validation\n };\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n await act(async () => {\n await expect(\n result.current.accept({\n suggestion: invalidSuggestion,\n shouldSubmit: false,\n }),\n ).rejects.toThrow(\"Message cannot be empty\");\n });\n\n expect(result.current.selectedSuggestionId).toBeNull();\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"use-suggestions.test.js","sourceRoot":"","sources":["../../../src/hooks/__tests__/use-suggestions.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient, UseQueryResult } from \"@tanstack/react-query\";\nimport { act, renderHook } from \"@testing-library/react\";\nimport {\n GenerationStage,\n TamboThreadMessage,\n} from \"../../model/generate-component-response\";\nimport { TamboThread } from \"../../model/tambo-thread\";\nimport {\n useTamboClient,\n useTamboQueryClient,\n} from \"../../providers/tambo-client-provider\";\nimport { TamboContextProps, useTambo } from \"../../providers/tambo-provider\";\nimport {\n CombinedTamboThreadContextProps,\n useTamboThread,\n} from \"../../providers/tambo-thread-provider\";\nimport { PartialTamboAI } from \"../../testing/types\";\nimport { useTamboMutation, useTamboQuery } from \"../react-query-hooks\";\nimport { useTamboSuggestions } from \"../use-suggestions\";\nimport { useTamboThreadInput, UseThreadInput } from \"../use-thread-input\";\n\n// Mock the required providers\njest.mock(\"../../providers/tambo-client-provider\", () => ({\n useTamboClient: jest.fn(),\n useTamboQueryClient: jest.fn(),\n}));\n\njest.mock(\"../../providers/tambo-provider\", () => ({ useTambo: jest.fn() }));\n\njest.mock(\"../../providers/tambo-thread-provider\", () => ({\n useTamboThread: jest.fn(),\n}));\n\njest.mock(\"../use-thread-input\", () => ({ useTamboThreadInput: jest.fn() }));\n\n// Mock the react-query-hooks\njest.mock(\"../react-query-hooks\", () => ({\n useTamboQuery: jest.fn(),\n useTamboMutation: jest.fn(),\n}));\n\ndescribe(\"useTamboSuggestions\", () => {\n const mockSuggestions: TamboAI.Beta.Threads.Suggestion[] = [\n {\n id: \"suggestion-1\",\n messageId: \"test-message-id\",\n title: \"Test Suggestion 1\",\n detailedSuggestion: \"Test suggestion 1\",\n },\n {\n id: \"suggestion-2\",\n messageId: \"test-message-id\",\n title: \"Test Suggestion 2\",\n detailedSuggestion: \"Test suggestion 2\",\n },\n ];\n\n // Helper function to create mock TamboThreadMessage\n const createMockMessage = (\n overrides: Partial<TamboThreadMessage> = {},\n ): TamboThreadMessage => ({\n id: \"test-message-id\",\n componentState: {},\n content: [{ type: \"text\", text: \"Test message\" }],\n createdAt: new Date().toISOString(),\n role: \"assistant\",\n threadId: \"test-thread-id\",\n ...overrides,\n });\n\n // Helper function to create mock TamboThread\n const createMockThread = (\n overrides: Partial<TamboThread> = {},\n ): TamboThread => ({\n id: \"test-thread-id\",\n projectId: \"test-project-id\",\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n messages: [],\n ...overrides,\n });\n\n // Helper function to create mock CombinedTamboThreadContextProps\n const createMockThreadContext = (\n overrides: Partial<CombinedTamboThreadContextProps> = {},\n ): CombinedTamboThreadContextProps => ({\n thread: createMockThread(),\n switchCurrentThread: jest.fn(),\n startNewThread: jest.fn(),\n updateThreadName: jest.fn(),\n generateThreadName: jest.fn(),\n addThreadMessage: jest.fn(),\n updateThreadMessage: jest.fn(),\n cancel: jest.fn(),\n streaming: false,\n sendThreadMessage: jest.fn(),\n generationStage: GenerationStage.IDLE,\n generationStatusMessage: \"\",\n isIdle: true,\n ...overrides,\n });\n\n beforeEach(() => {\n jest.mocked(useTamboQueryClient).mockReturnValue(new QueryClient());\n jest.mocked(useTamboMutation).mockImplementation(\n ({ mutationFn }) =>\n ({\n mutateAsync: mutationFn,\n isLoading: false,\n isError: false,\n error: null,\n }) as any,\n );\n // Setup default mock implementations\n jest.mocked(useTamboClient).mockReturnValue({\n beta: { threads: { suggestions: { generate: jest.fn() } } },\n } satisfies PartialTamboAI as any);\n jest.mocked(useTambo).mockReturnValue({\n sendThreadMessage: jest.fn(),\n } satisfies Partial<TamboContextProps> as any);\n jest.mocked(useTamboThread).mockReturnValue(\n createMockThreadContext({\n thread: createMockThread({\n messages: [\n createMockMessage({\n role: \"hydra\",\n }),\n ],\n }),\n }),\n );\n jest.mocked(useTamboThreadInput).mockReturnValue({\n setValue: jest.fn(),\n value: \"\",\n submit: jest.fn(),\n } satisfies Partial<UseThreadInput> as any);\n // Default query mock returns empty array\n jest.mocked(useTamboQuery).mockReturnValue({\n data: [],\n isLoading: false,\n isError: false,\n error: null,\n } satisfies Partial<UseQueryResult<unknown, unknown>> as any);\n });\n\n it(\"should initialize with empty suggestions and no selected suggestion\", () => {\n const { result } = renderHook(() => useTamboSuggestions());\n\n expect(result.current.suggestions).toEqual([]);\n expect(result.current.selectedSuggestionId).toBeNull();\n });\n\n it(\"should generate suggestions when latest message is from Tambo\", async () => {\n const mockGenerate = jest.fn().mockResolvedValue(mockSuggestions);\n jest.mocked(useTamboClient).mockReturnValue({\n beta: {\n threads: {\n suggestions: {\n generate: mockGenerate,\n list: jest.fn(),\n },\n },\n },\n } satisfies PartialTamboAI as any);\n jest.mocked(useTamboThread).mockReturnValue(\n createMockThreadContext({\n thread: createMockThread({\n messages: [\n createMockMessage({\n role: \"assistant\",\n }),\n ],\n }),\n }),\n );\n\n // Mock the query result to return the mock suggestions\n jest.mocked(useTamboQuery).mockReturnValue({\n data: mockSuggestions,\n isLoading: false,\n isError: false,\n error: null,\n } as UseQueryResult<unknown, unknown>);\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n // Wait for the effect to run\n await act(async () => {\n await new Promise((resolve) => setTimeout(resolve, 0));\n });\n\n // Since we're mocking useTamboQuery to return the suggestions directly,\n // the generate function won't be called, so we don't need to check that\n expect(result.current.suggestions).toEqual(mockSuggestions);\n });\n\n it(\"should not generate suggestions when latest message is not from Tambo\", async () => {\n const mockGenerate = jest.fn();\n jest.mocked(useTamboClient).mockReturnValue({\n beta: {\n threads: {\n suggestions: {\n generate: mockGenerate,\n list: jest.fn(),\n },\n },\n },\n } satisfies PartialTamboAI as any);\n // Mock the thread to have a non-Tambo message\n jest.mocked(useTamboThread).mockReturnValue(\n createMockThreadContext({\n thread: createMockThread({\n messages: [\n createMockMessage({\n role: \"user\",\n }),\n ],\n }),\n }),\n );\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n // Wait for the effect to run\n await act(async () => {\n await new Promise((resolve) => setTimeout(resolve, 0));\n });\n\n expect(mockGenerate).not.toHaveBeenCalled();\n expect(result.current.suggestions).toEqual([]);\n });\n\n it(\"should accept a suggestion and update input value\", async () => {\n const mockSetValue = jest.fn();\n jest.mocked(useTamboThreadInput).mockReturnValue({\n setValue: mockSetValue,\n value: \"\",\n submit: jest.fn(),\n } satisfies Partial<UseThreadInput> as any);\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n await act(async () => {\n await result.current.accept({\n suggestion: mockSuggestions[0],\n shouldSubmit: false,\n });\n });\n\n expect(mockSetValue).toHaveBeenCalledWith(\"Test suggestion 1\");\n expect(result.current.selectedSuggestionId).toBe(\"suggestion-1\");\n });\n\n it(\"should accept a suggestion and submit it\", async () => {\n const mockSendThreadMessage = jest.fn();\n jest.mocked(useTambo).mockReturnValue({\n sendThreadMessage: mockSendThreadMessage,\n } satisfies Partial<TamboContextProps> as any);\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n await act(async () => {\n await result.current.accept({\n suggestion: mockSuggestions[0],\n shouldSubmit: true,\n });\n });\n\n expect(mockSendThreadMessage).toHaveBeenCalledWith(\"Test suggestion 1\", {\n threadId: \"test-thread-id\",\n });\n expect(result.current.selectedSuggestionId).toBe(\"suggestion-1\");\n });\n\n it(\"should throw error when accepting invalid suggestion\", async () => {\n const invalidSuggestion = {\n id: \"invalid-suggestion\",\n messageId: \"test-message-id\",\n title: \"Invalid Suggestion\",\n detailedSuggestion: \"\", // Empty suggestion should fail validation\n };\n\n const { result } = renderHook(() => useTamboSuggestions());\n\n await act(async () => {\n await expect(\n result.current.accept({\n suggestion: invalidSuggestion,\n shouldSubmit: false,\n }),\n ).rejects.toThrow(\"Message cannot be empty\");\n });\n\n expect(result.current.selectedSuggestionId).toBeNull();\n });\n});\n"]}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { renderHook } from "@testing-library/react";
|
|
2
2
|
import { GenerationStage, } from "../../model/generate-component-response";
|
|
3
|
-
import { useTamboThread, } from "../../providers/tambo-thread-provider";
|
|
4
|
-
import { useTamboCurrentMessage } from "../use-current-message";
|
|
5
3
|
import { useTamboStreamStatus } from "../use-tambo-stream-status";
|
|
6
4
|
// Mock the required providers
|
|
7
5
|
jest.mock("../../providers/tambo-thread-provider", () => ({
|
|
8
|
-
|
|
6
|
+
useTamboGenerationStage: jest.fn(),
|
|
9
7
|
}));
|
|
10
8
|
jest.mock("../use-current-message", () => ({
|
|
11
9
|
useTamboCurrentMessage: jest.fn(),
|
|
12
10
|
}));
|
|
11
|
+
// Import the mocked functions
|
|
12
|
+
import { useTamboGenerationStage } from "../../providers/tambo-thread-provider";
|
|
13
|
+
import { useTamboCurrentMessage } from "../use-current-message";
|
|
13
14
|
// Mock window for SSR tests
|
|
14
15
|
const originalWindow = global.window;
|
|
15
16
|
// Helper function to create mock ComponentDecisionV2
|
|
@@ -30,15 +31,20 @@ const createMockMessage = (overrides = {}) => ({
|
|
|
30
31
|
threadId: "test-thread",
|
|
31
32
|
...overrides,
|
|
32
33
|
});
|
|
34
|
+
// Get the mocked functions
|
|
35
|
+
const mockUseTamboGenerationStage = jest.mocked(useTamboGenerationStage);
|
|
36
|
+
const mockUseTamboCurrentMessage = jest.mocked(useTamboCurrentMessage);
|
|
33
37
|
describe("useTamboStreamStatus", () => {
|
|
34
38
|
beforeEach(() => {
|
|
35
39
|
// Restore window for client-side tests
|
|
36
40
|
global.window = originalWindow;
|
|
37
41
|
// Default mock implementations
|
|
38
|
-
|
|
42
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
39
43
|
generationStage: GenerationStage.IDLE,
|
|
44
|
+
generationStatusMessage: "",
|
|
45
|
+
isIdle: true,
|
|
40
46
|
});
|
|
41
|
-
|
|
47
|
+
mockUseTamboCurrentMessage.mockReturnValue({
|
|
42
48
|
id: "test-message",
|
|
43
49
|
component: {
|
|
44
50
|
props: {},
|
|
@@ -50,10 +56,12 @@ describe("useTamboStreamStatus", () => {
|
|
|
50
56
|
});
|
|
51
57
|
describe("Initial State", () => {
|
|
52
58
|
it("should start with all flags as pending when idle and no props", () => {
|
|
53
|
-
|
|
59
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
54
60
|
generationStage: GenerationStage.IDLE,
|
|
61
|
+
generationStatusMessage: "",
|
|
62
|
+
isIdle: true,
|
|
55
63
|
});
|
|
56
|
-
|
|
64
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
57
65
|
component: createMockComponent({ title: "", body: "" }),
|
|
58
66
|
}));
|
|
59
67
|
const { result } = renderHook(() => useTamboStreamStatus());
|
|
@@ -80,10 +88,12 @@ describe("useTamboStreamStatus", () => {
|
|
|
80
88
|
});
|
|
81
89
|
describe("Generation vs Props Streaming", () => {
|
|
82
90
|
it("should show generation streaming but props still pending when STREAMING_RESPONSE with no prop content", () => {
|
|
83
|
-
|
|
91
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
84
92
|
generationStage: GenerationStage.STREAMING_RESPONSE,
|
|
93
|
+
generationStatusMessage: "",
|
|
94
|
+
isIdle: false,
|
|
85
95
|
});
|
|
86
|
-
|
|
96
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
87
97
|
component: createMockComponent({ title: "", body: "" }),
|
|
88
98
|
}));
|
|
89
99
|
const { result } = renderHook(() => useTamboStreamStatus());
|
|
@@ -97,10 +107,12 @@ describe("useTamboStreamStatus", () => {
|
|
|
97
107
|
expect(result.current.propStatus.body.isStreaming).toBe(false);
|
|
98
108
|
});
|
|
99
109
|
it("should show prop streaming when props receive content during STREAMING_RESPONSE", () => {
|
|
100
|
-
|
|
110
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
101
111
|
generationStage: GenerationStage.STREAMING_RESPONSE,
|
|
112
|
+
generationStatusMessage: "",
|
|
113
|
+
isIdle: false,
|
|
102
114
|
});
|
|
103
|
-
|
|
115
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
104
116
|
component: createMockComponent({ title: "Hello", body: "" }),
|
|
105
117
|
}));
|
|
106
118
|
const { result } = renderHook(() => useTamboStreamStatus());
|
|
@@ -117,10 +129,12 @@ describe("useTamboStreamStatus", () => {
|
|
|
117
129
|
describe("Boolean Lifecycle", () => {
|
|
118
130
|
it("should transition through Init -> Streaming -> Success lifecycle", () => {
|
|
119
131
|
// Start with IDLE (Init phase)
|
|
120
|
-
|
|
132
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
121
133
|
generationStage: GenerationStage.IDLE,
|
|
134
|
+
generationStatusMessage: "",
|
|
135
|
+
isIdle: true,
|
|
122
136
|
});
|
|
123
|
-
|
|
137
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
124
138
|
component: createMockComponent({ title: "", body: "" }),
|
|
125
139
|
}));
|
|
126
140
|
const { result, rerender } = renderHook(() => useTamboStreamStatus());
|
|
@@ -129,10 +143,12 @@ describe("useTamboStreamStatus", () => {
|
|
|
129
143
|
expect(result.current.streamStatus.isStreaming).toBe(false);
|
|
130
144
|
expect(result.current.streamStatus.isSuccess).toBe(false);
|
|
131
145
|
// Phase 2: Streaming - move to STREAMING_RESPONSE with content
|
|
132
|
-
|
|
146
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
133
147
|
generationStage: GenerationStage.STREAMING_RESPONSE,
|
|
148
|
+
generationStatusMessage: "",
|
|
149
|
+
isIdle: false,
|
|
134
150
|
});
|
|
135
|
-
|
|
151
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
136
152
|
component: createMockComponent({
|
|
137
153
|
title: "Hello World",
|
|
138
154
|
body: "Some content",
|
|
@@ -143,8 +159,10 @@ describe("useTamboStreamStatus", () => {
|
|
|
143
159
|
expect(result.current.streamStatus.isStreaming).toBe(true);
|
|
144
160
|
expect(result.current.streamStatus.isSuccess).toBe(false);
|
|
145
161
|
// Phase 3: Complete - move to COMPLETE
|
|
146
|
-
|
|
162
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
147
163
|
generationStage: GenerationStage.COMPLETE,
|
|
164
|
+
generationStatusMessage: "",
|
|
165
|
+
isIdle: false,
|
|
148
166
|
});
|
|
149
167
|
rerender();
|
|
150
168
|
expect(result.current.streamStatus.isPending).toBe(false);
|
|
@@ -152,11 +170,13 @@ describe("useTamboStreamStatus", () => {
|
|
|
152
170
|
expect(result.current.streamStatus.isSuccess).toBe(true);
|
|
153
171
|
});
|
|
154
172
|
it("should handle error state correctly", () => {
|
|
155
|
-
|
|
173
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
156
174
|
generationStage: GenerationStage.ERROR,
|
|
175
|
+
generationStatusMessage: "",
|
|
176
|
+
isIdle: false,
|
|
157
177
|
});
|
|
158
178
|
const errorMessage = "Generation failed";
|
|
159
|
-
|
|
179
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
160
180
|
component: createMockComponent({ title: "", body: "" }),
|
|
161
181
|
error: errorMessage,
|
|
162
182
|
}));
|
|
@@ -170,10 +190,12 @@ describe("useTamboStreamStatus", () => {
|
|
|
170
190
|
});
|
|
171
191
|
describe("Derivation Rules", () => {
|
|
172
192
|
it("should derive isPending correctly (no generation activity AND all props pending)", () => {
|
|
173
|
-
|
|
193
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
174
194
|
generationStage: GenerationStage.IDLE,
|
|
195
|
+
generationStatusMessage: "",
|
|
196
|
+
isIdle: true,
|
|
175
197
|
});
|
|
176
|
-
|
|
198
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
177
199
|
component: createMockComponent({ title: "", body: "", footer: "" }),
|
|
178
200
|
}));
|
|
179
201
|
const { result } = renderHook(() => useTamboStreamStatus());
|
|
@@ -184,11 +206,13 @@ describe("useTamboStreamStatus", () => {
|
|
|
184
206
|
expect(result.current.propStatus.footer.isPending).toBe(true);
|
|
185
207
|
});
|
|
186
208
|
it("should derive isStreaming correctly (generation streaming OR any prop streaming)", () => {
|
|
187
|
-
|
|
209
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
188
210
|
generationStage: GenerationStage.COMPLETE,
|
|
211
|
+
generationStatusMessage: "",
|
|
212
|
+
isIdle: false,
|
|
189
213
|
});
|
|
190
214
|
// One prop still streaming
|
|
191
|
-
|
|
215
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
192
216
|
component: createMockComponent({
|
|
193
217
|
title: "Complete Title",
|
|
194
218
|
body: "Still streaming...",
|
|
@@ -200,10 +224,12 @@ describe("useTamboStreamStatus", () => {
|
|
|
200
224
|
});
|
|
201
225
|
it("should derive isSuccess correctly (generation complete AND all props successful)", () => {
|
|
202
226
|
// Step 1: Start with streaming, props empty
|
|
203
|
-
|
|
227
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
204
228
|
generationStage: GenerationStage.STREAMING_RESPONSE,
|
|
229
|
+
generationStatusMessage: "",
|
|
230
|
+
isIdle: false,
|
|
205
231
|
});
|
|
206
|
-
|
|
232
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
207
233
|
component: createMockComponent({
|
|
208
234
|
title: "",
|
|
209
235
|
body: "",
|
|
@@ -211,7 +237,7 @@ describe("useTamboStreamStatus", () => {
|
|
|
211
237
|
}));
|
|
212
238
|
const { result, rerender } = renderHook(() => useTamboStreamStatus());
|
|
213
239
|
// Step 2: Simulate streaming in title
|
|
214
|
-
|
|
240
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
215
241
|
component: createMockComponent({
|
|
216
242
|
title: "Complete Title",
|
|
217
243
|
body: "",
|
|
@@ -219,7 +245,7 @@ describe("useTamboStreamStatus", () => {
|
|
|
219
245
|
}));
|
|
220
246
|
rerender();
|
|
221
247
|
// Step 3: Simulate streaming in body
|
|
222
|
-
|
|
248
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
223
249
|
component: createMockComponent({
|
|
224
250
|
title: "Complete Title",
|
|
225
251
|
body: "Complete Body",
|
|
@@ -227,8 +253,10 @@ describe("useTamboStreamStatus", () => {
|
|
|
227
253
|
}));
|
|
228
254
|
rerender();
|
|
229
255
|
// Step 4: Generation complete
|
|
230
|
-
|
|
256
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
231
257
|
generationStage: GenerationStage.COMPLETE,
|
|
258
|
+
generationStatusMessage: "",
|
|
259
|
+
isIdle: false,
|
|
232
260
|
});
|
|
233
261
|
rerender();
|
|
234
262
|
// Now both props should be successful
|
|
@@ -237,10 +265,12 @@ describe("useTamboStreamStatus", () => {
|
|
|
237
265
|
expect(result.current.streamStatus.isSuccess).toBe(true);
|
|
238
266
|
});
|
|
239
267
|
it("should derive isError correctly (generation error OR any prop error)", () => {
|
|
240
|
-
|
|
268
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
241
269
|
generationStage: GenerationStage.COMPLETE,
|
|
270
|
+
generationStatusMessage: "",
|
|
271
|
+
isIdle: false,
|
|
242
272
|
});
|
|
243
|
-
|
|
273
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
244
274
|
component: createMockComponent({
|
|
245
275
|
title: "Good Title",
|
|
246
276
|
body: "Good Body",
|
|
@@ -254,7 +284,7 @@ describe("useTamboStreamStatus", () => {
|
|
|
254
284
|
});
|
|
255
285
|
describe("Type Safety", () => {
|
|
256
286
|
it("should provide strongly typed prop status based on generic", () => {
|
|
257
|
-
|
|
287
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
258
288
|
component: createMockComponent({
|
|
259
289
|
title: "Test",
|
|
260
290
|
description: "Test desc",
|
|
@@ -270,7 +300,7 @@ describe("useTamboStreamStatus", () => {
|
|
|
270
300
|
// expect(result.current.propStatus.nonExistentProp).toBeDefined();
|
|
271
301
|
});
|
|
272
302
|
it("should work without generic type parameter", () => {
|
|
273
|
-
|
|
303
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
274
304
|
component: createMockComponent({ dynamicProp: "value" }),
|
|
275
305
|
}));
|
|
276
306
|
const { result } = renderHook(() => useTamboStreamStatus());
|
|
@@ -280,13 +310,20 @@ describe("useTamboStreamStatus", () => {
|
|
|
280
310
|
});
|
|
281
311
|
describe("Edge Cases", () => {
|
|
282
312
|
it("should handle missing message gracefully", () => {
|
|
283
|
-
|
|
313
|
+
mockUseTamboCurrentMessage.mockReturnValue({
|
|
314
|
+
id: "test-message",
|
|
315
|
+
role: "user",
|
|
316
|
+
content: [],
|
|
317
|
+
componentState: {},
|
|
318
|
+
createdAt: "",
|
|
319
|
+
threadId: "",
|
|
320
|
+
});
|
|
284
321
|
const { result } = renderHook(() => useTamboStreamStatus());
|
|
285
322
|
expect(result.current.streamStatus.isPending).toBe(true);
|
|
286
323
|
expect(result.current.propStatus).toEqual({});
|
|
287
324
|
});
|
|
288
325
|
it("should handle missing component props gracefully", () => {
|
|
289
|
-
|
|
326
|
+
mockUseTamboCurrentMessage.mockReturnValue({
|
|
290
327
|
id: "test-message",
|
|
291
328
|
// Missing component property
|
|
292
329
|
});
|
|
@@ -296,30 +333,36 @@ describe("useTamboStreamStatus", () => {
|
|
|
296
333
|
});
|
|
297
334
|
it("should reset prop tracking when generation restarts", () => {
|
|
298
335
|
// Step 1: Complete a message
|
|
299
|
-
|
|
336
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
300
337
|
generationStage: GenerationStage.STREAMING_RESPONSE,
|
|
338
|
+
generationStatusMessage: "",
|
|
339
|
+
isIdle: false,
|
|
301
340
|
});
|
|
302
|
-
|
|
341
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
303
342
|
component: createMockComponent({ title: "" }),
|
|
304
343
|
}));
|
|
305
344
|
const { result, rerender } = renderHook(() => useTamboStreamStatus());
|
|
306
345
|
// Simulate streaming in title
|
|
307
|
-
|
|
346
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
308
347
|
component: createMockComponent({ title: "First Title" }),
|
|
309
348
|
}));
|
|
310
349
|
rerender();
|
|
311
350
|
// Complete generation
|
|
312
|
-
|
|
351
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
313
352
|
generationStage: GenerationStage.COMPLETE,
|
|
353
|
+
generationStatusMessage: "",
|
|
354
|
+
isIdle: false,
|
|
314
355
|
});
|
|
315
356
|
rerender();
|
|
316
357
|
// Should be successful initially
|
|
317
358
|
expect(result.current.propStatus.title.isSuccess).toBe(true);
|
|
318
359
|
// Step 2: Start new generation with a new message ID to trigger reset
|
|
319
|
-
|
|
360
|
+
mockUseTamboGenerationStage.mockReturnValue({
|
|
320
361
|
generationStage: GenerationStage.CHOOSING_COMPONENT,
|
|
362
|
+
generationStatusMessage: "",
|
|
363
|
+
isIdle: false,
|
|
321
364
|
});
|
|
322
|
-
|
|
365
|
+
mockUseTamboCurrentMessage.mockReturnValue(createMockMessage({
|
|
323
366
|
id: "new-message-id", // Different message ID to trigger reset
|
|
324
367
|
component: createMockComponent({ title: "" }),
|
|
325
368
|
}));
|