diff --git a/src/__tests__/renderer/components/MainPanel.test.tsx b/src/__tests__/renderer/components/MainPanel.test.tsx
index 3b0a3c5a..e26013ed 100644
--- a/src/__tests__/renderer/components/MainPanel.test.tsx
+++ b/src/__tests__/renderer/components/MainPanel.test.tsx
@@ -175,6 +175,27 @@ vi.mock('../../../renderer/contexts/GitStatusContext', () => ({
getFileCount: (sessionId: string) => mockGitStatusData[sessionId]?.fileCount ?? 0,
getStatus: (sessionId: string) => mockGitStatusData[sessionId],
}),
+ useGitFileStatus: () => ({
+ getFileCount: (sessionId: string) => mockGitStatusData[sessionId]?.fileCount ?? 0,
+ hasChanges: (sessionId: string) => (mockGitStatusData[sessionId]?.fileCount ?? 0) > 0,
+ isLoading: false,
+ }),
+ useGitBranch: () => ({
+ getBranchInfo: (sessionId: string) => {
+ const status = mockGitStatusData[sessionId];
+ if (!status) return undefined;
+ return {
+ branch: status.branch,
+ remote: status.remote,
+ ahead: status.ahead || 0,
+ behind: status.behind || 0,
+ };
+ },
+ }),
+ useGitDetail: () => ({
+ getFileDetails: () => undefined,
+ refreshGitStatus: mockRefreshGitStatus,
+ }),
}));
// Import MainPanel after mocks
diff --git a/src/__tests__/renderer/components/RightPanel.test.tsx b/src/__tests__/renderer/components/RightPanel.test.tsx
index be0d0cba..651a48b0 100644
--- a/src/__tests__/renderer/components/RightPanel.test.tsx
+++ b/src/__tests__/renderer/components/RightPanel.test.tsx
@@ -356,9 +356,14 @@ describe('RightPanel', () => {
// Start resize
fireEvent.mouseDown(resizeHandle, { clientX: 500 });
- // Simulate mouse move
+ // Simulate mouse move (direct DOM update for performance, no state call yet)
fireEvent.mouseMove(document, { clientX: 450 }); // 50px to the left (makes panel wider since reversed)
+ // State is only updated on mouseUp for performance (avoids ~60 re-renders/sec)
+ expect(setRightPanelWidthState).not.toHaveBeenCalled();
+
+ // End resize - state is updated
+ fireEvent.mouseUp(document);
expect(setRightPanelWidthState).toHaveBeenCalled();
});
@@ -375,6 +380,9 @@ describe('RightPanel', () => {
// Try to make it very wide (delta = 500 - (-500) = 1000)
fireEvent.mouseMove(document, { clientX: -500 });
+ // End resize - state is updated on mouseUp
+ fireEvent.mouseUp(document);
+
// Should be clamped to max 800
const calls = setRightPanelWidthState.mock.calls;
const lastCall = calls[calls.length - 1][0];
diff --git a/src/__tests__/renderer/components/SessionList.test.tsx b/src/__tests__/renderer/components/SessionList.test.tsx
index 753e2721..fe234a2a 100644
--- a/src/__tests__/renderer/components/SessionList.test.tsx
+++ b/src/__tests__/renderer/components/SessionList.test.tsx
@@ -76,6 +76,18 @@ vi.mock('../../../renderer/contexts/GitStatusContext', () => ({
getFileCount: () => 0,
getStatus: () => undefined,
}),
+ useGitFileStatus: () => ({
+ getFileCount: () => 0,
+ hasChanges: () => false,
+ isLoading: false,
+ }),
+ useGitBranch: () => ({
+ getBranchInfo: () => undefined,
+ }),
+ useGitDetail: () => ({
+ getFileDetails: () => undefined,
+ refreshGitStatus: vi.fn().mockResolvedValue(undefined),
+ }),
}));
// Add tunnel mock to window.maestro
@@ -1155,10 +1167,14 @@ describe('SessionList', () => {
// Simulate drag
fireEvent.mouseDown(resizeHandle!, { clientX: 300 });
- // Move mouse
+ // Move mouse (direct DOM update for performance, no state call yet)
fireEvent.mouseMove(document, { clientX: 350 });
- // Width should be updated
+ // State is only updated on mouseUp for performance (avoids ~60 re-renders/sec)
+ expect(setLeftSidebarWidthState).not.toHaveBeenCalled();
+
+ // End resize - state is updated
+ fireEvent.mouseUp(document);
expect(setLeftSidebarWidthState).toHaveBeenCalled();
});
});
@@ -2618,13 +2634,20 @@ describe('SessionList', () => {
// Try to drag beyond max (600px)
fireEvent.mouseDown(resizeHandle!, { clientX: 300 });
fireEvent.mouseMove(document, { clientX: 1000 });
+ // State is only updated on mouseUp for performance
+ fireEvent.mouseUp(document);
// Should be clamped to 600
expect(setLeftSidebarWidthState).toHaveBeenCalledWith(600);
+ // Reset mock for next test
+ setLeftSidebarWidthState.mockClear();
+
// Try to drag below min (256px)
fireEvent.mouseDown(resizeHandle!, { clientX: 300 });
fireEvent.mouseMove(document, { clientX: 100 });
+ // State is only updated on mouseUp for performance
+ fireEvent.mouseUp(document);
// Should be clamped to 256
expect(setLeftSidebarWidthState).toHaveBeenCalledWith(256);
diff --git a/src/__tests__/renderer/components/UsageDashboard/AgentComparisonChart.test.tsx b/src/__tests__/renderer/components/UsageDashboard/AgentComparisonChart.test.tsx
index c5bb581c..8f813963 100644
--- a/src/__tests__/renderer/components/UsageDashboard/AgentComparisonChart.test.tsx
+++ b/src/__tests__/renderer/components/UsageDashboard/AgentComparisonChart.test.tsx
@@ -85,7 +85,7 @@ describe('AgentComparisonChart', () => {
it('renders the component with title', () => {
render();
- expect(screen.getByText('Agent Comparison')).toBeInTheDocument();
+ expect(screen.getByText('Provider Comparison')).toBeInTheDocument();
});
it('renders count and duration labels for each agent', () => {
@@ -302,7 +302,7 @@ describe('AgentComparisonChart', () => {
it('applies theme text colors', () => {
render();
- const title = screen.getByText('Agent Comparison');
+ const title = screen.getByText('Provider Comparison');
expect(title).toHaveStyle({
color: theme.colors.textMain,
});
@@ -313,7 +313,7 @@ describe('AgentComparisonChart', () => {
render();
- expect(screen.getByText('Agent Comparison')).toBeInTheDocument();
+ expect(screen.getByText('Provider Comparison')).toBeInTheDocument();
});
it('applies border colors from theme', () => {
diff --git a/src/__tests__/renderer/components/UsageDashboard/SourceDistributionChart.test.tsx b/src/__tests__/renderer/components/UsageDashboard/SourceDistributionChart.test.tsx
index f3f8d195..aa979b17 100644
--- a/src/__tests__/renderer/components/UsageDashboard/SourceDistributionChart.test.tsx
+++ b/src/__tests__/renderer/components/UsageDashboard/SourceDistributionChart.test.tsx
@@ -88,7 +88,7 @@ describe('SourceDistributionChart', () => {
it('renders the component with title', () => {
render();
- expect(screen.getByText('Source Distribution')).toBeInTheDocument();
+ expect(screen.getByText('Session Type')).toBeInTheDocument();
});
it('renders metric toggle buttons', () => {
@@ -376,7 +376,7 @@ describe('SourceDistributionChart', () => {
it('applies theme text colors', () => {
render();
- const title = screen.getByText('Source Distribution');
+ const title = screen.getByText('Session Type');
expect(title).toHaveStyle({
color: theme.colors.textMain,
});
@@ -387,7 +387,7 @@ describe('SourceDistributionChart', () => {
render();
- expect(screen.getByText('Source Distribution')).toBeInTheDocument();
+ expect(screen.getByText('Session Type')).toBeInTheDocument();
});
it('applies border colors from theme', () => {
diff --git a/src/__tests__/renderer/components/UsageDashboard/chart-accessibility.test.tsx b/src/__tests__/renderer/components/UsageDashboard/chart-accessibility.test.tsx
index 78c1b103..cd9032de 100644
--- a/src/__tests__/renderer/components/UsageDashboard/chart-accessibility.test.tsx
+++ b/src/__tests__/renderer/components/UsageDashboard/chart-accessibility.test.tsx
@@ -81,8 +81,8 @@ describe('Chart Accessibility - AgentComparisonChart', () => {
render();
const figure = screen.getByRole('figure');
expect(figure).toHaveAttribute('aria-label');
- expect(figure.getAttribute('aria-label')).toContain('Agent comparison chart');
- expect(figure.getAttribute('aria-label')).toContain('3 agents displayed');
+ expect(figure.getAttribute('aria-label')).toContain('Provider comparison chart');
+ expect(figure.getAttribute('aria-label')).toContain('3 providers displayed');
});
it('has proper aria attributes on meter elements', () => {
@@ -136,10 +136,10 @@ describe('Chart Accessibility - SourceDistributionChart', () => {
expect(figure).toBeInTheDocument();
});
- it('has descriptive aria-label mentioning source distribution', () => {
+ it('has descriptive aria-label mentioning session type', () => {
render();
const figure = screen.getByRole('figure');
- expect(figure.getAttribute('aria-label')).toContain('Source distribution');
+ expect(figure.getAttribute('aria-label')).toContain('Session type');
});
it('has aria-pressed on toggle buttons', () => {
@@ -353,11 +353,11 @@ describe('Chart Accessibility - General ARIA Patterns', () => {
it('all charts have proper heading structure', () => {
// Render each chart and verify h3 headings exist
const { unmount: u1 } = render();
- expect(screen.getByRole('heading', { level: 3, name: /agent comparison/i })).toBeInTheDocument();
+ expect(screen.getByRole('heading', { level: 3, name: /provider comparison/i })).toBeInTheDocument();
u1();
const { unmount: u2 } = render();
- expect(screen.getByRole('heading', { level: 3, name: /source distribution/i })).toBeInTheDocument();
+ expect(screen.getByRole('heading', { level: 3, name: /session type/i })).toBeInTheDocument();
u2();
const { unmount: u3 } = render();
@@ -429,7 +429,7 @@ describe('Chart Accessibility - Screen Reader Announcements', () => {
const ariaLabel = figure.getAttribute('aria-label') || '';
expect(ariaLabel).toContain('query counts');
expect(ariaLabel).toContain('duration');
- expect(ariaLabel).toContain('agents displayed');
+ expect(ariaLabel).toContain('providers displayed');
});
it('SourceDistributionChart provides percentage summary in SVG', () => {
diff --git a/src/__tests__/renderer/components/UsageDashboard/colorblind-palette.test.tsx b/src/__tests__/renderer/components/UsageDashboard/colorblind-palette.test.tsx
index 2609d0be..8c1935f9 100644
--- a/src/__tests__/renderer/components/UsageDashboard/colorblind-palette.test.tsx
+++ b/src/__tests__/renderer/components/UsageDashboard/colorblind-palette.test.tsx
@@ -140,12 +140,12 @@ describe('Colorblind Palette Constants', () => {
describe('AgentComparisonChart with colorBlindMode', () => {
it('renders with colorBlindMode=false by default', () => {
render();
- expect(screen.getByText('Agent Comparison')).toBeInTheDocument();
+ expect(screen.getByText('Provider Comparison')).toBeInTheDocument();
});
it('renders with colorBlindMode=true', () => {
render();
- expect(screen.getByText('Agent Comparison')).toBeInTheDocument();
+ expect(screen.getByText('Provider Comparison')).toBeInTheDocument();
});
it('uses colorblind palette colors when colorBlindMode is enabled', () => {
@@ -182,12 +182,12 @@ describe('AgentComparisonChart with colorBlindMode', () => {
describe('SourceDistributionChart with colorBlindMode', () => {
it('renders with colorBlindMode=false by default', () => {
render();
- expect(screen.getByText('Source Distribution')).toBeInTheDocument();
+ expect(screen.getByText('Session Type')).toBeInTheDocument();
});
it('renders with colorBlindMode=true', () => {
render();
- expect(screen.getByText('Source Distribution')).toBeInTheDocument();
+ expect(screen.getByText('Session Type')).toBeInTheDocument();
});
it('renders both Interactive and Auto Run labels', () => {
diff --git a/src/__tests__/renderer/components/UsageDashboardModal.test.tsx b/src/__tests__/renderer/components/UsageDashboardModal.test.tsx
index 4f6cf24d..3d4c3036 100644
--- a/src/__tests__/renderer/components/UsageDashboardModal.test.tsx
+++ b/src/__tests__/renderer/components/UsageDashboardModal.test.tsx
@@ -1731,10 +1731,10 @@ describe('UsageDashboardModal', () => {
expect(screen.getByTestId('section-summary-cards')).toHaveAttribute('aria-label', 'Summary Cards');
expect(screen.getByTestId('section-agent-comparison')).toHaveAttribute('tabIndex', '0');
- expect(screen.getByTestId('section-agent-comparison')).toHaveAttribute('aria-label', 'Agent Comparison Chart');
+ expect(screen.getByTestId('section-agent-comparison')).toHaveAttribute('aria-label', 'Provider Comparison Chart');
expect(screen.getByTestId('section-source-distribution')).toHaveAttribute('tabIndex', '0');
- expect(screen.getByTestId('section-source-distribution')).toHaveAttribute('aria-label', 'Source Distribution Chart');
+ expect(screen.getByTestId('section-source-distribution')).toHaveAttribute('aria-label', 'Session Type Chart');
expect(screen.getByTestId('section-activity-heatmap')).toHaveAttribute('tabIndex', '0');
expect(screen.getByTestId('section-activity-heatmap')).toHaveAttribute('aria-label', 'Activity Heatmap');
diff --git a/src/__tests__/web/mobile/MessageHistory.test.tsx b/src/__tests__/web/mobile/MessageHistory.test.tsx
index 3f9f9b3e..fa67620a 100644
--- a/src/__tests__/web/mobile/MessageHistory.test.tsx
+++ b/src/__tests__/web/mobile/MessageHistory.test.tsx
@@ -158,8 +158,9 @@ describe('MessageHistory', () => {
});
describe('Timestamp Formatting', () => {
- it('formats timestamp with hour and minute', () => {
- const timestamp = new Date('2024-01-15T14:30:00').getTime();
+ it('formats timestamp with hour and minute for today', () => {
+ // Use current date to ensure it's "today"
+ const timestamp = Date.now();
const logs: LogEntry[] = [
createLogEntry({ timestamp, text: 'Test', source: 'user' }),
];
@@ -169,6 +170,21 @@ describe('MessageHistory', () => {
const messageCard = container.querySelector('[style*="padding: 10px 12px"]');
expect(messageCard?.textContent).toMatch(/\d{1,2}:\d{2}/);
});
+
+ it('shows date and time for messages older than today', () => {
+ // Use a date from yesterday
+ const yesterday = new Date();
+ yesterday.setDate(yesterday.getDate() - 1);
+ const timestamp = yesterday.getTime();
+ const logs: LogEntry[] = [
+ createLogEntry({ timestamp, text: 'Old message', source: 'user' }),
+ ];
+ const { container } = render();
+ const messageCard = container.querySelector('[style*="padding: 10px 12px"]');
+ // Should contain both date (month/day) and time
+ // Format is like "Jan 15 14:30" - check for month abbreviation pattern
+ expect(messageCard?.textContent).toMatch(/[A-Z][a-z]{2}\s+\d{1,2}\s+\d{1,2}:\d{2}/);
+ });
});
describe('Source Type Styling', () => {
diff --git a/src/web/mobile/MessageHistory.tsx b/src/web/mobile/MessageHistory.tsx
index d5ea1820..2ddd23a7 100644
--- a/src/web/mobile/MessageHistory.tsx
+++ b/src/web/mobile/MessageHistory.tsx
@@ -40,10 +40,22 @@ export interface MessageHistoryProps {
/**
* Format timestamp for display
+ * Shows time only for today's messages, date + time for older messages
*/
function formatTime(timestamp: number): string {
const date = new Date(timestamp);
- return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
+ const now = new Date();
+ const isToday = date.toDateString() === now.toDateString();
+
+ if (isToday) {
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
+ } else {
+ return (
+ date.toLocaleDateString([], { month: 'short', day: 'numeric' }) +
+ ' ' +
+ date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
+ );
+ }
}
/**