Skip to content

Commit a565c42

Browse files
authored
Merge pull request #15 from All-Hands-AI/fix-timeline-display
Fix timeline entries display issue
2 parents 1da9280 + 1c27a1a commit a565c42

17 files changed

+1752
-147
lines changed

demo1.json

+631
Large diffs are not rendered by default.

src/components/RunDetails.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ const RunDetails: React.FC<RunDetailsProps> = ({ owner, repo, run, initialConten
5252
if (artifactContent?.content?.trajectory) {
5353
return convertOpenHandsTrajectory(artifactContent.content.trajectory);
5454
}
55+
// Check if this is an uploaded trajectory file
56+
if (artifactContent?.content?.fileType === 'trajectory' && artifactContent?.content?.trajectoryData) {
57+
return convertOpenHandsTrajectory(artifactContent.content.trajectoryData);
58+
}
5559
return artifactContent?.content?.history || artifactContent?.content?.jsonlHistory || [];
5660
} catch (error) {
5761
console.error('Failed to convert trajectory:', error);

src/components/timeline/TimelineStep.tsx

-133
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { render, screen } from '@testing-library/react';
2+
import '@testing-library/jest-dom';
3+
import { vi, describe, test, expect } from 'vitest';
4+
import { Timeline } from '../Timeline';
5+
import { TimelineEntry } from '../types';
6+
7+
// Mock the TimelineStep component
8+
vi.mock('../components/TimelineStep', () => ({
9+
default: ({ entry, index, isSelected, onSelect }: any) => (
10+
<div
11+
data-testid={`timeline-step-${index}`}
12+
data-selected={isSelected.toString()}
13+
onClick={() => onSelect(index)}
14+
>
15+
<span>{entry.type}</span>
16+
{entry.content && <span>{entry.content}</span>}
17+
{entry.command && <span>{entry.command}</span>}
18+
</div>
19+
)
20+
}));
21+
22+
describe('Timeline Component', () => {
23+
const mockOnStepSelect = vi.fn();
24+
const mockOnCommandClick = vi.fn();
25+
const mockFormatTimelineDate = vi.fn().mockReturnValue('12:34 PM');
26+
27+
// Basic test to ensure the component renders
28+
test('renders without crashing', () => {
29+
const entries: TimelineEntry[] = [
30+
{
31+
type: 'message',
32+
timestamp: '2025-03-28T12:34:56Z',
33+
content: 'Test content 1',
34+
actorType: 'User'
35+
},
36+
{
37+
type: 'message',
38+
timestamp: '2025-03-28T12:35:56Z',
39+
content: 'Test content 2',
40+
actorType: 'Assistant'
41+
}
42+
];
43+
44+
render(
45+
<Timeline
46+
entries={entries}
47+
selectedIndex={0}
48+
onStepSelect={mockOnStepSelect}
49+
onCommandClick={mockOnCommandClick}
50+
formatTimelineDate={mockFormatTimelineDate}
51+
createdAt="2025-03-28T12:34:56Z"
52+
/>
53+
);
54+
55+
expect(screen.getByTestId('timeline-step-0')).toBeInTheDocument();
56+
expect(screen.getByTestId('timeline-step-1')).toBeInTheDocument();
57+
});
58+
59+
// Test for empty state
60+
test('renders empty state when no entries are provided', () => {
61+
render(
62+
<Timeline
63+
entries={[]}
64+
selectedIndex={0}
65+
onStepSelect={mockOnStepSelect}
66+
onCommandClick={mockOnCommandClick}
67+
formatTimelineDate={mockFormatTimelineDate}
68+
createdAt="2025-03-28T12:34:56Z"
69+
/>
70+
);
71+
72+
expect(screen.getByText('No timeline entries available')).toBeInTheDocument();
73+
});
74+
75+
// Test for selected step
76+
test('passes isSelected prop correctly to TimelineStep', () => {
77+
const entries: TimelineEntry[] = [
78+
{
79+
type: 'message',
80+
timestamp: '2025-03-28T12:34:56Z',
81+
content: 'Test content 1',
82+
actorType: 'User'
83+
},
84+
{
85+
type: 'message',
86+
timestamp: '2025-03-28T12:35:56Z',
87+
content: 'Test content 2',
88+
actorType: 'Assistant'
89+
}
90+
];
91+
92+
render(
93+
<Timeline
94+
entries={entries}
95+
selectedIndex={1}
96+
onStepSelect={mockOnStepSelect}
97+
onCommandClick={mockOnCommandClick}
98+
formatTimelineDate={mockFormatTimelineDate}
99+
createdAt="2025-03-28T12:34:56Z"
100+
/>
101+
);
102+
103+
expect(screen.getByTestId('timeline-step-0')).toHaveAttribute('data-selected', 'false');
104+
expect(screen.getByTestId('timeline-step-1')).toHaveAttribute('data-selected', 'true');
105+
});
106+
107+
// Test for different entry types
108+
test('renders different types of timeline entries', () => {
109+
const entries: TimelineEntry[] = [
110+
{
111+
type: 'message',
112+
timestamp: '2025-03-28T12:34:56Z',
113+
content: 'Test message',
114+
actorType: 'User'
115+
},
116+
{
117+
type: 'command',
118+
timestamp: '2025-03-28T12:35:56Z',
119+
command: 'ls -la',
120+
actorType: 'Assistant'
121+
},
122+
{
123+
type: 'edit',
124+
timestamp: '2025-03-28T12:36:56Z',
125+
path: '/path/to/file.txt',
126+
actorType: 'Assistant'
127+
}
128+
];
129+
130+
render(
131+
<Timeline
132+
entries={entries}
133+
selectedIndex={0}
134+
onStepSelect={mockOnStepSelect}
135+
onCommandClick={mockOnCommandClick}
136+
formatTimelineDate={mockFormatTimelineDate}
137+
createdAt="2025-03-28T12:34:56Z"
138+
/>
139+
);
140+
141+
expect(screen.getByText('message')).toBeInTheDocument();
142+
expect(screen.getByText('command')).toBeInTheDocument();
143+
expect(screen.getByText('edit')).toBeInTheDocument();
144+
expect(screen.getByText('Test message')).toBeInTheDocument();
145+
expect(screen.getByText('ls -la')).toBeInTheDocument();
146+
});
147+
});

0 commit comments

Comments
 (0)