Increase test coverage for /modules/views (#3211)

* Increase test coverage for `/modules/views`

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>

* fix failing test

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>

* Refactor into smaller tests

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>

* fix linter

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>

* Fix unknown

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>
Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
gitstart-twenty 2024-01-05 09:58:19 +01:00 committed by GitHub
parent db17d46af3
commit 80c1c9aacc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1271 additions and 0 deletions

View File

@ -0,0 +1,47 @@
import { renderHook } from '@testing-library/react';
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
describe('useMoveViewColumns', () => {
it('should move columns to the left correctly', () => {
const { result } = renderHook(() => useMoveViewColumns());
const initialArray = [{ position: 0 }, { position: 1 }, { position: 2 }];
const movedArray = result.current.handleColumnMove('left', 1, initialArray);
expect(movedArray).toEqual([
{ position: 0, index: 0 },
{ position: 1, index: 1 },
{ position: 2 },
]);
});
it('should move columns to the right correctly', () => {
const { result } = renderHook(() => useMoveViewColumns());
const initialArray = [{ position: 0 }, { position: 1 }, { position: 2 }];
const movedArray = result.current.handleColumnMove(
'right',
1,
initialArray,
);
expect(movedArray).toEqual([
{ position: 0 },
{ position: 1, index: 1 },
{ position: 2, index: 2 },
]);
});
it('should handle invalid moves without modifying the array', () => {
const { result } = renderHook(() => useMoveViewColumns());
const initialArray = [{ position: 0 }, { position: 1 }, { position: 2 }];
const movedArray = result.current.handleColumnMove('left', 0, initialArray);
expect(movedArray).toEqual(initialArray);
});
});

View File

@ -0,0 +1,322 @@
import { act } from 'react-dom/test-utils';
import { MemoryRouter, useSearchParams } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import { renderHook, waitFor } from '@testing-library/react';
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil';
import { v4 as uuidv4 } from 'uuid';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { generateDeleteOneRecordMutation } from '@/object-record/utils/generateDeleteOneRecordMutation';
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
import {
filterDefinition,
viewFilter,
} from '@/views/hooks/__tests__/useViewBar_ViewFilters.test';
import {
sortDefinition,
viewSort,
} from '@/views/hooks/__tests__/useViewBar_ViewSorts.test';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useViewBar } from '@/views/hooks/useViewBar';
import { ViewScope } from '@/views/scopes/ViewScope';
import { entityCountInCurrentViewScopedState } from '@/views/states/entityCountInCurrentViewScopedState';
import { viewEditModeScopedState } from '@/views/states/viewEditModeScopedState';
import { viewObjectMetadataIdScopeState } from '@/views/states/viewObjectMetadataIdScopeState';
import { viewTypeScopedState } from '@/views/states/viewTypeScopedState';
import { ViewType } from '@/views/types/ViewType';
jest.mock('@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery', () => {
return {
useMapFieldMetadataToGraphQLQuery: jest.fn().mockReturnValue(() => '\n'),
};
});
const mockedUuid = 'mocked-uuid';
jest.mock('uuid');
(uuidv4 as jest.Mock).mockReturnValue(mockedUuid);
const mocks = [
{
request: {
query: generateDeleteOneRecordMutation({
objectMetadataItem: { nameSingular: 'view' } as ObjectMetadataItem,
}),
variables: { idToDelete: mockedUuid },
},
result: jest.fn(() => ({
data: { deleteView: { id: '' } },
})),
},
];
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<MemoryRouter
initialEntries={['/one', '/two', { pathname: '/three' }]}
initialIndex={1}
>
<MockedProvider mocks={mocks} addTypename={false}>
<RecoilRoot>
<ViewScope viewScopeId="viewScopeId">{children}</ViewScope>
</RecoilRoot>
</MockedProvider>
</MemoryRouter>
);
const renderHookConfig = {
wrapper: Wrapper,
};
const viewBarId = 'viewBarTestId';
describe('useViewBar', () => {
it('should set and get current view Id', () => {
const { result } = renderHook(
() => useViewBar({ viewBarId }),
renderHookConfig,
);
expect(result.current.scopeId).toBe(viewBarId);
expect(result.current.currentViewId).toBeUndefined();
act(() => {
result.current.setCurrentViewId('testId');
});
expect(result.current.currentViewId).toBe('testId');
});
it('should create view and update url params', async () => {
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
const searchParams = useSearchParams();
return {
viewBar,
searchParams,
};
}, renderHookConfig);
await act(async () => {
await result.current.viewBar.createView('Test View');
});
expect(result.current.searchParams[0].get('view')).toBe(mockedUuid);
});
it('should delete current view and remove id from params', async () => {
const { result } = renderHook(
() => ({
viewBar: useViewBar({ viewBarId }),
searchParams: useSearchParams(),
}),
renderHookConfig,
);
await act(async () => {
await result.current.viewBar.createView('Test View');
result.current.viewBar.setCurrentViewId(mockedUuid);
});
expect(result.current.searchParams[0].get('view')).toBe(mockedUuid);
await act(async () => {
await result.current.viewBar.removeView(mockedUuid);
});
expect(result.current.searchParams[0].get('view')).toBeNull();
const addBookMutationMock = mocks[0].result;
await waitFor(() => expect(addBookMutationMock).toHaveBeenCalled());
});
it('should resetViewBar', async () => {
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
const {
currentViewFiltersState,
currentViewSortsState,
viewEditModeState,
} = useViewScopedStates({
viewScopeId: viewBarId,
});
const currentViewFilters = useRecoilValue(currentViewFiltersState);
const currentViewSorts = useRecoilValue(currentViewSortsState);
const viewEditMode = useRecoilValue(viewEditModeState);
return {
viewBar,
currentViewFilters,
currentViewSorts,
viewEditMode,
};
}, renderHookConfig);
act(() => {
result.current.viewBar.resetViewBar();
});
expect(result.current.currentViewFilters).toStrictEqual([]);
expect(result.current.currentViewSorts).toStrictEqual([]);
expect(result.current.viewEditMode).toBe('none');
});
it('should handleViewNameSubmit', async () => {
const { result } = renderHook(
() => useViewBar({ viewBarId }),
renderHookConfig,
);
await act(async () => {
await result.current.handleViewNameSubmit('New View Name');
});
});
it('should update edit mode', async () => {
const { result } = renderHook(
() => ({
viewBar: useViewBar({ viewBarId }),
editMode: useRecoilState(
getScopedStateDeprecated(viewEditModeScopedState, viewBarId),
)[0],
}),
renderHookConfig,
);
expect(result.current.editMode).toBe('none');
await act(async () => {
result.current.viewBar.setViewEditMode('create');
});
expect(result.current.editMode).toBe('create');
await act(async () => {
result.current.viewBar.setViewEditMode('edit');
});
expect(result.current.editMode).toBe('edit');
});
it('should update url param', async () => {
const { result } = renderHook(
() => ({
viewBar: useViewBar({ viewBarId }),
searchParams: useSearchParams(),
}),
renderHookConfig,
);
expect(result.current.searchParams[0].get('view')).toBeNull();
await act(async () => {
result.current.viewBar.changeViewInUrl('view1');
});
expect(result.current.searchParams[0].get('view')).toBe('view1');
});
it('should update object metadata id', async () => {
const { result } = renderHook(
() => ({
viewBar: useViewBar({ viewBarId }),
metadataId: useRecoilState(
getScopedStateDeprecated(viewObjectMetadataIdScopeState, viewBarId),
)[0],
}),
renderHookConfig,
);
expect(result.current.metadataId).toBeUndefined();
await act(async () => {
result.current.viewBar.setViewObjectMetadataId('newId');
});
expect(result.current.metadataId).toBe('newId');
});
it('should update view type', async () => {
const { result } = renderHook(
() => ({
viewBar: useViewBar({ viewBarId }),
ViewType: useRecoilState(
getScopedStateDeprecated(viewTypeScopedState, viewBarId),
)[0],
}),
renderHookConfig,
);
expect(result.current.ViewType).toBe('table');
await act(async () => {
result.current.viewBar.setViewType(ViewType.Kanban);
});
expect(result.current.ViewType).toBe('kanban');
});
it('should update count in current view', async () => {
const { result } = renderHook(
() => ({
viewBar: useViewBar({ viewBarId }),
count: useRecoilState(
getScopedStateDeprecated(
entityCountInCurrentViewScopedState,
viewBarId,
),
)[0],
}),
renderHookConfig,
);
expect(result.current.count).toBe(0);
await act(async () => {
result.current.viewBar.setEntityCountInCurrentView(1);
});
expect(result.current.count).toBe(1);
});
it('should loadView', async () => {
const { result } = renderHook(
() => useViewBar({ viewBarId }),
renderHookConfig,
);
act(() => {
result.current.loadView(mockedUuid);
});
});
it('should updateCurrentView', async () => {
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
viewBar.setCurrentViewId(mockedUuid);
viewBar.setAvailableSortDefinitions([sortDefinition]);
viewBar.loadViewSorts(
{
edges: [
{
node: viewSort,
cursor: '',
},
],
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
},
mockedUuid,
);
viewBar.setAvailableFilterDefinitions([filterDefinition]);
viewBar.loadViewFilters(
{
edges: [
{
node: viewFilter,
cursor: '',
},
],
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
},
mockedUuid,
);
return { viewBar };
}, renderHookConfig);
await act(async () => {
await result.current.viewBar.updateCurrentView();
});
});
});

