pgadmin4/web/regression/javascript/SchemaView/store.spec.js
Ashesh Vashi e9af0c3226
Improved the extendability of the SchemaView and DataGridView. (#7876)
Restructured these modules for ease of maintenance and apply the single
responsibility principle (wherever applicable).

* SchemaView

 - Split the code based on the functionality and responsibility.
 - Introduced a new View 'InlineView' instead of using the
   'nextInline' configuration of the fields to have a better, and
   manageable view.
 - Using the separate class 'SchemaState' for managing the data and
   states of the SchemaView (separated from the 'useSchemaState'
   custom hook).
 - Introduced three new custom hooks 'useFieldValue',
   'useFieldOptions', 'useFieldError' for the individual control to
   use for each Schema Field.
 - Don't pass value as the parameter props, and let the
   'useFieldValue' and other custom hooks to decide, whether to
   rerender the control itself or the whole dialog/view. (single
   responsibility principle)
 - Introduced a new data store with a subscription facility.
 - Moving the field metadata (option) evaluation to a separate place
   for better management, and each option can be defined for a
   particular kind of field (for example - collection, row, cell,
   general, etc).
 - Allow to provide custom control for all kind of Schema field.

* DataGridView

 - Same as SchemaView, split the DataGridView call into smaller,
   manageable chunks. (For example - grid, row, mappedCell, etc).
 - Use context based approach for providing the row and table data
   instead of passing them as parameters to every component
   separately.
 - Have a facility to extend this feature separately in future.
   (for example - selectable cell, column grouping, etc.)
 - Separated the features like deletable, editable, reorder,
   expandable etc. cells using the above feature support.
 - Added ability to provide the CustomHeader, and CustomRow through the
   Schema field, which will extend the ability to customize better.
 - Removed the 'DataGridViewWithHeaderForm' as it has been achieved
   through providing 'CustomHeader', and also introduced
   'DataGridFormHeader' (a custom header) to achieve the same feature
   as 'DataGridViewWithHeaderForm'.
2024-09-09 14:27:31 +05:30

158 lines
4.4 KiB
JavaScript

/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { isValueEqual } from '../../../pgadmin/static/js/SchemaView/common';
import {
createStore
} from '../../../pgadmin/static/js/SchemaView/SchemaState/store';
const initData = {
id: 1,
field1: 'field1val',
field2: 1,
fieldcoll: [
{field3: 1, field4: 'field4val1', field5: 'field5val1'},
{field3: 2, field4: 'field4val2', field5: 'field5val2'},
],
field3: 3,
field4: 'field4val',
};
describe('store', ()=>{
describe('', () => {
it('getState', () => {
const store = createStore(initData);
const data = store.getState();
expect(isValueEqual(data, initData)).toBe(true);
});
it('get', () => {
const store = createStore(initData);
const firstField3 = store.get(['fieldcoll', 0, 'field3']);
expect(firstField3 == 1).toBe(true);
const firstFieldCollRow = store.get(['fieldcoll', '0']);
// Sending a copy of the data, and not itself.
expect(isValueEqual(firstFieldCollRow, initData.fieldcoll[0])).toBe(true);
});
it('setState', () => {
const store = createStore(initData);
const newData = {a: 1};
store.setState(newData);
const newState = store.getState();
expect(Object.is(newState, newData)).toBe(false);
expect(isValueEqual(newState, newData)).toBe(true);
});
it ('set', () => {
const store = createStore(initData);
const newData = {a: 1};
store.set(newData);
let newState = store.getState();
expect(Object.is(newState, newData)).toBe(false);
expect(isValueEqual(newState, newData)).toBe(true);
store.set((prevState) => ({...prevState, initData}));
newState = store.getState();
expect(Object.is(newState, initData)).toBe(false);
expect(isValueEqual(newState, initData)).toBe(false);
delete newState['a'];
store.set(() => (newState));
newState = store.getState();
expect(isValueEqual(newState, initData)).toBe(false);
});
it ('subscribe', () => {
const store = createStore(initData);
const listener = jest.fn();
const unsubscribe1 = store.subscribe(listener);
store.set((prevState) => (prevState));
expect(listener).not.toHaveBeenCalled();
store.set((prevState) => {
prevState.id = 2;
return prevState;
});
expect(listener).toHaveBeenCalled();
const listenForFirstField3 = jest.fn();
const unsubscribe2 = store.subscribeForPath(
['fieldcoll', '0', 'field3'], listenForFirstField3
);
const listenForSecondField3 = jest.fn();
const unsubscribe3 = store.subscribeForPath(
['fieldcoll', '1', 'field3'], listenForSecondField3
);
let changeTo = 10;
store.set((prevState) => {
prevState.fieldcoll[0].field3 = changeTo;
return prevState;
});
expect(listenForFirstField3).toHaveBeenCalled();
expect(listener).toHaveBeenCalledTimes(2);
expect(listenForSecondField3).not.toHaveBeenCalled();
store.set((prevState) => {
// There is no actual change from previous state.
prevState.fieldcoll[0].field3 = 10;
return prevState;
});
// Not expecting it be called.
expect(listenForFirstField3).toHaveBeenCalledTimes(1);
expect(listener).toHaveBeenCalledTimes(2);
expect(listenForSecondField3).not.toHaveBeenCalled();
unsubscribe1();
store.set((prevState) => {
prevState.fieldcoll[0].field3 = 50;
return prevState;
});
// Don't expect this to be called again.
expect(listener).toHaveBeenCalledTimes(2);
// Expect this one to be called
expect(listenForFirstField3).toHaveBeenCalledTimes(2);
expect(listenForSecondField3).not.toHaveBeenCalled();
unsubscribe2();
store.set((prevState) => {
prevState.fieldcoll[0].field3 = 100;
return prevState;
});
// Don't expect any of them to be called.
expect(listener).toHaveBeenCalledTimes(2);
expect(listenForFirstField3).toHaveBeenCalledTimes(2);
expect(listenForSecondField3).not.toHaveBeenCalled();
unsubscribe3();
});
});
});