test(web): increase frontend test coverage to ~70% page coverage
- Fix vitest config to include [locale] directory tests (was excluded)
- Fix register.spec.tsx: use getByRole('heading') to avoid duplicate text match
- Fix search.spec.tsx: add QueryClientProvider wrapper and mock saved searches hook
- Add 12 new page test files covering dashboard, admin, public, and OAuth pages:
- dashboard (main, profile, payments, subscription, KYC)
- admin (dashboard, users)
- public (landing, pricing)
- analytics
- OAuth callbacks (Google, Zalo)
- 29 test files, 174 tests, 16/23 pages covered (69.6%)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
/* eslint-disable import-x/order */
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { useAuthStore } from '@/lib/auth-store';
|
||||
|
||||
const mockReplace = vi.fn();
|
||||
let mockSearchParams = new URLSearchParams();
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({ replace: mockReplace, push: vi.fn() }),
|
||||
useSearchParams: () => mockSearchParams,
|
||||
}));
|
||||
|
||||
vi.mock('lucide-react', () => ({
|
||||
Loader2: ({ className }: { className?: string }) => <div data-testid="loader" className={className} />,
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/auth-store', () => {
|
||||
const store = {
|
||||
handleOAuthCallback: vi.fn(),
|
||||
};
|
||||
return {
|
||||
useAuthStore: vi.fn((selector) => {
|
||||
if (typeof selector === 'function') return selector(store);
|
||||
return store;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
import GoogleCallbackPage from '../google/page';
|
||||
|
||||
const mockedUseAuthStore = vi.mocked(useAuthStore);
|
||||
|
||||
describe('GoogleCallbackPage', () => {
|
||||
let mockStore: { handleOAuthCallback: ReturnType<typeof vi.fn> };
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockStore = {
|
||||
handleOAuthCallback: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
mockedUseAuthStore.mockImplementation((selector) => {
|
||||
if (typeof selector === 'function') return (selector as (s: typeof mockStore) => unknown)(mockStore);
|
||||
return mockStore as ReturnType<typeof useAuthStore>;
|
||||
});
|
||||
});
|
||||
|
||||
it('renders loading spinner and text', () => {
|
||||
mockSearchParams = new URLSearchParams();
|
||||
render(<GoogleCallbackPage />);
|
||||
expect(screen.getByTestId('loader')).toBeInTheDocument();
|
||||
expect(screen.getByText(/đang xử lý đăng nhập google/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('redirects to login on error param', async () => {
|
||||
mockSearchParams = new URLSearchParams('error=access_denied');
|
||||
render(<GoogleCallbackPage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockReplace).toHaveBeenCalledWith('/login?error=access_denied');
|
||||
});
|
||||
});
|
||||
|
||||
it('redirects to login when tokens are missing', async () => {
|
||||
mockSearchParams = new URLSearchParams();
|
||||
render(<GoogleCallbackPage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockReplace).toHaveBeenCalledWith('/login?error=oauth_failed');
|
||||
});
|
||||
});
|
||||
|
||||
it('calls handleOAuthCallback with tokens', async () => {
|
||||
mockSearchParams = new URLSearchParams('accessToken=abc&refreshToken=def&expiresIn=3600');
|
||||
render(<GoogleCallbackPage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockStore.handleOAuthCallback).toHaveBeenCalledWith('abc', 'def', 3600);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
/* eslint-disable import-x/order */
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { useAuthStore } from '@/lib/auth-store';
|
||||
|
||||
const mockReplace = vi.fn();
|
||||
let mockSearchParams = new URLSearchParams();
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({ replace: mockReplace, push: vi.fn() }),
|
||||
useSearchParams: () => mockSearchParams,
|
||||
}));
|
||||
|
||||
vi.mock('lucide-react', () => ({
|
||||
Loader2: ({ className }: { className?: string }) => <div data-testid="loader" className={className} />,
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/auth-store', () => {
|
||||
const store = {
|
||||
handleOAuthCallback: vi.fn(),
|
||||
};
|
||||
return {
|
||||
useAuthStore: vi.fn((selector) => {
|
||||
if (typeof selector === 'function') return selector(store);
|
||||
return store;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
import ZaloCallbackPage from '../zalo/page';
|
||||
|
||||
const mockedUseAuthStore = vi.mocked(useAuthStore);
|
||||
|
||||
describe('ZaloCallbackPage', () => {
|
||||
let mockStore: { handleOAuthCallback: ReturnType<typeof vi.fn> };
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockStore = {
|
||||
handleOAuthCallback: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
mockedUseAuthStore.mockImplementation((selector) => {
|
||||
if (typeof selector === 'function') return (selector as (s: typeof mockStore) => unknown)(mockStore);
|
||||
return mockStore as ReturnType<typeof useAuthStore>;
|
||||
});
|
||||
});
|
||||
|
||||
it('renders loading spinner and Zalo text', () => {
|
||||
mockSearchParams = new URLSearchParams();
|
||||
render(<ZaloCallbackPage />);
|
||||
expect(screen.getByTestId('loader')).toBeInTheDocument();
|
||||
expect(screen.getByText(/đang xử lý đăng nhập zalo/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('redirects to login on error param', async () => {
|
||||
mockSearchParams = new URLSearchParams('error=access_denied');
|
||||
render(<ZaloCallbackPage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockReplace).toHaveBeenCalledWith('/login?error=access_denied');
|
||||
});
|
||||
});
|
||||
|
||||
it('redirects to login when tokens are missing', async () => {
|
||||
mockSearchParams = new URLSearchParams();
|
||||
render(<ZaloCallbackPage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockReplace).toHaveBeenCalledWith('/login?error=oauth_failed');
|
||||
});
|
||||
});
|
||||
|
||||
it('calls handleOAuthCallback with tokens', async () => {
|
||||
mockSearchParams = new URLSearchParams('accessToken=zalo123&refreshToken=zaloref&expiresIn=1800');
|
||||
render(<ZaloCallbackPage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockStore.handleOAuthCallback).toHaveBeenCalledWith('zalo123', 'zaloref', 1800);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user