View File

@ -0,0 +1,179 @@
import { act } from 'react-dom/test-utils';
import { MemoryRouter } from 'react-router-dom';
import { gql } from '@apollo/client';
import { MockedProvider } from '@apollo/client/testing';
import { renderHook, waitFor } from '@testing-library/react';
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil';
import { FieldMetadata } from '@/object-record/field/types/FieldMetadata';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { getScopedFamilyStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedFamilyStateDeprecated';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useViewBar } from '@/views/hooks/useViewBar';
import { ViewScope } from '@/views/scopes/ViewScope';
import { currentViewFieldsScopedFamilyState } from '@/views/states/currentViewFieldsScopedFamilyState';
import { ViewField } from '@/views/types/ViewField';
jest.mock('@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery', () => {
return {
useMapFieldMetadataToGraphQLQuery: jest.fn().mockReturnValue(() => '\n'),
};
});
const fieldMetadataId = '12ecdf87-506f-44a7-98c6-393e5f05b225';
const fieldDefinition: ColumnDefinition<FieldMetadata> = {
size: 1,
position: 1,
fieldMetadataId,
label: 'label',
iconName: 'icon',
type: 'TEXT',
metadata: {
placeHolder: 'placeHolder',
fieldName: 'fieldName',
},
};
const viewField: ViewField = {
id: '88930a16-685f-493b-a96b-91ca55666bba',
fieldMetadataId,
position: 1,
isVisible: true,
size: 1,
definition: fieldDefinition,
};
const viewBarId = 'viewBarTestId';
const currentViewId = '23f5dceb-3482-4e3a-9bb4-2f52f2556be9';
const mocks = [
{
request: {
query: gql`
mutation CreateOneViewField($input: ViewFieldCreateInput!) {
createViewField(data: $input) {
id
}
}
`,
variables: {
input: {
fieldMetadataId,
viewId: currentViewId,
isVisible: true,
size: 1,
position: 1,
},
},
},
result: jest.fn(() => ({
data: { createViewField: { id: '' } },
})),
},
];
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<MemoryRouter
initialEntries={['/one', '/two', { pathname: '/three' }]}
initialIndex={1}
>
<MockedProvider mocks={mocks} addTypename={false}>
<RecoilRoot>
<ViewScope viewScopeId="viewScopeId">{children}</ViewScope>
</RecoilRoot>
</MockedProvider>
</MemoryRouter>
);
const renderHookConfig = {
wrapper: Wrapper,
};
describe('useViewBar > viewFields', () => {
it('should update current fields', async () => {
const { result } = renderHook(
() => ({
viewBar: useViewBar({ viewBarId }),
currentFields: useRecoilState(
getScopedFamilyStateDeprecated(
currentViewFieldsScopedFamilyState,
viewBarId,
currentViewId,
),
)[0],
}),
renderHookConfig,
);
expect(result.current.currentFields).toStrictEqual([]);
await act(async () => {
result.current.viewBar.setCurrentViewId(currentViewId);
result.current.viewBar.setViewObjectMetadataId('newId');
result.current.viewBar.persistViewFields([viewField]);
});
await waitFor(() =>
expect(result.current.currentFields).toEqual([viewField]),
);
});
it('should persist view fields', async () => {
const { result } = renderHook(
() => useViewBar({ viewBarId }),
renderHookConfig,
);
await act(async () => {
result.current.setCurrentViewId(currentViewId);
result.current.setViewObjectMetadataId('newId');
await result.current.persistViewFields([viewField]);
});
const persistViewFieldsMutation = mocks[0];
await waitFor(() =>
expect(persistViewFieldsMutation.result).toHaveBeenCalled(),
);
});
it('should load view fields', async () => {
const currentViewId = 'ac8807fd-0065-436d-bdf6-94333d75af6e';
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
const { currentViewFieldsState } = useViewScopedStates({
viewScopeId: viewBarId,
});
const currentViewFields = useRecoilValue(currentViewFieldsState);
return {
viewBar,
currentViewFields,
};
}, renderHookConfig);
expect(result.current.currentViewFields).toStrictEqual([]);
await act(async () => {
result.current.viewBar.setAvailableFieldDefinitions([fieldDefinition]);
await result.current.viewBar.loadViewFields(
{
edges: [
{
node: viewField,
cursor: '',
},
],
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
},
currentViewId,
);
result.current.viewBar.setCurrentViewId(currentViewId);
});
expect(result.current.currentViewFields).toStrictEqual([viewField]);
});
});

