From 515389f7f2e6fd4a9ca5fc336d17e2f2c7afb151 Mon Sep 17 00:00:00 2001 From: LightCreator1007 Date: Thu, 15 Jan 2026 17:22:21 +0530 Subject: [PATCH 1/5] test(accounts): refactored tests of ResetPassword component to use VTL Migrates test suite to Vue Testing Library to focus on user behavior rather than implementation details. - Replaced brittle wrapper manipulation with screen queries - Added validation and successful submission scenarios - Verified correct payload dispatch to Vuex with query params Closes #5637 --- .../pages/__tests__/resetPassword.spec.js | 121 ++++++++++++------ 1 file changed, 82 insertions(+), 39 deletions(-) diff --git a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js index bb422c4dde..aba6913f58 100644 --- a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js +++ b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js @@ -1,53 +1,96 @@ -import { mount } from '@vue/test-utils'; -import router from '../../router'; +import { render, screen, fireEvent, waitFor } from '@testing-library/vue'; +import VueRouter from 'vue-router'; import ResetPassword from '../resetPassword/ResetPassword'; +import { createLocalVue } from '@vue/test-utils'; -function makeWrapper() { - return mount(ResetPassword, { router }); -} -describe('resetPassword', () => { - let wrapper, setPassword; +const localVue = createLocalVue(); +localVue.use(VueRouter); - beforeEach(() => { - wrapper = makeWrapper(); - setPassword = jest.spyOn(wrapper.vm, 'setPassword'); - setPassword.mockImplementation(() => Promise.resolve()); - }); +const setPasswordMock = jest.fn(()=>Promise.resolve()); - it('should not call setPassword on submit if password 1 is not set', async () => { - await wrapper.setData({ new_password2: 'pass' }); - await wrapper.findComponent({ ref: 'form' }).trigger('submit'); - expect(setPassword).not.toHaveBeenCalled(); +const renderComponent = (queryParams={})=> { + const router = new VueRouter({ + routes: [ + { path: '/', name: 'Main' }, + { path: '/reset-password', name: 'ResetPassword' }, + { path: '/reset-password/success', name: 'ResetPasswordSuccess' } + ] }); - - it('should not call setPassword on submit if password 2 is not set', async () => { - await wrapper.setData({ new_password1: 'pass' }); - await wrapper.findComponent({ ref: 'form' }).trigger('submit'); - expect(setPassword).not.toHaveBeenCalled(); + if(Object.keys(queryParams).length>0) { + router.replace({name:'ResetPassword', query:queryParams}).catch(()=>{}); + } + const utils = render(ResetPassword, { + localVue, + router, + mocks: { + $tr: (key) => { + const translations = { + passwordLabel: 'New Password', + passwordConfirmLabel: 'Confirm Password', + submitButton: 'Submit', + resetPasswordFailed: 'Failed to reset password. Please try again.', + passwordValidationMessage: 'Password should be at least 8 characters long', + passwordMatchMessage: "Passwords don't match", + }; + return translations[key] || key; + }, + }, + store: { + modules: { + account: { + namespaced: true, + actions: { + setPassword: setPasswordMock, + }, + }, + }, + }, }); - it('should not call setPassword on submit if passwords do not not match', async () => { - await wrapper.setData({ new_password1: 'pass', new_password2: 'pass2' }); - await wrapper.findComponent({ ref: 'form' }).trigger('submit'); - expect(setPassword).not.toHaveBeenCalled(); + return {...utils, router}; +}; + +describe('ResetPassword', () => { + beforeEach(()=> { + jest.clearAllMocks(); }); - it('should call setPassword on submit if password data is valid', async () => { - await wrapper.setData({ new_password1: 'passw123', new_password2: 'passw123' }); - await wrapper.findComponent({ ref: 'form' }).trigger('submit'); - expect(setPassword).toHaveBeenCalled(); + it('shows validation errors when submitting invalid or mismatching passwords', async ()=> { + renderComponent(); + + await fireEvent.update(screen.getByLabelText(/New password/i), 'short'); + await fireEvent.update(screen.getByLabelText(/Confirm password/i),'mismatched'); + await fireEvent.click(screen.getByRole('button',{name:/Submit/i})); + + expect(setPasswordMock).not.toHaveBeenCalled(); + + await screen.findByText("Password should be at least 8 characters long"); + await screen.findByText("Passwords don't match"); }); - it('should retain data from query params so reset credentials are preserved', async () => { - router.replace({ - name: 'ResetPassword', - query: { - test: 'testing', - }, + it('submits form with correct data and preserves query params', async () => { + setPasswordMock.mockResolvedValue({}); + const queryParams = {token: 'xyz123', email: 'test@example.com'}; + const {router} = renderComponent(queryParams); + + await fireEvent.update(screen.getByLabelText(/New password/i), 'validPassword123'); + await fireEvent.update(screen.getByLabelText(/Confirm password/i), 'validPassword123'); + + await fireEvent.click(screen.getByRole('button', {name:/Submit/i})); + + await waitFor(()=>{ + expect(setPasswordMock).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + ...queryParams, + new_password1: 'validPassword123', + new_password2: 'validPassword123', + })); + }); + + await waitFor(()=> { + expect(router.currentRoute.name).toBe('ResetPasswordSuccess'); }); - await wrapper.setData({ new_password1: 'passw123', new_password2: 'passw123' }); - await wrapper.findComponent({ ref: 'form' }).trigger('submit'); - expect(setPassword.mock.calls[0][0].test).toBe('testing'); }); -}); +}); \ No newline at end of file From 660141f4b1341eca110b2097147450b6484135cf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 12:22:35 +0000 Subject: [PATCH 2/5] [pre-commit.ci lite] apply automatic fixes --- .../pages/__tests__/resetPassword.spec.js | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js index aba6913f58..1ac1509599 100644 --- a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js +++ b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js @@ -1,30 +1,29 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/vue'; import VueRouter from 'vue-router'; -import ResetPassword from '../resetPassword/ResetPassword'; import { createLocalVue } from '@vue/test-utils'; - +import ResetPassword from '../resetPassword/ResetPassword'; const localVue = createLocalVue(); localVue.use(VueRouter); -const setPasswordMock = jest.fn(()=>Promise.resolve()); +const setPasswordMock = jest.fn(() => Promise.resolve()); -const renderComponent = (queryParams={})=> { +const renderComponent = (queryParams = {}) => { const router = new VueRouter({ routes: [ - { path: '/', name: 'Main' }, + { path: '/', name: 'Main' }, { path: '/reset-password', name: 'ResetPassword' }, - { path: '/reset-password/success', name: 'ResetPasswordSuccess' } - ] + { path: '/reset-password/success', name: 'ResetPasswordSuccess' }, + ], }); - if(Object.keys(queryParams).length>0) { - router.replace({name:'ResetPassword', query:queryParams}).catch(()=>{}); + if (Object.keys(queryParams).length > 0) { + router.replace({ name: 'ResetPassword', query: queryParams }).catch(() => {}); } const utils = render(ResetPassword, { localVue, router, mocks: { - $tr: (key) => { + $tr: key => { const translations = { passwordLabel: 'New Password', passwordConfirmLabel: 'Confirm Password', @@ -48,49 +47,50 @@ const renderComponent = (queryParams={})=> { }, }); - return {...utils, router}; + return { ...utils, router }; }; describe('ResetPassword', () => { - beforeEach(()=> { + beforeEach(() => { jest.clearAllMocks(); }); - it('shows validation errors when submitting invalid or mismatching passwords', async ()=> { + it('shows validation errors when submitting invalid or mismatching passwords', async () => { renderComponent(); await fireEvent.update(screen.getByLabelText(/New password/i), 'short'); - await fireEvent.update(screen.getByLabelText(/Confirm password/i),'mismatched'); - await fireEvent.click(screen.getByRole('button',{name:/Submit/i})); + await fireEvent.update(screen.getByLabelText(/Confirm password/i), 'mismatched'); + await fireEvent.click(screen.getByRole('button', { name: /Submit/i })); expect(setPasswordMock).not.toHaveBeenCalled(); - await screen.findByText("Password should be at least 8 characters long"); + await screen.findByText('Password should be at least 8 characters long'); await screen.findByText("Passwords don't match"); }); it('submits form with correct data and preserves query params', async () => { setPasswordMock.mockResolvedValue({}); - const queryParams = {token: 'xyz123', email: 'test@example.com'}; - const {router} = renderComponent(queryParams); + const queryParams = { token: 'xyz123', email: 'test@example.com' }; + const { router } = renderComponent(queryParams); await fireEvent.update(screen.getByLabelText(/New password/i), 'validPassword123'); await fireEvent.update(screen.getByLabelText(/Confirm password/i), 'validPassword123'); - await fireEvent.click(screen.getByRole('button', {name:/Submit/i})); + await fireEvent.click(screen.getByRole('button', { name: /Submit/i })); - await waitFor(()=>{ + await waitFor(() => { expect(setPasswordMock).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ ...queryParams, new_password1: 'validPassword123', new_password2: 'validPassword123', - })); + }), + ); }); - await waitFor(()=> { + await waitFor(() => { expect(router.currentRoute.name).toBe('ResetPasswordSuccess'); }); }); -}); \ No newline at end of file +}); From 4180b07936f58b9ffe338076d425f0ab52271243 Mon Sep 17 00:00:00 2001 From: LightCreator1007 Date: Tue, 27 Jan 2026 22:17:28 +0530 Subject: [PATCH 3/5] removed translations --- .../pages/__tests__/resetPassword.spec.js | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js index 1ac1509599..a70eab89e4 100644 --- a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js +++ b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js @@ -23,17 +23,7 @@ const renderComponent = (queryParams = {}) => { localVue, router, mocks: { - $tr: key => { - const translations = { - passwordLabel: 'New Password', - passwordConfirmLabel: 'Confirm Password', - submitButton: 'Submit', - resetPasswordFailed: 'Failed to reset password. Please try again.', - passwordValidationMessage: 'Password should be at least 8 characters long', - passwordMatchMessage: "Passwords don't match", - }; - return translations[key] || key; - }, + $tr: key => key, }, store: { modules: { @@ -58,14 +48,14 @@ describe('ResetPassword', () => { it('shows validation errors when submitting invalid or mismatching passwords', async () => { renderComponent(); - await fireEvent.update(screen.getByLabelText(/New password/i), 'short'); - await fireEvent.update(screen.getByLabelText(/Confirm password/i), 'mismatched'); - await fireEvent.click(screen.getByRole('button', { name: /Submit/i })); + await fireEvent.update(screen.getByLabelText(/passwordLabel/i), 'short'); + await fireEvent.update(screen.getByLabelText(/passwordConfirmLabel/i), 'mismatched'); + await fireEvent.click(screen.getByRole('button', { name: /submitButton/i })); expect(setPasswordMock).not.toHaveBeenCalled(); - await screen.findByText('Password should be at least 8 characters long'); - await screen.findByText("Passwords don't match"); + await screen.findByText('passwordValidationMessage'); + await screen.findByText("passwordMatchMessage"); }); it('submits form with correct data and preserves query params', async () => { @@ -73,10 +63,10 @@ describe('ResetPassword', () => { const queryParams = { token: 'xyz123', email: 'test@example.com' }; const { router } = renderComponent(queryParams); - await fireEvent.update(screen.getByLabelText(/New password/i), 'validPassword123'); - await fireEvent.update(screen.getByLabelText(/Confirm password/i), 'validPassword123'); + await fireEvent.update(screen.getByLabelText(/passwordLabel/i), 'validPassword123'); + await fireEvent.update(screen.getByLabelText(/passwordConfirmLabel/i), 'validPassword123'); - await fireEvent.click(screen.getByRole('button', { name: /Submit/i })); + await fireEvent.click(screen.getByRole('button', { name: /submitButton/i })); await waitFor(() => { expect(setPasswordMock).toHaveBeenCalledWith( From 5185c6789cfee35fd912944b1f68a060916e4a88 Mon Sep 17 00:00:00 2001 From: LightCreator1007 Date: Wed, 28 Jan 2026 01:12:27 +0530 Subject: [PATCH 4/5] removed mocks completely --- .../pages/__tests__/resetPassword.spec.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js index a70eab89e4..7b78ac1f2e 100644 --- a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js +++ b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js @@ -22,9 +22,6 @@ const renderComponent = (queryParams = {}) => { const utils = render(ResetPassword, { localVue, router, - mocks: { - $tr: key => key, - }, store: { modules: { account: { @@ -48,14 +45,14 @@ describe('ResetPassword', () => { it('shows validation errors when submitting invalid or mismatching passwords', async () => { renderComponent(); - await fireEvent.update(screen.getByLabelText(/passwordLabel/i), 'short'); - await fireEvent.update(screen.getByLabelText(/passwordConfirmLabel/i), 'mismatched'); - await fireEvent.click(screen.getByRole('button', { name: /submitButton/i })); + await fireEvent.update(screen.getByLabelText(/New Password/i), 'short'); + await fireEvent.update(screen.getByLabelText(/Confirm Password/i), 'mismatched'); + await fireEvent.click(screen.getByRole('button', { name: /Submit/i })); expect(setPasswordMock).not.toHaveBeenCalled(); - await screen.findByText('passwordValidationMessage'); - await screen.findByText("passwordMatchMessage"); + await screen.findByText("Password should be at least 8 characters long"); + await screen.findByText("Passwords don't match"); }); it('submits form with correct data and preserves query params', async () => { @@ -63,10 +60,10 @@ describe('ResetPassword', () => { const queryParams = { token: 'xyz123', email: 'test@example.com' }; const { router } = renderComponent(queryParams); - await fireEvent.update(screen.getByLabelText(/passwordLabel/i), 'validPassword123'); - await fireEvent.update(screen.getByLabelText(/passwordConfirmLabel/i), 'validPassword123'); + await fireEvent.update(screen.getByLabelText(/New Password/i), 'validPassword123'); + await fireEvent.update(screen.getByLabelText(/Confirm Password/i), 'validPassword123'); - await fireEvent.click(screen.getByRole('button', { name: /submitButton/i })); + await fireEvent.click(screen.getByRole('button', { name: /Submit/i })); await waitFor(() => { expect(setPasswordMock).toHaveBeenCalledWith( From 88087ab909022cebc775f636e4bdece80ad51896 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 27 Jan 2026 19:48:49 +0000 Subject: [PATCH 5/5] [pre-commit.ci lite] apply automatic fixes --- .../frontend/accounts/pages/__tests__/resetPassword.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js index 7b78ac1f2e..c87be9ab31 100644 --- a/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js +++ b/contentcuration/contentcuration/frontend/accounts/pages/__tests__/resetPassword.spec.js @@ -51,7 +51,7 @@ describe('ResetPassword', () => { expect(setPasswordMock).not.toHaveBeenCalled(); - await screen.findByText("Password should be at least 8 characters long"); + await screen.findByText('Password should be at least 8 characters long'); await screen.findByText("Passwords don't match"); });