405 lines
11 KiB
JavaScript
405 lines
11 KiB
JavaScript
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);
|
|
});
|
|
});
|
|
});
|