Skip to content

Commit 137e1d3

Browse files
authored
Merge pull request #13 from tanzilahmed0/feature/frontend-documentation
docs: add detailed documentation for frontend
2 parents 811bc92 + 149c1ba commit 137e1d3

37 files changed

Lines changed: 5266 additions & 1211 deletions

frontend/frontend_gameplan.md

Whitespace-only changes.

frontend/next.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
eslint: {
4+
ignoreDuringBuilds: true,
5+
},
6+
typescript: {
7+
ignoreBuildErrors: true,
8+
},
9+
};
10+
11+
module.exports = nextConfig;

frontend/next.config.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

frontend/package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.1.0",
44
"private": true,
55
"scripts": {
6-
"dev": "next dev --turbopack",
6+
"dev": "next dev",
77
"build": "next build",
88
"start": "next start",
99
"lint": "next lint",
@@ -13,10 +13,11 @@
1313
"test:integration": "vitest run --config vitest.integration.config.ts"
1414
},
1515
"dependencies": {
16+
"axios": "^1.10.0",
1617
"daisyui": "^5.0.46",
17-
"next": "15.3.5",
18-
"react": "^19.0.0",
19-
"react-dom": "^19.0.0",
18+
"next": "^14.2.5",
19+
"react": "^18.3.1",
20+
"react-dom": "^18.3.1",
2021
"recharts": "^3.0.2",
2122
"zustand": "^5.0.6"
2223
},

