Skip to content

Commit f9c5305

Browse files
google-genai-botcopybara-github
authored andcommitted
ADK changes
PiperOrigin-RevId: 810439442
1 parent 8cd5de0 commit f9c5305

File tree

10 files changed

+528
-35
lines changed

10 files changed

+528
-35
lines changed

src/app/components/event-tab/event-tab.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
@if (isTraceView()) {
2525
<p>Trace</p>
2626
}
27-
@if (traceData()) {
27+
@if (traceData().length > 0) {
2828
<mat-button-toggle-group name="fontStyle" aria-label="Font Style" style="scale: 0.8" [(ngModel)]="view">
2929
<mat-button-toggle value="events">Events</mat-button-toggle>
3030
<mat-button-toggle value="trace">Trace</mat-button-toggle>

src/app/components/event-tab/event-tab.component.spec.ts

Lines changed: 211 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,234 @@
1515
* limitations under the License.
1616
*/
1717

18+
import {HarnessLoader} from '@angular/cdk/testing';
19+
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
1820
import {ComponentFixture, TestBed} from '@angular/core/testing';
19-
import {MatDialogModule, MatDialogRef} from '@angular/material/dialog';
21+
import {MatButtonToggleHarness} from '@angular/material/button-toggle/testing';
22+
import {MatDialog} from '@angular/material/dialog';
23+
import {MatListHarness} from '@angular/material/list/testing';
24+
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
2025

26+
import {Span} from '../../core/models/Trace';
2127

2228
import {EventTabComponent} from './event-tab.component';
29+
import {TraceChartComponent} from './trace-chart/trace-chart.component';
30+
31+
const MOCK_TRACE_DATA: Span[] = [
32+
{
33+
name: 'agent.act',
34+
start_time: 1733084700000000000,
35+
end_time: 1733084760000000000,
36+
span_id: 'span-1',
37+
trace_id: 'trace-1',
38+
attributes: {
39+
'event_id': 1,
40+
'gcp.vertex.agent.invocation_id': '21332-322222',
41+
'gcp.vertex.agent.llm_request':
42+
'{"contents":[{"role":"user","parts":[{"text":"Hello"}]},{"role":"agent","parts":[{"text":"Hi. What can I help you with?"}]},{"role":"user","parts":[{"text":"I need help with my project."}]}]}',
43+
},
44+
},
45+
{
46+
name: 'tool.invoke',
47+
start_time: 1733084705000000000,
48+
end_time: 1733084755000000000,
49+
span_id: 'span-2',
50+
parent_span_id: 'span-1',
51+
trace_id: 'trace-1',
52+
attributes: {
53+
'tool_name': 'project_helper',
54+
},
55+
children: [
56+
{
57+
name: 'sub-tool-1.invoke',
58+
start_time: 1733084710000000000,
59+
end_time: 1733084750000000000,
60+
span_id: 'span-3',
61+
parent_span_id: 'span-2',
62+
trace_id: 'trace-1',
63+
attributes: {
64+
'sub_tool_name': 'sub_project_helper_1',
65+
},
66+
children: [
67+
{
68+
name: 'sub-tool-2.invoke',
69+
start_time: 1733084715000000000,
70+
end_time: 1733084745000000000,
71+
span_id: 'span-4',
72+
parent_span_id: 'span-3',
73+
trace_id: 'trace-1',
74+
attributes: {
75+
'sub_tool_name': 'sub_project_helper_2',
76+
},
77+
children: [
78+
{
79+
name: 'sub-tool-3.invoke',
80+
start_time: 1733084720000000000,
81+
end_time: 1733084740000000000,
82+
span_id: 'span-5',
83+
parent_span_id: 'span-4',
84+
trace_id: 'trace-1',
85+
attributes: {
86+
'sub_tool_name': 'sub_project_helper_3',
87+
},
88+
children: [],
89+
},
90+
],
91+
},
92+
],
93+
},
94+
],
95+
}
96+
] as Span[];
97+
98+
const MOCK_EVENTS_MAP = new Map<string, any>([
99+
['event1', {title: 'Event 1 Title'}],
100+
['event2', {title: 'Event 2 Title'}],
101+
]);
23102

24103
describe('EventTabComponent', () => {
25104
let component: EventTabComponent;
26105
let fixture: ComponentFixture<EventTabComponent>;
27-
const mockDialogRef = {
28-
close: jasmine.createSpy('close'),
29-
};
106+
let loader: HarnessLoader;
107+
const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']);
30108

31109
beforeEach(async () => {
32-
await TestBed.configureTestingModule({
33-
imports: [MatDialogModule, EventTabComponent],
34-
providers: [{ provide: MatDialogRef, useValue: mockDialogRef }],
35-
}).compileComponents();
110+
await TestBed
111+
.configureTestingModule({
112+
imports: [EventTabComponent, NoopAnimationsModule],
113+
providers: [{provide: MatDialog, useValue: matDialogSpy}],
114+
})
115+
.compileComponents();
36116

37117
fixture = TestBed.createComponent(EventTabComponent);
38118
component = fixture.componentInstance;
119+
loader = TestbedHarnessEnvironment.loader(fixture);
120+
matDialogSpy.open.calls.reset();
39121
fixture.detectChanges();
40122
});
41123

42124
it('should create', () => {
43125
expect(component).toBeTruthy();
44126
});
127+
128+
it('should display "No conversations" if eventsMap is empty', () => {
129+
expect(fixture.nativeElement.textContent).toContain('No conversations');
130+
});
131+
132+
describe('with events', () => {
133+
beforeEach(async () => {
134+
fixture.componentRef.setInput('eventsMap', MOCK_EVENTS_MAP);
135+
fixture.detectChanges();
136+
await fixture.whenStable();
137+
});
138+
139+
it('should display events list by default', async () => {
140+
const list = await loader.getHarness(MatListHarness);
141+
const items = await list.getItems();
142+
expect(items.length).toBe(2);
143+
expect(await items[0].getFullText()).toContain('Event 1 Title');
144+
expect(await items[1].getFullText()).toContain('Event 2 Title');
145+
});
146+
147+
it('should emit selectedEvent on event click', async () => {
148+
spyOn(component.selectedEvent, 'emit');
149+
const list = await loader.getHarness(MatListHarness);
150+
const items = await list.getItems();
151+
await (await items[0].host()).click();
152+
expect(component.selectedEvent.emit).toHaveBeenCalledWith('event1');
153+
});
154+
155+
it('should not show toggle if traceData is empty', async () => {
156+
const hasToggleGroup = fixture.nativeElement.querySelector(
157+
'mat-button-toggle-group',
158+
);
159+
expect(hasToggleGroup).toBeNull();
160+
});
161+
});
162+
163+
describe('with trace data', () => {
164+
beforeEach(async () => {
165+
fixture.componentRef.setInput('eventsMap', MOCK_EVENTS_MAP);
166+
fixture.componentRef.setInput('traceData', MOCK_TRACE_DATA);
167+
fixture.detectChanges();
168+
await fixture.whenStable();
169+
});
170+
171+
it('should show toggle buttons', async () => {
172+
const toggles = await loader.getAllHarnesses(MatButtonToggleHarness);
173+
expect(toggles.length).toBe(2);
174+
expect(await toggles[0].getText()).toBe('Events');
175+
expect(await toggles[1].getText()).toBe('Trace');
176+
});
177+
178+
it('should switch to trace view and display traces', async () => {
179+
const traceToggle = await loader.getHarness(
180+
MatButtonToggleHarness.with({text: 'Trace'}),
181+
);
182+
await traceToggle.check();
183+
fixture.detectChanges();
184+
185+
const list = await loader.getHarness(MatListHarness);
186+
const items = await list.getItems();
187+
expect(items.length).toBe(1);
188+
expect(await items[0].getFullText()).toContain('Invocation 21332-322222');
189+
});
190+
191+
it('should open dialog when trace item is clicked', async () => {
192+
const traceToggle = await loader.getHarness(
193+
MatButtonToggleHarness.with({text: 'Trace'}),
194+
);
195+
await traceToggle.check();
196+
fixture.detectChanges();
197+
198+
const list = await loader.getHarness(MatListHarness);
199+
const items = await list.getItems();
200+
await (await items[0].host()).click();
201+
202+
expect(matDialogSpy.open).toHaveBeenCalledWith(TraceChartComponent, {
203+
width: 'auto',
204+
maxWidth: '90vw',
205+
data: {
206+
spans: component.spansByTraceId().get('trace-1'),
207+
invocId: '21332-322222',
208+
},
209+
});
210+
});
211+
212+
it('should display multiple traces if present', async () => {
213+
const MOCK_TRACE_DATA_WITH_MULTIPLE_TRACES: Span[] = [
214+
...MOCK_TRACE_DATA,
215+
{
216+
name: 'agent.act-2',
217+
start_time: 1733084700000000000,
218+
end_time: 1733084760000000000,
219+
span_id: 'span-10',
220+
trace_id: 'trace-2',
221+
attributes: {
222+
'event_id': 10,
223+
'gcp.vertex.agent.invocation_id': 'invoc-2',
224+
'gcp.vertex.agent.llm_request': '{}',
225+
},
226+
},
227+
];
228+
fixture.componentRef.setInput(
229+
'traceData',
230+
MOCK_TRACE_DATA_WITH_MULTIPLE_TRACES,
231+
);
232+
fixture.detectChanges();
233+
await fixture.whenStable();
234+
235+
const traceToggle = await loader.getHarness(
236+
MatButtonToggleHarness.with({text: 'Trace'}),
237+
);
238+
await traceToggle.check();
239+
fixture.detectChanges();
240+
241+
const list = await loader.getHarness(MatListHarness);
242+
const items = await list.getItems();
243+
expect(items.length).toBe(2);
244+
expect(await items[0].getFullText()).toContain('Invocation 21332-322222');
245+
expect(await items[1].getFullText()).toContain('Invocation invoc-2');
246+
});
247+
});
45248
});

src/app/components/event-tab/event-tab.component.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export class EventTabComponent {
4949
readonly view = signal<string>('events');
5050
readonly isTraceView = computed(() => this.view() === 'trace');
5151
readonly spansByTraceId = computed(() => {
52-
if (!this.traceData || this.traceData.length == 0) {
52+
if (!this.traceData() || this.traceData().length == 0) {
5353
return new Map<string, Span[]>();
5454
}
5555
return this.traceData().reduce((map, span) => {
@@ -66,12 +66,6 @@ export class EventTabComponent {
6666
}, new Map<string, Span[]>());
6767
});
6868

69-
showJson: boolean[] = Array(this.eventsMap().size).fill(false);
70-
71-
toggleJson(index: number) {
72-
this.showJson[index] = !this.showJson[index];
73-
}
74-
7569
selectEvent(key: string) {
7670
this.selectedEvent.emit(key);
7771
}

src/app/components/session-tab/session-tab.component.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
* limitations under the License.
1616
*/
1717

18-
import {Component, EventEmitter, Input, OnInit, Output, Inject} from '@angular/core';
18+
import {NgClass} from '@angular/common';
19+
import {ChangeDetectorRef, Component, EventEmitter, Inject, inject, Input, OnInit, Output} from '@angular/core';
1920
import {MatDialog} from '@angular/material/dialog';
2021
import {Subject} from 'rxjs';
2122
import {switchMap} from 'rxjs/operators';
23+
2224
import {Session} from '../../core/models/Session';
23-
import {SessionService, SESSION_SERVICE} from '../../core/services/session.service';
24-
import { NgClass } from '@angular/common';
25+
import {SESSION_SERVICE, SessionService} from '../../core/services/session.service';
2526

2627
@Component({
2728
selector: 'app-session-tab',
@@ -40,6 +41,7 @@ export class SessionTabComponent implements OnInit {
4041
sessionList: any[] = [];
4142

4243
private refreshSessionsSubject = new Subject<void>();
44+
private readonly changeDetectorRef = inject(ChangeDetectorRef);
4345

4446
constructor(
4547
@Inject(SESSION_SERVICE) private sessionService: SessionService,
@@ -58,6 +60,7 @@ export class SessionTabComponent implements OnInit {
5860
Number(b.lastUpdateTime) - Number(a.lastUpdateTime),
5961
);
6062
this.sessionList = res;
63+
this.changeDetectorRef.detectChanges();
6164
});
6265
}
6366

0 commit comments

Comments
 (0)