Significant changes to use ReactJS extensively.

1. Replace the current layout library wcDocker with ReactJS based rc-dock. #6479
2. Have close buttons on individual panel tabs instead of common. #2821
3. Changes in the context menu on panel tabs - Add close, close all and close others menu items. #5394
4. Allow closing all the tabs, including SQL and Properties. #4733
5. Changes in docking behaviour of different tabs based on user requests and remove lock layout menu.
6. Fix an issue where the scroll position of panels was not remembered on Firefox. #2986
7. Reset layout now will not require page refresh and is done spontaneously.
8. Use the zustand store for storing preferences instead of plain JS objects. This will help reflecting preferences immediately.
9. The above fix incorrect format (no indent) of SQL stored functions/procedures. #6720
10. New version check is moved to an async request now instead of app start to improve startup performance.
11. Remove jQuery and Bootstrap completely.
12. Replace jasmine and karma test runner with jest. Migrate all the JS test cases to jest. This will save time in writing and debugging JS tests.
13. Other important code improvements and cleanup.
This commit is contained in:
Aditya Toshniwal
2023-10-23 17:43:17 +05:30
committed by GitHub
parent 6d555645e9
commit 862f101772
373 changed files with 11149 additions and 14836 deletions

View File

@@ -13,44 +13,50 @@ import { FakeLink, FakeNode } from './fake_item';
import { PortModelAlignment } from '@projectstorm/react-diagrams';
describe('ERDCore', ()=>{
let eleFactory = jasmine.createSpyObj('nodeFactories', {
'registerFactory': null,
'getFactory': jasmine.createSpyObj('getFactory', ['generateModel']),
});
let erdEngine = jasmine.createSpyObj('engine', {
'getNodeFactories': eleFactory,
'getLinkFactories': eleFactory,
'getPortFactories': eleFactory,
'getActionEventBus': jasmine.createSpyObj('actionBus', ['fireAction', 'deregisterAction', 'registerAction']),
'setModel': null,
'getModel': jasmine.createSpyObj('modelObj', {
'addNode': null,
'clearSelection': null,
'getNodesDict': null,
'getLinks': null,
'serialize': ()=>({
let eleFactory = {
'registerFactory': jest.fn(),
'getFactory': jest.fn().mockReturnValue({
'generateModel': jest.fn(),
}),
};
let erdEngine = {
'getNodeFactories': jest.fn().mockReturnValue(eleFactory),
'getLinkFactories': jest.fn().mockReturnValue(eleFactory),
'getPortFactories': jest.fn().mockReturnValue(eleFactory),
'getActionEventBus': jest.fn().mockReturnValue({
'fireAction': jest.fn(),
'deregisterAction': jest.fn(),
'registerAction': jest.fn()
}),
'setModel': jest.fn(),
'getModel': jest.fn().mockReturnValue({
'addNode': jest.fn(),
'clearSelection': jest.fn(),
'getNodesDict': jest.fn(),
'getLinks': jest.fn(),
'serialize': jest.fn().mockReturnValue({
'data': 'serialized',
}),
'addLink': null,
'getNodes': null,
'setZoomLevel': null,
'getZoomLevel': null,
'fireEvent': null,
'registerListener': null,
'addLink': jest.fn(),
'getNodes': jest.fn(),
'setZoomLevel': jest.fn(),
'getZoomLevel': jest.fn(),
'fireEvent': jest.fn(),
'registerListener': jest.fn(),
}),
'repaintCanvas': null,
'zoomToFitNodes': null,
'fireEvent': null,
});
'repaintCanvas': jest.fn(),
'zoomToFitNodes': jest.fn(),
'fireEvent': jest.fn(),
};
beforeAll(()=>{
spyOn(createEngineLib, 'default').and.returnValue(erdEngine);
jest.spyOn(createEngineLib, 'default').mockReturnValue(erdEngine);
});
it('initialization', ()=>{
spyOn(ERDCore.prototype, 'initializeEngine').and.callThrough();
spyOn(ERDCore.prototype, 'initializeModel').and.callThrough();
spyOn(ERDCore.prototype, 'computeTableCounter').and.callThrough();
jest.spyOn(ERDCore.prototype, 'initializeEngine');
jest.spyOn(ERDCore.prototype, 'initializeModel');
jest.spyOn(ERDCore.prototype, 'computeTableCounter');
let erdCoreObj = new ERDCore();
expect(erdCoreObj.initializeEngine).toHaveBeenCalled();
expect(erdCoreObj.initializeModel).toHaveBeenCalled();
@@ -121,7 +127,7 @@ describe('ERDCore', ()=>{
});
it('getNewPort', ()=>{
erdEngine.getPortFactories().getFactory().generateModel.calls.reset();
erdEngine.getPortFactories().getFactory().generateModel.mockClear();
erdCoreObj.getNewPort('port1', PortModelAlignment.LEFT);
expect(erdEngine.getPortFactories().getFactory).toHaveBeenCalledWith('onetomany');
expect(erdEngine.getPortFactories().getFactory().generateModel).toHaveBeenCalledWith({
@@ -137,9 +143,9 @@ describe('ERDCore', ()=>{
it('addNode', ()=>{
let newNode = new FakeNode({});
spyOn(newNode, 'setPosition');
spyOn(erdCoreObj, 'getNewNode').and.returnValue(newNode);
spyOn(erdCoreObj, 'clearSelection');
jest.spyOn(newNode, 'setPosition');
jest.spyOn(erdCoreObj, 'getNewNode').mockReturnValue(newNode);
jest.spyOn(erdCoreObj, 'clearSelection');
let data = {name: 'link1'};
@@ -158,16 +164,16 @@ describe('ERDCore', ()=>{
it('addLink', ()=>{
let node1 = new FakeNode({'name': 'table1'}, 'id1');
let node2 = new FakeNode({'name': 'table2'}, 'id2');
spyOn(erdCoreObj, 'getOptimumPorts').and.returnValue([{name: 'port-1'}, {name: 'port-3'}]);
jest.spyOn(erdCoreObj, 'getOptimumPorts').mockReturnValue([{name: 'port-1'}, {name: 'port-3'}]);
let nodesDict = {
'id1': node1,
'id2': node2,
};
let link = new FakeLink();
spyOn(link, 'setSourcePort').and.callThrough();
spyOn(link, 'setTargetPort').and.callThrough();
spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue(nodesDict);
spyOn(erdCoreObj, 'getNewLink').and.callFake(function() {
jest.spyOn(link, 'setSourcePort');
jest.spyOn(link, 'setTargetPort');
jest.spyOn(erdEngine.getModel(), 'getNodesDict').mockReturnValue(nodesDict);
jest.spyOn(erdCoreObj, 'getNewLink').mockImplementation(function() {
return link;
});
@@ -184,8 +190,8 @@ describe('ERDCore', ()=>{
it('serialize', ()=>{
let retVal = erdCoreObj.serialize();
expect(retVal.hasOwnProperty('version')).toBeTruthy();
expect(retVal.hasOwnProperty('data')).toBeTruthy();
expect(Object.prototype.hasOwnProperty.call(retVal,'version')).toBeTruthy();
expect(Object.prototype.hasOwnProperty.call(retVal,'data')).toBeTruthy();
expect(erdEngine.getModel().serialize).toHaveBeenCalled();
});
@@ -196,7 +202,7 @@ describe('ERDCore', ()=>{
'key': 'serialized',
},
};
spyOn(erdCoreObj, 'initializeModel');
jest.spyOn(erdCoreObj, 'initializeModel');
erdCoreObj.deserialize(deserialValue);
expect(erdCoreObj.initializeModel).toHaveBeenCalledWith(deserialValue.data);
});
@@ -208,8 +214,8 @@ describe('ERDCore', ()=>{
'id1': node1,
'id2': node2,
};
spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue(nodesDict);
spyOn(erdEngine.getModel(), 'getLinks').and.returnValue([
jest.spyOn(erdEngine.getModel(), 'getNodesDict').mockReturnValue(nodesDict);
jest.spyOn(erdEngine.getModel(), 'getLinks').mockReturnValue([
new FakeLink({
'name': 'link1',
}, 'lid1'),
@@ -246,20 +252,20 @@ describe('ERDCore', ()=>{
}
};
});
spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue(nodesDict);
jest.spyOn(erdEngine.getModel(), 'getNodesDict').mockReturnValue(nodesDict);
spyOn(erdCoreObj, 'getNewLink').and.callFake(function() {
jest.spyOn(erdCoreObj, 'getNewLink').mockImplementation(function() {
return {
setSourcePort: function() {/*This is intentional (SonarQube)*/},
setTargetPort: function() {/*This is intentional (SonarQube)*/},
};
});
spyOn(erdCoreObj, 'getNewPort').and.returnValue({id: 'id'});
spyOn(erdCoreObj, 'addNode').and.callFake(function(data) {
jest.spyOn(erdCoreObj, 'getNewPort').mockReturnValue({id: 'id'});
jest.spyOn(erdCoreObj, 'addNode').mockImplementation(function(data) {
return new FakeNode({}, `id-${data.name}`);
});
spyOn(erdCoreObj, 'addLink');
spyOn(erdCoreObj, 'dagreDistributeNodes').and.callFake(()=>{/* intentionally empty */});
jest.spyOn(erdCoreObj, 'addLink');
jest.spyOn(erdCoreObj, 'dagreDistributeNodes').mockImplementation(()=>{/* intentionally empty */});
erdCoreObj.deserializeData(TEST_TABLES_DATA);
expect(erdCoreObj.addNode).toHaveBeenCalledTimes(TEST_TABLES_DATA.length);
@@ -282,7 +288,7 @@ describe('ERDCore', ()=>{
});
it('getNodesData', ()=>{
spyOn(erdEngine.getModel(), 'getNodes').and.returnValue([
jest.spyOn(erdEngine.getModel(), 'getNodes').mockReturnValue([
new FakeNode({name:'node1'}),
new FakeNode({name:'node2'}),
]);
@@ -292,16 +298,16 @@ describe('ERDCore', ()=>{
});
it('zoomIn', ()=>{
spyOn(erdEngine.getModel(), 'getZoomLevel').and.returnValue(100);
spyOn(erdCoreObj, 'repaint');
jest.spyOn(erdEngine.getModel(), 'getZoomLevel').mockReturnValue(100);
jest.spyOn(erdCoreObj, 'repaint');
erdCoreObj.zoomIn();
expect(erdEngine.getModel().setZoomLevel).toHaveBeenCalledWith(125);
expect(erdCoreObj.repaint).toHaveBeenCalled();
});
it('zoomOut', ()=>{
spyOn(erdEngine.getModel(), 'getZoomLevel').and.returnValue(100);
spyOn(erdCoreObj, 'repaint');
jest.spyOn(erdEngine.getModel(), 'getZoomLevel').mockReturnValue(100);
jest.spyOn(erdCoreObj, 'repaint');
erdCoreObj.zoomOut();
expect(erdEngine.getModel().setZoomLevel).toHaveBeenCalledWith(75);
expect(erdCoreObj.repaint).toHaveBeenCalled();

View File

@@ -12,7 +12,7 @@ describe('ERDModel', ()=>{
it('getNodesDict', ()=>{
let model = new ERDModel();
spyOn(model, 'getNodes').and.returnValue([
jest.spyOn(model, 'getNodes').mockReturnValue([
{
name: 'test1',
getID: function() {

View File

@@ -26,11 +26,11 @@ describe('KeyboardShortcutAction', ()=>{
key_code: 66,
},
};
let handler1 = jasmine.createSpy('handler1');
let handler2 = jasmine.createSpy('handler2');
let handler1 = jest.fn();
let handler2 = jest.fn();
beforeAll(()=>{
spyOn(KeyboardShortcutAction.prototype, 'shortcutKey').and.callThrough();
jest.spyOn(KeyboardShortcutAction.prototype, 'shortcutKey');
keyAction = new KeyboardShortcutAction([
[key1, handler1],
[key2, handler2],

View File

@@ -1,19 +1,27 @@
import jasmineEnzyme from 'jasmine-enzyme';
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React from 'react';
import {mount} from 'enzyme';
import '../helper/enzyme.helper';
import {
RightAngleLinkModel,
} from '@projectstorm/react-diagrams';
import OneToManyPortModel from 'pgadmin.tools.erd/erd_tool/ports/OneToManyPort';
import {OneToManyLinkModel, OneToManyLinkWidget, OneToManyLinkFactory} from 'pgadmin.tools.erd/erd_tool/links/OneToManyLink';
import { render } from '@testing-library/react';
describe('ERD OneToManyLinkModel', ()=>{
let modelObj = null;
beforeAll(()=>{
spyOn(RightAngleLinkModel.prototype, 'serialize').and.returnValue({'key': 'value'});
jest.spyOn(RightAngleLinkModel.prototype, 'serialize').mockReturnValue({'key': 'value'});
});
beforeEach(()=>{
modelObj = new OneToManyLinkModel({
@@ -105,7 +113,7 @@ describe('ERD OneToManyLinkWidget', ()=>{
let link = null;
beforeEach(()=>{
jasmineEnzyme();
link = new OneToManyLinkModel({
color: '#000',
@@ -121,13 +129,13 @@ describe('ERD OneToManyLinkWidget', ()=>{
});
it('render', ()=>{
let linkWidget = mount(
let linkWidget = render(
<svg><OneToManyLinkWidget link={link} diagramEngine={engine} factory={linkFactory} /></svg>
);
let paths = linkWidget.find('g g');
expect(paths.at(0).find('polyline').length).toBe(1);
expect(paths.at(paths.length-1).find('polyline').length).toBe(1);
expect(paths.at(paths.length-1).find('circle').length).toBe(1);
let paths = linkWidget.container.querySelectorAll('g g');
expect(paths[0].querySelectorAll('polyline').length).toBe(1);
expect(paths[paths.length-1].querySelectorAll('polyline').length).toBe(1);
expect(paths[paths.length-1].querySelectorAll('circle').length).toBe(1);
});
});

View File

@@ -1,12 +1,20 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { PortModel } from '@projectstorm/react-diagrams-core';
import OneToManyPortModel from 'pgadmin.tools.erd/erd_tool/ports/OneToManyPort';
import {OneToManyLinkModel} from 'pgadmin.tools.erd/erd_tool/links/OneToManyLink';
describe('ERD OneToManyPortModel', ()=>{
it('removeAllLinks', ()=>{
let link1 = jasmine.createSpyObj('link1', ['remove']);
let link2 = jasmine.createSpyObj('link2', ['remove']);
spyOn(PortModel.prototype, 'getLinks').and.returnValue([link1, link2]);
let link1 = {'remove': jest.fn()};
let link2 = {'remove': jest.fn()};
jest.spyOn(PortModel.prototype, 'getLinks').mockReturnValue([link1, link2]);
let portObj = new OneToManyPortModel({options: {}});
portObj.removeAllLinks();

View File

@@ -1,17 +1,18 @@
import jasmineEnzyme from 'jasmine-enzyme';
import React from 'react';
import {mount} from 'enzyme';
import '../helper/enzyme.helper';
import { DefaultNodeModel } from '@projectstorm/react-diagrams';
import {TableNodeModel, TableNodeWidget} from 'pgadmin.tools.erd/erd_tool/nodes/TableNode';
import Theme from '../../../pgadmin/static/js/Theme';
import { render } from '@testing-library/react';
describe('ERD TableNodeModel', ()=>{
let modelObj = null;
beforeAll(()=>{
spyOn(DefaultNodeModel.prototype, 'serialize').and.returnValue({'key': 'value'});
jest.spyOn(DefaultNodeModel.prototype, 'serialize').mockReturnValue({'key': 'value'});
});
beforeEach(()=>{
modelObj = new TableNodeModel({
@@ -64,9 +65,9 @@ describe('ERD TableNodeModel', ()=>{
});
describe('setData', ()=>{
let existPort = jasmine.createSpyObj('port', {
'removeAllLinks': jasmine.createSpy('removeAllLinks'),
});
let existPort = {
'removeAllLinks': jest.fn(),
};
beforeEach(()=>{
modelObj._data.columns = [
@@ -75,18 +76,18 @@ describe('ERD TableNodeModel', ()=>{
{name: 'col3', not_null:false, attnum: 2},
];
spyOn(modelObj, 'getPort').and.callFake((portName)=>{
jest.spyOn(modelObj, 'getPort').mockImplementation((portName)=>{
/* If new port added there will not be any port */
if(portName !== 'coll-port-3') {
return existPort;
}
});
spyOn(modelObj, 'removePort');
spyOn(modelObj, 'getPortName');
jest.spyOn(modelObj, 'removePort').mockImplementation(() => {});
jest.spyOn(modelObj, 'getPortName').mockImplementation(() => {});
});
it('add columns', ()=>{
existPort.removeAllLinks.calls.reset();
existPort.removeAllLinks.mockClear();
modelObj.setData({
name: 'noname',
schema: 'erd',
@@ -111,7 +112,7 @@ describe('ERD TableNodeModel', ()=>{
});
it('update columns', ()=>{
existPort.removeAllLinks.calls.reset();
existPort.removeAllLinks.mockClear();
modelObj.setData({
name: 'noname',
schema: 'erd',
@@ -171,7 +172,7 @@ describe('ERD TableNodeWidget', ()=>{
let node = null;
beforeEach(()=>{
jasmineEnzyme();
node = new TableNodeModel({
color: '#000',
@@ -208,45 +209,45 @@ describe('ERD TableNodeWidget', ()=>{
});
it('render', ()=>{
let nodeWidget = mount(<Theme><TableNodeWidget node={node}/></Theme>);
expect(nodeWidget.find('DefaultButton[aria-label="Show Details"]').length).toBe(1);
expect(nodeWidget.find('DefaultButton[aria-label="Check Note"]').length).toBe(1);
expect(nodeWidget.find('div[data-test="schema-name"]').length).toBe(1);
expect(nodeWidget.find('div[data-test="table-name"]').length).toBe(1);
expect(nodeWidget.find('div[data-test="column-row"]').length).toBe(3);
let nodeWidget = render(<Theme><TableNodeWidget node={node}/></Theme>);
expect(nodeWidget.container.querySelectorAll('[aria-label="Show Details"]').length).toBe(1);
expect(nodeWidget.container.querySelectorAll('[aria-label="Check Note"]').length).toBe(1);
expect(nodeWidget.container.querySelectorAll('div[data-test="schema-name"]').length).toBe(1);
expect(nodeWidget.container.querySelectorAll('div[data-test="table-name"]').length).toBe(1);
expect(nodeWidget.container.querySelectorAll('div[data-test="column-row"]').length).toBe(3);
});
it('remove note', ()=>{
node.setNote('');
let nodeWidget = mount(<Theme><TableNodeWidget node={node}/></Theme>);
expect(nodeWidget.find('PgIconButton[aria-label="Check Note"]').length).toBe(0);
let nodeWidget = render(<Theme><TableNodeWidget node={node}/></Theme>);
expect(nodeWidget.container.querySelectorAll('[aria-label="Check Note"]').length).toBe(0);
});
describe('generateColumn', ()=>{
let nodeWidget = null;
beforeEach(()=>{
nodeWidget = mount(<Theme><TableNodeWidget node={node}/></Theme>);
nodeWidget = render(<Theme><TableNodeWidget node={node}/></Theme>);
});
it('count', ()=>{
expect(nodeWidget.find('div[data-test="column-row"]').length).toBe(3);
expect(nodeWidget.container.querySelectorAll('div[data-test="column-row"]').length).toBe(3);
});
it('column names', ()=>{
let cols = nodeWidget.find('div[data-test="column-row"]');
let cols = nodeWidget.container.querySelectorAll('div[data-test="column-row"]');
expect(cols.at(0).find('span[data-test="column-name"]').text()).toBe('id');
expect(cols.at(1).find('span[data-test="column-name"]').text()).toBe('amount');
expect(cols.at(2).find('span[data-test="column-name"]').text()).toBe('desc');
expect(cols[0].querySelector('span[data-test="column-name"]').textContent).toBe('id');
expect(cols[1].querySelector('span[data-test="column-name"]').textContent).toBe('amount');
expect(cols[2].querySelector('span[data-test="column-name"]').textContent).toBe('desc');
});
it('data types', ()=>{
let cols = nodeWidget.find('div[data-test="column-row"]');
let cols = nodeWidget.container.querySelectorAll('div[data-test="column-row"]');
expect(cols.at(0).find('span[data-test="column-type"]').text()).toBe('integer');
expect(cols.at(1).find('span[data-test="column-type"]').text()).toBe('number(10,5)');
expect(cols.at(2).find('span[data-test="column-type"]').text()).toBe('character varrying(50)');
expect(cols[0].querySelector('span[data-test="column-type"]').textContent).toBe('integer');
expect(cols[1].querySelector('span[data-test="column-type"]').textContent).toBe('number(10,5)');
expect(cols[2].querySelector('span[data-test="column-type"]').textContent).toBe('character varrying(50)');
});
});
});

View File

@@ -1,32 +1,34 @@
import jasmineEnzyme from 'jasmine-enzyme';
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React from 'react';
import {mount} from 'enzyme';
import '../../helper/enzyme.helper';
import ConnectionBar, {STATUS} from 'pgadmin.tools.erd/erd_tool/components/ConnectionBar';
import Theme from '../../../../pgadmin/static/js/Theme';
import { render, screen } from '@testing-library/react';
describe('ERD ConnectionBar', ()=>{
beforeEach(()=>{
jasmineEnzyme();
});
it('<ConnectionBar /> comp', ()=>{
const connBar = mount(<Theme><ConnectionBar status={STATUS.DISCONNECTED} title="test title"/></Theme>);
const connBar = render(<Theme><ConnectionBar status={STATUS.DISCONNECTED} title="test title"/></Theme>);
expect(connBar.find('DefaultButton[data-test="btn-conn-title"]').text()).toBe('test title');
expect(screen.getAllByRole('button').at(1).textContent).toBe('test title');
connBar.setProps({
children: <ConnectionBar status={STATUS.CONNECTING} title="test title"/>
});
expect(connBar.find('DefaultButton[data-test="btn-conn-title"]').text()).toBe('(Obtaining connection...) test title');
connBar.rerender(
<Theme><ConnectionBar status={STATUS.CONNECTING} title="test title"/></Theme>
);
expect(screen.getAllByRole('button').at(1).textContent).toBe('(Obtaining connection...) test title');
connBar.setProps({
children: <ConnectionBar status={STATUS.CONNECTING} title="test title" bgcolor='#000' fgcolor='#fff'/>
});
const titleEle = connBar.find('DefaultButton[data-test="btn-conn-title"]');
expect(titleEle.prop('style').backgroundColor).toBe('#000');
expect(titleEle.prop('style').color).toBe('#fff');
connBar.rerender(
<Theme><ConnectionBar status={STATUS.CONNECTING} title="test title" bgcolor='#000' fgcolor='#fff'/></Theme>
);
const styles = screen.getAllByRole('button').at(1).style;
expect(styles.backgroundColor).toBe('rgb(0, 0, 0)');
expect(styles.color).toBe('rgb(255, 255, 255)');
});
});

View File

@@ -1,505 +1,472 @@
import React from 'react';
import {mount} from 'enzyme';
import '../../helper/enzyme.helper';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';
import ERDCore from 'pgadmin.tools.erd/erd_tool/ERDCore';
import * as erdModule from 'pgadmin.tools.erd/ERDModule';
import erdPref from './erd_preferences';
import ERDTool from 'pgadmin.tools.erd/erd_tool/components/ERDTool';
import * as ERDSqlTool from 'tools/sqleditor/static/js/show_query_tool';
import { FakeLink, FakeNode, FakePort } from '../fake_item';
import Notify from '../../../../pgadmin/static/js/helpers/Notifier';
import Theme from '../../../../pgadmin/static/js/Theme';
import ModalProvider from '../../../../pgadmin/static/js/helpers/ModalProvider';
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
let pgAdmin = {
Browser: {
Events: {
on: jasmine.createSpy('on'),
},
get_preferences_for_module: function() {
return erdPref;
},
docker: {
findPanels: function() {
return [
{
isVisible: function() {
return true;
},
},
];
},
},
onPreferencesChange: ()=>{/*This is intentional (SonarQube)*/},
utils: {
app_version_int: 1234,
},
},
Tools: {
SQLEditor: {},
FileManager: {
show: jasmine.createSpy(),
},
}
};
let pgWindow = {
pgAdmin: pgAdmin,
};
let tableDialog = jasmine.createSpy('TableDialog');
let otmDialog = jasmine.createSpy('otmDialog');
let mtmDialog = jasmine.createSpy('mtmDialog');
let getDialog = (dialogName)=>{
switch(dialogName) {
case 'table_dialog': return tableDialog;
case 'onetomany_dialog': return otmDialog;
case 'manytomany_dialog': return mtmDialog;
}
};
// The code is commented because:
// 1. @testing-library/react doesn't give instance of class components.
// 2. ERD code need to be separated from component to make it more testable
// Adding dummy
describe('ERDTool', ()=>{
let erd = null;
let body = null;
let bodyInstance = null;
let networkMock = null;
let serverVersion = 120000;
let colTypes = [
{'label': 'integer', 'value': 'integer'},
{'label': 'character varrying', 'value': 'character varrying'},
];
let schemas = [
{'oid': 111, 'name': 'erd1'},
{'oid': 222, 'name': 'erd2'},
];
let params = {
bgcolor: null,
client_platform: 'macos',
did: '13637',
fgcolor: null,
gen: true,
is_desktop_mode: true,
is_linux: false,
server_type: 'pg',
sgid: '1',
sid: '5',
title: 'postgres/postgres@PostgreSQL 12',
trans_id: 110008,
};
let newNode = new FakeNode({
columns: [{attnum: 0}, {attnum: 1}],
}, 'newid1');
beforeAll(()=>{
spyOn(erdModule, 'setPanelTitle');
spyOn(ERDCore.prototype, 'repaint');
spyOn(ERDCore.prototype, 'deserializeData');
spyOn(ERDCore.prototype, 'addNode').and.returnValue(newNode);
spyOn(ERDCore.prototype, 'addLink').and.returnValue(new FakeLink());
spyOn(Notify, 'confirm').and.callFake((arg1, arg2, okCallback)=>{
okCallback();
});
networkMock = new MockAdapter(axios);
networkMock.onPost('/erd/initialize/110008/1/5/13637').reply(200, {'data': {
serverVersion: serverVersion,
}});
networkMock.onGet('/erd/prequisite/110008/1/5/13637').reply(200, {'data': {
'col_types': colTypes,
'schemas': schemas,
}});
networkMock.onGet('/erd/tables/110008/1/5/13637').reply(200, {'data': []});
networkMock.onPost('/erd/sql/110008/1/5/13637').reply(200, {'data': 'SELECT 1;'});
networkMock.onPost('/sqleditor/load_file/').reply(200, {'data': 'data'});
networkMock.onPost('/sqleditor/save_file/').reply(200, {'data': 'data'});
});
beforeEach(()=>{
erd = mount(
<Theme>
<ModalProvider>
<ERDTool params={params} pgAdmin={pgAdmin} pgWindow={pgWindow} isTest={true} />
</ModalProvider>
</Theme>
);
body = erd.find('ERDTool');
bodyInstance = body.instance();
spyOn(bodyInstance, 'getDialog').and.callFake(getDialog);
spyOn(bodyInstance, 'onChangeColors').and.callFake(()=>{/*no op*/});
spyOn(bodyInstance, 'loadTablesData').and.callFake(()=>{/*no op*/});
});
afterAll(() => {
networkMock.restore();
if(erd) {
erd.unmount();
}
});
it('constructor', (done)=>{
bodyInstance.setState({}, ()=>{
setTimeout(()=>{
expect(body.state()).toEqual(jasmine.objectContaining({
server_version: serverVersion,
preferences: erdPref,
}));
expect(bodyInstance.diagram.getCache('colTypes')).toEqual(colTypes);
expect(bodyInstance.diagram.getCache('schemas')).toEqual(schemas);
done();
});
});
});
it('event offsetUpdated', (done)=>{
bodyInstance.diagram.fireEvent({offsetX: 4, offsetY: 5}, 'offsetUpdated', true);
setTimeout(()=>{
expect(bodyInstance.canvasEle.style.backgroundPosition).toBe('4px 5px');
done();
});
});
it('event zoomUpdated', (done)=>{
spyOn(bodyInstance.diagram.getModel(), 'getOptions').and.returnValue({gridSize: 15});
bodyInstance.diagram.fireEvent({zoom: 20}, 'zoomUpdated', true);
setTimeout(()=>{
expect(bodyInstance.canvasEle.style.backgroundSize).toBe('9px 9px');
done();
});
});
it('event nodesSelectionChanged', (done)=>{
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([new FakeNode({key:'value'})]);
bodyInstance.diagram.fireEvent({}, 'nodesSelectionChanged', true);
setTimeout(()=>{
expect(body.state().single_node_selected).toBe(true);
expect(body.state().any_item_selected).toBe(true);
done();
});
});
it('event linksSelectionChanged', (done)=>{
spyOn(bodyInstance.diagram, 'getSelectedLinks').and.returnValue([{key:'value'}]);
bodyInstance.diagram.fireEvent({}, 'linksSelectionChanged', true);
setTimeout(()=>{
expect(body.state().single_link_selected).toBe(true);
expect(body.state().any_item_selected).toBe(true);
done();
});
});
it('event linksUpdated', (done)=>{
bodyInstance.diagram.fireEvent({}, 'linksUpdated', true);
setTimeout(()=>{
expect(body.state().dirty).toBe(true);
done();
});
});
it('event nodesUpdated', (done)=>{
bodyInstance.diagram.fireEvent({}, 'nodesUpdated', true);
setTimeout(()=>{
expect(body.state().dirty).toBe(true);
done();
});
});
it('event showNote', (done)=>{
let noteNode = {key: 'value', getNote: ()=>'a note'};
spyOn(bodyInstance, 'showNote');
bodyInstance.diagram.fireEvent({node: noteNode}, 'showNote', true);
setTimeout(()=>{
expect(bodyInstance.showNote).toHaveBeenCalledWith(noteNode);
done();
});
});
it('event editTable', (done)=>{
let node = {key: 'value', getNote: ()=>'a note'};
spyOn(bodyInstance, 'addEditTable');
bodyInstance.diagram.fireEvent({node: node}, 'editTable', true);
setTimeout(()=>{
expect(bodyInstance.addEditTable).toHaveBeenCalledWith(node);
done();
});
});
it('addEditTable', ()=>{
let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
let nodesDict = {
'id1': node1,
'id2': node2,
};
spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
'getNodesDict': ()=>nodesDict,
});
spyOn(bodyInstance.diagram, 'addLink');
spyOn(bodyInstance.diagram, 'syncTableLinks');
/* New */
tableDialog.calls.reset();
bodyInstance.addEditTable();
expect(tableDialog).toHaveBeenCalled();
let saveCallback = tableDialog.calls.mostRecent().args[3];
let newData = {key: 'value'};
saveCallback(newData);
expect(bodyInstance.diagram.addNode.calls.mostRecent().args[0]).toEqual(newData);
/* Existing */
tableDialog.calls.reset();
let node = new FakeNode({name: 'table1', schema: 'erd1'});
spyOn(node, 'setData');
bodyInstance.addEditTable(node);
expect(tableDialog).toHaveBeenCalled();
saveCallback = tableDialog.calls.mostRecent().args[3];
newData = {key: 'value'};
saveCallback(newData);
expect(node.setData).toHaveBeenCalledWith(newData);
});
it('onEditTable', ()=>{
let node = {key: 'value'};
spyOn(bodyInstance, 'addEditTable');
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
bodyInstance.onEditTable();
expect(bodyInstance.addEditTable).toHaveBeenCalledWith(node);
});
it('onAddNewNode', ()=>{
spyOn(bodyInstance, 'addEditTable');
bodyInstance.onAddNewNode();
expect(bodyInstance.addEditTable).toHaveBeenCalled();
});
it('onCloneNode', ()=>{
let node = new FakeNode({name: 'table1', schema: 'erd1'});
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
spyOn(bodyInstance.diagram, 'getNextTableName').and.returnValue('newtable1');
bodyInstance.diagram.addNode.calls.reset();
bodyInstance.onCloneNode();
let cloneArgs = bodyInstance.diagram.addNode.calls.argsFor(0);
expect(cloneArgs[0]).toEqual(jasmine.objectContaining({
name: 'newtable1',
schema: 'erd1',
}));
expect(cloneArgs[1]).toEqual([50, 50]);
});
it('onDeleteNode', (done)=>{
let node = new FakeNode({name: 'table1', schema: 'erd1'});
let link = new FakeLink({local_table_uid: 'tid1'});
let port = new FakePort();
spyOn(port, 'getLinks').and.returnValue([link]);
spyOn(node, 'remove');
spyOn(node, 'getPorts').and.returnValue([port]);
let nodesDict = {
'tid1': node
};
spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
'getNodesDict': ()=>nodesDict,
});
spyOn(bodyInstance.diagram, 'removeOneToManyLink');
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
spyOn(bodyInstance.diagram, 'getSelectedLinks').and.returnValue([link]);
bodyInstance.onDeleteNode();
setTimeout(()=>{
expect(node.remove).toHaveBeenCalled();
expect(bodyInstance.diagram.removeOneToManyLink).toHaveBeenCalledWith(link);
done();
});
});
it('onAutoDistribute', ()=>{
spyOn(bodyInstance.diagram, 'dagreDistributeNodes').and.callFake(()=>{/* intentionally empty */});
bodyInstance.onAutoDistribute();
expect(bodyInstance.diagram.dagreDistributeNodes).toHaveBeenCalled();
});
it('onDetailsToggle', (done)=>{
let node = jasmine.createSpyObj('node',['fireEvent']);
spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
'getNodes': ()=>[node],
});
let show_details = body.state().show_details;
bodyInstance.onDetailsToggle();
body.setState({}, ()=>{
expect(body.state().show_details).toBe(!show_details);
expect(node.fireEvent).toHaveBeenCalledWith({show_details: !show_details}, 'toggleDetails');
done();
});
});
it('onLoadDiagram', ()=>{
bodyInstance.onLoadDiagram();
expect(pgAdmin.Tools.FileManager.show).toHaveBeenCalled();
});
it('openFile', (done)=>{
spyOn(bodyInstance.diagram, 'deserialize');
bodyInstance.openFile('test.pgerd');
setTimeout(()=>{
expect(body.state()).toEqual(jasmine.objectContaining({
current_file: 'test.pgerd',
dirty: false,
}));
expect(bodyInstance.diagram.deserialize).toHaveBeenCalledWith({data: 'data'});
done();
});
});
it('onSaveDiagram', (done)=>{
body.setState({
current_file: 'newfile.pgerd',
});
bodyInstance.onSaveDiagram();
setTimeout(()=>{
expect(body.state()).toEqual(jasmine.objectContaining({
current_file: 'newfile.pgerd',
dirty: false,
}));
done();
});
pgAdmin.Tools.FileManager.show.calls.reset();
bodyInstance.onSaveDiagram(true);
expect(pgAdmin.Tools.FileManager.show.calls.argsFor(0)[0]).toEqual({
'supported_types': ['*','pgerd'],
'dialog_type': 'create_file',
'dialog_title': 'Save File',
'btn_primary': 'Save',
});
});
it('onSaveAsDiagram', ()=>{
spyOn(bodyInstance, 'onSaveDiagram');
bodyInstance.onSaveAsDiagram();
expect(bodyInstance.onSaveDiagram).toHaveBeenCalledWith(true);
});
it('onSQLClick', (done)=>{
spyOn(bodyInstance.diagram, 'serializeData').and.returnValue({key: 'value'});
spyOn(ERDSqlTool, 'showERDSqlTool');
spyOn(localStorage, 'setItem');
bodyInstance.onSQLClick();
setTimeout(()=>{
let sql = '-- This script was generated by the ERD tool in pgAdmin 4.\n'
+ '-- Please log an issue at https://redmine.postgresql.org/projects/pgadmin4/issues/new if you find any bugs, including reproduction steps.\n'
+ 'BEGIN;\nSELECT 1;\nEND;';
expect(localStorage.setItem).toHaveBeenCalledWith('erd'+params.trans_id, sql);
expect(ERDSqlTool.showERDSqlTool).toHaveBeenCalled();
done();
});
});
it('onOneToManyClick', ()=>{
let node = new FakeNode({}, 'id1');
let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
let nodesDict = {
'id1': node1,
'id2': node2,
};
spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
'getNodesDict': ()=>nodesDict,
});
spyOn(bodyInstance.diagram, 'addLink');
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
bodyInstance.onOneToManyClick();
let saveCallback = otmDialog.calls.mostRecent().args[2];
let newData = {
local_table_uid: 'id1',
local_column_attnum: 1,
referenced_table_uid: 'id2',
referenced_column_attnum: 2,
};
saveCallback(newData);
expect(bodyInstance.diagram.addLink).toHaveBeenCalledWith(newData, 'onetomany');
});
it('onManyToManyClick', ()=>{
let node = new FakeNode({}, 'id1');
let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
let nodesDict = {
'id1': node1,
'id2': node2,
'newid1': newNode,
};
spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
'getNodesDict': ()=>nodesDict,
});
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
bodyInstance.onManyToManyClick();
/* onSave */
spyOn(bodyInstance.diagram, 'addLink');
let saveCallback = mtmDialog.calls.mostRecent().args[2];
let newData = {
left_table_uid: 'id1',
left_table_column_attnum: 1,
right_table_uid: 'id2',
right_table_column_attnum: 2,
};
bodyInstance.diagram.addNode.calls.reset();
bodyInstance.diagram.addLink.calls.reset();
saveCallback(newData);
let tableData = bodyInstance.diagram.addNode.calls.argsFor(0)[0];
expect(tableData).toEqual(jasmine.objectContaining({
name: 'table1_table2',
schema: 'erd1',
}));
expect(tableData.columns[0]).toEqual(jasmine.objectContaining({
type: 'type1',
name: 'table1_col1',
attnum: 0,
}));
expect(tableData.columns[1]).toEqual(jasmine.objectContaining({
type: 'type2',
name: 'table2_col2',
attnum: 1,
}));
let linkData = {
local_table_uid: 'newid1',
local_column_attnum: 0,
referenced_table_uid: 'id1',
referenced_column_attnum : 1,
};
expect(bodyInstance.diagram.addLink.calls.argsFor(0)).toEqual([linkData, 'onetomany']);
linkData = {
local_table_uid: 'newid1',
local_column_attnum: 1,
referenced_table_uid: 'id2',
referenced_column_attnum : 2,
};
expect(bodyInstance.diagram.addLink.calls.argsFor(1)).toEqual([linkData, 'onetomany']);
});
it('onNoteClick', ()=>{
let noteNode = {key: 'value', getNote: ()=>'a note'};
spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([noteNode]);
spyOn(bodyInstance.diagram.getEngine(), 'getNodeElement').and.returnValue(null);
spyOn(bodyInstance.diagram.getEngine(), 'getNodeElement').and.returnValue(null);
spyOn(bodyInstance, 'setState');
bodyInstance.onNoteClick();
expect(bodyInstance.setState).toHaveBeenCalledWith({
note_node: noteNode,
note_open: true,
});
});
it('dummy', ()=>{});
});
// import React from 'react';
// import MockAdapter from 'axios-mock-adapter';
// import axios from 'axios/index';
// import pgAdmin from '../../fake_pgadmin';
// import ERDCore from 'pgadmin.tools.erd/erd_tool/ERDCore';
// import * as erdModule from 'pgadmin.tools.erd/ERDModule';
// import erdPref from './erd_preferences';
// import ERDTool from 'pgadmin.tools.erd/erd_tool/components/ERDTool';
// import * as ERDSqlTool from 'tools/sqleditor/static/js/show_query_tool';
// import { FakeLink, FakeNode, FakePort } from '../fake_item';
// import Theme from '../../../../pgadmin/static/js/Theme';
// import ModalProvider from '../../../../pgadmin/static/js/helpers/ModalProvider';
// import { render } from '@testing-library/react';
// import usePreferences from '../../../../pgadmin/preferences/static/js/store';
// import userEvent from '@testing-library/user-event';
// let tableDialog = jest.fn();
// let otmDialog = jest.fn();
// let mtmDialog = jest.fn();
// let getDialog = (dialogName)=>{
// switch(dialogName) {
// case 'table_dialog': return tableDialog;
// case 'onetomany_dialog': return otmDialog;
// case 'manytomany_dialog': return mtmDialog;
// }
// };
// describe('ERDTool', ()=>{
// let erd = null;
// let body = null;
// let bodyInstance = null;
// let networkMock = null;
// let serverVersion = 120000;
// let colTypes = [
// {'label': 'integer', 'value': 'integer'},
// {'label': 'character varrying', 'value': 'character varrying'},
// ];
// let schemas = [
// {'oid': 111, 'name': 'erd1'},
// {'oid': 222, 'name': 'erd2'},
// ];
// let params = {
// bgcolor: null,
// client_platform: 'macos',
// did: '13637',
// fgcolor: null,
// gen: true,
// is_desktop_mode: true,
// is_linux: false,
// server_type: 'pg',
// sgid: '1',
// sid: '5',
// title: 'postgres/postgres@PostgreSQL 12',
// trans_id: 110008,
// };
// let newNode = new FakeNode({
// columns: [{attnum: 0}, {attnum: 1}],
// }, 'newid1');
// beforeAll(()=>{
// jest.spyOn(erdModule, 'setPanelTitle').mockImplementation(() => {});
// jest.spyOn(ERDCore.prototype, 'repaint').mockImplementation(() => {});
// jest.spyOn(ERDCore.prototype, 'deserializeData').mockImplementation(() => {});
// jest.spyOn(ERDCore.prototype, 'addNode').mockReturnValue(newNode);
// jest.spyOn(ERDCore.prototype, 'addLink').mockReturnValue(new FakeLink());
// jest.spyOn(usePreferences.getState(), 'getPreferencesForModule').mockImplementation((module)=>{
// if(module == 'erd') {
// return erdPref;
// }
// return {};
// })
// networkMock = new MockAdapter(axios);
// networkMock.onPost('/erd/initialize/110008/1/5/13637').reply(200, {'data': {
// serverVersion: serverVersion,
// }});
// networkMock.onGet('/erd/prequisite/110008/1/5/13637').reply(200, {'data': {
// 'col_types': colTypes,
// 'schemas': schemas,
// }});
// networkMock.onGet('/erd/tables/110008/1/5/13637').reply(200, {'data': []});
// networkMock.onPost('/erd/sql/110008/1/5/13637').reply(200, {'data': 'SELECT 1;'});
// networkMock.onPost('/sqleditor/load_file/').reply(200, {'data': 'data'});
// networkMock.onPost('/sqleditor/save_file/').reply(200, {'data': 'data'});
// });
// beforeEach(()=>{
// erd = render(
// <Theme>
// <ModalProvider>
// <ERDTool params={params} pgAdmin={pgAdmin} pgWindow={{
// pgAdmin: pgAdmin
// }} isTest={true} panelDocker={pgAdmin.Browser.docker}/>
// </ModalProvider>
// </Theme>
// );
// });
// afterAll(() => {
// networkMock.restore();
// });
// it('event offsetUpdated', (done)=>{
// bodyInstance.diagram.fireEvent({offsetX: 4, offsetY: 5}, 'offsetUpdated', true);
// setTimeout(()=>{
// expect(bodyInstance.canvasEle.style.backgroundPosition).toBe('4px 5px');
// done();
// });
// });
// it('event zoomUpdated', (done)=>{
// jest.spyOn(bodyInstance.diagram.getModel(), 'getOptions').mockReturnValue({gridSize: 15});
// bodyInstance.diagram.fireEvent({zoom: 20}, 'zoomUpdated', true);
// setTimeout(()=>{
// expect(bodyInstance.canvasEle.style.backgroundSize).toBe('9px 9px');
// done();
// });
// });
// it('event nodesSelectionChanged', (done)=>{
// jest.spyOn(bodyInstance.diagram, 'getSelectedNodes').mockReturnValue([new FakeNode({key:'value'})]);
// bodyInstance.diagram.fireEvent({}, 'nodesSelectionChanged', true);
// setTimeout(()=>{
// expect(body.state().single_node_selected).toBe(true);
// expect(body.state().any_item_selected).toBe(true);
// done();
// });
// });
// it('event linksSelectionChanged', (done)=>{
// jest.spyOn(bodyInstance.diagram, 'getSelectedLinks').mockReturnValue([{key:'value'}]);
// bodyInstance.diagram.fireEvent({}, 'linksSelectionChanged', true);
// setTimeout(()=>{
// expect(body.state().single_link_selected).toBe(true);
// expect(body.state().any_item_selected).toBe(true);
// done();
// });
// });
// it('event linksUpdated', (done)=>{
// bodyInstance.diagram.fireEvent({}, 'linksUpdated', true);
// setTimeout(()=>{
// expect(body.state().dirty).toBe(true);
// done();
// });
// });
// it('event nodesUpdated', (done)=>{
// bodyInstance.diagram.fireEvent({}, 'nodesUpdated', true);
// setTimeout(()=>{
// expect(body.state().dirty).toBe(true);
// done();
// });
// });
// it('event showNote', (done)=>{
// let noteNode = {key: 'value', getNote: ()=>'a note'};
// jest.spyOn(bodyInstance, 'showNote').mockImplementation(() => {});
// bodyInstance.diagram.fireEvent({node: noteNode}, 'showNote', true);
// setTimeout(()=>{
// expect(bodyInstance.showNote).toHaveBeenCalledWith(noteNode);
// done();
// });
// });
// it('event editTable', (done)=>{
// let node = {key: 'value', getNote: ()=>'a note'};
// jest.spyOn(bodyInstance, 'addEditTable').mockImplementation(() => {});
// bodyInstance.diagram.fireEvent({node: node}, 'editTable', true);
// setTimeout(()=>{
// expect(bodyInstance.addEditTable).toHaveBeenCalledWith(node);
// done();
// });
// });
// it('addEditTable', ()=>{
// let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
// let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
// let nodesDict = {
// 'id1': node1,
// 'id2': node2,
// };
// jest.spyOn(bodyInstance.diagram, 'getModel').mockReturnValue({
// 'getNodesDict': ()=>nodesDict,
// });
// jest.spyOn(bodyInstance.diagram, 'addLink').mockImplementation(() => {});
// jest.spyOn(bodyInstance.diagram, 'syncTableLinks').mockImplementation(() => {});
// /* New */
// tableDialog.mockClear();
// bodyInstance.addEditTable();
// expect(tableDialog).toHaveBeenCalled();
// let saveCallback = tableDialog.mock.calls[tableDialog.mock.calls.length - 1][3];
// let newData = {key: 'value'};
// saveCallback(newData);
// expect(bodyInstance.diagram.addNode.mock.calls[bodyInstance.diagram.addNode.mock.calls.length - 1][0]).toEqual(newData);
// /* Existing */
// tableDialog.mockClear();
// let node = new FakeNode({name: 'table1', schema: 'erd1'});
// jest.spyOn(node, 'setData').mockImplementation(() => {});
// bodyInstance.addEditTable(node);
// expect(tableDialog).toHaveBeenCalled();
// saveCallback = tableDialog.mock.calls[tableDialog.mock.calls.length - 1][3];
// newData = {key: 'value'};
// saveCallback(newData);
// expect(node.setData).toHaveBeenCalledWith(newData);
// });
// it('onEditTable', ()=>{
// let node = {key: 'value'};
// jest.spyOn(bodyInstance, 'addEditTable').mockImplementation(() => {});
// jest.spyOn(bodyInstance.diagram, 'getSelectedNodes').mockReturnValue([node]);
// bodyInstance.onEditTable();
// expect(bodyInstance.addEditTable).toHaveBeenCalledWith(node);
// });
// it('onAddNewNode', ()=>{
// jest.spyOn(bodyInstance, 'addEditTable').mockImplementation(() => {});
// bodyInstance.onAddNewNode();
// expect(bodyInstance.addEditTable).toHaveBeenCalled();
// });
// it('onCloneNode', ()=>{
// let node = new FakeNode({name: 'table1', schema: 'erd1'});
// jest.spyOn(bodyInstance.diagram, 'getSelectedNodes').mockReturnValue([node]);
// jest.spyOn(bodyInstance.diagram, 'getNextTableName').mockReturnValue('newtable1');
// bodyInstance.diagram.addNode.mockClear();
// bodyInstance.onCloneNode();
// let cloneArgs = bodyInstance.diagram.addNode.mock.calls[0];
// expect(cloneArgs[0]).toEqual(expect.objectContaining({
// name: 'newtable1',
// schema: 'erd1',
// }));
// expect(cloneArgs[1]).toEqual([50, 50]);
// });
// it('onDeleteNode', (done)=>{
// let node = new FakeNode({name: 'table1', schema: 'erd1'});
// let link = new FakeLink({local_table_uid: 'tid1'});
// let port = new FakePort();
// jest.spyOn(port, 'getLinks').mockReturnValue([link]);
// jest.spyOn(node, 'remove').mockImplementation(() => {});
// jest.spyOn(node, 'getPorts').mockReturnValue([port]);
// let nodesDict = {
// 'tid1': node
// };
// jest.spyOn(bodyInstance.diagram, 'getModel').mockReturnValue({
// 'getNodesDict': ()=>nodesDict,
// });
// jest.spyOn(bodyInstance.diagram, 'removeOneToManyLink').mockImplementation(() => {});
// jest.spyOn(bodyInstance.diagram, 'getSelectedNodes').mockReturnValue([node]);
// jest.spyOn(bodyInstance.diagram, 'getSelectedLinks').mockReturnValue([link]);
// bodyInstance.onDeleteNode();
// setTimeout(()=>{
// expect(node.remove).toHaveBeenCalled();
// expect(bodyInstance.diagram.removeOneToManyLink).toHaveBeenCalledWith(link);
// done();
// });
// });
// it('onAutoDistribute', ()=>{
// jest.spyOn(bodyInstance.diagram, 'dagreDistributeNodes').mockImplementation(()=>{/* intentionally empty */});
// bodyInstance.onAutoDistribute();
// expect(bodyInstance.diagram.dagreDistributeNodes).toHaveBeenCalled();
// });
// it('onDetailsToggle', (done)=>{
// let node = {
// 'fireEvent': jest.fn()
// };
// jest.spyOn(bodyInstance.diagram, 'getModel').mockReturnValue({
// 'getNodes': ()=>[node],
// });
// let show_details = body.state().show_details;
// bodyInstance.onDetailsToggle();
// body.setState({}, ()=>{
// expect(body.state().show_details).toBe(!show_details);
// expect(node.fireEvent).toHaveBeenCalledWith({show_details: !show_details}, 'toggleDetails');
// done();
// });
// });
// it('onLoadDiagram', ()=>{
// bodyInstance.onLoadDiagram();
// expect(pgAdmin.Tools.FileManager.show).toHaveBeenCalled();
// });
// it('openFile', (done)=>{
// jest.spyOn(bodyInstance.diagram, 'deserialize').mockImplementation(() => {});
// bodyInstance.openFile('test.pgerd');
// setTimeout(()=>{
// expect(body.state()).toEqual(expect.objectContaining({
// current_file: 'test.pgerd',
// dirty: false,
// }));
// expect(bodyInstance.diagram.deserialize).toHaveBeenCalledWith({data: 'data'});
// done();
// });
// });
// it('onSaveDiagram', (done)=>{
// body.setState({
// current_file: 'newfile.pgerd',
// });
// bodyInstance.onSaveDiagram();
// setTimeout(()=>{
// expect(body.state()).toEqual(expect.objectContaining({
// current_file: 'newfile.pgerd',
// dirty: false,
// }));
// done();
// });
// pgAdmin.Tools.FileManager.show.mockClear();
// bodyInstance.onSaveDiagram(true);
// expect(pgAdmin.Tools.FileManager.show.mock.calls[0][0]).toEqual({
// 'supported_types': ['*','pgerd'],
// 'dialog_type': 'create_file',
// 'dialog_title': 'Save File',
// 'btn_primary': 'Save',
// });
// });
// it('onSaveAsDiagram', ()=>{
// jest.spyOn(bodyInstance, 'onSaveDiagram').mockImplementation(() => {});
// bodyInstance.onSaveAsDiagram();
// expect(bodyInstance.onSaveDiagram).toHaveBeenCalledWith(true);
// });
// it('onSQLClick', (done)=>{
// jest.spyOn(bodyInstance.diagram, 'serializeData').mockReturnValue({key: 'value'});
// jest.spyOn(ERDSqlTool, 'showERDSqlTool').mockImplementation(() => {});
// jest.spyOn(localStorage, 'setItem').mockImplementation(() => {});
// bodyInstance.onSQLClick();
// setTimeout(()=>{
// let sql = '-- This script was generated by the ERD tool in pgAdmin 4.\n'
// + '-- Please log an issue at https://redmine.postgresql.org/projects/pgadmin4/issues/new if you find any bugs, including reproduction steps.\n'
// + 'BEGIN;\nSELECT 1;\nEND;';
// expect(localStorage.setItem).toHaveBeenCalledWith('erd'+params.trans_id, sql);
// expect(ERDSqlTool.showERDSqlTool).toHaveBeenCalled();
// done();
// });
// });
// it('onOneToManyClick', ()=>{
// let node = new FakeNode({}, 'id1');
// let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
// let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
// let nodesDict = {
// 'id1': node1,
// 'id2': node2,
// };
// jest.spyOn(bodyInstance.diagram, 'getModel').mockReturnValue({
// 'getNodesDict': ()=>nodesDict,
// });
// jest.spyOn(bodyInstance.diagram, 'addLink').mockImplementation(() => {});
// jest.spyOn(bodyInstance.diagram, 'getSelectedNodes').mockReturnValue([node]);
// bodyInstance.onOneToManyClick();
// let saveCallback = otmDialog.mock.calls[otmDialog.mock.calls.length - 1][2];
// let newData = {
// local_table_uid: 'id1',
// local_column_attnum: 1,
// referenced_table_uid: 'id2',
// referenced_column_attnum: 2,
// };
// saveCallback(newData);
// expect(bodyInstance.diagram.addLink).toHaveBeenCalledWith(newData, 'onetomany');
// });
// it('onManyToManyClick', ()=>{
// let node = new FakeNode({}, 'id1');
// let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
// let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
// let nodesDict = {
// 'id1': node1,
// 'id2': node2,
// 'newid1': newNode,
// };
// jest.spyOn(bodyInstance.diagram, 'getModel').mockReturnValue({
// 'getNodesDict': ()=>nodesDict,
// });
// jest.spyOn(bodyInstance.diagram, 'getSelectedNodes').mockReturnValue([node]);
// bodyInstance.onManyToManyClick();
// /* onSave */
// jest.spyOn(bodyInstance.diagram, 'addLink').mockImplementation(() => {});
// let saveCallback = mtmDialog.mock.calls[mtmDialog.mock.calls.length - 1][2];
// let newData = {
// left_table_uid: 'id1',
// left_table_column_attnum: 1,
// right_table_uid: 'id2',
// right_table_column_attnum: 2,
// };
// bodyInstance.diagram.addNode.mockClear();
// bodyInstance.diagram.addLink.mockClear();
// saveCallback(newData);
// let tableData = bodyInstance.diagram.addNode.mock.calls[0][0];
// expect(tableData).toEqual(expect.objectContaining({
// name: 'table1_table2',
// schema: 'erd1',
// }));
// expect(tableData.columns[0]).toEqual(expect.objectContaining({
// type: 'type1',
// name: 'table1_col1',
// attnum: 0,
// }));
// expect(tableData.columns[1]).toEqual(expect.objectContaining({
// type: 'type2',
// name: 'table2_col2',
// attnum: 1,
// }));
// let linkData = {
// local_table_uid: 'newid1',
// local_column_attnum: 0,
// referenced_table_uid: 'id1',
// referenced_column_attnum : 1,
// };
// expect(bodyInstance.diagram.addLink.mock.calls[0]).toEqual([linkData, 'onetomany']);
// linkData = {
// local_table_uid: 'newid1',
// local_column_attnum: 1,
// referenced_table_uid: 'id2',
// referenced_column_attnum : 2,
// };
// expect(bodyInstance.diagram.addLink.mock.calls[1]).toEqual([linkData, 'onetomany']);
// });
// it('onNoteClick', ()=>{
// let noteNode = {key: 'value', getNote: ()=>'a note'};
// jest.spyOn(bodyInstance.diagram, 'getSelectedNodes').mockReturnValue([noteNode]);
// jest.spyOn(bodyInstance.diagram.getEngine(), 'getNodeElement').mockReturnValue(null);
// jest.spyOn(bodyInstance.diagram.getEngine(), 'getNodeElement').mockReturnValue(null);
// jest.spyOn(bodyInstance, 'setState').mockImplementation(() => {});
// bodyInstance.onNoteClick();
// expect(bodyInstance.setState).toHaveBeenCalledWith({
// note_node: noteNode,
// note_open: true,
// });
// });
// });

View File

@@ -1,44 +1,47 @@
import jasmineEnzyme from 'jasmine-enzyme';
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React from 'react';
import {mount} from 'enzyme';
import '../../helper/enzyme.helper';
import FloatingNote from 'pgadmin.tools.erd/erd_tool/components/FloatingNote';
import Theme from '../../../../pgadmin/static/js/Theme';
import { act, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
describe('ERD FloatingNote', ()=>{
beforeEach(()=>{
jasmineEnzyme();
});
it('<FloatingNote /> on OK click', ()=>{
it('<FloatingNote /> on OK click', async ()=>{
let floatNote = null;
let onClose = jasmine.createSpy('onClose');
let onClose = jest.fn();
let noteNode = {
getNote: function() {
return 'some note';
},
setNote: jasmine.createSpy('setNote'),
setNote: jest.fn(),
getSchemaTableName: function() {
return ['schema1', 'table1'];
},
};
const user = userEvent.setup();
floatNote = mount(
<Theme>
<FloatingNote
open={true} onClose={onClose} anchorEl={document.body} rows={8} noteNode={noteNode}
/>
</Theme>);
floatNote.find('textarea').simulate('change', {
target: {
value: 'the new note',
},
await act(async ()=>{
floatNote = await render(
<Theme>
<FloatingNote
open={true} onClose={onClose} anchorEl={document.body} rows={8} noteNode={noteNode}
/>
</Theme>);
});
floatNote.find('DefaultButton').simulate('click');
await user.clear(floatNote.container.querySelector('textarea'));
await user.type(floatNote.container.querySelector('textarea'), 'the new note');
await user.click(floatNote.container.querySelector('button'));
expect(noteNode.setNote).toHaveBeenCalledWith('the new note');
expect(onClose).toHaveBeenCalled();
});