diff --git a/web/pgadmin/static/js/Dialogs/ChangePasswordContent.jsx b/web/pgadmin/static/js/Dialogs/ChangePasswordContent.jsx index d7f6fea1519..14235924d8c 100644 --- a/web/pgadmin/static/js/Dialogs/ChangePasswordContent.jsx +++ b/web/pgadmin/static/js/Dialogs/ChangePasswordContent.jsx @@ -4,7 +4,7 @@ import gettext from 'sources/gettext'; import BaseUISchema from '../SchemaView/base_schema.ui'; import SchemaView from '../SchemaView'; -class ChangePasswordSchema extends BaseUISchema { +export class ChangePasswordSchema extends BaseUISchema { constructor(user, isPgpassFileUsed, hasCsrfToken=false, showUser=true) { super({ user: user, @@ -64,11 +64,11 @@ class ChangePasswordSchema extends BaseUISchema { } export default function ChangePasswordContent({getInitData=() => { /*This is intentional (SonarQube)*/ }, - onSave, onClose, hasCsrfToken=false, showUser=true, userName=''}) { + onSave, onClose, hasCsrfToken=false, showUser=true, userName='', isPgpassFileUsed=false}) { const schema=React.useRef(null); if (!schema.current) schema.current = new ChangePasswordSchema( - userName, false, hasCsrfToken, showUser + userName, isPgpassFileUsed, hasCsrfToken, showUser ); return { /*This is int viewHelperProps={{ mode: 'create', }} - customSaveBtnName={'Change'} + customSaveBtnName={gettext('Change')} onSave={onSave} onClose={onClose} hasSQL={false} @@ -93,5 +93,6 @@ ChangePasswordContent.propTypes = { getInitData: PropTypes.func, hasCsrfToken: PropTypes.bool, showUser: PropTypes.bool, - userName: PropTypes.string + userName: PropTypes.string, + isPgpassFileUsed: PropTypes.bool }; diff --git a/web/regression/javascript/Dialogs/ChangePasswordContent.spec.js b/web/regression/javascript/Dialogs/ChangePasswordContent.spec.js new file mode 100644 index 00000000000..4ac99a0e3d1 --- /dev/null +++ b/web/regression/javascript/Dialogs/ChangePasswordContent.spec.js @@ -0,0 +1,62 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2026, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +// Only the schema that ChangePasswordContent builds is under test here, so +// SchemaView is replaced with a stub that records the props it receives. +const mockSchemaViewProps = []; +jest.mock('../../../pgadmin/static/js/SchemaView', () => ({ + __esModule: true, + default: (props) => { + mockSchemaViewProps.push(props); + return null; + }, +})); + +import { render } from '@testing-library/react'; +import ChangePasswordContent, { ChangePasswordSchema } from '../../../pgadmin/static/js/Dialogs/ChangePasswordContent'; + +function currentPasswordField(schema) { + return schema.baseFields.find((f) => f.id === 'password'); +} + +function renderedSchema(props) { + mockSchemaViewProps.length = 0; + render(); + return mockSchemaViewProps[mockSchemaViewProps.length - 1].schema; +} + +describe('ChangePasswordContent', () => { + it('requires the current password when no pgpass file is used', () => { + const schema = renderedSchema({userName: 'postgres'}); + const field = currentPasswordField(schema); + + expect(schema.isPgpassFileUsed).toBe(false); + expect(field.disabled).toBe(false); + expect(field.noEmpty).toBe(true); + }); + + it('disables and does not require the current password when a pgpass file is used', () => { + const schema = renderedSchema({userName: 'postgres', isPgpassFileUsed: true}); + const field = currentPasswordField(schema); + + expect(schema.isPgpassFileUsed).toBe(true); + expect(field.disabled).toBe(true); + expect(field.noEmpty).toBe(false); + }); +}); + +describe('ChangePasswordSchema', () => { + it('flags mismatched new passwords', () => { + const schema = new ChangePasswordSchema('postgres', false); + const setError = jest.fn(); + + expect(schema.validate({newPassword: 'a', confirmPassword: 'b'}, setError)).toBe(true); + expect(setError).toHaveBeenCalledWith('confirmPassword', 'Passwords do not match.'); + }); +});