@tambo-ai/react 0.47.0 → 0.49.0
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/context-helpers/current-interactables-context-helper.d.ts +28 -0
- package/dist/context-helpers/current-interactables-context-helper.d.ts.map +1 -0
- package/dist/context-helpers/current-interactables-context-helper.js +61 -0
- package/dist/context-helpers/current-interactables-context-helper.js.map +1 -0
- package/dist/context-helpers/index.d.ts +1 -0
- package/dist/context-helpers/index.d.ts.map +1 -1
- package/dist/context-helpers/index.js +1 -0
- package/dist/context-helpers/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/__tests__/tambo-mcp-provider.test.d.ts +2 -0
- package/dist/mcp/__tests__/tambo-mcp-provider.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/tambo-mcp-provider.test.js +115 -0
- package/dist/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -0
- package/dist/mcp/tambo-mcp-provider.d.ts +6 -0
- package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js +25 -2
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/providers/__tests__/tambo-interactables-additional-context-edge-cases.test.d.ts +2 -0
- package/dist/providers/__tests__/tambo-interactables-additional-context-edge-cases.test.d.ts.map +1 -0
- package/dist/providers/__tests__/tambo-interactables-additional-context-edge-cases.test.js +339 -0
- package/dist/providers/__tests__/tambo-interactables-additional-context-edge-cases.test.js.map +1 -0
- package/dist/providers/__tests__/tambo-interactables-additional-context.test.d.ts +2 -0
- package/dist/providers/__tests__/tambo-interactables-additional-context.test.d.ts.map +1 -0
- package/dist/providers/__tests__/tambo-interactables-additional-context.test.js +299 -0
- package/dist/providers/__tests__/tambo-interactables-additional-context.test.js.map +1 -0
- package/dist/providers/__tests__/tambo-registry-provider.test.d.ts +2 -0
- package/dist/providers/__tests__/tambo-registry-provider.test.d.ts.map +1 -0
- package/dist/providers/__tests__/tambo-registry-provider.test.js +224 -0
- package/dist/providers/__tests__/tambo-registry-provider.test.js.map +1 -0
- package/dist/providers/__tests__/tambo-thread-provider.test.js +99 -0
- package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.d.ts +20 -0
- package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/dist/providers/tambo-interactable-provider.js +32 -2
- package/dist/providers/tambo-interactable-provider.js.map +1 -1
- package/dist/providers/tambo-provider.d.ts.map +1 -1
- package/dist/providers/tambo-provider.js +2 -2
- package/dist/providers/tambo-provider.js.map +1 -1
- package/dist/providers/tambo-registry-provider.d.ts +12 -0
- package/dist/providers/tambo-registry-provider.d.ts.map +1 -1
- package/dist/providers/tambo-registry-provider.js +3 -1
- package/dist/providers/tambo-registry-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +5 -3
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/dist/util/tool-caller.d.ts +1 -1
- package/dist/util/tool-caller.d.ts.map +1 -1
- package/dist/util/tool-caller.js +11 -2
- package/dist/util/tool-caller.js.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.d.ts +28 -0
- package/esm/context-helpers/current-interactables-context-helper.d.ts.map +1 -0
- package/esm/context-helpers/current-interactables-context-helper.js +56 -0
- package/esm/context-helpers/current-interactables-context-helper.js.map +1 -0
- package/esm/context-helpers/index.d.ts +1 -0
- package/esm/context-helpers/index.d.ts.map +1 -1
- package/esm/context-helpers/index.js +1 -0
- package/esm/context-helpers/index.js.map +1 -1
- package/esm/index.d.ts +1 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -1
- package/esm/index.js.map +1 -1
- package/esm/mcp/__tests__/tambo-mcp-provider.test.d.ts +2 -0
- package/esm/mcp/__tests__/tambo-mcp-provider.test.d.ts.map +1 -0
- package/esm/mcp/__tests__/tambo-mcp-provider.test.js +113 -0
- package/esm/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -0
- package/esm/mcp/tambo-mcp-provider.d.ts +6 -0
- package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js +24 -2
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/providers/__tests__/tambo-interactables-additional-context-edge-cases.test.d.ts +2 -0
- package/esm/providers/__tests__/tambo-interactables-additional-context-edge-cases.test.d.ts.map +1 -0
- package/esm/providers/__tests__/tambo-interactables-additional-context-edge-cases.test.js +334 -0
- package/esm/providers/__tests__/tambo-interactables-additional-context-edge-cases.test.js.map +1 -0
- package/esm/providers/__tests__/tambo-interactables-additional-context.test.d.ts +2 -0
- package/esm/providers/__tests__/tambo-interactables-additional-context.test.d.ts.map +1 -0
- package/esm/providers/__tests__/tambo-interactables-additional-context.test.js +294 -0
- package/esm/providers/__tests__/tambo-interactables-additional-context.test.js.map +1 -0
- package/esm/providers/__tests__/tambo-registry-provider.test.d.ts +2 -0
- package/esm/providers/__tests__/tambo-registry-provider.test.d.ts.map +1 -0
- package/esm/providers/__tests__/tambo-registry-provider.test.js +219 -0
- package/esm/providers/__tests__/tambo-registry-provider.test.js.map +1 -0
- package/esm/providers/__tests__/tambo-thread-provider.test.js +99 -0
- package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.d.ts +20 -0
- package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/esm/providers/tambo-interactable-provider.js +30 -1
- package/esm/providers/tambo-interactable-provider.js.map +1 -1
- package/esm/providers/tambo-provider.d.ts.map +1 -1
- package/esm/providers/tambo-provider.js +2 -2
- package/esm/providers/tambo-provider.js.map +1 -1
- package/esm/providers/tambo-registry-provider.d.ts +12 -0
- package/esm/providers/tambo-registry-provider.d.ts.map +1 -1
- package/esm/providers/tambo-registry-provider.js +3 -1
- package/esm/providers/tambo-registry-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +5 -3
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/esm/util/tool-caller.d.ts +1 -1
- package/esm/util/tool-caller.d.ts.map +1 -1
- package/esm/util/tool-caller.js +11 -2
- package/esm/util/tool-caller.js.map +1 -1
- package/package.json +9 -9
- package/dist/mcp/mcp-tools-client.d.ts +0 -96
- package/dist/mcp/mcp-tools-client.d.ts.map +0 -1
- package/dist/mcp/mcp-tools-client.js +0 -178
- package/dist/mcp/mcp-tools-client.js.map +0 -1
- package/esm/mcp/mcp-tools-client.d.ts +0 -96
- package/esm/mcp/mcp-tools-client.d.ts.map +0 -1
- package/esm/mcp/mcp-tools-client.js +0 -174
- package/esm/mcp/mcp-tools-client.js.map +0 -1
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = require("@testing-library/react");
|
|
7
|
+
const react_2 = __importDefault(require("react"));
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const tambo_context_helpers_provider_1 = require("../tambo-context-helpers-provider");
|
|
10
|
+
const tambo_interactable_provider_1 = require("../tambo-interactable-provider");
|
|
11
|
+
const tambo_stubs_1 = require("../tambo-stubs");
|
|
12
|
+
const with_tambo_interactable_1 = require("../hoc/with-tambo-interactable");
|
|
13
|
+
function wrapperWithProviders(children) {
|
|
14
|
+
const thread = {
|
|
15
|
+
id: "t-1",
|
|
16
|
+
projectId: "p-1",
|
|
17
|
+
createdAt: new Date().toISOString(),
|
|
18
|
+
updatedAt: new Date().toISOString(),
|
|
19
|
+
messages: [],
|
|
20
|
+
metadata: {},
|
|
21
|
+
};
|
|
22
|
+
return (react_2.default.createElement(tambo_stubs_1.TamboStubProvider, { thread: thread, registerTool: () => { }, registerTools: () => { }, registerComponent: () => { }, addToolAssociation: () => { } },
|
|
23
|
+
react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, null, children)));
|
|
24
|
+
}
|
|
25
|
+
describe("Interactables AdditionalContext (provider-based)", () => {
|
|
26
|
+
test("registers default helper and returns payload with description and components", async () => {
|
|
27
|
+
const Note = ({ title }) => react_2.default.createElement("div", null, title);
|
|
28
|
+
const InteractableNote = (0, with_tambo_interactable_1.withTamboInteractable)(Note, {
|
|
29
|
+
componentName: "Note",
|
|
30
|
+
description: "A note",
|
|
31
|
+
propsSchema: zod_1.z.object({ title: zod_1.z.string() }),
|
|
32
|
+
});
|
|
33
|
+
let capturedContexts = [];
|
|
34
|
+
const TestComponent = () => {
|
|
35
|
+
const { getAdditionalContext } = (0, tambo_context_helpers_provider_1.useTamboContextHelpers)();
|
|
36
|
+
react_2.default.useEffect(() => {
|
|
37
|
+
let mounted = true;
|
|
38
|
+
getAdditionalContext().then((contexts) => {
|
|
39
|
+
if (mounted) {
|
|
40
|
+
capturedContexts = contexts;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return () => {
|
|
44
|
+
mounted = false;
|
|
45
|
+
};
|
|
46
|
+
}, [getAdditionalContext]);
|
|
47
|
+
return react_2.default.createElement("div", { "data-testid": "ready" }, "ready");
|
|
48
|
+
};
|
|
49
|
+
const { getByTestId } = (0, react_1.render)(wrapperWithProviders(react_2.default.createElement(tambo_interactable_provider_1.TamboInteractableProvider, null,
|
|
50
|
+
react_2.default.createElement(InteractableNote, { title: "hello" }),
|
|
51
|
+
react_2.default.createElement(TestComponent, null))));
|
|
52
|
+
await (0, react_1.waitFor)(() => {
|
|
53
|
+
expect(getByTestId("ready")).toBeInTheDocument();
|
|
54
|
+
const entry = capturedContexts.find((c) => c.name === "interactables");
|
|
55
|
+
expect(entry).toBeDefined();
|
|
56
|
+
expect(entry?.context?.description).toMatch(/interactable components/i);
|
|
57
|
+
expect(Array.isArray(entry?.context?.components)).toBe(true);
|
|
58
|
+
const comp = entry.context.components[0];
|
|
59
|
+
expect(comp.componentName).toBe("Note");
|
|
60
|
+
expect(comp.props).toEqual({ title: "hello" });
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
test("returns null when no interactable components are present", async () => {
|
|
64
|
+
let capturedContexts = [];
|
|
65
|
+
const TestComponent = () => {
|
|
66
|
+
const { getAdditionalContext } = (0, tambo_context_helpers_provider_1.useTamboContextHelpers)();
|
|
67
|
+
react_2.default.useEffect(() => {
|
|
68
|
+
let mounted = true;
|
|
69
|
+
getAdditionalContext().then((contexts) => {
|
|
70
|
+
if (mounted) {
|
|
71
|
+
capturedContexts = contexts;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return () => {
|
|
75
|
+
mounted = false;
|
|
76
|
+
};
|
|
77
|
+
}, [getAdditionalContext]);
|
|
78
|
+
return react_2.default.createElement("div", { "data-testid": "ready" }, "ready");
|
|
79
|
+
};
|
|
80
|
+
const { getByTestId } = (0, react_1.render)(wrapperWithProviders(react_2.default.createElement(tambo_interactable_provider_1.TamboInteractableProvider, null,
|
|
81
|
+
react_2.default.createElement("div", null, "No interactables here"),
|
|
82
|
+
react_2.default.createElement(TestComponent, null))));
|
|
83
|
+
await (0, react_1.waitFor)(() => {
|
|
84
|
+
expect(getByTestId("ready")).toBeInTheDocument();
|
|
85
|
+
const entry = capturedContexts.find((c) => c.name === "interactables");
|
|
86
|
+
expect(entry).toBeUndefined(); // Should be filtered out when helper returns null
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
test("context includes proper AI prompt with clear instructions", async () => {
|
|
90
|
+
const Note = ({ title }) => react_2.default.createElement("div", null, title);
|
|
91
|
+
const InteractableNote = (0, with_tambo_interactable_1.withTamboInteractable)(Note, {
|
|
92
|
+
componentName: "Note",
|
|
93
|
+
description: "A simple note",
|
|
94
|
+
propsSchema: zod_1.z.object({ title: zod_1.z.string() }),
|
|
95
|
+
});
|
|
96
|
+
let capturedContexts = [];
|
|
97
|
+
const TestComponent = () => {
|
|
98
|
+
const { getAdditionalContext } = (0, tambo_context_helpers_provider_1.useTamboContextHelpers)();
|
|
99
|
+
react_2.default.useEffect(() => {
|
|
100
|
+
let mounted = true;
|
|
101
|
+
getAdditionalContext().then((contexts) => {
|
|
102
|
+
if (mounted) {
|
|
103
|
+
capturedContexts = contexts;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return () => {
|
|
107
|
+
mounted = false;
|
|
108
|
+
};
|
|
109
|
+
}, [getAdditionalContext]);
|
|
110
|
+
return react_2.default.createElement("div", { "data-testid": "ready" }, "ready");
|
|
111
|
+
};
|
|
112
|
+
const { getByTestId } = (0, react_1.render)(wrapperWithProviders(react_2.default.createElement(tambo_interactable_provider_1.TamboInteractableProvider, null,
|
|
113
|
+
react_2.default.createElement(InteractableNote, { title: "test" }),
|
|
114
|
+
react_2.default.createElement(TestComponent, null))));
|
|
115
|
+
await (0, react_1.waitFor)(() => {
|
|
116
|
+
expect(getByTestId("ready")).toBeInTheDocument();
|
|
117
|
+
const entry = capturedContexts.find((c) => c.name === "interactables");
|
|
118
|
+
expect(entry?.context?.description).toContain("interactable components");
|
|
119
|
+
expect(entry?.context?.description).toContain("visible on the page");
|
|
120
|
+
expect(entry?.context?.description).toContain("you can read and modify");
|
|
121
|
+
expect(entry?.context?.description).toContain("tools to update");
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
test("includes component metadata in expected format", async () => {
|
|
125
|
+
const Note = ({ title, color = "white", }) => react_2.default.createElement("div", { className: `note-${color}` }, title);
|
|
126
|
+
const InteractableNote = (0, with_tambo_interactable_1.withTamboInteractable)(Note, {
|
|
127
|
+
componentName: "Note",
|
|
128
|
+
description: "A colorful note component",
|
|
129
|
+
propsSchema: zod_1.z.object({
|
|
130
|
+
title: zod_1.z.string(),
|
|
131
|
+
color: zod_1.z.string().optional(),
|
|
132
|
+
}),
|
|
133
|
+
});
|
|
134
|
+
let capturedContexts = [];
|
|
135
|
+
const TestComponent = () => {
|
|
136
|
+
const { getAdditionalContext } = (0, tambo_context_helpers_provider_1.useTamboContextHelpers)();
|
|
137
|
+
react_2.default.useEffect(() => {
|
|
138
|
+
let mounted = true;
|
|
139
|
+
getAdditionalContext().then((contexts) => {
|
|
140
|
+
if (mounted) {
|
|
141
|
+
capturedContexts = contexts;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
return () => {
|
|
145
|
+
mounted = false;
|
|
146
|
+
};
|
|
147
|
+
}, [getAdditionalContext]);
|
|
148
|
+
return react_2.default.createElement("div", { "data-testid": "ready" }, "ready");
|
|
149
|
+
};
|
|
150
|
+
const { getByTestId } = (0, react_1.render)(wrapperWithProviders(react_2.default.createElement(tambo_interactable_provider_1.TamboInteractableProvider, null,
|
|
151
|
+
react_2.default.createElement(InteractableNote, { title: "test note", color: "blue" }),
|
|
152
|
+
react_2.default.createElement(TestComponent, null))));
|
|
153
|
+
await (0, react_1.waitFor)(() => {
|
|
154
|
+
expect(getByTestId("ready")).toBeInTheDocument();
|
|
155
|
+
const entry = capturedContexts.find((c) => c.name === "interactables");
|
|
156
|
+
const component = entry.context.components[0];
|
|
157
|
+
expect(component).toMatchObject({
|
|
158
|
+
id: expect.any(String),
|
|
159
|
+
componentName: "Note",
|
|
160
|
+
description: "A colorful note component",
|
|
161
|
+
props: { title: "test note", color: "blue" },
|
|
162
|
+
propsSchema: "Available - use component-specific update tools",
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
test("snapshot hook returns immutable copies", async () => {
|
|
167
|
+
const Note = ({ title }) => react_2.default.createElement("div", null, title);
|
|
168
|
+
const InteractableNote = (0, with_tambo_interactable_1.withTamboInteractable)(Note, {
|
|
169
|
+
componentName: "Note",
|
|
170
|
+
description: "A note",
|
|
171
|
+
propsSchema: zod_1.z.object({ title: zod_1.z.string() }),
|
|
172
|
+
});
|
|
173
|
+
const TestComponent = () => {
|
|
174
|
+
const snapshot = (0, tambo_interactable_provider_1.useCurrentInteractablesSnapshot)();
|
|
175
|
+
const [testResult, setTestResult] = react_2.default.useState("pending");
|
|
176
|
+
react_2.default.useEffect(() => {
|
|
177
|
+
if (snapshot.length > 0) {
|
|
178
|
+
const originalLength = snapshot.length;
|
|
179
|
+
// Try to mutate the returned array and props
|
|
180
|
+
snapshot.push({
|
|
181
|
+
id: "fake",
|
|
182
|
+
name: "Fake",
|
|
183
|
+
description: "",
|
|
184
|
+
component: () => null,
|
|
185
|
+
props: {},
|
|
186
|
+
});
|
|
187
|
+
snapshot[0].props.title = "MUTATED";
|
|
188
|
+
// The mutations should succeed on the returned copy, proving it's a separate object
|
|
189
|
+
if (snapshot.length === originalLength + 1 &&
|
|
190
|
+
snapshot[0].props.title === "MUTATED") {
|
|
191
|
+
setTestResult("mutation-successful-but-isolated");
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
setTestResult("mutation-failed");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}, [snapshot]);
|
|
198
|
+
return react_2.default.createElement("div", { "data-testid": "test-result" }, testResult);
|
|
199
|
+
};
|
|
200
|
+
const { getByTestId } = (0, react_1.render)(wrapperWithProviders(react_2.default.createElement(tambo_interactable_provider_1.TamboInteractableProvider, null,
|
|
201
|
+
react_2.default.createElement(InteractableNote, { title: "immutable test" }),
|
|
202
|
+
react_2.default.createElement(TestComponent, null))));
|
|
203
|
+
await (0, react_1.waitFor)(() => {
|
|
204
|
+
const result = getByTestId("test-result").textContent;
|
|
205
|
+
expect(result).toBe("mutation-successful-but-isolated");
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
test("multiple interactables from same provider appear in context", async () => {
|
|
209
|
+
const Note = ({ title }) => react_2.default.createElement("div", null, title);
|
|
210
|
+
const Counter = ({ count }) => (react_2.default.createElement("div", null, count));
|
|
211
|
+
const InteractableNote = (0, with_tambo_interactable_1.withTamboInteractable)(Note, {
|
|
212
|
+
componentName: "Note",
|
|
213
|
+
description: "A note",
|
|
214
|
+
propsSchema: zod_1.z.object({ title: zod_1.z.string() }),
|
|
215
|
+
});
|
|
216
|
+
const InteractableCounter = (0, with_tambo_interactable_1.withTamboInteractable)(Counter, {
|
|
217
|
+
componentName: "Counter",
|
|
218
|
+
description: "A counter",
|
|
219
|
+
propsSchema: zod_1.z.object({ count: zod_1.z.number() }),
|
|
220
|
+
});
|
|
221
|
+
let capturedContexts = [];
|
|
222
|
+
const TestComponent = () => {
|
|
223
|
+
const { getAdditionalContext } = (0, tambo_context_helpers_provider_1.useTamboContextHelpers)();
|
|
224
|
+
react_2.default.useEffect(() => {
|
|
225
|
+
let mounted = true;
|
|
226
|
+
getAdditionalContext().then((contexts) => {
|
|
227
|
+
if (mounted) {
|
|
228
|
+
capturedContexts = contexts;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
return () => {
|
|
232
|
+
mounted = false;
|
|
233
|
+
};
|
|
234
|
+
}, [getAdditionalContext]);
|
|
235
|
+
return react_2.default.createElement("div", { "data-testid": "ready" }, "ready");
|
|
236
|
+
};
|
|
237
|
+
const { getByTestId } = (0, react_1.render)(wrapperWithProviders(react_2.default.createElement(tambo_interactable_provider_1.TamboInteractableProvider, null,
|
|
238
|
+
react_2.default.createElement(InteractableNote, { title: "first" }),
|
|
239
|
+
react_2.default.createElement(InteractableCounter, { count: 42 }),
|
|
240
|
+
react_2.default.createElement(TestComponent, null))));
|
|
241
|
+
await (0, react_1.waitFor)(() => {
|
|
242
|
+
expect(getByTestId("ready")).toBeInTheDocument();
|
|
243
|
+
const entry = capturedContexts.find((c) => c.name === "interactables");
|
|
244
|
+
expect(entry?.context?.components).toHaveLength(2);
|
|
245
|
+
const components = entry.context.components;
|
|
246
|
+
expect(components[0].componentName).toBe("Note");
|
|
247
|
+
expect(components[0].props).toEqual({ title: "first" });
|
|
248
|
+
expect(components[1].componentName).toBe("Counter");
|
|
249
|
+
expect(components[1].props).toEqual({ count: 42 });
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
test("can be disabled by returning null", async () => {
|
|
253
|
+
const Note = ({ title }) => react_2.default.createElement("div", null, title);
|
|
254
|
+
const InteractableNote = (0, with_tambo_interactable_1.withTamboInteractable)(Note, {
|
|
255
|
+
componentName: "Note",
|
|
256
|
+
description: "A note",
|
|
257
|
+
propsSchema: zod_1.z.object({ title: zod_1.z.string() }),
|
|
258
|
+
});
|
|
259
|
+
// Create a context helpers provider with a disabled interactables helper
|
|
260
|
+
const DisabledContextHelpers = ({ children, }) => (react_2.default.createElement(tambo_context_helpers_provider_1.TamboContextHelpersProvider, { contextHelpers: {
|
|
261
|
+
["interactables"]: () => null,
|
|
262
|
+
} }, children));
|
|
263
|
+
let capturedContexts = [];
|
|
264
|
+
const TestComponent = () => {
|
|
265
|
+
const { getAdditionalContext } = (0, tambo_context_helpers_provider_1.useTamboContextHelpers)();
|
|
266
|
+
react_2.default.useEffect(() => {
|
|
267
|
+
let mounted = true;
|
|
268
|
+
getAdditionalContext().then((contexts) => {
|
|
269
|
+
if (mounted) {
|
|
270
|
+
capturedContexts = contexts;
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
return () => {
|
|
274
|
+
mounted = false;
|
|
275
|
+
};
|
|
276
|
+
}, [getAdditionalContext]);
|
|
277
|
+
return react_2.default.createElement("div", { "data-testid": "ready" }, "ready");
|
|
278
|
+
};
|
|
279
|
+
const thread = {
|
|
280
|
+
id: "t-1",
|
|
281
|
+
projectId: "p-1",
|
|
282
|
+
createdAt: new Date().toISOString(),
|
|
283
|
+
updatedAt: new Date().toISOString(),
|
|
284
|
+
messages: [],
|
|
285
|
+
metadata: {},
|
|
286
|
+
};
|
|
287
|
+
const { getByTestId } = (0, react_1.render)(react_2.default.createElement(tambo_stubs_1.TamboStubProvider, { thread: thread, registerTool: () => { }, registerTools: () => { }, registerComponent: () => { }, addToolAssociation: () => { } },
|
|
288
|
+
react_2.default.createElement(DisabledContextHelpers, null,
|
|
289
|
+
react_2.default.createElement(tambo_interactable_provider_1.TamboInteractableProvider, null,
|
|
290
|
+
react_2.default.createElement(InteractableNote, { title: "should not appear" }),
|
|
291
|
+
react_2.default.createElement(TestComponent, null)))));
|
|
292
|
+
await (0, react_1.waitFor)(() => {
|
|
293
|
+
expect(getByTestId("ready")).toBeInTheDocument();
|
|
294
|
+
const entry = capturedContexts.find((c) => c.name === "interactables");
|
|
295
|
+
expect(entry).toBeUndefined(); // Should be filtered out when helper returns null
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
//# sourceMappingURL=tambo-interactables-additional-context.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tambo-interactables-additional-context.test.js","sourceRoot":"","sources":["../../../src/providers/__tests__/tambo-interactables-additional-context.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { render, waitFor } from \"@testing-library/react\";\nimport React from \"react\";\nimport { z } from \"zod\";\nimport {\n useTamboContextHelpers,\n TamboContextHelpersProvider,\n} from \"../tambo-context-helpers-provider\";\nimport {\n TamboInteractableProvider,\n useCurrentInteractablesSnapshot,\n} from \"../tambo-interactable-provider\";\nimport { TamboStubProvider } from \"../tambo-stubs\";\nimport { withTamboInteractable } from \"../hoc/with-tambo-interactable\";\n\nfunction wrapperWithProviders(children: React.ReactNode) {\n const thread = {\n id: \"t-1\",\n projectId: \"p-1\",\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n messages: [],\n metadata: {},\n } as any;\n\n return (\n <TamboStubProvider\n thread={thread}\n registerTool={() => {}}\n registerTools={() => {}}\n registerComponent={() => {}}\n addToolAssociation={() => {}}\n >\n <TamboContextHelpersProvider>{children}</TamboContextHelpersProvider>\n </TamboStubProvider>\n );\n}\n\ndescribe(\"Interactables AdditionalContext (provider-based)\", () => {\n test(\"registers default helper and returns payload with description and components\", async () => {\n const Note: React.FC<{ title: string }> = ({ title }) => <div>{title}</div>;\n\n const InteractableNote = withTamboInteractable(Note, {\n componentName: \"Note\",\n description: \"A note\",\n propsSchema: z.object({ title: z.string() }),\n });\n\n let capturedContexts: any[] = [];\n const TestComponent = () => {\n const { getAdditionalContext } = useTamboContextHelpers();\n\n React.useEffect(() => {\n let mounted = true;\n getAdditionalContext().then((contexts) => {\n if (mounted) {\n capturedContexts = contexts;\n }\n });\n return () => {\n mounted = false;\n };\n }, [getAdditionalContext]);\n\n return <div data-testid=\"ready\">ready</div>;\n };\n\n const { getByTestId } = render(\n wrapperWithProviders(\n <TamboInteractableProvider>\n <InteractableNote title=\"hello\" />\n <TestComponent />\n </TamboInteractableProvider>,\n ),\n );\n\n await waitFor(() => {\n expect(getByTestId(\"ready\")).toBeInTheDocument();\n const entry = capturedContexts.find(\n (c: any) => c.name === \"interactables\",\n );\n expect(entry).toBeDefined();\n expect(entry?.context?.description).toMatch(/interactable components/i);\n expect(Array.isArray(entry?.context?.components)).toBe(true);\n const comp = entry!.context.components[0];\n expect(comp.componentName).toBe(\"Note\");\n expect(comp.props).toEqual({ title: \"hello\" });\n });\n });\n\n test(\"returns null when no interactable components are present\", async () => {\n let capturedContexts: any[] = [];\n const TestComponent = () => {\n const { getAdditionalContext } = useTamboContextHelpers();\n\n React.useEffect(() => {\n let mounted = true;\n getAdditionalContext().then((contexts) => {\n if (mounted) {\n capturedContexts = contexts;\n }\n });\n return () => {\n mounted = false;\n };\n }, [getAdditionalContext]);\n\n return <div data-testid=\"ready\">ready</div>;\n };\n\n const { getByTestId } = render(\n wrapperWithProviders(\n <TamboInteractableProvider>\n <div>No interactables here</div>\n <TestComponent />\n </TamboInteractableProvider>,\n ),\n );\n\n await waitFor(() => {\n expect(getByTestId(\"ready\")).toBeInTheDocument();\n const entry = capturedContexts.find(\n (c: any) => c.name === \"interactables\",\n );\n expect(entry).toBeUndefined(); // Should be filtered out when helper returns null\n });\n });\n\n test(\"context includes proper AI prompt with clear instructions\", async () => {\n const Note: React.FC<{ title: string }> = ({ title }) => <div>{title}</div>;\n const InteractableNote = withTamboInteractable(Note, {\n componentName: \"Note\",\n description: \"A simple note\",\n propsSchema: z.object({ title: z.string() }),\n });\n\n let capturedContexts: any[] = [];\n const TestComponent = () => {\n const { getAdditionalContext } = useTamboContextHelpers();\n\n React.useEffect(() => {\n let mounted = true;\n getAdditionalContext().then((contexts) => {\n if (mounted) {\n capturedContexts = contexts;\n }\n });\n return () => {\n mounted = false;\n };\n }, [getAdditionalContext]);\n\n return <div data-testid=\"ready\">ready</div>;\n };\n\n const { getByTestId } = render(\n wrapperWithProviders(\n <TamboInteractableProvider>\n <InteractableNote title=\"test\" />\n <TestComponent />\n </TamboInteractableProvider>,\n ),\n );\n\n await waitFor(() => {\n expect(getByTestId(\"ready\")).toBeInTheDocument();\n const entry = capturedContexts.find(\n (c: any) => c.name === \"interactables\",\n );\n expect(entry?.context?.description).toContain(\"interactable components\");\n expect(entry?.context?.description).toContain(\"visible on the page\");\n expect(entry?.context?.description).toContain(\"you can read and modify\");\n expect(entry?.context?.description).toContain(\"tools to update\");\n });\n });\n\n test(\"includes component metadata in expected format\", async () => {\n const Note: React.FC<{ title: string; color?: string }> = ({\n title,\n color = \"white\",\n }) => <div className={`note-${color}`}>{title}</div>;\n\n const InteractableNote = withTamboInteractable(Note, {\n componentName: \"Note\",\n description: \"A colorful note component\",\n propsSchema: z.object({\n title: z.string(),\n color: z.string().optional(),\n }),\n });\n\n let capturedContexts: any[] = [];\n const TestComponent = () => {\n const { getAdditionalContext } = useTamboContextHelpers();\n\n React.useEffect(() => {\n let mounted = true;\n getAdditionalContext().then((contexts) => {\n if (mounted) {\n capturedContexts = contexts;\n }\n });\n return () => {\n mounted = false;\n };\n }, [getAdditionalContext]);\n\n return <div data-testid=\"ready\">ready</div>;\n };\n\n const { getByTestId } = render(\n wrapperWithProviders(\n <TamboInteractableProvider>\n <InteractableNote title=\"test note\" color=\"blue\" />\n <TestComponent />\n </TamboInteractableProvider>,\n ),\n );\n\n await waitFor(() => {\n expect(getByTestId(\"ready\")).toBeInTheDocument();\n const entry = capturedContexts.find(\n (c: any) => c.name === \"interactables\",\n );\n const component = entry!.context.components[0];\n\n expect(component).toMatchObject({\n id: expect.any(String),\n componentName: \"Note\",\n description: \"A colorful note component\",\n props: { title: \"test note\", color: \"blue\" },\n propsSchema: \"Available - use component-specific update tools\",\n });\n });\n });\n\n test(\"snapshot hook returns immutable copies\", async () => {\n const Note: React.FC<{ title: string }> = ({ title }) => <div>{title}</div>;\n const InteractableNote = withTamboInteractable(Note, {\n componentName: \"Note\",\n description: \"A note\",\n propsSchema: z.object({ title: z.string() }),\n });\n\n const TestComponent = () => {\n const snapshot = useCurrentInteractablesSnapshot();\n const [testResult, setTestResult] = React.useState<string>(\"pending\");\n\n React.useEffect(() => {\n if (snapshot.length > 0) {\n const originalLength = snapshot.length;\n\n // Try to mutate the returned array and props\n snapshot.push({\n id: \"fake\",\n name: \"Fake\",\n description: \"\",\n component: () => null,\n props: {},\n } as any);\n (snapshot[0].props as any).title = \"MUTATED\";\n\n // The mutations should succeed on the returned copy, proving it's a separate object\n if (\n snapshot.length === originalLength + 1 &&\n snapshot[0].props.title === \"MUTATED\"\n ) {\n setTestResult(\"mutation-successful-but-isolated\");\n } else {\n setTestResult(\"mutation-failed\");\n }\n }\n }, [snapshot]);\n\n return <div data-testid=\"test-result\">{testResult}</div>;\n };\n\n const { getByTestId } = render(\n wrapperWithProviders(\n <TamboInteractableProvider>\n <InteractableNote title=\"immutable test\" />\n <TestComponent />\n </TamboInteractableProvider>,\n ),\n );\n\n await waitFor(() => {\n const result = getByTestId(\"test-result\").textContent;\n expect(result).toBe(\"mutation-successful-but-isolated\");\n });\n });\n\n test(\"multiple interactables from same provider appear in context\", async () => {\n const Note: React.FC<{ title: string }> = ({ title }) => <div>{title}</div>;\n const Counter: React.FC<{ count: number }> = ({ count }) => (\n <div>{count}</div>\n );\n\n const InteractableNote = withTamboInteractable(Note, {\n componentName: \"Note\",\n description: \"A note\",\n propsSchema: z.object({ title: z.string() }),\n });\n\n const InteractableCounter = withTamboInteractable(Counter, {\n componentName: \"Counter\",\n description: \"A counter\",\n propsSchema: z.object({ count: z.number() }),\n });\n\n let capturedContexts: any[] = [];\n const TestComponent = () => {\n const { getAdditionalContext } = useTamboContextHelpers();\n\n React.useEffect(() => {\n let mounted = true;\n getAdditionalContext().then((contexts) => {\n if (mounted) {\n capturedContexts = contexts;\n }\n });\n return () => {\n mounted = false;\n };\n }, [getAdditionalContext]);\n\n return <div data-testid=\"ready\">ready</div>;\n };\n\n const { getByTestId } = render(\n wrapperWithProviders(\n <TamboInteractableProvider>\n <InteractableNote title=\"first\" />\n <InteractableCounter count={42} />\n <TestComponent />\n </TamboInteractableProvider>,\n ),\n );\n\n await waitFor(() => {\n expect(getByTestId(\"ready\")).toBeInTheDocument();\n const entry = capturedContexts.find(\n (c: any) => c.name === \"interactables\",\n );\n expect(entry?.context?.components).toHaveLength(2);\n\n const components = entry!.context.components;\n expect(components[0].componentName).toBe(\"Note\");\n expect(components[0].props).toEqual({ title: \"first\" });\n expect(components[1].componentName).toBe(\"Counter\");\n expect(components[1].props).toEqual({ count: 42 });\n });\n });\n\n test(\"can be disabled by returning null\", async () => {\n const Note: React.FC<{ title: string }> = ({ title }) => <div>{title}</div>;\n const InteractableNote = withTamboInteractable(Note, {\n componentName: \"Note\",\n description: \"A note\",\n propsSchema: z.object({ title: z.string() }),\n });\n\n // Create a context helpers provider with a disabled interactables helper\n const DisabledContextHelpers = ({\n children,\n }: {\n children: React.ReactNode;\n }) => (\n <TamboContextHelpersProvider\n contextHelpers={{\n [\"interactables\"]: () => null,\n }}\n >\n {children}\n </TamboContextHelpersProvider>\n );\n\n let capturedContexts: any[] = [];\n const TestComponent = () => {\n const { getAdditionalContext } = useTamboContextHelpers();\n\n React.useEffect(() => {\n let mounted = true;\n getAdditionalContext().then((contexts) => {\n if (mounted) {\n capturedContexts = contexts;\n }\n });\n return () => {\n mounted = false;\n };\n }, [getAdditionalContext]);\n\n return <div data-testid=\"ready\">ready</div>;\n };\n\n const thread = {\n id: \"t-1\",\n projectId: \"p-1\",\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n messages: [],\n metadata: {},\n } as any;\n\n const { getByTestId } = render(\n <TamboStubProvider\n thread={thread}\n registerTool={() => {}}\n registerTools={() => {}}\n registerComponent={() => {}}\n addToolAssociation={() => {}}\n >\n <DisabledContextHelpers>\n <TamboInteractableProvider>\n <InteractableNote title=\"should not appear\" />\n <TestComponent />\n </TamboInteractableProvider>\n </DisabledContextHelpers>\n </TamboStubProvider>,\n );\n\n await waitFor(() => {\n expect(getByTestId(\"ready\")).toBeInTheDocument();\n const entry = capturedContexts.find(\n (c: any) => c.name === \"interactables\",\n );\n expect(entry).toBeUndefined(); // Should be filtered out when helper returns null\n });\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tambo-registry-provider.test.d.ts","sourceRoot":"","sources":["../../../src/providers/__tests__/tambo-registry-provider.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = require("@testing-library/react");
|
|
7
|
+
const react_2 = __importDefault(require("react"));
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const tambo_registry_provider_1 = require("../tambo-registry-provider");
|
|
10
|
+
// Shared tool registry for all tests
|
|
11
|
+
const createMockTools = () => [
|
|
12
|
+
{
|
|
13
|
+
name: "test-tool-1",
|
|
14
|
+
description: "First test tool",
|
|
15
|
+
tool: jest.fn().mockResolvedValue("test-tool-1-result"),
|
|
16
|
+
toolSchema: zod_1.z
|
|
17
|
+
.function()
|
|
18
|
+
.args(zod_1.z.string().describe("input parameter"))
|
|
19
|
+
.returns(zod_1.z.string()),
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "test-tool-2",
|
|
23
|
+
description: "Second test tool",
|
|
24
|
+
tool: jest.fn().mockResolvedValue("test-tool-2-result"),
|
|
25
|
+
toolSchema: zod_1.z
|
|
26
|
+
.function()
|
|
27
|
+
.args(zod_1.z.number().describe("number parameter"))
|
|
28
|
+
.returns(zod_1.z.string()),
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
const createMockComponents = (tools) => [
|
|
32
|
+
{
|
|
33
|
+
name: "TestComponent",
|
|
34
|
+
component: () => react_2.default.createElement("div", null, "TestComponent"),
|
|
35
|
+
description: "Test component",
|
|
36
|
+
propsSchema: zod_1.z.object({
|
|
37
|
+
test: zod_1.z.string(),
|
|
38
|
+
}),
|
|
39
|
+
associatedTools: tools,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
describe("TamboRegistryProvider", () => {
|
|
43
|
+
const mockTools = createMockTools();
|
|
44
|
+
const mockComponents = createMockComponents(mockTools);
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
jest.clearAllMocks();
|
|
47
|
+
});
|
|
48
|
+
describe("Component and tool registration", () => {
|
|
49
|
+
it("should register components and tools correctly", () => {
|
|
50
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents }, children));
|
|
51
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
52
|
+
expect(result.current.componentList).toHaveProperty("TestComponent");
|
|
53
|
+
expect(result.current.toolRegistry).toHaveProperty("test-tool-1");
|
|
54
|
+
expect(result.current.toolRegistry).toHaveProperty("test-tool-2");
|
|
55
|
+
expect(result.current.componentToolAssociations).toHaveProperty("TestComponent");
|
|
56
|
+
expect(result.current.componentToolAssociations.TestComponent).toEqual([
|
|
57
|
+
"test-tool-1",
|
|
58
|
+
"test-tool-2",
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
it("should provide onCallUnregisteredTool in context", () => {
|
|
62
|
+
const mockonCallUnregisteredTool = jest
|
|
63
|
+
.fn()
|
|
64
|
+
.mockResolvedValue("test-result");
|
|
65
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents, onCallUnregisteredTool: mockonCallUnregisteredTool }, children));
|
|
66
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
67
|
+
// The onCallUnregisteredTool should be available in the context
|
|
68
|
+
expect(result.current.onCallUnregisteredTool).toBeDefined();
|
|
69
|
+
expect(typeof result.current.onCallUnregisteredTool).toBe("function");
|
|
70
|
+
});
|
|
71
|
+
it("should handle tool registration and association", () => {
|
|
72
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
|
|
73
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
74
|
+
// Register a new tool
|
|
75
|
+
(0, react_1.act)(() => {
|
|
76
|
+
result.current.registerTool(mockTools[0]);
|
|
77
|
+
});
|
|
78
|
+
expect(result.current.toolRegistry).toHaveProperty("test-tool-1");
|
|
79
|
+
// Register a new component
|
|
80
|
+
(0, react_1.act)(() => {
|
|
81
|
+
result.current.registerComponent(mockComponents[0]);
|
|
82
|
+
});
|
|
83
|
+
expect(result.current.componentList).toHaveProperty("TestComponent");
|
|
84
|
+
expect(result.current.componentToolAssociations).toHaveProperty("TestComponent");
|
|
85
|
+
});
|
|
86
|
+
it("should handle multiple tool registration", () => {
|
|
87
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
|
|
88
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
89
|
+
// Register multiple tools
|
|
90
|
+
(0, react_1.act)(() => {
|
|
91
|
+
result.current.registerTools(mockTools);
|
|
92
|
+
});
|
|
93
|
+
expect(result.current.toolRegistry).toHaveProperty("test-tool-1");
|
|
94
|
+
expect(result.current.toolRegistry).toHaveProperty("test-tool-2");
|
|
95
|
+
expect(Object.keys(result.current.toolRegistry)).toHaveLength(2);
|
|
96
|
+
});
|
|
97
|
+
it("should handle tool association with components", () => {
|
|
98
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents }, children));
|
|
99
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
100
|
+
// Add a new tool association
|
|
101
|
+
(0, react_1.act)(() => {
|
|
102
|
+
const newTool = {
|
|
103
|
+
name: "new-tool",
|
|
104
|
+
description: "New tool",
|
|
105
|
+
tool: jest.fn().mockResolvedValue("new-tool-result"),
|
|
106
|
+
toolSchema: zod_1.z
|
|
107
|
+
.function()
|
|
108
|
+
.args(zod_1.z.string().describe("input"))
|
|
109
|
+
.returns(zod_1.z.string()),
|
|
110
|
+
};
|
|
111
|
+
result.current.addToolAssociation("TestComponent", newTool);
|
|
112
|
+
});
|
|
113
|
+
expect(result.current.componentToolAssociations.TestComponent).toContain("new-tool");
|
|
114
|
+
});
|
|
115
|
+
it("should throw error when adding tool association to non-existent component", () => {
|
|
116
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
|
|
117
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
118
|
+
const newTool = {
|
|
119
|
+
name: "new-tool",
|
|
120
|
+
description: "New tool",
|
|
121
|
+
tool: jest.fn().mockResolvedValue("new-tool-result"),
|
|
122
|
+
toolSchema: zod_1.z
|
|
123
|
+
.function()
|
|
124
|
+
.args(zod_1.z.string().describe("input"))
|
|
125
|
+
.returns(zod_1.z.string()),
|
|
126
|
+
};
|
|
127
|
+
expect(() => {
|
|
128
|
+
(0, react_1.act)(() => {
|
|
129
|
+
result.current.addToolAssociation("NonExistentComponent", newTool);
|
|
130
|
+
});
|
|
131
|
+
}).toThrow("Component NonExistentComponent not found in registry");
|
|
132
|
+
});
|
|
133
|
+
it("should validate tool schemas and throw error for invalid schemas", () => {
|
|
134
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
|
|
135
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
136
|
+
const invalidTool = {
|
|
137
|
+
name: "invalid-tool",
|
|
138
|
+
description: "Invalid tool",
|
|
139
|
+
tool: jest.fn().mockResolvedValue("result"),
|
|
140
|
+
toolSchema: zod_1.z.record(zod_1.z.string()), // This should cause validation to fail
|
|
141
|
+
};
|
|
142
|
+
// This should throw during registration due to invalid schema
|
|
143
|
+
expect(() => {
|
|
144
|
+
(0, react_1.act)(() => {
|
|
145
|
+
result.current.registerTool(invalidTool);
|
|
146
|
+
});
|
|
147
|
+
}).toThrow('z.record() is not supported in toolSchema of tool "invalid-tool"');
|
|
148
|
+
});
|
|
149
|
+
it("should validate component schemas and throw error for invalid schemas", () => {
|
|
150
|
+
const invalidComponent = {
|
|
151
|
+
name: "InvalidComponent",
|
|
152
|
+
component: () => react_2.default.createElement("div", null, "Invalid"),
|
|
153
|
+
description: "Invalid component",
|
|
154
|
+
propsSchema: zod_1.z.record(zod_1.z.string()), // This should cause validation to fail
|
|
155
|
+
};
|
|
156
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
|
|
157
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
158
|
+
// This should throw during registration due to invalid schema
|
|
159
|
+
expect(() => {
|
|
160
|
+
(0, react_1.act)(() => {
|
|
161
|
+
result.current.registerComponent(invalidComponent);
|
|
162
|
+
});
|
|
163
|
+
}).toThrow('z.record() is not supported in propsSchema of component "InvalidComponent"');
|
|
164
|
+
});
|
|
165
|
+
it("should throw error when component has both propsSchema and propsDefinition", () => {
|
|
166
|
+
const invalidComponent = {
|
|
167
|
+
name: "InvalidComponent",
|
|
168
|
+
component: () => react_2.default.createElement("div", null, "Invalid"),
|
|
169
|
+
description: "Invalid component",
|
|
170
|
+
propsSchema: zod_1.z.object({ test: zod_1.z.string() }),
|
|
171
|
+
propsDefinition: { test: "string" }, // Both defined - should throw
|
|
172
|
+
};
|
|
173
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
|
|
174
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
175
|
+
expect(() => {
|
|
176
|
+
(0, react_1.act)(() => {
|
|
177
|
+
result.current.registerComponent(invalidComponent);
|
|
178
|
+
});
|
|
179
|
+
}).toThrow("Component InvalidComponent cannot have both propsSchema and propsDefinition defined");
|
|
180
|
+
});
|
|
181
|
+
it("should throw error when component has neither propsSchema nor propsDefinition", () => {
|
|
182
|
+
const invalidComponent = {
|
|
183
|
+
name: "InvalidComponent",
|
|
184
|
+
component: () => react_2.default.createElement("div", null, "Invalid"),
|
|
185
|
+
description: "Invalid component",
|
|
186
|
+
// Neither propsSchema nor propsDefinition defined - should throw
|
|
187
|
+
};
|
|
188
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null, children));
|
|
189
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
190
|
+
expect(() => {
|
|
191
|
+
(0, react_1.act)(() => {
|
|
192
|
+
result.current.registerComponent(invalidComponent);
|
|
193
|
+
});
|
|
194
|
+
}).toThrow("Component InvalidComponent must have either propsSchema (recommended) or propsDefinition defined");
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe("Tool call handling", () => {
|
|
198
|
+
it("should provide onCallUnregisteredTool callback for handling unknown tools", async () => {
|
|
199
|
+
const mockonCallUnregisteredTool = jest
|
|
200
|
+
.fn()
|
|
201
|
+
.mockResolvedValue("unknown-tool-result");
|
|
202
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents, onCallUnregisteredTool: mockonCallUnregisteredTool }, children));
|
|
203
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
204
|
+
// Verify the callback is available
|
|
205
|
+
expect(result.current.onCallUnregisteredTool).toBeDefined();
|
|
206
|
+
expect(typeof result.current.onCallUnregisteredTool).toBe("function");
|
|
207
|
+
// Simulate calling the unknown tool handler
|
|
208
|
+
const toolName = "unknown-tool";
|
|
209
|
+
const args = [{ parameterName: "input", parameterValue: "test-input" }];
|
|
210
|
+
await (0, react_1.act)(async () => {
|
|
211
|
+
if (result.current.onCallUnregisteredTool) {
|
|
212
|
+
await result.current.onCallUnregisteredTool(toolName, args);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
expect(mockonCallUnregisteredTool).toHaveBeenCalledWith(toolName, args);
|
|
216
|
+
});
|
|
217
|
+
it("should handle onCallUnregisteredTool being undefined", () => {
|
|
218
|
+
const wrapper = ({ children }) => (react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { components: mockComponents }, children));
|
|
219
|
+
const { result } = (0, react_1.renderHook)(() => (0, tambo_registry_provider_1.useTamboRegistry)(), { wrapper });
|
|
220
|
+
expect(result.current.onCallUnregisteredTool).toBeUndefined();
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
//# sourceMappingURL=tambo-registry-provider.test.js.map
|