SystemInfoPage.test.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /**
  2. * Tests for the SystemInfoPage component.
  3. */
  4. import { describe, it, expect, vi, beforeEach } from 'vitest';
  5. import { screen, waitFor } from '@testing-library/react';
  6. import { render } from '../utils';
  7. import { SystemInfoPage } from '../../pages/SystemInfoPage';
  8. import { api } from '../../api/client';
  9. // Mock the API client
  10. vi.mock('../../api/client', () => ({
  11. api: {
  12. getSystemInfo: vi.fn(),
  13. getSettings: vi.fn().mockResolvedValue({}),
  14. updateSettings: vi.fn().mockResolvedValue({}),
  15. },
  16. supportApi: {
  17. getDebugLoggingState: vi.fn().mockResolvedValue({ enabled: false, enabled_at: null, duration_seconds: null }),
  18. setDebugLogging: vi.fn().mockResolvedValue({ enabled: true, enabled_at: new Date().toISOString(), duration_seconds: 0 }),
  19. downloadSupportBundle: vi.fn().mockResolvedValue(undefined),
  20. },
  21. }));
  22. // Mock system info response
  23. const mockSystemInfo = {
  24. app: {
  25. version: '0.1.5b',
  26. base_dir: '/opt/bambuddy',
  27. archive_dir: '/opt/bambuddy/archives',
  28. },
  29. database: {
  30. archives: 150,
  31. archives_completed: 140,
  32. archives_failed: 8,
  33. archives_printing: 2,
  34. printers: 3,
  35. filaments: 25,
  36. projects: 5,
  37. smart_plugs: 2,
  38. total_print_time_seconds: 360000,
  39. total_print_time_formatted: '100h',
  40. total_filament_grams: 5000,
  41. total_filament_kg: 5.0,
  42. },
  43. printers: {
  44. total: 3,
  45. connected: 2,
  46. connected_list: [
  47. { id: 1, name: 'X1C-01', state: 'IDLE', model: 'X1C' },
  48. { id: 2, name: 'P1S-01', state: 'RUNNING', model: 'P1S' },
  49. ],
  50. },
  51. storage: {
  52. archive_size_bytes: 1073741824,
  53. archive_size_formatted: '1.0 GB',
  54. database_size_bytes: 10485760,
  55. database_size_formatted: '10.0 MB',
  56. disk_total_bytes: 107374182400,
  57. disk_total_formatted: '100.0 GB',
  58. disk_used_bytes: 53687091200,
  59. disk_used_formatted: '50.0 GB',
  60. disk_free_bytes: 53687091200,
  61. disk_free_formatted: '50.0 GB',
  62. disk_percent_used: 50.0,
  63. },
  64. system: {
  65. platform: 'Linux',
  66. platform_release: '5.15.0',
  67. platform_version: '#1 SMP',
  68. architecture: 'x86_64',
  69. hostname: 'bambuddy-server',
  70. python_version: '3.11.0',
  71. uptime_seconds: 86400,
  72. uptime_formatted: '1d',
  73. boot_time: '2024-12-11T00:00:00',
  74. },
  75. memory: {
  76. total_bytes: 17179869184,
  77. total_formatted: '16.0 GB',
  78. available_bytes: 8589934592,
  79. available_formatted: '8.0 GB',
  80. used_bytes: 8589934592,
  81. used_formatted: '8.0 GB',
  82. percent_used: 50.0,
  83. },
  84. cpu: {
  85. count: 4,
  86. count_logical: 8,
  87. percent: 25.0,
  88. },
  89. };
  90. describe('SystemInfoPage', () => {
  91. beforeEach(() => {
  92. vi.clearAllMocks();
  93. });
  94. it('renders loading state initially', async () => {
  95. // Make the API call never resolve to test loading state
  96. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockImplementation(
  97. () => new Promise(() => {})
  98. );
  99. render(<SystemInfoPage />);
  100. // Should show loading spinner
  101. expect(document.querySelector('.animate-spin')).toBeInTheDocument();
  102. });
  103. it('renders system info when data loads', async () => {
  104. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(mockSystemInfo);
  105. render(<SystemInfoPage />);
  106. await waitFor(() => {
  107. expect(screen.getByText('System Information')).toBeInTheDocument();
  108. });
  109. // Check for version
  110. expect(screen.getByText('v0.1.5b')).toBeInTheDocument();
  111. });
  112. it('displays application section', async () => {
  113. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(mockSystemInfo);
  114. render(<SystemInfoPage />);
  115. await waitFor(() => {
  116. expect(screen.getByText('Application')).toBeInTheDocument();
  117. });
  118. expect(screen.getByText('v0.1.5b')).toBeInTheDocument();
  119. expect(screen.getByText('bambuddy-server')).toBeInTheDocument();
  120. expect(screen.getByText('1d')).toBeInTheDocument();
  121. });
  122. it('displays database statistics', async () => {
  123. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(mockSystemInfo);
  124. render(<SystemInfoPage />);
  125. await waitFor(() => {
  126. expect(screen.getByText('Database')).toBeInTheDocument();
  127. });
  128. // Check archive counts
  129. expect(screen.getByText('150')).toBeInTheDocument(); // Total archives
  130. expect(screen.getByText('140')).toBeInTheDocument(); // Completed
  131. expect(screen.getByText('8')).toBeInTheDocument(); // Failed
  132. });
  133. it('displays connected printers', async () => {
  134. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(mockSystemInfo);
  135. render(<SystemInfoPage />);
  136. await waitFor(() => {
  137. expect(screen.getByText('Connected Printers')).toBeInTheDocument();
  138. });
  139. // Check connected printer names
  140. expect(screen.getByText('X1C-01')).toBeInTheDocument();
  141. expect(screen.getByText('P1S-01')).toBeInTheDocument();
  142. // Check printer states
  143. expect(screen.getByText('IDLE')).toBeInTheDocument();
  144. expect(screen.getByText('RUNNING')).toBeInTheDocument();
  145. });
  146. it('displays storage information', async () => {
  147. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(mockSystemInfo);
  148. render(<SystemInfoPage />);
  149. await waitFor(() => {
  150. expect(screen.getByText('Storage')).toBeInTheDocument();
  151. });
  152. expect(screen.getByText('1.0 GB')).toBeInTheDocument(); // Archive size
  153. expect(screen.getByText('10.0 MB')).toBeInTheDocument(); // Database size
  154. });
  155. it('displays memory usage', async () => {
  156. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(mockSystemInfo);
  157. render(<SystemInfoPage />);
  158. await waitFor(() => {
  159. expect(screen.getByText('Memory')).toBeInTheDocument();
  160. });
  161. expect(screen.getByText('8.0 GB available')).toBeInTheDocument();
  162. });
  163. it('displays CPU information', async () => {
  164. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(mockSystemInfo);
  165. render(<SystemInfoPage />);
  166. await waitFor(() => {
  167. expect(screen.getByText('CPU')).toBeInTheDocument();
  168. });
  169. expect(screen.getByText('4')).toBeInTheDocument(); // CPU cores
  170. expect(screen.getByText('25%')).toBeInTheDocument(); // CPU usage
  171. });
  172. it('displays system details', async () => {
  173. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(mockSystemInfo);
  174. render(<SystemInfoPage />);
  175. await waitFor(() => {
  176. expect(screen.getByText('System Details')).toBeInTheDocument();
  177. });
  178. expect(screen.getByText('Linux')).toBeInTheDocument();
  179. expect(screen.getByText('x86_64')).toBeInTheDocument();
  180. expect(screen.getByText('3.11.0')).toBeInTheDocument(); // Python version
  181. });
  182. it('shows error state when data fails to load', async () => {
  183. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(null);
  184. render(<SystemInfoPage />);
  185. await waitFor(() => {
  186. expect(screen.getByText(/failed to load/i)).toBeInTheDocument();
  187. });
  188. });
  189. it('shows no printers message when none connected', async () => {
  190. const noConnectedPrinters = {
  191. ...mockSystemInfo,
  192. printers: {
  193. total: 3,
  194. connected: 0,
  195. connected_list: [],
  196. },
  197. };
  198. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(noConnectedPrinters);
  199. render(<SystemInfoPage />);
  200. await waitFor(() => {
  201. expect(screen.getByText(/no printers connected/i)).toBeInTheDocument();
  202. });
  203. });
  204. it('has refresh button', async () => {
  205. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(mockSystemInfo);
  206. render(<SystemInfoPage />);
  207. await waitFor(() => {
  208. expect(screen.getByText('Refresh')).toBeInTheDocument();
  209. });
  210. });
  211. it('applies warning color for high disk usage', async () => {
  212. const highDiskUsage = {
  213. ...mockSystemInfo,
  214. storage: {
  215. ...mockSystemInfo.storage,
  216. disk_percent_used: 80,
  217. },
  218. };
  219. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(highDiskUsage);
  220. render(<SystemInfoPage />);
  221. await waitFor(() => {
  222. expect(screen.getByText('Storage')).toBeInTheDocument();
  223. });
  224. // The progress bar should have yellow color for 75-90% usage
  225. const progressBars = document.querySelectorAll('[class*="bg-yellow"]');
  226. expect(progressBars.length).toBeGreaterThan(0);
  227. });
  228. it('applies danger color for critical disk usage', async () => {
  229. const criticalDiskUsage = {
  230. ...mockSystemInfo,
  231. storage: {
  232. ...mockSystemInfo.storage,
  233. disk_percent_used: 95,
  234. },
  235. };
  236. (api.getSystemInfo as ReturnType<typeof vi.fn>).mockResolvedValue(criticalDiskUsage);
  237. render(<SystemInfoPage />);
  238. await waitFor(() => {
  239. expect(screen.getByText('Storage')).toBeInTheDocument();
  240. });
  241. // The progress bar should have red color for >90% usage
  242. const progressBars = document.querySelectorAll('[class*="bg-red"]');
  243. expect(progressBars.length).toBeGreaterThan(0);
  244. });
  245. });