const LogReaderService = require("../../../../src/services/logReader"); // Mock the LogReaderService jest.mock("../../../../src/services/logReader"); describe("LogViewerScreen - Auto Refresh", () => { let mockLogReader; let mockPaginatedData; beforeEach(() => { jest.clearAllMocks(); jest.useFakeTimers(); // Setup mock data mockPaginatedData = { entries: [ { id: "entry_1", type: "operation_start", timestamp: new Date("2025-08-06T20:30:00Z"), level: "INFO", message: "Price Update Operation Started", title: "Price Update Operation", details: "Target Tag: summer-sale\nPrice Adjustment: -10%", }, ], pagination: { currentPage: 0, pageSize: 10, totalEntries: 1, totalPages: 1, hasNextPage: false, hasPreviousPage: false, startIndex: 1, endIndex: 1, }, filters: { levelFilter: "ALL", searchTerm: "", }, }; // Setup LogReaderService mock mockLogReader = { getPaginatedEntries: jest.fn().mockResolvedValue(mockPaginatedData), getLogStatistics: jest.fn().mockResolvedValue({}), clearCache: jest.fn(), watchFile: jest.fn().mockReturnValue(() => {}), }; LogReaderService.mockImplementation(() => mockLogReader); }); afterEach(() => { jest.useRealTimers(); }); describe("File Watching", () => { test("sets up file watching on mount", () => { const logReader = new LogReaderService(); // Simulate component mount const cleanup = logReader.watchFile(() => {}); expect(mockLogReader.watchFile).toHaveBeenCalled(); expect(typeof cleanup).toBe("function"); }); test("calls refresh callback when file changes", () => { const logReader = new LogReaderService(); const mockCallback = jest.fn(); // Set up file watching logReader.watchFile(mockCallback); // Get the callback that was passed to watchFile const watchCallback = mockLogReader.watchFile.mock.calls[0][0]; // Simulate file change watchCallback(); expect(mockCallback).toHaveBeenCalled(); }); test("cleans up file watching on unmount", () => { const logReader = new LogReaderService(); const mockCleanup = jest.fn(); mockLogReader.watchFile.mockReturnValue(mockCleanup); const cleanup = logReader.watchFile(() => {}); cleanup(); expect(mockCleanup).toHaveBeenCalled(); }); }); describe("Periodic Refresh", () => { test("sets up periodic refresh timer", async () => { const logReader = new LogReaderService(); // Simulate component with auto-refresh enabled const refreshEnabled = true; if (refreshEnabled) { // Set up a timer (simulating the component's useEffect) const timer = setInterval(() => { logReader.clearCache(); }, 30000); // Verify that timer was created expect(jest.getTimerCount()).toBeGreaterThan(0); // Clean up clearInterval(timer); } }); test("respects auto-refresh toggle", () => { const logReader = new LogReaderService(); // When auto-refresh is disabled, no timers should be set const refreshEnabled = false; if (!refreshEnabled) { expect(jest.getTimerCount()).toBe(0); } }); test("prevents refresh when already loading", async () => { const logReader = new LogReaderService(); // Simulate loading state const isLoading = true; const isRefreshing = false; // Periodic refresh should not trigger when loading if (!isLoading && !isRefreshing) { logReader.clearCache(); await logReader.getPaginatedEntries({}); } // If loading, clearCache should not be called if (isLoading) { expect(mockLogReader.clearCache).not.toHaveBeenCalled(); } }); test("prevents refresh when already refreshing", async () => { const logReader = new LogReaderService(); // Simulate refreshing state const isLoading = false; const isRefreshing = true; // Periodic refresh should not trigger when already refreshing if (!isLoading && !isRefreshing) { logReader.clearCache(); await logReader.getPaginatedEntries({}); } // If refreshing, clearCache should not be called if (isRefreshing) { expect(mockLogReader.clearCache).not.toHaveBeenCalled(); } }); }); describe("Cache Management", () => { test("clears cache before refresh", async () => { const logReader = new LogReaderService(); // Simulate manual refresh logReader.clearCache(); await logReader.getPaginatedEntries({}); expect(mockLogReader.clearCache).toHaveBeenCalled(); expect(mockLogReader.getPaginatedEntries).toHaveBeenCalled(); }); test("maintains cache for regular navigation", async () => { const logReader = new LogReaderService(); // Simulate regular pagination (no cache clear) await logReader.getPaginatedEntries({ page: 1 }); expect(mockLogReader.clearCache).not.toHaveBeenCalled(); expect(mockLogReader.getPaginatedEntries).toHaveBeenCalledWith( expect.objectContaining({ page: 1 }) ); }); }); describe("Refresh State Management", () => { test("distinguishes between loading and refreshing states", async () => { const logReader = new LogReaderService(); // Initial load should be "loading" const isInitialLoad = true; const isAutoRefresh = false; if (isInitialLoad && !isAutoRefresh) { // This would set loading state expect(true).toBe(true); // Placeholder for state check } // Auto refresh should be "refreshing" if (!isInitialLoad && isAutoRefresh) { // This would set refreshing state expect(true).toBe(true); // Placeholder for state check } }); test("updates last refresh timestamp", async () => { const logReader = new LogReaderService(); const beforeRefresh = new Date(); // Simulate refresh await logReader.getPaginatedEntries({}); const afterRefresh = new Date(); // Last refresh should be between before and after expect(afterRefresh.getTime()).toBeGreaterThanOrEqual( beforeRefresh.getTime() ); }); }); describe("Performance Optimization", () => { test("avoids unnecessary refreshes", () => { const logReader = new LogReaderService(); // Multiple rapid file change events should be handled efficiently const watchCallback = mockLogReader.watchFile.mock.calls[0]?.[0]; if (watchCallback) { // Simulate rapid file changes watchCallback(); watchCallback(); watchCallback(); // Should handle gracefully without excessive API calls expect(mockLogReader.watchFile).toHaveBeenCalledTimes(1); } }); test("handles refresh errors gracefully", async () => { const logReader = new LogReaderService(); // Mock refresh failure mockLogReader.getPaginatedEntries.mockRejectedValueOnce( new Error("Refresh failed") ); try { await logReader.getPaginatedEntries({}); } catch (error) { expect(error.message).toBe("Refresh failed"); } // Should still be able to retry mockLogReader.getPaginatedEntries.mockResolvedValueOnce( mockPaginatedData ); const result = await logReader.getPaginatedEntries({}); expect(result).toEqual(mockPaginatedData); }); test("maintains selection during refresh", async () => { const logReader = new LogReaderService(); // Simulate refresh with current selection const currentSelection = 0; const result = await logReader.getPaginatedEntries({}); // Selection should be maintained if still valid if (currentSelection < result.entries.length) { expect(currentSelection).toBeLessThan(result.entries.length); } }); }); describe("Auto-refresh Toggle", () => { test("enables auto-refresh by default", () => { // Auto-refresh should be enabled by default const defaultAutoRefresh = true; expect(defaultAutoRefresh).toBe(true); }); test("allows toggling auto-refresh", () => { let autoRefresh = true; // Toggle off autoRefresh = !autoRefresh; expect(autoRefresh).toBe(false); // Toggle on autoRefresh = !autoRefresh; expect(autoRefresh).toBe(true); }); test("stops file watching when auto-refresh is disabled", () => { const logReader = new LogReaderService(); const autoRefresh = false; // When auto-refresh is disabled, file watching should not be set up if (autoRefresh) { logReader.watchFile(() => {}); expect(mockLogReader.watchFile).toHaveBeenCalled(); } else { expect(mockLogReader.watchFile).not.toHaveBeenCalled(); } }); test("stops periodic refresh when auto-refresh is disabled", () => { const autoRefresh = false; // When auto-refresh is disabled, no periodic timers should be active if (!autoRefresh) { expect(jest.getTimerCount()).toBe(0); } }); }); describe("Visual Indicators", () => { test("shows different states in status bar", () => { // Test different status messages const states = [ { loading: true, refreshing: false, expected: "Loading..." }, { loading: false, refreshing: true, expected: "Refreshing..." }, { loading: false, refreshing: false, expected: "Filter status" }, ]; states.forEach(({ loading, refreshing, expected }) => { let statusMessage; if (loading) { statusMessage = "Loading..."; } else if (refreshing) { statusMessage = "Refreshing..."; } else { statusMessage = "Filter status"; } expect(statusMessage).toContain(expected.split(" ")[0]); }); }); test("displays auto-refresh status", () => { const autoRefresh = true; const statusText = `Auto: ${autoRefresh ? "ON" : "OFF"}`; expect(statusText).toBe("Auto: ON"); }); test("displays last refresh time", () => { const lastRefresh = new Date(); const timeString = lastRefresh.toLocaleTimeString(); expect(timeString).toMatch(/\d{1,2}:\d{2}:\d{2}/); }); }); describe("Error Handling", () => { test("handles file watching errors gracefully", () => { const logReader = new LogReaderService(); // Mock file watching error mockLogReader.watchFile.mockImplementation(() => { throw new Error("File watching failed"); }); // Should not crash the application expect(() => { try { logReader.watchFile(() => {}); } catch (error) { // Error should be handled gracefully expect(error.message).toBe("File watching failed"); } }).not.toThrow(); }); test("continues working after refresh errors", async () => { const logReader = new LogReaderService(); // Mock temporary error mockLogReader.getPaginatedEntries .mockRejectedValueOnce(new Error("Temporary error")) .mockResolvedValueOnce(mockPaginatedData); // First call fails try { await logReader.getPaginatedEntries({}); } catch (error) { expect(error.message).toBe("Temporary error"); } // Second call succeeds const result = await logReader.getPaginatedEntries({}); expect(result).toEqual(mockPaginatedData); }); }); });