diff --git a/src/components/InputFields/BaseInput.vue b/src/components/InputFields/BaseInput.vue index 8e20e43d95e8ac8cf46e6253d1ef39c4ea52d351..19a7c2e0188220bde30f3c5bdd60fc9e5ba3a931 100644 --- a/src/components/InputFields/BaseInput.vue +++ b/src/components/InputFields/BaseInput.vue @@ -63,7 +63,7 @@ const onInputEvent = (event: any) => { :required="required" /> <div class="valid-feedback">{{ validMessage }}</div> - <div class="invalid-feedback">{{ invalidMessage }}</div> + <div class="invalid-feedback" id="invalid">{{ invalidMessage }}</div> </div> </template> diff --git a/src/components/Login/LoginLink.vue b/src/components/Login/LoginLink.vue index 0f23a2edbeed887995e5db725c0aac37092effa6..8f1db76b8746d93bbdb0ec2c16526d93fc55149b 100644 --- a/src/components/Login/LoginLink.vue +++ b/src/components/Login/LoginLink.vue @@ -3,7 +3,7 @@ </script> <template> - <p>Already have an account? <RouterLink to="/login">Login</RouterLink></p> + <p>Already have an account? <RouterLink to="/login" id="login">Login</RouterLink></p> </template> <style scoped> diff --git a/src/components/Login/__tests__/LoginForm.spec.ts b/src/components/Login/__tests__/LoginForm.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..399d96c794edc716ef1749a30801147067a99b2c --- /dev/null +++ b/src/components/Login/__tests__/LoginForm.spec.ts @@ -0,0 +1,120 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { mount } from '@vue/test-utils'; +import { createRouter, createMemoryHistory } from 'vue-router'; +import { createPinia, setActivePinia } from 'pinia'; +import { useUserInfoStore } from '@/stores/UserStore'; +import MyComponent from '@/components/Login/LoginForm.vue'; // Adjust path as needed +import router from '@/router/index'; // Adjust path as needed +import { access } from 'fs'; +import { render, fireEvent, cleanup, screen } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; + +describe('Menu and Router Tests', () => { + let store: any, mockRouter: any; + + beforeEach(() => { + // Create a fresh Pinia and Router instance before each test + setActivePinia(createPinia()); + store = useUserInfoStore(); + mockRouter = createRouter({ + history: createMemoryHistory(), + routes: router.getRoutes(), + }); + router.beforeEach((to, from, next) => { + const isAuthenticated = store.accessToken; + if (to.matched.some(record => record.meta.requiresAuth) && !isAuthenticated) { + next({ name: 'login' }); + } else { + next(); + } + }); + + }); + + describe('Component Rendering', () => { + it('renders form correctly', () => { + store.setUserInfo({ firstname: 'Jane', lastname: 'Doe', accessToken: 'thisIsATestToken' }); + + const wrapper = mount(MyComponent, { + global: { + plugins: [mockRouter], + }, + }); + + expect(wrapper.text()).toContain('email'); + expect(wrapper.text()).toContain('password'); + }); + }); + + describe('Navigation Guards', () => { + it('redirects an unauthenticated user to login when accessing a protected route', async () => { + store.$patch({ accessToken: '' }); + + router.push('/'); + await router.isReady(); + + expect(router.currentRoute.value.name).toBe('login'); + }); + + it('allows an unauthenticated user to visit signup', async () => { + store.$patch({ accessToken: 'valid-token' }); + + mockRouter.push('/sign-up'); + + await mockRouter.isReady(); + + expect(mockRouter.currentRoute.value.name).toBe('sign up'); + }); + }); + + + describe('Input fields', () => { + it('updates user credetials correctly', async () => { + const { getByPlaceholderText } = render(MyComponent); + + const emailInput = getByPlaceholderText('Enter your email'); + const passwordInput = getByPlaceholderText('Enter password'); + await fireEvent.update(emailInput, 'user@example.com'); + await fireEvent.update(passwordInput, 'Password1'); + + expect(emailInput.value).toBe('user@example.com'); + expect(passwordInput.value).toBe('Password1'); + }); + + it('Password error msg', async () => { + const { container } = render(MyComponent, { + global: { + plugins: [mockRouter], + }, + }); + + const errorMsg = container.querySelector('#invalid'); // Use the actual ID here + expect(errorMsg?.textContent === "Password must be between 4 and 16 characters and contain one capital letter, small letter and a number") + }); + + it('logout should have empty store at application start', () => { + expect(store.firstname).toBe(''); + expect(store.lastname).toBe(''); + expect(store.accessToken).toBe(''); + }); + }); + + describe('Menu Actions', () => { + it('signup redirects to signup', async () => { + const { container } = render(MyComponent, { + global: { + plugins: [mockRouter], + }, + }); + + // Assuming there's an element with id="home-link" that you want to click + const signupLink = container.querySelector('#signup'); // Use the actual ID here + if (signupLink) { + await userEvent.click(signupLink); + await mockRouter.isReady(); + } + + expect(mockRouter.currentRoute.value.name).toBe('sign up'); // Assuming 'Home' is the route name for '/' + }); + }); +}); diff --git a/src/components/Login/__tests__/LoginLink.spec.ts b/src/components/Login/__tests__/LoginLink.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..acb566e492bf595bd940ce5e09f0f335f6766070 --- /dev/null +++ b/src/components/Login/__tests__/LoginLink.spec.ts @@ -0,0 +1,73 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { render } from '@testing-library/vue'; +import { createPinia, setActivePinia } from 'pinia'; +import { createRouter, createMemoryHistory } from 'vue-router'; +import LoginPrompt from '@/components/Login/LoginLink.vue'; +import { useUserInfoStore } from '@/stores/UserStore'; +import router from '@/router/index'; +import { render, screen } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; + +describe('LoginPrompt', () => { + let store, mockRouter; + + beforeEach(() => { + // Create a fresh Pinia and Router instance before each test + setActivePinia(createPinia()); + store = useUserInfoStore(); + mockRouter = createRouter({ + history: createMemoryHistory(), + routes: router.getRoutes(), + }); + router.beforeEach((to, from, next) => { + const isAuthenticated = store.accessToken; + if (to.matched.some(record => record.meta.requiresAuth) && !isAuthenticated) { + next({ name: 'login' }); + } else { + next(); + } + }); + }); + + + it('renders login link correctly', async () => { + const router = createRouter({ + history: createMemoryHistory(), + routes: [{ path: '/login', component: { template: 'Login Page' } }], + }); + + const { getByText } = render(LoginPrompt, { + global: { + plugins: [router], + }, + }); + + await router.isReady(); // Ensure the router is ready before asserting + + const loginLink = getByText('Login'); + expect(loginLink).toBeDefined(); // Check if the 'Login' link is rendered + }); + + it('navigates to the login page when the login link is clicked', async () => { + const mockRouter = createRouter({ + history: createMemoryHistory(), + routes: [{ path: '/login', name: 'login', component: { template: 'Login Page' } }], + }); + + const { container } = render(LoginPrompt, { + global: { + plugins: [mockRouter], + }, + }); + + await mockRouter.isReady(); // Ensure the router is ready before asserting + + const loginLink = container.querySelector('#login'); // Use the actual ID here + if (loginLink) { + await userEvent.click(loginLink); + await mockRouter.isReady(); + } + + expect(mockRouter.currentRoute.value.path).toBe('/login'); // Check if the router navigated to the login page + }, 10000); +}); diff --git a/src/components/SignUp/SignUpLink.vue b/src/components/SignUp/SignUpLink.vue index f7c354b6b9d64f7cc776d64f5336d739a95fa116..e16871208dcc1d42fd4dc7783587772215d8e1c9 100644 --- a/src/components/SignUp/SignUpLink.vue +++ b/src/components/SignUp/SignUpLink.vue @@ -3,7 +3,7 @@ </script> <template> - <p>Don't have an account? <RouterLink to="/sign-up">Sign up</RouterLink></p> + <p>Don't have an account? <RouterLink to="/sign-up" id="signup">Sign up</RouterLink></p> </template> <style scoped> diff --git a/src/components/SignUp/__tests__/SignUpForm.spec.ts b/src/components/SignUp/__tests__/SignUpForm.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0146a6e93ee78315a2bf670c1e3871e473bb6ffb --- /dev/null +++ b/src/components/SignUp/__tests__/SignUpForm.spec.ts @@ -0,0 +1,125 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { mount } from '@vue/test-utils'; +import { createRouter, createMemoryHistory } from 'vue-router'; +import { createPinia, setActivePinia } from 'pinia'; +import { useUserInfoStore } from '@/stores/UserStore'; +import MyComponent from '@/components/SignUp/SignUpForm.vue'; // Adjust path as needed +import router from '@/router/index'; // Adjust path as needed +import { access } from 'fs'; +import { render, fireEvent, cleanup, screen } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; + +describe('Menu and Router Tests', () => { + let store: any, mockRouter: any; + + beforeEach(() => { + // Create a fresh Pinia and Router instance before each test + setActivePinia(createPinia()); + store = useUserInfoStore(); + mockRouter = createRouter({ + history: createMemoryHistory(), + routes: router.getRoutes(), + }); + router.beforeEach((to, from, next) => { + const isAuthenticated = store.accessToken; + if (to.matched.some(record => record.meta.requiresAuth) && !isAuthenticated) { + next({ name: 'login' }); + } else { + next(); + } + }); + + }); + + describe('Component Rendering', () => { + it('renders form correctly', () => { + const wrapper = mount(MyComponent, { + global: { + plugins: [mockRouter], + }, + }); + + expect(wrapper.text()).toContain('First name'); + expect(wrapper.text()).toContain('Surname'); + expect(wrapper.text()).toContain('Email'); + }); + }); + + describe('Navigation Guards', () => { + it('redirects an unauthenticated user to login when accessing a protected route', async () => { + store.$patch({ accessToken: '' }); + + router.push('/'); + await router.isReady(); + + expect(router.currentRoute.value.name).toBe('login'); + }); + + it('allows an unauthenticated user to visit login', async () => { + store.$patch({ accessToken: 'valid-token' }); + + mockRouter.push('/login'); + + await mockRouter.isReady(); + + expect(mockRouter.currentRoute.value.name).toBe('login'); + }); + }); + + + describe('Input fields', () => { + it('updates user credetials correctly', async () => { + const { getByPlaceholderText } = render(MyComponent); + + const firstInput = getByPlaceholderText('Enter your first name'); + const lastInput = getByPlaceholderText('Enter your surname'); + const emailInput = getByPlaceholderText('Enter your email'); + const passwordInput = getByPlaceholderText('Enter password'); + await fireEvent.update(firstInput, 'Alice'); + await fireEvent.update(lastInput, 'Alicon'); + await fireEvent.update(emailInput, 'user@example.com'); + await fireEvent.update(passwordInput, 'Password1'); + + expect(firstInput.value).toBe('Alice'); + expect(lastInput.value).toBe('Alicon'); + expect(emailInput.value).toBe('user@example.com'); + expect(passwordInput.value).toBe('Password1'); + }); + + it('Password error msg', async () => { + const { container } = render(MyComponent, { + global: { + plugins: [mockRouter], + }, + }); + + const errorMsg = container.querySelector('#invalid'); // Use the actual ID here + expect(errorMsg?.textContent === "Password must be between 4 and 16 characters and contain one capital letter, small letter and a number") + }); + + it('logout should have empty store at application start', () => { + expect(store.firstname).toBe(''); + expect(store.lastname).toBe(''); + expect(store.accessToken).toBe(''); + }); + }); + + describe('Menu Actions', () => { + it('signup redirects to signup', async () => { + const { container } = render(MyComponent, { + global: { + plugins: [mockRouter], + }, + }); + + // Assuming there's an element with id="home-link" that you want to click + const signupLink = container.querySelector('#login'); // Use the actual ID here + if (signupLink) { + await userEvent.click(signupLink); + await mockRouter.isReady(); + } + + expect(mockRouter.currentRoute.value.name).toBe('login'); // Assuming 'Home' is the route name for '/' + }); + }); +}); diff --git a/src/components/SignUp/__tests__/SignUpLink.spec.ts b/src/components/SignUp/__tests__/SignUpLink.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4701d55d2f7d7c4690b0b24cc5f687b8c0fc2285 --- /dev/null +++ b/src/components/SignUp/__tests__/SignUpLink.spec.ts @@ -0,0 +1,71 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { render } from '@testing-library/vue'; +import { createPinia, setActivePinia } from 'pinia'; +import { createRouter, createMemoryHistory } from 'vue-router'; +import LoginPrompt from '@/components/SignUp/SignUpLink.vue'; +import { useUserInfoStore } from '@/stores/UserStore'; +import router from '@/router/index'; +import { render, screen } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; + +describe('LoginPrompt', () => { + let store, mockRouter; + + beforeEach(() => { + // Create a fresh Pinia and Router instance before each test + setActivePinia(createPinia()); + store = useUserInfoStore(); + mockRouter = createRouter({ + history: createMemoryHistory(), + routes: router.getRoutes(), + }); + router.beforeEach((to, from, next) => { + const isAuthenticated = store.accessToken; + if (to.matched.some(record => record.meta.requiresAuth) && !isAuthenticated) { + next({ name: 'login' }); + } else { + next(); + } + }); + }); + + + it('renders login link correctly', async () => { + const router = createRouter({ + history: createMemoryHistory(), + routes: [{ path: '/signup', component: { template: 'Signup Page' } }], + }); + + const { getByText } = render(LoginPrompt, { + global: { + plugins: [router], + }, + }); + + const loginLink = getByText('Sign up'); + expect(loginLink).toBeDefined(); // Check if the 'Login' link is rendered + }); + + it('navigates to the login page when the login link is clicked', async () => { + const mockRouter = createRouter({ + history: createMemoryHistory(), + routes: [{ path: '/login', name: 'login', component: { template: 'Login Page' } }], + }); + + const { container } = render(LoginPrompt, { + global: { + plugins: [mockRouter], + }, + }); + + await mockRouter.isReady(); // Ensure the router is ready before asserting + + const signupLink = container.querySelector('#signup'); // Use the actual ID here + if (signupLink) { + await userEvent.click(signupLink); + await mockRouter.isReady(); + } + + expect(mockRouter.currentRoute.value.path).toBe('/sign-up'); // Check if the router navigated to the login page + }, 10000); +});