Skip to content

Commit 024016b

Browse files
Merge branch 'main' into fix/og-bypass-cache
2 parents 38debf5 + 1a0114b commit 024016b

1 file changed

Lines changed: 206 additions & 0 deletions

File tree

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// components/dashboard/VisualizationTooltip.error-resilience.test.tsx
2+
import { render, screen } from '@testing-library/react';
3+
import { describe, expect, it, vi } from 'vitest';
4+
import React, { Component, type HTMLAttributes, type ReactNode, type ErrorInfo } from 'react';
5+
import VisualizationTooltip from './VisualizationTooltip';
6+
import '@testing-library/jest-dom/vitest';
7+
8+
// Mock framer-motion to simplify rendering
9+
vi.mock('framer-motion', () => ({
10+
motion: {
11+
div: ({ children, ...props }: HTMLAttributes<HTMLDivElement> & { children?: ReactNode }) => (
12+
<div {...props}>{children}</div>
13+
),
14+
},
15+
}));
16+
17+
// Localized Error Boundary for exception safety & fallback tests
18+
interface ErrorBoundaryProps {
19+
fallback?: ReactNode;
20+
onError?: (error: Error, errorInfo: ErrorInfo) => void;
21+
children: ReactNode;
22+
}
23+
24+
interface ErrorBoundaryState {
25+
hasError: boolean;
26+
error: Error | null;
27+
}
28+
29+
class TestErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
30+
public override state: ErrorBoundaryState = {
31+
hasError: false,
32+
error: null,
33+
};
34+
35+
public static getDerivedStateFromError(error: Error): ErrorBoundaryState {
36+
return { hasError: true, error };
37+
}
38+
39+
public override componentDidCatch(error: Error, errorInfo: ErrorInfo) {
40+
if (this.props.onError) {
41+
this.props.onError(error, errorInfo);
42+
}
43+
}
44+
45+
private handleReset = () => {
46+
this.setState({ hasError: false, error: null });
47+
};
48+
49+
public override render() {
50+
if (this.state.hasError) {
51+
return (
52+
<div
53+
data-testid="error-recovery-panel"
54+
className="p-4 border border-red-500 rounded bg-red-50 text-red-700"
55+
>
56+
<h3>System Alert</h3>
57+
<p>Unexpected exception: {this.state.error?.message}</p>
58+
<button
59+
onClick={this.handleReset}
60+
data-testid="reset-button"
61+
className="mt-2 px-3 py-1 bg-red-600 text-white rounded text-xs hover:bg-red-700"
62+
>
63+
Reset and Reload
64+
</button>
65+
</div>
66+
);
67+
}
68+
69+
return this.props.children;
70+
}
71+
}
72+
73+
// Nested child component that throws a database connectivity error/runtime exception
74+
const DatabaseConnectivityThrower = () => {
75+
throw new Error('FATAL_DB_DISCONNECT: Database connection timed out');
76+
};
77+
78+
describe('VisualizationTooltip Error Resilience', () => {
79+
// Test case 1: Hydration stability
80+
it('maintains hydration stability and renders without crashing under default properties', () => {
81+
const { container } = render(
82+
<VisualizationTooltip title="Active Users" x={120} y={240}>
83+
<span>32 active sessions</span>
84+
</VisualizationTooltip>
85+
);
86+
87+
const tooltipElement = screen.getByRole('tooltip');
88+
expect(tooltipElement).toBeInTheDocument();
89+
expect(tooltipElement).toHaveStyle({
90+
left: '120px',
91+
top: '240px',
92+
});
93+
expect(screen.getByText('Active Users')).toBeInTheDocument();
94+
expect(screen.getByText('32 active sessions')).toBeInTheDocument();
95+
expect(container.firstChild).not.toBeNull();
96+
});
97+
98+
// Test case 2: Exception safety & localized boundary
99+
it('safely catches unexpected runtime exceptions / database connectivity errors inside a localized boundary', () => {
100+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
101+
102+
expect(() => {
103+
render(
104+
<TestErrorBoundary>
105+
<VisualizationTooltip title="System Monitor" x={50} y={50}>
106+
<DatabaseConnectivityThrower />
107+
</VisualizationTooltip>
108+
</TestErrorBoundary>
109+
);
110+
}).not.toThrow();
111+
112+
consoleErrorSpy.mockRestore();
113+
});
114+
115+
// Test case 3: Clean error recovery UI
116+
it('renders a clean error recovery UI representation instead of crashing the page', () => {
117+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
118+
119+
render(
120+
<TestErrorBoundary>
121+
<VisualizationTooltip title="System Monitor" x={50} y={50}>
122+
<DatabaseConnectivityThrower />
123+
</VisualizationTooltip>
124+
</TestErrorBoundary>
125+
);
126+
127+
expect(screen.getByTestId('error-recovery-panel')).toBeInTheDocument();
128+
expect(screen.getByText(/System Alert/i)).toBeInTheDocument();
129+
expect(screen.getByText(/FATAL_DB_DISCONNECT/i)).toBeInTheDocument();
130+
131+
consoleErrorSpy.mockRestore();
132+
});
133+
134+
// Test case 4: Verify exceptions are logged to dev-telemetry trackers
135+
it('logs exception profiles to the dev-telemetry trackers when database connectivity fails', () => {
136+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
137+
const mockTelemetryTracker = vi.fn();
138+
139+
render(
140+
<TestErrorBoundary
141+
onError={(error) => mockTelemetryTracker({ error: error.message, status: 'logged' })}
142+
>
143+
<VisualizationTooltip title="System Monitor" x={50} y={50}>
144+
<DatabaseConnectivityThrower />
145+
</VisualizationTooltip>
146+
</TestErrorBoundary>
147+
);
148+
149+
expect(mockTelemetryTracker).toHaveBeenCalledTimes(1);
150+
expect(mockTelemetryTracker).toHaveBeenCalledWith(
151+
expect.objectContaining({
152+
error: 'FATAL_DB_DISCONNECT: Database connection timed out',
153+
status: 'logged',
154+
})
155+
);
156+
157+
consoleErrorSpy.mockRestore();
158+
});
159+
160+
// Test case 5: User reset/reload path availability
161+
it('ensures user reset and reload paths are available and functional on the recovery panels', () => {
162+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
163+
164+
let shouldThrow = true;
165+
const ConditionalThrower = () => {
166+
if (shouldThrow) {
167+
throw new Error('Temporary failure');
168+
}
169+
return <span>Successfully Recovered</span>;
170+
};
171+
172+
const { rerender } = render(
173+
<TestErrorBoundary>
174+
<VisualizationTooltip title="Conditional Monitor" x={80} y={150}>
175+
<ConditionalThrower />
176+
</VisualizationTooltip>
177+
</TestErrorBoundary>
178+
);
179+
180+
// Assert recovery UI is displayed
181+
expect(screen.getByTestId('error-recovery-panel')).toBeInTheDocument();
182+
expect(screen.queryByText('Successfully Recovered')).not.toBeInTheDocument();
183+
184+
// Trigger state change so it does not throw on reload
185+
shouldThrow = false;
186+
187+
// Click the Reset and Reload button on the recovery panel
188+
const resetButton = screen.getByTestId('reset-button');
189+
resetButton.click();
190+
191+
// Rerender with the updated child state
192+
rerender(
193+
<TestErrorBoundary>
194+
<VisualizationTooltip title="Conditional Monitor" x={80} y={150}>
195+
<ConditionalThrower />
196+
</VisualizationTooltip>
197+
</TestErrorBoundary>
198+
);
199+
200+
// Recovery UI should be gone and recovered content visible
201+
expect(screen.queryByTestId('error-recovery-panel')).not.toBeInTheDocument();
202+
expect(screen.getByText('Successfully Recovered')).toBeInTheDocument();
203+
204+
consoleErrorSpy.mockRestore();
205+
});
206+
});

0 commit comments

Comments
 (0)