View File

@ -0,0 +1,211 @@
import { act } from 'react-dom/test-utils';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import { renderHook } from '@testing-library/react';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useViewBar } from '@/views/hooks/useViewBar';
import { ViewScope } from '@/views/scopes/ViewScope';
import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<MemoryRouter
initialEntries={['/one', '/two', { pathname: '/three' }]}
initialIndex={1}
>
<MockedProvider addTypename={false}>
<RecoilRoot>
<ViewScope viewScopeId="viewScopeId">{children}</ViewScope>
</RecoilRoot>
</MockedProvider>
</MemoryRouter>
);
const renderHookConfig = {
wrapper: Wrapper,
};
const viewBarId = 'viewBarTestId';
export const filterDefinition: FilterDefinition = {
fieldMetadataId: '113ea8f8-1908-4c9c-9984-3f23c96b92f5',
label: 'label',
iconName: 'iconName',
type: 'TEXT',
};
export const viewFilter: ViewFilter = {
id: 'id',
fieldMetadataId: '113ea8f8-1908-4c9c-9984-3f23c96b92f5',
operand: ViewFilterOperand.Is,
value: 'value',
displayValue: 'displayValue',
definition: filterDefinition,
};
const currentViewId = '23f5dceb-3482-4e3a-9bb4-2f52f2556be9';
describe('useViewBar > viewFilters', () => {
it('should load view filters', async () => {
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
const { currentViewFiltersState } = useViewScopedStates({
viewScopeId: viewBarId,
});
const currentViewFilters = useRecoilValue(currentViewFiltersState);
return {
viewBar,
currentViewFilters,
};
}, renderHookConfig);
expect(result.current.currentViewFilters).toStrictEqual([]);
await act(async () => {
result.current.viewBar.setAvailableFilterDefinitions([filterDefinition]);
await result.current.viewBar.loadViewFilters(
{
edges: [
{
node: viewFilter,
cursor: '',
},
],
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
},
currentViewId,
);
result.current.viewBar.setCurrentViewId(currentViewId);
});
expect(result.current.currentViewFilters).toStrictEqual([viewFilter]);
});
it('should upsertViewFilter', async () => {
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
viewBar.setAvailableFilterDefinitions([filterDefinition]);
viewBar.loadViewFilters(
{
edges: [
{
node: viewFilter,
cursor: '',
},
],
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
},
currentViewId,
);
viewBar.setCurrentViewId(currentViewId);
const { currentViewFiltersState } = useViewScopedStates({
viewScopeId: viewBarId,
});
const currentViewFilters = useRecoilValue(currentViewFiltersState);
return {
viewBar,
currentViewFilters,
};
}, renderHookConfig);
expect(result.current.currentViewFilters).toStrictEqual([viewFilter]);
const newFilters: Filter[] = [
{
fieldMetadataId: '113ea8f8-1908-4c9c-9984-3f23c96b92f5',
value: 'value',
displayValue: 'displayValue',
operand: ViewFilterOperand.IsNot,
definition: {
fieldMetadataId: 'id',
label: 'label',
iconName: 'icon',
type: 'TEXT',
},
},
{
fieldMetadataId: 'd9487757-183e-4fa0-a554-a980850cb23d',
value: 'value',
displayValue: 'displayValue',
operand: ViewFilterOperand.Contains,
definition: {
fieldMetadataId: 'id',
label: 'label',
iconName: 'icon',
type: 'TEXT',
},
},
];
// upsert an existing filter
act(() => {
result.current.viewBar.upsertViewFilter(newFilters[0]);
});
expect(result.current.currentViewFilters).toStrictEqual([
{ ...newFilters[0], id: viewFilter.id },
]);
// upsert a new filter
act(() => {
result.current.viewBar.upsertViewFilter(newFilters[1]);
});
// expect currentViewFilters to contain both filters
expect(result.current.currentViewFilters).toStrictEqual([
{ ...newFilters[0], id: viewFilter.id },
{ ...newFilters[1], id: undefined },
]);
});
it('should remove view filter', () => {
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
viewBar.setAvailableFilterDefinitions([filterDefinition]);
viewBar.loadViewFilters(
{
edges: [
{
node: viewFilter,
cursor: '',
},
],
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
},
currentViewId,
);
viewBar.setCurrentViewId(currentViewId);
const { currentViewFiltersState } = useViewScopedStates({
viewScopeId: viewBarId,
});
const currentViewFilters = useRecoilValue(currentViewFiltersState);
return {
viewBar,
currentViewFilters,
};
}, renderHookConfig);
expect(result.current.currentViewFilters).toStrictEqual([viewFilter]);
// remove an existing filter
act(() => {
result.current.viewBar.removeViewFilter(filterDefinition.fieldMetadataId);
});
expect(result.current.currentViewFilters).toStrictEqual([]);
});
});

