diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py index 905e08550..d60859964 100644 --- a/web/pgadmin/feature_tests/query_tool_journey_test.py +++ b/web/pgadmin/feature_tests/query_tool_journey_test.py @@ -78,6 +78,10 @@ class QueryToolJourneyTest(BaseFeatureTest): self.page.click_tab("History") selected_history_entry = self.page.find_by_css_selector("#query_list .selected") self.assertIn("SELECT * FROM shoes", selected_history_entry.text) + failed_history_detail_pane = self.page.find_by_id("query_detail") + + self.assertIn("Error Message relation \"shoes\" does not exist", failed_history_detail_pane.text) + ActionChains(self.page.driver) \ .send_keys(Keys.ARROW_DOWN) \ .perform() diff --git a/web/pgadmin/static/jsx/history/detail/history_error_message.jsx b/web/pgadmin/static/jsx/history/detail/history_error_message.jsx new file mode 100644 index 000000000..0b5e27bf1 --- /dev/null +++ b/web/pgadmin/static/jsx/history/detail/history_error_message.jsx @@ -0,0 +1,30 @@ +////////////////////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2017, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////////////////// + +import React from 'react'; + +import Shapes from '../../react_shapes'; + +export default class HistoryErrorMessage extends React.Component { + + parseErrorMessage(message) { + return message.match(/ERROR:\s*([^\n\r]*)/i)[1]; + } + + render() { + return ( +
+ Error Message {this.parseErrorMessage(this.props.historyEntry.message)} +
); + } +} + +HistoryErrorMessage.propTypes = { + historyEntry: Shapes.historyDetail, +}; \ No newline at end of file diff --git a/web/pgadmin/static/jsx/history/query_history_detail.jsx b/web/pgadmin/static/jsx/history/query_history_detail.jsx index ec391f6a4..fede2f729 100644 --- a/web/pgadmin/static/jsx/history/query_history_detail.jsx +++ b/web/pgadmin/static/jsx/history/query_history_detail.jsx @@ -11,14 +11,23 @@ import React from 'react'; import HistoryDetailMetadata from './detail/history_detail_metadata'; import HistoryDetailQuery from './detail/history_detail_query'; import HistoryDetailMessage from './detail/history_detail_message'; +import HistoryErrorMessage from './detail/history_error_message'; import Shapes from '../react_shapes'; export default class QueryHistoryDetail extends React.Component { render() { if (!_.isUndefined(this.props.historyEntry)) { + let historyErrorMessage = null; + if (!this.props.historyEntry.status) { + historyErrorMessage =
+ +
; + } + return (
+ {historyErrorMessage}
diff --git a/web/pgadmin/static/scss/sqleditor/_history.scss b/web/pgadmin/static/scss/sqleditor/_history.scss index c8255762b..ed0c10f77 100644 --- a/web/pgadmin/static/scss/sqleditor/_history.scss +++ b/web/pgadmin/static/scss/sqleditor/_history.scss @@ -11,7 +11,7 @@ border: 2px solid transparent; margin-left: 1px; - .other-info{ + .other-info { @extend .text-13; @extend .font-gray-4; font-family: monospace; @@ -47,7 +47,7 @@ font-weight: bold; border: 2px solid; - .other-info{ + .other-info { @extend .font-primary-blue; font-weight: bold; } @@ -62,19 +62,37 @@ .query-detail { width: 100%; - padding-top: 10px; display: flex; flex-direction: column; + .error-message-block { + @extend .bg-red-1; + flex: 0.3; + padding-left: 20px; + + .history-error-text { + @extend .text-12; + padding: 7px 0; + + span { + @extend .font-red-3; + font-weight: 500; + margin-right: 8px; + } + } + } + .metadata-block { - flex: 1; - padding: 0 10px; + flex: 0.4; + padding: 10px 20px; .metadata { display: flex; + flex-wrap: wrap; .item { flex: 1; + min-width: 130px; .value { @extend .text-14; @@ -106,7 +124,7 @@ .message-block { flex: 2; display: flex; - padding-left: 10px; + padding: 0 20px; .message { flex: 2 2 0%; diff --git a/web/regression/javascript/history/query_history_spec.jsx b/web/regression/javascript/history/query_history_spec.jsx index a60aeb1e9..2fa6a7db8 100644 --- a/web/regression/javascript/history/query_history_spec.jsx +++ b/web/regression/javascript/history/query_history_spec.jsx @@ -49,6 +49,7 @@ describe('QueryHistory', () => { expect(foundChildren.length).toBe(0); done(); }); + it('nothing is displayed on right panel', (done) => { let foundChildren = historyWrapper.find(QueryHistoryDetail); expect(foundChildren.length).toBe(1); @@ -68,7 +69,7 @@ describe('QueryHistory', () => { status: true, row_affected: 12345, total_time: '14 msec', - message: 'message from first sql query', + message: 'something important ERROR: message from first sql query', }, { query: 'second sql statement', start_time: new Date(2016, 11, 11, 1, 33, 5, 99), @@ -191,27 +192,41 @@ describe('QueryHistory', () => { done(); }, 1000); }); + + describe('when the query failed', () => { + let failedEntry; + + beforeEach(() => { + failedEntry = foundChildren.at(1); + failedEntry.simulate('click'); + }); + it('displays the error message on top of the details pane', () => { + expect(queryDetail.at(0).text()).toContain('Error Message message from second sql query'); + }); + }); }); describe('when the older query is clicked on', () => { + let firstEntry, secondEntry; + beforeEach(() => { - foundChildren.at(1).simulate('click'); + firstEntry = foundChildren.at(0); + secondEntry = foundChildren.at(1); + secondEntry.simulate('click'); }); it('displays the query in the right pane', () => { expect(queryDetail.at(0).text()).toContain('second sql statement'); }); - it('renders the most recent query as selected in the left pane', () => { - expect(foundChildren.at(0).nodes.length).toBe(1); - expect(foundChildren.at(0).hasClass('selected')).toBeFalsy(); - expect(foundChildren.at(0).hasClass('error')).toBeFalsy(); + it('deselects the first history entry', () => { + expect(firstEntry.nodes.length).toBe(1); + expect(firstEntry.hasClass('selected')).toBeFalsy(); }); - it('renders the older query as selected in the left pane', () => { - expect(foundChildren.at(1).nodes.length).toBe(1); - expect(foundChildren.at(1).hasClass('selected')).toBeTruthy(); - expect(foundChildren.at(1).hasClass('error')).toBeTruthy(); + it('selects the second history entry', () => { + expect(secondEntry.nodes.length).toBe(1); + expect(secondEntry.hasClass('selected')).toBeTruthy(); }); }); @@ -223,7 +238,7 @@ describe('QueryHistory', () => { status: false, row_affected: 5, total_time: '26 msec', - message: 'third sql message', + message: 'pretext ERROR: third sql message', }); foundChildren = historyWrapper.find(QueryHistoryEntry);