Skip to main content
Glama
form-modification-operations.test.ts18.9 kB
import { FormModificationOperations, ModificationOperationResult } from '../form-modification-operations'; import { ParsedModificationCommand, ModificationOperation, ModificationTarget } from '../form-modification-parser'; import { FormConfig, QuestionType, TextQuestionConfig, EmailQuestionConfig, MultipleChoiceQuestionConfig } from '../../models'; import { TallyForm } from '../../models/tally-schemas'; describe('FormModificationOperations', () => { let formOperations: FormModificationOperations; let sampleFormConfig: FormConfig; let sampleTallyForm: TallyForm; beforeEach(() => { formOperations = new FormModificationOperations(); sampleFormConfig = { title: 'Test Form', description: 'A test form', questions: [ { id: '1', type: QuestionType.TEXT, label: 'Name', required: true } as TextQuestionConfig, { id: '2', type: QuestionType.EMAIL, label: 'Email Address', required: false } as EmailQuestionConfig, { id: '3', type: QuestionType.MULTIPLE_CHOICE, label: 'Favorite Color', required: false, options: [ { text: 'Red', value: 'red' }, { text: 'Blue', value: 'blue' }, { text: 'Green', value: 'green' } ] } as MultipleChoiceQuestionConfig ], settings: { submissionBehavior: 'MESSAGE' as any, submissionMessage: 'Thank you!' }, branding: { theme: 'DEFAULT' as any } }; sampleTallyForm = { id: 'form-123', title: 'Test Form', description: 'A test form', createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z' }; }); describe('executeOperation', () => { it('should handle unknown operation types', () => { const command: ParsedModificationCommand = { rawCommand: 'unknown command', operation: 'UNKNOWN_OPERATION' as ModificationOperation, target: ModificationTarget.FIELD, parameters: {}, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(false); expect(result.message).toContain('not yet implemented'); expect(result.errors).toContain('Unsupported operation: UNKNOWN_OPERATION'); }); it('should handle errors gracefully', () => { const command: ParsedModificationCommand = { rawCommand: 'add text field', operation: ModificationOperation.ADD_FIELD, target: ModificationTarget.FIELD, parameters: { fieldType: QuestionType.TEXT }, confidence: 1.0 }; const result = formOperations.executeOperation(command, null, null as any); expect(result.success).toBe(false); expect(result.message).toBe('An error occurred while executing the modification operation'); expect(result.errors).toBeDefined(); }); }); describe('ADD_FIELD operation', () => { it('should add a text field successfully', () => { const command: ParsedModificationCommand = { rawCommand: 'add text field called Phone', operation: ModificationOperation.ADD_FIELD, target: ModificationTarget.FIELD, parameters: { fieldType: QuestionType.TEXT, fieldLabel: 'Phone' }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions).toHaveLength(4); expect(result.modifiedFormConfig?.questions[3].label).toBe('Phone'); expect(result.modifiedFormConfig?.questions[3].type).toBe(QuestionType.TEXT); expect(result.changes).toContain('Added new field: Phone'); }); it('should add a choice field with options', () => { const command: ParsedModificationCommand = { rawCommand: 'add multiple choice field', operation: ModificationOperation.ADD_FIELD, target: ModificationTarget.FIELD, parameters: { fieldType: QuestionType.MULTIPLE_CHOICE, fieldLabel: 'Department', options: ['Sales', 'Marketing', 'Engineering'] }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions[3].type).toBe(QuestionType.MULTIPLE_CHOICE); expect((result.modifiedFormConfig?.questions[3] as any).options).toHaveLength(3); expect((result.modifiedFormConfig?.questions[3] as any).options[0].text).toBe('Sales'); }); it('should generate default label when not provided', () => { const command: ParsedModificationCommand = { rawCommand: 'add email field', operation: ModificationOperation.ADD_FIELD, target: ModificationTarget.FIELD, parameters: { fieldType: QuestionType.EMAIL }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions[3].label).toBe('Email Address'); }); it('should fail when field type is missing', () => { const command: ParsedModificationCommand = { rawCommand: 'add field', operation: ModificationOperation.ADD_FIELD, target: ModificationTarget.FIELD, parameters: {}, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(false); expect(result.message).toBe('Field type is required for adding a field'); expect(result.errors).toContain('Missing field type'); }); }); describe('REMOVE_FIELD operation', () => { it('should remove field by number', () => { const command: ParsedModificationCommand = { rawCommand: 'remove field 2', operation: ModificationOperation.REMOVE_FIELD, target: ModificationTarget.FIELD, parameters: { fieldNumber: 2 }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions).toHaveLength(2); expect(result.modifiedFormConfig?.questions.find(q => q.label === 'Email Address')).toBeUndefined(); expect(result.changes).toContain('Removed field: Email Address'); }); it('should remove field by label', () => { const command: ParsedModificationCommand = { rawCommand: 'remove the email field', operation: ModificationOperation.REMOVE_FIELD, target: ModificationTarget.FIELD, parameters: { fieldLabel: 'email' }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions).toHaveLength(2); expect(result.modifiedFormConfig?.questions.find(q => q.label === 'Email Address')).toBeUndefined(); }); it('should fail when field is not found', () => { const command: ParsedModificationCommand = { rawCommand: 'remove field 10', operation: ModificationOperation.REMOVE_FIELD, target: ModificationTarget.FIELD, parameters: { fieldNumber: 10 }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(false); expect(result.message).toContain('out of range'); expect(result.errors).toContain('Field number 10 is out of range'); }); }); describe('MAKE_REQUIRED/MAKE_OPTIONAL operations', () => { it('should make field required', () => { const command: ParsedModificationCommand = { rawCommand: 'make field 2 required', operation: ModificationOperation.MAKE_REQUIRED, target: ModificationTarget.FIELD, parameters: { fieldNumber: 2 }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions[1].required).toBe(true); expect(result.changes).toContain('Set required: true'); }); it('should make field optional', () => { const command: ParsedModificationCommand = { rawCommand: 'make name optional', operation: ModificationOperation.MAKE_OPTIONAL, target: ModificationTarget.FIELD, parameters: { fieldLabel: 'name' }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions[0].required).toBe(false); expect(result.changes).toContain('Set required: false'); }); }); describe('UPDATE_TITLE operation', () => { it('should update form title', () => { const command: ParsedModificationCommand = { rawCommand: 'update title to New Title', operation: ModificationOperation.UPDATE_TITLE, target: ModificationTarget.FORM, parameters: { newValue: 'New Title' }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.title).toBe('New Title'); expect(result.changes).toContain('Changed title to: "New Title"'); }); it('should fail when new title is missing', () => { const command: ParsedModificationCommand = { rawCommand: 'update title', operation: ModificationOperation.UPDATE_TITLE, target: ModificationTarget.FORM, parameters: {}, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(false); expect(result.message).toBe('New title value is required'); }); }); describe('UPDATE_DESCRIPTION operation', () => { it('should update form description', () => { const command: ParsedModificationCommand = { rawCommand: 'update description to New description', operation: ModificationOperation.UPDATE_DESCRIPTION, target: ModificationTarget.FORM, parameters: { newValue: 'New description' }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.description).toBe('New description'); expect(result.changes).toContain('Changed description to: "New description"'); }); }); describe('REORDER_FIELD operation', () => { it('should reorder field successfully', () => { const command: ParsedModificationCommand = { rawCommand: 'move field 3 to position 1', operation: ModificationOperation.REORDER_FIELD, target: ModificationTarget.FIELD, parameters: { sourcePosition: 3, targetPosition: 1 }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions[0].label).toBe('Favorite Color'); expect(result.modifiedFormConfig?.questions[1].label).toBe('Name'); expect(result.changes).toContain('From position: 3'); expect(result.changes).toContain('To position: 1'); }); it('should fail with invalid source position', () => { const command: ParsedModificationCommand = { rawCommand: 'move field 10 to position 1', operation: ModificationOperation.REORDER_FIELD, target: ModificationTarget.FIELD, parameters: { sourcePosition: 10, targetPosition: 1 }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(false); expect(result.message).toContain('out of range'); }); }); describe('ADD_OPTION operation', () => { it('should add option to choice field', () => { const command: ParsedModificationCommand = { rawCommand: 'add option Purple to question 3', operation: ModificationOperation.ADD_OPTION, target: ModificationTarget.OPTION, parameters: { fieldNumber: 3, optionText: 'Purple' }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); const choiceField = result.modifiedFormConfig?.questions[2] as any; expect(choiceField.options).toHaveLength(4); expect(choiceField.options[3].text).toBe('Purple'); expect(choiceField.options[3].value).toBe('purple'); }); it('should fail when adding option to non-choice field', () => { const command: ParsedModificationCommand = { rawCommand: 'add option to text field', operation: ModificationOperation.ADD_OPTION, target: ModificationTarget.OPTION, parameters: { fieldNumber: 1, optionText: 'Option' }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(false); expect(result.message).toContain('Cannot add options to field type'); }); it('should fail when option text is missing', () => { const command: ParsedModificationCommand = { rawCommand: 'add option to field 3', operation: ModificationOperation.ADD_OPTION, target: ModificationTarget.OPTION, parameters: { fieldNumber: 3 }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(false); expect(result.message).toBe('Option text is required'); }); }); describe('MODIFY_FIELD operation', () => { it('should modify field label', () => { const command: ParsedModificationCommand = { rawCommand: 'change field 1 label to Full Name', operation: ModificationOperation.MODIFY_FIELD, target: ModificationTarget.FIELD, parameters: { fieldNumber: 1, fieldLabel: 'Full Name' }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions[0].label).toBe('Full Name'); expect(result.changes).toContain('Updated label from "Name" to "Full Name"'); }); it('should modify multiple field properties', () => { const command: ParsedModificationCommand = { rawCommand: 'modify field 2', operation: ModificationOperation.MODIFY_FIELD, target: ModificationTarget.FIELD, parameters: { fieldNumber: 2, fieldLabel: 'Work Email', description: 'Enter your work email address', required: true }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(true); expect(result.modifiedFormConfig?.questions[1].label).toBe('Work Email'); expect(result.modifiedFormConfig?.questions[1].description).toBe('Enter your work email address'); expect(result.modifiedFormConfig?.questions[1].required).toBe(true); expect(result.changes).toHaveLength(4); // Modified field + 3 changes }); it('should fail when no modifications are specified', () => { const command: ParsedModificationCommand = { rawCommand: 'modify field 1', operation: ModificationOperation.MODIFY_FIELD, target: ModificationTarget.FIELD, parameters: { fieldNumber: 1 }, confidence: 1.0 }; const result = formOperations.executeOperation(command, sampleTallyForm, sampleFormConfig); expect(result.success).toBe(false); expect(result.message).toBe('No modifications specified'); }); }); describe('validateFormConfig', () => { it('should validate valid form configuration', () => { const result = formOperations.validateFormConfig(sampleFormConfig); expect(result.valid).toBe(true); expect(result.errors).toHaveLength(0); }); it('should detect duplicate field IDs', () => { const invalidForm = { ...sampleFormConfig, questions: [ ...sampleFormConfig.questions, { id: '1', type: QuestionType.TEXT, label: 'Duplicate Field', required: false } as TextQuestionConfig ] }; const result = formOperations.validateFormConfig(invalidForm); expect(result.valid).toBe(false); expect(result.errors).toContain('Duplicate field IDs found: 1'); }); it('should detect empty field labels', () => { const invalidForm = { ...sampleFormConfig, questions: [ ...sampleFormConfig.questions, { id: '4', type: QuestionType.TEXT, label: '', // Empty label required: false } ] }; const result = formOperations.validateFormConfig(invalidForm); expect(result.valid).toBe(false); expect(result.errors).toContain('1 field(s) have empty labels'); }); it('should detect choice fields without options', () => { const invalidForm = { ...sampleFormConfig, questions: [ ...sampleFormConfig.questions, { id: '4', type: QuestionType.MULTIPLE_CHOICE, label: 'Choice Field', required: false // Missing options } ] }; const result = formOperations.validateFormConfig(invalidForm); expect(result.valid).toBe(false); expect(result.errors).toContain('1 choice field(s) have no options'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/learnwithcc/tally-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server