Skip to content

Commit

Permalink
feat: implement useMembers hook
Browse files Browse the repository at this point in the history
  • Loading branch information
sounmind committed Nov 7, 2024
1 parent eff0718 commit 6cfc282
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 0 deletions.
71 changes: 71 additions & 0 deletions src/hooks/useMemberInfo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { faker } from "@faker-js/faker";
import { renderHook, waitFor } from "@testing-library/react";
import { expect, test, vi } from "vitest";
import useMemberInfo, { type Member } from "./useMemberInfo";

test("fetch member info successfully and update state", async () => {
// Generate fake member data using faker
const mockMembers: Member[] = Array.from({ length: 5 }, () => ({
id: faker.string.uuid(),
name: faker.person.fullName(),
cohort: faker.number.int({ min: 1, max: 10 }),
profileUrl: faker.internet.url(),
totalSubmissions: faker.number.int({ min: 0, max: 100 }),
progress: faker.number.int({ min: 0, max: 100 }),
grade: faker.helpers.arrayElement([
"SEED",
"SPROUT",
"SMALL_TREE",
"BIG_TREE",
]),
submissions: Array.from(
{ length: faker.number.int({ min: 0, max: 10 }) },
() => ({
memberId: faker.string.uuid(),
problemTitle: faker.lorem.words(3),
language: faker.helpers.arrayElement(["JavaScript", "Python", "Java"]),
}),
),
}));

// Mock the getMembers function to return the fake data
const getMembers = vi.fn().mockResolvedValue(mockMembers);

// Use the hook with the mocked getMembers function
const { result } = renderHook(() => useMemberInfo({ getMembers }));

// Initial state validation
expect(result.current.isLoading).toBe(true);
expect(result.current.members).toBeNull();
expect(result.current.error).toBeNull();

// Wait for the hook to finish fetching data
await waitFor(() => expect(result.current.isLoading).toBe(false));

// Validate the updated state
expect(result.current.members).toEqual(mockMembers);
expect(result.current.error).toBeNull();
expect(getMembers).toHaveBeenCalledTimes(1);
});

test("handle error when fetching member info fails", async () => {
// Create a mock error
const mockError = new Error("Fetch error");
const getMembers = vi.fn().mockRejectedValue(mockError);

// Use the hook with the mocked getMembers function that rejects
const { result } = renderHook(() => useMemberInfo({ getMembers }));

// Initial state validation
expect(result.current.isLoading).toBe(true);
expect(result.current.members).toBeNull();
expect(result.current.error).toBeNull();

// Wait for the hook to handle the error
await waitFor(() => expect(result.current.isLoading).toBe(false));

// Validate the state after error
expect(result.current.members).toBeNull();
expect(result.current.error).toEqual(mockError);
expect(getMembers).toHaveBeenCalledTimes(1);
});
53 changes: 53 additions & 0 deletions src/hooks/useMemberInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useEffect, useState } from "react";

export interface Member {
id: string;
name: string;
cohort: number;
profileUrl?: string;
totalSubmissions: number;
progress: number;
grade: "SEED" | "SPROUT" | "SMALL_TREE" | "BIG_TREE";
submissions: {
memberId: string;
problemTitle: string;
language: string;
}[];
}

type UseMemberInfo = (params: { getMembers: () => Promise<Member[]> }) => {
members: Member[] | null;
isLoading: boolean;
error: unknown | null;
};

const useMemberInfo: UseMemberInfo = function ({ getMembers }) {
const [members, setMembers] = useState<Member[] | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<unknown | null>(null);

useEffect(() => {
const fetchMemberInfo = async () => {
setIsLoading(true);

try {
const members = await getMembers();
setMembers(members);
} catch (error) {
setError(error);
} finally {
setIsLoading(false);
}
};

fetchMemberInfo();
}, [getMembers]);

return {
members,
isLoading,
error,
};
};

export default useMemberInfo;

0 comments on commit 6cfc282

Please sign in to comment.