frontend/public/favicon.ico

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 17 additions & 0 deletions
Loading
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/**
2+
* Login Page Integration Tests
3+
*
4+
* Integration tests for the login page and authentication flow.
5+
*/
6+
7+
import React from 'react';
8+
import { render, screen, fireEvent } from '@testing-library/react';
9+
import LoginPage from '@/app/login/page';
10+
import { useAuth } from '@/components/auth/AuthProvider';
11+
12+
// Mock the auth context
13+
jest.mock('@/components/auth/AuthProvider', () => ({
14+
useAuth: jest.fn(),
15+
}));
16+
17+
// Mock Next.js navigation
18+
jest.mock('next/navigation', () => ({
19+
useRouter: () => ({
20+
push: jest.fn(),
21+
}),
22+
useSearchParams: () => new URLSearchParams(),
23+
}));
24+
25+
const mockUseAuth = useAuth as jest.MockedFunction<typeof useAuth>;
26+
27+
describe('Login Page', () => {
28+
beforeEach(() => {
29+
jest.clearAllMocks();
30+
mockUseAuth.mockReturnValue({
31+
user: null,
32+
accessToken: null,
33+
isAuthenticated: false,
34+
isLoading: false,
35+
error: null,
36+
login: jest.fn(),
37+
logout: jest.fn(),
38+
refreshToken: jest.fn(),
39+
setError: jest.fn(),
40+
});
41+
});
42+
43+
describe('Page Rendering', () => {
44+
it('should render login page with SmartQuery branding', () => {
45+
render(<LoginPage />);
46+
47+
expect(screen.getByText('Welcome to SmartQuery')).toBeInTheDocument();
48+
expect(screen.getByText('Sign in to access your data analysis dashboard')).toBeInTheDocument();
49+
});
50+
51+
it('should render Google login buttons', () => {
52+
render(<LoginPage />);
53+
54+
expect(screen.getByText('Continue with Google')).toBeInTheDocument();
55+
expect(screen.getByText('Sign in with Google')).toBeInTheDocument();
56+
});
57+
58+
it('should render features preview section', () => {
59+
render(<LoginPage />);
60+
61+
expect(screen.getByText('What you can do with SmartQuery')).toBeInTheDocument();
62+
expect(screen.getByText(/Upload and analyze CSV files/)).toBeInTheDocument();
63+
expect(screen.getByText(/Generate interactive charts/)).toBeInTheDocument();
64+
expect(screen.getByText(/Get instant insights/)).toBeInTheDocument();
65+
});
66+
67+
it('should render terms and privacy links', () => {
68+
render(<LoginPage />);
69+
70+
expect(screen.getByText('Terms of Service')).toBeInTheDocument();
71+
expect(screen.getByText('Privacy Policy')).toBeInTheDocument();
72+
});
73+
});
74+
75+
describe('Authentication States', () => {
76+
it('should redirect to dashboard when already authenticated', () => {
77+
const mockPush = jest.fn();
78+
jest.doMock('next/navigation', () => ({
79+
useRouter: () => ({ push: mockPush }),
80+
useSearchParams: () => new URLSearchParams(),
81+
}));
82+
83+
mockUseAuth.mockReturnValue({
84+
user: { id: '1', name: 'Test User', email: 'test@example.com' },
85+
accessToken: 'token',
86+
isAuthenticated: true,
87+
isLoading: false,
88+
error: null,
89+
login: jest.fn(),
90+
logout: jest.fn(),
91+
refreshToken: jest.fn(),
92+
setError: jest.fn(),
93+
});
94+
95+
render(<LoginPage />);
96+
97+
expect(mockPush).toHaveBeenCalledWith('/dashboard');
98+
});
99+
100+
it('should show error message when authentication fails', () => {
101+
const mockSetError = jest.fn();
102+
mockUseAuth.mockReturnValue({
103+
user: null,
104+
accessToken: null,
105+
isAuthenticated: false,
106+
isLoading: false,
107+
error: 'Authentication failed',
108+
login: jest.fn(),
109+
logout: jest.fn(),
110+
refreshToken: jest.fn(),
111+
setError: mockSetError,
112+
});
113+
114+
render(<LoginPage />);
115+
116+
expect(screen.getByText('Authentication Error')).toBeInTheDocument();
117+
expect(screen.getByText('Authentication failed')).toBeInTheDocument();
118+
});
119+
120+
it('should handle OAuth errors from URL parameters', () => {
121+
const mockSetError = jest.fn();
122+
mockUseAuth.mockReturnValue({
123+
user: null,
124+
accessToken: null,
125+
isAuthenticated: false,
126+
isLoading: false,
127+
error: null,
128+
login: jest.fn(),
129+
logout: jest.fn(),
130+
refreshToken: jest.fn(),
131+
setError: mockSetError,
132+
});
133+
134+
// Mock useSearchParams to return an error
135+
jest.doMock('next/navigation', () => ({
136+
useRouter: () => ({ push: jest.fn() }),
137+
useSearchParams: () => new URLSearchParams('?error=access_denied'),
138+
}));
139+
140+
render(<LoginPage />);
141+
142+
expect(mockSetError).toHaveBeenCalledWith('Login failed: access_denied');
143+
});
144+
});
145+
146+
describe('Button Interactions', () => {
147+
it('should handle Google login button clicks', () => {
148+
const originalLocation = window.location;
149+
delete (window as any).location;
150+
window.location = { href: '' } as any;
151+
152+
render(<LoginPage />);
153+
154+
const googleButton = screen.getByText('Continue with Google');
155+
fireEvent.click(googleButton);
156+
157+
expect(window.location.href).toBe('http://localhost:8000/auth/google');
158+
159+
window.location = originalLocation;
160+
});
161+
162+
it('should handle alternative login button clicks', () => {
163+
const originalLocation = window.location;
164+
delete (window as any).location;
165+
window.location = { href: '' } as any;
166+
167+
render(<LoginPage />);
168+
169+
const altButton = screen.getByText('Sign in with Google');
170+
fireEvent.click(altButton);
171+
172+
expect(window.location.href).toBe('http://localhost:8000/auth/google');
173+
174+
window.location = originalLocation;
175+
});
176+
});
177+
178+
describe('Page Layout', () => {
179+
it('should have proper responsive layout', () => {
180+
render(<LoginPage />);
181+
182+
const container = screen.getByText('Welcome to SmartQuery').closest('div');
183+
expect(container).toHaveClass('min-h-screen', 'bg-gradient-to-br', 'from-blue-50', 'to-indigo-100');
184+
});
185+
186+
it('should have proper card styling', () => {
187+
render(<LoginPage />);
188+
189+
const loginCard = screen.getByText('Continue with Google').closest('div');
190+
expect(loginCard).toHaveClass('bg-white', 'py-8', 'px-6', 'shadow-xl', 'rounded-lg');
191+
});
192+
193+
it('should have proper button styling', () => {
194+
render(<LoginPage />);
195+
196+
const googleButton = screen.getByText('Continue with Google');
197+
expect(googleButton).toHaveClass('w-full', 'max-w-sm', 'mx-auto', 'bg-white', 'text-gray-700');
198+
});
199+
});
200+
201+
describe('Accessibility', () => {
202+
it('should have proper button roles', () => {
203+
render(<LoginPage />);
204+
205+
const buttons = screen.getAllByRole('button');
206+
expect(buttons).toHaveLength(2); // Two login buttons
207+
});
208+
209+
it('should have proper heading structure', () => {
210+
render(<LoginPage />);
211+
212+
const mainHeading = screen.getByRole('heading', { level: 1 });
213+
expect(mainHeading).toHaveTextContent('Welcome to SmartQuery');
214+
215+
const subHeading = screen.getByRole('heading', { level: 3 });
216+
expect(subHeading).toHaveTextContent('What you can do with SmartQuery');
217+
});
218+
219+
it('should have proper link elements', () => {
220+
render(<LoginPage />);
221+
222+
const links = screen.getAllByRole('link');
223+
expect(links).toHaveLength(2); // Terms and Privacy links
224+
});
225+
});
226+
});

0 commit comments

Comments
 (0)