pgadmin4/web/regression/javascript/sqleditor/keyboard_shortcuts_spec.js
Yosry Muhammad 710d520631 Add support for editing of resultsets in the Query Tool, if the data can be identified as updatable. Fixes #1760
When a query is run in the Query Tool, check if the source of the columns
can be identified as being from a single table, and that we have all
columns that make up the primary key. If so, consider the resultset to
be editable and allow the user to edit data and add/remove rows in the
grid. Changes to data are saved using SAVEPOINTs as part of any
transaction that's in progress, and rolled back if there are integrity
violations, without otherwise affecting the ongoing transaction.

Implemented by Yosry Muhammad as a Google Summer of Code project.
2019-07-17 11:45:20 +01:00

692 lines
20 KiB
JavaScript

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2019, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////////////////
import * as keyboardShortcuts from 'sources/keyboard_shortcuts';
import {queryToolActions} from 'sources/sqleditor/query_tool_actions';
import gettext from 'sources/gettext';
describe('the keyboard shortcuts', () => {
const F1_KEY = 112,
F5_KEY = 116,
F6_KEY = 117,
F7_KEY = 118,
F8_KEY = 119,
PERIOD_KEY = 190,
FWD_SLASH_KEY = 191;
let sqlEditorControllerSpy, event, queryToolActionsSpy;
beforeEach(() => {
event = {
shift: false,
which: undefined,
preventDefault: jasmine.createSpy('preventDefault'),
cancelBubble: false,
stopPropagation: jasmine.createSpy('stopPropagation'),
stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
};
let gridView = {
query_tool_obj: {
getSelection: jasmine.createSpy('getSelection'),
getValue: jasmine.createSpy('getValue'),
},
};
sqlEditorControllerSpy = jasmine.createSpyObj('SqlEditorController', [
'isQueryRunning',
'execute',
]);
sqlEditorControllerSpy.gridView = gridView;
sqlEditorControllerSpy.preferences = {
execute_query: {
alt: false,
shift: false,
control: false,
key: {
key_code: F5_KEY,
},
},
explain_query: {
alt: false,
shift: false,
control: false,
key: {
key_code: F7_KEY,
},
},
explain_analyze_query: {
alt: false,
shift: true,
control: false,
key: {
key_code: F7_KEY,
},
},
download_csv: {
alt: false,
shift: false,
control: false,
key: {
key_code: F8_KEY,
},
},
move_next: {
alt: false,
shift: false,
control: false,
key: {
key_code: null,
},
},
move_previous: {
alt: false,
shift: false,
control: false,
key: {
key_code: null,
},
},
commit_transaction: {
alt: false,
shift: true,
control: true,
key: {
key_code: 'm',
},
},
rollback_transaction: {
alt: false,
shift: true,
control: true,
key: {
key_code: 'r',
},
},
save_data: {
alt : false,
shift: false,
control: false,
key: {
key_code: F6_KEY,
},
},
};
queryToolActionsSpy = jasmine.createSpyObj(queryToolActions, [
'explainAnalyze',
'explain',
'download',
'commentBlockCode',
'commentLineCode',
'uncommentLineCode',
'executeQuery',
'executeCommit',
'executeRollback',
'saveDataChanges',
]);
});
describe('when the key is not handled by the function', function () {
beforeEach(() => {
event.which = F1_KEY;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should allow event to propagate', () => {
expect(event.preventDefault).not.toHaveBeenCalled();
});
});
describe('F5', () => {
describe('when there is no query already running', () => {
beforeEach(() => {
event.keyCode = F5_KEY;
event.altKey = false;
event.shiftKey = false;
event.ctrlKey = false;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should execute the query', () => {
expect(queryToolActionsSpy.executeQuery).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
it('should stop event propagation', () => {
expect(event.preventDefault).toHaveBeenCalled();
});
});
describe('when the query is already running', () => {
it('does nothing', () => {
event.keyCode = F5_KEY;
event.altKey = false;
event.shiftKey = false;
event.ctrlKey = false;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.executeQuery).not.toHaveBeenCalled();
});
});
});
describe('F6', () => {
describe('when there is not a query already running', () => {
beforeEach(() => {
event.which = F6_KEY;
event.altKey = false;
event.shiftKey = false;
event.ctrlKey = false;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should save the changed data', () => {
expect(queryToolActionsSpy.saveDataChanges).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
describe('when the query is already running', () => {
it('does nothing', () => {
event.keyCode = F6_KEY;
event.altKey = false;
event.shiftKey = false;
event.ctrlKey = false;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.saveDataChanges).not.toHaveBeenCalled();
});
});
});
describe('F7', () => {
describe('when there is not a query already running', () => {
beforeEach(() => {
event.which = F7_KEY;
event.altKey = false;
event.shiftKey = false;
event.ctrlKey = false;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should explain the query plan', () => {
expect(queryToolActionsSpy.explain).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
describe('when the query is already running', () => {
it('does nothing', () => {
event.keyCode = F7_KEY;
event.altKey = false;
event.shiftKey = false;
event.ctrlKey = false;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.explain).not.toHaveBeenCalled();
});
});
});
describe('Shift+F7', () => {
describe('when there is not a query already running', () => {
beforeEach(() => {
event.shiftKey = true;
event.which = F7_KEY;
event.altKey = false;
event.ctrlKey = false;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should analyze explain the query plan', () => {
expect(queryToolActionsSpy.explainAnalyze).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
describe('when the query is already running', () => {
it('does nothing', () => {
event.shiftKey = true;
event.which = F7_KEY;
event.altKey = false;
event.ctrlKey = false;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.explainAnalyze).not.toHaveBeenCalled();
});
});
});
describe('F8', () => {
describe('when there is not a query already running', () => {
beforeEach(() => {
event.which = F8_KEY;
event.altKey = false;
event.shiftKey = false;
event.ctrlKey = false;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should download the query results as a CSV', () => {
expect(queryToolActionsSpy.download).toHaveBeenCalled();
});
it('should stop event propagation', () => {
expect(event.preventDefault).toHaveBeenCalled();
});
});
describe('when the query is already running', () => {
it('does nothing', () => {
event.keyCode = F8_KEY;
event.altKey = false;
event.shiftKey = false;
event.ctrlKey = false;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.download).not.toHaveBeenCalled();
});
});
});
describe('inlineComment', () => {
describe('when there is not a query already running', () => {
describe('and the system is a Mac', () => {
beforeEach(() => {
macKeysSetup();
event.which = FWD_SLASH_KEY;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should comment the line', () => {
expect(queryToolActionsSpy.commentLineCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
describe('and the system is Windows', () => {
beforeEach(() => {
windowsKeysSetup();
event.which = FWD_SLASH_KEY;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should comment the line', () => {
expect(queryToolActionsSpy.commentLineCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
});
describe('when the query is already running', () => {
beforeEach(() => {
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
});
describe('and the system is a Mac', () => {
beforeEach(() => {
macKeysSetup();
event.which = FWD_SLASH_KEY;
});
it('does nothing', () => {
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.commentLineCode).not.toHaveBeenCalled();
});
});
describe('and the system is a Windows', () => {
beforeEach(() => {
windowsKeysSetup();
event.which = FWD_SLASH_KEY;
});
it('does nothing', () => {
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.commentLineCode).not.toHaveBeenCalled();
});
});
});
});
describe('inlineUncomment', () => {
describe('when there is not a query already running', () => {
describe('and the system is a mac', () => {
beforeEach(() => {
macKeysSetup();
event.which = PERIOD_KEY;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should uncomment the line', () => {
expect(queryToolActionsSpy.uncommentLineCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
describe('and the system is a windows', () => {
beforeEach(() => {
windowsKeysSetup();
event.which = PERIOD_KEY;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should uncomment the line', () => {
expect(queryToolActionsSpy.uncommentLineCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
});
describe('when the query is already running', () => {
beforeEach(() => {
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
});
describe('and the system is a Mac', () => {
beforeEach(() => {
macKeysSetup();
event.which = PERIOD_KEY;
});
it('does nothing', () => {
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.uncommentLineCode).not.toHaveBeenCalled();
});
});
describe('and the system is a Windows', () => {
beforeEach(() => {
windowsKeysSetup();
event.which = PERIOD_KEY;
});
it('does nothing', () => {
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.uncommentLineCode).not.toHaveBeenCalled();
});
});
});
});
describe('blockComment', () => {
describe('when there is not a query already running', () => {
describe('and the system is a Mac', () => {
beforeEach(() => {
macKeysSetup();
event.which = FWD_SLASH_KEY;
event.shiftKey = true;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should comment out the block selection', () => {
expect(queryToolActionsSpy.commentBlockCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
});
describe('when there is not a query already running', () => {
describe('and the system is a Windows', () => {
beforeEach(() => {
windowsKeysSetup();
event.which = FWD_SLASH_KEY;
event.shiftKey = true;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should comment out the block selection', () => {
expect(queryToolActionsSpy.commentBlockCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
});
describe('when there is a query already running', () => {
beforeEach(() => {
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
});
describe('and the system is a Mac', () => {
beforeEach(() => {
macKeysSetup();
event.which = FWD_SLASH_KEY;
event.shiftKey = true;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('does nothing', () => {
expect(queryToolActionsSpy.commentBlockCode).not.toHaveBeenCalled();
});
});
describe('and the system is a Windows', () => {
beforeEach(() => {
windowsKeysSetup();
event.which = FWD_SLASH_KEY;
event.shiftKey = true;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('does nothing', () => {
expect(queryToolActionsSpy.commentBlockCode).not.toHaveBeenCalled();
});
});
});
});
describe('shortcut to text converters', ()=> {
var shortcut = {
alt: false,
shift: false,
control: false,
key: {
char: 'a',
key_code: 65,
},
};
it('shortcut_key',()=>{
expect(keyboardShortcuts.shortcut_key(shortcut)).toEqual('A');
});
it('shortcut_accesskey_title',()=>{
expect(keyboardShortcuts.shortcut_accesskey_title(
'Title', shortcut)).toEqual(gettext('Title (accesskey + A)'));
});
it('shortcut_title',()=>{
shortcut.alt = true, shortcut.shift = false, shortcut.control = false;
expect(keyboardShortcuts.shortcut_title(
'Title', shortcut)).toEqual(gettext('Title (Alt+A)'));
shortcut.alt = false, shortcut.shift = true, shortcut.control = false;
expect(keyboardShortcuts.shortcut_title(
'Title', shortcut)).toEqual(gettext('Title (Shift+A)'));
shortcut.alt = false, shortcut.shift = false, shortcut.control = true;
expect(keyboardShortcuts.shortcut_title(
'Title', shortcut)).toEqual(gettext('Title (Ctrl+A)'));
shortcut.alt = true, shortcut.shift = true, shortcut.control = true;
expect(keyboardShortcuts.shortcut_title(
'Title', shortcut)).toEqual(gettext('Title (Alt+Shift+Ctrl+A)'));
});
});
describe('Shift+Ctrl+C', () => {
describe('when there is not a query already running', () => {
beforeEach(() => {
event.shiftKey = true;
event.which = 'm';
event.altKey = false;
event.ctrlKey = true;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should commit the transaction', () => {
expect(queryToolActionsSpy.executeCommit).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
describe('when the query is already running', () => {
it('does nothing', () => {
event.shiftKey = true;
event.which = 'm';
event.altKey = false;
event.ctrlKey = true;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.executeCommit).not.toHaveBeenCalled();
});
});
});
describe('Shift+Ctrl+R', () => {
describe('when there is not a query already running', () => {
beforeEach(() => {
event.shiftKey = true;
event.which = 'r';
event.altKey = false;
event.ctrlKey = true;
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
});
it('should rollback the transaction', () => {
expect(queryToolActionsSpy.executeRollback).toHaveBeenCalledWith(sqlEditorControllerSpy);
});
expectEventPropagationToStop();
});
describe('when the query is already running', () => {
it('does nothing', () => {
event.shiftKey = true;
event.which = 'r';
event.altKey = false;
event.ctrlKey = true;
sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
keyboardShortcuts.processEventQueryTool(
sqlEditorControllerSpy, queryToolActionsSpy, event
);
expect(queryToolActionsSpy.executeRollback).not.toHaveBeenCalled();
});
});
});
function expectEventPropagationToStop() {
describe('stops all event propogation', () => {
it('should cancel the bubble', () => {
expect(event.cancelBubble).toEqual(true);
});
it('should prevent the default behavior', () => {
expect(event.preventDefault).toHaveBeenCalled();
});
it('should stop event propagation', () => {
expect(event.stopPropagation).toHaveBeenCalled();
expect(event.stopImmediatePropagation).toHaveBeenCalled();
});
});
}
function windowsKeysSetup() {
spyOn(keyboardShortcuts, 'isMac').and.returnValue(false);
event.ctrlKey = true;
event.metaKey = false;
}
function macKeysSetup() {
spyOn(keyboardShortcuts, 'isMac').and.returnValue(true);
event.ctrlKey = false;
event.metaKey = true;
}
});