Hi, Please find the attached patch for RM #2781 : New option to set the quotation mark for copying to clipboard.
This patch includes: - Provide options in preferences to control the CSV output which includes copy to clipboard and download as CSV features in Query Tool - Modified related jasmine tests - Modified related feature tests Thanks, Khushboo
diff --git a/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py b/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py index d01bf66..41fd6da 100644 --- a/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py +++ b/web/pgadmin/feature_tests/copy_selected_query_results_feature_test.py @@ -66,7 +66,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): self.page.find_by_xpath("//*[@id='btn-copy-row']").click() - self.assertEqual("'Some-Name','6','some info'", + self.assertEqual('"Some-Name","6","some info"', pyperclip.paste()) def _copies_columns(self): @@ -75,9 +75,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): self.page.find_by_xpath("//*[@id='btn-copy-row']").click() self.assertEqual( - """'Some-Name' -'Some-Other-Name' -'Yet-Another-Name'""", + """\"Some-Name" +"Some-Other-Name" +"Yet-Another-Name\"""", pyperclip.paste()) def _copies_row_using_keyboard_shortcut(self): @@ -86,7 +86,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform() - self.assertEqual("'Some-Name','6','some info'", + self.assertEqual('"Some-Name","6","some info"', pyperclip.paste()) def _copies_column_using_keyboard_shortcut(self): @@ -96,9 +96,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform() self.assertEqual( - """'Some-Name' -'Some-Other-Name' -'Yet-Another-Name'""", + """\"Some-Name" +"Some-Other-Name" +"Yet-Another-Name\"""", pyperclip.paste()) def _copies_rectangular_selection(self): @@ -112,8 +112,8 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): .release(bottom_right_cell).perform() ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform() - self.assertEqual("""'Some-Other-Name','22' -'Yet-Another-Name','14'""", pyperclip.paste()) + self.assertEqual("""\"Some-Other-Name","22" +"Yet-Another-Name","14\"""", pyperclip.paste()) def _shift_resizes_rectangular_selection(self): pyperclip.copy("old clipboard contents") @@ -128,8 +128,8 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): ActionChains(self.page.driver).key_down(Keys.SHIFT).send_keys(Keys.ARROW_RIGHT).key_up(Keys.SHIFT).perform() ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform() - self.assertEqual("""'Some-Other-Name','22','some other info' -'Yet-Another-Name','14','cool info'""", pyperclip.paste()) + self.assertEqual("""\"Some-Other-Name","22","some other info" +"Yet-Another-Name","14","cool info\"""", pyperclip.paste()) def _shift_resizes_column_selection(self): pyperclip.copy("old clipboard contents") @@ -141,9 +141,9 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform() self.assertEqual( - """'Some-Name','6' -'Some-Other-Name','22' -'Yet-Another-Name','14'""", + """\"Some-Name","6" +"Some-Other-Name","22" +"Yet-Another-Name","14\"""", pyperclip.paste()) def _mouseup_outside_grid_still_makes_a_selection(self): @@ -160,7 +160,7 @@ class CopySelectedQueryResultsFeatureTest(BaseFeatureTest): ActionChains(self.page.driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform() - self.assertEqual("'cool info'", pyperclip.paste()) + self.assertEqual('"cool info"', pyperclip.paste()) def after(self): self.page.close_query_tool() diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py index 87514b1..6b79948 100644 --- a/web/pgadmin/feature_tests/query_tool_journey_test.py +++ b/web/pgadmin/feature_tests/query_tool_journey_test.py @@ -54,7 +54,7 @@ class QueryToolJourneyTest(BaseFeatureTest): 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'", + self.assertEqual('"Some-Name","6","some info"', pyperclip.paste()) def _test_copies_columns(self): @@ -65,9 +65,9 @@ class QueryToolJourneyTest(BaseFeatureTest): 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()) + 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() diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 297e8ec..865746f 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -1723,7 +1723,7 @@ ' <% var option = options[i]; %>', ' <option ', ' <% if (option.image) { %> data-image=<%=option.image%> <%}%>', - ' value=<%= formatter.fromRaw(option.value) %>', + ' value=<%- formatter.fromRaw(option.value) %>', ' <% if (option.selected) {%>selected="selected"<%} else {%>', ' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>', ' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>', diff --git a/web/pgadmin/static/js/selection/copy_data.js b/web/pgadmin/static/js/selection/copy_data.js index 2f30dd7..c098614 100644 --- a/web/pgadmin/static/js/selection/copy_data.js +++ b/web/pgadmin/static/js/selection/copy_data.js @@ -14,6 +14,7 @@ function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) { var selectedRanges = grid.getSelectionModel().getSelectedRanges(); var dataView = grid.getData(); var rows = grid.getSelectedRows(); + var CSVOptions = grid.CSVOptions; if (RangeSelectionHelper.areAllRangesCompleteRows(grid, selectedRanges)) { self.copied_rows = rows.map(function (rowIndex) { @@ -24,7 +25,7 @@ function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) { self.copied_rows = []; setPasteRowButtonEnablement(self.can_edit, false); } - var csvText = rangeBoundaryNavigator.rangesToCsv(dataView.getItems(), columnDefinitions, selectedRanges); + var csvText = rangeBoundaryNavigator.rangesToCsv(dataView.getItems(), columnDefinitions, selectedRanges, CSVOptions); if (csvText) { clipboard.copyTextToClipboard(csvText); } diff --git a/web/pgadmin/static/js/selection/range_boundary_navigator.js b/web/pgadmin/static/js/selection/range_boundary_navigator.js index 274b621..d5014c1 100644 --- a/web/pgadmin/static/js/selection/range_boundary_navigator.js +++ b/web/pgadmin/static/js/selection/range_boundary_navigator.js @@ -57,7 +57,7 @@ function (RangeSelectionHelper) { }.bind(this)); }, - rangesToCsv: function (data, columnDefinitions, selectedRanges) { + rangesToCsv: function (data, columnDefinitions, selectedRanges, CSVOptions) { var rowRangeBounds = selectedRanges.map(function (range) { return [range.fromRow, range.toRow]; @@ -70,8 +70,9 @@ function (RangeSelectionHelper) { colRangeBounds = this.removeFirstColumn(colRangeBounds); } - var csvRows = this.mapOver2DArray(rowRangeBounds, colRangeBounds, this.csvCell.bind(this, data, columnDefinitions), function (rowData) { - return rowData.join(','); + var csvRows = this.mapOver2DArray(rowRangeBounds, colRangeBounds, this.csvCell.bind(this, data, columnDefinitions, CSVOptions), function (rowData) { + var field_separator = CSVOptions.field_separator || ','; + return rowData.join(field_separator); }); return csvRows.join('\n'); @@ -102,17 +103,30 @@ function (RangeSelectionHelper) { return unionedColRanges; }, - csvCell: function (data, columnDefinitions, rowId, colId) { - var val = data[rowId][columnDefinitions[colId].field]; - - if (val && _.isObject(val)) { - val = '\'' + JSON.stringify(val) + '\''; - } else if (val && typeof val != 'number' && typeof val != 'boolean') { - val = '\'' + val.toString() + '\''; - } else if (_.isNull(val) || _.isUndefined(val)) { - val = ''; + csvCell: function (data, columnDefinitions, CSVOptions, rowId, colId) { + var val = data[rowId][columnDefinitions[colId].field], + quoting = CSVOptions.quoting || 'strings', + quote_char = CSVOptions.quote_char || '\''; + + if (quoting == 'all') { + if (val && _.isObject(val)) { + val = quote_char + JSON.stringify(val) + quote_char; + } else if (val) { + val = quote_char + val.toString() + quote_char; + } else if (_.isNull(val) || _.isUndefined(val)) { + val = ''; + } + } + else if(quoting == 'strings') { + if (val && _.isObject(val)) { + val = quote_char + JSON.stringify(val) + quote_char; + } else if (val && typeof val != 'number' && typeof val != 'boolean') { + val = quote_char + val.toString() + quote_char; + } else if (_.isNull(val) || _.isUndefined(val)) { + val = ''; + } } return val; }, }; -}); \ No newline at end of file +}); diff --git a/web/pgadmin/static/js/slickgrid/event_handlers/handle_query_output_keyboard_event.js b/web/pgadmin/static/js/slickgrid/event_handlers/handle_query_output_keyboard_event.js index 50ebd47..eb76513 100644 --- a/web/pgadmin/static/js/slickgrid/event_handlers/handle_query_output_keyboard_event.js +++ b/web/pgadmin/static/js/slickgrid/event_handlers/handle_query_output_keyboard_event.js @@ -10,6 +10,7 @@ function (copyData, RangeSelectionHelper) { var isModifierDown = event.ctrlKey || event.metaKey; var self = this || window; self.slickgrid = args.grid; + self.csvOptions if (isModifierDown && modifiedKey == KEY_C) { copyData.apply(self); @@ -19,4 +20,4 @@ function (copyData, RangeSelectionHelper) { RangeSelectionHelper.selectAll(self.slickgrid); } } -}); \ No newline at end of file +}); diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py index 6be9e0b..843a09d 100644 --- a/web/pgadmin/tools/sqleditor/__init__.py +++ b/web/pgadmin/tools/sqleditor/__init__.py @@ -235,6 +235,32 @@ class SqlEditorModule(PgAdminModule): ) ) + self.csv_quoting = self.preference.register( + 'CSV_output', 'csv_quoting', + gettext("CSV quoting"), 'options', 'strings', + category_label=gettext('CSV Output'), + options=[{'label': 'None', 'value': 'none'}, + {'label': 'All', 'value': 'all'}, + {'label': 'Strings', 'value': 'strings'}] + ) + + self.csv_quote_char = self.preference.register( + 'CSV_output', 'csv_quote_char', + gettext("CSV quote character"), 'options', '"', + category_label=gettext('CSV Output'), + options=[{'label': '"', 'value': '"'}, + {'label': '\'', 'value': '\''}] + ) + + self.csv_field_separator = self.preference.register( + 'CSV_output', 'csv_field_separator', + gettext("CSV field separator"), 'options', ',', + category_label=gettext('CSV Output'), + options=[{'label': ';', 'value': ';'}, + {'label': ',', 'value': ','}, + {'label': '|', 'value': '|'}, + {'label': 'Tab', 'value': '\t'}] + ) blueprint = SqlEditorModule(MODULE_NAME, __name__, static_url_path='/static') @@ -1634,7 +1660,9 @@ def start_query_download_tool(trans_id): r.call_on_close(cleanup) return r - r = Response(gen(), mimetype='text/csv') + r = Response(gen(quote=blueprint.csv_quoting.get(), + quote_char=blueprint.csv_quote_char.get(), + field_separator=blueprint.csv_field_separator.get()), mimetype='text/csv') if 'filename' in data and data['filename'] != "": filename = data['filename'] diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js index d50cca7..c38b172 100644 --- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js @@ -792,6 +792,20 @@ define('tools.querytool', [ handleQueryOutputKeyboardEvent(event, args); }); } else { + var pref_cache = undefined; + args.grid.CSVOptions = {}; + + if (self.handler.is_new_browser_tab) { + pref_cache = window.opener.pgAdmin.Browser.preferences_cache; + } else { + pref_cache = window.top.pgAdmin.Browser.preferences_cache + } + + // Get CSV options from preferences cache + args.grid.CSVOptions.quoting = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'csv_quoting'}).value; + args.grid.CSVOptions.quote_char = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'csv_quote_char'}).value; + args.grid.CSVOptions.field_separator = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'csv_field_separator'}).value; + handleQueryOutputKeyboardEvent(event, args); } }); @@ -1236,7 +1250,20 @@ define('tools.querytool', [ // Callback function for copy button click. on_copy_row: function () { - var self = this; + var self = this, + pref_cache = undefined; + self.grid.CSVOptions = {}; + + if (self.handler.is_new_browser_tab) { + pref_cache = window.opener.pgAdmin.Browser.preferences_cache; + } else { + pref_cache = window.top.pgAdmin.Browser.preferences_cache + } + + // Get CSV options from preferences cache + self.grid.CSVOptions.quoting = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'csv_quoting'}).value; + self.grid.CSVOptions.quote_char = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'csv_quote_char'}).value; + self.grid.CSVOptions.field_separator = _.findWhere(pref_cache, {'module': 'sqleditor', 'name': 'csv_field_separator'}).value; // Trigger the copy signal to the SqlEditorController class self.handler.trigger( @@ -1244,6 +1271,7 @@ define('tools.querytool', [ self, self.handler ); + }, // Callback function for paste button click. @@ -1496,7 +1524,7 @@ define('tools.querytool', [ keyAction: function (event) { keyboardShortcuts.processEvent(this.handler, queryToolActions, event); - }, + } }); /* Defining controller class for data grid, which actually @@ -3647,7 +3675,6 @@ define('tools.querytool', [ explain_timing = res.data.explain_timing; auto_commit = res.data.auto_commit; auto_rollback = res.data.auto_rollback; - updateUI(); } }, diff --git a/web/pgadmin/utils/driver/psycopg2/__init__.py b/web/pgadmin/utils/driver/psycopg2/__init__.py index eabe3b0..6913094 100644 --- a/web/pgadmin/utils/driver/psycopg2/__init__.py +++ b/web/pgadmin/utils/driver/psycopg2/__init__.py @@ -697,7 +697,6 @@ WHERE params: Additional parameters formatted_exception_msg: For exception records: Number of initial records - Returns: Generator response """ @@ -787,7 +786,7 @@ WHERE ) return new_results - def gen(): + def gen(quote='strings', quote_char="'", field_separator=','): results = cur.fetchmany(records) if not results: @@ -816,9 +815,17 @@ WHERE res_io = StringIO() + if quote == 'strings': + quote = csv.QUOTE_NONNUMERIC + elif quote == 'all': + quote = csv.QUOTE_ALL + else: + quote = csv.QUOTE_NONE + csv_writer = csv.DictWriter( - res_io, fieldnames=header, delimiter=u',', - quoting=csv.QUOTE_NONNUMERIC + res_io, fieldnames=header, delimiter=field_separator, + quoting=quote, + quotechar=quote_char ) csv_writer.writeheader() @@ -837,8 +844,9 @@ WHERE res_io = StringIO() csv_writer = csv.DictWriter( - res_io, fieldnames=header, delimiter=u',', - quoting=csv.QUOTE_NONNUMERIC + res_io, fieldnames=header, delimiter=field_separator, + quoting=quote, + quotechar=quote_char ) if IS_PY2: diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py index 030d05e..26e10cd 100644 --- a/web/regression/feature_utils/pgadmin_page.py +++ b/web/regression/feature_utils/pgadmin_page.py @@ -73,6 +73,7 @@ class PgadminPage: self.find_by_partial_link_text("Query Tool").click() self.click_tab('Query -') + def enable_menu_item(self, menu_item, wait_time): start_time = time.time() # wait until menu becomes enabled. diff --git a/web/regression/javascript/selection/copy_data_spec.js b/web/regression/javascript/selection/copy_data_spec.js index 2b623b3..7a18b1b 100644 --- a/web/regression/javascript/selection/copy_data_spec.js +++ b/web/regression/javascript/selection/copy_data_spec.js @@ -22,11 +22,12 @@ describe('copyData', function () { beforeEach(function () { SlickGrid = Slick.Grid; - var data = [{'id': 1, 'brand':'leopord', 'size':'12', '__temp_PK': '123'}, - {'id': 2, 'brand':'lion', 'size':'13', '__temp_PK': '456'}, - {'id': 3, 'brand':'puma', 'size':'9', '__temp_PK': '789'}], + var data = [{'id': 1, 'brand':'leopord', 'size':12, '__temp_PK': '123'}, + {'id': 2, 'brand':'lion', 'size':13, '__temp_PK': '456'}, + {'id': 3, 'brand':'puma', 'size':9, '__temp_PK': '789'}], dataView = new Slick.Data.DataView(); + var CSVOptions = {'quoting': 'strings', 'quote_char': '"', 'field_separator': ','}; var columns = [ { id: 'row-header-column', @@ -66,6 +67,7 @@ describe('copyData', function () { buttonPasteRow = $('<button id="btn-paste-row" disabled></button>'); $('body').append(buttonPasteRow); grid = new SlickGrid('#grid', dataView, columns, {}); + grid.CSVOptions = CSVOptions; dataView.setItems(data, '__temp_PK'); grid.setSelectionModel(new XCellSelectionModel()); sqlEditor = {slickgrid: grid}; @@ -93,8 +95,8 @@ describe('copyData', function () { expect(sqlEditor.copied_rows.length).toBe(2); expect(clipboard.copyTextToClipboard).toHaveBeenCalled(); - expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('1,\'leopord\',\'12\''); - expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('3,\'puma\',\'9\''); + expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('1,"leopord",12'); + expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('3,"puma",9'); }); describe('when the user can edit the grid', function () { diff --git a/web/regression/javascript/selection/range_boundary_navigator_spec.js b/web/regression/javascript/selection/range_boundary_navigator_spec.js index 2d56b05..40c754d 100644 --- a/web/regression/javascript/selection/range_boundary_navigator_spec.js +++ b/web/regression/javascript/selection/range_boundary_navigator_spec.js @@ -132,7 +132,7 @@ describe('RangeBoundaryNavigator', function () { }); describe('#rangesToCsv', function () { - var data, columnDefinitions, ranges; + var data, columnDefinitions, ranges, CSVOptions; beforeEach(function () { data = [{'id':1, 'animal':'leopard', 'size':'12'}, {'id':2, 'animal':'lion', 'size':'13'}, @@ -143,16 +143,21 @@ describe('RangeBoundaryNavigator', function () { {name: 'animal', field: 'animal', pos: 1}, {name: 'size', field: 'size', pos: 2}]; ranges = [new Slick.Range(0, 0, 0, 2), new Slick.Range(3, 0, 3, 2)]; + + CSVOptions = [{'quoting': 'all', 'quote_char': '"', 'field_separator': ','}, + {'quoting': 'strings', 'quote_char': '"', 'field_separator': ';'}, + {'quoting': 'strings', 'quote_char': '\'', 'field_separator': '|'}, + {'quoting': 'none', 'quote_char': '"', 'field_separator': '\t'}]; }); - it('returns csv for the provided ranges', function () { - var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges); - expect(csvResult).toEqual('1,\'leopard\',\'12\'\n4,\'tiger\',\'10\''); + it('returns csv for the provided ranges for CSV options quoting All with char " with field separator ,', function () { + var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges, CSVOptions[0]); + expect(csvResult).toEqual('"1","leopard","12"\n"4","tiger","10"'); }); - describe('when no cells are selected', function () { + describe('when no cells are selected for CSV options quoting Strings with char " with field separator ;', function () { it('should return an empty string', function () { - var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []); + var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, [], CSVOptions[1]); expect(csvResult).toEqual(''); }); @@ -167,14 +172,14 @@ describe('RangeBoundaryNavigator', function () { ranges = [new Slick.Range(0, 0, 0, 3), new Slick.Range(3, 0, 3, 3)]; }); - it('returns csv for the columns with data', function () { - var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges); + it('returns csv for the columns with data for CSV options quoting Strings with char \' with field separator |', function () { + var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges, CSVOptions[2]); - expect(csvResult).toEqual('1,\'leopard\',\'12\'\n4,\'tiger\',\'10\''); + expect(csvResult).toEqual('1|\'leopard\'|\'12\'\n4|\'tiger\'|\'10\''); }); - describe('when no cells are selected', function () { + describe('when no cells are selected for CSV options quoting none with field separator tab', function () { it('should return an empty string', function () { - var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []); + var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, [], CSVOptions[3]); expect(csvResult).toEqual(''); }); diff --git a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js index 12e3f66..7ab78a4 100644 --- a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js +++ b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js @@ -36,10 +36,12 @@ describe('#handleQueryOutputKeyboardEvent', function () { columnDefinitions = [{name: 'checkboxColumn'}, {pos: 1, name: 'firstColumn', field: 'firstColumn'}, { pos: 2, name: 'secondColumn', field: 'secondColumn'}], - dataView = new Slick.Data.DataView(); + dataView = new Slick.Data.DataView(), + CSVOptions = {'quoting': 'all', 'quote_char': '\'', 'field_separator': ','}; grid = new Slick.Grid($('<div></div>'), dataView, columnDefinitions); grid.setSelectionModel(new XCellSelectionModel()); + grid.CSVOptions = CSVOptions; dataView.setItems(data, '__temp_PK'); slickEvent = { grid: grid,