Files
twenty/packages/twenty-server/test/integration/metadata/suites/view/upsert-view-widget.integration-spec.ts
T
2026-04-29 09:41:02 +00:00

1120 lines
32 KiB
TypeScript

import {
VIEW_FIELD_GQL_FIELDS,
VIEW_FILTER_GQL_FIELDS,
VIEW_FILTER_GROUP_GQL_FIELDS,
VIEW_GQL_FIELDS,
VIEW_SORT_GQL_FIELDS,
} from 'test/integration/constants/view-gql-fields.constants';
import { createOnePageLayoutTab } from 'test/integration/metadata/suites/page-layout-tab/utils/create-one-page-layout-tab.util';
import { destroyOnePageLayoutTab } from 'test/integration/metadata/suites/page-layout-tab/utils/destroy-one-page-layout-tab.util';
import { createOnePageLayoutWidget } from 'test/integration/metadata/suites/page-layout-widget/utils/create-one-page-layout-widget.util';
import { destroyOnePageLayoutWidget } from 'test/integration/metadata/suites/page-layout-widget/utils/destroy-one-page-layout-widget.util';
import {
fetchTestFieldMetadataIds,
type TestFieldMetadataIds,
} from 'test/integration/metadata/suites/page-layout-widget/utils/fetch-test-field-metadata-ids.util';
import { createOnePageLayout } from 'test/integration/metadata/suites/page-layout/utils/create-one-page-layout.util';
import { destroyOnePageLayout } from 'test/integration/metadata/suites/page-layout/utils/destroy-one-page-layout.util';
import { findViewFields } from 'test/integration/metadata/suites/view-field/utils/find-view-fields.util';
import { findViewFilters } from 'test/integration/metadata/suites/view-filter/utils/find-view-filters.util';
import { createOneView } from 'test/integration/metadata/suites/view/utils/create-one-view.util';
import { destroyOneView } from 'test/integration/metadata/suites/view/utils/destroy-one-view.util';
import { upsertViewWidget } from 'test/integration/metadata/suites/view/utils/upsert-view-widget.util';
import {
ViewFilterGroupLogicalOperator,
ViewFilterOperand,
ViewSortDirection,
ViewType,
} from 'twenty-shared/types';
import { v4 as uuidv4 } from 'uuid';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
type ViewWidgetTestSetup = {
pageLayoutId: string;
pageLayoutTabId: string;
widgetId: string;
viewId: string;
labelIdentifierFieldMetadataId: string | null;
viewFields: Array<{
id: string;
fieldMetadataId: string;
position: number;
isVisible: boolean;
}>;
fieldMetadataIds: string[];
};
const VIEW_WITH_ALL_RELATIONS_GQL_FIELDS = `
${VIEW_GQL_FIELDS}
viewFields {
${VIEW_FIELD_GQL_FIELDS}
}
viewFilters {
${VIEW_FILTER_GQL_FIELDS}
}
viewFilterGroups {
${VIEW_FILTER_GROUP_GQL_FIELDS}
}
viewSorts {
${VIEW_SORT_GQL_FIELDS}
}
`;
describe('upsertViewWidget', () => {
let testSetup: ViewWidgetTestSetup;
beforeAll(async () => {
const testFieldMetadataIds: TestFieldMetadataIds =
await fetchTestFieldMetadataIds();
const { data: layoutData } = await createOnePageLayout({
expectToFail: false,
input: { name: 'Test Page Layout For View Widget' },
});
const pageLayoutId = layoutData.createPageLayout.id;
const { data: tabData } = await createOnePageLayoutTab({
expectToFail: false,
input: {
title: 'Test Tab For View Widget',
pageLayoutId,
},
});
const pageLayoutTabId = tabData.createPageLayoutTab.id;
const { data: viewData } = await createOneView({
expectToFail: false,
input: {
name: 'testViewWidgetView',
objectMetadataId: testFieldMetadataIds.objectMetadataId,
icon: 'IconTable',
type: ViewType.TABLE,
},
});
const viewId = viewData.createView.id;
const { data: widgetData } = await createOnePageLayoutWidget({
expectToFail: false,
input: {
title: 'Test Record Table Widget',
type: WidgetType.RECORD_TABLE,
pageLayoutTabId,
objectMetadataId: testFieldMetadataIds.objectMetadataId,
gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 },
configuration: {
configurationType: WidgetConfigurationType.RECORD_TABLE,
viewId,
},
},
});
const widgetId = widgetData.createPageLayoutWidget.id;
const objectMetadataRows = await global.testDataSource.query(
`SELECT "labelIdentifierFieldMetadataId"
FROM core."objectMetadata"
WHERE id = $1`,
[testFieldMetadataIds.objectMetadataId],
);
const labelIdentifierFieldMetadataId =
objectMetadataRows[0]?.labelIdentifierFieldMetadataId ?? null;
const allFieldMetadataRows = await global.testDataSource.query(
`SELECT id FROM core."fieldMetadata"
WHERE "objectMetadataId" = $1
AND "isActive" = true
ORDER BY id
LIMIT 10`,
[testFieldMetadataIds.objectMetadataId],
);
const allFieldMetadataIds: string[] = allFieldMetadataRows.map(
(row: { id: string }) => row.id,
);
if (labelIdentifierFieldMetadataId) {
const idx = allFieldMetadataIds.indexOf(labelIdentifierFieldMetadataId);
if (idx > 0) {
allFieldMetadataIds.splice(idx, 1);
allFieldMetadataIds.unshift(labelIdentifierFieldMetadataId);
} else if (idx === -1) {
allFieldMetadataIds.unshift(labelIdentifierFieldMetadataId);
}
}
const seedFieldMetadataIds = allFieldMetadataIds.slice(0, 3);
await upsertViewWidget({
expectToFail: false,
input: {
widgetId,
viewFields: seedFieldMetadataIds.map((fmId, index) => ({
fieldMetadataId: fmId,
isVisible: true,
position: index,
})),
},
});
const { data: fieldsData } = await findViewFields({
viewId,
gqlFields: 'id fieldMetadataId position isVisible',
expectToFail: false,
});
testSetup = {
pageLayoutId,
pageLayoutTabId,
widgetId,
viewId,
labelIdentifierFieldMetadataId,
viewFields: fieldsData.getViewFields,
fieldMetadataIds: allFieldMetadataIds,
};
});
afterAll(async () => {
await destroyOnePageLayoutWidget({
expectToFail: false,
input: { id: testSetup.widgetId },
});
await destroyOneView({
expectToFail: false,
viewId: testSetup.viewId,
});
await destroyOnePageLayoutTab({
expectToFail: false,
input: { id: testSetup.pageLayoutTabId },
});
await destroyOnePageLayout({
expectToFail: false,
input: { id: testSetup.pageLayoutId },
});
});
describe('view fields', () => {
it('should create a new view field via fieldMetadataId', async () => {
const existingFieldMetadataIds = new Set(
testSetup.viewFields.map((f) => f.fieldMetadataId),
);
const newFieldMetadataId = testSetup.fieldMetadataIds.find(
(id) => !existingFieldMetadataIds.has(id),
);
expect(newFieldMetadataId).toBeDefined();
const { data, errors } = await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFields: [
...testSetup.viewFields.map((f) => ({
viewFieldId: f.id,
fieldMetadataId: f.fieldMetadataId,
isVisible: f.isVisible,
position: f.position,
})),
{
fieldMetadataId: newFieldMetadataId!,
isVisible: true,
position: 99,
},
],
},
gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS,
});
expect(errors).toBeUndefined();
expect(data.upsertViewWidget).toBeDefined();
const { data: fieldsData } = await findViewFields({
viewId: testSetup.viewId,
gqlFields: 'id fieldMetadataId position isVisible',
expectToFail: false,
});
const createdField = fieldsData.getViewFields.find(
(f: { fieldMetadataId: string }) =>
f.fieldMetadataId === newFieldMetadataId!,
);
expect(createdField).toBeDefined();
expect(createdField!.isVisible).toBe(true);
expect(createdField!.position).toBe(99);
});
it('should update an existing view field position and visibility', async () => {
const targetField = testSetup.viewFields.find(
(field) =>
field.fieldMetadataId !== testSetup.labelIdentifierFieldMetadataId,
)!;
expect(targetField).toBeDefined();
const labelIdentifierField = testSetup.viewFields.find(
(field) =>
field.fieldMetadataId === testSetup.labelIdentifierFieldMetadataId,
);
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFields: [
...(labelIdentifierField
? [
{
viewFieldId: labelIdentifierField.id,
fieldMetadataId: labelIdentifierField.fieldMetadataId,
isVisible: true,
position: 0,
},
]
: []),
{
viewFieldId: targetField.id,
fieldMetadataId: targetField.fieldMetadataId,
isVisible: false,
position: 42,
},
],
},
});
const { data: fieldsData } = await findViewFields({
viewId: testSetup.viewId,
gqlFields: 'id isVisible position',
expectToFail: false,
});
const updatedField = fieldsData.getViewFields.find(
(f: { id: string }) => f.id === targetField.id,
);
expect(updatedField).toBeDefined();
expect(updatedField!.isVisible).toBe(false);
expect(updatedField!.position).toBe(42);
});
it('should not modify fields when viewFields is omitted', async () => {
const { data: fieldsBefore } = await findViewFields({
viewId: testSetup.viewId,
gqlFields: 'id position isVisible',
expectToFail: false,
});
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [],
},
});
const { data: fieldsAfter } = await findViewFields({
viewId: testSetup.viewId,
gqlFields: 'id position isVisible',
expectToFail: false,
});
expect(fieldsAfter.getViewFields.length).toBe(
fieldsBefore.getViewFields.length,
);
});
});
describe('view filter groups', () => {
it('should create a new filter group', async () => {
const filterGroupId = uuidv4();
const { data, errors } = await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilterGroups: [
{
id: filterGroupId,
logicalOperator: ViewFilterGroupLogicalOperator.AND,
},
],
},
gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS,
});
expect(errors).toBeUndefined();
expect(data.upsertViewWidget).toBeDefined();
const filterGroups = await global.testDataSource.query(
`SELECT id, "logicalOperator"
FROM core."viewFilterGroup"
WHERE id = $1 AND "deletedAt" IS NULL`,
[filterGroupId],
);
expect(filterGroups.length).toBe(1);
expect(filterGroups[0].logicalOperator).toBe(
ViewFilterGroupLogicalOperator.AND,
);
});
it('should update an existing filter group logical operator', async () => {
const filterGroupId = uuidv4();
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilterGroups: [
{
id: filterGroupId,
logicalOperator: ViewFilterGroupLogicalOperator.AND,
},
],
},
});
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilterGroups: [
{
id: filterGroupId,
logicalOperator: ViewFilterGroupLogicalOperator.OR,
},
],
},
});
const filterGroups = await global.testDataSource.query(
`SELECT id, "logicalOperator"
FROM core."viewFilterGroup"
WHERE id = $1 AND "deletedAt" IS NULL`,
[filterGroupId],
);
expect(filterGroups.length).toBe(1);
expect(filterGroups[0].logicalOperator).toBe(
ViewFilterGroupLogicalOperator.OR,
);
});
it('should remove filter groups not included in the input', async () => {
const groupToKeepId = uuidv4();
const groupToRemoveId = uuidv4();
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilterGroups: [
{
id: groupToKeepId,
logicalOperator: ViewFilterGroupLogicalOperator.AND,
},
{
id: groupToRemoveId,
logicalOperator: ViewFilterGroupLogicalOperator.OR,
},
],
},
});
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilterGroups: [
{
id: groupToKeepId,
logicalOperator: ViewFilterGroupLogicalOperator.AND,
},
],
},
});
const removedGroup = await global.testDataSource.query(
`SELECT id FROM core."viewFilterGroup"
WHERE id = $1 AND "deletedAt" IS NULL`,
[groupToRemoveId],
);
expect(removedGroup.length).toBe(0);
const keptGroup = await global.testDataSource.query(
`SELECT id FROM core."viewFilterGroup"
WHERE id = $1 AND "deletedAt" IS NULL`,
[groupToKeepId],
);
expect(keptGroup.length).toBe(1);
});
it('should create a nested filter group with parent reference', async () => {
const parentGroupId = uuidv4();
const childGroupId = uuidv4();
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilterGroups: [
{
id: parentGroupId,
logicalOperator: ViewFilterGroupLogicalOperator.AND,
},
],
},
});
const { errors } = await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilterGroups: [
{
id: parentGroupId,
logicalOperator: ViewFilterGroupLogicalOperator.AND,
},
{
id: childGroupId,
logicalOperator: ViewFilterGroupLogicalOperator.OR,
parentViewFilterGroupId: parentGroupId,
},
],
},
});
expect(errors).toBeUndefined();
const childGroup = await global.testDataSource.query(
`SELECT id, "parentViewFilterGroupId"
FROM core."viewFilterGroup"
WHERE id = $1 AND "deletedAt" IS NULL`,
[childGroupId],
);
expect(childGroup.length).toBe(1);
expect(childGroup[0].parentViewFilterGroupId).toBe(parentGroupId);
});
});
describe('view filters', () => {
it('should create a new filter', async () => {
const fieldMetadataId = testSetup.fieldMetadataIds[0];
const { data, errors } = await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilters: [
{
fieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: 'test-value',
},
],
},
gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS,
});
expect(errors).toBeUndefined();
expect(data.upsertViewWidget).toBeDefined();
const { data: filtersData } = await findViewFilters({
viewId: testSetup.viewId,
gqlFields: 'id fieldMetadataId operand value',
expectToFail: false,
});
const createdFilter = filtersData.getViewFilters.find(
(f: { fieldMetadataId: string; value: unknown }) =>
f.fieldMetadataId === fieldMetadataId && f.value === 'test-value',
);
expect(createdFilter).toBeDefined();
expect(createdFilter!.operand).toBe(ViewFilterOperand.CONTAINS);
});
it('should update an existing filter value and operand', async () => {
const filterId = uuidv4();
const fieldMetadataId = testSetup.fieldMetadataIds[0];
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilters: [
{
id: filterId,
fieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: 'initial',
},
],
},
});
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilters: [
{
id: filterId,
fieldMetadataId,
operand: ViewFilterOperand.IS,
value: 'updated',
},
],
},
});
const { data: filtersData } = await findViewFilters({
viewId: testSetup.viewId,
gqlFields: 'id operand value',
expectToFail: false,
});
const updatedFilter = filtersData.getViewFilters.find(
(f: { id: string }) => f.id === filterId,
);
expect(updatedFilter).toBeDefined();
expect(updatedFilter!.operand).toBe(ViewFilterOperand.IS);
expect(updatedFilter!.value).toBe('updated');
});
it('should remove filters not included in the input', async () => {
const filterToKeepId = uuidv4();
const filterToRemoveId = uuidv4();
const fieldMetadataId = testSetup.fieldMetadataIds[0];
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilters: [
{
id: filterToKeepId,
fieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: 'keep',
},
{
id: filterToRemoveId,
fieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: 'remove',
},
],
},
});
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilters: [
{
id: filterToKeepId,
fieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: 'keep',
},
],
},
});
const { data: filtersData } = await findViewFilters({
viewId: testSetup.viewId,
gqlFields: 'id',
expectToFail: false,
});
const keptFilter = filtersData.getViewFilters.find(
(f: { id: string }) => f.id === filterToKeepId,
);
expect(keptFilter).toBeDefined();
const removedFilter = filtersData.getViewFilters.find(
(f: { id: string }) => f.id === filterToRemoveId,
);
expect(removedFilter).toBeUndefined();
});
it('should create a filter within a filter group', async () => {
const filterGroupId = uuidv4();
const filterId = uuidv4();
const fieldMetadataId = testSetup.fieldMetadataIds[0];
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilterGroups: [
{
id: filterGroupId,
logicalOperator: ViewFilterGroupLogicalOperator.AND,
},
],
viewFilters: [
{
id: filterId,
fieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: 'grouped-filter',
viewFilterGroupId: filterGroupId,
},
],
},
});
const filters = await global.testDataSource.query(
`SELECT id, "viewFilterGroupId"
FROM core."viewFilter"
WHERE id = $1 AND "deletedAt" IS NULL`,
[filterId],
);
expect(filters.length).toBe(1);
expect(filters[0].viewFilterGroupId).toBe(filterGroupId);
});
});
describe('view sorts', () => {
afterEach(async () => {
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [],
},
});
});
it('should create a new sort', async () => {
const sortId = uuidv4();
const fieldMetadataId = testSetup.fieldMetadataIds[0];
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [
{
id: sortId,
fieldMetadataId,
direction: ViewSortDirection.DESC,
},
],
},
});
const sorts = await global.testDataSource.query(
`SELECT id, "fieldMetadataId", direction
FROM core."viewSort"
WHERE id = $1 AND "deletedAt" IS NULL`,
[sortId],
);
expect(sorts.length).toBe(1);
expect(sorts[0].fieldMetadataId).toBe(fieldMetadataId);
expect(sorts[0].direction).toBe(ViewSortDirection.DESC);
});
it('should update an existing sort direction', async () => {
const sortId = uuidv4();
const fieldMetadataId = testSetup.fieldMetadataIds[1];
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [
{
id: sortId,
fieldMetadataId,
direction: ViewSortDirection.ASC,
},
],
},
});
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [
{
id: sortId,
fieldMetadataId,
direction: ViewSortDirection.DESC,
},
],
},
});
const sorts = await global.testDataSource.query(
`SELECT id, direction
FROM core."viewSort"
WHERE id = $1 AND "deletedAt" IS NULL`,
[sortId],
);
expect(sorts.length).toBe(1);
expect(sorts[0].direction).toBe(ViewSortDirection.DESC);
});
it('should remove sorts not included in the input', async () => {
const sortToKeepId = uuidv4();
const sortToRemoveId = uuidv4();
const fieldMetadataId = testSetup.fieldMetadataIds[0];
const secondFieldMetadataId = testSetup.fieldMetadataIds[1];
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [
{
id: sortToKeepId,
fieldMetadataId,
direction: ViewSortDirection.ASC,
},
{
id: sortToRemoveId,
fieldMetadataId: secondFieldMetadataId,
direction: ViewSortDirection.DESC,
},
],
},
});
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [
{
id: sortToKeepId,
fieldMetadataId,
direction: ViewSortDirection.ASC,
},
],
},
});
const keptSort = await global.testDataSource.query(
`SELECT id FROM core."viewSort"
WHERE id = $1 AND "deletedAt" IS NULL`,
[sortToKeepId],
);
expect(keptSort.length).toBe(1);
const removedSort = await global.testDataSource.query(
`SELECT id FROM core."viewSort"
WHERE id = $1 AND "deletedAt" IS NULL`,
[sortToRemoveId],
);
expect(removedSort.length).toBe(0);
});
});
describe('combined operations', () => {
it('should upsert view fields, filters, filter groups, and sorts in a single call', async () => {
const filterGroupId = uuidv4();
const filterId = uuidv4();
const sortId = uuidv4();
const fieldMetadataId = testSetup.fieldMetadataIds[2];
const targetField = testSetup.viewFields.find(
(field) =>
field.fieldMetadataId !== testSetup.labelIdentifierFieldMetadataId,
)!;
expect(targetField).toBeDefined();
const labelIdentifierField = testSetup.viewFields.find(
(field) =>
field.fieldMetadataId === testSetup.labelIdentifierFieldMetadataId,
);
const { data, errors } = await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFields: [
...(labelIdentifierField
? [
{
viewFieldId: labelIdentifierField.id,
fieldMetadataId: labelIdentifierField.fieldMetadataId,
isVisible: true,
position: 0,
},
]
: []),
{
viewFieldId: targetField.id,
fieldMetadataId: targetField.fieldMetadataId,
isVisible: true,
position: 1,
},
],
viewFilterGroups: [
{
id: filterGroupId,
logicalOperator: ViewFilterGroupLogicalOperator.AND,
},
],
viewFilters: [
{
id: filterId,
fieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: 'combined-test',
viewFilterGroupId: filterGroupId,
},
],
viewSorts: [
{
id: sortId,
fieldMetadataId,
direction: ViewSortDirection.DESC,
},
],
},
gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS,
});
expect(errors).toBeUndefined();
expect(data.upsertViewWidget).toBeDefined();
expect(data.upsertViewWidget.id).toBeDefined();
const filterGroups = await global.testDataSource.query(
`SELECT id FROM core."viewFilterGroup"
WHERE id = $1 AND "deletedAt" IS NULL`,
[filterGroupId],
);
expect(filterGroups.length).toBe(1);
const filters = await global.testDataSource.query(
`SELECT id, value FROM core."viewFilter"
WHERE id = $1 AND "deletedAt" IS NULL`,
[filterId],
);
expect(filters.length).toBe(1);
expect(filters[0].value).toBe('combined-test');
const sorts = await global.testDataSource.query(
`SELECT id, direction FROM core."viewSort"
WHERE id = $1 AND "deletedAt" IS NULL`,
[sortId],
);
expect(sorts.length).toBe(1);
expect(sorts[0].direction).toBe(ViewSortDirection.DESC);
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [],
viewFilters: [],
viewFilterGroups: [],
},
});
});
});
describe('validation', () => {
it('should fail when widget id does not exist', async () => {
const { errors } = await upsertViewWidget({
expectToFail: true,
input: {
widgetId: uuidv4(),
viewFields: [
{
fieldMetadataId: testSetup.fieldMetadataIds[0],
isVisible: true,
position: 0,
},
],
},
});
expect(errors).toBeDefined();
expect(errors.length).toBeGreaterThan(0);
});
it('should return the view without changes when no sub-entity arrays are provided', async () => {
const { data, errors } = await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
},
gqlFields: VIEW_GQL_FIELDS,
});
expect(errors).toBeUndefined();
expect(data.upsertViewWidget).toBeDefined();
expect(data.upsertViewWidget.id).toBeDefined();
});
it('should remove all filters when an empty array is provided', async () => {
const filterId = uuidv4();
const fieldMetadataId = testSetup.fieldMetadataIds[0];
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilters: [
{
id: filterId,
fieldMetadataId,
operand: ViewFilterOperand.CONTAINS,
value: 'to-be-removed',
},
],
},
});
const { data: filtersBefore } = await findViewFilters({
viewId: testSetup.viewId,
gqlFields: 'id',
expectToFail: false,
});
const filterExistsBefore = filtersBefore.getViewFilters.find(
(f: { id: string }) => f.id === filterId,
);
expect(filterExistsBefore).toBeDefined();
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFilters: [],
},
});
const { data: filtersAfter } = await findViewFilters({
viewId: testSetup.viewId,
gqlFields: 'id',
expectToFail: false,
});
const filterExistsAfter = filtersAfter.getViewFilters.find(
(f: { id: string }) => f.id === filterId,
);
expect(filterExistsAfter).toBeUndefined();
});
it('should remove all sorts when an empty array is provided', async () => {
const sortId = uuidv4();
const fieldMetadataId = testSetup.fieldMetadataIds[3];
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [
{
id: sortId,
fieldMetadataId,
direction: ViewSortDirection.ASC,
},
],
},
});
await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewSorts: [],
},
});
const sortsAfter = await global.testDataSource.query(
`SELECT id FROM core."viewSort"
WHERE id = $1 AND "deletedAt" IS NULL`,
[sortId],
);
expect(sortsAfter.length).toBe(0);
});
});
describe('return type', () => {
it('should return a view with the expected fields and relations', async () => {
const targetField = testSetup.viewFields.find(
(field) =>
field.fieldMetadataId !== testSetup.labelIdentifierFieldMetadataId,
)!;
expect(targetField).toBeDefined();
const labelIdentifierField = testSetup.viewFields.find(
(field) =>
field.fieldMetadataId === testSetup.labelIdentifierFieldMetadataId,
);
const { data } = await upsertViewWidget({
expectToFail: false,
input: {
widgetId: testSetup.widgetId,
viewFields: [
...(labelIdentifierField
? [
{
viewFieldId: labelIdentifierField.id,
fieldMetadataId: labelIdentifierField.fieldMetadataId,
isVisible: true,
position: 0,
},
]
: []),
{
viewFieldId: targetField.id,
fieldMetadataId: targetField.fieldMetadataId,
isVisible: true,
position: 1,
},
],
},
gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS,
});
const view = data.upsertViewWidget;
expect(view.id).toBeDefined();
expect(view.name).toBeDefined();
expect(view.objectMetadataId).toBeDefined();
expect(view.workspaceId).toBeDefined();
expect(view.createdAt).toBeDefined();
expect(view.updatedAt).toBeDefined();
expect(Array.isArray(view.viewFields)).toBe(true);
expect(Array.isArray(view.viewFilters)).toBe(true);
expect(Array.isArray(view.viewFilterGroups)).toBe(true);
expect(Array.isArray(view.viewSorts)).toBe(true);
});
});
});