Initial re-vamp of the History tab.
parent
16a15bf934
commit
1208206bc0
web
pgadmin
static
tools/sqleditor/templates/sqleditor/js
utils/javascript
regression
feature_utils
python_test_utils
|
@ -5,7 +5,10 @@ module.exports = {
|
||||||
'amd': true,
|
'amd': true,
|
||||||
'jasmine': true,
|
'jasmine': true,
|
||||||
},
|
},
|
||||||
'extends': 'eslint:recommended',
|
'extends': [
|
||||||
|
'eslint:recommended',
|
||||||
|
"plugin:react/recommended",
|
||||||
|
],
|
||||||
'parserOptions': {
|
'parserOptions': {
|
||||||
'ecmaFeatures': {
|
'ecmaFeatures': {
|
||||||
'experimentalObjectRestSpread': true,
|
'experimentalObjectRestSpread': true,
|
||||||
|
@ -40,6 +43,6 @@ module.exports = {
|
||||||
'comma-dangle': [
|
'comma-dangle': [
|
||||||
'error',
|
'error',
|
||||||
'always-multiline'
|
'always-multiline'
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -29,7 +29,7 @@ module.exports = function (config) {
|
||||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||||
preprocessors: {
|
preprocessors: {
|
||||||
'regression/javascript/**/*.js': ['webpack'],
|
'regression/javascript/**/*.js': ['webpack'],
|
||||||
// 'regression/javascript/**/*.jsx': ['webpack'],
|
'regression/javascript/**/*.jsx': ['webpack'],
|
||||||
},
|
},
|
||||||
|
|
||||||
webpack: webpackConfig,
|
webpack: webpackConfig,
|
||||||
|
|
|
@ -5,9 +5,11 @@
|
||||||
"babel-preset-es2015": "~6.24.0",
|
"babel-preset-es2015": "~6.24.0",
|
||||||
"babel-preset-react": "~6.23.0",
|
"babel-preset-react": "~6.23.0",
|
||||||
"enzyme": "~2.8.2",
|
"enzyme": "~2.8.2",
|
||||||
|
"enzyme-matchers": "^3.1.0",
|
||||||
"eslint": "^3.19.0",
|
"eslint": "^3.19.0",
|
||||||
"eslint-plugin-react": "^6.10.3",
|
"eslint-plugin-react": "^6.10.3",
|
||||||
"jasmine-core": "~2.5.2",
|
"jasmine-core": "~2.5.2",
|
||||||
|
"jasmine-enzyme": "^3.1.0",
|
||||||
"karma": "~1.5.0",
|
"karma": "~1.5.0",
|
||||||
"karma-babel-preprocessor": "^6.0.1",
|
"karma-babel-preprocessor": "^6.0.1",
|
||||||
"karma-browserify": "~5.1.1",
|
"karma-browserify": "~5.1.1",
|
||||||
|
@ -28,7 +30,9 @@
|
||||||
"babelify": "~7.3.0",
|
"babelify": "~7.3.0",
|
||||||
"browserify": "~14.1.0",
|
"browserify": "~14.1.0",
|
||||||
"exports-loader": "~0.6.4",
|
"exports-loader": "~0.6.4",
|
||||||
|
"immutability-helper": "^2.2.0",
|
||||||
"imports-loader": "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d",
|
"imports-loader": "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d",
|
||||||
|
"moment": "^2.18.1",
|
||||||
"react": "~15.4.2",
|
"react": "~15.4.2",
|
||||||
"react-dom": "~15.4.2",
|
"react-dom": "~15.4.2",
|
||||||
"requirejs": "~2.3.3",
|
"requirejs": "~2.3.3",
|
||||||
|
@ -38,7 +42,9 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"linter": "yarn run eslint pgadmin/static/jsx/**/*.jsx pgadmin/static/js/selection/*.js regression/javascript/**/*.jsx regression/javascript/**/*.js *.js",
|
"linter": "yarn run eslint pgadmin/static/jsx/**/*.jsx pgadmin/static/js/selection/*.js regression/javascript/**/*.jsx regression/javascript/**/*.js *.js",
|
||||||
"webpacker": "yarn run webpack -- --optimize-minimize --config webpack.config.js",
|
"webpacker": "yarn run webpack -- --optimize-minimize --config webpack.config.js",
|
||||||
|
"webpacker:dev": "yarn run webpack -- --config webpack.config.js",
|
||||||
"bundle": "yarn run linter && yarn run webpacker",
|
"bundle": "yarn run linter && yarn run webpacker",
|
||||||
|
"bundle:dev": "yarn run linter && yarn run webpacker:dev",
|
||||||
"test:karma-once": "yarn run linter && yarn run karma start -- --single-run",
|
"test:karma-once": "yarn run linter && yarn run karma start -- --single-run",
|
||||||
"test:karma": "yarn run linter && yarn run karma start",
|
"test:karma": "yarn run linter && yarn run karma start",
|
||||||
"test:feature": "yarn run bundle && python regression/runtests.py --pkg feature_tests",
|
"test:feature": "yarn run bundle && python regression/runtests.py --pkg feature_tests",
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
import pyperclip
|
||||||
|
import time
|
||||||
|
|
||||||
|
from selenium.webdriver import ActionChains
|
||||||
|
|
||||||
|
from regression.python_test_utils import test_utils
|
||||||
|
from regression.feature_utils.base_feature_test import BaseFeatureTest
|
||||||
|
|
||||||
|
|
||||||
|
class QueryToolJourneyTest(BaseFeatureTest):
|
||||||
|
"""
|
||||||
|
Tests the path through the query tool
|
||||||
|
"""
|
||||||
|
|
||||||
|
scenarios = [
|
||||||
|
("Tests the path through the query tool", dict())
|
||||||
|
]
|
||||||
|
|
||||||
|
def before(self):
|
||||||
|
connection = test_utils.get_db_connection(self.server['db'],
|
||||||
|
self.server['username'],
|
||||||
|
self.server['db_password'],
|
||||||
|
self.server['host'],
|
||||||
|
self.server['port'])
|
||||||
|
test_utils.drop_database(connection, "acceptance_test_db")
|
||||||
|
test_utils.create_database(self.server, "acceptance_test_db")
|
||||||
|
test_utils.create_table(self.server, "acceptance_test_db", "test_table")
|
||||||
|
self.page.add_server(self.server)
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
self._navigate_to_query_tool()
|
||||||
|
self._execute_query("SELECT * FROM test_table ORDER BY value")
|
||||||
|
|
||||||
|
self._test_copies_rows()
|
||||||
|
self._test_copies_columns()
|
||||||
|
self._test_history_tab()
|
||||||
|
|
||||||
|
def _test_copies_rows(self):
|
||||||
|
pyperclip.copy("old clipboard contents")
|
||||||
|
time.sleep(5)
|
||||||
|
self.page.driver.switch_to.default_content()
|
||||||
|
self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
|
||||||
|
self.page.find_by_xpath("//*[contains(@class, 'slick-row')]/*[1]").click()
|
||||||
|
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||||
|
|
||||||
|
self.assertEqual("'Some-Name','6','some info'",
|
||||||
|
pyperclip.paste())
|
||||||
|
|
||||||
|
def _test_copies_columns(self):
|
||||||
|
pyperclip.copy("old clipboard contents")
|
||||||
|
|
||||||
|
self.page.driver.switch_to.default_content()
|
||||||
|
self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
|
||||||
|
self.page.find_by_xpath("//*[@data-test='output-column-header' and contains(., 'some_column')]").click()
|
||||||
|
self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
|
||||||
|
|
||||||
|
self.assertTrue("'Some-Name'" in pyperclip.paste())
|
||||||
|
self.assertTrue("'Some-Other-Name'" in pyperclip.paste())
|
||||||
|
self.assertTrue("'Yet-Another-Name'" in pyperclip.paste())
|
||||||
|
|
||||||
|
def _test_history_tab(self):
|
||||||
|
self.__clear_query_tool()
|
||||||
|
|
||||||
|
editor_input = self.page.find_by_id("output-panel")
|
||||||
|
self.page.click_element(editor_input)
|
||||||
|
self._execute_query("SELECT * FROM shoes")
|
||||||
|
|
||||||
|
self.page.click_tab("History")
|
||||||
|
history_element = self.page.find_by_id("history_grid")
|
||||||
|
self.assertIn("SELECT * FROM test_table", history_element.text)
|
||||||
|
self.assertIn("SELECT * FROM shoes", history_element.text)
|
||||||
|
|
||||||
|
def __clear_query_tool(self):
|
||||||
|
self.page.click_element(self.page.find_by_xpath("//*[@id='btn-edit']"))
|
||||||
|
self.page.click_modal('Yes')
|
||||||
|
|
||||||
|
def _navigate_to_query_tool(self):
|
||||||
|
self.page.toggle_open_tree_item(self.server['name'])
|
||||||
|
self.page.toggle_open_tree_item('Databases')
|
||||||
|
self.page.toggle_open_tree_item('acceptance_test_db')
|
||||||
|
time.sleep(5)
|
||||||
|
self.page.find_by_partial_link_text("Tools").click()
|
||||||
|
self.page.find_by_partial_link_text("Query Tool").click()
|
||||||
|
self.page.click_tab('Query-1')
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
def _execute_query(self, query):
|
||||||
|
ActionChains(self.page.driver).send_keys(query).perform()
|
||||||
|
self.page.driver.switch_to.default_content()
|
||||||
|
self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
|
||||||
|
self.page.find_by_id("btn-flash").click()
|
||||||
|
|
||||||
|
def after(self):
|
||||||
|
self.page.close_query_tool()
|
||||||
|
self.page.remove_server(self.server)
|
||||||
|
|
||||||
|
connection = test_utils.get_db_connection(self.server['db'],
|
||||||
|
self.server['username'],
|
||||||
|
self.server['db_password'],
|
||||||
|
self.server['host'],
|
||||||
|
self.server['port'])
|
||||||
|
test_utils.drop_database(connection, "acceptance_test_db")
|
|
@ -83,7 +83,7 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
|
||||||
|
|
||||||
# If debugger plugin is not found
|
# If debugger plugin is not found
|
||||||
if is_error and is_error == "Debugger Error":
|
if is_error and is_error == "Debugger Error":
|
||||||
self.page.click_modal_ok()
|
self.page.click_modal('OK')
|
||||||
self.skipTest("Please make sure that debugger plugin is properly configured")
|
self.skipTest("Please make sure that debugger plugin is properly configured")
|
||||||
else:
|
else:
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// pgAdmin 4 - PostgreSQL Tools
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||||
|
// This software is released under the PostgreSQL Licence
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
export default class HistoryCollection {
|
||||||
|
|
||||||
|
constructor(history_model) {
|
||||||
|
this.historyList = history_model;
|
||||||
|
this.onChange(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
length() {
|
||||||
|
return this.historyList.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(object) {
|
||||||
|
this.historyList.push(object);
|
||||||
|
this.onChangeHandler(this.historyList);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.historyList = [];
|
||||||
|
this.onChangeHandler(this.historyList);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(onChangeHandler) {
|
||||||
|
this.onChangeHandler = onChangeHandler;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// pgAdmin 4 - PostgreSQL Tools
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||||
|
// This software is released under the PostgreSQL Licence
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
import historyCollection from './history_collection';
|
||||||
|
|
||||||
|
export {
|
||||||
|
historyCollection,
|
||||||
|
};
|
|
@ -1,8 +1,10 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {render} from 'react-dom';
|
import {render} from 'react-dom';
|
||||||
|
import QueryHistory from './history/query_history';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
render,
|
render,
|
||||||
React,
|
React,
|
||||||
|
QueryHistory,
|
||||||
};
|
};
|
|
@ -0,0 +1,49 @@
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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 QueryHistoryEntry from './query_history_entry';
|
||||||
|
|
||||||
|
const liStyle = {
|
||||||
|
borderBottom: '1px solid #cccccc',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class QueryHistory extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
history: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.setState({history: this.props.historyCollection.historyList});
|
||||||
|
this.props.historyCollection.onChange((historyList) => this.setState({history: historyList}));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <ul>
|
||||||
|
{_.chain(this.state.history)
|
||||||
|
.sortBy(historyEntry => historyEntry.start_time)
|
||||||
|
.reverse()
|
||||||
|
.map((entry, index) =>
|
||||||
|
<li key={index} style={liStyle}>
|
||||||
|
<QueryHistoryEntry historyEntry={entry}/>
|
||||||
|
</li>)
|
||||||
|
.value()
|
||||||
|
}
|
||||||
|
</ul>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryHistory.propTypes = {
|
||||||
|
historyCollection: React.PropTypes.object.isRequired,
|
||||||
|
};
|
|
@ -0,0 +1,93 @@
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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 update from 'immutability-helper';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
const outerDivStyle = {
|
||||||
|
paddingLeft: '10px',
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
paddingRight: '20px',
|
||||||
|
fontSize: '14px',
|
||||||
|
backgroundColor: '#FFF',
|
||||||
|
};
|
||||||
|
const sqlStyle = {
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
overflow: 'hidden',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
userSelect: 'auto',
|
||||||
|
};
|
||||||
|
const secondLineStyle = {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
fontSize: '13px',
|
||||||
|
color: '#888888',
|
||||||
|
};
|
||||||
|
const timestampStyle = {
|
||||||
|
alignSelf: 'flex-start',
|
||||||
|
};
|
||||||
|
const rowsAffectedStyle = {
|
||||||
|
alignSelf: 'flex-end',
|
||||||
|
};
|
||||||
|
const errorMessageStyle = {
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
overflow: 'hidden',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
userSelect: 'auto',
|
||||||
|
fontSize: '13px',
|
||||||
|
color: '#888888',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class QueryHistoryEntry extends React.Component {
|
||||||
|
formatDate(date) {
|
||||||
|
return (moment(date).format('MMM D YYYY [–] HH:mm:ss'));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div style={this.queryEntryBackgroundColor()}>
|
||||||
|
<div style={sqlStyle}>
|
||||||
|
{this.props.historyEntry.query}
|
||||||
|
</div>
|
||||||
|
<div style={secondLineStyle}>
|
||||||
|
<div style={timestampStyle}>
|
||||||
|
{this.formatDate(this.props.historyEntry.start_time)} /
|
||||||
|
total time: {this.props.historyEntry.total_time}
|
||||||
|
</div>
|
||||||
|
<div style={rowsAffectedStyle}>
|
||||||
|
{this.props.historyEntry.row_affected} rows affected
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={errorMessageStyle}>
|
||||||
|
{this.props.historyEntry.message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
queryEntryBackgroundColor() {
|
||||||
|
if (!this.props.historyEntry.status) {
|
||||||
|
return update(outerDivStyle, {$merge: {backgroundColor: '#F7D0D5'}});
|
||||||
|
}
|
||||||
|
return outerDivStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryHistoryEntry.propTypes = {
|
||||||
|
historyEntry: React.PropTypes.shape({
|
||||||
|
query: React.PropTypes.string,
|
||||||
|
start_time: React.PropTypes.instanceOf(Date),
|
||||||
|
status: React.PropTypes.bool,
|
||||||
|
total_time: React.PropTypes.string,
|
||||||
|
row_affected: React.PropTypes.int,
|
||||||
|
message: React.PropTypes.string,
|
||||||
|
}),
|
||||||
|
};
|
|
@ -9,7 +9,10 @@ define([
|
||||||
'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
|
'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
|
||||||
'sources/selection/xcell_selection_model',
|
'sources/selection/xcell_selection_model',
|
||||||
'sources/selection/set_staged_rows',
|
'sources/selection/set_staged_rows',
|
||||||
'sources/gettext', 'sources/sqleditor_utils',
|
'sources/gettext',
|
||||||
|
'sources/sqleditor_utils',
|
||||||
|
'sources/generated/history',
|
||||||
|
'sources/generated/reactComponents',
|
||||||
|
|
||||||
'slickgrid', 'bootstrap', 'pgadmin.browser', 'wcdocker',
|
'slickgrid', 'bootstrap', 'pgadmin.browser', 'wcdocker',
|
||||||
'codemirror/mode/sql/sql', 'codemirror/addon/selection/mark-selection',
|
'codemirror/mode/sql/sql', 'codemirror/addon/selection/mark-selection',
|
||||||
|
@ -32,9 +35,9 @@ define([
|
||||||
'slickgrid/plugins/slick.rowselectionmodel',
|
'slickgrid/plugins/slick.rowselectionmodel',
|
||||||
'slickgrid/slick.grid'
|
'slickgrid/slick.grid'
|
||||||
], function(
|
], function(
|
||||||
$, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror, pgExplain, GridSelector,
|
$, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror,
|
||||||
ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
|
pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
|
||||||
XCellSelectionModel, setStagedRows, gettext, SqlEditorUtils
|
XCellSelectionModel, setStagedRows, gettext, SqlEditorUtils, HistoryBundle, reactComponents
|
||||||
) {
|
) {
|
||||||
/* Return back, this has been called more than once */
|
/* Return back, this has been called more than once */
|
||||||
if (pgAdmin.SqlEditor)
|
if (pgAdmin.SqlEditor)
|
||||||
|
@ -877,144 +880,11 @@ define([
|
||||||
self.history_grid.remove();
|
self.history_grid.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
var history_model = Backbone.Model.extend({
|
self.history_collection = new HistoryBundle.historyCollection([]);
|
||||||
defaults: {
|
|
||||||
status: undefined,
|
|
||||||
start_time: undefined,
|
|
||||||
query: undefined,
|
|
||||||
row_affected: 0,
|
|
||||||
row_retrieved: 0,
|
|
||||||
total_time: undefined,
|
|
||||||
message: ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var history_collection = self.history_collection = new (Backbone.Collection.extend({
|
let queryHistoryElement = reactComponents.React.createElement(
|
||||||
model: history_model,
|
reactComponents.QueryHistory, {historyCollection: self.history_collection});
|
||||||
// comparator to sort the history in reverse order of the start_time
|
reactComponents.render(queryHistoryElement, $('#history_grid')[0]);
|
||||||
comparator: function(a, b) {
|
|
||||||
return -a.get('start_time').localeCompare(b.get('start_time'));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
var columns = [{
|
|
||||||
name: "status",
|
|
||||||
label: "",
|
|
||||||
cell: Backgrid.Cell.extend({
|
|
||||||
class: 'sql-status-cell',
|
|
||||||
render: function() {
|
|
||||||
this.$el.empty();
|
|
||||||
var $btn = $('<button></button>', {
|
|
||||||
class: 'btn btn-circle'
|
|
||||||
}).appendTo(this.$el);
|
|
||||||
var $circleDiv = $('<i></i>', {class: 'fa'}).appendTo($btn);
|
|
||||||
if (this.model.get('status')) {
|
|
||||||
$btn.addClass('btn-success');
|
|
||||||
$circleDiv.addClass('fa-check');
|
|
||||||
} else {
|
|
||||||
$btn.addClass('btn-danger');
|
|
||||||
$circleDiv.addClass('fa-times');
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
editable: false
|
|
||||||
}),
|
|
||||||
editable: false
|
|
||||||
}, {
|
|
||||||
name: "start_time",
|
|
||||||
label: "Date",
|
|
||||||
cell: "string",
|
|
||||||
editable: false,
|
|
||||||
resizeable: true
|
|
||||||
}, {
|
|
||||||
name: "query",
|
|
||||||
label: "Query",
|
|
||||||
cell: "string",
|
|
||||||
editable: false,
|
|
||||||
resizeable: true
|
|
||||||
}, {
|
|
||||||
name: "row_affected",
|
|
||||||
label: "Rows affected",
|
|
||||||
cell: "integer",
|
|
||||||
editable: false,
|
|
||||||
resizeable: true
|
|
||||||
}, {
|
|
||||||
name: "total_time",
|
|
||||||
label: "Total Time",
|
|
||||||
cell: "string",
|
|
||||||
editable: false,
|
|
||||||
resizeable: true
|
|
||||||
}, {
|
|
||||||
name: "message",
|
|
||||||
label: "Message",
|
|
||||||
cell: "string",
|
|
||||||
editable: false,
|
|
||||||
resizeable: true
|
|
||||||
}];
|
|
||||||
|
|
||||||
|
|
||||||
// Create Collection of Backgrid columns
|
|
||||||
var columnsColl = new Backgrid.Columns(columns);
|
|
||||||
var $history_grid = self.$el.find('#history_grid');
|
|
||||||
|
|
||||||
var grid = self.history_grid = new Backgrid.Grid({
|
|
||||||
columns: columnsColl,
|
|
||||||
collection: history_collection,
|
|
||||||
className: "backgrid table-bordered presentation table backgrid-striped"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Render the grid
|
|
||||||
$history_grid.append(grid.render().$el);
|
|
||||||
|
|
||||||
var sizeAbleCol = new Backgrid.Extension.SizeAbleColumns({
|
|
||||||
collection: history_collection,
|
|
||||||
columns: columnsColl,
|
|
||||||
grid: self.history_grid
|
|
||||||
});
|
|
||||||
|
|
||||||
$history_grid.find('thead').before(sizeAbleCol.render().el);
|
|
||||||
|
|
||||||
// Add resize handlers
|
|
||||||
var sizeHandler = new Backgrid.Extension.SizeAbleColumnsHandlers({
|
|
||||||
sizeAbleColumns: sizeAbleCol,
|
|
||||||
grid: self.history_grid,
|
|
||||||
saveColumnWidth: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// sizeHandler should render only when table grid loaded completely.
|
|
||||||
setTimeout(function() {
|
|
||||||
$history_grid.find('thead').before(sizeHandler.render().el);
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
// re render sizeHandler whenever history panel tab becomes visible
|
|
||||||
self.history_panel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function(ev) {
|
|
||||||
$history_grid.find('thead').before(sizeHandler.render().el);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialized table width 0 still not calculated
|
|
||||||
var table_width = 0;
|
|
||||||
// Listen to resize events
|
|
||||||
columnsColl.on('resize',
|
|
||||||
function(columnModel, newWidth, oldWidth, offset) {
|
|
||||||
var $grid_el = $history_grid.find('table'),
|
|
||||||
tbl_orig_width = $grid_el.width(),
|
|
||||||
offset = oldWidth - newWidth,
|
|
||||||
tbl_new_width = tbl_orig_width - offset;
|
|
||||||
|
|
||||||
if (table_width == 0) {
|
|
||||||
table_width = tbl_orig_width
|
|
||||||
}
|
|
||||||
// Table new width cannot be less than original width
|
|
||||||
if (tbl_new_width >= table_width) {
|
|
||||||
$($grid_el).css('width', tbl_new_width + 'px');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// reset if calculated tbl_new_width is less than original
|
|
||||||
// table width
|
|
||||||
tbl_new_width = table_width;
|
|
||||||
$($grid_el).css('width', tbl_new_width + 'px');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Callback function for Add New Row button click.
|
// Callback function for Add New Row button click.
|
||||||
|
@ -1317,7 +1187,7 @@ define([
|
||||||
this._stopEventPropogation(ev);
|
this._stopEventPropogation(ev);
|
||||||
this._closeDropDown(ev);
|
this._closeDropDown(ev);
|
||||||
// ask for confirmation only if anything to clear
|
// ask for confirmation only if anything to clear
|
||||||
if(!self.history_collection.length) { return; }
|
if(!self.history_collection.length()) { return; }
|
||||||
|
|
||||||
alertify.confirm(gettext("Clear history"),
|
alertify.confirm(gettext("Clear history"),
|
||||||
gettext("Are you sure you wish to clear the history?"),
|
gettext("Are you sure you wish to clear the history?"),
|
||||||
|
@ -2140,11 +2010,13 @@ define([
|
||||||
$("#btn-flash").prop('disabled', false);
|
$("#btn-flash").prop('disabled', false);
|
||||||
self.trigger('pgadmin-sqleditor:loading-icon:hide');
|
self.trigger('pgadmin-sqleditor:loading-icon:hide');
|
||||||
self.gridView.history_collection.add({
|
self.gridView.history_collection.add({
|
||||||
'status' : status, 'start_time': self.query_start_time.toString(),
|
'status' : status,
|
||||||
'query': self.query, 'row_affected': self.rows_affected,
|
'start_time': self.query_start_time,
|
||||||
'total_time': self.total_time, 'message':msg
|
'query': self.query,
|
||||||
|
'row_affected': self.rows_affected,
|
||||||
|
'total_time': self.total_time,
|
||||||
|
'message':msg,
|
||||||
});
|
});
|
||||||
self.gridView.history_collection.sort();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2417,10 +2289,13 @@ define([
|
||||||
|
|
||||||
// Update the sql results in history tab
|
// Update the sql results in history tab
|
||||||
_.each(res.data.query_result, function(r) {
|
_.each(res.data.query_result, function(r) {
|
||||||
self.gridView.history_collection.add(
|
self.gridView.history_collection.add({
|
||||||
{'status' : r.status, 'start_time': self.query_start_time.toString(),
|
'status': r.status,
|
||||||
'query': r.sql, 'row_affected': r.rows_affected,
|
'start_time': self.query_start_time,
|
||||||
'total_time': self.total_time, 'message': r.result
|
'query': r.sql,
|
||||||
|
'row_affected': r.rows_affected,
|
||||||
|
'total_time': self.total_time,
|
||||||
|
'message': r.result,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
self.trigger('pgadmin-sqleditor:loading-icon:hide');
|
self.trigger('pgadmin-sqleditor:loading-icon:hide');
|
||||||
|
|
|
@ -58,5 +58,5 @@ def webdir_path():
|
||||||
|
|
||||||
def try_building_js():
|
def try_building_js():
|
||||||
with pushd(webdir_path()):
|
with pushd(webdir_path()):
|
||||||
if call(['yarn', 'run', 'bundle']) != 0:
|
if call(['yarn', 'run', 'bundle:dev']) != 0:
|
||||||
raise OSError('Error executing bundling the application')
|
raise OSError('Error executing bundling the application')
|
||||||
|
|
|
@ -61,7 +61,7 @@ class JavascriptBundlerTestCase(BaseTestGenerator):
|
||||||
self.mockOs.listdir.return_value = [u'history.js', u'reactComponents.js']
|
self.mockOs.listdir.return_value = [u'history.js', u'reactComponents.js']
|
||||||
|
|
||||||
javascriptBundler.bundle()
|
javascriptBundler.bundle()
|
||||||
self.mockSubprocess.call.assert_called_once_with(['yarn', 'run', 'bundle'])
|
self.mockSubprocess.call.assert_called_once_with(['yarn', 'run', 'bundle:dev'])
|
||||||
|
|
||||||
reportedState = javascriptBundler.report()
|
reportedState = javascriptBundler.report()
|
||||||
expectedState = self.JsState.NEW
|
expectedState = self.JsState.NEW
|
||||||
|
@ -110,7 +110,7 @@ class JavascriptBundlerTestCase(BaseTestGenerator):
|
||||||
self.mockOs.listdir.return_value = [u'history.js', u'reactComponents.js']
|
self.mockOs.listdir.return_value = [u'history.js', u'reactComponents.js']
|
||||||
|
|
||||||
javascriptBundler.bundle()
|
javascriptBundler.bundle()
|
||||||
self.mockSubprocess.call.assert_called_once_with(['yarn', 'run', 'bundle'])
|
self.mockSubprocess.call.assert_called_once_with(['yarn', 'run', 'bundle:dev'])
|
||||||
|
|
||||||
reportedState = javascriptBundler.report()
|
reportedState = javascriptBundler.report()
|
||||||
expectedState = self.JsState.OLD
|
expectedState = self.JsState.OLD
|
||||||
|
|
|
@ -11,6 +11,7 @@ import subprocess
|
||||||
import signal
|
import signal
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
class AppStarter:
|
class AppStarter:
|
||||||
""" Helper for starting the full pgadmin4 app and loading the page via
|
""" Helper for starting the full pgadmin4 app and loading the page via
|
||||||
|
@ -40,6 +41,7 @@ class AppStarter:
|
||||||
)
|
)
|
||||||
|
|
||||||
self.driver.set_window_size(1024, 1024)
|
self.driver.set_window_size(1024, 1024)
|
||||||
|
time.sleep(10)
|
||||||
self.driver.get(
|
self.driver.get(
|
||||||
"http://" + self.app_config.DEFAULT_SERVER + ":" +
|
"http://" + self.app_config.DEFAULT_SERVER + ":" +
|
||||||
random_server_port)
|
random_server_port)
|
||||||
|
|
|
@ -33,15 +33,16 @@ class PgadminPage:
|
||||||
def reset_layout(self):
|
def reset_layout(self):
|
||||||
self.click_element(self.find_by_partial_link_text("File"))
|
self.click_element(self.find_by_partial_link_text("File"))
|
||||||
self.find_by_partial_link_text("Reset Layout").click()
|
self.find_by_partial_link_text("Reset Layout").click()
|
||||||
self.click_modal_ok()
|
self.click_modal('OK')
|
||||||
self.wait_for_reloading_indicator_to_disappear()
|
self.wait_for_reloading_indicator_to_disappear()
|
||||||
|
|
||||||
def click_modal_ok(self):
|
def click_modal(self, button_text):
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
# Find active alertify dialog in case of multiple alertify dialog & click on that dialog
|
# Find active alertify dialog in case of multiple alertify dialog & click on that dialog
|
||||||
self.click_element(
|
modal_button = self.find_by_xpath(
|
||||||
self.find_by_xpath("//div[contains(@class, 'alertify') and not(contains(@class, 'ajs-hidden'))]//button[.='OK']")
|
"//div[contains(@class, 'alertify') and not(contains(@class, 'ajs-hidden'))]//button[.='%s']"
|
||||||
)
|
% button_text)
|
||||||
|
self.click_element(modal_button)
|
||||||
|
|
||||||
def add_server(self, server_config):
|
def add_server(self, server_config):
|
||||||
self.find_by_xpath("//*[@class='aciTreeText' and contains(.,'Servers')]").click()
|
self.find_by_xpath("//*[@class='aciTreeText' and contains(.,'Servers')]").click()
|
||||||
|
@ -78,10 +79,13 @@ class PgadminPage:
|
||||||
|
|
||||||
def remove_server(self, server_config):
|
def remove_server(self, server_config):
|
||||||
self.driver.switch_to.default_content()
|
self.driver.switch_to.default_content()
|
||||||
self.find_by_xpath("//*[@id='tree']//*[.='" + server_config['name'] + "' and @class='aciTreeItem']").click()
|
server_to_remove = self.find_by_xpath("//*[@id='tree']//*[.='" + server_config['name'] + "' and @class='aciTreeItem']")
|
||||||
self.find_by_partial_link_text("Object").click()
|
self.click_element(server_to_remove)
|
||||||
self.find_by_partial_link_text("Delete/Drop").click()
|
object_menu_item = self.find_by_partial_link_text("Object")
|
||||||
self.click_modal_ok()
|
self.click_element(object_menu_item)
|
||||||
|
delete_menu_item = self.find_by_partial_link_text("Delete/Drop")
|
||||||
|
self.click_element(delete_menu_item)
|
||||||
|
self.click_modal('OK')
|
||||||
|
|
||||||
def select_tree_item(self, tree_item_text):
|
def select_tree_item(self, tree_item_text):
|
||||||
self.find_by_xpath("//*[@id='tree']//*[.='" + tree_item_text + "' and @class='aciTreeItem']").click()
|
self.find_by_xpath("//*[@id='tree']//*[.='" + tree_item_text + "' and @class='aciTreeItem']").click()
|
||||||
|
@ -130,6 +134,7 @@ class PgadminPage:
|
||||||
)
|
)
|
||||||
|
|
||||||
def click_element(self, element):
|
def click_element(self, element):
|
||||||
|
# driver must be here to adhere to the method contract in selenium.webdriver.support.wait.WebDriverWait.until()
|
||||||
def click_succeeded(driver):
|
def click_succeeded(driver):
|
||||||
try:
|
try:
|
||||||
element.click()
|
element.click()
|
||||||
|
@ -175,8 +180,9 @@ class PgadminPage:
|
||||||
time.sleep(sleep_time)
|
time.sleep(sleep_time)
|
||||||
|
|
||||||
def click_tab(self, tab_name):
|
def click_tab(self, tab_name):
|
||||||
self.find_by_xpath("//*[contains(@class,'wcTabTop')]//*[contains(@class,'wcPanelTab') "
|
tab = self.find_by_xpath("//*[contains(@class,'wcTabTop')]//*[contains(@class,'wcPanelTab') "
|
||||||
"and contains(.,'" + tab_name + "')]").click()
|
"and contains(.,'" + tab_name + "')]")
|
||||||
|
self.click_element(tab)
|
||||||
|
|
||||||
def wait_for_input_field_content(self, field_name, content):
|
def wait_for_input_field_content(self, field_name, content):
|
||||||
def input_field_has_content(driver):
|
def input_field_has_content(driver):
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// pgAdmin 4 - PostgreSQL Tools
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 - 2017, The pgAdmin Development Team
|
||||||
|
// This software is released under the PostgreSQL Licence
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
import HistoryCollection from '../../../pgadmin/static/js/history/history_collection';
|
||||||
|
|
||||||
|
describe('historyCollection', function () {
|
||||||
|
let historyCollection, historyModel, onChangeSpy;
|
||||||
|
beforeEach(() => {
|
||||||
|
historyModel = [{some: 'thing', someOther: ['array element']}];
|
||||||
|
historyCollection = new HistoryCollection(historyModel);
|
||||||
|
onChangeSpy = jasmine.createSpy('onChangeHandler');
|
||||||
|
|
||||||
|
historyCollection.onChange(onChangeSpy);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('length', function () {
|
||||||
|
it('returns 0 when underlying history model has no elements', function () {
|
||||||
|
historyCollection = new HistoryCollection([]);
|
||||||
|
|
||||||
|
expect(historyCollection.length()).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the length of the underlying history model', function () {
|
||||||
|
expect(historyCollection.length()).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('add', function () {
|
||||||
|
let expectedHistory;
|
||||||
|
beforeEach(() => {
|
||||||
|
historyCollection.add({some: 'new thing', someOther: ['value1', 'value2']});
|
||||||
|
|
||||||
|
expectedHistory = [
|
||||||
|
{some: 'thing', someOther: ['array element']},
|
||||||
|
{some: 'new thing', someOther: ['value1', 'value2']},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds a passed entry', function () {
|
||||||
|
expect(historyCollection.historyList).toEqual(expectedHistory);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the onChange function', function () {
|
||||||
|
expect(onChangeSpy).toHaveBeenCalledWith(expectedHistory);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('reset', function () {
|
||||||
|
beforeEach(() => {
|
||||||
|
historyCollection.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drops the history', function () {
|
||||||
|
expect(historyCollection.historyList).toEqual([]);
|
||||||
|
expect(historyCollection.length()).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the onChange function', function () {
|
||||||
|
expect(onChangeSpy).toHaveBeenCalledWith([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sort', function () {
|
||||||
|
it('doesn\'t sort');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when instantiated', function () {
|
||||||
|
describe('from a history model', function () {
|
||||||
|
it('has the historyModel', () => {
|
||||||
|
let content = historyCollection.historyList;
|
||||||
|
|
||||||
|
expect(content).toEqual(historyModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,50 @@
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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 QueryHistoryEntry from '../../../pgadmin/static/jsx/history/query_history_entry';
|
||||||
|
|
||||||
|
import {mount} from 'enzyme';
|
||||||
|
import jasmineEnzyme from 'jasmine-enzyme';
|
||||||
|
|
||||||
|
describe('QueryHistoryEntry', () => {
|
||||||
|
let historyWrapper;
|
||||||
|
beforeEach(() => {
|
||||||
|
jasmineEnzyme();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('for a failed query', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const historyEntry = {
|
||||||
|
query: 'second sql statement',
|
||||||
|
start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
|
||||||
|
status: false,
|
||||||
|
};
|
||||||
|
historyWrapper = mount(<QueryHistoryEntry historyEntry={historyEntry}/>);
|
||||||
|
});
|
||||||
|
it('displays a pink background color', () => {
|
||||||
|
expect(historyWrapper.find('div').first()).toHaveStyle('backgroundColor', '#F7D0D5');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('for a successful query', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const historyEntry = {
|
||||||
|
query: 'second sql statement',
|
||||||
|
start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
|
||||||
|
status: true,
|
||||||
|
};
|
||||||
|
historyWrapper = mount(<QueryHistoryEntry historyEntry={historyEntry}/>);
|
||||||
|
});
|
||||||
|
it('does not display a pink background color', () => {
|
||||||
|
expect(historyWrapper.find('div').first()).toHaveStyle('backgroundColor', '#FFF');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,103 @@
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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 QueryHistory from '../../../pgadmin/static/jsx/history/query_history';
|
||||||
|
import QueryHistoryEntry from '../../../pgadmin/static/jsx/history/query_history_entry';
|
||||||
|
import HistoryCollection from '../../../pgadmin/static/js/history/history_collection';
|
||||||
|
import jasmineEnzyme from 'jasmine-enzyme';
|
||||||
|
|
||||||
|
import {mount, shallow} from 'enzyme';
|
||||||
|
|
||||||
|
describe('QueryHistory', () => {
|
||||||
|
let historyWrapper;
|
||||||
|
beforeEach(() => {
|
||||||
|
jasmineEnzyme();
|
||||||
|
const historyCollection = new HistoryCollection([]);
|
||||||
|
historyWrapper = shallow(<QueryHistory historyCollection={historyCollection}/>);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('on construction', () => {
|
||||||
|
it('has no entries', (done) => {
|
||||||
|
let foundChildren = historyWrapper.find(QueryHistoryEntry);
|
||||||
|
expect(foundChildren.length).toBe(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when it has history', () => {
|
||||||
|
describe('when two SQL queries were executed', () => {
|
||||||
|
let foundChildren;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const historyObjects = [
|
||||||
|
{
|
||||||
|
query: 'second sql statement',
|
||||||
|
start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
|
||||||
|
status: false,
|
||||||
|
row_affected: 1,
|
||||||
|
total_time: '234 msec',
|
||||||
|
message: 'some other message',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
query: 'first sql statement',
|
||||||
|
start_time: new Date(2017, 5, 3, 14, 3, 15, 150),
|
||||||
|
status: true,
|
||||||
|
row_affected: 2,
|
||||||
|
total_time: '14 msec',
|
||||||
|
message: 'a very important message',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const historyCollection = new HistoryCollection(historyObjects);
|
||||||
|
|
||||||
|
historyWrapper = mount(<QueryHistory historyCollection={historyCollection}/>);
|
||||||
|
|
||||||
|
foundChildren = historyWrapper.find(QueryHistoryEntry);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has two query history entries', () => {
|
||||||
|
expect(foundChildren.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the SQL of the queries in order', () => {
|
||||||
|
expect(foundChildren.at(0).text()).toContain('first sql statement');
|
||||||
|
expect(foundChildren.at(1).text()).toContain('second sql statement');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the formatted timestamp of the queries in chronological order by most recent first', () => {
|
||||||
|
expect(foundChildren.at(0).text()).toContain('Jun 3 2017 – 14:03:15');
|
||||||
|
expect(foundChildren.at(1).text()).toContain('Dec 11 2016 – 01:33:05');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the number of rows affected', () => {
|
||||||
|
expect(foundChildren.at(1).text()).toContain('1 rows affected');
|
||||||
|
expect(foundChildren.at(0).text()).toContain('2 rows affected');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the total time', () => {
|
||||||
|
expect(foundChildren.at(0).text()).toContain('total time: 14 msec');
|
||||||
|
expect(foundChildren.at(1).text()).toContain('total time: 234 msec');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the truncated message', () => {
|
||||||
|
expect(foundChildren.at(0).text()).toContain('a very important message');
|
||||||
|
expect(foundChildren.at(1).text()).toContain('some other message');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when there are one failing and one successful query each', () => {
|
||||||
|
it('adds a white background color for the successful query', () => {
|
||||||
|
expect(foundChildren.at(0).find('div').first()).toHaveStyle('backgroundColor', '#FFF');
|
||||||
|
});
|
||||||
|
it('adds a red background color for the failed query', () => {
|
||||||
|
expect(foundChildren.at(1).find('div').first()).toHaveStyle('backgroundColor', '#F7D0D5');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -227,6 +227,7 @@ def create_constraint(
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc(file=sys.stderr)
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def create_debug_function(server, db_name, function_name="test_func"):
|
def create_debug_function(server, db_name, function_name="test_func"):
|
||||||
try:
|
try:
|
||||||
connection = get_db_connection(db_name,
|
connection = get_db_connection(db_name,
|
||||||
|
@ -305,6 +306,7 @@ def drop_database(connection, database_name):
|
||||||
connection.commit()
|
connection.commit()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
def drop_tablespace(connection):
|
def drop_tablespace(connection):
|
||||||
"""This function used to drop the tablespace"""
|
"""This function used to drop the tablespace"""
|
||||||
pg_cursor = connection.cursor()
|
pg_cursor = connection.cursor()
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
/* eslint-env node */
|
/* eslint-env node */
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
context: __dirname + '/pgadmin/static/jsx',
|
context: __dirname + '/pgadmin/static',
|
||||||
entry: './components.jsx',
|
entry: {
|
||||||
|
reactComponents: './jsx/components.jsx',
|
||||||
|
history: './js/history/index.js',
|
||||||
|
},
|
||||||
output: {
|
output: {
|
||||||
libraryTarget: 'amd',
|
libraryTarget: 'amd',
|
||||||
path: __dirname + '/pgadmin/static/js/generated',
|
path: __dirname + '/pgadmin/static/js/generated',
|
||||||
filename: 'reactComponents.js',
|
filename: '[name].js',
|
||||||
},
|
},
|
||||||
|
|
||||||
module: {
|
module: {
|
||||||
rules: [{
|
rules: [{
|
||||||
test: /\.jsx?$/,
|
test: /\.jsx?$/,
|
||||||
exclude: /node_modules/,
|
exclude: [/node_modules/, /vendor/],
|
||||||
use: {
|
use: {
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
|
|
|
@ -22,7 +22,7 @@ module.exports = {
|
||||||
use: {
|
use: {
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
presets: ['es2015'],
|
presets: ['es2015', 'react'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -51,6 +51,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
|
extensions: ['.js', '.jsx'],
|
||||||
alias: {
|
alias: {
|
||||||
'alertify': sourcesDir + '/vendor/alertifyjs/alertify',
|
'alertify': sourcesDir + '/vendor/alertifyjs/alertify',
|
||||||
'jquery': sourcesDir + '/vendor/jquery/jquery-1.11.2',
|
'jquery': sourcesDir + '/vendor/jquery/jquery-1.11.2',
|
||||||
|
@ -67,4 +68,11 @@ module.exports = {
|
||||||
'pgadmin': sourcesDir + '/js/pgadmin',
|
'pgadmin': sourcesDir + '/js/pgadmin',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
externals: {
|
||||||
|
'react/addons': true,
|
||||||
|
'react/lib/ReactContext': true,
|
||||||
|
'react/lib/ExecutionEnvironment': true,
|
||||||
|
'react-dom/test-utils': true,
|
||||||
|
'react-test-renderer/shallow': true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1371,6 +1371,12 @@ decamelize@^1.0.0, decamelize@^1.1.1:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||||
|
|
||||||
|
deep-equal-ident@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz#06f4b89e53710cd6cea4a7781c7a956642de8dc9"
|
||||||
|
dependencies:
|
||||||
|
lodash.isequal "^3.0"
|
||||||
|
|
||||||
deep-extend@~0.4.0:
|
deep-extend@~0.4.0:
|
||||||
version "0.4.2"
|
version "0.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
|
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
|
||||||
|
@ -1608,6 +1614,12 @@ entities@^1.1.1, entities@~1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
|
||||||
|
|
||||||
|
enzyme-matchers@^3.1.0, enzyme-matchers@^3.2.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/enzyme-matchers/-/enzyme-matchers-3.2.0.tgz#4718779a3b9eb5e8ebad46804f8d3e66045d0181"
|
||||||
|
dependencies:
|
||||||
|
deep-equal-ident "^1.1.1"
|
||||||
|
|
||||||
enzyme@~2.8.2:
|
enzyme@~2.8.2:
|
||||||
version "2.8.2"
|
version "2.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.8.2.tgz#6c8bcb05012abc4aa4bc3213fb23780b9b5b1714"
|
resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.8.2.tgz#6c8bcb05012abc4aa4bc3213fb23780b9b5b1714"
|
||||||
|
@ -2299,6 +2311,12 @@ ignore@^3.2.0:
|
||||||
version "3.3.3"
|
version "3.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
|
||||||
|
|
||||||
|
immutability-helper@^2.2.0:
|
||||||
|
version "2.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-2.2.2.tgz#e7e9da728b3de2fad34a216f4157b326dbccc892"
|
||||||
|
dependencies:
|
||||||
|
invariant "^2.2.0"
|
||||||
|
|
||||||
"imports-loader@git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d":
|
"imports-loader@git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d":
|
||||||
version "0.7.1"
|
version "0.7.1"
|
||||||
resolved "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d"
|
resolved "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d"
|
||||||
|
@ -2568,6 +2586,12 @@ jasmine-core@~2.5.2:
|
||||||
version "2.5.2"
|
version "2.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6cab6ff297"
|
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6cab6ff297"
|
||||||
|
|
||||||
|
jasmine-enzyme@^3.1.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jasmine-enzyme/-/jasmine-enzyme-3.2.0.tgz#0eeb370d4fa965db03e04347ca9c4ed5a60fadc2"
|
||||||
|
dependencies:
|
||||||
|
enzyme-matchers "^3.2.0"
|
||||||
|
|
||||||
jodid25519@^1.0.0:
|
jodid25519@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967"
|
resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967"
|
||||||
|
@ -2828,6 +2852,22 @@ loader-utils@^1.0.2:
|
||||||
emojis-list "^2.0.0"
|
emojis-list "^2.0.0"
|
||||||
json5 "^0.5.0"
|
json5 "^0.5.0"
|
||||||
|
|
||||||
|
lodash._baseisequal@^3.0.0:
|
||||||
|
version "3.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz#d8025f76339d29342767dcc887ce5cb95a5b51f1"
|
||||||
|
dependencies:
|
||||||
|
lodash.isarray "^3.0.0"
|
||||||
|
lodash.istypedarray "^3.0.0"
|
||||||
|
lodash.keys "^3.0.0"
|
||||||
|
|
||||||
|
lodash._bindcallback@^3.0.0:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
|
||||||
|
|
||||||
|
lodash._getnative@^3.0.0:
|
||||||
|
version "3.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
|
||||||
|
|
||||||
lodash.assignin@^4.0.9:
|
lodash.assignin@^4.0.9:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
|
resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
|
||||||
|
@ -2852,6 +2892,33 @@ lodash.foreach@^4.3.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
|
resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
|
||||||
|
|
||||||
|
lodash.isarguments@^3.0.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
|
||||||
|
|
||||||
|
lodash.isarray@^3.0.0:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
|
||||||
|
|
||||||
|
lodash.isequal@^3.0:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-3.0.4.tgz#1c35eb3b6ef0cd1ff51743e3ea3cf7fdffdacb64"
|
||||||
|
dependencies:
|
||||||
|
lodash._baseisequal "^3.0.0"
|
||||||
|
lodash._bindcallback "^3.0.0"
|
||||||
|
|
||||||
|
lodash.istypedarray@^3.0.0:
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8494d283b87c39281cef62"
|
||||||
|
|
||||||
|
lodash.keys@^3.0.0:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
|
||||||
|
dependencies:
|
||||||
|
lodash._getnative "^3.0.0"
|
||||||
|
lodash.isarguments "^3.0.0"
|
||||||
|
lodash.isarray "^3.0.0"
|
||||||
|
|
||||||
lodash.map@^4.4.0:
|
lodash.map@^4.4.0:
|
||||||
version "4.6.0"
|
version "4.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
|
resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
|
||||||
|
@ -3013,6 +3080,10 @@ module-deps@^4.0.8:
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
xtend "^4.0.0"
|
xtend "^4.0.0"
|
||||||
|
|
||||||
|
moment@^2.18.1:
|
||||||
|
version "2.18.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
|
||||||
|
|
||||||
ms@0.7.1:
|
ms@0.7.1:
|
||||||
version "0.7.1"
|
version "0.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
|
||||||
|
@ -3499,10 +3570,10 @@ randomatic@^1.1.3:
|
||||||
kind-of "^3.0.2"
|
kind-of "^3.0.2"
|
||||||
|
|
||||||
randombytes@^2.0.0, randombytes@^2.0.1:
|
randombytes@^2.0.0, randombytes@^2.0.1:
|
||||||
version "2.0.4"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.4.tgz#9551df208422c8f80eb58e2326dd0b840ff22efd"
|
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4b1b79"
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.1.0"
|
||||||
|
|
||||||
range-parser@^1.0.3, range-parser@^1.2.0:
|
range-parser@^1.0.3, range-parser@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
|
@ -3818,7 +3889,7 @@ rx-lite@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
|
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
|
||||||
|
|
||||||
safe-buffer@^5.0.1:
|
safe-buffer@^5.0.1, safe-buffer@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue