public inbox for [email protected]
help / color / mirror / Atom feedFrom: Surinder Kumar <[email protected]>
To: Dave Page <[email protected]>
Cc: pgadmin-hackers <[email protected]>
Subject: Re: [pgAdmin4][Patch][RM2257]: Query tool - Insert row doesn't use default values
Date: Fri, 28 Apr 2017 14:49:58 +0530
Message-ID: <CAM5-9D8MdAqvix74_K+eMkeGkP7r4Aost1Uqz4hupMRPxwqzAw@mail.gmail.com> (raw)
In-Reply-To: <CA+OCxoySjV87N+YEkhXRzWaaGNCX4o+KuKnVGrYhLuWYeDx+SA@mail.gmail.com>
References: <CAM5-9D_SSL81uT4AqsRr8WPABWA6S-iE34OxLb8YqfVN3myeJg@mail.gmail.com>
<CA+OCxoySjV87N+YEkhXRzWaaGNCX4o+KuKnVGrYhLuWYeDx+SA@mail.gmail.com>
List-Unsubscribe: <mailto:[email protected]?body=unsub%20pgadmin-hackers>
Hi Dave,
Please find updated patch for RM case and a separate patch for Feature
tests.
*Python:*
- Added [default] label for cells with default values while inserting a new
row.
- Introduced a FieldValidator function for cells that don't allow null
values. If user tries to insert null value, field with be highlighted with
red borders around.
-
If a cell contains blank string('') and when we set it to null, the change
into the cell is not detected. It was because the comparison
for (defaultValue == null) return true if defaultValue is undefined. Hence
_.isNull(value) is used to fix this.
*Feature Test cases:*
- Introduced a new method create_table_with_query(server, db_name, query)
in test_utils.py which executes the given query on connected server.
- Added a new file test_data.json that has test data for test cases.
On Fri, Apr 7, 2017 at 2:21 PM, Dave Page <[email protected]> wrote:
> Hi
>
> On Sat, Apr 1, 2017 at 12:45 PM, Surinder Kumar
> <[email protected]> wrote:
> > Hi
> >
> > Issues fixed:
> >
> > 1. If a column is defined with a default modifier, there is now way to
> > insert the row with those defaults.
> > The column will be left blank and it will take default value
> automatically.
> >
> > 2. If a column has a not-null constraint then an error is returned and
> the
> > row is not inserted.
> > The column will be left blank
> >
> > The default values for new added rows will be displayed on
> refresh/execute.
> >
> > Please find attached patch and review.
>
> This largely works as expected, but there is some weirdness. I have a
> test table that looks like this:
>
> CREATE TABLE public.defaults
> (
> id bigint NOT NULL DEFAULT nextval('defaults_id_seq'::regclass),
> data_default_nulls text COLLATE pg_catalog."default" DEFAULT
> 'abc123'::text,
> data_default_no_nulls text COLLATE pg_catalog."default" NOT NULL
> DEFAULT 'def456'::text,
> data_nulls text COLLATE pg_catalog."default",
> data_no_nulls text COLLATE pg_catalog."default" NOT NULL,
> CONSTRAINT defaults_pkey PRIMARY KEY (id)
> )
>
> Remember that the expected behaviour is:
>
> - Set a value to empty to update the column to null.
> - Set a value to '' to update the column to an empty string
> - Set a value to anything else to update the column to that value
>
> 1) In a row with values in each column, if I try to set the value of
> data_default_nulls to null, the query executed is:
>
> UPDATE public.defaults SET
> data_default_nulls = '' WHERE
> id = '2';
>
> 2) If I do the same in the data_nulls column, the value is immediately
> shown as [null] and the query executed is:
>
> UPDATE public.defaults SET
> data_nulls = NULL WHERE
> id = '2';
>
> 3) If I then edit the value in data_default_nulls, it shows the
> current value as ''. Removing the quotes (to set it to null) doesn't
> get detected as a change.
>
Taken care.
>
> 4) When I manually executed "update defaults set data_default_nulls =
> null where id = 2" in a query tool window, I got:
>
> 2017-04-07 09:43:02,987: INFO werkzeug: 127.0.0.1 - - [07/Apr/2017
> 09:43:02] "GET /sqleditor/columns/8745675 HTTP/1.1" 500 -
> Traceback (most recent call last):
> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
> packages/flask/app.py",
> line 2000, in __call__
> return self.wsgi_app(environ, start_response)
> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
> packages/flask/app.py",
> line 1991, in wsgi_app
> response = self.make_response(self.handle_exception(e))
> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
> packages/flask/app.py",
> line 1567, in handle_exception
> reraise(exc_type, exc_value, tb)
> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
> packages/flask/app.py",
> line 1988, in wsgi_app
> response = self.full_dispatch_request()
> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
> packages/flask/app.py",
> line 1641, in full_dispatch_request
> rv = self.handle_user_exception(e)
> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
> packages/flask/app.py",
> line 1544, in handle_user_exception
> reraise(exc_type, exc_value, tb)
> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
> packages/flask/app.py",
> line 1639, in full_dispatch_request
> rv = self.dispatch_request()
> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
> packages/flask/app.py",
> line 1625, in dispatch_request
> return self.view_functions[rule.endpoint](**req.view_args)
> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
> packages/flask_login.py",
> line 792, in decorated_view
> return func(*args, **kwargs)
> File "/Users/dpage/git/pgadmin4/web/pgadmin/tools/sqleditor/__
> init__.py",
> line 452, in get_columns
> tid=command_obj.obj_id)
> AttributeError: 'QueryToolCommand' object has no attribute 'obj_id'
>
Fixed.
>
> 5) When I run the query again in pgAdmin III, then refresh the data in
> pgAdmin 4, the data_default_nulls column is displayed without the
> [null] marker (despite having a null value, which I confirmed in
> pgAdmin 3).
>
Fixed.
>
> I'm sure there are other combinations of issues here. Please fix and
> thoroughly re-test to ensure behaviour is consistent - and to avoid
> future issues, please add some appropriate feature tests to check
> nulls, defaults and empty strings are properly handled in view, insert
> and updates. Murtuza recently wrote some feature tests for the query
> tool - you should be able to use those as a starting point.
>
Added feature tests
>
> Thanks.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] RM_2257_v1.patch (10.3K, 3-RM_2257_v1.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql
index 759e657..f3353d6 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql
@@ -1,4 +1,5 @@
-SELECT att.attname as name, att.attnum as OID, format_type(ty.oid,NULL) AS datatype
+SELECT att.attname as name, att.attnum as OID, format_type(ty.oid,NULL) AS datatype,
+att.attnotnull as not_null, att.atthasdef as has_default_val
FROM pg_attribute att
JOIN pg_type ty ON ty.oid=atttypid
JOIN pg_namespace tn ON tn.oid=ty.typnamespace
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/default/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/default/nodes.sql
index 7536a9c..4f1de2a 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/default/nodes.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/default/nodes.sql
@@ -1,4 +1,5 @@
-SELECT att.attname as name, att.attnum as OID, format_type(ty.oid,NULL) AS datatype
+SELECT att.attname as name, att.attnum as OID, format_type(ty.oid,NULL) AS datatype,
+att.attnotnull as not_null, att.atthasdef as has_default_val
FROM pg_attribute att
JOIN pg_type ty ON ty.oid=atttypid
JOIN pg_namespace tn ON tn.oid=ty.typnamespace
diff --git a/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js b/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js
index cdfba4d..0505f40 100644
--- a/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js
+++ b/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js
@@ -110,7 +110,12 @@
// When text editor opens
this.loadValue = function (item) {
- if (item[args.column.pos] === "") {
+ var col = args.column;
+
+ if (_.isUndefined(item[args.column.pos]) && col.has_default_val) {
+ $input.val("");
+ }
+ else if (item[args.column.pos] === "") {
$input.val("''");
}
else {
@@ -145,7 +150,10 @@
};
this.isValueChanged = function () {
- return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
+ // Use _.isNull(value) for comparison for null instead of
+ // defaultValue == null, because it returns true for undefined value.
+ return (!($input.val() == "" && _.isNull(defaultValue))) &&
+ ($input.val() != defaultValue);
};
this.validate = function () {
diff --git a/web/pgadmin/static/js/slickgrid/slick.pgadmin.formatters.js b/web/pgadmin/static/js/slickgrid/slick.pgadmin.formatters.js
index 290bddd..bfd66e8 100644
--- a/web/pgadmin/static/js/slickgrid/slick.pgadmin.formatters.js
+++ b/web/pgadmin/static/js/slickgrid/slick.pgadmin.formatters.js
@@ -66,7 +66,14 @@
}
function TextFormatter(row, cell, value, columnDef, dataContext) {
- if (_.isUndefined(value) || value === null) {
+ // If column has default value, set placeholder
+ if (_.isUndefined(value) && columnDef.has_default_val) {
+ return "<span class='pull-left'>[default]</span>";
+ }
+ else if (_.isUndefined(value) && columnDef.not_null) {
+ return ''; // If null value not allowed, set cell to blank
+ }
+ else if (_.isUndefined(value) || _.isNull(value)) {
return "<span class='pull-left'>[null]</span>";
}
else {
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index d114988..f7466d8 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -440,8 +440,23 @@ def get_columns(trans_id):
columns = dict()
columns_info = None
primary_keys = None
+ rset = None
status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id)
if status and conn is not None and session_obj is not None:
+
+ ver = conn.manager.version
+ # Get the template path for the column
+ template_path = 'column/sql/#{0}#'.format(ver)
+ command_obj = pickle.loads(session_obj['command_obj'])
+ if hasattr(command_obj, 'obj_id'):
+ SQL = render_template("/".join([template_path,
+ 'nodes.sql']),
+ tid=command_obj.obj_id)
+ # rows with attribute not_null
+ status, rset = conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
# Check PK column info is available or not
if 'primary_keys' in session_obj:
primary_keys = session_obj['primary_keys']
@@ -449,10 +464,17 @@ def get_columns(trans_id):
# Fetch column information
columns_info = conn.get_column_info()
if columns_info is not None:
- for col in columns_info:
+ for key, col in enumerate(columns_info):
col_type = dict()
col_type['type_code'] = col['type_code']
col_type['type_name'] = None
+ if rset:
+ col_type['not_null'] = col['not_null'] = \
+ rset['rows'][key]['not_null']
+
+ col_type['has_default_val'] = col['has_default_val'] = \
+ rset['rows'][key]['has_default_val']
+
columns[col['name']] = col_type
# As we changed the transaction object we need to
@@ -602,6 +624,7 @@ def save(trans_id):
status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id)
if status and conn is not None \
and trans_obj is not None and session_obj is not None:
+ setattr(trans_obj, 'columns_info', session_obj['columns_info'])
# If there is no primary key found then return from the function.
if len(session_obj['primary_keys']) <= 0 or len(changed_data) <= 0:
diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py
index be7f21f..a9ff617 100644
--- a/web/pgadmin/tools/sqleditor/command.py
+++ b/web/pgadmin/tools/sqleditor/command.py
@@ -442,6 +442,23 @@ class TableCommand(GridCommand):
# For newly added rows
if of_type == 'added':
+
+ # When new rows are added, only changed columns data is
+ # sent from client side. But if column is not_null and has
+ # no_default_value, set column to blank, instead
+ # of not null which is set by default.
+ column_data = {}
+ column_type = {}
+ for each_col in self.columns_info:
+ if (
+ self.columns_info[each_col]['not_null'] and
+ not self.columns_info[each_col][
+ 'has_default_val']
+ ):
+ column_data[each_col] = ""
+ column_type[each_col] =\
+ self.columns_info[each_col]['type_name']
+
for each_row in changed_data[of_type]:
data = changed_data[of_type][each_row]['data']
# Remove our unique tracking key
@@ -450,12 +467,18 @@ class TableCommand(GridCommand):
data_type = set_column_names(changed_data[of_type][each_row]['data_type'])
list_of_rowid.append(data.get('__temp_PK'))
+ # Update columns value and data type
+ # with columns having not_null=False and has
+ # no default value
+ column_data.update(data)
+ column_type.update(data_type)
+
sql = render_template("/".join([self.sql_path, 'insert.sql']),
- data_to_be_saved=data,
+ data_to_be_saved=column_data,
primary_keys=None,
object_name=self.object_name,
nsp_name=self.nsp_name,
- data_type=data_type)
+ data_type=column_type)
list_of_sql.append(sql)
# For updated rows
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js
index 2062aa2..ad9a565 100644
--- a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js
@@ -526,6 +526,13 @@ define(
render_grid: function(collection, columns, is_editable) {
var self = this;
+ var requiredFieldValidator = function (value) {
+ if (value == null || value == undefined || !value.length) {
+ return {valid: false, msg: "This is a required field"};
+ } else {
+ return {valid: true, msg: null};
+ }
+ }
// This will work as data store and holds all the
// inserted/updated/deleted data from grid
self.handler.data_store = {
@@ -557,7 +564,10 @@ define(
id: c.name,
pos: c.pos,
field: c.name,
- name: c.label
+ name: c.label,
+ not_null: c.not_null,
+ has_default_val: c.has_default_val,
+ validator: c.not_null ? requiredFieldValidator : null
};
// Get the columns width based on data type
@@ -2077,7 +2087,9 @@ define(
'label': column_label,
'cell': col_cell,
'can_edit': self.can_edit,
- 'type': type
+ 'type': type,
+ 'not_null': c.not_null,
+ 'has_default_val': c.has_default_val
};
columns.push(col);
});
[application/octet-stream] features_test_cases_RM_2257.patch (15.7K, 4-features_test_cases_RM_2257.patch)
download | inline diff:
diff --git a/web/pgadmin/feature_tests/test_data.json b/web/pgadmin/feature_tests/test_data.json
new file mode 100644
index 0000000..7100048
--- /dev/null
+++ b/web/pgadmin/feature_tests/test_data.json
@@ -0,0 +1,23 @@
+{
+ "table_insert_update_cases": {
+ "insert": {
+ "insert_with_defaults": {
+ "id": "1",
+ "data_default_nulls": "abc123",
+ "data_default_no_nulls": "def456"
+ }
+ },
+ "update": {
+ "update_with_null_value": {
+ "id": "3",
+ "data_default_nulls": "",
+ "data_nulls": ""
+ },
+ "update_with_empty_string": {
+ "id": "2",
+ "data_default_nulls": "''",
+ "data_nulls": "''"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/pgadmin/feature_tests/view_data_dml_queries.py b/web/pgadmin/feature_tests/view_data_dml_queries.py
new file mode 100644
index 0000000..5c45071
--- /dev/null
+++ b/web/pgadmin/feature_tests/view_data_dml_queries.py
@@ -0,0 +1,301 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+import os
+from selenium.webdriver import ActionChains
+from regression.python_test_utils import test_utils
+from regression.feature_utils.base_feature_test import BaseFeatureTest
+import time
+from selenium.webdriver.common.keys import Keys
+
+
+CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
+
+try:
+ with open(CURRENT_PATH + '/test_data.json') as data_file:
+ config_data = json.load(data_file)['table_insert_update_cases']
+except Exception as e:
+ print(str(e))
+
+
+class CheckForViewDataTest(BaseFeatureTest):
+ """
+ Test cases to validate insert, update operations in table
+ with input test data
+
+ First of all, the test data is inserted/updated into table and then
+ inserted data is compared with original data to check if expected data
+ is returned from table or not.
+
+ We will cover test cases for,
+ 1) Insert with default values
+ 2) Update with null values
+ 3) Update with blank string
+ 4) Delete table row
+ """
+
+ scenarios = [
+ ("Validate Insert, Update operations in View data with given test "
+ "data",
+ dict())
+ ]
+
+ # To create column id with nextval, first a sequence must be created.
+ create_sequence = """
+CREATE SEQUENCE public.defaults_id_seq
+ INCREMENT 1
+ START 9
+ MINVALUE 1
+ MAXVALUE 9223372036854775807
+ CACHE 1;
+
+ALTER SEQUENCE public.defaults_id_seq
+ OWNER TO postgres;
+ """
+
+ # query for creating 'defaults' table
+ create_table_query = """
+CREATE TABLE public.defaults
+(
+ id bigint NOT NULL DEFAULT nextval('defaults_id_seq'::regclass),
+ data_default_nulls text COLLATE pg_catalog."default" DEFAULT 'abc123'::text,
+ data_default_no_nulls text COLLATE pg_catalog."default" NOT NULL
+DEFAULT 'def456'::text,
+ data_nulls text COLLATE pg_catalog."default",
+ data_no_nulls text COLLATE pg_catalog."default" NOT NULL,
+ CONSTRAINT defaults_pkey PRIMARY KEY (id)
+)
+ """
+
+ 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_with_query(
+ self.server,
+ "acceptance_test_db",
+ CheckForViewDataTest.create_sequence)
+
+ test_utils.create_table_with_query(
+ self.server,
+ "acceptance_test_db",
+ CheckForViewDataTest.create_table_query)
+
+ def runTest(self):
+ self.page.wait_for_spinner_to_disappear()
+ self._connects_to_server()
+ self._tables_node_expandable()
+
+ # Open Object -> View data
+ self._check_xss_in_view_data()
+
+ # Run test to insert a new row in table with default values
+ self._insert_row_in_table()
+
+ # Run test to update a row in table with null values
+ self._update_row_in_table()
+
+ # Run test case to remove existed row
+ self._remove_row()
+
+ def after(self):
+ time.sleep(1)
+ 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")
+
+ def _connects_to_server(self):
+ self.page.find_by_xpath("//*[@class='aciTreeText' and .='Servers']").click()
+ self.page.driver.find_element_by_link_text("Object").click()
+ ActionChains(self.page.driver) \
+ .move_to_element(self.page.driver.find_element_by_link_text("Create")) \
+ .perform()
+ self.page.find_by_partial_link_text("Server...").click()
+
+ server_config = self.server
+ self.page.fill_input_by_field_name("name", server_config['name'])
+ self.page.find_by_partial_link_text("Connection").click()
+ self.page.fill_input_by_field_name("host", server_config['host'])
+ self.page.fill_input_by_field_name("port", server_config['port'])
+ self.page.fill_input_by_field_name("username", server_config['username'])
+ self.page.fill_input_by_field_name("password", server_config['db_password'])
+ self.page.find_by_xpath("//button[contains(.,'Save')]").click()
+
+ def _tables_node_expandable(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')
+ self.page.toggle_open_tree_item('Schemas')
+ self.page.toggle_open_tree_item('public')
+ self.page.toggle_open_tree_item('Tables')
+ self.page.select_tree_item("defaults")
+
+ def _check_xss_in_view_data(self):
+ self.page.driver.find_element_by_link_text("Object").click()
+ ActionChains(self.page.driver) \
+ .move_to_element(
+ self.page.driver.find_element_by_link_text("View Data")) \
+ .perform()
+ self.page.find_by_partial_link_text("View All Rows").click()
+ time.sleep(3)
+ self.page.driver.switch_to.frame(
+ self.page.driver.find_element_by_tag_name('iframe')
+ )
+
+ def _insert_row_in_table(self):
+ xpath_col1 = "//div[contains(@class, 'new-row')]//div[" \
+ "contains(@class, 'r1')]"
+ time.sleep(1)
+ new_row = self.page.find_by_xpath(xpath_col1)
+ new_row.click()
+ field = new_row.find_element_by_xpath(".//input")
+ field.click()
+ ActionChains(self.driver).send_keys(
+ config_data['insert']['insert_with_defaults']['id']
+ ).perform()
+ field.send_keys(Keys.TAB)
+ self.page.find_by_id("btn-save").click()
+ self._verify_insert_data()
+
+ def _verify_insert_data(self):
+ time.sleep(0.5)
+ self.page.find_by_id("btn-flash").click()
+ time.sleep(1)
+ main_el = self.page.find_by_xpath('//*[@id="datagrid"]')
+ cell1 = main_el.find_element_by_xpath(
+ './/div[contains(@class, "r1")]//span'
+ ).get_attribute('innerHTML')
+ cell2 = main_el.find_element_by_xpath(
+ './/div[contains(@class, "r2")]'
+ ).get_attribute('innerHTML')
+ cell3 = main_el.find_element_by_xpath(
+ './/div[contains(@class, "r3")]'
+ ).get_attribute('innerHTML')
+
+ test_verify_data = config_data['insert']['insert_with_defaults']
+
+ # compare updated cell values with original values
+ self.assertEquals(cell1, test_verify_data['id'])
+ self.assertEquals(cell2, test_verify_data['data_default_nulls'])
+ self.assertEquals(cell3, test_verify_data['data_default_no_nulls'])
+
+ def _update_row_in_table(self):
+ xpath_cell1 = "//div[contains(@class, 'even')]//div[" \
+ "contains(@class, 'r1')]"
+ xpath_cell2 = "//div[contains(@class, 'even')]//div[" \
+ "contains(@class, 'r2')]"
+ xpath_cell3 = "//div[contains(@class, 'even')]//div[" \
+ "contains(@class, 'r4')]"
+ for value in config_data['update']:
+ cell1 = config_data['update'][value]['id']
+ cell2 = config_data['update'][value]['data_default_nulls']
+ cell3 = config_data['update'][value]['data_nulls']
+
+ # Search cell 1 and update with given data
+ time.sleep(2)
+ cell1_el = self.page.find_by_xpath(xpath_cell1)
+ ActionChains(self.driver).move_to_element(cell1_el).double_click(
+ cell1_el
+ ).perform()
+ field = cell1_el.find_element_by_xpath(".//input")
+ field.clear()
+ field.click()
+ ActionChains(self.driver).send_keys(cell1).perform()
+ field.send_keys(Keys.TAB) # Press tab key
+
+ # Search cell 2 and update with given data
+ cell2_el = self.page.find_by_xpath(xpath_cell2)
+ ActionChains(self.driver).move_to_element(cell2_el).double_click(
+ cell2_el).perform()
+ field = self.page.driver.find_element_by_css_selector(
+ "div[style*='z-index: 1000'] textarea"
+ )
+ field.clear()
+ field.click()
+ time.sleep(1)
+ ActionChains(self.driver).send_keys(cell2).perform()
+ time.sleep(1)
+ self.page.driver.find_element_by_css_selector(
+ "div[style*='z-index: 1000'] div button:first-child"
+ ).click() # Click on editor's Save button
+
+ # Search cell 3 and update with given data
+ cell3_el = self.page.find_by_xpath(xpath_cell3)
+ ActionChains(self.driver).move_to_element(cell3_el).double_click(
+ cell3_el).perform()
+ field = self.page.driver.find_element_by_css_selector(
+ "div[style*='z-index: 1000'] textarea"
+ )
+ field.clear()
+ field.click()
+ time.sleep(0.5)
+ ActionChains(self.driver).send_keys(cell3).perform()
+ time.sleep(0.5)
+ self.page.driver.find_element_by_css_selector(
+ "div[style*='z-index: 1000'] div button:first-child"
+ ).click() # Click on editor's Save button
+ self.page.find_by_id("btn-save").click() # Save data
+ self._verify_update_data(value) # Verify updated data with original
+
+ def _verify_update_data(self, test_case):
+ time.sleep(0.5)
+ self.page.find_by_id("btn-flash").click()
+ time.sleep(1)
+ main_el = self.page.find_by_xpath('//*[@id="datagrid"]')
+ cell1 = main_el.find_element_by_xpath(
+ './/div[contains(@class, "r1")]//span'
+ ).get_attribute('innerHTML')
+
+ from selenium.common.exceptions import NoSuchElementException
+ try:
+ cell2 = main_el.find_element_by_xpath(
+ './/div[contains(@class, "r2")]//span'
+ ).get_attribute('innerHTML')
+ except NoSuchElementException:
+ # if [null] not found, then it is an empty string
+ cell2 = "''"
+
+ try:
+ cell4 = main_el.find_element_by_xpath(
+ './/div[contains(@class, "r4")]//span'
+ ).get_attribute('innerHTML')
+ except NoSuchElementException:
+ # if [null] not found, then it is an empty string
+ cell4 = "''"
+
+ # Handle cells having [null] values
+ # If cell has [null] replace it with '' single quotes
+ cell1 = '' if cell1 == '[null]' else cell1
+ cell2 = '' if cell2 == '[null]' else cell2
+ cell4 = '' if cell4 == '[null]' else cell4
+
+ test_verify_data = config_data['update'][test_case]
+
+ # compare updated cell values with original values
+ self.assertEquals(cell1, test_verify_data['id'])
+ self.assertEquals(cell2, test_verify_data['data_default_nulls'])
+ self.assertEquals(cell4, test_verify_data['data_nulls'])
+
+ def _remove_row(self):
+ xpath_col0 = "//div[contains(@class, 'ui-widget')]//div[" \
+ "contains(@class, 'r0')]"
+ self.page.find_by_xpath(xpath_col0).click()
+ time.sleep(1)
+ self.page.find_by_xpath('//*[@id="btn-toolbar"]//button['
+ '@id="btn-delete-row"]').click()
+ self.page.click_modal_ok('Yes')
diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py
index f5d0ac7..4bb7d0f 100644
--- a/web/regression/feature_utils/pgadmin_page.py
+++ b/web/regression/feature_utils/pgadmin_page.py
@@ -33,11 +33,11 @@ class PgadminPage:
self.click_modal_ok()
self.wait_for_reloading_indicator_to_disappear()
- def click_modal_ok(self):
+ def click_modal_ok(self, btn_label='OK'):
time.sleep(0.5)
# Find active alertify dialog in case of multiple alertify dialog & click on that dialog
self.click_element(
- self.find_by_xpath("//div[contains(@class, 'alertify') and not(contains(@class, 'ajs-hidden'))]//button[.='OK']")
+ self.find_by_xpath("//*//div[contains(@class, 'alertify') and not(contains(@class, 'ajs-hidden'))]//button[.='{0}']".format(btn_label))
)
def add_server(self, server_config):
diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py
index 2b7c695..c50dd31 100644
--- a/web/regression/python_test_utils/test_utils.py
+++ b/web/regression/python_test_utils/test_utils.py
@@ -172,6 +172,35 @@ def create_table(server, db_name, table_name):
except Exception:
traceback.print_exc(file=sys.stderr)
+
+def create_table_with_query(server, db_name, query):
+ """
+ This function create the table in given database name
+ :param server: server details
+ :type server: dict
+ :param db_name: database name
+ :type db_name: str
+ :param query: create table query
+ :type query: str
+ :return: None
+ """
+ try:
+ connection = get_db_connection(db_name,
+ server['username'],
+ server['db_password'],
+ server['host'],
+ server['port'])
+ old_isolation_level = connection.isolation_level
+ connection.set_isolation_level(0)
+ pg_cursor = connection.cursor()
+ pg_cursor.execute(query)
+ connection.set_isolation_level(old_isolation_level)
+ connection.commit()
+
+ except Exception:
+ traceback.print_exc(file=sys.stderr)
+
+
def create_constraint(
server, db_name, table_name,
constraint_type="unique", constraint_name="test_unique"):
@@ -274,7 +303,6 @@ def drop_database(connection, database_name):
connection.commit()
connection.close()
-
def drop_tablespace(connection):
"""This function used to drop the tablespace"""
pg_cursor = connection.cursor()
view thread (25+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected]
Subject: Re: [pgAdmin4][Patch][RM2257]: Query tool - Insert row doesn't use default values
In-Reply-To: <CAM5-9D8MdAqvix74_K+eMkeGkP7r4Aost1Uqz4hupMRPxwqzAw@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox