Support define is tool logic function (#17926)
- supports isTool and timeout settings in defineLogicFunction in apps and in setting tabs definition - compute for all toolInputSchema for logic funciton, in settings and in code steps <img width="991" height="802" alt="image" src="https://github.com/user-attachments/assets/05dc1221-cac9-45a3-87b0-3b13161446fd" />
This commit is contained in:
+98
@@ -0,0 +1,98 @@
|
||||
import { computeNewPositionOfDraggedRecord } from '@/object-record/utils/computeNewPositionOfDraggedRecord';
|
||||
|
||||
describe('computeNewPositionOfDraggedRecord', () => {
|
||||
const records = [
|
||||
{ id: 'a', position: 1 },
|
||||
{ id: 'b', position: 2 },
|
||||
{ id: 'c', position: 3 },
|
||||
{ id: 'd', position: 4 },
|
||||
];
|
||||
|
||||
it('should return same position when dragging to itself', () => {
|
||||
const result = computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: records,
|
||||
idOfItemToMove: 'b',
|
||||
idOfTargetItem: 'b',
|
||||
isDroppedAfterList: false,
|
||||
});
|
||||
|
||||
expect(result).toBe(2);
|
||||
});
|
||||
|
||||
it('should return target position + 1 when dropped after list', () => {
|
||||
const result = computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: records,
|
||||
idOfItemToMove: 'a',
|
||||
idOfTargetItem: 'd',
|
||||
isDroppedAfterList: true,
|
||||
});
|
||||
|
||||
expect(result).toBe(5);
|
||||
});
|
||||
|
||||
it('should return target position - 1 when moving to first position', () => {
|
||||
const result = computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: records,
|
||||
idOfItemToMove: 'c',
|
||||
idOfTargetItem: 'a',
|
||||
isDroppedAfterList: false,
|
||||
});
|
||||
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('should compute intermediary position when moving after target', () => {
|
||||
const result = computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: records,
|
||||
idOfItemToMove: 'a',
|
||||
idOfTargetItem: 'b',
|
||||
isDroppedAfterList: false,
|
||||
});
|
||||
|
||||
expect(result).toBe(2.5);
|
||||
});
|
||||
|
||||
it('should compute intermediary position when moving before target', () => {
|
||||
const result = computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: records,
|
||||
idOfItemToMove: 'd',
|
||||
idOfTargetItem: 'c',
|
||||
isDroppedAfterList: false,
|
||||
});
|
||||
|
||||
expect(result).toBe(2.5);
|
||||
});
|
||||
|
||||
it('should throw when target item is not found', () => {
|
||||
expect(() =>
|
||||
computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: records,
|
||||
idOfItemToMove: 'a',
|
||||
idOfTargetItem: 'unknown',
|
||||
isDroppedAfterList: false,
|
||||
}),
|
||||
).toThrow('Cannot find item to move for id : unknown');
|
||||
});
|
||||
|
||||
it('should handle item not in table moved before target', () => {
|
||||
const result = computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: records,
|
||||
idOfItemToMove: 'new-item',
|
||||
idOfTargetItem: 'c',
|
||||
isDroppedAfterList: false,
|
||||
});
|
||||
|
||||
expect(result).toBe(2.5);
|
||||
});
|
||||
|
||||
it('should return target + 1 when moving after last item', () => {
|
||||
const result = computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: records,
|
||||
idOfItemToMove: 'a',
|
||||
idOfTargetItem: 'd',
|
||||
isDroppedAfterList: false,
|
||||
});
|
||||
|
||||
expect(result).toBe(4 + 1);
|
||||
});
|
||||
});
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
||||
|
||||
describe('filterAvailableTableColumns', () => {
|
||||
const createColumnDefinition = (fieldName: string) =>
|
||||
({
|
||||
metadata: { fieldName },
|
||||
}) as any;
|
||||
|
||||
it('should return false for denied column names', () => {
|
||||
expect(
|
||||
filterAvailableTableColumns(createColumnDefinition('attachments')),
|
||||
).toBe(false);
|
||||
expect(
|
||||
filterAvailableTableColumns(createColumnDefinition('activities')),
|
||||
).toBe(false);
|
||||
expect(
|
||||
filterAvailableTableColumns(createColumnDefinition('timelineActivities')),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for allowed column names', () => {
|
||||
expect(filterAvailableTableColumns(createColumnDefinition('name'))).toBe(
|
||||
true,
|
||||
);
|
||||
expect(filterAvailableTableColumns(createColumnDefinition('email'))).toBe(
|
||||
true,
|
||||
);
|
||||
expect(filterAvailableTableColumns(createColumnDefinition('phone'))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
|
||||
|
||||
describe('getDropdownFocusIdForRecordField', () => {
|
||||
it('should generate correct dropdown focus id for table-cell', () => {
|
||||
const result = getDropdownFocusIdForRecordField({
|
||||
recordId: 'record-123',
|
||||
fieldMetadataId: 'field-456',
|
||||
componentType: 'table-cell',
|
||||
instanceId: 'instance-789',
|
||||
});
|
||||
|
||||
expect(result).toBe(
|
||||
'dropdown-instance-789-table-cell-record-record-123-field-field-456',
|
||||
);
|
||||
});
|
||||
|
||||
it('should generate correct dropdown focus id for inline-cell', () => {
|
||||
const result = getDropdownFocusIdForRecordField({
|
||||
recordId: 'record-abc',
|
||||
fieldMetadataId: 'field-def',
|
||||
componentType: 'inline-cell',
|
||||
instanceId: 'instance-ghi',
|
||||
});
|
||||
|
||||
expect(result).toBe(
|
||||
'dropdown-instance-ghi-inline-cell-record-record-abc-field-field-def',
|
||||
);
|
||||
});
|
||||
});
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import { getQueryIdentifier } from '@/object-record/utils/getQueryIdentifier';
|
||||
|
||||
describe('getQueryIdentifier', () => {
|
||||
it('should create identifier from object name and variables', () => {
|
||||
const result = getQueryIdentifier({
|
||||
objectNameSingular: 'person',
|
||||
filter: { name: { eq: 'Alice' } },
|
||||
orderBy: [{ name: 'AscNullsFirst' }],
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
expect(result).toContain('person');
|
||||
expect(result).toContain('Alice');
|
||||
expect(result).toContain('10');
|
||||
});
|
||||
|
||||
it('should include cursor filter when present', () => {
|
||||
const result = getQueryIdentifier({
|
||||
objectNameSingular: 'company',
|
||||
filter: {},
|
||||
orderBy: [],
|
||||
limit: 20,
|
||||
cursorFilter: { cursor: 'cursor-123', cursorDirection: 'before' },
|
||||
});
|
||||
|
||||
expect(result).toContain('cursor-123');
|
||||
});
|
||||
|
||||
it('should include groupBy when present', () => {
|
||||
const result = getQueryIdentifier({
|
||||
objectNameSingular: 'task',
|
||||
filter: {},
|
||||
orderBy: [],
|
||||
limit: 10,
|
||||
groupBy: [{ status: 'Done' }],
|
||||
});
|
||||
|
||||
expect(result).toContain('Done');
|
||||
});
|
||||
|
||||
it('should produce different identifiers for different filters', () => {
|
||||
const resultA = getQueryIdentifier({
|
||||
objectNameSingular: 'person',
|
||||
filter: { name: { eq: 'Alice' } },
|
||||
orderBy: [],
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
const resultB = getQueryIdentifier({
|
||||
objectNameSingular: 'person',
|
||||
filter: { name: { eq: 'Bob' } },
|
||||
orderBy: [],
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
expect(resultA).not.toBe(resultB);
|
||||
});
|
||||
});
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
|
||||
|
||||
describe('getUpdateOneRecordMutationResponseField', () => {
|
||||
it('should capitalize and prefix with "update"', () => {
|
||||
expect(getUpdateOneRecordMutationResponseField('person')).toBe(
|
||||
'updatePerson',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle multi-word object names', () => {
|
||||
expect(getUpdateOneRecordMutationResponseField('company')).toBe(
|
||||
'updateCompany',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle already capitalized names', () => {
|
||||
expect(getUpdateOneRecordMutationResponseField('Person')).toBe(
|
||||
'updatePerson',
|
||||
);
|
||||
});
|
||||
});
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { getUpdatedFieldsFromRecordInput } from '@/object-record/utils/getUpdatedFieldsFromRecordInput';
|
||||
|
||||
describe('getUpdatedFieldsFromRecordInput', () => {
|
||||
it('should return field entries excluding id', () => {
|
||||
const input = { id: '123', name: 'Alice', age: 30 };
|
||||
|
||||
const result = getUpdatedFieldsFromRecordInput(input);
|
||||
|
||||
expect(result).toEqual([{ name: 'Alice' }, { age: 30 }]);
|
||||
});
|
||||
|
||||
it('should return empty array when only id is present', () => {
|
||||
const input = { id: '123' };
|
||||
|
||||
const result = getUpdatedFieldsFromRecordInput(input);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle inputs without id', () => {
|
||||
const input = { name: 'Bob', email: 'bob@test.com' };
|
||||
|
||||
const result = getUpdatedFieldsFromRecordInput(input);
|
||||
|
||||
expect(result).toEqual([{ name: 'Bob' }, { email: 'bob@test.com' }]);
|
||||
});
|
||||
|
||||
it('should handle empty input', () => {
|
||||
const result = getUpdatedFieldsFromRecordInput({});
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { isSystemSearchVectorField } from '@/object-record/utils/isSystemSearchVectorField';
|
||||
|
||||
describe('isSystemSearchVectorField', () => {
|
||||
it('should return true for searchVector field', () => {
|
||||
expect(isSystemSearchVectorField('searchVector')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for other field names', () => {
|
||||
expect(isSystemSearchVectorField('name')).toBe(false);
|
||||
expect(isSystemSearchVectorField('id')).toBe(false);
|
||||
expect(isSystemSearchVectorField('position')).toBe(false);
|
||||
expect(isSystemSearchVectorField('')).toBe(false);
|
||||
});
|
||||
});
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition';
|
||||
|
||||
describe('sortRecordsByPosition', () => {
|
||||
it('should sort by numeric position', () => {
|
||||
const record1 = { id: '1', __typename: 'Record', position: 1 };
|
||||
const record2 = { id: '2', __typename: 'Record', position: 2 };
|
||||
|
||||
expect(sortRecordsByPosition(record1, record2)).toBeLessThan(0);
|
||||
expect(sortRecordsByPosition(record2, record1)).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should return 0 for equal numeric positions', () => {
|
||||
const record1 = { id: '1', __typename: 'Record', position: 5 };
|
||||
const record2 = { id: '2', __typename: 'Record', position: 5 };
|
||||
|
||||
expect(sortRecordsByPosition(record1, record2)).toBe(0);
|
||||
});
|
||||
|
||||
it('should place "first" before other positions', () => {
|
||||
const record1 = { id: '1', __typename: 'Record', position: 'first' };
|
||||
const record2 = { id: '2', __typename: 'Record', position: 'last' };
|
||||
|
||||
expect(sortRecordsByPosition(record1 as any, record2 as any)).toBe(-1);
|
||||
});
|
||||
|
||||
it('should place "last" after other positions', () => {
|
||||
const record1 = { id: '1', __typename: 'Record', position: 'last' };
|
||||
const record2 = { id: '2', __typename: 'Record', position: 'first' };
|
||||
|
||||
expect(sortRecordsByPosition(record1 as any, record2 as any)).toBe(1);
|
||||
});
|
||||
|
||||
it('should return 0 for unknown position types', () => {
|
||||
const record1 = {
|
||||
id: '1',
|
||||
__typename: 'Record',
|
||||
position: undefined as any,
|
||||
};
|
||||
const record2 = {
|
||||
id: '2',
|
||||
__typename: 'Record',
|
||||
position: undefined as any,
|
||||
};
|
||||
|
||||
expect(sortRecordsByPosition(record1, record2)).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user