diff --git a/src/contexts/ThemeProvider.tsx b/src/contexts/ThemeProvider.tsx index 074bebe5..f0cf2918 100644 --- a/src/contexts/ThemeProvider.tsx +++ b/src/contexts/ThemeProvider.tsx @@ -1,4 +1,5 @@ "use client"; +import { useMediaQuery } from "@mui/material"; import { createContext, useState } from "react"; type ThemeContextType = { @@ -17,7 +18,9 @@ export const ThemeContext = createContext( // ThemeProvider component to provide the theme context to its children export const ThemeProvider = ({ children }: ThemeProviderProps) => { // State to manage the current theme mode - const [theme, setTheme] = useState<"light" | "dark">("light"); + const [theme, setTheme] = useState<"light" | "dark">( + useMediaQuery("(prefers-color-scheme: dark)") ? "dark" : "light", + ); // Function to toggle the theme mode const toggleTheme = () => { diff --git a/test/unit-tests/Dashboard.test.tsx b/test/unit-tests/Dashboard.test.tsx new file mode 100644 index 00000000..542aece6 --- /dev/null +++ b/test/unit-tests/Dashboard.test.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { render, fireEvent } from "@testing-library/react"; +import Dashboard from "@/components/layout/Dashboard"; +import { ThemeProvider } from "@/contexts/ThemeProvider"; +import { useOidc, useOidcAccessToken } from "@axa-fr/react-oidc"; + +// Mock the module +jest.mock("@axa-fr/react-oidc", () => ({ + useOidcAccessToken: jest.fn(), + useOidc: jest.fn(), +})); + +describe("", () => { + beforeEach(() => { + // Mock the return value for each test + (useOidcAccessToken as jest.Mock).mockReturnValue({ + accessTokenPayload: { + test: "test", + }, + }); + (useOidc as jest.Mock).mockReturnValue({ + isAuthenticated: false, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + // Normal case + it("renders on desktop screen", () => { + const { getByTestId } = render( + + +

Test

+
+
, + ); + + // `drawer-temporary` should not even be in the DOM for desktop screen sizes + expect(() => getByTestId("drawer-temporary")).toThrow(); + // Expect `drawer-permanent` to now be visible + expect(getByTestId("drawer-permanent")).toBeVisible(); + }); + + // Testing a hypothetical toggle button for the drawer + it("renders on mobile screen", () => { + global.innerWidth = 350; // e.g., 350px width for mobile + global.dispatchEvent(new Event("resize")); + + const { getByTestId } = render( + + +

Test

+
+
, + ); + const toggleButton = getByTestId("drawer-toggle-button"); + + // Assuming the drawer is initially closed + // `drawer-temporary` should not even be in the DOM initially + expect(() => getByTestId("drawer-temporary")).toThrow(); + + // Simulate a button click + fireEvent.click(toggleButton); + + // Expect the drawer to now be visible + expect(getByTestId("drawer-temporary")).toBeVisible(); + }); +}); diff --git a/test/unit-tests/DashboardButton.test.tsx b/test/unit-tests/DashboardButton.test.tsx new file mode 100644 index 00000000..e16f40a5 --- /dev/null +++ b/test/unit-tests/DashboardButton.test.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import { DashboardButton } from "@/components/ui/DashboardButton"; +import { useOidcAccessToken } from "@axa-fr/react-oidc"; + +// Mocking the useOidcAccessToken hook +jest.mock("@axa-fr/react-oidc", () => ({ + useOidcAccessToken: jest.fn(), +})); + +describe("", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it("renders the button when user is connected (has accessToken)", () => { + // Mocking the return value of useOidcAccessToken to simulate a user with an accessToken + (useOidcAccessToken as jest.Mock).mockReturnValue({ + accessToken: "mocked_token", + }); + + const { getByText } = render(); + const button = getByText("Dashboard"); + + expect(button).toBeInTheDocument(); + }); + + it("does not render the button when user is not connected (no accessToken)", () => { + // Mocking the return value of useOidcAccessToken to simulate a user without an accessToken + (useOidcAccessToken as jest.Mock).mockReturnValue({ accessToken: null }); + + const { queryByText } = render(); + const button = queryByText("Dashboard"); + + expect(button).not.toBeInTheDocument(); + }); +}); diff --git a/test/unit-tests/DiracLogo.test.tsx b/test/unit-tests/DiracLogo.test.tsx new file mode 100644 index 00000000..9f2a9985 --- /dev/null +++ b/test/unit-tests/DiracLogo.test.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import { DiracLogo } from "@/components/ui/DiracLogo"; + +describe("", () => { + it("renders the logo image with correct attributes", () => { + const { getByAltText } = render(); + + // Check if the image is rendered with the correct alt text + const logoImage = getByAltText("DIRAC logo"); + expect(logoImage).toBeInTheDocument(); + }); + + it("renders the link that redirects to the root page", () => { + const { getByRole } = render(); + + // Check if the link is rendered and points to the root page + const link = getByRole("link"); + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute("href", "/"); + }); +}); diff --git a/test/unit-tests/JobDataGrid.test.tsx b/test/unit-tests/JobDataGrid.test.tsx new file mode 100644 index 00000000..6784d3fd --- /dev/null +++ b/test/unit-tests/JobDataGrid.test.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import { JobDataGrid } from "@/components/ui/JobDataGrid"; +import { useJobs } from "@/hooks/jobs"; + +// Mocking the useJobs hook +jest.mock("../../src/hooks/jobs"); + +describe("", () => { + it("displays loading state", () => { + (useJobs as jest.Mock).mockReturnValue({ isLoading: true }); + + const { getByText } = render(); + expect(getByText("Loading...")).toBeInTheDocument(); + }); + + it("displays error state", () => { + (useJobs as jest.Mock).mockReturnValue({ error: true }); + + const { getByText } = render(); + expect( + getByText("An error occurred while fetching jobs."), + ).toBeInTheDocument(); + }); + + it("displays no jobs data state", () => { + (useJobs as jest.Mock).mockReturnValue({ data: [] }); + + const { getByText } = render(); + expect(getByText("No job submitted.")).toBeInTheDocument(); + }); + + it("displays jobs data in the grid", () => { + const mockData = [ + { + JobID: "1", + JobName: "TestJob1", + Status: "Running", + MinorStatus: "Processing", + SubmissionTime: "2023-10-13", + }, + ]; + (useJobs as jest.Mock).mockReturnValue({ data: mockData }); + + const { getByText } = render(); + expect(getByText("TestJob1")).toBeInTheDocument(); + }); +}); diff --git a/test/unit-tests/LoginButton.test.tsx b/test/unit-tests/LoginButton.test.tsx new file mode 100644 index 00000000..0efcdf38 --- /dev/null +++ b/test/unit-tests/LoginButton.test.tsx @@ -0,0 +1,69 @@ +import React from "react"; +import { render, fireEvent } from "@testing-library/react"; +import { LoginButton } from "@/components/ui/LoginButton"; +import { useOidcAccessToken, useOidc } from "@axa-fr/react-oidc"; + +// Mocking the hooks +jest.mock("@axa-fr/react-oidc"); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe("", () => { + it('displays the "Login" button when not authenticated', () => { + (useOidc as jest.Mock).mockReturnValue({ isAuthenticated: false }); + (useOidcAccessToken as jest.Mock).mockReturnValue({}); + + const { getByText } = render(); + expect(getByText("Login")).toBeInTheDocument(); + }); + + it("displays the user avatar when authenticated", () => { + (useOidc as jest.Mock).mockReturnValue({ isAuthenticated: true }); + (useOidcAccessToken as jest.Mock).mockReturnValue({ + accessToken: "mockAccessToken", + accessTokenPayload: { preferred_username: "John" }, + }); + + const { getByText } = render(); + expect(getByText("J")).toBeInTheDocument(); // Assuming 'John' is the preferred username and 'J' is the first letter. + }); + + it("opens the menu when avatar is clicked", () => { + (useOidc as jest.Mock).mockReturnValue({ isAuthenticated: true }); + (useOidcAccessToken as jest.Mock).mockReturnValue({ + accessToken: "mockAccessToken", + accessTokenPayload: { preferred_username: "John" }, + }); + + const { getByText, queryByText } = render(); + fireEvent.click(getByText("J")); + expect(queryByText("Profile")).toBeInTheDocument(); + expect(queryByText("Logout")).toBeInTheDocument(); + }); + + it('calls the logout function when "Logout" is clicked', () => { + const mockLogout = jest.fn(); + + (useOidc as jest.Mock).mockReturnValue({ + isAuthenticated: true, + logout: mockLogout, + }); + (useOidcAccessToken as jest.Mock).mockReturnValue({ + accessToken: "mockAccessToken", + accessTokenPayload: { preferred_username: "John" }, + }); + + const { getByText } = render(); + + // Open the menu by clicking the avatar + fireEvent.click(getByText("J")); + + // Click the "Logout" option + fireEvent.click(getByText("Logout")); + + // Ensure the mockLogout function was called + expect(mockLogout).toHaveBeenCalled(); + }); +}); diff --git a/test/ThemeToggleButton.test.tsx b/test/unit-tests/ThemeToggleButton.test.tsx similarity index 97% rename from test/ThemeToggleButton.test.tsx rename to test/unit-tests/ThemeToggleButton.test.tsx index 9472d721..faea8258 100644 --- a/test/ThemeToggleButton.test.tsx +++ b/test/unit-tests/ThemeToggleButton.test.tsx @@ -4,7 +4,7 @@ import { ThemeToggleButton } from "@/components/ui/ThemeToggleButton"; import { useTheme } from "@/hooks/theme"; // Mocking the useTheme hook -jest.mock("../src/hooks/theme", () => ({ +jest.mock("../../src/hooks/theme", () => ({ useTheme: jest.fn(), })); diff --git a/tsconfig.json b/tsconfig.json index 0914988a..be05ba1f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,5 +24,5 @@ } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules", "test/*.ts", "test/*.tsx", "jest.setup.ts"] + "exclude": ["node_modules", "test/*/*.ts", "test/*/*.tsx", "jest.setup.ts"] }