View File

@ -0,0 +1,198 @@
import { act } from 'react-dom/test-utils';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import { renderHook } from '@testing-library/react';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useViewBar } from '@/views/hooks/useViewBar';
import { ViewScope } from '@/views/scopes/ViewScope';
import { ViewSort } from '@/views/types/ViewSort';
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<MemoryRouter
initialEntries={['/one', '/two', { pathname: '/three' }]}
initialIndex={1}
>
<MockedProvider addTypename={false}>
<RecoilRoot>
<ViewScope viewScopeId="viewScopeId">{children}</ViewScope>
</RecoilRoot>
</MockedProvider>
</MemoryRouter>
);
const renderHookConfig = {
wrapper: Wrapper,
};
const viewBarId = 'viewBarTestId';
export const sortDefinition: SortDefinition = {
fieldMetadataId: '12ecdf87-506f-44a7-98c6-393e5f05b225',
label: 'label',
iconName: 'icon',
};
export const viewSort: ViewSort = {
id: '88930a16-685f-493b-a96b-91ca55666bba',
fieldMetadataId: '12ecdf87-506f-44a7-98c6-393e5f05b225',
direction: 'asc',
definition: sortDefinition,
};
describe('View Sorts', () => {
const currentViewId = 'ac8807fd-0065-436d-bdf6-94333d75af6e';
it('should load view sorts', async () => {
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
const { currentViewSortsState } = useViewScopedStates({
viewScopeId: viewBarId,
});
const currentViewSorts = useRecoilValue(currentViewSortsState);
return {
viewBar,
currentViewSorts,
};
}, renderHookConfig);
expect(result.current.currentViewSorts).toStrictEqual([]);
await act(async () => {
result.current.viewBar.setAvailableSortDefinitions([sortDefinition]);
await result.current.viewBar.loadViewSorts(
{
edges: [
{
node: viewSort,
cursor: '',
},
],
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
},
currentViewId,
);
result.current.viewBar.setCurrentViewId(currentViewId);
});
expect(result.current.currentViewSorts).toStrictEqual([viewSort]);
});
it('should upsertViewSort', async () => {
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
viewBar.setAvailableSortDefinitions([sortDefinition]);
viewBar.loadViewSorts(
{
edges: [
{
node: viewSort,
cursor: '',
},
],
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
},
currentViewId,
);
viewBar.setCurrentViewId(currentViewId);
const { currentViewSortsState } = useViewScopedStates({
viewScopeId: viewBarId,
});
const currentViewSorts = useRecoilValue(currentViewSortsState);
return {
viewBar,
currentViewSorts,
};
}, renderHookConfig);
expect(result.current.currentViewSorts).toStrictEqual([viewSort]);
const newSortFieldMetadataId = 'd9487757-183e-4fa0-a554-a980850cb23d';
const newSorts: Sort[] = [
{
fieldMetadataId: viewSort.fieldMetadataId,
direction: 'desc',
definition: sortDefinition,
},
{
fieldMetadataId: newSortFieldMetadataId,
direction: 'asc',
definition: {
...sortDefinition,
fieldMetadataId: newSortFieldMetadataId,
},
},
];
// upsert an existing sort
act(() => {
result.current.viewBar.upsertViewSort(newSorts[0]);
});
expect(result.current.currentViewSorts).toStrictEqual([
{ ...newSorts[0], id: viewSort.id },
]);
// upsert a new sort
act(() => {
result.current.viewBar.upsertViewSort(newSorts[1]);
});
// expect currentViewSorts to contain both sorts
expect(result.current.currentViewSorts).toStrictEqual([
{ ...newSorts[0], id: viewSort.id },
{ ...newSorts[1], id: undefined },
]);
});
it('should remove view sort', () => {
const { result } = renderHook(() => {
const viewBar = useViewBar({ viewBarId });
viewBar.setAvailableSortDefinitions([sortDefinition]);
viewBar.loadViewSorts(
{
edges: [
{
node: viewSort,
cursor: '',
},
],
pageInfo: { hasNextPage: false, startCursor: '', endCursor: '' },
},
currentViewId,
);
viewBar.setCurrentViewId(currentViewId);
const { currentViewSortsState } = useViewScopedStates({
viewScopeId: viewBarId,
});
const currentViewSorts = useRecoilValue(currentViewSortsState);
return {
viewBar,
currentViewSorts,
};
}, renderHookConfig);
expect(result.current.currentViewSorts).toStrictEqual([viewSort]);
// remove an existing sort
act(() => {
result.current.viewBar.removeViewSort(sortDefinition.fieldMetadataId);
});
expect(result.current.currentViewSorts).toStrictEqual([]);
});
});

