@memberjunction/ng-conversations 5.43.0 → 5.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts +0 -1
- package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts.map +1 -1
- package/dist/lib/components/active-tasks/active-tasks-panel.component.js +56 -52
- package/dist/lib/components/active-tasks/active-tasks-panel.component.js.map +1 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.js +37 -41
- package/dist/lib/components/artifact/artifact-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.js +10 -14
- package/dist/lib/components/collection/collection-form-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.js +41 -45
- package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +22 -36
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.d.ts +4 -0
- package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.js +134 -166
- package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +73 -8
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +487 -123
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/pinned-messages-panel.component.js +6 -9
- package/dist/lib/components/conversation/pinned-messages-panel.component.js.map +1 -1
- package/dist/lib/components/library/library-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/library/library-full-view.component.js +20 -24
- package/dist/lib/components/library/library-full-view.component.js.map +1 -1
- package/dist/lib/components/members/members-modal.component.d.ts.map +1 -1
- package/dist/lib/components/members/members-modal.component.js +10 -11
- package/dist/lib/components/members/members-modal.component.js.map +1 -1
- package/dist/lib/components/mention/mention-dropdown.component.js +49 -28
- package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.js +24 -10
- package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
- package/dist/lib/components/message/message-input-box.component.d.ts +11 -16
- package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input-box.component.js +52 -46
- package/dist/lib/components/message/message-input-box.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +83 -18
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +238 -151
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +10 -1
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +67 -15
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.js +6 -9
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts +3 -0
- package/dist/lib/components/overlay/chat-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.js +9 -4
- package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +45 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +10 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -1
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts +46 -0
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/channels/client-context-channel.js +174 -0
- package/dist/lib/components/realtime/channels/client-context-channel.js.map +1 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.d.ts +123 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.js +397 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-state.d.ts +103 -0
- package/dist/lib/components/realtime/media/media-channel-state.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-state.js +143 -0
- package/dist/lib/components/realtime/media/media-channel-state.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-tools.d.ts +24 -0
- package/dist/lib/components/realtime/media/media-channel-tools.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-tools.js +107 -0
- package/dist/lib/components/realtime/media/media-channel-tools.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel.d.ts +89 -0
- package/dist/lib/components/realtime/media/media-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/media-channel.js +253 -0
- package/dist/lib/components/realtime/media/media-channel.js.map +1 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts +141 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js +574 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +59 -14
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-activity-rail.component.js +277 -74
- package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +28 -27
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.js +144 -143
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +12 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.js +105 -43
- package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts +2 -2
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-audio-visuals.js.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.d.ts +41 -7
- package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.js +216 -102
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +0 -2
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-delegation-card.component.js +67 -71
- package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +364 -44
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +839 -153
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.d.ts +5 -5
- package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tab-style.d.ts +70 -0
- package/dist/lib/components/realtime/realtime-surface-tab-style.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tab-style.js +115 -0
- package/dist/lib/components/realtime/realtime-surface-tab-style.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +55 -57
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js +165 -143
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +80 -71
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js +83 -113
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.d.ts +270 -0
- package/dist/lib/components/realtime/realtime-ui-config.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-ui-config.js +212 -0
- package/dist/lib/components/realtime/realtime-ui-config.js.map +1 -0
- package/dist/lib/components/search/search-panel.component.d.ts +2 -0
- package/dist/lib/components/search/search-panel.component.d.ts.map +1 -1
- package/dist/lib/components/search/search-panel.component.js +92 -101
- package/dist/lib/components/search/search-panel.component.js.map +1 -1
- package/dist/lib/components/share/share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/share/share-modal.component.js +10 -11
- package/dist/lib/components/share/share-modal.component.js.map +1 -1
- package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -1
- package/dist/lib/components/shared/user-picker.component.js +12 -15
- package/dist/lib/components/shared/user-picker.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +5 -0
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +18 -7
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +29 -26
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +37 -6
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/models/conversation-state.model.d.ts +6 -3
- package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
- package/dist/lib/models/conversation-state.model.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +2 -2
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +6 -2
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.d.ts +17 -1
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +58 -2
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/realtime-audio-recorder.d.ts +143 -0
- package/dist/lib/services/realtime-audio-recorder.d.ts.map +1 -0
- package/dist/lib/services/realtime-audio-recorder.js +379 -0
- package/dist/lib/services/realtime-audio-recorder.js.map +1 -0
- package/dist/lib/services/realtime-pcm-wav.d.ts +75 -0
- package/dist/lib/services/realtime-pcm-wav.d.ts.map +1 -0
- package/dist/lib/services/realtime-pcm-wav.js +205 -0
- package/dist/lib/services/realtime-pcm-wav.js.map +1 -0
- package/dist/lib/services/realtime-session-review.service.d.ts +28 -1
- package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session-review.service.js +12 -3
- package/dist/lib/services/realtime-session-review.service.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts +210 -20
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +452 -21
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/dist/lib/services/realtime-sessions-adapter.js +1 -1
- package/dist/lib/services/realtime-sessions-adapter.js.map +1 -1
- package/dist/lib/services/ui-command-handler.service.d.ts +7 -2
- package/dist/lib/services/ui-command-handler.service.d.ts.map +1 -1
- package/dist/lib/services/ui-command-handler.service.js +6 -2
- package/dist/lib/services/ui-command-handler.service.js.map +1 -1
- package/dist/lib/utils/plan-mode-preference.d.ts +62 -0
- package/dist/lib/utils/plan-mode-preference.d.ts.map +1 -0
- package/dist/lib/utils/plan-mode-preference.js +130 -0
- package/dist/lib/utils/plan-mode-preference.js.map +1 -0
- package/dist/public-api.d.ts +6 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +8 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +28 -26
- package/dist/__tests__/attachment-badge.test.d.ts +0 -2
- package/dist/__tests__/attachment-badge.test.d.ts.map +0 -1
- package/dist/__tests__/attachment-badge.test.js +0 -66
- package/dist/__tests__/attachment-badge.test.js.map +0 -1
- package/dist/__tests__/channel-optional-surface.test.d.ts +0 -2
- package/dist/__tests__/channel-optional-surface.test.d.ts.map +0 -1
- package/dist/__tests__/channel-optional-surface.test.js +0 -53
- package/dist/__tests__/channel-optional-surface.test.js.map +0 -1
- package/dist/__tests__/chat-events.test.d.ts +0 -14
- package/dist/__tests__/chat-events.test.d.ts.map +0 -1
- package/dist/__tests__/chat-events.test.js +0 -109
- package/dist/__tests__/chat-events.test.js.map +0 -1
- package/dist/__tests__/collections-full-view.test.d.ts +0 -2
- package/dist/__tests__/collections-full-view.test.d.ts.map +0 -1
- package/dist/__tests__/collections-full-view.test.js +0 -225
- package/dist/__tests__/collections-full-view.test.js.map +0 -1
- package/dist/__tests__/conversation-cache.test.d.ts +0 -2
- package/dist/__tests__/conversation-cache.test.d.ts.map +0 -1
- package/dist/__tests__/conversation-cache.test.js +0 -233
- package/dist/__tests__/conversation-cache.test.js.map +0 -1
- package/dist/__tests__/conversation-naming.test.d.ts +0 -2
- package/dist/__tests__/conversation-naming.test.d.ts.map +0 -1
- package/dist/__tests__/conversation-naming.test.js +0 -110
- package/dist/__tests__/conversation-naming.test.js.map +0 -1
- package/dist/__tests__/delegation-result-parser.test.d.ts +0 -2
- package/dist/__tests__/delegation-result-parser.test.d.ts.map +0 -1
- package/dist/__tests__/delegation-result-parser.test.js +0 -107
- package/dist/__tests__/delegation-result-parser.test.js.map +0 -1
- package/dist/__tests__/distinct-artifacts.test.d.ts +0 -2
- package/dist/__tests__/distinct-artifacts.test.d.ts.map +0 -1
- package/dist/__tests__/distinct-artifacts.test.js +0 -56
- package/dist/__tests__/distinct-artifacts.test.js.map +0 -1
- package/dist/__tests__/event-wiring.test.d.ts +0 -15
- package/dist/__tests__/event-wiring.test.d.ts.map +0 -1
- package/dist/__tests__/event-wiring.test.js +0 -100
- package/dist/__tests__/event-wiring.test.js.map +0 -1
- package/dist/__tests__/index.test.d.ts +0 -2
- package/dist/__tests__/index.test.d.ts.map +0 -1
- package/dist/__tests__/index.test.js +0 -7
- package/dist/__tests__/index.test.js.map +0 -1
- package/dist/__tests__/narration-template.test.d.ts +0 -2
- package/dist/__tests__/narration-template.test.d.ts.map +0 -1
- package/dist/__tests__/narration-template.test.js +0 -76
- package/dist/__tests__/narration-template.test.js.map +0 -1
- package/dist/__tests__/notification-model.test.d.ts +0 -2
- package/dist/__tests__/notification-model.test.d.ts.map +0 -1
- package/dist/__tests__/notification-model.test.js +0 -68
- package/dist/__tests__/notification-model.test.js.map +0 -1
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts +0 -2
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-agent-picker-models.test.js +0 -49
- package/dist/__tests__/realtime-agent-picker-models.test.js.map +0 -1
- package/dist/__tests__/realtime-audio-visuals.test.d.ts +0 -2
- package/dist/__tests__/realtime-audio-visuals.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-audio-visuals.test.js +0 -123
- package/dist/__tests__/realtime-audio-visuals.test.js.map +0 -1
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts +0 -2
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-delegation-card-cancel.test.js +0 -48
- package/dist/__tests__/realtime-delegation-card-cancel.test.js.map +0 -1
- package/dist/__tests__/realtime-disclosure.test.d.ts +0 -2
- package/dist/__tests__/realtime-disclosure.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-disclosure.test.js +0 -164
- package/dist/__tests__/realtime-disclosure.test.js.map +0 -1
- package/dist/__tests__/realtime-pairing.test.d.ts +0 -2
- package/dist/__tests__/realtime-pairing.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-pairing.test.js +0 -207
- package/dist/__tests__/realtime-pairing.test.js.map +0 -1
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts +0 -2
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-review-lifecycle.test.js +0 -154
- package/dist/__tests__/realtime-review-lifecycle.test.js.map +0 -1
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-cancel-usage.test.js +0 -230
- package/dist/__tests__/realtime-session-cancel-usage.test.js.map +0 -1
- package/dist/__tests__/realtime-session-channels.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-channels.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-channels.test.js +0 -252
- package/dist/__tests__/realtime-session-channels.test.js.map +0 -1
- package/dist/__tests__/realtime-session-client-tools.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-client-tools.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-client-tools.test.js +0 -103
- package/dist/__tests__/realtime-session-client-tools.test.js.map +0 -1
- package/dist/__tests__/realtime-session-minimized.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-minimized.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-minimized.test.js +0 -32
- package/dist/__tests__/realtime-session-minimized.test.js.map +0 -1
- package/dist/__tests__/realtime-session-mint.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-mint.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-mint.test.js +0 -69
- package/dist/__tests__/realtime-session-mint.test.js.map +0 -1
- package/dist/__tests__/realtime-session-policy.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-policy.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-policy.test.js +0 -303
- package/dist/__tests__/realtime-session-policy.test.js.map +0 -1
- package/dist/__tests__/realtime-session-review.service.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-review.service.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-review.service.test.js +0 -743
- package/dist/__tests__/realtime-session-review.service.test.js.map +0 -1
- package/dist/__tests__/realtime-session-state.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-state.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-state.test.js +0 -83
- package/dist/__tests__/realtime-session-state.test.js.map +0 -1
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-timeline-card.test.js +0 -106
- package/dist/__tests__/realtime-session-timeline-card.test.js.map +0 -1
- package/dist/__tests__/realtime-session-timeline.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-timeline.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-timeline.test.js +0 -142
- package/dist/__tests__/realtime-session-timeline.test.js.map +0 -1
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts +0 -19
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-sessions-adapter.test.js +0 -188
- package/dist/__tests__/realtime-sessions-adapter.test.js.map +0 -1
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts +0 -2
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-surface-panel-prefs.test.js +0 -100
- package/dist/__tests__/realtime-surface-panel-prefs.test.js.map +0 -1
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts +0 -2
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-surface-tabs-model.test.js +0 -193
- package/dist/__tests__/realtime-surface-tabs-model.test.js.map +0 -1
- package/dist/__tests__/remote-browser-audio-player.test.d.ts +0 -2
- package/dist/__tests__/remote-browser-audio-player.test.d.ts.map +0 -1
- package/dist/__tests__/remote-browser-audio-player.test.js +0 -137
- package/dist/__tests__/remote-browser-audio-player.test.js.map +0 -1
- package/dist/__tests__/remote-browser-channel.test.d.ts +0 -2
- package/dist/__tests__/remote-browser-channel.test.d.ts.map +0 -1
- package/dist/__tests__/remote-browser-channel.test.js +0 -585
- package/dist/__tests__/remote-browser-channel.test.js.map +0 -1
- package/dist/__tests__/search-service.test.d.ts +0 -2
- package/dist/__tests__/search-service.test.d.ts.map +0 -1
- package/dist/__tests__/search-service.test.js +0 -114
- package/dist/__tests__/search-service.test.js.map +0 -1
- package/dist/__tests__/slot-defaults.test.d.ts +0 -24
- package/dist/__tests__/slot-defaults.test.d.ts.map +0 -1
- package/dist/__tests__/slot-defaults.test.js +0 -63
- package/dist/__tests__/slot-defaults.test.js.map +0 -1
- package/dist/__tests__/toast-service.test.d.ts +0 -2
- package/dist/__tests__/toast-service.test.d.ts.map +0 -1
- package/dist/__tests__/toast-service.test.js +0 -143
- package/dist/__tests__/toast-service.test.js.map +0 -1
- package/dist/__tests__/user-authorization.test.d.ts +0 -2
- package/dist/__tests__/user-authorization.test.d.ts.map +0 -1
- package/dist/__tests__/user-authorization.test.js +0 -97
- package/dist/__tests__/user-authorization.test.js.map +0 -1
- package/dist/__tests__/voice-session-narration.test.d.ts +0 -2
- package/dist/__tests__/voice-session-narration.test.d.ts.map +0 -1
- package/dist/__tests__/voice-session-narration.test.js +0 -609
- package/dist/__tests__/voice-session-narration.test.js.map +0 -1
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts +0 -2
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts.map +0 -1
- package/dist/__tests__/whiteboard-artifact-viewer.test.js +0 -101
- package/dist/__tests__/whiteboard-artifact-viewer.test.js.map +0 -1
- package/dist/__tests__/whiteboard-channel.test.d.ts +0 -2
- package/dist/__tests__/whiteboard-channel.test.d.ts.map +0 -1
- package/dist/__tests__/whiteboard-channel.test.js +0 -260
- package/dist/__tests__/whiteboard-channel.test.js.map +0 -1
- package/dist/__tests__/whiteboard-restore-state.test.d.ts +0 -2
- package/dist/__tests__/whiteboard-restore-state.test.d.ts.map +0 -1
- package/dist/__tests__/whiteboard-restore-state.test.js +0 -108
- package/dist/__tests__/whiteboard-restore-state.test.js.map +0 -1
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
/**
|
|
3
|
-
* Extracts the cache management logic from ConversationChatAreaComponent
|
|
4
|
-
* for isolated testing without Angular dependencies.
|
|
5
|
-
*/
|
|
6
|
-
class TestableConversationCache {
|
|
7
|
-
cache = new Map();
|
|
8
|
-
has(conversationId) {
|
|
9
|
-
return this.cache.has(conversationId);
|
|
10
|
-
}
|
|
11
|
-
get(conversationId) {
|
|
12
|
-
return this.cache.get(conversationId);
|
|
13
|
-
}
|
|
14
|
-
/** Store raw data from server (first visit) */
|
|
15
|
-
setRaw(conversationId, raw) {
|
|
16
|
-
this.cache.set(conversationId, { raw, entities: null });
|
|
17
|
-
}
|
|
18
|
-
/** Store hydrated entities after conversion */
|
|
19
|
-
setEntities(conversationId, entities) {
|
|
20
|
-
const entry = this.cache.get(conversationId);
|
|
21
|
-
if (entry) {
|
|
22
|
-
entry.entities = entities;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/** Invalidate both raw and entities */
|
|
26
|
-
invalidate(conversationId) {
|
|
27
|
-
this.cache.delete(conversationId);
|
|
28
|
-
}
|
|
29
|
-
/** Patch raw data in-place (e.g., after pin toggle) */
|
|
30
|
-
patchRaw(conversationId, messageId, patch) {
|
|
31
|
-
const entry = this.cache.get(conversationId);
|
|
32
|
-
if (entry) {
|
|
33
|
-
const row = entry.raw.find(r => r.ID === messageId);
|
|
34
|
-
if (row) {
|
|
35
|
-
Object.assign(row, patch);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/** Update raw data with fresh server data (invalidates entities) */
|
|
40
|
-
refreshRaw(conversationId, raw) {
|
|
41
|
-
this.cache.set(conversationId, { raw, entities: null });
|
|
42
|
-
}
|
|
43
|
-
get size() {
|
|
44
|
-
return this.cache.size;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
/** Simulate the 3-path loadMessages logic */
|
|
48
|
-
function resolveLoadPath(cache, conversationId) {
|
|
49
|
-
const entry = cache.get(conversationId);
|
|
50
|
-
if (entry?.entities)
|
|
51
|
-
return 'cached-entities';
|
|
52
|
-
if (entry)
|
|
53
|
-
return 'cached-raw';
|
|
54
|
-
return 'server-fetch';
|
|
55
|
-
}
|
|
56
|
-
// ─── Test data ───────────────────────────────────────────────────────────
|
|
57
|
-
function createRawRows(conversationId, count) {
|
|
58
|
-
return Array.from({ length: count }, (_, i) => ({
|
|
59
|
-
ID: `detail-${i}`,
|
|
60
|
-
ConversationID: conversationId,
|
|
61
|
-
Message: `Message ${i}`,
|
|
62
|
-
Role: i % 2 === 0 ? 'user' : 'AI',
|
|
63
|
-
Status: 'Completed',
|
|
64
|
-
IsPinned: false,
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
function createEntities(rawRows) {
|
|
68
|
-
return rawRows.map(r => ({
|
|
69
|
-
ID: r.ID,
|
|
70
|
-
Message: r.Message,
|
|
71
|
-
Status: r.Status,
|
|
72
|
-
IsPinned: r.IsPinned,
|
|
73
|
-
}));
|
|
74
|
-
}
|
|
75
|
-
// ─── Tests ───────────────────────────────────────────────────────────────
|
|
76
|
-
describe('Conversation Compound Cache', () => {
|
|
77
|
-
let cache;
|
|
78
|
-
const convA = 'conv-aaa';
|
|
79
|
-
const convB = 'conv-bbb';
|
|
80
|
-
beforeEach(() => {
|
|
81
|
-
cache = new TestableConversationCache();
|
|
82
|
-
});
|
|
83
|
-
describe('Cache structure', () => {
|
|
84
|
-
it('should start empty', () => {
|
|
85
|
-
expect(cache.size).toBe(0);
|
|
86
|
-
expect(cache.has(convA)).toBe(false);
|
|
87
|
-
});
|
|
88
|
-
it('should store raw data with null entities on first visit', () => {
|
|
89
|
-
const raw = createRawRows(convA, 5);
|
|
90
|
-
cache.setRaw(convA, raw);
|
|
91
|
-
const entry = cache.get(convA);
|
|
92
|
-
expect(entry).toBeDefined();
|
|
93
|
-
expect(entry.raw).toBe(raw);
|
|
94
|
-
expect(entry.entities).toBeNull();
|
|
95
|
-
});
|
|
96
|
-
it('should store entities alongside raw data after conversion', () => {
|
|
97
|
-
const raw = createRawRows(convA, 5);
|
|
98
|
-
cache.setRaw(convA, raw);
|
|
99
|
-
const entities = createEntities(raw);
|
|
100
|
-
cache.setEntities(convA, entities);
|
|
101
|
-
const entry = cache.get(convA);
|
|
102
|
-
expect(entry.raw).toBe(raw);
|
|
103
|
-
expect(entry.entities).toBe(entities);
|
|
104
|
-
expect(entry.entities).toHaveLength(5);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
describe('Load path resolution', () => {
|
|
108
|
-
it('should resolve to server-fetch when cache is empty', () => {
|
|
109
|
-
expect(resolveLoadPath(cache, convA)).toBe('server-fetch');
|
|
110
|
-
});
|
|
111
|
-
it('should resolve to cached-raw when only raw data exists', () => {
|
|
112
|
-
cache.setRaw(convA, createRawRows(convA, 3));
|
|
113
|
-
expect(resolveLoadPath(cache, convA)).toBe('cached-raw');
|
|
114
|
-
});
|
|
115
|
-
it('should resolve to cached-entities when entities are populated', () => {
|
|
116
|
-
const raw = createRawRows(convA, 3);
|
|
117
|
-
cache.setRaw(convA, raw);
|
|
118
|
-
cache.setEntities(convA, createEntities(raw));
|
|
119
|
-
expect(resolveLoadPath(cache, convA)).toBe('cached-entities');
|
|
120
|
-
});
|
|
121
|
-
it('should handle multiple conversations independently', () => {
|
|
122
|
-
const rawA = createRawRows(convA, 3);
|
|
123
|
-
cache.setRaw(convA, rawA);
|
|
124
|
-
cache.setEntities(convA, createEntities(rawA));
|
|
125
|
-
cache.setRaw(convB, createRawRows(convB, 2));
|
|
126
|
-
expect(resolveLoadPath(cache, convA)).toBe('cached-entities');
|
|
127
|
-
expect(resolveLoadPath(cache, convB)).toBe('cached-raw');
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
describe('Invalidation', () => {
|
|
131
|
-
it('should remove both raw and entities on invalidate', () => {
|
|
132
|
-
const raw = createRawRows(convA, 3);
|
|
133
|
-
cache.setRaw(convA, raw);
|
|
134
|
-
cache.setEntities(convA, createEntities(raw));
|
|
135
|
-
cache.invalidate(convA);
|
|
136
|
-
expect(cache.has(convA)).toBe(false);
|
|
137
|
-
expect(cache.get(convA)).toBeUndefined();
|
|
138
|
-
expect(resolveLoadPath(cache, convA)).toBe('server-fetch');
|
|
139
|
-
});
|
|
140
|
-
it('should not affect other conversations when invalidating one', () => {
|
|
141
|
-
cache.setRaw(convA, createRawRows(convA, 3));
|
|
142
|
-
cache.setRaw(convB, createRawRows(convB, 2));
|
|
143
|
-
cache.invalidate(convA);
|
|
144
|
-
expect(cache.has(convA)).toBe(false);
|
|
145
|
-
expect(cache.has(convB)).toBe(true);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
describe('Raw data refresh', () => {
|
|
149
|
-
it('should replace raw data and clear entities on refresh', () => {
|
|
150
|
-
const raw = createRawRows(convA, 3);
|
|
151
|
-
cache.setRaw(convA, raw);
|
|
152
|
-
cache.setEntities(convA, createEntities(raw));
|
|
153
|
-
const freshRaw = createRawRows(convA, 5);
|
|
154
|
-
cache.refreshRaw(convA, freshRaw);
|
|
155
|
-
const entry = cache.get(convA);
|
|
156
|
-
expect(entry.raw).toBe(freshRaw);
|
|
157
|
-
expect(entry.raw).toHaveLength(5);
|
|
158
|
-
expect(entry.entities).toBeNull(); // entities cleared
|
|
159
|
-
expect(resolveLoadPath(cache, convA)).toBe('cached-raw');
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
describe('In-place raw patching', () => {
|
|
163
|
-
it('should patch a specific row in raw data', () => {
|
|
164
|
-
const raw = createRawRows(convA, 3);
|
|
165
|
-
cache.setRaw(convA, raw);
|
|
166
|
-
cache.patchRaw(convA, 'detail-1', { IsPinned: true });
|
|
167
|
-
const entry = cache.get(convA);
|
|
168
|
-
expect(entry.raw[1].IsPinned).toBe(true);
|
|
169
|
-
// Other rows unaffected
|
|
170
|
-
expect(entry.raw[0].IsPinned).toBe(false);
|
|
171
|
-
expect(entry.raw[2].IsPinned).toBe(false);
|
|
172
|
-
});
|
|
173
|
-
it('should not affect entities when patching raw data', () => {
|
|
174
|
-
const raw = createRawRows(convA, 3);
|
|
175
|
-
cache.setRaw(convA, raw);
|
|
176
|
-
const entities = createEntities(raw);
|
|
177
|
-
cache.setEntities(convA, entities);
|
|
178
|
-
cache.patchRaw(convA, 'detail-1', { IsPinned: true });
|
|
179
|
-
// Raw is patched
|
|
180
|
-
expect(cache.get(convA).raw[1].IsPinned).toBe(true);
|
|
181
|
-
// Entity objects are separate — not patched by raw patch
|
|
182
|
-
// (In the real component, the entity is already mutated by .Save())
|
|
183
|
-
expect(cache.get(convA).entities[1].IsPinned).toBe(false);
|
|
184
|
-
});
|
|
185
|
-
it('should be a no-op for non-existent conversation', () => {
|
|
186
|
-
cache.patchRaw('nonexistent', 'detail-0', { IsPinned: true });
|
|
187
|
-
expect(cache.has('nonexistent')).toBe(false);
|
|
188
|
-
});
|
|
189
|
-
it('should be a no-op for non-existent message ID', () => {
|
|
190
|
-
const raw = createRawRows(convA, 2);
|
|
191
|
-
cache.setRaw(convA, raw);
|
|
192
|
-
cache.patchRaw(convA, 'nonexistent-detail', { IsPinned: true });
|
|
193
|
-
// Nothing changed
|
|
194
|
-
expect(raw.every(r => r.IsPinned === false)).toBe(true);
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
describe('Conversation switching simulation', () => {
|
|
198
|
-
it('should provide instant switch when entities are cached', () => {
|
|
199
|
-
// Visit conversation A — full load
|
|
200
|
-
const rawA = createRawRows(convA, 10);
|
|
201
|
-
cache.setRaw(convA, rawA);
|
|
202
|
-
cache.setEntities(convA, createEntities(rawA));
|
|
203
|
-
// Visit conversation B — full load
|
|
204
|
-
const rawB = createRawRows(convB, 5);
|
|
205
|
-
cache.setRaw(convB, rawB);
|
|
206
|
-
cache.setEntities(convB, createEntities(rawB));
|
|
207
|
-
// Switch back to A — should be instant (cached-entities path)
|
|
208
|
-
expect(resolveLoadPath(cache, convA)).toBe('cached-entities');
|
|
209
|
-
const entryA = cache.get(convA);
|
|
210
|
-
expect(entryA.entities).toHaveLength(10);
|
|
211
|
-
// Switch to B — also instant
|
|
212
|
-
expect(resolveLoadPath(cache, convB)).toBe('cached-entities');
|
|
213
|
-
const entryB = cache.get(convB);
|
|
214
|
-
expect(entryB.entities).toHaveLength(5);
|
|
215
|
-
});
|
|
216
|
-
it('should fall back to raw conversion after invalidation', () => {
|
|
217
|
-
const raw = createRawRows(convA, 3);
|
|
218
|
-
cache.setRaw(convA, raw);
|
|
219
|
-
cache.setEntities(convA, createEntities(raw));
|
|
220
|
-
// New message arrives — cache invalidated
|
|
221
|
-
cache.invalidate(convA);
|
|
222
|
-
// Re-fetch from server, store new raw
|
|
223
|
-
const freshRaw = createRawRows(convA, 4);
|
|
224
|
-
cache.setRaw(convA, freshRaw);
|
|
225
|
-
// Should need conversion (cached-raw path, not cached-entities)
|
|
226
|
-
expect(resolveLoadPath(cache, convA)).toBe('cached-raw');
|
|
227
|
-
// After conversion
|
|
228
|
-
cache.setEntities(convA, createEntities(freshRaw));
|
|
229
|
-
expect(resolveLoadPath(cache, convA)).toBe('cached-entities');
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
//# sourceMappingURL=conversation-cache.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-cache.test.js","sourceRoot":"","sources":["../../src/__tests__/conversation-cache.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect, beforeEach } from 'vitest';\n\n/**\n * Tests for the compound conversation data cache used by ConversationChatAreaComponent.\n *\n * The cache stores both raw query data and hydrated entity objects in a single Map entry.\n * This test exercises the cache logic in isolation (no Angular, no real entities) to verify:\n * - Compound cache structure (raw + nullable entities)\n * - Cache hit paths: entities present vs only raw present vs miss\n * - Invalidation clears both raw and entities\n * - In-place patching of raw data preserves entity references\n */\n\n/** Minimal stand-in for ConversationDetailComplete (raw query row) */\ninterface MockRawRow {\n ID: string;\n ConversationID: string;\n Message: string;\n Role: string;\n Status: string;\n IsPinned: boolean;\n}\n\n/** Minimal stand-in for a hydrated entity object */\ninterface MockEntity {\n ID: string;\n Message: string;\n Status: string;\n IsPinned: boolean;\n}\n\n/** The compound cache entry shape matching the component's implementation */\ninterface CacheEntry {\n raw: MockRawRow[];\n entities: MockEntity[] | null;\n}\n\n/**\n * Extracts the cache management logic from ConversationChatAreaComponent\n * for isolated testing without Angular dependencies.\n */\nclass TestableConversationCache {\n private cache = new Map<string, CacheEntry>();\n\n has(conversationId: string): boolean {\n return this.cache.has(conversationId);\n }\n\n get(conversationId: string): CacheEntry | undefined {\n return this.cache.get(conversationId);\n }\n\n /** Store raw data from server (first visit) */\n setRaw(conversationId: string, raw: MockRawRow[]): void {\n this.cache.set(conversationId, { raw, entities: null });\n }\n\n /** Store hydrated entities after conversion */\n setEntities(conversationId: string, entities: MockEntity[]): void {\n const entry = this.cache.get(conversationId);\n if (entry) {\n entry.entities = entities;\n }\n }\n\n /** Invalidate both raw and entities */\n invalidate(conversationId: string): void {\n this.cache.delete(conversationId);\n }\n\n /** Patch raw data in-place (e.g., after pin toggle) */\n patchRaw(conversationId: string, messageId: string, patch: Partial<MockRawRow>): void {\n const entry = this.cache.get(conversationId);\n if (entry) {\n const row = entry.raw.find(r => r.ID === messageId);\n if (row) {\n Object.assign(row, patch);\n }\n }\n }\n\n /** Update raw data with fresh server data (invalidates entities) */\n refreshRaw(conversationId: string, raw: MockRawRow[]): void {\n this.cache.set(conversationId, { raw, entities: null });\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n\n/** Simulate the 3-path loadMessages logic */\nfunction resolveLoadPath(cache: TestableConversationCache, conversationId: string): 'cached-entities' | 'cached-raw' | 'server-fetch' {\n const entry = cache.get(conversationId);\n if (entry?.entities) return 'cached-entities';\n if (entry) return 'cached-raw';\n return 'server-fetch';\n}\n\n// ─── Test data ───────────────────────────────────────────────────────────\n\nfunction createRawRows(conversationId: string, count: number): MockRawRow[] {\n return Array.from({ length: count }, (_, i) => ({\n ID: `detail-${i}`,\n ConversationID: conversationId,\n Message: `Message ${i}`,\n Role: i % 2 === 0 ? 'user' : 'AI',\n Status: 'Completed',\n IsPinned: false,\n }));\n}\n\nfunction createEntities(rawRows: MockRawRow[]): MockEntity[] {\n return rawRows.map(r => ({\n ID: r.ID,\n Message: r.Message,\n Status: r.Status,\n IsPinned: r.IsPinned,\n }));\n}\n\n// ─── Tests ───────────────────────────────────────────────────────────────\n\ndescribe('Conversation Compound Cache', () => {\n let cache: TestableConversationCache;\n const convA = 'conv-aaa';\n const convB = 'conv-bbb';\n\n beforeEach(() => {\n cache = new TestableConversationCache();\n });\n\n describe('Cache structure', () => {\n it('should start empty', () => {\n expect(cache.size).toBe(0);\n expect(cache.has(convA)).toBe(false);\n });\n\n it('should store raw data with null entities on first visit', () => {\n const raw = createRawRows(convA, 5);\n cache.setRaw(convA, raw);\n\n const entry = cache.get(convA);\n expect(entry).toBeDefined();\n expect(entry!.raw).toBe(raw);\n expect(entry!.entities).toBeNull();\n });\n\n it('should store entities alongside raw data after conversion', () => {\n const raw = createRawRows(convA, 5);\n cache.setRaw(convA, raw);\n\n const entities = createEntities(raw);\n cache.setEntities(convA, entities);\n\n const entry = cache.get(convA);\n expect(entry!.raw).toBe(raw);\n expect(entry!.entities).toBe(entities);\n expect(entry!.entities).toHaveLength(5);\n });\n });\n\n describe('Load path resolution', () => {\n it('should resolve to server-fetch when cache is empty', () => {\n expect(resolveLoadPath(cache, convA)).toBe('server-fetch');\n });\n\n it('should resolve to cached-raw when only raw data exists', () => {\n cache.setRaw(convA, createRawRows(convA, 3));\n expect(resolveLoadPath(cache, convA)).toBe('cached-raw');\n });\n\n it('should resolve to cached-entities when entities are populated', () => {\n const raw = createRawRows(convA, 3);\n cache.setRaw(convA, raw);\n cache.setEntities(convA, createEntities(raw));\n expect(resolveLoadPath(cache, convA)).toBe('cached-entities');\n });\n\n it('should handle multiple conversations independently', () => {\n const rawA = createRawRows(convA, 3);\n cache.setRaw(convA, rawA);\n cache.setEntities(convA, createEntities(rawA));\n\n cache.setRaw(convB, createRawRows(convB, 2));\n\n expect(resolveLoadPath(cache, convA)).toBe('cached-entities');\n expect(resolveLoadPath(cache, convB)).toBe('cached-raw');\n });\n });\n\n describe('Invalidation', () => {\n it('should remove both raw and entities on invalidate', () => {\n const raw = createRawRows(convA, 3);\n cache.setRaw(convA, raw);\n cache.setEntities(convA, createEntities(raw));\n\n cache.invalidate(convA);\n\n expect(cache.has(convA)).toBe(false);\n expect(cache.get(convA)).toBeUndefined();\n expect(resolveLoadPath(cache, convA)).toBe('server-fetch');\n });\n\n it('should not affect other conversations when invalidating one', () => {\n cache.setRaw(convA, createRawRows(convA, 3));\n cache.setRaw(convB, createRawRows(convB, 2));\n\n cache.invalidate(convA);\n\n expect(cache.has(convA)).toBe(false);\n expect(cache.has(convB)).toBe(true);\n });\n });\n\n describe('Raw data refresh', () => {\n it('should replace raw data and clear entities on refresh', () => {\n const raw = createRawRows(convA, 3);\n cache.setRaw(convA, raw);\n cache.setEntities(convA, createEntities(raw));\n\n const freshRaw = createRawRows(convA, 5);\n cache.refreshRaw(convA, freshRaw);\n\n const entry = cache.get(convA);\n expect(entry!.raw).toBe(freshRaw);\n expect(entry!.raw).toHaveLength(5);\n expect(entry!.entities).toBeNull(); // entities cleared\n expect(resolveLoadPath(cache, convA)).toBe('cached-raw');\n });\n });\n\n describe('In-place raw patching', () => {\n it('should patch a specific row in raw data', () => {\n const raw = createRawRows(convA, 3);\n cache.setRaw(convA, raw);\n\n cache.patchRaw(convA, 'detail-1', { IsPinned: true });\n\n const entry = cache.get(convA);\n expect(entry!.raw[1].IsPinned).toBe(true);\n // Other rows unaffected\n expect(entry!.raw[0].IsPinned).toBe(false);\n expect(entry!.raw[2].IsPinned).toBe(false);\n });\n\n it('should not affect entities when patching raw data', () => {\n const raw = createRawRows(convA, 3);\n cache.setRaw(convA, raw);\n const entities = createEntities(raw);\n cache.setEntities(convA, entities);\n\n cache.patchRaw(convA, 'detail-1', { IsPinned: true });\n\n // Raw is patched\n expect(cache.get(convA)!.raw[1].IsPinned).toBe(true);\n // Entity objects are separate — not patched by raw patch\n // (In the real component, the entity is already mutated by .Save())\n expect(cache.get(convA)!.entities![1].IsPinned).toBe(false);\n });\n\n it('should be a no-op for non-existent conversation', () => {\n cache.patchRaw('nonexistent', 'detail-0', { IsPinned: true });\n expect(cache.has('nonexistent')).toBe(false);\n });\n\n it('should be a no-op for non-existent message ID', () => {\n const raw = createRawRows(convA, 2);\n cache.setRaw(convA, raw);\n\n cache.patchRaw(convA, 'nonexistent-detail', { IsPinned: true });\n\n // Nothing changed\n expect(raw.every(r => r.IsPinned === false)).toBe(true);\n });\n });\n\n describe('Conversation switching simulation', () => {\n it('should provide instant switch when entities are cached', () => {\n // Visit conversation A — full load\n const rawA = createRawRows(convA, 10);\n cache.setRaw(convA, rawA);\n cache.setEntities(convA, createEntities(rawA));\n\n // Visit conversation B — full load\n const rawB = createRawRows(convB, 5);\n cache.setRaw(convB, rawB);\n cache.setEntities(convB, createEntities(rawB));\n\n // Switch back to A — should be instant (cached-entities path)\n expect(resolveLoadPath(cache, convA)).toBe('cached-entities');\n const entryA = cache.get(convA)!;\n expect(entryA.entities).toHaveLength(10);\n\n // Switch to B — also instant\n expect(resolveLoadPath(cache, convB)).toBe('cached-entities');\n const entryB = cache.get(convB)!;\n expect(entryB.entities).toHaveLength(5);\n });\n\n it('should fall back to raw conversion after invalidation', () => {\n const raw = createRawRows(convA, 3);\n cache.setRaw(convA, raw);\n cache.setEntities(convA, createEntities(raw));\n\n // New message arrives — cache invalidated\n cache.invalidate(convA);\n\n // Re-fetch from server, store new raw\n const freshRaw = createRawRows(convA, 4);\n cache.setRaw(convA, freshRaw);\n\n // Should need conversion (cached-raw path, not cached-entities)\n expect(resolveLoadPath(cache, convA)).toBe('cached-raw');\n\n // After conversion\n cache.setEntities(convA, createEntities(freshRaw));\n expect(resolveLoadPath(cache, convA)).toBe('cached-entities');\n });\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-naming.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/conversation-naming.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the SHARED conversation auto-naming helper — the single
|
|
3
|
-
* implementation behind both the composer's first-message naming and the realtime
|
|
4
|
-
* session path (sessions that created their own conversation name it on call end).
|
|
5
|
-
*/
|
|
6
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
-
import { AIEngineBase } from '@memberjunction/ai-engine-base';
|
|
8
|
-
import { ConversationEngine } from '@memberjunction/core-entities';
|
|
9
|
-
import { GenerateAndApplyConversationName, NAME_CONVERSATION_PROMPT } from '../lib/services/conversation-naming';
|
|
10
|
-
const USER = { ID: 'user-1' };
|
|
11
|
-
const PROVIDER = {};
|
|
12
|
-
describe('GenerateAndApplyConversationName', () => {
|
|
13
|
-
let saveSpy;
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
vi.spyOn(AIEngineBase.Instance, 'Config').mockResolvedValue(undefined);
|
|
16
|
-
vi.spyOn(AIEngineBase.Instance, 'Prompts', 'get').mockReturnValue([
|
|
17
|
-
{ ID: 'prompt-1', Name: NAME_CONVERSATION_PROMPT }
|
|
18
|
-
]);
|
|
19
|
-
saveSpy = vi.spyOn(ConversationEngine.Instance, 'SaveConversation').mockResolvedValue(true);
|
|
20
|
-
});
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
vi.restoreAllMocks();
|
|
23
|
-
});
|
|
24
|
-
it('runs the Name Conversation prompt, saves the parsed name + description, and returns them', async () => {
|
|
25
|
-
const runPrompt = vi.fn().mockResolvedValue({
|
|
26
|
-
success: true,
|
|
27
|
-
parsedResult: { name: 'Ottawa Weather Briefing', description: 'Weather and stocks' }
|
|
28
|
-
});
|
|
29
|
-
const result = await GenerateAndApplyConversationName({
|
|
30
|
-
ConversationId: 'conv-1',
|
|
31
|
-
MessageText: 'whats the weather in ottawa?',
|
|
32
|
-
Provider: PROVIDER,
|
|
33
|
-
CurrentUser: USER,
|
|
34
|
-
RunPrompt: runPrompt
|
|
35
|
-
});
|
|
36
|
-
expect(runPrompt).toHaveBeenCalledWith('prompt-1', 'whats the weather in ottawa?');
|
|
37
|
-
expect(saveSpy).toHaveBeenCalledWith('conv-1', { Name: 'Ottawa Weather Briefing', Description: 'Weather and stocks' }, USER);
|
|
38
|
-
expect(result).toEqual({ Name: 'Ottawa Weather Briefing', Description: 'Weather and stocks' });
|
|
39
|
-
});
|
|
40
|
-
it('parses fenced raw output when parsedResult is absent', async () => {
|
|
41
|
-
const result = await GenerateAndApplyConversationName({
|
|
42
|
-
ConversationId: 'conv-2',
|
|
43
|
-
MessageText: 'hello',
|
|
44
|
-
Provider: PROVIDER,
|
|
45
|
-
CurrentUser: USER,
|
|
46
|
-
RunPrompt: vi.fn().mockResolvedValue({
|
|
47
|
-
success: true,
|
|
48
|
-
output: '```json\n{"name":"Quick Hello"}\n```'
|
|
49
|
-
})
|
|
50
|
-
});
|
|
51
|
-
expect(result).toEqual({ Name: 'Quick Hello', Description: '' });
|
|
52
|
-
expect(saveSpy).toHaveBeenCalledWith('conv-2', { Name: 'Quick Hello', Description: '' }, USER);
|
|
53
|
-
});
|
|
54
|
-
it('returns null (no save) on timeout — the conversation keeps its default name', async () => {
|
|
55
|
-
vi.useFakeTimers();
|
|
56
|
-
const never = new Promise(() => undefined);
|
|
57
|
-
const pending = GenerateAndApplyConversationName({
|
|
58
|
-
ConversationId: 'conv-3',
|
|
59
|
-
MessageText: 'slow',
|
|
60
|
-
Provider: PROVIDER,
|
|
61
|
-
CurrentUser: USER,
|
|
62
|
-
TimeoutMs: 5000,
|
|
63
|
-
RunPrompt: vi.fn().mockReturnValue(never)
|
|
64
|
-
});
|
|
65
|
-
await vi.advanceTimersByTimeAsync(5001);
|
|
66
|
-
const result = await pending;
|
|
67
|
-
vi.useRealTimers();
|
|
68
|
-
expect(result).toBeNull();
|
|
69
|
-
expect(saveSpy).not.toHaveBeenCalled();
|
|
70
|
-
});
|
|
71
|
-
it('returns null when the Name Conversation prompt is not seeded', async () => {
|
|
72
|
-
vi.spyOn(AIEngineBase.Instance, 'Prompts', 'get').mockReturnValue([]);
|
|
73
|
-
const result = await GenerateAndApplyConversationName({
|
|
74
|
-
ConversationId: 'conv-4',
|
|
75
|
-
MessageText: 'x',
|
|
76
|
-
Provider: PROVIDER,
|
|
77
|
-
CurrentUser: USER,
|
|
78
|
-
RunPrompt: vi.fn()
|
|
79
|
-
});
|
|
80
|
-
expect(result).toBeNull();
|
|
81
|
-
});
|
|
82
|
-
it.each([
|
|
83
|
-
['failed run', { success: false }],
|
|
84
|
-
['empty result', null],
|
|
85
|
-
['unparseable output', { success: true, output: 'not json at all {{{' }],
|
|
86
|
-
['parsed without a name', { success: true, parsedResult: { description: 'only desc' } }]
|
|
87
|
-
])('returns null (no save) for %s', async (_label, runResult) => {
|
|
88
|
-
const result = await GenerateAndApplyConversationName({
|
|
89
|
-
ConversationId: 'conv-5',
|
|
90
|
-
MessageText: 'x',
|
|
91
|
-
Provider: PROVIDER,
|
|
92
|
-
CurrentUser: USER,
|
|
93
|
-
RunPrompt: vi.fn().mockResolvedValue(runResult)
|
|
94
|
-
});
|
|
95
|
-
expect(result).toBeNull();
|
|
96
|
-
expect(saveSpy).not.toHaveBeenCalled();
|
|
97
|
-
});
|
|
98
|
-
it('returns null when the save fails (name generated but not applied)', async () => {
|
|
99
|
-
saveSpy.mockResolvedValue(false);
|
|
100
|
-
const result = await GenerateAndApplyConversationName({
|
|
101
|
-
ConversationId: 'conv-6',
|
|
102
|
-
MessageText: 'x',
|
|
103
|
-
Provider: PROVIDER,
|
|
104
|
-
CurrentUser: USER,
|
|
105
|
-
RunPrompt: vi.fn().mockResolvedValue({ success: true, parsedResult: { name: 'N' } })
|
|
106
|
-
});
|
|
107
|
-
expect(result).toBeNull();
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
//# sourceMappingURL=conversation-naming.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-naming.test.js","sourceRoot":"","sources":["../../src/__tests__/conversation-naming.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unit tests for the SHARED conversation auto-naming helper — the single\n * implementation behind both the composer's first-message naming and the realtime\n * session path (sessions that created their own conversation name it on call end).\n */\nimport { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';\nimport { UserInfo } from '@memberjunction/core';\nimport { GraphQLDataProvider } from '@memberjunction/graphql-dataprovider';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\nimport { ConversationEngine } from '@memberjunction/core-entities';\nimport { GenerateAndApplyConversationName, NAME_CONVERSATION_PROMPT } from '../lib/services/conversation-naming';\n\nconst USER = { ID: 'user-1' } as unknown as UserInfo;\nconst PROVIDER = {} as unknown as GraphQLDataProvider;\n\ndescribe('GenerateAndApplyConversationName', () => {\n let saveSpy: ReturnType<typeof vi.spyOn>;\n\n beforeEach(() => {\n vi.spyOn(AIEngineBase.Instance, 'Config').mockResolvedValue(undefined as never);\n vi.spyOn(AIEngineBase.Instance, 'Prompts', 'get').mockReturnValue([\n { ID: 'prompt-1', Name: NAME_CONVERSATION_PROMPT }\n ] as never);\n saveSpy = vi.spyOn(ConversationEngine.Instance, 'SaveConversation').mockResolvedValue(true as never);\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n });\n\n it('runs the Name Conversation prompt, saves the parsed name + description, and returns them', async () => {\n const runPrompt = vi.fn().mockResolvedValue({\n success: true,\n parsedResult: { name: 'Ottawa Weather Briefing', description: 'Weather and stocks' }\n });\n\n const result = await GenerateAndApplyConversationName({\n ConversationId: 'conv-1',\n MessageText: 'whats the weather in ottawa?',\n Provider: PROVIDER,\n CurrentUser: USER,\n RunPrompt: runPrompt\n });\n\n expect(runPrompt).toHaveBeenCalledWith('prompt-1', 'whats the weather in ottawa?');\n expect(saveSpy).toHaveBeenCalledWith(\n 'conv-1',\n { Name: 'Ottawa Weather Briefing', Description: 'Weather and stocks' },\n USER\n );\n expect(result).toEqual({ Name: 'Ottawa Weather Briefing', Description: 'Weather and stocks' });\n });\n\n it('parses fenced raw output when parsedResult is absent', async () => {\n const result = await GenerateAndApplyConversationName({\n ConversationId: 'conv-2',\n MessageText: 'hello',\n Provider: PROVIDER,\n CurrentUser: USER,\n RunPrompt: vi.fn().mockResolvedValue({\n success: true,\n output: '```json\\n{\"name\":\"Quick Hello\"}\\n```'\n })\n });\n\n expect(result).toEqual({ Name: 'Quick Hello', Description: '' });\n expect(saveSpy).toHaveBeenCalledWith('conv-2', { Name: 'Quick Hello', Description: '' }, USER);\n });\n\n it('returns null (no save) on timeout — the conversation keeps its default name', async () => {\n vi.useFakeTimers();\n const never = new Promise<never>(() => undefined);\n const pending = GenerateAndApplyConversationName({\n ConversationId: 'conv-3',\n MessageText: 'slow',\n Provider: PROVIDER,\n CurrentUser: USER,\n TimeoutMs: 5000,\n RunPrompt: vi.fn().mockReturnValue(never)\n });\n await vi.advanceTimersByTimeAsync(5001);\n const result = await pending;\n vi.useRealTimers();\n\n expect(result).toBeNull();\n expect(saveSpy).not.toHaveBeenCalled();\n });\n\n it('returns null when the Name Conversation prompt is not seeded', async () => {\n vi.spyOn(AIEngineBase.Instance, 'Prompts', 'get').mockReturnValue([] as never);\n const result = await GenerateAndApplyConversationName({\n ConversationId: 'conv-4',\n MessageText: 'x',\n Provider: PROVIDER,\n CurrentUser: USER,\n RunPrompt: vi.fn()\n });\n expect(result).toBeNull();\n });\n\n it.each([\n ['failed run', { success: false }],\n ['empty result', null],\n ['unparseable output', { success: true, output: 'not json at all {{{' }],\n ['parsed without a name', { success: true, parsedResult: { description: 'only desc' } }]\n ])('returns null (no save) for %s', async (_label, runResult) => {\n const result = await GenerateAndApplyConversationName({\n ConversationId: 'conv-5',\n MessageText: 'x',\n Provider: PROVIDER,\n CurrentUser: USER,\n RunPrompt: vi.fn().mockResolvedValue(runResult)\n });\n expect(result).toBeNull();\n expect(saveSpy).not.toHaveBeenCalled();\n });\n\n it('returns null when the save fails (name generated but not applied)', async () => {\n saveSpy.mockResolvedValue(false as never);\n const result = await GenerateAndApplyConversationName({\n ConversationId: 'conv-6',\n MessageText: 'x',\n Provider: PROVIDER,\n CurrentUser: USER,\n RunPrompt: vi.fn().mockResolvedValue({ success: true, parsedResult: { name: 'N' } })\n });\n expect(result).toBeNull();\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"delegation-result-parser.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/delegation-result-parser.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { ParseDelegationResultJson } from '../lib/services/delegation-result-parser';
|
|
3
|
-
describe('ParseDelegationResultJson', () => {
|
|
4
|
-
it('parses a successful result with a runId', () => {
|
|
5
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'all done', runId: 'run-123' }));
|
|
6
|
-
expect(parsed).toEqual({ Success: true, Output: 'all done', RunID: 'run-123' });
|
|
7
|
-
});
|
|
8
|
-
it('parses a successful result without a runId (RunID undefined)', () => {
|
|
9
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'all done' }));
|
|
10
|
-
expect(parsed.Success).toBe(true);
|
|
11
|
-
expect(parsed.Output).toBe('all done');
|
|
12
|
-
expect(parsed.RunID).toBeUndefined();
|
|
13
|
-
});
|
|
14
|
-
it('ignores an empty-string runId', () => {
|
|
15
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'x', runId: '' }));
|
|
16
|
-
expect(parsed.RunID).toBeUndefined();
|
|
17
|
-
});
|
|
18
|
-
it('parses the structured error shape ({success:false, error})', () => {
|
|
19
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ success: false, error: 'boom' }));
|
|
20
|
-
expect(parsed).toEqual({ Success: false, Output: 'boom', RunID: undefined });
|
|
21
|
-
});
|
|
22
|
-
it('still carries runId on a failed result that reported one', () => {
|
|
23
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ success: false, error: 'boom', runId: 'run-9' }));
|
|
24
|
-
expect(parsed.Success).toBe(false);
|
|
25
|
-
expect(parsed.RunID).toBe('run-9');
|
|
26
|
-
});
|
|
27
|
-
it('treats success as true unless explicitly false', () => {
|
|
28
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ output: 'no flag' }));
|
|
29
|
-
expect(parsed.Success).toBe(true);
|
|
30
|
-
expect(parsed.Output).toBe('no flag');
|
|
31
|
-
});
|
|
32
|
-
it('surfaces a non-JSON payload as the raw output (success assumed)', () => {
|
|
33
|
-
const parsed = ParseDelegationResultJson('plain text result');
|
|
34
|
-
expect(parsed).toEqual({ Success: true, Output: 'plain text result' });
|
|
35
|
-
});
|
|
36
|
-
it('prefers output over error when both are present', () => {
|
|
37
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ success: false, output: 'partial', error: 'late failure' }));
|
|
38
|
-
expect(parsed.Output).toBe('partial');
|
|
39
|
-
});
|
|
40
|
-
describe('artifacts', () => {
|
|
41
|
-
it('parses artifacts into PascalCase entries', () => {
|
|
42
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({
|
|
43
|
-
success: true,
|
|
44
|
-
output: 'done',
|
|
45
|
-
runId: 'run-1',
|
|
46
|
-
artifacts: [{ artifactId: 'a-1', artifactVersionId: 'av-1', name: 'Weather Report' }]
|
|
47
|
-
}));
|
|
48
|
-
expect(parsed.Artifacts).toEqual([
|
|
49
|
-
{ ArtifactID: 'a-1', ArtifactVersionID: 'av-1', Name: 'Weather Report' }
|
|
50
|
-
]);
|
|
51
|
-
});
|
|
52
|
-
it('leaves Artifacts undefined when the field is absent', () => {
|
|
53
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'done' }));
|
|
54
|
-
expect(parsed.Artifacts).toBeUndefined();
|
|
55
|
-
});
|
|
56
|
-
it('leaves Artifacts undefined for an empty array', () => {
|
|
57
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'done', artifacts: [] }));
|
|
58
|
-
expect(parsed.Artifacts).toBeUndefined();
|
|
59
|
-
});
|
|
60
|
-
it('leaves Artifacts undefined when the field is not an array', () => {
|
|
61
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'done', artifacts: 'nope' }));
|
|
62
|
-
expect(parsed.Artifacts).toBeUndefined();
|
|
63
|
-
});
|
|
64
|
-
it('drops malformed entries (missing ids) and keeps valid ones', () => {
|
|
65
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({
|
|
66
|
-
success: true,
|
|
67
|
-
output: 'done',
|
|
68
|
-
artifacts: [
|
|
69
|
-
{ artifactId: 'a-1' }, // no version id → dropped
|
|
70
|
-
{ artifactVersionId: 'av-2' }, // no artifact id → dropped
|
|
71
|
-
{ artifactId: 42, artifactVersionId: 'av-3' }, // non-string id → dropped
|
|
72
|
-
{ artifactId: 'a-4', artifactVersionId: 'av-4', name: 'Kept' }
|
|
73
|
-
]
|
|
74
|
-
}));
|
|
75
|
-
expect(parsed.Artifacts).toEqual([{ ArtifactID: 'a-4', ArtifactVersionID: 'av-4', Name: 'Kept' }]);
|
|
76
|
-
});
|
|
77
|
-
it('falls back to "Artifact" for a missing or blank name', () => {
|
|
78
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({
|
|
79
|
-
success: true,
|
|
80
|
-
output: 'done',
|
|
81
|
-
artifacts: [
|
|
82
|
-
{ artifactId: 'a-1', artifactVersionId: 'av-1' },
|
|
83
|
-
{ artifactId: 'a-2', artifactVersionId: 'av-2', name: ' ' }
|
|
84
|
-
]
|
|
85
|
-
}));
|
|
86
|
-
expect(parsed.Artifacts?.map(a => a.Name)).toEqual(['Artifact', 'Artifact']);
|
|
87
|
-
});
|
|
88
|
-
it('trims artifact names', () => {
|
|
89
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({
|
|
90
|
-
success: true,
|
|
91
|
-
output: 'done',
|
|
92
|
-
artifacts: [{ artifactId: 'a-1', artifactVersionId: 'av-1', name: ' Q3 Summary ' }]
|
|
93
|
-
}));
|
|
94
|
-
expect(parsed.Artifacts?.[0].Name).toBe('Q3 Summary');
|
|
95
|
-
});
|
|
96
|
-
it('still carries artifacts on a failed result that reported them', () => {
|
|
97
|
-
const parsed = ParseDelegationResultJson(JSON.stringify({
|
|
98
|
-
success: false,
|
|
99
|
-
error: 'partial failure',
|
|
100
|
-
artifacts: [{ artifactId: 'a-1', artifactVersionId: 'av-1', name: 'Partial' }]
|
|
101
|
-
}));
|
|
102
|
-
expect(parsed.Success).toBe(false);
|
|
103
|
-
expect(parsed.Artifacts).toHaveLength(1);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
//# sourceMappingURL=delegation-result-parser.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"delegation-result-parser.test.js","sourceRoot":"","sources":["../../src/__tests__/delegation-result-parser.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport { ParseDelegationResultJson } from '../lib/services/delegation-result-parser';\n\ndescribe('ParseDelegationResultJson', () => {\n it('parses a successful result with a runId', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'all done', runId: 'run-123' }));\n expect(parsed).toEqual({ Success: true, Output: 'all done', RunID: 'run-123' });\n });\n\n it('parses a successful result without a runId (RunID undefined)', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'all done' }));\n expect(parsed.Success).toBe(true);\n expect(parsed.Output).toBe('all done');\n expect(parsed.RunID).toBeUndefined();\n });\n\n it('ignores an empty-string runId', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'x', runId: '' }));\n expect(parsed.RunID).toBeUndefined();\n });\n\n it('parses the structured error shape ({success:false, error})', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ success: false, error: 'boom' }));\n expect(parsed).toEqual({ Success: false, Output: 'boom', RunID: undefined });\n });\n\n it('still carries runId on a failed result that reported one', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ success: false, error: 'boom', runId: 'run-9' }));\n expect(parsed.Success).toBe(false);\n expect(parsed.RunID).toBe('run-9');\n });\n\n it('treats success as true unless explicitly false', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ output: 'no flag' }));\n expect(parsed.Success).toBe(true);\n expect(parsed.Output).toBe('no flag');\n });\n\n it('surfaces a non-JSON payload as the raw output (success assumed)', () => {\n const parsed = ParseDelegationResultJson('plain text result');\n expect(parsed).toEqual({ Success: true, Output: 'plain text result' });\n });\n\n it('prefers output over error when both are present', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ success: false, output: 'partial', error: 'late failure' }));\n expect(parsed.Output).toBe('partial');\n });\n\n describe('artifacts', () => {\n it('parses artifacts into PascalCase entries', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({\n success: true,\n output: 'done',\n runId: 'run-1',\n artifacts: [{ artifactId: 'a-1', artifactVersionId: 'av-1', name: 'Weather Report' }]\n }));\n expect(parsed.Artifacts).toEqual([\n { ArtifactID: 'a-1', ArtifactVersionID: 'av-1', Name: 'Weather Report' }\n ]);\n });\n\n it('leaves Artifacts undefined when the field is absent', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'done' }));\n expect(parsed.Artifacts).toBeUndefined();\n });\n\n it('leaves Artifacts undefined for an empty array', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'done', artifacts: [] }));\n expect(parsed.Artifacts).toBeUndefined();\n });\n\n it('leaves Artifacts undefined when the field is not an array', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({ success: true, output: 'done', artifacts: 'nope' }));\n expect(parsed.Artifacts).toBeUndefined();\n });\n\n it('drops malformed entries (missing ids) and keeps valid ones', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({\n success: true,\n output: 'done',\n artifacts: [\n { artifactId: 'a-1' }, // no version id → dropped\n { artifactVersionId: 'av-2' }, // no artifact id → dropped\n { artifactId: 42, artifactVersionId: 'av-3' }, // non-string id → dropped\n { artifactId: 'a-4', artifactVersionId: 'av-4', name: 'Kept' }\n ]\n }));\n expect(parsed.Artifacts).toEqual([{ ArtifactID: 'a-4', ArtifactVersionID: 'av-4', Name: 'Kept' }]);\n });\n\n it('falls back to \"Artifact\" for a missing or blank name', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({\n success: true,\n output: 'done',\n artifacts: [\n { artifactId: 'a-1', artifactVersionId: 'av-1' },\n { artifactId: 'a-2', artifactVersionId: 'av-2', name: ' ' }\n ]\n }));\n expect(parsed.Artifacts?.map(a => a.Name)).toEqual(['Artifact', 'Artifact']);\n });\n\n it('trims artifact names', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({\n success: true,\n output: 'done',\n artifacts: [{ artifactId: 'a-1', artifactVersionId: 'av-1', name: ' Q3 Summary ' }]\n }));\n expect(parsed.Artifacts?.[0].Name).toBe('Q3 Summary');\n });\n\n it('still carries artifacts on a failed result that reported them', () => {\n const parsed = ParseDelegationResultJson(JSON.stringify({\n success: false,\n error: 'partial failure',\n artifacts: [{ artifactId: 'a-1', artifactVersionId: 'av-1', name: 'Partial' }]\n }));\n expect(parsed.Success).toBe(false);\n expect(parsed.Artifacts).toHaveLength(1);\n });\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"distinct-artifacts.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/distinct-artifacts.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { selectDistinctLatestArtifacts } from '../lib/utils/distinct-artifacts';
|
|
3
|
-
/**
|
|
4
|
-
* Regression guard for the bug where a message carrying TWO distinct artifacts
|
|
5
|
-
* (e.g. a research report + a standalone generated infographic) only ever showed
|
|
6
|
-
* ONE of them — the message list previously displayed `artifactList[length - 1]`,
|
|
7
|
-
* silently hiding the report behind the image. The fix surfaces every distinct
|
|
8
|
-
* artifact (latest version each); these tests lock that behavior in.
|
|
9
|
-
*/
|
|
10
|
-
describe('selectDistinctLatestArtifacts', () => {
|
|
11
|
-
const make = (artifactId, versionNumber, tag = '') => ({ artifactId, versionNumber, tag });
|
|
12
|
-
it('returns an empty array for empty input', () => {
|
|
13
|
-
expect(selectDistinctLatestArtifacts([])).toEqual([]);
|
|
14
|
-
});
|
|
15
|
-
it('keeps BOTH distinct artifacts (the report-hidden-behind-image bug)', () => {
|
|
16
|
-
const report = make('report-id', 1, 'report');
|
|
17
|
-
const image = make('image-id', 1, 'image');
|
|
18
|
-
const result = selectDistinctLatestArtifacts([report, image]);
|
|
19
|
-
expect(result).toHaveLength(2);
|
|
20
|
-
expect(result.map(r => r.artifactId)).toEqual(['report-id', 'image-id']);
|
|
21
|
-
});
|
|
22
|
-
it('collapses multiple versions of the SAME artifact to the latest', () => {
|
|
23
|
-
const v1 = make('a', 1, 'v1');
|
|
24
|
-
const v2 = make('a', 2, 'v2');
|
|
25
|
-
const v3 = make('a', 3, 'v3');
|
|
26
|
-
const result = selectDistinctLatestArtifacts([v1, v3, v2]);
|
|
27
|
-
expect(result).toHaveLength(1);
|
|
28
|
-
expect(result[0].versionNumber).toBe(3);
|
|
29
|
-
expect(result[0].tag).toBe('v3');
|
|
30
|
-
});
|
|
31
|
-
it('handles a mix: distinct artifacts each collapsed to their latest version', () => {
|
|
32
|
-
const reportV1 = make('report', 1, 'r1');
|
|
33
|
-
const reportV2 = make('report', 2, 'r2');
|
|
34
|
-
const image = make('image', 1, 'img');
|
|
35
|
-
const result = selectDistinctLatestArtifacts([reportV1, image, reportV2]);
|
|
36
|
-
expect(result).toHaveLength(2);
|
|
37
|
-
const byId = Object.fromEntries(result.map(r => [r.artifactId, r]));
|
|
38
|
-
expect(byId['report'].versionNumber).toBe(2);
|
|
39
|
-
expect(byId['report'].tag).toBe('r2');
|
|
40
|
-
expect(byId['image'].versionNumber).toBe(1);
|
|
41
|
-
});
|
|
42
|
-
it('preserves first-seen ordering of distinct artifacts', () => {
|
|
43
|
-
const image = make('image', 1);
|
|
44
|
-
const report = make('report', 1);
|
|
45
|
-
// image seen first → image stays first even though report has a later update
|
|
46
|
-
const result = selectDistinctLatestArtifacts([image, report, make('report', 2)]);
|
|
47
|
-
expect(result.map(r => r.artifactId)).toEqual(['image', 'report']);
|
|
48
|
-
});
|
|
49
|
-
it('does not mutate the input array', () => {
|
|
50
|
-
const input = [make('a', 1), make('a', 2), make('b', 1)];
|
|
51
|
-
const copy = [...input];
|
|
52
|
-
selectDistinctLatestArtifacts(input);
|
|
53
|
-
expect(input).toEqual(copy);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
//# sourceMappingURL=distinct-artifacts.test.js.map
|