@copilotkitnext/angular 0.0.2 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/README.md +3 -3
- package/dist/components/chat/copilot-chat-assistant-message.component.d.ts +10 -10
- package/dist/components/chat/copilot-chat-message-view.component.d.ts +42 -42
- package/dist/components/chat/copilot-chat-view.component.d.ts +14 -14
- package/dist/core/copilotkit.providers.d.ts +1 -1
- package/dist/core/copilotkit.service.d.ts +5 -5
- package/dist/core/copilotkit.types.d.ts +8 -10
- package/dist/directives/copilotkit-frontend-tool.directive.d.ts +1 -1
- package/dist/esm2022/components/chat/copilot-chat-assistant-message-buttons.component.mjs +384 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message-renderer.component.mjs +286 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message-toolbar.component.mjs +27 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message.component.mjs +433 -0
- package/dist/esm2022/components/chat/copilot-chat-assistant-message.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat-audio-recorder.component.mjs +202 -0
- package/dist/esm2022/components/chat/copilot-chat-buttons.component.mjs +321 -0
- package/dist/esm2022/components/chat/copilot-chat-input-defaults.mjs +38 -0
- package/dist/esm2022/components/chat/copilot-chat-input.component.mjs +666 -0
- package/dist/esm2022/components/chat/copilot-chat-input.types.mjs +10 -0
- package/dist/esm2022/components/chat/copilot-chat-message-view-cursor.component.mjs +45 -0
- package/dist/esm2022/components/chat/copilot-chat-message-view.component.mjs +296 -0
- package/dist/esm2022/components/chat/copilot-chat-message-view.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat-textarea.component.mjs +188 -0
- package/dist/esm2022/components/chat/copilot-chat-tool-calls-view.component.mjs +216 -0
- package/dist/esm2022/components/chat/copilot-chat-toolbar.component.mjs +25 -0
- package/dist/esm2022/components/chat/copilot-chat-tools-menu.component.mjs +199 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-branch-navigation.component.mjs +137 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-buttons.component.mjs +207 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-renderer.component.mjs +35 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message-toolbar.component.mjs +34 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message.component.mjs +341 -0
- package/dist/esm2022/components/chat/copilot-chat-user-message.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat-view-disclaimer.component.mjs +52 -0
- package/dist/esm2022/components/chat/copilot-chat-view-feather.component.mjs +55 -0
- package/dist/esm2022/components/chat/copilot-chat-view-handlers.service.mjs +19 -0
- package/dist/esm2022/components/chat/copilot-chat-view-input-container.component.mjs +110 -0
- package/dist/esm2022/components/chat/copilot-chat-view-scroll-to-bottom-button.component.mjs +93 -0
- package/dist/esm2022/components/chat/copilot-chat-view-scroll-view.component.mjs +443 -0
- package/dist/esm2022/components/chat/copilot-chat-view.component.mjs +479 -0
- package/dist/esm2022/components/chat/copilot-chat-view.types.mjs +2 -0
- package/dist/esm2022/components/chat/copilot-chat.component.mjs +214 -0
- package/dist/esm2022/components/copilotkit-tool-render.component.mjs +153 -0
- package/dist/esm2022/copilotkitnext-angular.mjs +5 -0
- package/dist/esm2022/core/chat-configuration/chat-configuration.providers.mjs +65 -0
- package/dist/esm2022/core/chat-configuration/chat-configuration.service.mjs +145 -0
- package/dist/esm2022/core/chat-configuration/chat-configuration.types.mjs +26 -0
- package/dist/esm2022/core/copilotkit.providers.mjs +34 -0
- package/dist/esm2022/core/copilotkit.service.mjs +426 -0
- package/dist/esm2022/core/copilotkit.types.mjs +13 -0
- package/dist/esm2022/directives/copilotkit-agent-context.directive.mjs +130 -0
- package/dist/esm2022/directives/copilotkit-agent.directive.mjs +217 -0
- package/dist/esm2022/directives/copilotkit-chat-config.directive.mjs +218 -0
- package/dist/esm2022/directives/copilotkit-config.directive.mjs +94 -0
- package/dist/esm2022/directives/copilotkit-frontend-tool.directive.mjs +128 -0
- package/dist/esm2022/directives/copilotkit-human-in-the-loop.directive.mjs +265 -0
- package/dist/esm2022/directives/stick-to-bottom.directive.mjs +181 -0
- package/dist/esm2022/index.mjs +70 -0
- package/dist/esm2022/lib/directives/tooltip.directive.mjs +211 -0
- package/dist/esm2022/lib/slots/copilot-slot.component.mjs +144 -0
- package/dist/esm2022/lib/slots/slot.types.mjs +6 -0
- package/dist/esm2022/lib/slots/slot.utils.mjs +222 -0
- package/dist/esm2022/lib/utils.mjs +10 -0
- package/dist/esm2022/services/resize-observer.service.mjs +152 -0
- package/dist/esm2022/services/scroll-position.service.mjs +124 -0
- package/dist/esm2022/types/frontend-tool.mjs +2 -0
- package/dist/esm2022/types/human-in-the-loop.mjs +2 -0
- package/dist/esm2022/utils/agent-context.utils.mjs +114 -0
- package/dist/esm2022/utils/agent.utils.mjs +204 -0
- package/dist/esm2022/utils/chat-config.utils.mjs +186 -0
- package/dist/esm2022/utils/copilotkit.utils.mjs +20 -0
- package/dist/esm2022/utils/frontend-tool.utils.mjs +224 -0
- package/dist/esm2022/utils/human-in-the-loop.utils.mjs +293 -0
- package/dist/fesm2022/copilotkitnext-angular.mjs +174 -187
- package/dist/fesm2022/copilotkitnext-angular.mjs.map +1 -1
- package/dist/utils/frontend-tool.utils.d.ts +1 -1
- package/package.json +23 -20
- package/vitest.config.mts +32 -21
- package/.turbo/turbo-build.log +0 -38
- package/.turbo/turbo-check-types.log +0 -0
- package/.turbo/turbo-test.log +0 -71
- package/ng-package.json +0 -19
- package/src/components/chat/__tests__/copilot-chat-assistant-message.component.spec.ts +0 -282
- package/src/components/chat/__tests__/copilot-chat-input.component.spec.ts +0 -419
- package/src/components/chat/__tests__/copilot-chat-message-view.component.spec.ts +0 -372
- package/src/components/chat/__tests__/copilot-chat-user-message.component.spec.ts +0 -249
- package/src/components/chat/copilot-chat-assistant-message-buttons.component.ts +0 -292
- package/src/components/chat/copilot-chat-assistant-message-renderer.component.ts +0 -472
- package/src/components/chat/copilot-chat-assistant-message-toolbar.component.ts +0 -29
- package/src/components/chat/copilot-chat-assistant-message.component.ts +0 -463
- package/src/components/chat/copilot-chat-assistant-message.types.ts +0 -50
- package/src/components/chat/copilot-chat-audio-recorder.component.ts +0 -241
- package/src/components/chat/copilot-chat-buttons.component.ts +0 -308
- package/src/components/chat/copilot-chat-buttons.component.ts.bak +0 -471
- package/src/components/chat/copilot-chat-input-defaults.ts +0 -47
- package/src/components/chat/copilot-chat-input.component.ts +0 -512
- package/src/components/chat/copilot-chat-input.types.ts +0 -148
- package/src/components/chat/copilot-chat-message-view-cursor.component.ts +0 -51
- package/src/components/chat/copilot-chat-message-view.component.ts +0 -233
- package/src/components/chat/copilot-chat-message-view.types.ts +0 -39
- package/src/components/chat/copilot-chat-textarea.component.ts +0 -220
- package/src/components/chat/copilot-chat-tool-calls-view.component.ts +0 -261
- package/src/components/chat/copilot-chat-toolbar.component.ts +0 -35
- package/src/components/chat/copilot-chat-tools-menu.component.ts +0 -185
- package/src/components/chat/copilot-chat-user-message-branch-navigation.component.ts +0 -121
- package/src/components/chat/copilot-chat-user-message-buttons.component.ts +0 -170
- package/src/components/chat/copilot-chat-user-message-renderer.component.ts +0 -37
- package/src/components/chat/copilot-chat-user-message-toolbar.component.ts +0 -37
- package/src/components/chat/copilot-chat-user-message.component.ts +0 -247
- package/src/components/chat/copilot-chat-user-message.types.ts +0 -42
- package/src/components/chat/copilot-chat-view-disclaimer.component.ts +0 -51
- package/src/components/chat/copilot-chat-view-feather.component.ts +0 -47
- package/src/components/chat/copilot-chat-view-handlers.service.ts +0 -14
- package/src/components/chat/copilot-chat-view-input-container.component.ts +0 -87
- package/src/components/chat/copilot-chat-view-scroll-to-bottom-button.component.ts +0 -79
- package/src/components/chat/copilot-chat-view-scroll-view.component.ts +0 -322
- package/src/components/chat/copilot-chat-view.component.ts +0 -420
- package/src/components/chat/copilot-chat-view.types.ts +0 -52
- package/src/components/chat/copilot-chat.component.ts +0 -232
- package/src/components/copilotkit-tool-render.component.ts +0 -169
- package/src/core/__tests__/copilotkit.service.spec.ts +0 -1051
- package/src/core/__tests__/copilotkit.service.wildcard.spec.ts +0 -316
- package/src/core/chat-configuration/__tests__/chat-configuration.service.spec.ts +0 -287
- package/src/core/chat-configuration/chat-configuration.providers.ts +0 -71
- package/src/core/chat-configuration/chat-configuration.service.ts +0 -162
- package/src/core/chat-configuration/chat-configuration.types.ts +0 -57
- package/src/core/copilotkit.providers.ts +0 -59
- package/src/core/copilotkit.service.ts +0 -542
- package/src/core/copilotkit.types.ts +0 -132
- package/src/directives/__tests__/copilotkit-agent-context.directive.spec.ts +0 -384
- package/src/directives/__tests__/copilotkit-agent.directive.spec.ts +0 -253
- package/src/directives/__tests__/copilotkit-chat-config.directive.spec.ts +0 -385
- package/src/directives/__tests__/copilotkit-config.directive.spec.ts +0 -69
- package/src/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts +0 -60
- package/src/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts +0 -108
- package/src/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts +0 -452
- package/src/directives/copilotkit-agent-context.directive.ts +0 -138
- package/src/directives/copilotkit-agent.directive.ts +0 -225
- package/src/directives/copilotkit-chat-config.directive.ts +0 -241
- package/src/directives/copilotkit-config.directive.ts +0 -81
- package/src/directives/copilotkit-frontend-tool.directive.ts +0 -145
- package/src/directives/copilotkit-human-in-the-loop.directive.ts +0 -281
- package/src/directives/stick-to-bottom.directive.ts +0 -204
- package/src/index.ts +0 -105
- package/src/lib/directives/tooltip.directive.ts +0 -292
- package/src/lib/slots/__tests__/slot.utils.spec.ts +0 -377
- package/src/lib/slots/copilot-slot.component.ts +0 -135
- package/src/lib/slots/index.ts +0 -3
- package/src/lib/slots/slot.types.ts +0 -64
- package/src/lib/slots/slot.utils.ts +0 -289
- package/src/lib/utils.ts +0 -10
- package/src/public-api.ts +0 -1
- package/src/services/resize-observer.service.ts +0 -181
- package/src/services/scroll-position.service.ts +0 -169
- package/src/styles/globals.css +0 -266
- package/src/styles/index.css +0 -3
- package/src/test-setup.ts +0 -15
- package/src/testing/index.ts +0 -3
- package/src/testing/testing.utils.ts +0 -248
- package/src/types/frontend-tool.ts +0 -44
- package/src/types/human-in-the-loop.ts +0 -52
- package/src/utils/__tests__/agent.utils.spec.ts +0 -234
- package/src/utils/__tests__/chat-config.utils.spec.ts +0 -306
- package/src/utils/__tests__/frontend-tool-inject.spec.ts +0 -350
- package/src/utils/__tests__/frontend-tool-integration.spec.ts +0 -199
- package/src/utils/__tests__/frontend-tool.utils.spec.ts +0 -272
- package/src/utils/__tests__/human-in-the-loop.utils.spec.ts +0 -365
- package/src/utils/agent-context.utils.ts +0 -133
- package/src/utils/agent.utils.ts +0 -239
- package/src/utils/chat-config.utils.ts +0 -221
- package/src/utils/copilotkit.utils.ts +0 -20
- package/src/utils/frontend-tool.utils.ts +0 -266
- package/src/utils/human-in-the-loop.utils.ts +0 -359
- package/tsconfig.spec.json +0 -12
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
-
import { Component, TemplateRef, ViewChild } from '@angular/core';
|
|
3
|
-
import { CommonModule } from '@angular/common';
|
|
4
|
-
import { CopilotChatMessageViewComponent } from '../copilot-chat-message-view.component';
|
|
5
|
-
import { CopilotChatMessageViewCursorComponent } from '../copilot-chat-message-view-cursor.component';
|
|
6
|
-
import { CopilotChatAssistantMessageComponent } from '../copilot-chat-assistant-message.component';
|
|
7
|
-
import { CopilotChatUserMessageComponent } from '../copilot-chat-user-message.component';
|
|
8
|
-
import { CopilotChatViewHandlersService } from '../copilot-chat-view-handlers.service';
|
|
9
|
-
import { provideCopilotKit } from '../../../core/copilotkit.providers';
|
|
10
|
-
import { provideCopilotChatConfiguration } from '../../../core/chat-configuration/chat-configuration.providers';
|
|
11
|
-
import { Message, AssistantMessage, UserMessage } from '@ag-ui/client';
|
|
12
|
-
|
|
13
|
-
describe('CopilotChatMessageViewComponent', () => {
|
|
14
|
-
let component: CopilotChatMessageViewComponent;
|
|
15
|
-
let fixture: ComponentFixture<CopilotChatMessageViewComponent>;
|
|
16
|
-
|
|
17
|
-
const mockAssistantMessage: AssistantMessage = {
|
|
18
|
-
id: 'assistant-1',
|
|
19
|
-
role: 'assistant',
|
|
20
|
-
content: 'Hello! How can I help you today?',
|
|
21
|
-
createdAt: new Date()
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const mockUserMessage: UserMessage = {
|
|
25
|
-
id: 'user-1',
|
|
26
|
-
role: 'user',
|
|
27
|
-
content: 'I need help with Angular',
|
|
28
|
-
createdAt: new Date()
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const mockMessages: Message[] = [
|
|
32
|
-
mockUserMessage,
|
|
33
|
-
mockAssistantMessage
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
beforeEach(async () => {
|
|
37
|
-
await TestBed.configureTestingModule({
|
|
38
|
-
imports: [
|
|
39
|
-
CommonModule,
|
|
40
|
-
CopilotChatMessageViewComponent,
|
|
41
|
-
CopilotChatMessageViewCursorComponent,
|
|
42
|
-
CopilotChatAssistantMessageComponent,
|
|
43
|
-
CopilotChatUserMessageComponent
|
|
44
|
-
],
|
|
45
|
-
providers: [
|
|
46
|
-
provideCopilotKit({}),
|
|
47
|
-
provideCopilotChatConfiguration({}),
|
|
48
|
-
CopilotChatViewHandlersService
|
|
49
|
-
]
|
|
50
|
-
}).compileComponents();
|
|
51
|
-
|
|
52
|
-
fixture = TestBed.createComponent(CopilotChatMessageViewComponent);
|
|
53
|
-
component = fixture.componentInstance;
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('Basic Rendering', () => {
|
|
57
|
-
it('should create', () => {
|
|
58
|
-
expect(component).toBeTruthy();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should render with default classes "flex flex-col"', () => {
|
|
62
|
-
fixture.detectChanges();
|
|
63
|
-
const container = fixture.nativeElement.querySelector('div');
|
|
64
|
-
expect(container).toBeTruthy();
|
|
65
|
-
expect(container.className).toContain('flex');
|
|
66
|
-
expect(container.className).toContain('flex-col');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should render empty when no messages provided', () => {
|
|
70
|
-
component.messages = [];
|
|
71
|
-
fixture.detectChanges();
|
|
72
|
-
|
|
73
|
-
const messages = fixture.nativeElement.querySelectorAll('copilot-chat-assistant-message, copilot-chat-user-message');
|
|
74
|
-
expect(messages.length).toBe(0);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should render single assistant message', () => {
|
|
78
|
-
component.messages = [mockAssistantMessage];
|
|
79
|
-
fixture.detectChanges();
|
|
80
|
-
|
|
81
|
-
const assistantMessages = fixture.nativeElement.querySelectorAll('copilot-chat-assistant-message');
|
|
82
|
-
const userMessages = fixture.nativeElement.querySelectorAll('copilot-chat-user-message');
|
|
83
|
-
|
|
84
|
-
expect(assistantMessages.length).toBe(1);
|
|
85
|
-
expect(userMessages.length).toBe(0);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should render single user message', () => {
|
|
89
|
-
component.messages = [mockUserMessage];
|
|
90
|
-
fixture.detectChanges();
|
|
91
|
-
|
|
92
|
-
const assistantMessages = fixture.nativeElement.querySelectorAll('copilot-chat-assistant-message');
|
|
93
|
-
const userMessages = fixture.nativeElement.querySelectorAll('copilot-chat-user-message');
|
|
94
|
-
|
|
95
|
-
expect(assistantMessages.length).toBe(0);
|
|
96
|
-
expect(userMessages.length).toBe(1);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should render mixed messages in order', () => {
|
|
100
|
-
component.messages = mockMessages;
|
|
101
|
-
fixture.detectChanges();
|
|
102
|
-
|
|
103
|
-
const assistantMessages = fixture.nativeElement.querySelectorAll('copilot-chat-assistant-message');
|
|
104
|
-
const userMessages = fixture.nativeElement.querySelectorAll('copilot-chat-user-message');
|
|
105
|
-
|
|
106
|
-
expect(userMessages.length).toBe(1);
|
|
107
|
-
expect(assistantMessages.length).toBe(1);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should filter out messages with invalid roles', () => {
|
|
111
|
-
const invalidMessage: any = {
|
|
112
|
-
id: 'system-1',
|
|
113
|
-
role: 'system',
|
|
114
|
-
content: 'System message'
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
component.messages = [...mockMessages, invalidMessage];
|
|
118
|
-
fixture.detectChanges();
|
|
119
|
-
|
|
120
|
-
const assistantMessages = fixture.nativeElement.querySelectorAll('copilot-chat-assistant-message');
|
|
121
|
-
const userMessages = fixture.nativeElement.querySelectorAll('copilot-chat-user-message');
|
|
122
|
-
|
|
123
|
-
expect(userMessages.length).toBe(1);
|
|
124
|
-
expect(assistantMessages.length).toBe(1);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should handle null/undefined messages gracefully', () => {
|
|
128
|
-
component.messages = [null as any, undefined as any, mockAssistantMessage];
|
|
129
|
-
fixture.detectChanges();
|
|
130
|
-
|
|
131
|
-
const assistantMessages = fixture.nativeElement.querySelectorAll('copilot-chat-assistant-message');
|
|
132
|
-
expect(assistantMessages.length).toBe(1);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe('Cursor Functionality', () => {
|
|
137
|
-
it('should not show cursor by default', () => {
|
|
138
|
-
const cursor = fixture.nativeElement.querySelector('copilot-chat-message-view-cursor');
|
|
139
|
-
expect(cursor).toBeFalsy();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('should show cursor when showCursor is true', () => {
|
|
143
|
-
component.showCursor = true;
|
|
144
|
-
fixture.detectChanges();
|
|
145
|
-
|
|
146
|
-
const cursor = fixture.nativeElement.querySelector('copilot-chat-message-view-cursor');
|
|
147
|
-
expect(cursor).toBeTruthy();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('should apply correct cursor classes', () => {
|
|
151
|
-
component.showCursor = true;
|
|
152
|
-
fixture.detectChanges();
|
|
153
|
-
|
|
154
|
-
const cursorDiv = fixture.nativeElement.querySelector('copilot-chat-message-view-cursor div');
|
|
155
|
-
expect(cursorDiv).toBeTruthy();
|
|
156
|
-
expect(cursorDiv.className).toContain('w-[11px]');
|
|
157
|
-
expect(cursorDiv.className).toContain('h-[11px]');
|
|
158
|
-
expect(cursorDiv.className).toContain('rounded-full');
|
|
159
|
-
expect(cursorDiv.className).toContain('bg-foreground');
|
|
160
|
-
expect(cursorDiv.className).toContain('animate-pulse-cursor');
|
|
161
|
-
expect(cursorDiv.className).toContain('ml-1');
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('should apply custom cursor class', () => {
|
|
165
|
-
component.showCursor = true;
|
|
166
|
-
component.cursorClass = 'custom-cursor-class';
|
|
167
|
-
fixture.detectChanges();
|
|
168
|
-
|
|
169
|
-
const cursorDiv = fixture.nativeElement.querySelector('copilot-chat-message-view-cursor div');
|
|
170
|
-
expect(cursorDiv).toBeTruthy();
|
|
171
|
-
// The custom class is merged with default classes
|
|
172
|
-
expect(cursorDiv.className).toContain('w-[11px]');
|
|
173
|
-
expect(cursorDiv.className).toContain('h-[11px]');
|
|
174
|
-
// Note: Custom class is passed but may be overridden by Tailwind merge
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe('CSS Class Handling', () => {
|
|
179
|
-
it('should merge custom classes with default classes', () => {
|
|
180
|
-
component.inputClass = 'custom-container-class';
|
|
181
|
-
fixture.detectChanges();
|
|
182
|
-
|
|
183
|
-
const container = fixture.nativeElement.querySelector('div');
|
|
184
|
-
expect(container.className).toContain('flex');
|
|
185
|
-
expect(container.className).toContain('flex-col');
|
|
186
|
-
expect(container.className).toContain('custom-container-class');
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('should apply custom assistant message class', () => {
|
|
190
|
-
component.messages = [mockAssistantMessage];
|
|
191
|
-
component.assistantMessageClass = 'custom-assistant-class';
|
|
192
|
-
fixture.detectChanges();
|
|
193
|
-
|
|
194
|
-
const assistantMessage = fixture.nativeElement.querySelector('copilot-chat-assistant-message');
|
|
195
|
-
expect(assistantMessage).toBeTruthy();
|
|
196
|
-
// Class is passed as input to the component
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should apply custom user message class', () => {
|
|
200
|
-
component.messages = [mockUserMessage];
|
|
201
|
-
component.userMessageClass = 'custom-user-class';
|
|
202
|
-
fixture.detectChanges();
|
|
203
|
-
|
|
204
|
-
const userMessage = fixture.nativeElement.querySelector('copilot-chat-user-message');
|
|
205
|
-
expect(userMessage).toBeTruthy();
|
|
206
|
-
// Class is passed as input to the component
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe('Slot System', () => {
|
|
211
|
-
it('should pass message prop to assistant message component', () => {
|
|
212
|
-
component.messages = [mockAssistantMessage];
|
|
213
|
-
fixture.detectChanges();
|
|
214
|
-
|
|
215
|
-
const assistantMessage = fixture.nativeElement.querySelector('copilot-chat-assistant-message');
|
|
216
|
-
expect(assistantMessage).toBeTruthy();
|
|
217
|
-
// Message prop is passed via Angular input binding
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it('should pass message prop to user message component', () => {
|
|
221
|
-
component.messages = [mockUserMessage];
|
|
222
|
-
fixture.detectChanges();
|
|
223
|
-
|
|
224
|
-
const userMessage = fixture.nativeElement.querySelector('copilot-chat-user-message');
|
|
225
|
-
expect(userMessage).toBeTruthy();
|
|
226
|
-
// Message prop is passed via Angular input binding
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('should support custom assistant message props', () => {
|
|
230
|
-
component.messages = [mockAssistantMessage];
|
|
231
|
-
component.assistantMessageClass = 'custom-class';
|
|
232
|
-
fixture.detectChanges();
|
|
233
|
-
|
|
234
|
-
// Props are merged and passed to slot system
|
|
235
|
-
expect(component.mergeAssistantProps(mockAssistantMessage)).toEqual({
|
|
236
|
-
message: mockAssistantMessage,
|
|
237
|
-
messages: [mockAssistantMessage],
|
|
238
|
-
isLoading: false,
|
|
239
|
-
inputClass: 'custom-class'
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('should support custom user message props', () => {
|
|
244
|
-
component.messages = [mockUserMessage];
|
|
245
|
-
component.userMessageClass = 'custom-class';
|
|
246
|
-
fixture.detectChanges();
|
|
247
|
-
|
|
248
|
-
// Props are merged and passed to slot system
|
|
249
|
-
expect(component.mergeUserProps(mockUserMessage)).toEqual({
|
|
250
|
-
message: mockUserMessage,
|
|
251
|
-
inputClass: 'custom-class'
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
describe('Custom Layout Template', () => {
|
|
257
|
-
@Component({
|
|
258
|
-
template: `
|
|
259
|
-
<copilot-chat-message-view [messages]="messages" [showCursor]="showCursor">
|
|
260
|
-
<ng-template #customLayout let-messages="messages" let-showCursor="showCursor" let-messageElements="messageElements">
|
|
261
|
-
<div class="custom-layout">
|
|
262
|
-
<h2>Custom Layout</h2>
|
|
263
|
-
<div class="message-count">{{ messages?.length || 0 }} messages</div>
|
|
264
|
-
<div class="cursor-status">Cursor: {{ showCursor }}</div>
|
|
265
|
-
<div class="elements-count">Elements: {{ messageElements?.length || 0 }}</div>
|
|
266
|
-
</div>
|
|
267
|
-
</ng-template>
|
|
268
|
-
</copilot-chat-message-view>
|
|
269
|
-
`,
|
|
270
|
-
standalone: true,
|
|
271
|
-
imports: [CommonModule, CopilotChatMessageViewComponent]
|
|
272
|
-
})
|
|
273
|
-
class TestHostComponent {
|
|
274
|
-
messages = mockMessages;
|
|
275
|
-
showCursor = true;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
it('should render custom layout template when provided', async () => {
|
|
279
|
-
const hostFixture = TestBed.createComponent(TestHostComponent);
|
|
280
|
-
hostFixture.detectChanges();
|
|
281
|
-
|
|
282
|
-
const customLayout = hostFixture.nativeElement.querySelector('.custom-layout');
|
|
283
|
-
expect(customLayout).toBeTruthy();
|
|
284
|
-
|
|
285
|
-
const messageCount = hostFixture.nativeElement.querySelector('.message-count');
|
|
286
|
-
expect(messageCount.textContent).toContain('2 messages');
|
|
287
|
-
|
|
288
|
-
const cursorStatus = hostFixture.nativeElement.querySelector('.cursor-status');
|
|
289
|
-
expect(cursorStatus.textContent).toContain('Cursor: true');
|
|
290
|
-
|
|
291
|
-
const elementsCount = hostFixture.nativeElement.querySelector('.elements-count');
|
|
292
|
-
expect(elementsCount.textContent).toContain('Elements: 2');
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
describe('Performance', () => {
|
|
297
|
-
it('should handle large message lists efficiently', () => {
|
|
298
|
-
const largeMessageList: Message[] = [];
|
|
299
|
-
for (let i = 0; i < 100; i++) {
|
|
300
|
-
largeMessageList.push({
|
|
301
|
-
id: `msg-${i}`,
|
|
302
|
-
role: i % 2 === 0 ? 'user' : 'assistant',
|
|
303
|
-
content: `Message ${i}`,
|
|
304
|
-
createdAt: new Date()
|
|
305
|
-
} as Message);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
component.messages = largeMessageList;
|
|
309
|
-
fixture.detectChanges();
|
|
310
|
-
|
|
311
|
-
const allMessages = fixture.nativeElement.querySelectorAll('copilot-chat-assistant-message, copilot-chat-user-message');
|
|
312
|
-
expect(allMessages.length).toBe(100);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it('should use trackBy function for message iteration', () => {
|
|
316
|
-
const message: Message = {
|
|
317
|
-
id: 'test-1',
|
|
318
|
-
role: 'user',
|
|
319
|
-
content: 'Test'
|
|
320
|
-
} as Message;
|
|
321
|
-
|
|
322
|
-
const trackById = component.trackByMessageId(0, message);
|
|
323
|
-
expect(trackById).toBe('test-1');
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
describe('Edge Cases', () => {
|
|
328
|
-
it('should handle empty message content', () => {
|
|
329
|
-
const emptyMessage: Message = {
|
|
330
|
-
id: 'empty-1',
|
|
331
|
-
role: 'assistant',
|
|
332
|
-
content: ''
|
|
333
|
-
} as Message;
|
|
334
|
-
|
|
335
|
-
component.messages = [emptyMessage];
|
|
336
|
-
fixture.detectChanges();
|
|
337
|
-
|
|
338
|
-
const assistantMessage = fixture.nativeElement.querySelector('copilot-chat-assistant-message');
|
|
339
|
-
expect(assistantMessage).toBeTruthy();
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it('should handle messages with only whitespace', () => {
|
|
343
|
-
const whitespaceMessage: Message = {
|
|
344
|
-
id: 'whitespace-1',
|
|
345
|
-
role: 'user',
|
|
346
|
-
content: ' '
|
|
347
|
-
} as Message;
|
|
348
|
-
|
|
349
|
-
component.messages = [whitespaceMessage];
|
|
350
|
-
fixture.detectChanges();
|
|
351
|
-
|
|
352
|
-
const userMessage = fixture.nativeElement.querySelector('copilot-chat-user-message');
|
|
353
|
-
expect(userMessage).toBeTruthy();
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it('should maintain message order', () => {
|
|
357
|
-
const orderedMessages: Message[] = [
|
|
358
|
-
{ id: '1', role: 'user', content: 'First' } as Message,
|
|
359
|
-
{ id: '2', role: 'assistant', content: 'Second' } as Message,
|
|
360
|
-
{ id: '3', role: 'user', content: 'Third' } as Message,
|
|
361
|
-
{ id: '4', role: 'assistant', content: 'Fourth' } as Message
|
|
362
|
-
];
|
|
363
|
-
|
|
364
|
-
component.messages = orderedMessages;
|
|
365
|
-
fixture.detectChanges();
|
|
366
|
-
|
|
367
|
-
const allMessages = fixture.nativeElement.querySelectorAll('copilot-chat-assistant-message, copilot-chat-user-message');
|
|
368
|
-
expect(allMessages.length).toBe(4);
|
|
369
|
-
// Order is maintained through the template iteration
|
|
370
|
-
});
|
|
371
|
-
});
|
|
372
|
-
});
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
-
import { Component, DebugElement } from '@angular/core';
|
|
3
|
-
import { By } from '@angular/platform-browser';
|
|
4
|
-
import { CommonModule } from '@angular/common';
|
|
5
|
-
import { vi } from 'vitest';
|
|
6
|
-
import { CopilotChatUserMessageComponent } from '../copilot-chat-user-message.component';
|
|
7
|
-
import { provideCopilotChatConfiguration } from '../../../core/chat-configuration/chat-configuration.providers';
|
|
8
|
-
import { UserMessage } from '../copilot-chat-user-message.types';
|
|
9
|
-
|
|
10
|
-
describe('CopilotChatUserMessageComponent', () => {
|
|
11
|
-
let component: CopilotChatUserMessageComponent;
|
|
12
|
-
let fixture: ComponentFixture<CopilotChatUserMessageComponent>;
|
|
13
|
-
let element: HTMLElement;
|
|
14
|
-
|
|
15
|
-
const mockMessage: UserMessage = {
|
|
16
|
-
id: 'test-message-1',
|
|
17
|
-
content: 'Hello, this is a test message',
|
|
18
|
-
role: 'user',
|
|
19
|
-
timestamp: new Date()
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
beforeEach(async () => {
|
|
23
|
-
await TestBed.configureTestingModule({
|
|
24
|
-
imports: [CommonModule, CopilotChatUserMessageComponent],
|
|
25
|
-
providers: [
|
|
26
|
-
provideCopilotChatConfiguration({
|
|
27
|
-
labels: {
|
|
28
|
-
userMessageToolbarCopyMessageLabel: 'Copy',
|
|
29
|
-
userMessageToolbarEditMessageLabel: 'Edit'
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
]
|
|
33
|
-
}).compileComponents();
|
|
34
|
-
|
|
35
|
-
fixture = TestBed.createComponent(CopilotChatUserMessageComponent);
|
|
36
|
-
component = fixture.componentInstance;
|
|
37
|
-
element = fixture.nativeElement;
|
|
38
|
-
|
|
39
|
-
// Set required input
|
|
40
|
-
component.message = mockMessage;
|
|
41
|
-
fixture.detectChanges();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should create', () => {
|
|
45
|
-
expect(component).toBeTruthy();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should render message content', () => {
|
|
49
|
-
const messageElement = element.querySelector('copilot-chat-user-message-renderer');
|
|
50
|
-
expect(messageElement?.textContent?.trim()).toBe(mockMessage.content);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should have message id as data attribute', () => {
|
|
54
|
-
const messageContainer = element.querySelector('[data-message-id]');
|
|
55
|
-
expect(messageContainer?.getAttribute('data-message-id')).toBe(mockMessage.id);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should show copy button', () => {
|
|
59
|
-
const copyButton = element.querySelector('copilot-chat-user-message-copy-button');
|
|
60
|
-
expect(copyButton).toBeTruthy();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should show edit button when editMessage event is observed', async () => {
|
|
64
|
-
// Create a fresh component with a subscription
|
|
65
|
-
const freshFixture = TestBed.createComponent(CopilotChatUserMessageComponent);
|
|
66
|
-
const freshComponent = freshFixture.componentInstance;
|
|
67
|
-
const freshElement = freshFixture.nativeElement;
|
|
68
|
-
|
|
69
|
-
// Set required input
|
|
70
|
-
freshComponent.message = mockMessage;
|
|
71
|
-
|
|
72
|
-
// Subscribe before first change detection
|
|
73
|
-
const subscription = freshComponent.editMessage.subscribe(() => {});
|
|
74
|
-
|
|
75
|
-
// Now detect changes
|
|
76
|
-
freshFixture.detectChanges();
|
|
77
|
-
|
|
78
|
-
const editButton = freshElement.querySelector('copilot-chat-user-message-edit-button');
|
|
79
|
-
expect(editButton).toBeTruthy();
|
|
80
|
-
|
|
81
|
-
// Clean up
|
|
82
|
-
subscription.unsubscribe();
|
|
83
|
-
freshFixture.destroy();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should not show edit button when editMessage is not observed', () => {
|
|
87
|
-
const editButton = element.querySelector('copilot-chat-user-message-edit-button');
|
|
88
|
-
expect(editButton).toBeFalsy();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should emit editMessage event when edit button is clicked', (done) => {
|
|
92
|
-
component.editMessage.subscribe((event) => {
|
|
93
|
-
expect(event.message).toEqual(mockMessage);
|
|
94
|
-
done();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
fixture.detectChanges();
|
|
98
|
-
|
|
99
|
-
const editButton = element.querySelector('copilot-chat-user-message-edit-button button');
|
|
100
|
-
(editButton as HTMLButtonElement)?.click();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should copy message content to clipboard when copy button is clicked', async () => {
|
|
104
|
-
const writeTextSpy = vi.fn().mockResolvedValue(undefined);
|
|
105
|
-
Object.defineProperty(navigator, 'clipboard', {
|
|
106
|
-
value: { writeText: writeTextSpy },
|
|
107
|
-
writable: true
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const copyButton = element.querySelector('copilot-chat-user-message-copy-button button');
|
|
111
|
-
(copyButton as HTMLButtonElement)?.click();
|
|
112
|
-
|
|
113
|
-
await fixture.whenStable();
|
|
114
|
-
|
|
115
|
-
expect(writeTextSpy).toHaveBeenCalledWith(mockMessage.content);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('should show branch navigation when numberOfBranches > 1', () => {
|
|
119
|
-
component.numberOfBranches = 3;
|
|
120
|
-
component.branchIndex = 1;
|
|
121
|
-
component.switchToBranch.subscribe(); // Make it observed
|
|
122
|
-
fixture.detectChanges();
|
|
123
|
-
|
|
124
|
-
const branchNav = element.querySelector('copilot-chat-user-message-branch-navigation');
|
|
125
|
-
expect(branchNav).toBeTruthy();
|
|
126
|
-
|
|
127
|
-
const branchText = element.querySelector('.text-muted-foreground');
|
|
128
|
-
expect(branchText?.textContent?.trim()).toBe('2/3');
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should not show branch navigation when numberOfBranches = 1', () => {
|
|
132
|
-
component.numberOfBranches = 1;
|
|
133
|
-
component.switchToBranch.subscribe(); // Make it observed
|
|
134
|
-
fixture.detectChanges();
|
|
135
|
-
|
|
136
|
-
const branchNav = element.querySelector('copilot-chat-user-message-branch-navigation');
|
|
137
|
-
expect(branchNav).toBeFalsy();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('should emit switchToBranch event when branch navigation is clicked', (done) => {
|
|
141
|
-
component.numberOfBranches = 3;
|
|
142
|
-
component.branchIndex = 1;
|
|
143
|
-
|
|
144
|
-
component.switchToBranch.subscribe((event) => {
|
|
145
|
-
expect(event.branchIndex).toBe(2);
|
|
146
|
-
expect(event.numberOfBranches).toBe(3);
|
|
147
|
-
expect(event.message).toEqual(mockMessage);
|
|
148
|
-
done();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
fixture.detectChanges();
|
|
152
|
-
|
|
153
|
-
// Click next button
|
|
154
|
-
const buttons = element.querySelectorAll('copilot-chat-user-message-branch-navigation button');
|
|
155
|
-
const nextButton = buttons[1] as HTMLButtonElement;
|
|
156
|
-
nextButton?.click();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should apply custom class', () => {
|
|
160
|
-
component.inputClass = 'custom-test-class';
|
|
161
|
-
fixture.detectChanges();
|
|
162
|
-
|
|
163
|
-
const container = element.querySelector('.custom-test-class');
|
|
164
|
-
expect(container).toBeTruthy();
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it.skip('should render additional toolbar items', () => {
|
|
168
|
-
// This test is skipped as it requires a separate TestBed setup
|
|
169
|
-
// which conflicts with the existing setup
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('should handle empty message content', () => {
|
|
173
|
-
const emptyMessage = { ...mockMessage, content: undefined };
|
|
174
|
-
component.message = emptyMessage;
|
|
175
|
-
fixture.componentRef.setInput('message', emptyMessage);
|
|
176
|
-
fixture.detectChanges();
|
|
177
|
-
|
|
178
|
-
const messageElement = element.querySelector('copilot-chat-user-message-renderer');
|
|
179
|
-
expect(messageElement?.textContent?.trim()).toBe('');
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should handle multiline message content', () => {
|
|
183
|
-
const multilineMessage: UserMessage = {
|
|
184
|
-
id: 'multiline-message',
|
|
185
|
-
content: 'Line 1\nLine 2\nLine 3',
|
|
186
|
-
role: 'user',
|
|
187
|
-
timestamp: new Date()
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
component.message = multilineMessage;
|
|
191
|
-
fixture.componentRef.setInput('message', multilineMessage);
|
|
192
|
-
fixture.detectChanges();
|
|
193
|
-
|
|
194
|
-
const messageElement = element.querySelector('copilot-chat-user-message-renderer');
|
|
195
|
-
expect(messageElement?.textContent).toContain('Line 1');
|
|
196
|
-
expect(messageElement?.textContent).toContain('Line 2');
|
|
197
|
-
expect(messageElement?.textContent).toContain('Line 3');
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
describe('Branch Navigation', () => {
|
|
201
|
-
beforeEach(() => {
|
|
202
|
-
component.numberOfBranches = 5;
|
|
203
|
-
component.branchIndex = 2;
|
|
204
|
-
component.switchToBranch.subscribe();
|
|
205
|
-
fixture.detectChanges();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('should disable previous button on first branch', () => {
|
|
209
|
-
component.branchIndex = 0;
|
|
210
|
-
fixture.detectChanges();
|
|
211
|
-
|
|
212
|
-
const buttons = element.querySelectorAll('copilot-chat-user-message-branch-navigation button');
|
|
213
|
-
const prevButton = buttons[0] as HTMLButtonElement;
|
|
214
|
-
|
|
215
|
-
expect(prevButton.disabled).toBeTruthy();
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('should disable next button on last branch', () => {
|
|
219
|
-
component.branchIndex = 4;
|
|
220
|
-
fixture.detectChanges();
|
|
221
|
-
|
|
222
|
-
const buttons = element.querySelectorAll('copilot-chat-user-message-branch-navigation button');
|
|
223
|
-
const nextButton = buttons[1] as HTMLButtonElement;
|
|
224
|
-
|
|
225
|
-
expect(nextButton.disabled).toBeTruthy();
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('should enable both buttons on middle branch', () => {
|
|
229
|
-
const buttons = element.querySelectorAll('copilot-chat-user-message-branch-navigation button');
|
|
230
|
-
const prevButton = buttons[0] as HTMLButtonElement;
|
|
231
|
-
const nextButton = buttons[1] as HTMLButtonElement;
|
|
232
|
-
|
|
233
|
-
expect(prevButton.disabled).toBeFalsy();
|
|
234
|
-
expect(nextButton.disabled).toBeFalsy();
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
describe('Template Slots', () => {
|
|
239
|
-
it.skip('should use custom message renderer template', () => {
|
|
240
|
-
// This test is skipped as it requires a separate TestBed setup
|
|
241
|
-
// which conflicts with the existing setup
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it.skip('should use custom copy button template', () => {
|
|
245
|
-
// This test is skipped as it requires a separate TestBed setup
|
|
246
|
-
// which conflicts with the existing setup
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
});
|