Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix-#6: integration test completed for signin component #413

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const app = express();
app.use(
cors({
// added origin
origin: FRONTEND_URL,
origin: [FRONTEND_URL, 'http://localhost:3000'],
credentials: true,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we are doing an integration test with the real API. The jsdom is our frontend and it's default URL is localhost:3000, so if we doesn't include it we will get an cors error during integration test..

})
);
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/__tests__/integration-test/signin/client-utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { render } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import Signin from '@/pages/signin-page';
import {
PASSWORDINPUT_PLACEHOLDER,
SIGNINBUTTON_TEXT,
SIGNIN_EMAIL_PLACEHOLDER,
} from '@/constants/images';

export const formSetup = () => {
const form = render(
<BrowserRouter>
<Signin />
</BrowserRouter>
);

const usernameInput = form.getByPlaceholderText(SIGNIN_EMAIL_PLACEHOLDER);

const passwordInput = form.getByPlaceholderText(PASSWORDINPUT_PLACEHOLDER);

const signinbuttonText = form.getByText(SIGNINBUTTON_TEXT);

if (
!(usernameInput instanceof HTMLInputElement) ||
!(passwordInput instanceof HTMLInputElement) ||
!(signinbuttonText instanceof HTMLButtonElement)
) {
throw new Error('Issue during test setup, some input elemnts are not rendered');
}

return { form, usernameInput, passwordInput, signinbuttonText };
};
70 changes: 70 additions & 0 deletions frontend/src/__tests__/integration-test/signin/signin.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { waitFor } from '@testing-library/react';
import { vi } from 'vitest';
import userEvent from '@testing-library/user-event';
import { formSetup } from './client-utils';
import { toast } from 'react-toastify';

interface sucessdata {
status: number;
message: string;
}

interface errordata {
status: number;
message: string;
}

interface Messages {
success: {
render: (data: { data: sucessdata }) => void;
};
error: {
render: (error: { error: errordata }) => void;
};
}

const mockedUseNavigate = vi.fn();

// Mocking the useNavigate hook from React Router DOM
vi.mock('react-router-dom', async () => {
const mod = await vi.importActual<typeof import('react-router-dom')>('react-router-dom');
return {
...mod,
useNavigate: () => mockedUseNavigate,
};
});

// Mock react-toastify's toast.promise
vi.mock('react-toastify', () => ({
toast: {
promise: vi.fn((promise: Promise<sucessdata>, messages: Messages) => {
return promise.then(
(data: sucessdata) => {
messages.success.render({ data });
return data;
},
(error: errordata) => {
messages.error.render({ error });
throw error;
}
);
}),
},
}));

describe('Integration Tests : Signin Component', async () => {
test('Signin : Sucess - Sucessfully sign in', async () => {
const mockedToastPromise = toast.promise;
const userActions = userEvent.setup();
const { usernameInput, passwordInput, signinbuttonText } = await formSetup();

await userActions.type(usernameInput, '[email protected]');
await userActions.type(passwordInput, 'Test@1234');
await userActions.click(signinbuttonText);

await waitFor(() => {
expect(mockedToastPromise).toHaveBeenCalled();
expect(mockedUseNavigate).toHaveBeenCalled();
});
});
});
2 changes: 1 addition & 1 deletion frontend/src/__tests__/mocks/handeler-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { http, HttpResponse } from 'msw';

