Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
149cf40
Add tests for useGetRecommendedUpdateLevels hook
dileepapeiris Feb 16, 2026
489aae1
Remove useGetUpdatesStats hook tests
dileepapeiris Feb 16, 2026
0b345a5
Remove useGetProductUpdatesStats hook
dileepapeiris Feb 16, 2026
8c15c65
Add useGetRecommendedUpdateLevels hook
dileepapeiris Feb 16, 2026
d672cbd
Remove useGetUpdatesStats hook
dileepapeiris Feb 16, 2026
2b71e9d
Add data-testid to ErrorIndicator button
dileepapeiris Feb 16, 2026
2ca1b1a
Remove UpdatesStatsGrid.test.tsx
dileepapeiris Feb 16, 2026
7e8e461
Add tests for StatCard component
dileepapeiris Feb 16, 2026
b3c5add
Add StatCard component for updates
dileepapeiris Feb 16, 2026
b9d514e
Switch to recommended update levels hook
dileepapeiris Feb 16, 2026
842ba13
Add RecommendedUpdateLevelItem interface
dileepapeiris Feb 16, 2026
8758a4d
Add mock recommended update levels and update stats
dileepapeiris Feb 16, 2026
18c53b7
Rename UPDATES_STATS to RECOMMENDED_UPDATE_LEVELS
dileepapeiris Feb 16, 2026
9008b2e
Remove UpdatesStatsGrid component
dileepapeiris Feb 16, 2026
0bbea5e
Add UpdatesStatsGrid component
dileepapeiris Feb 16, 2026
c0d3172
Add tests for UpdatesStatsGrid component
dileepapeiris Feb 16, 2026
30ccb8a
Add getMockRecommendedUpdateLevels helper
dileepapeiris Feb 16, 2026
b9a6d6f
Merge branch 'customer-portal-milestone-1' into feat/add-upadate-stat…
dileepapeiris Feb 16, 2026
202f11d
Add update utilities and tests; minor fixes
dileepapeiris Feb 16, 2026
91f3d4a
Remove projectId param from recommended updates hook
dileepapeiris Feb 16, 2026
f50bbb1
Refactor updates stats logic to utils
dileepapeiris Feb 16, 2026
3009d65
Refactor tests: mock providers and API client
dileepapeiris Feb 16, 2026
f268375
Move loading flag, update tests, and clean JSDoc
dileepapeiris Feb 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright (c) 2026 WSO2 LLC. (https://www.wso2.com).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import { renderHook, waitFor } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { useGetRecommendedUpdateLevels } from "@api/useGetRecommendedUpdateLevels";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { ReactNode } from "react";

// Mock logger
const mockLogger = {
debug: vi.fn(),
error: vi.fn(),
};
vi.mock("@/hooks/useLogger", () => ({
useLogger: () => mockLogger,
}));

vi.mock("@/constants/apiConstants", async (importOriginal) => {
const actual = (await importOriginal()) as any;
return {
...actual,
API_MOCK_DELAY: 0,
};
});

// Mock @asgardeo/react
const mockGetIdToken = vi.fn().mockResolvedValue("mock-token");
vi.mock("@asgardeo/react", () => ({
useAsgardeo: () => ({
getIdToken: mockGetIdToken,
isSignedIn: true,
isLoading: false,
}),
}));

// Mock MockConfigProvider
let mockIsMockEnabled = true;
vi.mock("@/providers/MockConfigProvider", () => ({
useMockConfig: () => ({
isMockEnabled: mockIsMockEnabled,
}),
}));

// Mock AuthApiContext - directly mock the hook useAuthApiClient
const mockFetchFn = vi.fn();
vi.mock("@/context/AuthApiContext", () => ({
useAuthApiClient: () => mockFetchFn,
}));

describe("useGetRecommendedUpdateLevels", () => {
let queryClient: QueryClient;

beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
staleTime: 0,
gcTime: 0,
},
},
});
mockLogger.debug.mockClear();
mockLogger.error.mockClear();
mockIsMockEnabled = true;
mockFetchFn.mockReset();
vi.clearAllMocks();
});

afterEach(() => {
vi.unstubAllGlobals();
queryClient.clear();
});

const wrapper = ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);

it("should return loading state initially", async () => {
const { result } = renderHook(() => useGetRecommendedUpdateLevels(), {
wrapper,
});

expect(result.current.isLoading).toBe(true);
});

it("should return mock data when mock is enabled", async () => {
mockIsMockEnabled = true;
const { result } = renderHook(() => useGetRecommendedUpdateLevels(), {
wrapper,
});

await waitFor(() => expect(result.current.isSuccess).toBe(true), {
timeout: 10000,
});

expect(result.current.data).toBeDefined();
expect(Array.isArray(result.current.data)).toBe(true);
expect(mockLogger.debug).toHaveBeenCalledWith(
expect.stringContaining("Fetching recommended update levels, mock: true"),
);
}, 15000);

it("should have correct query options", () => {
renderHook(() => useGetRecommendedUpdateLevels(), {
wrapper,
});

const query = queryClient.getQueryCache().findAll({
queryKey: ["recommended-update-levels", true],
})[0];

expect((query?.options as any).staleTime).toBe(5 * 60 * 1000);
});

it("should fetch real data when mock is disabled", async () => {
mockIsMockEnabled = false;
const mockResponse = [
{
productName: "wso2am-analytics",
productBaseVersion: "2.6.0",
channel: "full",
startingUpdateLevel: 0,
endingUpdateLevel: 33,
installedUpdatesCount: 44,
installedSecurityUpdatesCount: 23,
timestamp: 1684415113845,
recommendedUpdateLevel: 33,
availableUpdatesCount: 0,
availableSecurityUpdatesCount: 0,
},
];

const originalConfig = (window as any).config;
(window as any).config = {
CUSTOMER_PORTAL_BACKEND_BASE_URL: "https://api.test",
};

mockFetchFn.mockResolvedValue({
ok: true,
json: () => Promise.resolve(mockResponse),
status: 200,
} as Response);

const { result } = renderHook(() => useGetRecommendedUpdateLevels(), {
wrapper,
});

try {
await waitFor(
() => {
if (result.current.isError) {
throw new Error(`Hook error: ${result.current.error?.message}`);
}
return expect(result.current.isSuccess).toBe(true);
},
{ timeout: 10000 },
);

expect(result.current.data).toEqual(mockResponse);
expect(mockLogger.debug).toHaveBeenCalledWith(
expect.stringContaining(
"Fetching recommended update levels, mock: false",
),
);
} finally {
(window as any).config = originalConfig;
}
}, 15000);

it("should handle API error when mock is disabled", async () => {
mockIsMockEnabled = false;
const originalConfig = (window as any).config;
(window as any).config = {
CUSTOMER_PORTAL_BACKEND_BASE_URL: "https://api.test",
};

mockFetchFn.mockResolvedValue({
ok: false,
statusText: "Internal Server Error",
status: 500,
} as Response);

const { result } = renderHook(() => useGetRecommendedUpdateLevels(), {
wrapper,
});

try {
await waitFor(() => expect(result.current.isError).toBe(true), {
timeout: 10000,
});
expect(result.current.error?.message).toContain(
"Error fetching recommended update levels: Internal Server Error",
);
expect(mockLogger.error).toHaveBeenCalled();
} finally {
(window as any).config = originalConfig;
}
}, 15000);
});
Loading