fix(frontend): resolve lint blockers for gitea frontend-ci

This commit is contained in:
WorkClub Automation
2026-03-06 22:26:55 +01:00
parent ad6a23621d
commit 5cf43976f6
16 changed files with 112 additions and 96 deletions

View File

@@ -22,28 +22,28 @@ describe('AuthGuard', () => {
beforeEach(() => {
vi.clearAllMocks();
(useRouter as any).mockReturnValue({ push: mockPush } as any);
(useRouter as ReturnType<typeof vi.fn>).mockReturnValue({ push: mockPush });
});
it('renders loading state when session is loading', () => {
(useSession as any).mockReturnValue({ data: null, status: 'loading' } as any);
(useTenant as any).mockReturnValue({ activeClubId: null, clubs: [], setActiveClub: vi.fn(), userRole: null });
(useSession as ReturnType<typeof vi.fn>).mockReturnValue({ data: null, status: 'loading' });
(useTenant as ReturnType<typeof vi.fn>).mockReturnValue({ activeClubId: null, clubs: [], setActiveClub: vi.fn(), userRole: null });
render(<AuthGuard><div>Protected</div></AuthGuard>);
expect(screen.getByText('Loading...')).toBeInTheDocument();
});
it('redirects to /login when unauthenticated', () => {
(useSession as any).mockReturnValue({ data: null, status: 'unauthenticated' } as any);
(useTenant as any).mockReturnValue({ activeClubId: null, clubs: [], setActiveClub: vi.fn(), userRole: null });
(useSession as ReturnType<typeof vi.fn>).mockReturnValue({ data: null, status: 'unauthenticated' });
(useTenant as ReturnType<typeof vi.fn>).mockReturnValue({ activeClubId: null, clubs: [], setActiveClub: vi.fn(), userRole: null });
render(<AuthGuard><div>Protected</div></AuthGuard>);
expect(mockPush).toHaveBeenCalledWith('/login');
});
it('shows Contact admin when 0 clubs', () => {
(useSession as any).mockReturnValue({ data: { user: {} }, status: 'authenticated' } as any);
(useTenant as any).mockReturnValue({ activeClubId: null, clubs: [], setActiveClub: vi.fn(), userRole: null });
(useSession as ReturnType<typeof vi.fn>).mockReturnValue({ data: { user: {} }, status: 'authenticated' });
(useTenant as ReturnType<typeof vi.fn>).mockReturnValue({ activeClubId: null, clubs: [], setActiveClub: vi.fn(), userRole: null });
render(<AuthGuard><div>Protected</div></AuthGuard>);
expect(screen.getByText('Contact admin to get access to a club')).toBeInTheDocument();
@@ -51,39 +51,39 @@ describe('AuthGuard', () => {
it('auto-selects when 1 club and no active club', () => {
const mockSetActiveClub = vi.fn();
(useSession as any).mockReturnValue({ data: { user: {} }, status: 'authenticated' } as any);
(useTenant as any).mockReturnValue({
(useSession as ReturnType<typeof vi.fn>).mockReturnValue({ data: { user: {} }, status: 'authenticated' });
(useTenant as ReturnType<typeof vi.fn>).mockReturnValue({
activeClubId: null,
clubs: [{ id: 'club-1', name: 'Club 1' }],
setActiveClub: mockSetActiveClub,
userRole: null
} as any);
});
render(<AuthGuard><div>Protected</div></AuthGuard>);
expect(mockSetActiveClub).toHaveBeenCalledWith('club-1');
});
it('redirects to /select-club when multiple clubs and no active club', () => {
(useSession as any).mockReturnValue({ data: { user: {} }, status: 'authenticated' } as any);
(useTenant as any).mockReturnValue({
(useSession as ReturnType<typeof vi.fn>).mockReturnValue({ data: { user: {} }, status: 'authenticated' });
(useTenant as ReturnType<typeof vi.fn>).mockReturnValue({
activeClubId: null,
clubs: [{ id: 'club-1', name: 'Club 1' }, { id: 'club-2', name: 'Club 2' }],
setActiveClub: vi.fn(),
userRole: null
} as any);
});
render(<AuthGuard><div>Protected</div></AuthGuard>);
expect(mockPush).toHaveBeenCalledWith('/select-club');
});
it('renders children when authenticated and active club is set', () => {
(useSession as any).mockReturnValue({ data: { user: {} }, status: 'authenticated' } as any);
(useTenant as any).mockReturnValue({
(useSession as ReturnType<typeof vi.fn>).mockReturnValue({ data: { user: {} }, status: 'authenticated' });
(useTenant as ReturnType<typeof vi.fn>).mockReturnValue({
activeClubId: 'club-1',
clubs: [{ id: 'club-1', name: 'Club 1' }],
setActiveClub: vi.fn(),
userRole: 'admin'
} as any);
});
render(<AuthGuard><div>Protected Content</div></AuthGuard>);
expect(screen.getByText('Protected Content')).toBeInTheDocument();

View File

@@ -9,7 +9,7 @@ vi.mock('../../contexts/tenant-context', () => ({
vi.mock('../ui/dropdown-menu', () => ({
DropdownMenu: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
DropdownMenuTrigger: ({ children, asChild }: { children: React.ReactNode, asChild?: boolean }) => <div data-testid="trigger">{children}</div>,
DropdownMenuTrigger: ({ children }: { children: React.ReactNode }) => <div data-testid="trigger">{children}</div>,
DropdownMenuContent: ({ children }: { children: React.ReactNode }) => <div data-testid="content">{children}</div>,
DropdownMenuItem: ({ children, onClick }: { children: React.ReactNode, onClick: () => void }) => <div onClick={onClick} data-testid="menu-item">{children}</div>,
DropdownMenuLabel: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
@@ -22,19 +22,19 @@ describe('ClubSwitcher', () => {
});
it('renders loading state when clubs is empty', () => {
(useTenant as any).mockReturnValue({
(useTenant as ReturnType<typeof vi.fn>).mockReturnValue({
activeClubId: null,
clubs: [],
setActiveClub: vi.fn(),
userRole: null
} as any);
});
render(<ClubSwitcher />);
expect(screen.getByRole('button')).toHaveTextContent('Select Club');
});
it('renders current club name and sport type badge', () => {
(useTenant as any).mockReturnValue({
(useTenant as ReturnType<typeof vi.fn>).mockReturnValue({
activeClubId: 'club-1',
clubs: [
{ id: 'club-1', name: 'Tennis Club', sportType: 'Tennis' },
@@ -42,7 +42,7 @@ describe('ClubSwitcher', () => {
],
setActiveClub: vi.fn(),
userRole: 'admin'
} as any);
});
render(<ClubSwitcher />);
expect(screen.getAllByText('Tennis Club')[0]).toBeInTheDocument();
@@ -50,7 +50,7 @@ describe('ClubSwitcher', () => {
it('calls setActiveClub when club is selected', () => {
const mockSetActiveClub = vi.fn();
(useTenant as any).mockReturnValue({
(useTenant as ReturnType<typeof vi.fn>).mockReturnValue({
activeClubId: 'club-1',
clubs: [
{ id: 'club-1', name: 'Tennis Club', sportType: 'Tennis' },
@@ -58,7 +58,7 @@ describe('ClubSwitcher', () => {
],
setActiveClub: mockSetActiveClub,
userRole: 'admin'
} as any);
});
render(<ClubSwitcher />);

View File

@@ -38,12 +38,12 @@ describe('ShiftDetailPage', () => {
beforeEach(() => {
vi.clearAllMocks();
(useSignUpShift as any).mockReturnValue({ mutateAsync: mockSignUp, isPending: false });
(useCancelSignUp as any).mockReturnValue({ mutateAsync: mockCancel, isPending: false });
(useSignUpShift as ReturnType<typeof vi.fn>).mockReturnValue({ mutateAsync: mockSignUp, isPending: false });
(useCancelSignUp as ReturnType<typeof vi.fn>).mockReturnValue({ mutateAsync: mockCancel, isPending: false });
});
it('shows "Sign Up" button if capacity available', async () => {
(useShift as any).mockReturnValue({
(useShift as ReturnType<typeof vi.fn>).mockReturnValue({
data: {
id: '1',
title: 'Detail Shift',
@@ -69,7 +69,7 @@ describe('ShiftDetailPage', () => {
});
it('shows "Cancel Sign-up" button if user is signed up', async () => {
(useShift as any).mockReturnValue({
(useShift as ReturnType<typeof vi.fn>).mockReturnValue({
data: {
id: '1',
title: 'Detail Shift',
@@ -95,7 +95,7 @@ describe('ShiftDetailPage', () => {
});
it('calls sign up mutation on click', async () => {
(useShift as any).mockReturnValue({
(useShift as ReturnType<typeof vi.fn>).mockReturnValue({
data: {
id: '1',
title: 'Detail Shift',

View File

@@ -20,18 +20,18 @@ describe('TaskDetailPage', () => {
const mockMutate = vi.fn();
beforeEach(() => {
(useUpdateTask as any).mockReturnValue({
(useUpdateTask as ReturnType<typeof vi.fn>).mockReturnValue({
mutate: mockMutate,
isPending: false,
} as any);
});
});
it('shows valid transitions for Open status', async () => {
(useTask as any).mockReturnValue({
(useTask as ReturnType<typeof vi.fn>).mockReturnValue({
data: { id: '1', title: 'Task 1', status: 'Open', description: 'Desc', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
isLoading: false,
error: null,
} as any);
});
const params = Promise.resolve({ id: '1' });
await act(async () => {
@@ -44,11 +44,11 @@ describe('TaskDetailPage', () => {
});
it('shows valid transitions for InProgress status', async () => {
(useTask as any).mockReturnValue({
(useTask as ReturnType<typeof vi.fn>).mockReturnValue({
data: { id: '1', title: 'Task 1', status: 'InProgress', description: 'Desc', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
isLoading: false,
error: null,
} as any);
});
const params = Promise.resolve({ id: '1' });
await act(async () => {
@@ -60,11 +60,11 @@ describe('TaskDetailPage', () => {
});
it('shows valid transitions for Review status (including back transition)', async () => {
(useTask as any).mockReturnValue({
(useTask as ReturnType<typeof vi.fn>).mockReturnValue({
data: { id: '1', title: 'Task 1', status: 'Review', description: 'Desc', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
isLoading: false,
error: null,
} as any);
});
const params = Promise.resolve({ id: '1' });
await act(async () => {

View File

@@ -24,7 +24,7 @@ vi.mock('@/hooks/useTasks', () => ({
describe('TaskListPage', () => {
beforeEach(() => {
(useTasks as any).mockReturnValue({
(useTasks as ReturnType<typeof vi.fn>).mockReturnValue({
data: {
items: [
{ id: '1', title: 'Test Task 1', status: 'Open', assigneeId: null, createdAt: '2024-01-01' },
@@ -37,7 +37,7 @@ describe('TaskListPage', () => {
},
isLoading: false,
error: null,
} as any);
});
});
it('renders task list with 3 data rows', () => {

View File

@@ -6,7 +6,7 @@ import { ReactNode, useEffect } from 'react';
import { useTenant } from '../contexts/tenant-context';
export function AuthGuard({ children }: { children: ReactNode }) {
const { data: session, status } = useSession();
const { status } = useSession();
const { activeClubId, clubs, setActiveClub, clubsLoading } = useTenant();
const router = useRouter();