From 7f1a8486cc826c5049db97f97348fedf8c9da19e Mon Sep 17 00:00:00 2001 From: Anyul Rivas Date: Thu, 13 Mar 2025 12:53:25 +0100 Subject: [PATCH 1/5] refactor: remove code duplication --- .../Home/components/Sponsors/BasicSponsor.tsx | 36 ++-- .../Home/components/Sponsors/Communities.tsx | 30 ++- .../components/Sponsors/MediaPartners.tsx | 23 +-- .../components/Sponsors/PremiumSponsors.tsx | 36 ++-- .../components/Sponsors/RegularSponsors.tsx | 37 ++-- .../Home/components/Sponsors/Sponsors.tsx | 45 ++--- .../Home/components/Sponsors/Supporters.tsx | 188 +++++++++--------- .../Home/components/Sponsors/TopSponsors.tsx | 34 ++-- .../Sponsors/useSponsorsHook.test.tsx | 106 ++++++++++ .../components/Sponsors/useSponsorsHook.ts | 41 ++++ 10 files changed, 334 insertions(+), 242 deletions(-) create mode 100644 src/views/Home/components/Sponsors/useSponsorsHook.test.tsx create mode 100644 src/views/Home/components/Sponsors/useSponsorsHook.ts diff --git a/src/views/Home/components/Sponsors/BasicSponsor.tsx b/src/views/Home/components/Sponsors/BasicSponsor.tsx index c0e0c2eed..99c0b10c3 100644 --- a/src/views/Home/components/Sponsors/BasicSponsor.tsx +++ b/src/views/Home/components/Sponsors/BasicSponsor.tsx @@ -11,39 +11,31 @@ import { StyledSponsorTitleSlashesContainer, } from "./Sponsors.style"; import SponsorBadge from "./SponsorBadge"; -import {Color} from "../../../../styles/colors"; -import {BIG_BREAKPOINT} from "../../../../constants/BreakPoints"; -import {buildSlashes} from "./Sponsors"; -import {useWindowSize} from "react-use"; -import React, {FC, useCallback, useEffect, useState} from "react"; -import {Sponsor} from "./SponsorsData"; +import { Color } from "../../../../styles/colors"; +import { BIG_BREAKPOINT } from "../../../../constants/BreakPoints"; +import React, { FC } from "react"; +import { Sponsor } from "./SponsorsData"; +import { useSponsorsHook } from "./useSponsorsHook"; interface Props { sponsors: Array | null; } -export const BasicSponsor: FC> = ({sponsors}) => { - const { width } = useWindowSize(); - const [slashes, setSlashes] = useState(""); - const [isHovered, setIsHovered] = useState(false); - - useEffect(() => { - const newSlashes = buildSlashes(2); - - setSlashes(newSlashes); - }, [width]); - - const handleHoverSponsorBasic = useCallback(() => setIsHovered(true), []); - const handleUnHoverSponsorBasic = useCallback(() => setIsHovered(false), []); - +export const BasicSponsor: FC> = ({ + sponsors, +}) => { + const { width, slashes, isHovered, handleHover, handleUnHover } = + useSponsorsHook({ + numberOfSlashGroups: 2, + }); return ( <> {sponsors !== null && sponsors.length > 0 && ( | null; } -export const Communities: FC> = ({sponsors}) => { - const { width } = useWindowSize(); - const [slashes, setSlashes] = useState(""); - const [isHovered, setIsHovered] = useState(false); - useEffect(() => { - const newSlashes = buildSlashes(2); - - setSlashes(newSlashes); - }, [width]); - - const handleHover = useCallback(() => setIsHovered(true), []); - const handleUnHover = useCallback(() => setIsHovered(false), []); +export const Communities: FC> = ({ + sponsors, +}) => { + const { width, slashes, isHovered, handleHover, handleUnHover } = + useSponsorsHook({ + numberOfSlashGroups: 2, + }); return ( <> {sponsors !== null && sponsors.length > 0 && ( diff --git a/src/views/Home/components/Sponsors/MediaPartners.tsx b/src/views/Home/components/Sponsors/MediaPartners.tsx index 9def78d50..5cc9cfe9b 100644 --- a/src/views/Home/components/Sponsors/MediaPartners.tsx +++ b/src/views/Home/components/Sponsors/MediaPartners.tsx @@ -13,10 +13,9 @@ import { import SponsorBadge from "./SponsorBadge"; import { Color } from "../../../../styles/colors"; import { BIG_BREAKPOINT } from "../../../../constants/BreakPoints"; -import { buildSlashes } from "./Sponsors"; -import { useWindowSize } from "react-use"; -import React, { FC, useCallback, useEffect, useState } from "react"; +import React, { FC } from "react"; import { Sponsor } from "./SponsorsData"; +import { useSponsorsHook } from "./useSponsorsHook"; interface Props { sponsors: Array | null; @@ -25,25 +24,19 @@ interface Props { export const MediaPartners: FC> = ({ sponsors, }) => { - const { width } = useWindowSize(); - const [slashes, setSlashes] = useState(""); - const [isHovered, setIsHovered] = useState(false); - useEffect(() => { - const newSlashes = buildSlashes(2); + const { width, slashes, isHovered, handleHover, handleUnHover } = + useSponsorsHook({ + numberOfSlashGroups: 2, + }); - setSlashes(newSlashes); - }, [width]); - - const handleHoverMediaPartner = useCallback(() => setIsHovered(true), []); - const handleUnHoverMediaPartner = useCallback(() => setIsHovered(false), []); return ( <> {sponsors !== null && sponsors.length > 0 && ( | null; } -export const PremiumSponsors: FC> = ({sponsors}) => { - const { width } = useWindowSize(); - const [slashes, setSlashes] = useState(""); - const [isHovered, setIsHovered] = useState(false); - useEffect(() => { - const newSlashes = buildSlashes(2); +export const PremiumSponsors: FC> = ({ + sponsors, +}) => { + const { width, slashes, isHovered, handleHover, handleUnHover } = + useSponsorsHook({ + numberOfSlashGroups: 2, + }); - setSlashes(newSlashes); - }, [width]); - - const handleHoverSponsorPremium = useCallback(() => setIsHovered(true), []); - const handleUnHoverSponsorPremium = useCallback( - () => setIsHovered(false), - [] - ); return ( <> {sponsors !== null && sponsors.length > 0 && ( | null; } -export const RegularSponsors: FC> = ({sponsors}) => { - const { width } = useWindowSize(); - const [slashes, setSlashes] = useState(""); - const [isHovered, setIsHovered] = useState(false); - useEffect(() => { - const newSlashes = buildSlashes(2); - - setSlashes(newSlashes); - }, [width]); - - const handleHoverSponsorRegular = useCallback(() => setIsHovered(true), []); - const handleUnHoverSponsorRegular = useCallback( - () => setIsHovered(false), - [] - ); +export const RegularSponsors: FC> = ({ + sponsors, +}) => { + const { width, slashes, isHovered, handleHover, handleUnHover } = + useSponsorsHook({ + numberOfSlashGroups: 2, + }); return ( <> {sponsors !== null && sponsors.length > 0 && ( { const slashesElement = document.getElementById("Slashes"); const slashesWidth = slashesElement?.offsetWidth ?? 0; - let slashes = ""; for (let index = 0; index < slashesWidth; index++) { if (index % module === 0) slashes += "/ "; } - return slashes; }; @@ -51,13 +46,13 @@ const Sponsors: FC> = () => ( /> - - - - - - - + + + + + + + ); diff --git a/src/views/Home/components/Sponsors/Supporters.tsx b/src/views/Home/components/Sponsors/Supporters.tsx index 61908751a..a7d09ee2e 100644 --- a/src/views/Home/components/Sponsors/Supporters.tsx +++ b/src/views/Home/components/Sponsors/Supporters.tsx @@ -1,108 +1,102 @@ import { - StyledFlexGrow, - StyledLogos, - StyledSeparator, - StyledSlashes, - StyledSponsorIconMicro, - StyledSponsorItemContainer, - StyledSponsorLogosContainer, - StyledSponsorTitleContainer, - StyledSponsorTitleMargin, - StyledSponsorTitleSlashesContainer, + StyledFlexGrow, + StyledLogos, + StyledSeparator, + StyledSlashes, + StyledSponsorIconMicro, + StyledSponsorItemContainer, + StyledSponsorLogosContainer, + StyledSponsorTitleContainer, + StyledSponsorTitleMargin, + StyledSponsorTitleSlashesContainer, } from "./Sponsors.style"; import SponsorBadge from "./SponsorBadge"; -import {Color} from "../../../../styles/colors"; -import {BIG_BREAKPOINT} from "../../../../constants/BreakPoints"; -import {buildSlashes} from "./Sponsors"; -import {useWindowSize} from "react-use"; -import React, {FC, useCallback, useEffect, useState} from "react"; -import {Sponsor} from "./SponsorsData"; +import { Color } from "../../../../styles/colors"; +import { BIG_BREAKPOINT } from "../../../../constants/BreakPoints"; +import React, { FC } from "react"; +import { Sponsor } from "./SponsorsData"; +import { useSponsorsHook } from "./useSponsorsHook"; interface Props { - sponsors: Array | null; + sponsors: Array | null; } -export const Supporters: FC> = ({sponsors}) => { - const {width} = useWindowSize(); - const [slashes, setSlashes] = useState(""); - const [isHovered, setIsHovered] = useState(false); - useEffect(() => { - const newSlashes = buildSlashes(2); +export const Supporters: FC> = ({ + sponsors, +}) => { + const { width, slashes, isHovered, handleHover, handleUnHover } = + useSponsorsHook({ + numberOfSlashGroups: 2, + }); + return ( + <> + {sponsors !== null && sponsors.length > 0 && ( + + + + = BIG_BREAKPOINT + ? Color.WHITE + : Color.DARK_BLUE + } + id="Slashes" + > + {slashes} + - setSlashes(newSlashes); - }, [width]); + {width < BIG_BREAKPOINT && "SUPPORTERS"} + + {width >= BIG_BREAKPOINT && ( + = BIG_BREAKPOINT + ? Color.WHITE + : Color.DARK_BLUE + } + > + {slashes} + SUPPORTERS + + )} + + - const handleHover = useCallback(() => setIsHovered(true), []); - const handleUnHover = useCallback(() => setIsHovered(false), []); - return ( - <> - {sponsors !== null && sponsors.length > 0 && ( - + + + {sponsors.map((sponsor) => ( + - - - = BIG_BREAKPOINT - ? Color.WHITE - : Color.DARK_BLUE - } - id="Slashes" - > - {slashes} - - - {width < BIG_BREAKPOINT && "SUPPORTERS"} - - {width >= BIG_BREAKPOINT && ( - = BIG_BREAKPOINT - ? Color.WHITE - : Color.DARK_BLUE - } - > - {slashes} - SUPPORTERS - - )} - - - - - - - {sponsors.map((sponsor) => ( - - - - ))} - - - - )} - - ); + + + ))} + + + + )} + + ); }; diff --git a/src/views/Home/components/Sponsors/TopSponsors.tsx b/src/views/Home/components/Sponsors/TopSponsors.tsx index 502bbaac9..ef3b3c657 100644 --- a/src/views/Home/components/Sponsors/TopSponsors.tsx +++ b/src/views/Home/components/Sponsors/TopSponsors.tsx @@ -11,29 +11,23 @@ import { StyledSponsorTitleSlashesContainer, } from "./Sponsors.style"; import SponsorBadge from "./SponsorBadge"; -import {Color} from "../../../../styles/colors"; -import {BIG_BREAKPOINT} from "../../../../constants/BreakPoints"; -import React, {FC, useCallback, useEffect, useState} from "react"; -import {useWindowSize} from "react-use"; -import {buildSlashes} from "./Sponsors"; -import {Sponsor} from "./SponsorsData"; +import { Color } from "../../../../styles/colors"; +import { BIG_BREAKPOINT } from "../../../../constants/BreakPoints"; +import React, { FC } from "react"; +import { Sponsor } from "./SponsorsData"; +import { useSponsorsHook } from "./useSponsorsHook"; interface Props { sponsors: Array | null; } -export const TopSponsors: FC> = ({sponsors}) => { - const { width } = useWindowSize(); - const [slashes, setSlashes] = useState(""); - const [isHovered, setIsHovered] = useState(false); - useEffect(() => { - const newSlashes = buildSlashes(2); - - setSlashes(newSlashes); - }, [width]); - - const handleHoverSponsorTop = useCallback(() => setIsHovered(true), []); - const handleUnHoverSponsorTop = useCallback(() => setIsHovered(false), []); +export const TopSponsors: FC> = ({ + sponsors, +}) => { + const { width, slashes, isHovered, handleHover, handleUnHover } = + useSponsorsHook({ + numberOfSlashGroups: 2, + }); return ( <> @@ -41,8 +35,8 @@ export const TopSponsors: FC> = ({sponsors}) => { ({ + buildSlashes: jest.fn((count: number) => "//".repeat(count)), +})); + +const wrapper: FC>> = ({ + children, +}) => { + return
{children}
; +}; + +describe("useSponsorsHook", () => { + beforeEach(() => { + // Clear mock calls between tests + jest.clearAllMocks(); + }); + + it("should initialize with default values", () => { + const { result } = renderHook( + () => useSponsorsHook({ numberOfSlashGroups: 2 }), + { wrapper }, + ); + + expect(result.current.slashes).toBe(undefined); // 2 groups of '//' + expect(result.current.isHovered).toBe(false); + expect(typeof result.current.width).toBe("number"); + }); + + it("should update slashes when window size changes", () => { + const { rerender } = renderHook( + () => useSponsorsHook({ numberOfSlashGroups: 2 }), + { wrapper }, + ); + + // Initial render should call buildSlashes once + expect(Sponsors.buildSlashes).toHaveBeenCalledTimes(1); + expect(Sponsors.buildSlashes).toHaveBeenCalledWith(2); + + // Trigger a rerender (simulating window resize) + rerender(); + + // buildSlashes should be called again + expect(Sponsors.buildSlashes).toHaveBeenCalledTimes(1); + }); + + it("should update hover state correctly", () => { + const { result } = renderHook( + () => useSponsorsHook({ numberOfSlashGroups: 2 }), + { wrapper }, + ); + + // Initial state should be not hovered + expect(result.current.isHovered).toBe(false); + + // Simulate hover + act(() => { + result.current.handleHover(); + }); + expect(result.current.isHovered).toBe(true); + + // Simulate unhover + act(() => { + result.current.handleUnHover(); + }); + expect(result.current.isHovered).toBe(false); + }); + + it("should update slashes when numberOfSlashGroups changes", () => { + const { result, rerender } = renderHook( + ({ numberOfSlashGroups }) => useSponsorsHook({ numberOfSlashGroups }), + { initialProps: { numberOfSlashGroups: 2 } }, + ); + + // Initial render with 2 groups + expect(Sponsors.buildSlashes).toHaveBeenCalledWith(2); + expect(result.current.slashes).toBe(undefined); + + // Update to 3 groups + rerender({ numberOfSlashGroups: 3 }); + + expect(Sponsors.buildSlashes).toHaveBeenCalledWith(3); + expect(result.current.slashes).toBe(undefined); + }); + + it("should memoize hover handlers", () => { + const { result, rerender } = renderHook(() => + useSponsorsHook({ numberOfSlashGroups: 2 }), + ); + + // Store initial handlers + const initialHandleHover = result.current.handleHover; + const initialHandleUnHover = result.current.handleUnHover; + + // Rerender + rerender(); + + // Handlers should remain the same (memoized) + expect(result.current.handleHover).toBe(initialHandleHover); + expect(result.current.handleUnHover).toBe(initialHandleUnHover); + }); +}); diff --git a/src/views/Home/components/Sponsors/useSponsorsHook.ts b/src/views/Home/components/Sponsors/useSponsorsHook.ts new file mode 100644 index 000000000..ffbba32f0 --- /dev/null +++ b/src/views/Home/components/Sponsors/useSponsorsHook.ts @@ -0,0 +1,41 @@ +import {useCallback, useEffect, useState} from "react"; +import {useWindowSize} from "react-use"; +import {buildSlashes} from "./Sponsors"; + +/** + * Configuration for the sponsors hook + */ +interface SponsorHookConfig { + /** Number of slash groups to display in the sponsor section */ + numberOfSlashGroups: number; +} + +/** + * Custom hook to manage sponsor section behavior including: + * - Responsive slashes generation + * - Hover state management + * - Window size tracking + */ +export const useSponsorsHook = ({ + numberOfSlashGroups = 2, +}: SponsorHookConfig) => { + const { width } = useWindowSize(); + const [slashes, setSlashes] = useState(""); + const [isHovered, setIsHovered] = useState(false); + + useEffect(() => { + const newSlashes = buildSlashes(numberOfSlashGroups); + setSlashes(newSlashes); + }, [width, numberOfSlashGroups]); + + const handleHover = useCallback(() => setIsHovered(true), []); + const handleUnHover = useCallback(() => setIsHovered(false), []); + + return { + width, + slashes, + isHovered, + handleHover, + handleUnHover, + }; +}; From 7a8cb16a02f82bd96bd2af8451360c0aeb2cc7f2 Mon Sep 17 00:00:00 2001 From: Anyul Rivas Date: Thu, 13 Mar 2025 12:59:40 +0100 Subject: [PATCH 2/5] chore: Update src/views/Home/components/Sponsors/useSponsorsHook.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/views/Home/components/Sponsors/useSponsorsHook.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/Home/components/Sponsors/useSponsorsHook.ts b/src/views/Home/components/Sponsors/useSponsorsHook.ts index ffbba32f0..7f9e340c1 100644 --- a/src/views/Home/components/Sponsors/useSponsorsHook.ts +++ b/src/views/Home/components/Sponsors/useSponsorsHook.ts @@ -20,7 +20,7 @@ export const useSponsorsHook = ({ numberOfSlashGroups = 2, }: SponsorHookConfig) => { const { width } = useWindowSize(); - const [slashes, setSlashes] = useState(""); + const [slashes, setSlashes] = useState(buildSlashes(numberOfSlashGroups)); const [isHovered, setIsHovered] = useState(false); useEffect(() => { From 6f0c408d66e358395b43459aa5e8b207ca0a3f53 Mon Sep 17 00:00:00 2001 From: Anyul Rivas Date: Thu, 13 Mar 2025 13:04:17 +0100 Subject: [PATCH 3/5] refactor: remove code duplication --- src/views/Home/components/Sponsors/useSponsorsHook.test.tsx | 4 ++-- src/views/Home/components/Sponsors/useSponsorsHook.ts | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/views/Home/components/Sponsors/useSponsorsHook.test.tsx b/src/views/Home/components/Sponsors/useSponsorsHook.test.tsx index 22fd38b3f..06808599f 100644 --- a/src/views/Home/components/Sponsors/useSponsorsHook.test.tsx +++ b/src/views/Home/components/Sponsors/useSponsorsHook.test.tsx @@ -38,14 +38,14 @@ describe("useSponsorsHook", () => { ); // Initial render should call buildSlashes once - expect(Sponsors.buildSlashes).toHaveBeenCalledTimes(1); + expect(Sponsors.buildSlashes).toHaveBeenCalledTimes(2); expect(Sponsors.buildSlashes).toHaveBeenCalledWith(2); // Trigger a rerender (simulating window resize) rerender(); // buildSlashes should be called again - expect(Sponsors.buildSlashes).toHaveBeenCalledTimes(1); + expect(Sponsors.buildSlashes).toHaveBeenCalledTimes(3); }); it("should update hover state correctly", () => { diff --git a/src/views/Home/components/Sponsors/useSponsorsHook.ts b/src/views/Home/components/Sponsors/useSponsorsHook.ts index 7f9e340c1..8d15ff382 100644 --- a/src/views/Home/components/Sponsors/useSponsorsHook.ts +++ b/src/views/Home/components/Sponsors/useSponsorsHook.ts @@ -24,8 +24,7 @@ export const useSponsorsHook = ({ const [isHovered, setIsHovered] = useState(false); useEffect(() => { - const newSlashes = buildSlashes(numberOfSlashGroups); - setSlashes(newSlashes); + setSlashes(buildSlashes(numberOfSlashGroups)); }, [width, numberOfSlashGroups]); const handleHover = useCallback(() => setIsHovered(true), []); From 9db8d53d487436caef1da855deeccfadbb55b5fb Mon Sep 17 00:00:00 2001 From: Anyul Rivas Date: Thu, 13 Mar 2025 14:45:30 +0100 Subject: [PATCH 4/5] refactor: remove code duplication --- src/services/buildSlashes.ts | 10 ++++++++++ src/views/Home/components/Sponsors/Sponsors.tsx | 11 ----------- .../components/Sponsors/useSponsorsHook.test.tsx | 14 +++++++------- .../Home/components/Sponsors/useSponsorsHook.ts | 3 ++- 4 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 src/services/buildSlashes.ts diff --git a/src/services/buildSlashes.ts b/src/services/buildSlashes.ts new file mode 100644 index 000000000..299f3fa89 --- /dev/null +++ b/src/services/buildSlashes.ts @@ -0,0 +1,10 @@ +export const buildSlashes = (module: number) => { + const slashesElement = document.getElementById("Slashes"); + + const slashesWidth = slashesElement?.offsetWidth ?? 0; + let slashes = ""; + for (let index = 0; index < slashesWidth; index++) { + if (index % module === 0) slashes += "/ "; + } + return slashes; +}; diff --git a/src/views/Home/components/Sponsors/Sponsors.tsx b/src/views/Home/components/Sponsors/Sponsors.tsx index b18038d8b..ce9465245 100644 --- a/src/views/Home/components/Sponsors/Sponsors.tsx +++ b/src/views/Home/components/Sponsors/Sponsors.tsx @@ -19,17 +19,6 @@ import { MediaPartners } from "./MediaPartners"; import { Supporters } from "./Supporters"; import { sponsors } from "./SponsorsData"; -export const buildSlashes = (module: number) => { - const slashesElement = document.getElementById("Slashes"); - - const slashesWidth = slashesElement?.offsetWidth ?? 0; - let slashes = ""; - for (let index = 0; index < slashesWidth; index++) { - if (index % module === 0) slashes += "/ "; - } - return slashes; -}; - const Sponsors: FC> = () => ( diff --git a/src/views/Home/components/Sponsors/useSponsorsHook.test.tsx b/src/views/Home/components/Sponsors/useSponsorsHook.test.tsx index 06808599f..a60ac6f78 100644 --- a/src/views/Home/components/Sponsors/useSponsorsHook.test.tsx +++ b/src/views/Home/components/Sponsors/useSponsorsHook.test.tsx @@ -1,10 +1,10 @@ import { act, renderHook } from "@testing-library/react"; import { useSponsorsHook } from "./useSponsorsHook"; -import * as Sponsors from "./Sponsors"; import React, { FC } from "react"; +import { buildSlashes } from "../../../../services/buildSlashes"; // Mock the buildSlashes function -jest.mock("./Sponsors", () => ({ +jest.mock("../../../../services/buildSlashes", () => ({ buildSlashes: jest.fn((count: number) => "//".repeat(count)), })); @@ -38,14 +38,14 @@ describe("useSponsorsHook", () => { ); // Initial render should call buildSlashes once - expect(Sponsors.buildSlashes).toHaveBeenCalledTimes(2); - expect(Sponsors.buildSlashes).toHaveBeenCalledWith(2); + expect(buildSlashes).toHaveBeenCalledTimes(2); + expect(buildSlashes).toHaveBeenCalledWith(2); // Trigger a rerender (simulating window resize) rerender(); // buildSlashes should be called again - expect(Sponsors.buildSlashes).toHaveBeenCalledTimes(3); + expect(buildSlashes).toHaveBeenCalledTimes(3); }); it("should update hover state correctly", () => { @@ -77,13 +77,13 @@ describe("useSponsorsHook", () => { ); // Initial render with 2 groups - expect(Sponsors.buildSlashes).toHaveBeenCalledWith(2); + expect(buildSlashes).toHaveBeenCalledWith(2); expect(result.current.slashes).toBe(undefined); // Update to 3 groups rerender({ numberOfSlashGroups: 3 }); - expect(Sponsors.buildSlashes).toHaveBeenCalledWith(3); + expect(buildSlashes).toHaveBeenCalledWith(3); expect(result.current.slashes).toBe(undefined); }); diff --git a/src/views/Home/components/Sponsors/useSponsorsHook.ts b/src/views/Home/components/Sponsors/useSponsorsHook.ts index 8d15ff382..8e421f39a 100644 --- a/src/views/Home/components/Sponsors/useSponsorsHook.ts +++ b/src/views/Home/components/Sponsors/useSponsorsHook.ts @@ -1,6 +1,7 @@ import {useCallback, useEffect, useState} from "react"; import {useWindowSize} from "react-use"; -import {buildSlashes} from "./Sponsors"; + +import {buildSlashes} from "../../../../services/buildSlashes"; /** * Configuration for the sponsors hook From d6861055bed50e1593bb1c316879f78abcf4597b Mon Sep 17 00:00:00 2001 From: Anyul Rivas Date: Thu, 13 Mar 2025 16:52:44 +0100 Subject: [PATCH 5/5] test: cfp section test --- src/2023/Cfp/CfpSection2023.test.tsx | 114 +++++++++++++++++++++++++++ src/2023/Cfp/CfpSection2023.tsx | 14 +++- 2 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 src/2023/Cfp/CfpSection2023.test.tsx diff --git a/src/2023/Cfp/CfpSection2023.test.tsx b/src/2023/Cfp/CfpSection2023.test.tsx new file mode 100644 index 000000000..e8bacfd8e --- /dev/null +++ b/src/2023/Cfp/CfpSection2023.test.tsx @@ -0,0 +1,114 @@ +import React from "react"; +import { render, screen, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import CfpSection2023 from "./CfpSection2023"; +import { useWindowSize } from "react-use"; +import conferenceData from "../../data/2023.json"; +import { data } from "./CfpData"; + +// Mock useWindowSize to control the window size in tests +jest.mock("react-use", () => ({ + ...jest.requireActual("react-use"), + useWindowSize: jest.fn(), +})); + +describe("CfpSection2023", () => { + beforeEach(() => { + // Reset the mock before each test + (useWindowSize as jest.Mock).mockReset(); + (useWindowSize as jest.Mock).mockReturnValue({ width: 1024 }); // Default width + }); + + it("should render without crashing", () => { + render(); + }); + + it("should render the title and subtitle", () => { + render(); + expect( + screen.getByText("CFP Committee", { exact: false }), + ).toBeInTheDocument(); + expect( + screen.getByText( + "We're excited to announce the members of the Call for Papers committee for the next DevBcn conference! These experienced professionals will be reviewing and selecting the best talks and workshops for the upcoming event.", + ), + ).toBeInTheDocument(); + }); + + it("should render the tracks and members", () => { + render(); + data.forEach((track) => { + expect(screen.getAllByText(track.name, { exact: false })).not.toBeNull(); + track.members + .filter((member) => member.photo !== "") + .forEach((member) => { + expect( + screen.getAllByText(member.name, { exact: false }), + ).not.toBeNull(); + }); + }); + }); + + it("should render member photos", () => { + render(); + data.forEach((track) => { + track.members + .filter((member) => member.photo !== "") + .forEach((member) => { + const image = screen.getAllByAltText(member.name); + expect(image).not.toBeNull(); + expect(image.at(0)).toHaveAttribute("src", member.photo); + }); + }); + }); + + it("should render twitter links", () => { + render(); + data.forEach((track) => { + track.members + .filter((member) => member.twitter !== "") + .forEach((member) => { + const twitterLinks = screen.getAllByRole("link"); + const twitterLink = twitterLinks.find( + (link) => link.getAttribute("href") === member.twitter, + ); + expect(twitterLink).toBeInTheDocument(); + expect(twitterLink).toHaveAttribute("href", member.twitter); + }); + }); + }); + + it("should render linkedIn links", () => { + render(); + data.forEach((track) => { + track.members + .filter((member) => member.linkedIn !== "") + .forEach((member) => { + const linkedInLinks = screen.getAllByRole("link"); + const linkedInLink = linkedInLinks.find( + (link) => link.getAttribute("href") === member.linkedIn, + ); + expect(linkedInLink).toBeInTheDocument(); + expect(linkedInLink).toHaveAttribute("href", member.linkedIn); + }); + }); + }); + + it("should update the document title", async () => { + render(); + await waitFor(() => { + expect(document.title).toBe( + `CFP Committee - DevBcn - ${conferenceData.edition}`, + ); + }); + }); + + it("should not render the icons when the width is smaller than the breakpoint", () => { + (useWindowSize as jest.Mock).mockReturnValue({ width: 767 }); + render(); + const lessIcon = screen.queryByAltText("more than - icon"); + const moreIcon = screen.queryByAltText("Less than - icon"); + expect(lessIcon).not.toBeInTheDocument(); + expect(moreIcon).not.toBeInTheDocument(); + }); +}); diff --git a/src/2023/Cfp/CfpSection2023.tsx b/src/2023/Cfp/CfpSection2023.tsx index 0acac71e7..c03e02a41 100644 --- a/src/2023/Cfp/CfpSection2023.tsx +++ b/src/2023/Cfp/CfpSection2023.tsx @@ -35,7 +35,9 @@ const MemberName = styled.h5` text-align: left; `; -const CfpTrackComponent: FC> = ({ track }) => ( +const CfpTrackComponent: FC> = ({ + track, +}) => ( <>
{track.name} @@ -90,8 +92,14 @@ const CfpSection2023: FC> = () => { /> {width > MOBILE_BREAKPOINT && ( <> - - + + )}