View File

@ -0,0 +1,314 @@
import { FieldMetadata } from '@/object-record/field/types/FieldMetadata';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
import { BoardFieldDefinition } from '@/object-record/record-board/types/BoardFieldDefinition';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { ViewField } from '@/views/types/ViewField';
import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { ViewSort } from '@/views/types/ViewSort';
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
import { mapViewFieldsToBoardFieldDefinitions } from '@/views/utils/mapViewFieldsToBoardFieldDefinitions';
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
const baseDefinition = {
fieldMetadataId: 'fieldMetadataId',
label: 'label',
iconName: 'iconName',
};
describe('mapViewSortsToSorts', () => {
it('should map each ViewSort object to a corresponding Sort object', () => {
const viewSorts: ViewSort[] = [
{
id: 'id',
fieldMetadataId: 'fieldMetadataId',
direction: 'asc',
definition: baseDefinition,
},
];
const expectedSorts: Sort[] = [
{
fieldMetadataId: 'fieldMetadataId',
direction: 'asc',
definition: baseDefinition,
},
];
expect(mapViewSortsToSorts(viewSorts)).toEqual(expectedSorts);
});
});
describe('mapViewFiltersToFilters', () => {
it('should map each ViewFilter object to a corresponding Filter object', () => {
const viewFilters: ViewFilter[] = [
{
id: 'id',
fieldMetadataId: '1',
value: 'testValue',
displayValue: 'Test Display Value',
operand: ViewFilterOperand.Is,
definition: {
...baseDefinition,
type: 'FULL_NAME',
},
},
];
const expectedFilters: Filter[] = [
{
fieldMetadataId: '1',
value: 'testValue',
displayValue: 'Test Display Value',
operand: ViewFilterOperand.Is,
definition: {
...baseDefinition,
type: 'FULL_NAME',
},
},
];
expect(mapViewFiltersToFilters(viewFilters)).toEqual(expectedFilters);
});
});
describe('mapViewFieldsToColumnDefinitions', () => {
it('should map visible ViewFields to ColumnDefinitions and filter out missing fieldMetadata', () => {
const viewFields: ViewField[] = [
{
id: '1',
fieldMetadataId: '1',
position: 1,
size: 1,
isVisible: false,
definition: {
fieldMetadataId: '1',
label: 'label 1',
metadata: { fieldName: 'fieldName 1' },
infoTooltipContent: 'infoTooltipContent 1',
iconName: 'iconName 1',
type: 'TEXT',
position: 1,
size: 1,
isVisible: false,
viewFieldId: '1',
},
},
{
id: '2',
fieldMetadataId: '2',
position: 2,
size: 2,
isVisible: false,
definition: {
fieldMetadataId: '2',
label: 'label 2',
metadata: { fieldName: 'fieldName 2' },
infoTooltipContent: 'infoTooltipContent 2',
iconName: 'iconName 2',
type: 'TEXT',
position: 2,
size: 1,
isVisible: false,
viewFieldId: '2',
},
},
{
id: '3',
fieldMetadataId: '3',
position: 3,
size: 3,
isVisible: true,
definition: {
fieldMetadataId: '3',
label: 'label 3',
metadata: { fieldName: 'fieldName 3' },
infoTooltipContent: 'infoTooltipContent 3',
iconName: 'iconName 3',
type: 'TEXT',
position: 3,
size: 1,
isVisible: false,
viewFieldId: '3',
},
},
];
const fieldsMetadata: ColumnDefinition<FieldMetadata>[] = [
{
fieldMetadataId: '1',
label: 'label 1',
position: 1,
metadata: { fieldName: 'fieldName 1' },
infoTooltipContent: 'infoTooltipContent 1',
iconName: 'iconName 1',
type: 'TEXT',
size: 1,
},
{
fieldMetadataId: '3',
label: 'label 3',
position: 3,
metadata: { fieldName: 'fieldName 3' },
infoTooltipContent: 'infoTooltipContent 3',
iconName: 'iconName 3',
type: 'TEXT',
size: 3,
},
];
const expectedColumnDefinitions: ColumnDefinition<FieldMetadata>[] = [
{
fieldMetadataId: '1',
label: 'label 1',
metadata: { fieldName: 'fieldName 1' },
infoTooltipContent: 'infoTooltipContent 1',
iconName: 'iconName 1',
type: 'TEXT',
size: 1,
position: 1,
isVisible: false,
viewFieldId: '1',
},
{
fieldMetadataId: '3',
label: 'label 3',
metadata: { fieldName: 'fieldName 3' },
infoTooltipContent: 'infoTooltipContent 3',
iconName: 'iconName 3',
type: 'TEXT',
size: 3,
position: 3,
isVisible: true,
viewFieldId: '3',
},
];
const actualColumnDefinitions = mapViewFieldsToColumnDefinitions(
viewFields,
fieldsMetadata,
);
expect(actualColumnDefinitions).toEqual(expectedColumnDefinitions);
});
});
describe('mapViewFieldsToBoardFieldDefinitions', () => {
it('should map visible ViewFields to BoardFieldDefinitions and filter out missing fieldMetadata', () => {
const viewFields = [
{
id: 1,
fieldMetadataId: 1,
position: 1,
isVisible: true,
},
{
id: 2,
fieldMetadataId: 2,
position: 2,
isVisible: false,
},
{
id: 3,
fieldMetadataId: 3,
position: 3,
isVisible: true,
},
];
const fieldsMetadata = [
{
fieldMetadataId: 1,
label: 'Field 1',
metadata: {},
position: 1,
infoTooltipContent: 'Tooltip content for Field 1',
iconName: 'icon-field-1',
type: 'string',
},
{
fieldMetadataId: 3,
label: 'Field 3',
metadata: {},
position: 3,
infoTooltipContent: 'Tooltip for Field 3',
iconName: 'icon-field-3',
type: 'number',
},
];
const expectedBoardFieldDefinitions = [
{
fieldMetadataId: 1,
label: 'Field 1',
metadata: {},
position: 1,
infoTooltipContent: 'Tooltip content for Field 1',
iconName: 'icon-field-1',
type: 'string',
isVisible: true,
viewFieldId: 1,
},
{
fieldMetadataId: 3,
label: 'Field 3',
metadata: {},
position: 3,
infoTooltipContent: 'Tooltip for Field 3',
iconName: 'icon-field-3',
type: 'number',
isVisible: true,
viewFieldId: 3,
},
];
const actualBoardFieldDefinitions = mapViewFieldsToBoardFieldDefinitions(
viewFields as unknown as ViewField[],
fieldsMetadata as unknown as BoardFieldDefinition<FieldMetadata>[],
);
expect(actualBoardFieldDefinitions).toEqual(expectedBoardFieldDefinitions);
});
});
describe('mapColumnDefinitionsToViewFields', () => {
it('should map ColumnDefinitions to ViewFields, setting defaults and using viewFieldId if present', () => {
const columnDefinitions = [
{
fieldMetadataId: 1,
position: 1,
isVisible: true,
viewFieldId: 'custom-id-1',
},
{
fieldMetadataId: 2,
position: 2,
size: 200,
isVisible: false,
},
];
const expectedViewFields = [
{
id: 'custom-id-1',
fieldMetadataId: 1,
position: 1,
isVisible: true,
definition: columnDefinitions[0],
},
{
id: '',
fieldMetadataId: 2,
position: 2,
size: 200,
isVisible: false,
definition: columnDefinitions[1],
},
];
const actualViewFields = mapColumnDefinitionsToViewFields(
columnDefinitions as unknown as ColumnDefinition<FieldMetadata>[],
);
expect(actualViewFields).toEqual(expectedViewFields);
});
});