@tambo-ai/react 0.68.0 → 0.69.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/README.md +1 -1
- package/dist/context-helpers/context-helpers.test.js +16 -4
- package/dist/context-helpers/context-helpers.test.js.map +1 -1
- package/dist/context-helpers/current-interactables-context-helper.d.ts +2 -2
- package/dist/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
- package/dist/context-helpers/current-interactables-context-helper.js +31 -15
- package/dist/context-helpers/current-interactables-context-helper.js.map +1 -1
- package/dist/context-helpers/registry.d.ts +2 -2
- package/dist/context-helpers/registry.d.ts.map +1 -1
- package/dist/context-helpers/registry.js.map +1 -1
- package/dist/context-helpers/types.d.ts +2 -2
- package/dist/context-helpers/types.d.ts.map +1 -1
- package/dist/context-helpers/types.js.map +1 -1
- package/dist/hooks/use-message-images.test.js +174 -37
- package/dist/hooks/use-message-images.test.js.map +1 -1
- package/dist/hooks/use-tambo-voice.d.ts +1 -1
- package/dist/hooks/use-tambo-voice.js +1 -1
- package/dist/hooks/use-tambo-voice.js.map +1 -1
- package/dist/hooks/use-tambo-voice.test.d.ts +2 -0
- package/dist/hooks/use-tambo-voice.test.d.ts.map +1 -0
- package/dist/hooks/use-tambo-voice.test.js +239 -0
- package/dist/hooks/use-tambo-voice.test.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/elicitation.d.ts.map +1 -1
- package/dist/mcp/elicitation.js +12 -0
- package/dist/mcp/elicitation.js.map +1 -1
- package/dist/mcp/elicitation.test.js +8 -1
- package/dist/mcp/elicitation.test.js.map +1 -1
- package/dist/mcp/mcp-client.d.ts +6 -10
- package/dist/mcp/mcp-client.d.ts.map +1 -1
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/mcp-hooks.d.ts +12 -60
- package/dist/mcp/mcp-hooks.d.ts.map +1 -1
- package/dist/mcp/mcp-hooks.js +90 -10
- package/dist/mcp/mcp-hooks.js.map +1 -1
- package/dist/mcp/mcp-hooks.test.js +423 -0
- package/dist/mcp/mcp-hooks.test.js.map +1 -1
- package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js +3 -0
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/mcp/tambo-mcp-provider.test.js +37 -0
- package/dist/mcp/tambo-mcp-provider.test.js.map +1 -1
- package/dist/model/component-metadata.d.ts +53 -20
- package/dist/model/component-metadata.d.ts.map +1 -1
- package/dist/model/component-metadata.js.map +1 -1
- package/dist/model/tambo-interactable.d.ts +6 -0
- package/dist/model/tambo-interactable.d.ts.map +1 -1
- package/dist/model/tambo-interactable.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.map +1 -1
- package/dist/providers/tambo-client-provider.d.ts +8 -0
- package/dist/providers/tambo-client-provider.d.ts.map +1 -1
- package/dist/providers/tambo-client-provider.js +10 -11
- package/dist/providers/tambo-client-provider.js.map +1 -1
- package/dist/providers/tambo-client-provider.test.d.ts +2 -0
- package/dist/providers/tambo-client-provider.test.d.ts.map +1 -0
- package/dist/providers/tambo-client-provider.test.js +208 -0
- package/dist/providers/tambo-client-provider.test.js.map +1 -0
- package/dist/providers/tambo-context-attachment-provider.d.ts +34 -92
- package/dist/providers/tambo-context-attachment-provider.d.ts.map +1 -1
- package/dist/providers/tambo-context-attachment-provider.js +62 -105
- package/dist/providers/tambo-context-attachment-provider.js.map +1 -1
- package/dist/providers/tambo-context-attachment-provider.test.js +229 -463
- package/dist/providers/tambo-context-attachment-provider.test.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.d.ts +2 -0
- package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/dist/providers/tambo-interactable-provider.js +29 -4
- package/dist/providers/tambo-interactable-provider.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.test.js +1 -1
- package/dist/providers/tambo-interactable-provider.test.js.map +1 -1
- package/dist/providers/tambo-interactables-additional-context.test.js +2 -5
- package/dist/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/dist/providers/tambo-provider.d.ts +2 -3
- package/dist/providers/tambo-provider.d.ts.map +1 -1
- package/dist/providers/tambo-provider.js +5 -6
- package/dist/providers/tambo-provider.js.map +1 -1
- package/dist/providers/tambo-registry-provider.test.js +16 -0
- package/dist/providers/tambo-registry-provider.test.js.map +1 -1
- package/dist/providers/tambo-registry-schema-compat.test.js +31 -0
- package/dist/providers/tambo-registry-schema-compat.test.js.map +1 -1
- package/dist/providers/tambo-thread-input-provider.d.ts +1 -1
- package/dist/providers/tambo-thread-input-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-input-provider.js +5 -1
- package/dist/providers/tambo-thread-input-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider-initial-messages.test.js +84 -2
- package/dist/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +56 -43
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.test.js +456 -262
- package/dist/providers/tambo-thread-provider.test.js.map +1 -1
- package/dist/schema/json-schema.js +29 -29
- package/dist/schema/json-schema.js.map +1 -1
- package/dist/schema/schema.test.js +237 -0
- package/dist/schema/schema.test.js.map +1 -1
- package/dist/schema/standard-schema.d.ts +1 -0
- package/dist/schema/standard-schema.d.ts.map +1 -1
- package/dist/schema/standard-schema.js +18 -13
- package/dist/schema/standard-schema.js.map +1 -1
- package/dist/schema/standard-schema.test.d.ts +2 -0
- package/dist/schema/standard-schema.test.d.ts.map +1 -0
- package/dist/schema/standard-schema.test.js +165 -0
- package/dist/schema/standard-schema.test.js.map +1 -0
- package/dist/schema/validate.test.js +149 -0
- package/dist/schema/validate.test.js.map +1 -1
- package/dist/schema/zod.d.ts +7 -4
- package/dist/schema/zod.d.ts.map +1 -1
- package/dist/schema/zod.js +65 -22
- package/dist/schema/zod.js.map +1 -1
- package/dist/schema/zod.test.js +112 -0
- package/dist/schema/zod.test.js.map +1 -1
- package/dist/testing/tools.d.ts +4 -1
- package/dist/testing/tools.d.ts.map +1 -1
- package/dist/testing/tools.js +6 -1
- package/dist/testing/tools.js.map +1 -1
- package/dist/util/generate-component.d.ts.map +1 -1
- package/dist/util/generate-component.js +18 -3
- package/dist/util/generate-component.js.map +1 -1
- package/dist/util/generate-component.test.d.ts +2 -0
- package/dist/util/generate-component.test.d.ts.map +1 -0
- package/dist/util/generate-component.test.js +340 -0
- package/dist/util/generate-component.test.js.map +1 -0
- package/dist/util/is-promise.d.ts +9 -0
- package/dist/util/is-promise.d.ts.map +1 -0
- package/dist/util/is-promise.js +20 -0
- package/dist/util/is-promise.js.map +1 -0
- package/dist/util/is-promise.test.d.ts +2 -0
- package/dist/util/is-promise.test.d.ts.map +1 -0
- package/dist/util/is-promise.test.js +48 -0
- package/dist/util/is-promise.test.js.map +1 -0
- package/dist/util/query-utils.test.d.ts +2 -0
- package/dist/util/query-utils.test.d.ts.map +1 -0
- package/dist/util/query-utils.test.js +382 -0
- package/dist/util/query-utils.test.js.map +1 -0
- package/dist/util/registry-validators.d.ts.map +1 -1
- package/dist/util/registry-validators.js +7 -0
- package/dist/util/registry-validators.js.map +1 -1
- package/dist/util/registry-validators.test.js +57 -0
- package/dist/util/registry-validators.test.js.map +1 -1
- package/dist/util/registry.d.ts.map +1 -1
- package/dist/util/registry.js +9 -0
- package/dist/util/registry.js.map +1 -1
- package/dist/util/registry.test.js +323 -1
- package/dist/util/registry.test.js.map +1 -1
- package/dist/util/resource-validators.test.d.ts +2 -0
- package/dist/util/resource-validators.test.d.ts.map +1 -0
- package/dist/util/resource-validators.test.js +90 -0
- package/dist/util/resource-validators.test.js.map +1 -0
- package/dist/util/tool-caller.d.ts +2 -2
- package/dist/util/tool-caller.d.ts.map +1 -1
- package/dist/util/tool-caller.js +8 -8
- package/dist/util/tool-caller.js.map +1 -1
- package/dist/util/validate-component-name.test.d.ts +2 -0
- package/dist/util/validate-component-name.test.d.ts.map +1 -0
- package/dist/util/validate-component-name.test.js +35 -0
- package/dist/util/validate-component-name.test.js.map +1 -0
- package/esm/context-helpers/context-helpers.test.js +16 -4
- package/esm/context-helpers/context-helpers.test.js.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.d.ts +2 -2
- package/esm/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.js +31 -15
- package/esm/context-helpers/current-interactables-context-helper.js.map +1 -1
- package/esm/context-helpers/registry.d.ts +2 -2
- package/esm/context-helpers/registry.d.ts.map +1 -1
- package/esm/context-helpers/registry.js.map +1 -1
- package/esm/context-helpers/types.d.ts +2 -2
- package/esm/context-helpers/types.d.ts.map +1 -1
- package/esm/context-helpers/types.js.map +1 -1
- package/esm/hooks/use-message-images.test.js +174 -37
- package/esm/hooks/use-message-images.test.js.map +1 -1
- package/esm/hooks/use-tambo-voice.d.ts +1 -1
- package/esm/hooks/use-tambo-voice.js +1 -1
- package/esm/hooks/use-tambo-voice.js.map +1 -1
- package/esm/hooks/use-tambo-voice.test.d.ts +2 -0
- package/esm/hooks/use-tambo-voice.test.d.ts.map +1 -0
- package/esm/hooks/use-tambo-voice.test.js +234 -0
- package/esm/hooks/use-tambo-voice.test.js.map +1 -0
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js.map +1 -1
- package/esm/mcp/elicitation.d.ts.map +1 -1
- package/esm/mcp/elicitation.js +12 -0
- package/esm/mcp/elicitation.js.map +1 -1
- package/esm/mcp/elicitation.test.js +8 -1
- package/esm/mcp/elicitation.test.js.map +1 -1
- package/esm/mcp/mcp-client.d.ts +6 -10
- package/esm/mcp/mcp-client.d.ts.map +1 -1
- package/esm/mcp/mcp-client.js.map +1 -1
- package/esm/mcp/mcp-hooks.d.ts +12 -60
- package/esm/mcp/mcp-hooks.d.ts.map +1 -1
- package/esm/mcp/mcp-hooks.js +57 -10
- package/esm/mcp/mcp-hooks.js.map +1 -1
- package/esm/mcp/mcp-hooks.test.js +423 -0
- package/esm/mcp/mcp-hooks.test.js.map +1 -1
- package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js +3 -0
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/mcp/tambo-mcp-provider.test.js +37 -0
- package/esm/mcp/tambo-mcp-provider.test.js.map +1 -1
- package/esm/model/component-metadata.d.ts +53 -20
- package/esm/model/component-metadata.d.ts.map +1 -1
- package/esm/model/component-metadata.js.map +1 -1
- package/esm/model/tambo-interactable.d.ts +6 -0
- package/esm/model/tambo-interactable.d.ts.map +1 -1
- package/esm/model/tambo-interactable.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.map +1 -1
- package/esm/providers/tambo-client-provider.d.ts +8 -0
- package/esm/providers/tambo-client-provider.d.ts.map +1 -1
- package/esm/providers/tambo-client-provider.js +11 -12
- package/esm/providers/tambo-client-provider.js.map +1 -1
- package/esm/providers/tambo-client-provider.test.d.ts +2 -0
- package/esm/providers/tambo-client-provider.test.d.ts.map +1 -0
- package/esm/providers/tambo-client-provider.test.js +203 -0
- package/esm/providers/tambo-client-provider.test.js.map +1 -0
- package/esm/providers/tambo-context-attachment-provider.d.ts +34 -92
- package/esm/providers/tambo-context-attachment-provider.d.ts.map +1 -1
- package/esm/providers/tambo-context-attachment-provider.js +63 -106
- package/esm/providers/tambo-context-attachment-provider.js.map +1 -1
- package/esm/providers/tambo-context-attachment-provider.test.js +230 -464
- package/esm/providers/tambo-context-attachment-provider.test.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.d.ts +2 -0
- package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/esm/providers/tambo-interactable-provider.js +29 -4
- package/esm/providers/tambo-interactable-provider.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.test.js +1 -1
- package/esm/providers/tambo-interactable-provider.test.js.map +1 -1
- package/esm/providers/tambo-interactables-additional-context.test.js +2 -5
- package/esm/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/esm/providers/tambo-provider.d.ts +2 -3
- package/esm/providers/tambo-provider.d.ts.map +1 -1
- package/esm/providers/tambo-provider.js +5 -6
- package/esm/providers/tambo-provider.js.map +1 -1
- package/esm/providers/tambo-registry-provider.test.js +16 -0
- package/esm/providers/tambo-registry-provider.test.js.map +1 -1
- package/esm/providers/tambo-registry-schema-compat.test.js +31 -0
- package/esm/providers/tambo-registry-schema-compat.test.js.map +1 -1
- package/esm/providers/tambo-thread-input-provider.d.ts +1 -1
- package/esm/providers/tambo-thread-input-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-input-provider.js +5 -1
- package/esm/providers/tambo-thread-input-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider-initial-messages.test.js +84 -2
- package/esm/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +56 -43
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.test.js +456 -262
- package/esm/providers/tambo-thread-provider.test.js.map +1 -1
- package/esm/schema/json-schema.js +1 -1
- package/esm/schema/json-schema.js.map +1 -1
- package/esm/schema/schema.test.js +238 -1
- package/esm/schema/schema.test.js.map +1 -1
- package/esm/schema/standard-schema.d.ts +1 -0
- package/esm/schema/standard-schema.d.ts.map +1 -1
- package/esm/schema/standard-schema.js +18 -13
- package/esm/schema/standard-schema.js.map +1 -1
- package/esm/schema/standard-schema.test.d.ts +2 -0
- package/esm/schema/standard-schema.test.d.ts.map +1 -0
- package/esm/schema/standard-schema.test.js +130 -0
- package/esm/schema/standard-schema.test.js.map +1 -0
- package/esm/schema/validate.test.js +149 -0
- package/esm/schema/validate.test.js.map +1 -1
- package/esm/schema/zod.d.ts +7 -4
- package/esm/schema/zod.d.ts.map +1 -1
- package/esm/schema/zod.js +65 -22
- package/esm/schema/zod.js.map +1 -1
- package/esm/schema/zod.test.js +113 -1
- package/esm/schema/zod.test.js.map +1 -1
- package/esm/testing/tools.d.ts +4 -1
- package/esm/testing/tools.d.ts.map +1 -1
- package/esm/testing/tools.js +6 -1
- package/esm/testing/tools.js.map +1 -1
- package/esm/util/generate-component.d.ts.map +1 -1
- package/esm/util/generate-component.js +18 -3
- package/esm/util/generate-component.js.map +1 -1
- package/esm/util/generate-component.test.d.ts +2 -0
- package/esm/util/generate-component.test.d.ts.map +1 -0
- package/esm/util/generate-component.test.js +302 -0
- package/esm/util/generate-component.test.js.map +1 -0
- package/esm/util/is-promise.d.ts +9 -0
- package/esm/util/is-promise.d.ts.map +1 -0
- package/esm/util/is-promise.js +17 -0
- package/esm/util/is-promise.js.map +1 -0
- package/esm/util/is-promise.test.d.ts +2 -0
- package/esm/util/is-promise.test.d.ts.map +1 -0
- package/esm/util/is-promise.test.js +46 -0
- package/esm/util/is-promise.test.js.map +1 -0
- package/esm/util/query-utils.test.d.ts +2 -0
- package/esm/util/query-utils.test.d.ts.map +1 -0
- package/esm/util/query-utils.test.js +380 -0
- package/esm/util/query-utils.test.js.map +1 -0
- package/esm/util/registry-validators.d.ts.map +1 -1
- package/esm/util/registry-validators.js +7 -0
- package/esm/util/registry-validators.js.map +1 -1
- package/esm/util/registry-validators.test.js +57 -0
- package/esm/util/registry-validators.test.js.map +1 -1
- package/esm/util/registry.d.ts.map +1 -1
- package/esm/util/registry.js +9 -0
- package/esm/util/registry.js.map +1 -1
- package/esm/util/registry.test.js +324 -2
- package/esm/util/registry.test.js.map +1 -1
- package/esm/util/resource-validators.test.d.ts +2 -0
- package/esm/util/resource-validators.test.d.ts.map +1 -0
- package/esm/util/resource-validators.test.js +88 -0
- package/esm/util/resource-validators.test.js.map +1 -0
- package/esm/util/tool-caller.d.ts +2 -2
- package/esm/util/tool-caller.d.ts.map +1 -1
- package/esm/util/tool-caller.js +8 -8
- package/esm/util/tool-caller.js.map +1 -1
- package/esm/util/validate-component-name.test.d.ts +2 -0
- package/esm/util/validate-component-name.test.d.ts.map +1 -0
- package/esm/util/validate-component-name.test.js +33 -0
- package/esm/util/validate-component-name.test.js.map +1 -0
- package/package.json +15 -23
- package/dist/schema/alias.d.ts +0 -3
- package/dist/schema/alias.d.ts.map +0 -1
- package/dist/schema/alias.js +0 -6
- package/dist/schema/alias.js.map +0 -1
- package/esm/schema/alias.d.ts +0 -3
- package/esm/schema/alias.d.ts.map +0 -1
- package/esm/schema/alias.js +0 -13
- package/esm/schema/alias.js.map +0 -1
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { TamboContextAttachmentProvider, useTamboContextAttachment, } from "./tambo-context-attachment-provider";
|
|
4
|
-
import {
|
|
4
|
+
import { useTamboContextHelpers } from "./tambo-context-helpers-provider";
|
|
5
|
+
// Mock the context helpers provider
|
|
6
|
+
jest.mock("./tambo-context-helpers-provider");
|
|
7
|
+
const mockAddContextHelper = jest.fn();
|
|
8
|
+
const mockRemoveContextHelper = jest.fn();
|
|
9
|
+
const CONTEXT_ATTACHMENTS_HELPER_KEY = "contextAttachments";
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
useTamboContextHelpers.mockReturnValue({
|
|
13
|
+
addContextHelper: mockAddContextHelper,
|
|
14
|
+
removeContextHelper: mockRemoveContextHelper,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
5
17
|
/**
|
|
6
18
|
* Test suite for TamboContextAttachmentProvider
|
|
7
19
|
*
|
|
8
20
|
* Tests the context attachment feature which allows:
|
|
9
|
-
* -
|
|
10
|
-
* - Automatic context
|
|
11
|
-
* - Custom suggestions that override auto-generated ones
|
|
12
|
-
* - Dynamic context data customization via getContextHelperData
|
|
21
|
+
* - Adding context attachments that will be sent with the next message
|
|
22
|
+
* - Automatic registration/deregistration of context helpers
|
|
13
23
|
*/
|
|
14
24
|
describe("TamboContextAttachmentProvider", () => {
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
jest.clearAllMocks();
|
|
17
|
-
});
|
|
18
25
|
/**
|
|
19
|
-
* Base wrapper with
|
|
20
|
-
*
|
|
21
|
-
* @param getContextHelperData - Optional custom function to get context helper data
|
|
22
|
-
* @returns A React component that wraps children with the necessary providers
|
|
26
|
+
* Base wrapper with TamboContextAttachmentProvider
|
|
27
|
+
* @returns A React component that wraps children with the provider
|
|
23
28
|
*/
|
|
24
|
-
const createWrapper = (
|
|
25
|
-
const Wrapper = ({ children }) => (React.createElement(
|
|
26
|
-
React.createElement(TamboContextAttachmentProvider, { getContextHelperData: getContextHelperData }, children)));
|
|
29
|
+
const createWrapper = () => {
|
|
30
|
+
const Wrapper = ({ children }) => (React.createElement(TamboContextAttachmentProvider, null, children));
|
|
27
31
|
Wrapper.displayName = "TestWrapper";
|
|
28
32
|
return Wrapper;
|
|
29
33
|
};
|
|
@@ -39,8 +43,6 @@ describe("TamboContextAttachmentProvider", () => {
|
|
|
39
43
|
expect(result.current).toHaveProperty("addContextAttachment");
|
|
40
44
|
expect(result.current).toHaveProperty("removeContextAttachment");
|
|
41
45
|
expect(result.current).toHaveProperty("clearContextAttachments");
|
|
42
|
-
expect(result.current).toHaveProperty("customSuggestions");
|
|
43
|
-
expect(result.current).toHaveProperty("setCustomSuggestions");
|
|
44
46
|
});
|
|
45
47
|
/**
|
|
46
48
|
* Hook should throw error when used outside of provider
|
|
@@ -58,154 +60,318 @@ describe("TamboContextAttachmentProvider", () => {
|
|
|
58
60
|
});
|
|
59
61
|
describe("Adding Context Attachments", () => {
|
|
60
62
|
/**
|
|
61
|
-
* Should add a context attachment
|
|
63
|
+
* Should add a context attachment and register/update the merged context helper
|
|
62
64
|
*/
|
|
63
|
-
it("should add a context attachment", () => {
|
|
65
|
+
it("should add a context attachment", async () => {
|
|
64
66
|
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
65
67
|
wrapper: createWrapper(),
|
|
66
68
|
});
|
|
69
|
+
let attachment;
|
|
67
70
|
act(() => {
|
|
68
|
-
result.current.addContextAttachment({
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
attachment = result.current.addContextAttachment({
|
|
72
|
+
context: "selectedFile",
|
|
73
|
+
displayName: "Button.tsx",
|
|
74
|
+
type: "file",
|
|
71
75
|
});
|
|
72
76
|
});
|
|
73
77
|
expect(result.current.attachments).toHaveLength(1);
|
|
74
78
|
expect(result.current.attachments[0]).toMatchObject({
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
displayName: "Button.tsx",
|
|
80
|
+
context: "selectedFile",
|
|
81
|
+
type: "file",
|
|
77
82
|
});
|
|
78
83
|
expect(result.current.attachments[0].id).toBeDefined();
|
|
84
|
+
expect(attachment.id).toBe(result.current.attachments[0].id);
|
|
85
|
+
// Wait for useEffect to run and register the merged helper with the attachment
|
|
86
|
+
await waitFor(() => {
|
|
87
|
+
const lastCall = mockAddContextHelper.mock.calls
|
|
88
|
+
.filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
|
|
89
|
+
.pop();
|
|
90
|
+
if (lastCall) {
|
|
91
|
+
const helperFn = lastCall[1];
|
|
92
|
+
const helperResult = helperFn();
|
|
93
|
+
expect(helperResult).not.toBeNull();
|
|
94
|
+
expect(helperResult).toHaveLength(1);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
throw new Error("Helper not registered");
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// Verify the helper function returns the merged attachments array
|
|
101
|
+
const helperFn = mockAddContextHelper.mock.calls
|
|
102
|
+
.filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
|
|
103
|
+
.pop()?.[1];
|
|
104
|
+
expect(helperFn).toBeDefined();
|
|
105
|
+
const helperResult = helperFn();
|
|
106
|
+
expect(helperResult).toEqual([
|
|
107
|
+
{
|
|
108
|
+
id: attachment.id,
|
|
109
|
+
displayName: "Button.tsx",
|
|
110
|
+
context: "selectedFile",
|
|
111
|
+
type: "file",
|
|
112
|
+
},
|
|
113
|
+
]);
|
|
79
114
|
});
|
|
80
115
|
/**
|
|
81
|
-
* Should add
|
|
116
|
+
* Should add a context attachment without displayName
|
|
82
117
|
*/
|
|
83
|
-
it("should add
|
|
118
|
+
it("should add a context attachment without displayName", () => {
|
|
119
|
+
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
120
|
+
wrapper: createWrapper(),
|
|
121
|
+
});
|
|
122
|
+
let attachment;
|
|
123
|
+
act(() => {
|
|
124
|
+
attachment = result.current.addContextAttachment({
|
|
125
|
+
context: "selectedFile",
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
expect(result.current.attachments).toHaveLength(1);
|
|
129
|
+
expect(result.current.attachments[0]).toMatchObject({
|
|
130
|
+
context: "selectedFile",
|
|
131
|
+
});
|
|
132
|
+
expect(result.current.attachments[0].displayName).toBeUndefined();
|
|
133
|
+
expect(attachment.id).toBe(result.current.attachments[0].id);
|
|
134
|
+
});
|
|
135
|
+
/**
|
|
136
|
+
* Should add multiple different context attachments and update the merged helper
|
|
137
|
+
*/
|
|
138
|
+
it("should add multiple context attachments", async () => {
|
|
84
139
|
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
85
140
|
wrapper: createWrapper(),
|
|
86
141
|
});
|
|
87
142
|
act(() => {
|
|
88
143
|
result.current.addContextAttachment({
|
|
89
|
-
|
|
144
|
+
context: "file1",
|
|
145
|
+
displayName: "Button.tsx",
|
|
146
|
+
type: "file",
|
|
90
147
|
});
|
|
91
148
|
result.current.addContextAttachment({
|
|
92
|
-
|
|
149
|
+
context: "file2",
|
|
150
|
+
displayName: "Card.tsx",
|
|
151
|
+
type: "file",
|
|
93
152
|
});
|
|
94
153
|
});
|
|
95
154
|
expect(result.current.attachments).toHaveLength(2);
|
|
96
|
-
expect(result.current.attachments[0].
|
|
97
|
-
expect(result.current.attachments[1].
|
|
155
|
+
expect(result.current.attachments[0].displayName).toBe("Button.tsx");
|
|
156
|
+
expect(result.current.attachments[1].displayName).toBe("Card.tsx");
|
|
157
|
+
// Wait for useEffect to run and update the merged helper
|
|
158
|
+
await waitFor(() => {
|
|
159
|
+
// The helper should be called/updated for each attachment change
|
|
160
|
+
expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
|
|
161
|
+
});
|
|
162
|
+
// Verify the helper function returns all attachments
|
|
163
|
+
const helperFn = mockAddContextHelper.mock.calls
|
|
164
|
+
.filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
|
|
165
|
+
.pop()?.[1];
|
|
166
|
+
expect(helperFn).toBeDefined();
|
|
167
|
+
const helperResult = helperFn();
|
|
168
|
+
expect(helperResult).toHaveLength(2);
|
|
169
|
+
expect(helperResult[0].displayName).toBe("Button.tsx");
|
|
170
|
+
expect(helperResult[1].displayName).toBe("Card.tsx");
|
|
98
171
|
});
|
|
99
172
|
/**
|
|
100
|
-
* Should
|
|
173
|
+
* Should allow multiple attachments with the same context value
|
|
101
174
|
*/
|
|
102
|
-
it("should
|
|
175
|
+
it("should allow multiple attachments with same context value", async () => {
|
|
103
176
|
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
104
177
|
wrapper: createWrapper(),
|
|
105
178
|
});
|
|
106
179
|
act(() => {
|
|
107
180
|
result.current.addContextAttachment({
|
|
108
|
-
|
|
181
|
+
context: "selectedFile",
|
|
182
|
+
displayName: "Button.tsx",
|
|
183
|
+
type: "file",
|
|
109
184
|
});
|
|
110
185
|
result.current.addContextAttachment({
|
|
111
|
-
|
|
186
|
+
context: "selectedFile",
|
|
187
|
+
displayName: "Card.tsx",
|
|
188
|
+
type: "file",
|
|
112
189
|
});
|
|
113
190
|
});
|
|
114
|
-
expect(result.current.attachments).toHaveLength(
|
|
191
|
+
expect(result.current.attachments).toHaveLength(2);
|
|
192
|
+
// Wait for useEffect to update the merged helper
|
|
193
|
+
await waitFor(() => {
|
|
194
|
+
expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
|
|
195
|
+
});
|
|
196
|
+
// Verify both attachments are included in the merged result
|
|
197
|
+
const helperFn = mockAddContextHelper.mock.calls
|
|
198
|
+
.filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
|
|
199
|
+
.pop()?.[1];
|
|
200
|
+
expect(helperFn).toBeDefined();
|
|
201
|
+
const helperResult = helperFn();
|
|
202
|
+
expect(helperResult).toHaveLength(2);
|
|
115
203
|
});
|
|
116
204
|
/**
|
|
117
|
-
* Should support optional
|
|
205
|
+
* Should support optional type property
|
|
118
206
|
*/
|
|
119
|
-
it("should support
|
|
207
|
+
it("should support optional type property", () => {
|
|
120
208
|
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
121
209
|
wrapper: createWrapper(),
|
|
122
210
|
});
|
|
123
|
-
const icon = React.createElement("span", null, "\uD83D\uDCC4");
|
|
124
211
|
act(() => {
|
|
125
212
|
result.current.addContextAttachment({
|
|
126
|
-
|
|
127
|
-
|
|
213
|
+
context: "file1",
|
|
214
|
+
displayName: "Button.tsx",
|
|
215
|
+
type: "file",
|
|
216
|
+
});
|
|
217
|
+
result.current.addContextAttachment({
|
|
218
|
+
context: "page1",
|
|
219
|
+
displayName: "Dashboard",
|
|
220
|
+
type: "page",
|
|
128
221
|
});
|
|
129
222
|
});
|
|
130
|
-
expect(result.current.attachments[0].
|
|
223
|
+
expect(result.current.attachments[0].type).toBe("file");
|
|
224
|
+
expect(result.current.attachments[1].type).toBe("page");
|
|
131
225
|
});
|
|
132
226
|
});
|
|
133
227
|
describe("Removing Context Attachments", () => {
|
|
134
228
|
/**
|
|
135
|
-
* Should remove a specific attachment by ID
|
|
229
|
+
* Should remove a specific context attachment by ID and update the merged helper
|
|
136
230
|
*/
|
|
137
|
-
it("should remove context attachment by ID", () => {
|
|
231
|
+
it("should remove context attachment by ID", async () => {
|
|
138
232
|
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
139
233
|
wrapper: createWrapper(),
|
|
140
234
|
});
|
|
235
|
+
let attachmentId = "";
|
|
141
236
|
act(() => {
|
|
142
|
-
result.current.addContextAttachment({
|
|
143
|
-
|
|
237
|
+
const attachment = result.current.addContextAttachment({
|
|
238
|
+
context: "selectedFile",
|
|
239
|
+
displayName: "Button.tsx",
|
|
144
240
|
});
|
|
241
|
+
attachmentId = attachment.id;
|
|
145
242
|
});
|
|
146
243
|
expect(result.current.attachments).toHaveLength(1);
|
|
147
|
-
|
|
244
|
+
// Wait for initial helper registration
|
|
245
|
+
await waitFor(() => {
|
|
246
|
+
expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
|
|
247
|
+
});
|
|
148
248
|
act(() => {
|
|
149
249
|
result.current.removeContextAttachment(attachmentId);
|
|
150
250
|
});
|
|
151
251
|
expect(result.current.attachments).toHaveLength(0);
|
|
252
|
+
// Wait for useEffect to update the helper (should return null when empty)
|
|
253
|
+
await waitFor(() => {
|
|
254
|
+
const lastCall = mockAddContextHelper.mock.calls
|
|
255
|
+
.filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
|
|
256
|
+
.pop();
|
|
257
|
+
if (lastCall) {
|
|
258
|
+
const helperFn = lastCall[1];
|
|
259
|
+
expect(helperFn()).toBeNull();
|
|
260
|
+
}
|
|
261
|
+
});
|
|
152
262
|
});
|
|
153
263
|
/**
|
|
154
264
|
* Should only remove the specified attachment when multiple exist
|
|
155
265
|
*/
|
|
156
|
-
it("should only remove specified attachment", () => {
|
|
266
|
+
it("should only remove specified attachment", async () => {
|
|
157
267
|
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
158
268
|
wrapper: createWrapper(),
|
|
159
269
|
});
|
|
270
|
+
let firstId = "";
|
|
160
271
|
act(() => {
|
|
161
|
-
result.current.addContextAttachment({
|
|
162
|
-
|
|
272
|
+
const first = result.current.addContextAttachment({
|
|
273
|
+
context: "file1",
|
|
274
|
+
displayName: "First.tsx",
|
|
275
|
+
});
|
|
276
|
+
result.current.addContextAttachment({
|
|
277
|
+
context: "file2",
|
|
278
|
+
displayName: "Second.tsx",
|
|
279
|
+
});
|
|
280
|
+
firstId = first.id;
|
|
163
281
|
});
|
|
164
282
|
expect(result.current.attachments).toHaveLength(2);
|
|
165
|
-
|
|
283
|
+
// Wait for initial helper registration
|
|
284
|
+
await waitFor(() => {
|
|
285
|
+
expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
|
|
286
|
+
});
|
|
166
287
|
act(() => {
|
|
167
288
|
result.current.removeContextAttachment(firstId);
|
|
168
289
|
});
|
|
169
290
|
expect(result.current.attachments).toHaveLength(1);
|
|
170
|
-
expect(result.current.attachments[0].
|
|
291
|
+
expect(result.current.attachments[0].displayName).toBe("Second.tsx");
|
|
292
|
+
// Wait for useEffect to update the helper with remaining attachment
|
|
293
|
+
await waitFor(() => {
|
|
294
|
+
const lastCall = mockAddContextHelper.mock.calls
|
|
295
|
+
.filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
|
|
296
|
+
.pop();
|
|
297
|
+
if (lastCall) {
|
|
298
|
+
const helperFn = lastCall[1];
|
|
299
|
+
const helperResult = helperFn();
|
|
300
|
+
expect(helperResult).toHaveLength(1);
|
|
301
|
+
expect(helperResult[0].displayName).toBe("Second.tsx");
|
|
302
|
+
}
|
|
303
|
+
});
|
|
171
304
|
});
|
|
172
305
|
/**
|
|
173
306
|
* Should handle removing non-existent attachment gracefully
|
|
174
307
|
*/
|
|
175
|
-
it("should handle removing non-existent attachment gracefully", () => {
|
|
308
|
+
it("should handle removing non-existent attachment gracefully", async () => {
|
|
176
309
|
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
177
310
|
wrapper: createWrapper(),
|
|
178
311
|
});
|
|
312
|
+
// Wait for initial helper registration (useEffect runs on mount)
|
|
313
|
+
await waitFor(() => {
|
|
314
|
+
expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
|
|
315
|
+
});
|
|
179
316
|
expect(() => {
|
|
180
317
|
act(() => {
|
|
181
318
|
result.current.removeContextAttachment("non-existent-id");
|
|
182
319
|
});
|
|
183
320
|
}).not.toThrow();
|
|
321
|
+
// The helper is registered on mount, so removeContextHelper will be called
|
|
322
|
+
// when the component unmounts or when attachments change, but not for
|
|
323
|
+
// removing a non-existent attachment since attachments didn't change
|
|
324
|
+
// However, the cleanup function from the initial useEffect registration
|
|
325
|
+
// may be called. Let's just verify the attachment list is still empty.
|
|
326
|
+
expect(result.current.attachments).toHaveLength(0);
|
|
184
327
|
});
|
|
185
328
|
});
|
|
186
|
-
describe("Clearing All Attachments", () => {
|
|
329
|
+
describe("Clearing All Context Attachments", () => {
|
|
187
330
|
/**
|
|
188
|
-
* Should clear all attachments
|
|
331
|
+
* Should clear all context attachments and update the merged helper to return null
|
|
189
332
|
*/
|
|
190
|
-
it("should clear all context attachments", () => {
|
|
333
|
+
it("should clear all context attachments", async () => {
|
|
191
334
|
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
192
335
|
wrapper: createWrapper(),
|
|
193
336
|
});
|
|
194
337
|
act(() => {
|
|
195
|
-
result.current.addContextAttachment({
|
|
196
|
-
|
|
197
|
-
|
|
338
|
+
result.current.addContextAttachment({
|
|
339
|
+
context: "file1",
|
|
340
|
+
displayName: "First.tsx",
|
|
341
|
+
});
|
|
342
|
+
result.current.addContextAttachment({
|
|
343
|
+
context: "file2",
|
|
344
|
+
displayName: "Second.tsx",
|
|
345
|
+
});
|
|
346
|
+
result.current.addContextAttachment({
|
|
347
|
+
context: "file3",
|
|
348
|
+
displayName: "Third.tsx",
|
|
349
|
+
});
|
|
198
350
|
});
|
|
199
351
|
expect(result.current.attachments).toHaveLength(3);
|
|
352
|
+
// Wait for initial helper registration
|
|
353
|
+
await waitFor(() => {
|
|
354
|
+
expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
|
|
355
|
+
});
|
|
200
356
|
act(() => {
|
|
201
357
|
result.current.clearContextAttachments();
|
|
202
358
|
});
|
|
203
359
|
expect(result.current.attachments).toHaveLength(0);
|
|
360
|
+
// Wait for useEffect to update the helper (should return null when empty)
|
|
361
|
+
await waitFor(() => {
|
|
362
|
+
const lastCall = mockAddContextHelper.mock.calls
|
|
363
|
+
.filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
|
|
364
|
+
.pop();
|
|
365
|
+
if (lastCall) {
|
|
366
|
+
const helperFn = lastCall[1];
|
|
367
|
+
expect(helperFn()).toBeNull();
|
|
368
|
+
}
|
|
369
|
+
});
|
|
204
370
|
});
|
|
205
371
|
/**
|
|
206
372
|
* Should handle clearing when no attachments exist
|
|
207
373
|
*/
|
|
208
|
-
it("should handle clearing when no attachments exist", () => {
|
|
374
|
+
it("should handle clearing when no attachments exist", async () => {
|
|
209
375
|
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
210
376
|
wrapper: createWrapper(),
|
|
211
377
|
});
|
|
@@ -215,413 +381,13 @@ describe("TamboContextAttachmentProvider", () => {
|
|
|
215
381
|
});
|
|
216
382
|
}).not.toThrow();
|
|
217
383
|
expect(result.current.attachments).toHaveLength(0);
|
|
218
|
-
|
|
219
|
-
});
|
|
220
|
-
describe("Context Helpers Integration", () => {
|
|
221
|
-
/**
|
|
222
|
-
* Should automatically register context helpers when attachments are added
|
|
223
|
-
*/
|
|
224
|
-
it("should register context helpers for attachments", async () => {
|
|
225
|
-
const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
|
|
226
|
-
React.createElement(TamboContextAttachmentProvider, null, children)));
|
|
227
|
-
const { result } = renderHook(() => ({
|
|
228
|
-
attachment: useTamboContextAttachment(),
|
|
229
|
-
helpers: useTamboContextHelpers(),
|
|
230
|
-
}), { wrapper });
|
|
231
|
-
// Add attachment
|
|
232
|
-
act(() => {
|
|
233
|
-
result.current.attachment.addContextAttachment({
|
|
234
|
-
name: "Button.tsx",
|
|
235
|
-
metadata: { filePath: "/src/Button.tsx" },
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
// Wait for effect to run
|
|
239
|
-
await waitFor(async () => {
|
|
240
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
241
|
-
expect(contexts.length).toBeGreaterThan(0);
|
|
242
|
-
});
|
|
243
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
244
|
-
const attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
|
|
245
|
-
expect(attachmentContext).toBeDefined();
|
|
246
|
-
expect(attachmentContext?.context).toHaveProperty("selectedComponent");
|
|
247
|
-
});
|
|
248
|
-
/**
|
|
249
|
-
* Should unregister context helpers when attachments are removed
|
|
250
|
-
*/
|
|
251
|
-
it("should unregister context helpers when attachments are removed", async () => {
|
|
252
|
-
const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
|
|
253
|
-
React.createElement(TamboContextAttachmentProvider, null, children)));
|
|
254
|
-
const { result } = renderHook(() => ({
|
|
255
|
-
attachment: useTamboContextAttachment(),
|
|
256
|
-
helpers: useTamboContextHelpers(),
|
|
257
|
-
}), { wrapper });
|
|
258
|
-
// Add attachment
|
|
259
|
-
act(() => {
|
|
260
|
-
result.current.attachment.addContextAttachment({
|
|
261
|
-
name: "Button.tsx",
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
// Wait for context helper to be registered
|
|
265
|
-
await waitFor(async () => {
|
|
266
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
267
|
-
expect(contexts.length).toBeGreaterThan(0);
|
|
268
|
-
});
|
|
269
|
-
const initialContexts = await result.current.helpers.getAdditionalContext();
|
|
270
|
-
const initialCount = initialContexts.length;
|
|
271
|
-
const attachmentId = result.current.attachment.attachments[0].id;
|
|
272
|
-
// Remove attachment
|
|
273
|
-
act(() => {
|
|
274
|
-
result.current.attachment.removeContextAttachment(attachmentId);
|
|
275
|
-
});
|
|
276
|
-
// Wait for context helper to be unregistered
|
|
277
|
-
await waitFor(async () => {
|
|
278
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
279
|
-
expect(contexts.length).toBeLessThan(initialCount);
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
/**
|
|
283
|
-
* Should use default context data structure when no custom function provided
|
|
284
|
-
*/
|
|
285
|
-
it("should use default context data structure", async () => {
|
|
286
|
-
const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
|
|
287
|
-
React.createElement(TamboContextAttachmentProvider, null, children)));
|
|
288
|
-
const { result } = renderHook(() => ({
|
|
289
|
-
attachment: useTamboContextAttachment(),
|
|
290
|
-
helpers: useTamboContextHelpers(),
|
|
291
|
-
}), { wrapper });
|
|
292
|
-
act(() => {
|
|
293
|
-
result.current.attachment.addContextAttachment({
|
|
294
|
-
name: "Button.tsx",
|
|
295
|
-
metadata: { filePath: "/src/Button.tsx", type: "component" },
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
await waitFor(async () => {
|
|
299
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
300
|
-
expect(contexts.length).toBeGreaterThan(0);
|
|
301
|
-
});
|
|
302
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
303
|
-
const attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
|
|
304
|
-
expect(attachmentContext?.context).toMatchObject({
|
|
305
|
-
selectedComponent: {
|
|
306
|
-
name: "Button.tsx",
|
|
307
|
-
instruction: expect.stringContaining("Tambo interactable component"),
|
|
308
|
-
filePath: "/src/Button.tsx",
|
|
309
|
-
type: "component",
|
|
310
|
-
},
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
/**
|
|
314
|
-
* Should use custom getContextHelperData function when provided
|
|
315
|
-
*/
|
|
316
|
-
it("should use custom getContextHelperData function", async () => {
|
|
317
|
-
const customGetContextHelperData = jest.fn(async (context) => ({
|
|
318
|
-
selectedFile: {
|
|
319
|
-
name: context.name,
|
|
320
|
-
path: context.metadata?.filePath,
|
|
321
|
-
customField: "custom value",
|
|
322
|
-
},
|
|
323
|
-
}));
|
|
324
|
-
const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
|
|
325
|
-
React.createElement(TamboContextAttachmentProvider, { getContextHelperData: customGetContextHelperData }, children)));
|
|
326
|
-
const { result } = renderHook(() => ({
|
|
327
|
-
attachment: useTamboContextAttachment(),
|
|
328
|
-
helpers: useTamboContextHelpers(),
|
|
329
|
-
}), { wrapper });
|
|
330
|
-
act(() => {
|
|
331
|
-
result.current.attachment.addContextAttachment({
|
|
332
|
-
name: "Button.tsx",
|
|
333
|
-
metadata: { filePath: "/src/Button.tsx" },
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
// Wait for context helper to be registered and called
|
|
337
|
-
await waitFor(async () => {
|
|
338
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
339
|
-
expect(contexts.length).toBeGreaterThan(0);
|
|
340
|
-
}, { timeout: 2000 });
|
|
341
|
-
expect(customGetContextHelperData).toHaveBeenCalled();
|
|
342
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
343
|
-
const attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
|
|
344
|
-
expect(attachmentContext?.context).toEqual({
|
|
345
|
-
selectedFile: {
|
|
346
|
-
name: "Button.tsx",
|
|
347
|
-
path: "/src/Button.tsx",
|
|
348
|
-
customField: "custom value",
|
|
349
|
-
},
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
/**
|
|
353
|
-
* Should update context helpers when getContextHelperData function changes
|
|
354
|
-
* This tests the bug fix for stale closure issue
|
|
355
|
-
*/
|
|
356
|
-
it("should update existing context helpers when getContextHelperData changes", async () => {
|
|
357
|
-
// Create a wrapper component with state to manage the function
|
|
358
|
-
const TestWrapper = ({ children }) => {
|
|
359
|
-
const [version, setVersion] = React.useState("v1");
|
|
360
|
-
const getContextHelperData = React.useCallback(async (context) => ({
|
|
361
|
-
version,
|
|
362
|
-
name: context.name,
|
|
363
|
-
}), [version]);
|
|
364
|
-
// Expose setVersion for the test
|
|
365
|
-
TestWrapper.setVersion = setVersion;
|
|
366
|
-
return (React.createElement(TamboContextHelpersProvider, null,
|
|
367
|
-
React.createElement(TamboContextAttachmentProvider, { getContextHelperData: getContextHelperData }, children)));
|
|
368
|
-
};
|
|
369
|
-
const { result } = renderHook(() => ({
|
|
370
|
-
attachment: useTamboContextAttachment(),
|
|
371
|
-
helpers: useTamboContextHelpers(),
|
|
372
|
-
}), { wrapper: TestWrapper });
|
|
373
|
-
// Add attachment with first version
|
|
374
|
-
act(() => {
|
|
375
|
-
result.current.attachment.addContextAttachment({
|
|
376
|
-
name: "Button.tsx",
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
// Wait for context helper to be registered
|
|
380
|
-
await waitFor(async () => {
|
|
381
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
382
|
-
expect(contexts.length).toBeGreaterThan(0);
|
|
383
|
-
}, { timeout: 2000 });
|
|
384
|
-
// Verify v1 context
|
|
385
|
-
let contexts = await result.current.helpers.getAdditionalContext();
|
|
386
|
-
let attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
|
|
387
|
-
expect(attachmentContext?.context).toMatchObject({ version: "v1" });
|
|
388
|
-
// Change the version which will trigger a new getContextHelperData function
|
|
389
|
-
act(() => {
|
|
390
|
-
TestWrapper.setVersion("v2");
|
|
391
|
-
});
|
|
392
|
-
// Wait for context to update to v2
|
|
393
|
-
await waitFor(async () => {
|
|
394
|
-
const contexts = await result.current.helpers.getAdditionalContext();
|
|
395
|
-
const context = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
|
|
396
|
-
return context?.context.version === "v2";
|
|
397
|
-
}, { timeout: 2000 });
|
|
398
|
-
// Verify v2 context - should be updated, not stale
|
|
399
|
-
contexts = await result.current.helpers.getAdditionalContext();
|
|
400
|
-
attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
|
|
401
|
-
expect(attachmentContext?.context).toMatchObject({ version: "v2" });
|
|
402
|
-
});
|
|
403
|
-
/**
|
|
404
|
-
* Should handle errors in getContextHelperData gracefully
|
|
405
|
-
*/
|
|
406
|
-
it("should handle errors in getContextHelperData gracefully", async () => {
|
|
407
|
-
const consoleErrorSpy = jest
|
|
408
|
-
.spyOn(console, "error")
|
|
409
|
-
.mockImplementation(() => { });
|
|
410
|
-
const errorGetData = jest.fn(async () => {
|
|
411
|
-
throw new Error("Custom data error");
|
|
412
|
-
});
|
|
413
|
-
const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
|
|
414
|
-
React.createElement(TamboContextAttachmentProvider, { getContextHelperData: errorGetData }, children)));
|
|
415
|
-
const { result } = renderHook(() => ({
|
|
416
|
-
attachment: useTamboContextAttachment(),
|
|
417
|
-
helpers: useTamboContextHelpers(),
|
|
418
|
-
}), { wrapper });
|
|
419
|
-
act(() => {
|
|
420
|
-
result.current.attachment.addContextAttachment({
|
|
421
|
-
name: "Button.tsx",
|
|
422
|
-
});
|
|
423
|
-
});
|
|
424
|
-
// Wait for effect to run and try to call the function
|
|
384
|
+
// Wait for useEffect to run - helper should be registered and return null
|
|
425
385
|
await waitFor(() => {
|
|
426
|
-
expect(
|
|
427
|
-
}, { timeout: 2000 });
|
|
428
|
-
// Try to get contexts, which will trigger the error
|
|
429
|
-
await result.current.helpers.getAdditionalContext();
|
|
430
|
-
expect(errorGetData).toHaveBeenCalled();
|
|
431
|
-
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
432
|
-
consoleErrorSpy.mockRestore();
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
describe("Custom Suggestions", () => {
|
|
436
|
-
/**
|
|
437
|
-
* Should start with null custom suggestions
|
|
438
|
-
*/
|
|
439
|
-
it("should initialize with null custom suggestions", () => {
|
|
440
|
-
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
441
|
-
wrapper: createWrapper(),
|
|
442
|
-
});
|
|
443
|
-
expect(result.current.customSuggestions).toBeNull();
|
|
444
|
-
});
|
|
445
|
-
/**
|
|
446
|
-
* Should set custom suggestions
|
|
447
|
-
*/
|
|
448
|
-
it("should set custom suggestions", () => {
|
|
449
|
-
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
450
|
-
wrapper: createWrapper(),
|
|
451
|
-
});
|
|
452
|
-
const suggestions = [
|
|
453
|
-
{
|
|
454
|
-
id: "1",
|
|
455
|
-
title: "Edit component",
|
|
456
|
-
detailedSuggestion: "Modify the Button component",
|
|
457
|
-
messageId: "",
|
|
458
|
-
},
|
|
459
|
-
{
|
|
460
|
-
id: "2",
|
|
461
|
-
title: "Add feature",
|
|
462
|
-
detailedSuggestion: "Add a new feature",
|
|
463
|
-
messageId: "",
|
|
464
|
-
},
|
|
465
|
-
];
|
|
466
|
-
act(() => {
|
|
467
|
-
result.current.setCustomSuggestions(suggestions);
|
|
468
|
-
});
|
|
469
|
-
expect(result.current.customSuggestions).toEqual(suggestions);
|
|
470
|
-
});
|
|
471
|
-
/**
|
|
472
|
-
* Should clear custom suggestions by setting to null
|
|
473
|
-
*/
|
|
474
|
-
it("should clear custom suggestions", () => {
|
|
475
|
-
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
476
|
-
wrapper: createWrapper(),
|
|
477
|
-
});
|
|
478
|
-
const suggestions = [
|
|
479
|
-
{
|
|
480
|
-
id: "1",
|
|
481
|
-
title: "Test",
|
|
482
|
-
detailedSuggestion: "Test suggestion",
|
|
483
|
-
messageId: "",
|
|
484
|
-
},
|
|
485
|
-
];
|
|
486
|
-
act(() => {
|
|
487
|
-
result.current.setCustomSuggestions(suggestions);
|
|
488
|
-
});
|
|
489
|
-
expect(result.current.customSuggestions).toEqual(suggestions);
|
|
490
|
-
act(() => {
|
|
491
|
-
result.current.setCustomSuggestions(null);
|
|
492
|
-
});
|
|
493
|
-
expect(result.current.customSuggestions).toBeNull();
|
|
494
|
-
});
|
|
495
|
-
/**
|
|
496
|
-
* Should update custom suggestions when changed
|
|
497
|
-
*/
|
|
498
|
-
it("should update custom suggestions", () => {
|
|
499
|
-
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
500
|
-
wrapper: createWrapper(),
|
|
501
|
-
});
|
|
502
|
-
const firstSuggestions = [
|
|
503
|
-
{
|
|
504
|
-
id: "1",
|
|
505
|
-
title: "First",
|
|
506
|
-
detailedSuggestion: "First suggestion",
|
|
507
|
-
messageId: "",
|
|
508
|
-
},
|
|
509
|
-
];
|
|
510
|
-
const secondSuggestions = [
|
|
511
|
-
{
|
|
512
|
-
id: "2",
|
|
513
|
-
title: "Second",
|
|
514
|
-
detailedSuggestion: "Second suggestion",
|
|
515
|
-
messageId: "",
|
|
516
|
-
},
|
|
517
|
-
];
|
|
518
|
-
act(() => {
|
|
519
|
-
result.current.setCustomSuggestions(firstSuggestions);
|
|
520
|
-
});
|
|
521
|
-
expect(result.current.customSuggestions).toEqual(firstSuggestions);
|
|
522
|
-
act(() => {
|
|
523
|
-
result.current.setCustomSuggestions(secondSuggestions);
|
|
524
|
-
});
|
|
525
|
-
expect(result.current.customSuggestions).toEqual(secondSuggestions);
|
|
526
|
-
});
|
|
527
|
-
});
|
|
528
|
-
describe("Combined Workflows", () => {
|
|
529
|
-
/**
|
|
530
|
-
* Should handle adding attachment and setting custom suggestions together
|
|
531
|
-
*/
|
|
532
|
-
it("should handle attachment and custom suggestions together", () => {
|
|
533
|
-
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
534
|
-
wrapper: createWrapper(),
|
|
535
|
-
});
|
|
536
|
-
const suggestions = [
|
|
537
|
-
{
|
|
538
|
-
id: "1",
|
|
539
|
-
title: "Edit file",
|
|
540
|
-
detailedSuggestion: "Edit this file",
|
|
541
|
-
messageId: "",
|
|
542
|
-
},
|
|
543
|
-
];
|
|
544
|
-
act(() => {
|
|
545
|
-
result.current.addContextAttachment({
|
|
546
|
-
name: "Button.tsx",
|
|
547
|
-
metadata: { filePath: "/src/Button.tsx" },
|
|
548
|
-
});
|
|
549
|
-
result.current.setCustomSuggestions(suggestions);
|
|
550
|
-
});
|
|
551
|
-
expect(result.current.attachments).toHaveLength(1);
|
|
552
|
-
expect(result.current.customSuggestions).toEqual(suggestions);
|
|
553
|
-
});
|
|
554
|
-
/**
|
|
555
|
-
* Should clear suggestions when clearing attachments
|
|
556
|
-
*/
|
|
557
|
-
it("should independently manage attachments and suggestions", () => {
|
|
558
|
-
const { result } = renderHook(() => useTamboContextAttachment(), {
|
|
559
|
-
wrapper: createWrapper(),
|
|
560
|
-
});
|
|
561
|
-
const suggestions = [
|
|
562
|
-
{
|
|
563
|
-
id: "1",
|
|
564
|
-
title: "Test",
|
|
565
|
-
detailedSuggestion: "Test suggestion",
|
|
566
|
-
messageId: "",
|
|
567
|
-
},
|
|
568
|
-
];
|
|
569
|
-
act(() => {
|
|
570
|
-
result.current.addContextAttachment({ name: "File.tsx" });
|
|
571
|
-
result.current.setCustomSuggestions(suggestions);
|
|
572
|
-
});
|
|
573
|
-
// Clear attachments
|
|
574
|
-
act(() => {
|
|
575
|
-
result.current.clearContextAttachments();
|
|
576
|
-
});
|
|
577
|
-
// Suggestions should remain
|
|
578
|
-
expect(result.current.attachments).toHaveLength(0);
|
|
579
|
-
expect(result.current.customSuggestions).toEqual(suggestions);
|
|
580
|
-
// Can clear suggestions separately
|
|
581
|
-
act(() => {
|
|
582
|
-
result.current.setCustomSuggestions(null);
|
|
583
|
-
});
|
|
584
|
-
expect(result.current.customSuggestions).toBeNull();
|
|
585
|
-
});
|
|
586
|
-
});
|
|
587
|
-
describe("Cleanup", () => {
|
|
588
|
-
/**
|
|
589
|
-
* Should cleanup context helpers on unmount
|
|
590
|
-
*/
|
|
591
|
-
it("should cleanup context helpers on unmount", async () => {
|
|
592
|
-
// Use a shared provider wrapper so context helpers persist
|
|
593
|
-
const SharedWrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null, children));
|
|
594
|
-
// First render with attachment provider
|
|
595
|
-
const { result: attachmentResult, unmount } = renderHook(() => ({
|
|
596
|
-
attachment: useTamboContextAttachment(),
|
|
597
|
-
helpers: useTamboContextHelpers(),
|
|
598
|
-
}), {
|
|
599
|
-
wrapper: ({ children }) => (React.createElement(SharedWrapper, null,
|
|
600
|
-
React.createElement(TamboContextAttachmentProvider, null, children))),
|
|
601
|
-
});
|
|
602
|
-
// Add attachment
|
|
603
|
-
act(() => {
|
|
604
|
-
attachmentResult.current.attachment.addContextAttachment({
|
|
605
|
-
name: "Button.tsx",
|
|
606
|
-
});
|
|
386
|
+
expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
|
|
607
387
|
});
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const contexts = await attachmentResult.current.helpers.getAdditionalContext();
|
|
612
|
-
const hasContext = contexts.some((c) => c.name.includes(attachmentId));
|
|
613
|
-
expect(hasContext).toBe(true);
|
|
614
|
-
});
|
|
615
|
-
// Unmount the attachment provider
|
|
616
|
-
unmount();
|
|
617
|
-
// Create new hook to check cleanup
|
|
618
|
-
const { result: helpersResult } = renderHook(() => useTamboContextHelpers(), { wrapper: SharedWrapper });
|
|
619
|
-
// Wait a bit for cleanup effect to run
|
|
620
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
621
|
-
const contexts = await helpersResult.current.getAdditionalContext();
|
|
622
|
-
// Should not contain the attachment context after unmount
|
|
623
|
-
const hasAttachmentContext = contexts.some((c) => c.name.includes(attachmentId));
|
|
624
|
-
expect(hasAttachmentContext).toBe(false);
|
|
388
|
+
const helperFn = mockAddContextHelper.mock.calls.find((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)?.[1];
|
|
389
|
+
expect(helperFn).toBeDefined();
|
|
390
|
+
expect(helperFn()).toBeNull();
|
|
625
391
|
});
|
|
626
392
|
});
|
|
627
393
|
});
|