diff --git a/frontend/__tests__/unit/components/SkeletonBase.test.tsx b/frontend/__tests__/unit/components/SkeletonBase.test.tsx
new file mode 100644
index 0000000000..1c214c340c
--- /dev/null
+++ b/frontend/__tests__/unit/components/SkeletonBase.test.tsx
@@ -0,0 +1,473 @@
+import { render, screen } from '@testing-library/react'
+import React from 'react'
+import SkeletonBase from 'components/SkeletonsBase'
+
+jest.mock('@heroui/skeleton', () => ({
+ Skeleton: ({ className, children }: { className?: string; children?: React.ReactNode }) => (
+
+ {children}
+
+ ),
+}))
+
+jest.mock('components/LoadingSpinner', () => {
+ return function MockLoadingSpinner({ imageUrl }: { imageUrl: string }) {
+ return
+ }
+})
+
+jest.mock('components/skeletons/Card', () => {
+ return function MockCardSkeleton({
+ showLevel = true,
+ showIcons = true,
+ showLink = true,
+ numIcons = 1,
+ showContributors = true,
+ showSocial = true,
+ }: {
+ showLevel?: boolean
+ showIcons?: boolean
+ showLink?: boolean
+ numIcons?: number
+ showContributors?: boolean
+ showSocial?: boolean
+ }) {
+ return (
+
+ )
+ }
+})
+
+jest.mock('components/skeletons/UserCard', () => {
+ return function MockUserCardSkeleton() {
+ return
+ }
+})
+
+describe('SkeletonBase', () => {
+ const defaultProps = {
+ indexName: 'projects',
+ loadingImageUrl: 'https://example.com/loading.gif',
+ }
+
+ describe('Basic Rendering', () => {
+ it('renders successfully with minimal required props', () => {
+ render()
+ expect(screen.getAllByTestId('card-skeleton')[0]).toBeInTheDocument()
+ })
+
+ it('renders without crashing when props are provided', () => {
+ const { container } = render()
+ expect(container.firstChild).toBeInTheDocument()
+ })
+
+ it('renders different components based on indexName prop', () => {
+ const { rerender } = render()
+ expect(screen.getAllByTestId('card-skeleton')).toHaveLength(4)
+
+ rerender()
+ expect(screen.getAllByTestId('user-card-skeleton')).toHaveLength(12)
+ })
+ })
+
+ describe('Conditional Rendering Logic', () => {
+ it('renders LoadingSpinner for unknown indexName', () => {
+ render()
+
+ const spinner = screen.getByTestId('loading-spinner')
+ expect(spinner).toBeInTheDocument()
+ expect(spinner).toHaveAttribute('data-image-url', 'test-image.jpg')
+ })
+
+ it('renders LoadingSpinner for empty indexName', () => {
+ render()
+
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument()
+ })
+
+ it('renders user cards grid for users indexName', () => {
+ render()
+
+ const userCards = screen.getAllByTestId('user-card-skeleton')
+ expect(userCards).toHaveLength(12)
+
+ const gridContainer = userCards[0].parentElement
+ expect(gridContainer).toHaveClass(
+ 'grid',
+ 'grid-cols-1',
+ 'gap-6',
+ 'sm:grid-cols-2',
+ 'lg:grid-cols-3',
+ 'xl:grid-cols-4'
+ )
+ })
+
+ it('renders skeleton components for non-users indexName', () => {
+ render()
+
+ const skeletonComponents = screen.getAllByTestId('card-skeleton')
+ expect(skeletonComponents).toHaveLength(4)
+ })
+ })
+
+ describe('Prop-based Behaviour', () => {
+ it('configures chapters skeleton correctly', () => {
+ render()
+
+ expect(screen.getByTestId('hero-skeleton')).toBeInTheDocument()
+ expect(screen.getByTestId('hero-skeleton')).toHaveClass('mb-2', 'h-96', 'w-full', 'max-w-6xl')
+
+ const cardSkeletons = screen.getAllByTestId('card-skeleton')
+ cardSkeletons.forEach((skeleton) => {
+ expect(skeleton).toHaveAttribute('data-show-level', 'false')
+ expect(skeleton).toHaveAttribute('data-show-icons', 'false')
+ expect(skeleton).toHaveAttribute('data-show-link', 'false')
+ })
+ })
+
+ it('configures issues skeleton correctly', () => {
+ render()
+
+ const cardSkeletons = screen.getAllByTestId('card-skeleton')
+ cardSkeletons.forEach((skeleton) => {
+ expect(skeleton).toHaveAttribute('data-show-level', 'false')
+ expect(skeleton).toHaveAttribute('data-show-icons', 'true')
+ expect(skeleton).toHaveAttribute('data-num-icons', '2')
+ expect(skeleton).toHaveAttribute('data-show-contributors', 'false')
+ expect(skeleton).toHaveAttribute('data-show-social', 'false')
+ })
+ })
+
+ it('configures projects skeleton correctly', () => {
+ render()
+
+ const cardSkeletons = screen.getAllByTestId('card-skeleton')
+ cardSkeletons.forEach((skeleton) => {
+ expect(skeleton).toHaveAttribute('data-show-link', 'false')
+ expect(skeleton).toHaveAttribute('data-show-social', 'false')
+ expect(skeleton).toHaveAttribute('data-show-icons', 'true')
+ expect(skeleton).toHaveAttribute('data-num-icons', '3')
+ })
+ })
+
+ it('configures committees skeleton correctly', () => {
+ render()
+
+ const cardSkeletons = screen.getAllByTestId('card-skeleton')
+ cardSkeletons.forEach((skeleton) => {
+ expect(skeleton).toHaveAttribute('data-show-link', 'false')
+ expect(skeleton).toHaveAttribute('data-show-level', 'false')
+ expect(skeleton).toHaveAttribute('data-show-icons', 'true')
+ expect(skeleton).toHaveAttribute('data-num-icons', '1')
+ })
+ })
+
+ it('passes loadingImageUrl to LoadingSpinner correctly', () => {
+ const testImageUrl = 'https://example.com/custom-loading.gif'
+ render()
+
+ const spinner = screen.getByTestId('loading-spinner')
+ expect(spinner).toHaveAttribute('data-image-url', testImageUrl)
+ })
+ })
+
+ describe('State Changers / Internal Logic', () => {
+ it('switches between different skeleton configurations based on indexName', () => {
+ const { rerender } = render()
+
+ let cardSkeletons = screen.getAllByTestId('card-skeleton')
+ expect(cardSkeletons[0]).toHaveAttribute('data-num-icons', '2')
+
+ rerender()
+ cardSkeletons = screen.getAllByTestId('card-skeleton')
+ expect(cardSkeletons[0]).toHaveAttribute('data-num-icons', '3')
+
+ rerender()
+ cardSkeletons = screen.getAllByTestId('card-skeleton')
+ expect(cardSkeletons[0]).toHaveAttribute('data-num-icons', '1')
+ })
+
+ it('correctly determines component type based on switch logic', () => {
+ const { unmount: unmountChapters } = render(
+
+ )
+ expect(screen.getAllByTestId('card-skeleton')[0]).toBeInTheDocument()
+ unmountChapters()
+
+ const { unmount: unmountIssues } = render(
+
+ )
+ expect(screen.getAllByTestId('card-skeleton')[0]).toBeInTheDocument()
+ unmountIssues()
+
+ const { unmount: unmountProjects } = render(
+
+ )
+ expect(screen.getAllByTestId('card-skeleton')[0]).toBeInTheDocument()
+ unmountProjects()
+
+ const { unmount: unmountCommittees } = render(
+
+ )
+ expect(screen.getAllByTestId('card-skeleton')[0]).toBeInTheDocument()
+ unmountCommittees()
+
+ const { unmount: unmountUsers } = render(
+
+ )
+ expect(screen.getAllByTestId('user-card-skeleton')).toHaveLength(12)
+ unmountUsers()
+
+ const { unmount: unmountUnknown } = render(
+
+ )
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument()
+ unmountUnknown()
+ })
+ })
+
+ describe('Default Values and Fallbacks', () => {
+ it('falls back to LoadingSpinner for unhandled indexName values', () => {
+ const unhandledValues = ['random', 'test', 'invalid', '123', 'null']
+
+ unhandledValues.forEach((value) => {
+ const { container } = render(
+
+ )
+
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument()
+ container.remove()
+ })
+ })
+
+ it('handles case-sensitive indexName correctly', () => {
+ const { rerender } = render()
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument()
+
+ rerender()
+ expect(screen.getAllByTestId('user-card-skeleton')).toHaveLength(12)
+ expect(screen.queryByTestId('loading-spinner')).not.toBeInTheDocument()
+ })
+
+ it('uses default CardSkeleton props when not explicitly set', () => {
+ render()
+
+ const cardSkeletons = screen.getAllByTestId('card-skeleton')
+ cardSkeletons.forEach((skeleton) => {
+ expect(skeleton).toHaveAttribute('data-show-level', 'true')
+ expect(skeleton).toHaveAttribute('data-show-contributors', 'true')
+ })
+ })
+ })
+
+ describe('Text and Content Rendering', () => {
+ it('renders correct number of skeleton components', () => {
+ const { rerender } = render()
+ expect(screen.getAllByTestId('card-skeleton')).toHaveLength(4)
+
+ rerender()
+ expect(screen.getAllByTestId('user-card-skeleton')).toHaveLength(12)
+
+ expect(screen.queryByTestId('card-skeleton')).not.toBeInTheDocument()
+ })
+
+ it('renders hero skeleton only for chapters', () => {
+ const { rerender } = render()
+ expect(screen.getByTestId('hero-skeleton')).toBeInTheDocument()
+
+ rerender()
+ expect(screen.queryByTestId('hero-skeleton')).not.toBeInTheDocument()
+ })
+
+ it('does not render extra components for non-chapters types', () => {
+ render()
+
+ expect(screen.queryByTestId('hero-skeleton')).not.toBeInTheDocument()
+ expect(screen.getAllByTestId('card-skeleton')).toHaveLength(4)
+ })
+ })
+
+ describe('Edge Cases and Invalid Inputs', () => {
+ it('handles null indexName', () => {
+ render()
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument()
+ })
+
+ it('handles undefined indexName', () => {
+ render()
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument()
+ })
+
+ it('handles empty string loadingImageUrl', () => {
+ render()
+
+ const spinner = screen.getByTestId('loading-spinner')
+ expect(spinner).toHaveAttribute('data-image-url', '')
+ })
+
+ it('handles null loadingImageUrl', () => {
+ render()
+
+ const spinner = screen.getByTestId('loading-spinner')
+ expect(spinner).toHaveAttribute('data-image-url', '')
+ })
+
+ it('handles special characters in indexName', () => {
+ const specialNames = ['test-name', 'test_name', 'test.name', 'test name', '!@#$%']
+
+ specialNames.forEach((name) => {
+ const { container } = render()
+
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument()
+ container.remove()
+ })
+ })
+
+ it('handles very long indexName strings', () => {
+ const longName = 'a'.repeat(1000)
+ render()
+
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument()
+ })
+
+ it('handles very long loadingImageUrl strings', () => {
+ const longUrl = 'https://example.com/' + 'a'.repeat(1000) + '.jpg'
+ render()
+
+ const spinner = screen.getByTestId('loading-spinner')
+ expect(spinner).toHaveAttribute('data-image-url', longUrl)
+ })
+ })
+
+ describe('Accessibility', () => {
+ it('maintains proper component structure for screen readers', () => {
+ const { container } = render()
+
+ const mainContainer = container.querySelector('div')
+ expect(mainContainer).toHaveClass(
+ 'flex',
+ 'w-full',
+ 'flex-col',
+ 'items-center',
+ 'justify-center'
+ )
+ })
+
+ it('provides accessible grid structure for users', () => {
+ render()
+
+ const userCards = screen.getAllByTestId('user-card-skeleton')
+ const gridContainer = userCards[0].parentElement
+
+ expect(gridContainer).toHaveClass('grid')
+ expect(gridContainer).toHaveAttribute('class')
+ })
+
+ it('ensures skeleton components are properly nested', () => {
+ render()
+
+ const heroSkeleton = screen.getByTestId('hero-skeleton')
+ const cardSkeletons = screen.getAllByTestId('card-skeleton')
+
+ expect(heroSkeleton.parentElement).toHaveClass('flex', 'w-full', 'flex-col')
+ cardSkeletons.forEach((skeleton) => {
+ expect(skeleton.parentElement).toHaveClass('flex', 'w-full', 'flex-col')
+ })
+ })
+ })
+
+ describe('DOM Structure / ClassNames / Styles', () => {
+ it('applies correct container classes for non-users components', () => {
+ const { container } = render()
+
+ const mainContainer = container.firstChild as HTMLElement
+ expect(mainContainer).toHaveClass(
+ 'flex',
+ 'w-full',
+ 'flex-col',
+ 'items-center',
+ 'justify-center'
+ )
+ })
+
+ it('applies correct hero skeleton classes for chapters', () => {
+ render()
+
+ const heroSkeleton = screen.getByTestId('hero-skeleton')
+ expect(heroSkeleton).toHaveClass('mb-2', 'h-96', 'w-full', 'max-w-6xl')
+ })
+
+ it('applies correct grid classes for users', () => {
+ render()
+
+ const userCards = screen.getAllByTestId('user-card-skeleton')
+ const gridContainer = userCards[0].parentElement
+
+ expect(gridContainer).toHaveClass(
+ 'grid',
+ 'grid-cols-1',
+ 'gap-6',
+ 'sm:grid-cols-2',
+ 'lg:grid-cols-3',
+ 'xl:grid-cols-4'
+ )
+ })
+
+ it('maintains consistent DOM structure across different skeleton types', () => {
+ const skeletonTypes = ['chapters', 'issues', 'projects', 'committees']
+
+ skeletonTypes.forEach((type) => {
+ const { container } = render()
+
+ const mainContainer = container.querySelector('div')
+ expect(mainContainer).toHaveClass('flex', 'w-full', 'flex-col')
+
+ container.remove()
+ })
+ })
+ })
+
+ describe('Component Integration', () => {
+ it('properly integrates with mocked HeroUI Skeleton component', () => {
+ render()
+
+ const heroSkeleton = screen.getByTestId('hero-skeleton')
+ expect(heroSkeleton).toBeInTheDocument()
+ expect(heroSkeleton).toHaveClass('mb-2', 'h-96', 'w-full', 'max-w-6xl')
+ })
+
+ it('properly integrates with mocked CardSkeleton component', () => {
+ render()
+
+ const cardSkeletons = screen.getAllByTestId('card-skeleton')
+ expect(cardSkeletons).toHaveLength(4)
+
+ cardSkeletons.forEach((skeleton) => {
+ expect(skeleton).toHaveAttribute('data-show-link', 'false')
+ expect(skeleton).toHaveAttribute('data-show-social', 'false')
+ })
+ })
+
+ it('properly integrates with mocked UserCardSkeleton component', () => {
+ render()
+
+ const userCards = screen.getAllByTestId('user-card-skeleton')
+ expect(userCards).toHaveLength(12)
+ })
+
+ it('properly integrates with mocked LoadingSpinner component', () => {
+ render()
+
+ const spinner = screen.getByTestId('loading-spinner')
+ expect(spinner).toHaveAttribute('data-image-url', 'test-spinner.gif')
+ })
+ })
+})