1120 lines
32 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|