export const handlers = [
http.post('http://localhost:5000/api/auth/email-password/signup', () => {
http.post('http://localhost:8080/api/auth/email-password/signup', () => {
return new HttpResponse(null, {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No hard coding please

status: 200,
});
Expand Down
14 changes: 12 additions & 2 deletions frontend/src/__tests__/unit-test/signup/client-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Signup from '@/pages/signup-page';
import {
CONFIRMPASSWORD_PLACEHOLDER,
EMAILINPUT_PLACEHOLDER,
NAME,
PASSWORDINPUT_PLACEHOLDER,
SIGNUPBUTTON_TEXT,
USERNAME_PLACEHOLDER,
Expand All @@ -15,14 +16,15 @@ export const formSetup = () => {
<Signup />
</BrowserRouter>
);

const nameInput = form.getByPlaceholderText(NAME);
const usernameInput = form.getByPlaceholderText(USERNAME_PLACEHOLDER);
const emailInput = form.getByPlaceholderText(EMAILINPUT_PLACEHOLDER);
const passwordInput = form.getByPlaceholderText(PASSWORDINPUT_PLACEHOLDER);
const confirmpasswordInput = form.getByPlaceholderText(CONFIRMPASSWORD_PLACEHOLDER);
const signupbuttonText = form.getByText(SIGNUPBUTTON_TEXT);

if (
!(nameInput instanceof HTMLInputElement) ||
!(usernameInput instanceof HTMLInputElement) ||
!(emailInput instanceof HTMLInputElement) ||
!(passwordInput instanceof HTMLInputElement) ||
Expand All @@ -32,5 +34,13 @@ export const formSetup = () => {
throw new Error('Issue during test setup, some input elemnts are not rendered');
}

return { form, usernameInput, emailInput, passwordInput, confirmpasswordInput, signupbuttonText };
return {
form,
usernameInput,
emailInput,
passwordInput,
confirmpasswordInput,
signupbuttonText,
nameInput,
};
};
79 changes: 67 additions & 12 deletions frontend/src/__tests__/unit-test/signup/signup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,30 @@ import {
EMAIL_EMPTY_ERRORMESSAGE,
INVALID_CONFIRMPWD_ERRORMESSAGE,
INVALID_PWD_ERRORMESSAGE,
INVALID_USERNAME_ERRORMESSAGE,
NAME_EMPTY_ERRORMESSAGE,
PASSWORD_EMPTY_ERRORMESSAGE,
USERNAME_EMPTY_ERRORMESSAGE,
} from '@/constants/images';
import { toast } from 'react-toastify';

interface sucessdata {
status: number;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this used multiple places, can we have a seperate types folder? and make it reusable

message: string;
}

interface errordata {
status: number;
message: string;
}

interface Messages {
success: {
render: (data: { data: sucessdata }) => void;
};
error: {
render: (error: { error: errordata }) => void;
};
}
const mockedUseNavigate = vi.fn();
// Mocking the useNavigate hook from React Router DOM
vi.mock('react-router-dom', async () => {
Expand All @@ -21,6 +41,24 @@ vi.mock('react-router-dom', async () => {
};
});

// Mock react-toastify's toast.promise
vi.mock('react-toastify', () => ({
toast: {
promise: vi.fn((promise: Promise<sucessdata>, messages: Messages) => {
return promise.then(
(data: sucessdata) => {
messages.success.render({ data });
return data;
},
(error: errordata) => {
messages.error.render({ error });
throw error;
}
);
}),
},
}));

describe('Unit Tests : Signup Component', async () => {
test('Signup : Failure - Invalid Email Address', async () => {
const userActions = userEvent.setup();
Expand All @@ -42,17 +80,19 @@ describe('Unit Tests : Signup Component', async () => {
passwordInput,
confirmpasswordInput,
signupbuttonText,
nameInput,
} = await formSetup();
await userActions.type(usernameInput, 'aryastark');
await userActions.type(nameInput, 'arya');
await userActions.type(emailInput, '[email protected]');
await userActions.type(passwordInput, '12345678');
await userActions.type(confirmpasswordInput, '1234');
await userActions.type(passwordInput, 'Test@1234');
await userActions.type(confirmpasswordInput, 'Tesr@1897');
await userActions.click(signupbuttonText);
await waitFor(() => {
expect(form.getByText(INVALID_CONFIRMPWD_ERRORMESSAGE)).toBeInTheDocument();
});
});

/*
test('Signup : Failure - Invalid Username', async () => {
const userActions = userEvent.setup();
const {
Expand All @@ -62,23 +102,27 @@ describe('Unit Tests : Signup Component', async () => {
passwordInput,
confirmpasswordInput,
signupbuttonText,
nameInput
} = await formSetup();
await userActions.type(usernameInput, 'ary');
await userActions.type(nameInput,'arya')
await userActions.type(emailInput, '[email protected]');
await userActions.type(passwordInput, '12345678');
await userActions.type(confirmpasswordInput, '12345678');
await userActions.type(passwordInput, 'Test@1234');
await userActions.type(confirmpasswordInput, 'Test@1234');
await userActions.click(signupbuttonText);
await waitFor(() => {
expect(form.getByText(INVALID_USERNAME_ERRORMESSAGE)).toBeInTheDocument();
});
});
*/

test('Signup : Failure - Form is submitted without any values', async () => {
const userActions = userEvent.setup();
const { form, signupbuttonText } = await formSetup();
await userActions.click(signupbuttonText);
await waitFor(() => {
expect(form.getByText(USERNAME_EMPTY_ERRORMESSAGE)).toBeInTheDocument();
expect(form.getByText(NAME_EMPTY_ERRORMESSAGE)).toBeInTheDocument();
expect(form.getByText(EMAIL_EMPTY_ERRORMESSAGE)).toBeInTheDocument();
expect(form.getByText(PASSWORD_EMPTY_ERRORMESSAGE)).toBeInTheDocument();
expect(form.getByText(CONFIRMPASSWORD_EMPTY_ERRORMESSAGE)).toBeInTheDocument();
Expand All @@ -94,32 +138,43 @@ describe('Unit Tests : Signup Component', async () => {
passwordInput,
confirmpasswordInput,
signupbuttonText,
nameInput,
} = await formSetup();
await userActions.type(usernameInput, 'abcd');
await userActions.type(nameInput, 'ab');
await userActions.type(emailInput, '[email protected]');
await userActions.type(passwordInput, '123');
await userActions.type(confirmpasswordInput, '1234');
await userActions.click(signupbuttonText);
await waitFor(() => {
expect(form.getByText(INVALID_USERNAME_ERRORMESSAGE)).toBeInTheDocument();
expect(form.getByText(NAME_EMPTY_ERRORMESSAGE)).toBeInTheDocument();
expect(form.getByText(INVALID_PWD_ERRORMESSAGE)).toBeInTheDocument();
expect(form.getByText(INVALID_CONFIRMPWD_ERRORMESSAGE)).toBeInTheDocument();
});
});

test('should call the signup api when all the input values are valid and should redirect to home page', async () => {
const userActions = userEvent.setup();
const mockedToastPromise = toast.promise;

const { usernameInput, emailInput, passwordInput, confirmpasswordInput, signupbuttonText } =
await formSetup();
const {
usernameInput,
emailInput,
passwordInput,
confirmpasswordInput,
signupbuttonText,
nameInput,
} = await formSetup();
await userActions.type(nameInput, 'arya');
await userActions.type(usernameInput, 'aryastark');
await userActions.type(emailInput, '[email protected]');
await userActions.type(passwordInput, '123456789');
await userActions.type(confirmpasswordInput, '123456789');
await userActions.type(passwordInput, 'Test@1234');
await userActions.type(confirmpasswordInput, 'Test@1234');
await userActions.click(signupbuttonText);

await waitFor(() => {
expect(mockedUseNavigate).toHaveBeenCalledTimes(1);
expect(mockedToastPromise).toHaveBeenCalled();
expect(mockedUseNavigate).toHaveBeenCalled();
});
});
});
13 changes: 9 additions & 4 deletions frontend/src/constants/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@ export const imageUrls: string[] = [
'https://i.ibb.co/qxFMj1H/sunset-horizon-clean-sky-nature.webp',
];
export const USERNAME_EMPTY_ERRORMESSAGE = 'Username is required';
export const EMAIL_EMPTY_ERRORMESSAGE = 'Email is required';
export const PASSWORD_EMPTY_ERRORMESSAGE = 'Password is required';
export const NAME_EMPTY_ERRORMESSAGE = 'Name must be at least 3 character';
export const EMAIL_EMPTY_ERRORMESSAGE = 'Enter valid email';
export const PASSWORD_EMPTY_ERRORMESSAGE = 'Password must be at least 8 character';
export const CONFIRMPASSWORD_EMPTY_ERRORMESSAGE = 'Confirm Password is required';

export const NAME = 'Name';
export const USERNAME_PLACEHOLDER = 'Username';
export const EMAILINPUT_PLACEHOLDER = 'Email';
export const PASSWORDINPUT_PLACEHOLDER = 'Password';
export const CONFIRMPASSWORD_PLACEHOLDER = 'Confirm Password';
export const SIGNUPBUTTON_TEXT = 'Sign Up';

export const SIGNINBUTTON_TEXT = 'Log In';
export const SIGNIN_EMAIL_PLACEHOLDER = 'Username or Email';

export const INVALID_USERNAME_ERRORMESSAGE = 'Username must be at least 5 characters long';
export const INVALID_EMAIL_ERRORMESSAGE = 'Invalid email address';
export const INVALID_PWD_ERRORMESSAGE = 'Password must be at least 8 characters long';
export const INVALID_CONFIRMPWD_ERRORMESSAGE = 'Passwords do not match';
export const INVALID_PWD_ERRORMESSAGE = 'Password must be at least 8 character';
export const INVALID_CONFIRMPWD_ERRORMESSAGE = 'Confirm Password do not match';
2 changes: 1 addition & 1 deletion frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default defineConfig({
reporters: ['verbose'],
exclude: [
...configDefaults.exclude,
'./src/__tests__/integration/home.test.tsx',
'./src/__tests__/integration-test/home.test.tsx',
'./src/__tests__/App.test.tsx',
],

Expand Down
Loading