@assistant-ui/react 0.14.27 → 0.14.29
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/client/ExternalThread.d.ts +11 -1
- package/dist/client/ExternalThread.d.ts.map +1 -1
- package/dist/client/ExternalThread.js +758 -513
- package/dist/client/ExternalThread.js.map +1 -1
- package/dist/context/react/utils/createContextStoreHook.d.ts.map +1 -1
- package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
- package/dist/legacy-runtime/cloud/auiV0.d.ts +12 -1
- package/dist/legacy-runtime/cloud/auiV0.d.ts.map +1 -1
- package/dist/legacy-runtime/cloud/auiV0.js +2 -1
- package/dist/legacy-runtime/cloud/auiV0.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +7 -6
- package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +30 -15
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +8 -4
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +1 -0
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/model-context/makeAssistantVisible.d.ts.map +1 -1
- package/dist/model-context/makeAssistantVisible.js +7 -1
- package/dist/model-context/makeAssistantVisible.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarCopy.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarEdit.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarReload.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarRoot.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarRoot.js +3 -3
- package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarSpeak.d.ts.map +1 -1
- package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts.map +1 -1
- package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts +7 -6
- package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts.map +1 -1
- package/dist/primitives/actionBar/useActionBarFloatStatus.js +10 -11
- package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreContent.d.ts.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreItem.d.ts.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreSeparator.d.ts.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreTrigger.d.ts.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalAnchor.d.ts.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalContent.d.ts.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalTrigger.d.ts.map +1 -1
- package/dist/primitives/assistantModal/popoverRenderPrimitives.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.js +1 -0
- package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRoot.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.d.ts.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNext.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerRoot.d.ts.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.d.ts.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +1 -0
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtRoot.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js +8 -2
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -3
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/ComposerCancel.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerDictate.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerDictationTranscript.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +6 -4
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerQuote.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerQuote.js.map +1 -1
- package/dist/primitives/composer/ComposerRoot.d.ts +5 -0
- package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerRoot.js +86 -40
- package/dist/primitives/composer/ComposerRoot.js.map +1 -1
- package/dist/primitives/composer/ComposerSend.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerSend.js +1 -0
- package/dist/primitives/composer/ComposerSend.js.map +1 -1
- package/dist/primitives/composer/ComposerStopDictation.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerStopDictation.js +1 -0
- package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverBack.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
- package/dist/primitives/composer/trigger/detectTrigger.d.ts +1 -1
- package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
- package/dist/primitives/dropdownMenuRenderPrimitives.d.ts.map +1 -1
- package/dist/primitives/error/ErrorMessage.d.ts.map +1 -1
- package/dist/primitives/error/ErrorMessage.js.map +1 -1
- package/dist/primitives/error/ErrorRoot.d.ts.map +1 -1
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/message/MessageRoot.d.ts.map +1 -1
- package/dist/primitives/messagePart/MessagePartImage.d.ts.map +1 -1
- package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -1
- package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartData.d.ts +3 -2
- package/dist/primitives/messagePart/useMessagePartData.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartData.js +3 -2
- package/dist/primitives/messagePart/useMessagePartData.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartFile.d.ts +5 -4
- package/dist/primitives/messagePart/useMessagePartFile.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartFile.js +11 -3
- package/dist/primitives/messagePart/useMessagePartFile.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartImage.d.ts +5 -4
- package/dist/primitives/messagePart/useMessagePartImage.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartImage.js +10 -3
- package/dist/primitives/messagePart/useMessagePartImage.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartReasoning.d.ts +5 -4
- package/dist/primitives/messagePart/useMessagePartReasoning.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartReasoning.js +10 -3
- package/dist/primitives/messagePart/useMessagePartReasoning.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartSource.d.ts +6 -4
- package/dist/primitives/messagePart/useMessagePartSource.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartSource.js +12 -3
- package/dist/primitives/messagePart/useMessagePartSource.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartText.d.ts +6 -5
- package/dist/primitives/messagePart/useMessagePartText.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartText.js +10 -3
- package/dist/primitives/messagePart/useMessagePartText.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.d.ts.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.js +1 -0
- package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.d.ts.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.js +1 -0
- package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.d.ts.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.d.ts.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.d.ts.map +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTitle.d.ts.map +1 -1
- package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.d.ts.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.js +1 -0
- package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
- package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadScrollToBottom.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadScrollToBottom.js +1 -0
- package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
- package/dist/primitives/thread/ThreadSuggestion.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js +53 -19
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/ThreadViewportFooter.d.ts.map +1 -1
- package/dist/primitives/thread/topAnchor/createReserveObservers.d.ts.map +1 -1
- package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts +4 -0
- package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts.map +1 -1
- package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js +14 -1
- package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js.map +1 -1
- package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts +13 -1
- package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts.map +1 -1
- package/dist/primitives/thread/topAnchor/topAnchorTurn.js +14 -1
- package/dist/primitives/thread/topAnchor/topAnchorTurn.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +2 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/primitives/threadList/ThreadListLoadMore.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListNew.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.d.ts.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
- package/dist/primitives/threadListFocusGroup.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemArchive.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemDelete.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.d.ts.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.d.ts.map +1 -1
- package/dist/tests/setup.d.ts +1 -1
- package/dist/utils/Primitive.d.ts.map +1 -1
- package/dist/utils/Primitive.js.map +1 -1
- package/dist/utils/createActionButton.d.ts.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.js +1 -0
- package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
- package/dist/utils/hooks/useSizeHandle.js +1 -0
- package/dist/utils/hooks/useSizeHandle.js.map +1 -1
- package/dist/utils/smooth/useSmooth.d.ts.map +1 -1
- package/dist/utils/smooth/useSmooth.js +2 -2
- package/dist/utils/smooth/useSmooth.js.map +1 -1
- package/package.json +20 -20
- package/src/client/ExternalThread.ts +341 -53
- package/src/legacy-runtime/cloud/auiV0.ts +14 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +4 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +37 -10
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-delivery.test.tsx +79 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-parentid.test.tsx +97 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +113 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransport.spec.md +10 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +189 -0
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +20 -4
- package/src/model-context/makeAssistantVisible.test.tsx +146 -0
- package/src/model-context/makeAssistantVisible.tsx +18 -1
- package/src/primitives/actionBar/useActionBarFloatStatus.ts +7 -5
- package/src/primitives/composer/ComposerAddAttachment.test.tsx +108 -0
- package/src/primitives/composer/ComposerAddAttachment.ts +10 -4
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +3 -2
- package/src/primitives/composer/ComposerAttachmentDropzone.tsx +3 -2
- package/src/primitives/composer/ComposerInput.test.tsx +98 -1
- package/src/primitives/composer/ComposerInput.tsx +10 -8
- package/src/primitives/composer/ComposerRoot.test.tsx +226 -5
- package/src/primitives/composer/ComposerRoot.tsx +50 -1
- package/src/primitives/messagePart/useMessagePartData.ts +3 -2
- package/src/primitives/messagePart/useMessagePartFile.ts +19 -7
- package/src/primitives/messagePart/useMessagePartImage.ts +18 -7
- package/src/primitives/messagePart/useMessagePartReasoning.ts +15 -6
- package/src/primitives/messagePart/useMessagePartSource.ts +20 -7
- package/src/primitives/messagePart/useMessagePartText.ts +15 -5
- package/src/primitives/thread/ThreadViewport.tsx +15 -0
- package/src/primitives/thread/topAnchor/mountTopAnchorReserve.test.ts +243 -0
- package/src/primitives/thread/topAnchor/mountTopAnchorReserve.ts +28 -7
- package/src/primitives/thread/topAnchor/topAnchorTurn.test.ts +69 -0
- package/src/primitives/thread/topAnchor/topAnchorTurn.ts +29 -0
- package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +86 -1
- package/src/primitives/thread/useThreadViewportAutoScroll.ts +6 -1
- package/src/tests/DataRenderers.test.tsx +212 -0
- package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +1 -11
- package/src/tests/external-thread-attachments.test.tsx +372 -0
- package/src/tests/external-thread-parity.test.tsx +232 -0
- package/src/tests/generative-ui.test.tsx +1 -46
- package/src/tests/messagePartSwitchRace.test.tsx +172 -0
- package/src/tests/messagePartTypeMismatch.test.tsx +146 -0
- package/src/tests/threadMessageById.test.tsx +0 -7
- package/src/utils/smooth/useSmooth.ts +7 -4
- package/src/tests/BaseComposerRuntimeCore.test.ts +0 -612
- package/src/tests/BaseSubscribable.test.ts +0 -54
- package/src/tests/DataRenderers.test.ts +0 -355
- package/src/tests/ExternalStoreThreadRuntimeCore.test.ts +0 -685
- package/src/tests/LocalThreadRuntimeCore.test.ts +0 -324
- package/src/tests/auiV0Encode.test.ts +0 -188
- package/src/tests/external-message-converter.test.ts +0 -346
- package/src/tests/shouldContinue.test.ts +0 -122
|
@@ -1,612 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { BaseComposerRuntimeCore } from "../legacy-runtime/runtime-cores/composer/BaseComposerRuntimeCore";
|
|
3
|
-
import type {
|
|
4
|
-
AppendMessage,
|
|
5
|
-
AttachmentAdapter,
|
|
6
|
-
CreateAttachment,
|
|
7
|
-
DictationAdapter,
|
|
8
|
-
PendingAttachment,
|
|
9
|
-
SendOptions,
|
|
10
|
-
} from "@assistant-ui/core";
|
|
11
|
-
|
|
12
|
-
class TestComposerCore extends BaseComposerRuntimeCore {
|
|
13
|
-
private _attachmentAdapter: AttachmentAdapter | undefined;
|
|
14
|
-
private _dictationAdapter: DictationAdapter | undefined;
|
|
15
|
-
public sentMessages: Array<Omit<AppendMessage, "parentId" | "sourceId">> = [];
|
|
16
|
-
public sentOptions: Array<SendOptions | undefined> = [];
|
|
17
|
-
public cancelCalled = false;
|
|
18
|
-
|
|
19
|
-
protected getAttachmentAdapter() {
|
|
20
|
-
return this._attachmentAdapter;
|
|
21
|
-
}
|
|
22
|
-
protected getDictationAdapter() {
|
|
23
|
-
return this._dictationAdapter;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
setAttachmentAdapter(adapter: AttachmentAdapter | undefined) {
|
|
27
|
-
this._attachmentAdapter = adapter;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
setDictationAdapter(adapter: DictationAdapter | undefined) {
|
|
31
|
-
this._dictationAdapter = adapter;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get canCancel() {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
get canSend() {
|
|
39
|
-
return !this.isEmpty;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
protected handleSend(
|
|
43
|
-
message: Omit<AppendMessage, "parentId" | "sourceId">,
|
|
44
|
-
options?: SendOptions,
|
|
45
|
-
) {
|
|
46
|
-
this.sentMessages.push(message);
|
|
47
|
-
this.sentOptions.push(options);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
protected handleCancel() {
|
|
51
|
-
this.cancelCalled = true;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const makePendingAttachment = (
|
|
56
|
-
id: string,
|
|
57
|
-
name = "file.txt",
|
|
58
|
-
): PendingAttachment => ({
|
|
59
|
-
id,
|
|
60
|
-
type: "file",
|
|
61
|
-
name,
|
|
62
|
-
contentType: "text/plain",
|
|
63
|
-
file: new File(["content"], name),
|
|
64
|
-
status: { type: "requires-action", reason: "composer-send" },
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe("BaseComposerRuntimeCore", () => {
|
|
68
|
-
let composer: TestComposerCore;
|
|
69
|
-
|
|
70
|
-
beforeEach(() => {
|
|
71
|
-
composer = new TestComposerCore();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("sets and gets text", () => {
|
|
75
|
-
composer.setText("hello");
|
|
76
|
-
expect(composer.text).toBe("hello");
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("setText does not notify when value unchanged", () => {
|
|
80
|
-
composer.setText("same");
|
|
81
|
-
const listener = vi.fn();
|
|
82
|
-
composer.subscribe(listener);
|
|
83
|
-
|
|
84
|
-
composer.setText("same");
|
|
85
|
-
expect(listener).not.toHaveBeenCalled();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("isEmpty returns true when no text and no attachments", () => {
|
|
89
|
-
expect(composer.isEmpty).toBe(true);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("isEmpty returns false when text is present", () => {
|
|
93
|
-
composer.setText("hi");
|
|
94
|
-
expect(composer.isEmpty).toBe(false);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("isEmpty returns true for whitespace-only text", () => {
|
|
98
|
-
composer.setText(" ");
|
|
99
|
-
expect(composer.isEmpty).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("sets and gets role", () => {
|
|
103
|
-
composer.setRole("assistant");
|
|
104
|
-
expect(composer.role).toBe("assistant");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("setRole does not notify when value unchanged", () => {
|
|
108
|
-
composer.setRole("user");
|
|
109
|
-
const listener = vi.fn();
|
|
110
|
-
composer.subscribe(listener);
|
|
111
|
-
|
|
112
|
-
composer.setRole("user");
|
|
113
|
-
expect(listener).not.toHaveBeenCalled();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("sets and gets runConfig", () => {
|
|
117
|
-
const config = { custom: { model: "gpt-5.4-nano" } };
|
|
118
|
-
composer.setRunConfig(config);
|
|
119
|
-
expect(composer.runConfig).toBe(config);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("sets and gets quote", () => {
|
|
123
|
-
const quote = { text: "some quote", messageId: "msg-1" };
|
|
124
|
-
composer.setQuote(quote);
|
|
125
|
-
expect(composer.quote).toBe(quote);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("setQuote does not notify when value unchanged", () => {
|
|
129
|
-
const quote = { text: "q", messageId: "m1" };
|
|
130
|
-
composer.setQuote(quote);
|
|
131
|
-
const listener = vi.fn();
|
|
132
|
-
composer.subscribe(listener);
|
|
133
|
-
|
|
134
|
-
composer.setQuote(quote);
|
|
135
|
-
expect(listener).not.toHaveBeenCalled();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("reset clears text, attachments, role, runConfig, and quote", async () => {
|
|
139
|
-
composer.setText("hello");
|
|
140
|
-
composer.setRole("assistant");
|
|
141
|
-
composer.setRunConfig({ custom: { k: "v" } });
|
|
142
|
-
composer.setQuote({ text: "q", messageId: "m1" });
|
|
143
|
-
|
|
144
|
-
await composer.reset();
|
|
145
|
-
|
|
146
|
-
expect(composer.text).toBe("");
|
|
147
|
-
expect(composer.role).toBe("user");
|
|
148
|
-
expect(composer.runConfig).toEqual({});
|
|
149
|
-
expect(composer.quote).toBeUndefined();
|
|
150
|
-
expect(composer.attachments).toEqual([]);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it("reset does nothing when already in default state", async () => {
|
|
154
|
-
const listener = vi.fn();
|
|
155
|
-
composer.subscribe(listener);
|
|
156
|
-
|
|
157
|
-
await composer.reset();
|
|
158
|
-
expect(listener).not.toHaveBeenCalled();
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("send constructs message with text content and fires send event", async () => {
|
|
162
|
-
composer.setText("hello world");
|
|
163
|
-
|
|
164
|
-
await composer.send();
|
|
165
|
-
|
|
166
|
-
expect(composer.sentMessages).toHaveLength(1);
|
|
167
|
-
const msg = composer.sentMessages[0]!;
|
|
168
|
-
expect(msg.role).toBe("user");
|
|
169
|
-
expect(msg.content).toEqual([{ type: "text", text: "hello world" }]);
|
|
170
|
-
expect(msg.attachments).toEqual([]);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("send clears text and attachments after sending", async () => {
|
|
174
|
-
composer.setText("hello");
|
|
175
|
-
|
|
176
|
-
await composer.send();
|
|
177
|
-
|
|
178
|
-
expect(composer.text).toBe("");
|
|
179
|
-
expect(composer.attachments).toEqual([]);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("send includes quote in metadata and clears it", async () => {
|
|
183
|
-
const quote = { text: "quoted", messageId: "m1" };
|
|
184
|
-
composer.setText("reply");
|
|
185
|
-
composer.setQuote(quote);
|
|
186
|
-
|
|
187
|
-
await composer.send();
|
|
188
|
-
|
|
189
|
-
const msg = composer.sentMessages[0]!;
|
|
190
|
-
expect(msg.metadata).toEqual({ custom: { quote } });
|
|
191
|
-
expect(composer.quote).toBeUndefined();
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it("send with empty text is a no-op", async () => {
|
|
195
|
-
await composer.send();
|
|
196
|
-
|
|
197
|
-
expect(composer.sentMessages).toHaveLength(0);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("addAttachment throws when no adapter", async () => {
|
|
201
|
-
const file = new File(["data"], "test.txt");
|
|
202
|
-
await expect(composer.addAttachment(file)).rejects.toThrow(
|
|
203
|
-
"Attachments are not supported",
|
|
204
|
-
);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("addAttachment adds via adapter", async () => {
|
|
208
|
-
const pending = makePendingAttachment("att-1", "test.txt");
|
|
209
|
-
const adapter: AttachmentAdapter = {
|
|
210
|
-
accept: "text/*",
|
|
211
|
-
add: vi.fn().mockResolvedValue(pending),
|
|
212
|
-
remove: vi.fn().mockResolvedValue(undefined),
|
|
213
|
-
send: vi.fn(),
|
|
214
|
-
};
|
|
215
|
-
composer.setAttachmentAdapter(adapter);
|
|
216
|
-
|
|
217
|
-
await composer.addAttachment(
|
|
218
|
-
new File(["data"], "test.txt", { type: "text/plain" }),
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
expect(composer.attachments).toHaveLength(1);
|
|
222
|
-
expect(composer.attachments[0]!.id).toBe("att-1");
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it("removeAttachment removes via adapter", async () => {
|
|
226
|
-
const pending = makePendingAttachment("att-1");
|
|
227
|
-
const adapter: AttachmentAdapter = {
|
|
228
|
-
accept: "*",
|
|
229
|
-
add: vi.fn().mockResolvedValue(pending),
|
|
230
|
-
remove: vi.fn().mockResolvedValue(undefined),
|
|
231
|
-
send: vi.fn(),
|
|
232
|
-
};
|
|
233
|
-
composer.setAttachmentAdapter(adapter);
|
|
234
|
-
|
|
235
|
-
await composer.addAttachment(new File([""], "f.txt"));
|
|
236
|
-
expect(composer.attachments).toHaveLength(1);
|
|
237
|
-
|
|
238
|
-
await composer.removeAttachment("att-1");
|
|
239
|
-
expect(composer.attachments).toHaveLength(0);
|
|
240
|
-
expect(adapter.remove).toHaveBeenCalledWith(pending);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it("removeAttachment throws for unknown id", async () => {
|
|
244
|
-
const adapter: AttachmentAdapter = {
|
|
245
|
-
accept: "*",
|
|
246
|
-
add: vi.fn(),
|
|
247
|
-
remove: vi.fn(),
|
|
248
|
-
send: vi.fn(),
|
|
249
|
-
};
|
|
250
|
-
composer.setAttachmentAdapter(adapter);
|
|
251
|
-
|
|
252
|
-
await expect(composer.removeAttachment("nonexistent")).rejects.toThrow(
|
|
253
|
-
"Attachment not found",
|
|
254
|
-
);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it("unstable_on registers event listener for send", async () => {
|
|
258
|
-
const callback = vi.fn();
|
|
259
|
-
composer.unstable_on("send", callback);
|
|
260
|
-
|
|
261
|
-
composer.setText("test");
|
|
262
|
-
await composer.send();
|
|
263
|
-
|
|
264
|
-
expect(callback).toHaveBeenCalledTimes(1);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it("unstable_on unsubscribe stops notifications", async () => {
|
|
268
|
-
const callback = vi.fn();
|
|
269
|
-
const unsub = composer.unstable_on("send", callback);
|
|
270
|
-
unsub();
|
|
271
|
-
|
|
272
|
-
composer.setText("test");
|
|
273
|
-
await composer.send();
|
|
274
|
-
|
|
275
|
-
expect(callback).not.toHaveBeenCalled();
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it("unstable_on fires attachmentAdd event", async () => {
|
|
279
|
-
const callback = vi.fn();
|
|
280
|
-
composer.unstable_on("attachmentAdd", callback);
|
|
281
|
-
|
|
282
|
-
const pending = makePendingAttachment("att-1");
|
|
283
|
-
const adapter: AttachmentAdapter = {
|
|
284
|
-
accept: "*",
|
|
285
|
-
add: vi.fn().mockResolvedValue(pending),
|
|
286
|
-
remove: vi.fn(),
|
|
287
|
-
send: vi.fn(),
|
|
288
|
-
};
|
|
289
|
-
composer.setAttachmentAdapter(adapter);
|
|
290
|
-
|
|
291
|
-
await composer.addAttachment(new File([""], "f.txt"));
|
|
292
|
-
expect(callback).toHaveBeenCalledTimes(1);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it("isEditing is always true", () => {
|
|
296
|
-
expect(composer.isEditing).toBe(true);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it("attachmentAccept returns adapter accept or default", () => {
|
|
300
|
-
expect(composer.attachmentAccept).toBe("*");
|
|
301
|
-
|
|
302
|
-
const adapter: AttachmentAdapter = {
|
|
303
|
-
accept: "image/*",
|
|
304
|
-
add: vi.fn(),
|
|
305
|
-
remove: vi.fn(),
|
|
306
|
-
send: vi.fn(),
|
|
307
|
-
};
|
|
308
|
-
composer.setAttachmentAdapter(adapter);
|
|
309
|
-
expect(composer.attachmentAccept).toBe("image/*");
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it("cancel delegates to handleCancel", () => {
|
|
313
|
-
composer.cancel();
|
|
314
|
-
expect(composer.cancelCalled).toBe(true);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it("subscribe notifies on text change", () => {
|
|
318
|
-
const listener = vi.fn();
|
|
319
|
-
composer.subscribe(listener);
|
|
320
|
-
|
|
321
|
-
composer.setText("new");
|
|
322
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it("subscribe returns working unsubscribe function", () => {
|
|
326
|
-
const listener = vi.fn();
|
|
327
|
-
const unsub = composer.subscribe(listener);
|
|
328
|
-
unsub();
|
|
329
|
-
|
|
330
|
-
composer.setText("change");
|
|
331
|
-
expect(listener).not.toHaveBeenCalled();
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
describe("CreateAttachment (external source)", () => {
|
|
335
|
-
const makeCreateAttachment = (
|
|
336
|
-
overrides?: Partial<CreateAttachment>,
|
|
337
|
-
): CreateAttachment => ({
|
|
338
|
-
name: "external-doc.pdf",
|
|
339
|
-
content: [{ type: "text", text: "extracted content" }],
|
|
340
|
-
...overrides,
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it("addAttachment with CreateAttachment adds without adapter", async () => {
|
|
344
|
-
// No adapter set — should still work
|
|
345
|
-
const att = makeCreateAttachment();
|
|
346
|
-
await composer.addAttachment(att);
|
|
347
|
-
|
|
348
|
-
expect(composer.attachments).toHaveLength(1);
|
|
349
|
-
expect(composer.attachments[0]!.name).toBe("external-doc.pdf");
|
|
350
|
-
expect(composer.attachments[0]!.status).toEqual({ type: "complete" });
|
|
351
|
-
expect(composer.attachments[0]!.type).toBe("document");
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
it("addAttachment with CreateAttachment uses provided id and type", async () => {
|
|
355
|
-
const att = makeCreateAttachment({
|
|
356
|
-
id: "custom-id",
|
|
357
|
-
type: "image",
|
|
358
|
-
contentType: "image/png",
|
|
359
|
-
});
|
|
360
|
-
await composer.addAttachment(att);
|
|
361
|
-
|
|
362
|
-
expect(composer.attachments[0]!.id).toBe("custom-id");
|
|
363
|
-
expect(composer.attachments[0]!.type).toBe("image");
|
|
364
|
-
expect(composer.attachments[0]!.contentType).toBe("image/png");
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
it("addAttachment with CreateAttachment generates id when not provided", async () => {
|
|
368
|
-
const att = makeCreateAttachment();
|
|
369
|
-
await composer.addAttachment(att);
|
|
370
|
-
|
|
371
|
-
expect(composer.attachments[0]!.id).toBeTruthy();
|
|
372
|
-
expect(typeof composer.attachments[0]!.id).toBe("string");
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
it("addAttachment with CreateAttachment fires attachmentAdd event", async () => {
|
|
376
|
-
const callback = vi.fn();
|
|
377
|
-
composer.unstable_on("attachmentAdd", callback);
|
|
378
|
-
|
|
379
|
-
await composer.addAttachment(makeCreateAttachment());
|
|
380
|
-
expect(callback).toHaveBeenCalledTimes(1);
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
it("removeAttachment on complete attachment does not require adapter", async () => {
|
|
384
|
-
await composer.addAttachment(makeCreateAttachment({ id: "ext-1" }));
|
|
385
|
-
expect(composer.attachments).toHaveLength(1);
|
|
386
|
-
|
|
387
|
-
// No adapter set — should still remove complete attachments
|
|
388
|
-
await composer.removeAttachment("ext-1");
|
|
389
|
-
expect(composer.attachments).toHaveLength(0);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it("isEmpty returns false when external attachment present", async () => {
|
|
393
|
-
expect(composer.isEmpty).toBe(true);
|
|
394
|
-
|
|
395
|
-
await composer.addAttachment(makeCreateAttachment());
|
|
396
|
-
expect(composer.isEmpty).toBe(false);
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
it("send with mixed pending + complete attachments", async () => {
|
|
400
|
-
const pending = makePendingAttachment("att-pending");
|
|
401
|
-
const completedFromAdapter = {
|
|
402
|
-
id: "att-pending",
|
|
403
|
-
type: "file" as const,
|
|
404
|
-
name: "file.txt",
|
|
405
|
-
contentType: "text/plain",
|
|
406
|
-
content: [{ type: "text" as const, text: "file content" }],
|
|
407
|
-
status: { type: "complete" as const },
|
|
408
|
-
};
|
|
409
|
-
const adapter: AttachmentAdapter = {
|
|
410
|
-
accept: "*",
|
|
411
|
-
add: vi.fn().mockResolvedValue(pending),
|
|
412
|
-
remove: vi.fn(),
|
|
413
|
-
send: vi.fn().mockResolvedValue(completedFromAdapter),
|
|
414
|
-
};
|
|
415
|
-
composer.setAttachmentAdapter(adapter);
|
|
416
|
-
|
|
417
|
-
// Add a file-based attachment via adapter
|
|
418
|
-
await composer.addAttachment(new File(["data"], "file.txt"));
|
|
419
|
-
// Add an external attachment
|
|
420
|
-
await composer.addAttachment(makeCreateAttachment({ id: "ext-1" }));
|
|
421
|
-
|
|
422
|
-
expect(composer.attachments).toHaveLength(2);
|
|
423
|
-
|
|
424
|
-
composer.setText("hello");
|
|
425
|
-
await composer.send();
|
|
426
|
-
|
|
427
|
-
expect(composer.sentMessages).toHaveLength(1);
|
|
428
|
-
const msg = composer.sentMessages[0]!;
|
|
429
|
-
expect(msg.attachments).toHaveLength(2);
|
|
430
|
-
// Pending was sent through adapter
|
|
431
|
-
expect(adapter.send).toHaveBeenCalledTimes(1);
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
describe("adapter.accept enforcement", () => {
|
|
435
|
-
const makeImageAdapter = (): AttachmentAdapter => ({
|
|
436
|
-
accept: "image/*",
|
|
437
|
-
add: vi.fn(),
|
|
438
|
-
remove: vi.fn(),
|
|
439
|
-
send: vi.fn(),
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
it("rejects external attachment with contentType not matching adapter.accept", async () => {
|
|
443
|
-
composer.setAttachmentAdapter(makeImageAdapter());
|
|
444
|
-
const onError = vi.fn();
|
|
445
|
-
const onAdd = vi.fn();
|
|
446
|
-
composer.unstable_on("attachmentAddError", onError);
|
|
447
|
-
composer.unstable_on("attachmentAdd", onAdd);
|
|
448
|
-
|
|
449
|
-
await expect(
|
|
450
|
-
composer.addAttachment(
|
|
451
|
-
makeCreateAttachment({
|
|
452
|
-
name: "doc.pdf",
|
|
453
|
-
contentType: "application/pdf",
|
|
454
|
-
}),
|
|
455
|
-
),
|
|
456
|
-
).rejects.toThrow(/File type application\/pdf is not accepted/);
|
|
457
|
-
|
|
458
|
-
expect(composer.attachments).toHaveLength(0);
|
|
459
|
-
expect(onError).toHaveBeenCalledTimes(1);
|
|
460
|
-
expect(onError).toHaveBeenCalledWith(
|
|
461
|
-
expect.objectContaining({
|
|
462
|
-
reason: "not-accepted",
|
|
463
|
-
message: expect.stringContaining(
|
|
464
|
-
"File type application/pdf is not accepted",
|
|
465
|
-
),
|
|
466
|
-
error: expect.any(Error),
|
|
467
|
-
}),
|
|
468
|
-
);
|
|
469
|
-
expect(onAdd).not.toHaveBeenCalled();
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
it("accepts external attachment with contentType matching adapter.accept", async () => {
|
|
473
|
-
composer.setAttachmentAdapter(makeImageAdapter());
|
|
474
|
-
const onError = vi.fn();
|
|
475
|
-
|
|
476
|
-
composer.unstable_on("attachmentAddError", onError);
|
|
477
|
-
await composer.addAttachment(
|
|
478
|
-
makeCreateAttachment({
|
|
479
|
-
name: "photo.png",
|
|
480
|
-
contentType: "image/png",
|
|
481
|
-
}),
|
|
482
|
-
);
|
|
483
|
-
|
|
484
|
-
expect(composer.attachments).toHaveLength(1);
|
|
485
|
-
expect(onError).not.toHaveBeenCalled();
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
it("accepts external attachment when only filename extension matches adapter.accept", async () => {
|
|
489
|
-
composer.setAttachmentAdapter({
|
|
490
|
-
accept: ".pdf",
|
|
491
|
-
add: vi.fn(),
|
|
492
|
-
remove: vi.fn(),
|
|
493
|
-
send: vi.fn(),
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
await composer.addAttachment(
|
|
497
|
-
makeCreateAttachment({ name: "report.pdf" }),
|
|
498
|
-
);
|
|
499
|
-
|
|
500
|
-
expect(composer.attachments).toHaveLength(1);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
it("rejects external attachment when contentType missing and extension does not match", async () => {
|
|
504
|
-
composer.setAttachmentAdapter(makeImageAdapter());
|
|
505
|
-
const onError = vi.fn();
|
|
506
|
-
composer.unstable_on("attachmentAddError", onError);
|
|
507
|
-
|
|
508
|
-
await expect(
|
|
509
|
-
composer.addAttachment(makeCreateAttachment({ name: "notes.txt" })),
|
|
510
|
-
).rejects.toThrow(/is not accepted/);
|
|
511
|
-
|
|
512
|
-
expect(onError).toHaveBeenCalledTimes(1);
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
it("adds external attachment without check when no adapter is configured", async () => {
|
|
516
|
-
// No adapter — preserves the original "no adapter required" guarantee
|
|
517
|
-
await composer.addAttachment(
|
|
518
|
-
makeCreateAttachment({
|
|
519
|
-
name: "anything.xyz",
|
|
520
|
-
contentType: "application/x-anything",
|
|
521
|
-
}),
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
expect(composer.attachments).toHaveLength(1);
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
it("does not let subscriber errors mask the original throw", async () => {
|
|
528
|
-
composer.setAttachmentAdapter(makeImageAdapter());
|
|
529
|
-
composer.unstable_on("attachmentAddError", () => {
|
|
530
|
-
throw new Error("subscriber boom");
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
await expect(
|
|
534
|
-
composer.addAttachment(
|
|
535
|
-
makeCreateAttachment({
|
|
536
|
-
name: "doc.pdf",
|
|
537
|
-
contentType: "application/pdf",
|
|
538
|
-
}),
|
|
539
|
-
),
|
|
540
|
-
).rejects.toThrow(/is not accepted/);
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
it("does not fire attachmentAddError when an attachmentAdd subscriber throws", async () => {
|
|
544
|
-
composer.setAttachmentAdapter(makeImageAdapter());
|
|
545
|
-
const onError = vi.fn();
|
|
546
|
-
composer.unstable_on("attachmentAdd", () => {
|
|
547
|
-
throw new Error("add subscriber boom");
|
|
548
|
-
});
|
|
549
|
-
composer.unstable_on("attachmentAddError", onError);
|
|
550
|
-
|
|
551
|
-
await expect(
|
|
552
|
-
composer.addAttachment(
|
|
553
|
-
makeCreateAttachment({
|
|
554
|
-
name: "photo.png",
|
|
555
|
-
contentType: "image/png",
|
|
556
|
-
}),
|
|
557
|
-
),
|
|
558
|
-
).rejects.toThrow("add subscriber boom");
|
|
559
|
-
|
|
560
|
-
expect(composer.attachments).toHaveLength(1);
|
|
561
|
-
expect(onError).not.toHaveBeenCalled();
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
it("does not fire attachmentAddError when a state subscriber throws on add", async () => {
|
|
565
|
-
composer.setAttachmentAdapter(makeImageAdapter());
|
|
566
|
-
const onError = vi.fn();
|
|
567
|
-
composer.subscribe(() => {
|
|
568
|
-
throw new Error("state subscriber boom");
|
|
569
|
-
});
|
|
570
|
-
composer.unstable_on("attachmentAddError", onError);
|
|
571
|
-
|
|
572
|
-
await expect(
|
|
573
|
-
composer.addAttachment(
|
|
574
|
-
makeCreateAttachment({
|
|
575
|
-
name: "photo.png",
|
|
576
|
-
contentType: "image/png",
|
|
577
|
-
}),
|
|
578
|
-
),
|
|
579
|
-
).rejects.toThrow("state subscriber boom");
|
|
580
|
-
|
|
581
|
-
expect(composer.attachments).toHaveLength(1);
|
|
582
|
-
expect(onError).not.toHaveBeenCalled();
|
|
583
|
-
});
|
|
584
|
-
});
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
describe("send options", () => {
|
|
588
|
-
it("send() passes undefined options by default", async () => {
|
|
589
|
-
composer.setText("hello");
|
|
590
|
-
await composer.send();
|
|
591
|
-
|
|
592
|
-
expect(composer.sentOptions).toHaveLength(1);
|
|
593
|
-
expect(composer.sentOptions[0]).toBeUndefined();
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
it("send({ startRun: true }) forwards options to handleSend", async () => {
|
|
597
|
-
composer.setText("hello");
|
|
598
|
-
await composer.send({ startRun: true });
|
|
599
|
-
|
|
600
|
-
expect(composer.sentOptions).toHaveLength(1);
|
|
601
|
-
expect(composer.sentOptions[0]).toEqual({ startRun: true });
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
it("send({ startRun: false }) forwards options to handleSend", async () => {
|
|
605
|
-
composer.setText("hello");
|
|
606
|
-
await composer.send({ startRun: false });
|
|
607
|
-
|
|
608
|
-
expect(composer.sentOptions).toHaveLength(1);
|
|
609
|
-
expect(composer.sentOptions[0]).toEqual({ startRun: false });
|
|
610
|
-
});
|
|
611
|
-
});
|
|
612
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
2
|
-
import { BaseSubscribable } from "@assistant-ui/core/internal";
|
|
3
|
-
|
|
4
|
-
class TestSubscribable extends BaseSubscribable {
|
|
5
|
-
public notify() {
|
|
6
|
-
this._notifySubscribers();
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
describe("BaseSubscribable", () => {
|
|
11
|
-
let subscribable: TestSubscribable;
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
subscribable = new TestSubscribable();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("subscribe registers a callback that is called on notify", () => {
|
|
18
|
-
const callback = vi.fn();
|
|
19
|
-
subscribable.subscribe(callback);
|
|
20
|
-
subscribable.notify();
|
|
21
|
-
expect(callback).toHaveBeenCalledOnce();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("unsubscribe stops notifications", () => {
|
|
25
|
-
const callback = vi.fn();
|
|
26
|
-
const unsub = subscribable.subscribe(callback);
|
|
27
|
-
unsub();
|
|
28
|
-
subscribable.notify();
|
|
29
|
-
expect(callback).not.toHaveBeenCalled();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("multiple subscribers all get notified", () => {
|
|
33
|
-
const cb1 = vi.fn();
|
|
34
|
-
const cb2 = vi.fn();
|
|
35
|
-
subscribable.subscribe(cb1);
|
|
36
|
-
subscribable.subscribe(cb2);
|
|
37
|
-
subscribable.notify();
|
|
38
|
-
expect(cb1).toHaveBeenCalledOnce();
|
|
39
|
-
expect(cb2).toHaveBeenCalledOnce();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("subscribe returns an unsubscribe function", () => {
|
|
43
|
-
const cb1 = vi.fn();
|
|
44
|
-
const cb2 = vi.fn();
|
|
45
|
-
const unsub1 = subscribable.subscribe(cb1);
|
|
46
|
-
subscribable.subscribe(cb2);
|
|
47
|
-
|
|
48
|
-
unsub1();
|
|
49
|
-
subscribable.notify();
|
|
50
|
-
|
|
51
|
-
expect(cb1).not.toHaveBeenCalled();
|
|
52
|
-
expect(cb2).toHaveBeenCalledOnce();
|
|
53
|
-
});
|
|
54
|
-
});
|