diff --git a/web/pgadmin/misc/__init__.py b/web/pgadmin/misc/__init__.py
index 972245c0ead..e47f1d46948 100644
--- a/web/pgadmin/misc/__init__.py
+++ b/web/pgadmin/misc/__init__.py
@@ -335,23 +335,19 @@ def validate_binary_path():
if data != '':
data = json.loads(data)
- version_str = ''
-
# Do not allow storage dir as utility path
if 'utility_path' in data and data['utility_path'] is not None and \
Path(config.STORAGE_DIR) != Path(data['utility_path']) and \
Path(config.STORAGE_DIR) not in Path(data['utility_path']).parents:
binary_versions = get_binary_path_versions(data['utility_path'])
- for utility, version in binary_versions.items():
- if version is None:
- version_str += "" + utility + ": " + \
- "not found on the specified binary path.
"
- else:
- version_str += "" + utility + ": " + version + "
"
+ utilities = [
+ {'utility': utility, 'version': version}
+ for utility, version in binary_versions.items()
+ ]
else:
return precondition_required(gettext('Invalid binary path.'))
- return make_json_response(data=gettext(version_str), status=200)
+ return make_json_response(data=utilities, status=200)
@blueprint.route("/upgrade_check", endpoint="upgrade_check",
diff --git a/web/pgadmin/preferences/static/js/components/binary_path.ui.js b/web/pgadmin/preferences/static/js/components/binary_path.ui.jsx
similarity index 84%
rename from web/pgadmin/preferences/static/js/components/binary_path.ui.js
rename to web/pgadmin/preferences/static/js/components/binary_path.ui.jsx
index 6d5f28bc5c5..da4291fda5d 100644
--- a/web/pgadmin/preferences/static/js/components/binary_path.ui.js
+++ b/web/pgadmin/preferences/static/js/components/binary_path.ui.jsx
@@ -13,6 +13,7 @@ import url_for from 'sources/url_for';
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
import getApiInstance from '../../../../static/js/api_instance';
import pgAdmin from 'sources/pgadmin';
+import { SafeMessage } from '../../../../static/js/components/SafeMessage';
export function getBinaryPathSchema() {
@@ -68,7 +69,13 @@ export default class BinaryPathSchema extends BaseUISchema {
api.post(url_for('misc.validate_binary_path'),
JSON.stringify({ 'utility_path': data }))
.then(function (res) {
- pgAdmin.Browser.notifier.alertText(gettext('Validate binary path'), gettext(res.data.data));
+ const rows = (res.data.data ?? []).map(({utility, version}) => (
+
+ {utility}:{' '}
+
+
+ ));
+ pgAdmin.Browser.notifier.alert(gettext('Validate binary path'), <>{rows}>);
})
.catch(function (error) {
pgAdmin.Browser.notifier.pgNotifier('error', error, gettext('Failed to validate binary path.'));
diff --git a/web/pgadmin/static/js/helpers/ModalProvider.jsx b/web/pgadmin/static/js/helpers/ModalProvider.jsx
index 3b7406da6b5..e9fba9d18b0 100644
--- a/web/pgadmin/static/js/helpers/ModalProvider.jsx
+++ b/web/pgadmin/static/js/helpers/ModalProvider.jsx
@@ -68,7 +68,7 @@ export function AlertContent({ text, confirm, okLabel = gettext('OK'), cancelLab
);
}
AlertContent.propTypes = {
- text: PropTypes.string,
+ text: PropTypes.node,
confirm: PropTypes.bool,
onOkClick: PropTypes.func,
onCancelClick: PropTypes.func,
diff --git a/web/regression/javascript/schema_ui_files/binary_path.ui.spec.js b/web/regression/javascript/schema_ui_files/binary_path.ui.spec.js
index dee2cb3d288..dadf2f218cf 100644
--- a/web/regression/javascript/schema_ui_files/binary_path.ui.spec.js
+++ b/web/regression/javascript/schema_ui_files/binary_path.ui.spec.js
@@ -8,10 +8,14 @@
//////////////////////////////////////////////////////////////
+import {render} from '@testing-library/react';
import {genericBeforeEach, getEditView} from '../genericFunctions';
import pgAdmin from '../fake_pgadmin';
import { getBinaryPathSchema } from '../../../pgadmin/preferences/static/js/components/binary_path.ui';
+let mockPost = jest.fn();
+jest.mock('sources/api_instance', () => () => ({ post: mockPost }));
+
describe('BinaryPathschema', ()=>{
let schemaObj = getBinaryPathSchema();
@@ -25,16 +29,51 @@ describe('BinaryPathschema', ()=>{
beforeEach(()=>{
genericBeforeEach();
+ mockPost.mockReset();
+ pgAdmin.Browser.notifier.alert.mockClear();
});
it('edit', async ()=>{
await getEditView(schemaObj, getInitData);
});
- it('validate path', ()=>{
+ it('validate path - empty path', ()=>{
let validate = _.find(schemaObj.fields, (f)=>f.id=='binaryPath').validate;
let status = validate('');
expect(status).toBe(true);
});
+ it('validate path - renders bold labels and line breaks, not raw markup', async ()=>{
+ mockPost.mockResolvedValue({
+ data: {
+ data: [
+ {utility: 'pg_dump', version: null},
+ {utility: 'psql', version: 'psql 17.2'},
+ ],
+ },
+ });
+
+ let validate = _.find(schemaObj.fields, (f)=>f.id=='binaryPath').validate;
+ let status = validate('/some/path');
+ expect(status).toBe(true);
+
+ // Let the post().then() microtask run.
+ await Promise.resolve();
+ await Promise.resolve();
+
+ expect(pgAdmin.Browser.notifier.alert).toHaveBeenCalledTimes(1);
+ const [title, node] = pgAdmin.Browser.notifier.alert.mock.calls[0];
+ expect(title).toBe('Validate binary path');
+
+ const ctrl = render(node);
+ expect(ctrl.container.querySelectorAll('b')).toHaveLength(2);
+ expect(ctrl.container.textContent).toContain('pg_dump:');
+ expect(ctrl.container.textContent).toContain('not found on the specified binary path.');
+ expect(ctrl.container.textContent).toContain('psql:');
+ expect(ctrl.container.textContent).toContain('psql 17.2');
+ // No literal markup should ever appear in the rendered text.
+ expect(ctrl.container.textContent).not.toContain('');
+ expect(ctrl.container.textContent).not.toContain('
');
+ });
+
});