Skip to main content
Glama
field-validation.test.js13.6 kB
/** * Field Validation Test Suite * * Comprehensive tests for field-aware validation using * the field registry to ensure 100% valid structure. */ import { FieldAwareValidator } from '../config/field-validation.js'; import { fieldRegistry, getFieldDefinition, isCompoundField, isArrayField, detectFieldVariant } from '../field-definitions/field-registry.js'; // Test runner let passedTests = 0; let failedTests = 0; const errors = []; function test(description, fn) { try { fn(); passedTests++; console.log(`✅ ${description}`); } catch (error) { failedTests++; errors.push({ test: description, error: error.message }); console.log(`❌ ${description}`); console.log(` Error: ${error.message}`); } } function assert(condition, message) { if (!condition) { throw new Error(message || 'Assertion failed'); } } function assertEqual(actual, expected, message) { if (actual !== expected) { throw new Error(message || `Expected ${expected}, got ${actual}`); } } function assertDeepEqual(actual, expected, message) { if (JSON.stringify(actual) !== JSON.stringify(expected)) { throw new Error(message || `Objects not equal:\nActual: ${JSON.stringify(actual)}\nExpected: ${JSON.stringify(expected)}`); } } console.log('🧪 Starting Field Validation Tests...\n'); // Test 1: Field Registry Loading test('Field registry contains all major field types', () => { assert(fieldRegistry.text, 'Text field should exist'); assert(fieldRegistry.email, 'Email field should exist'); assert(fieldRegistry.address, 'Address field should exist'); assert(fieldRegistry.fileupload, 'File upload field should exist'); assert(fieldRegistry.checkbox, 'Checkbox field should exist'); const fieldCount = Object.keys(fieldRegistry).length; assert(fieldCount >= 40, `Should have 40+ field types, got ${fieldCount}`); }); // Test 2: Field Type Detection test('Correctly identifies compound fields', () => { assert(isCompoundField('address'), 'Address should be compound'); assert(isCompoundField('name'), 'Name should be compound'); assert(isCompoundField('creditcard'), 'Credit card should be compound'); assert(!isCompoundField('text'), 'Text should not be compound'); assert(!isCompoundField('email'), 'Email should not be compound'); }); test('Correctly identifies array fields', () => { assert(isArrayField('checkbox'), 'Checkbox should be array'); assert(isArrayField('multiselect'), 'Multiselect should be array'); assert(isArrayField('list'), 'List should be array'); assert(!isArrayField('text'), 'Text should not be array'); assert(!isArrayField('radio'), 'Radio should not be array'); }); // Test 3: Field Variant Detection test('Detects field variants correctly', () => { const passwordField = { type: 'text', enablePasswordInput: true }; assertEqual(detectFieldVariant(passwordField), 'password', 'Should detect password variant'); const normalTextField = { type: 'text' }; assertEqual(detectFieldVariant(normalTextField), 'default', 'Should detect default variant'); const multiFileField = { type: 'fileupload', multipleFiles: true }; assertEqual(detectFieldVariant(multiFileField), 'multiple', 'Should detect multiple files variant'); }); // Test 4: Form Field Validation test('Validates basic form fields', () => { const fields = [ { id: '1', type: 'text', label: 'Name' }, { id: '2', type: 'email', label: 'Email', isRequired: true } ]; const validated = FieldAwareValidator.validateFormFields(fields); assertEqual(validated.length, 2, 'Should validate 2 fields'); assert(validated[0]._meta, 'Should add metadata'); assertEqual(validated[0]._variant, 'default', 'Should detect default variant'); }); test('Rejects invalid field configurations', () => { const invalidFields = [ { // Missing type id: '1', label: 'Test' } ]; try { FieldAwareValidator.validateFormFields(invalidFields); throw new Error('Should have thrown validation error'); } catch (error) { assert(error.message.includes('Field must have a type'), 'Should reject field without type'); } }); test('Validates choice fields', () => { const radioField = { id: '1', type: 'radio', label: 'Choose One', choices: [ { text: 'Option 1', value: 'opt1' }, { text: 'Option 2', value: 'opt2' } ] }; const validation = FieldAwareValidator.validateField(radioField); assert(validation.isValid, 'Should validate radio field with choices'); }); test('Validates conditional logic', () => { const fieldWithLogic = { id: '1', type: 'text', label: 'Conditional Field', conditionalLogic: { actionType: 'show', logicType: 'all', rules: [ { fieldId: '2', operator: 'is', value: 'yes' } ] } }; const validation = FieldAwareValidator.validateField(fieldWithLogic); assert(validation.isValid, 'Should validate conditional logic'); }); // Test 5: Entry Data Validation test('Validates entry data against form', () => { const form = { id: '1', fields: [ { id: '1', type: 'text', label: 'Name', isRequired: true }, { id: '2', type: 'email', label: 'Email' } ] }; const entryData = { '1': 'John Doe', '2': 'john@example.com' }; const validated = FieldAwareValidator.validateEntryData(entryData, form); assert(validated['1'] === 'John Doe', 'Should preserve text value'); assert(validated['2'] === 'john@example.com', 'Should preserve email value'); }); test('Validates required fields in entries', () => { const form = { id: '1', fields: [ { id: '1', type: 'text', label: 'Required Field', isRequired: true } ] }; const entryData = { '1': '' // Empty required field }; try { FieldAwareValidator.validateEntryData(entryData, form); throw new Error('Should have thrown validation error'); } catch (error) { assert(error.message.includes('is required'), 'Should reject empty required field'); } }); test('Validates email format', () => { const form = { id: '1', fields: [ { id: '1', type: 'email', label: 'Email' } ] }; const invalidEntry = { '1': 'not-an-email' }; try { FieldAwareValidator.validateEntryData(invalidEntry, form); throw new Error('Should have thrown validation error'); } catch (error) { assert(error.message.includes('Invalid email'), 'Should reject invalid email'); } }); // Test 6: Compound Field Handling test('Handles compound field data correctly', () => { const form = { id: '1', fields: [ { id: '3', type: 'address', label: 'Address' } ] }; const entryData = { '3.1': '123 Main St', '3.2': 'Apt 4', '3.3': 'New York', '3.4': 'NY', '3.5': '10001', '3.6': 'United States' }; const validated = FieldAwareValidator.validateEntryData(entryData, form); assert(validated['3.1'] === '123 Main St', 'Should preserve street address'); assert(validated['3.3'] === 'New York', 'Should preserve city'); }); test('Extracts compound field values', () => { const field = { id: '3', type: 'address' }; const entryData = { '3.1': '123 Main St', '3.3': 'New York', '3.4': 'NY' }; const definition = getFieldDefinition('address'); const value = FieldAwareValidator.getFieldValue(entryData, field, definition); assert(value.street === '123 Main St', 'Should extract street'); assert(value.city === 'New York', 'Should extract city'); assert(value.state === 'NY', 'Should extract state'); }); // Test 7: Array Field Handling test('Handles checkbox array data', () => { const form = { id: '1', fields: [ { id: '4', type: 'checkbox', label: 'Options', choices: [ { text: 'Option 1', value: 'opt1' }, { text: 'Option 2', value: 'opt2' }, { text: 'Option 3', value: 'opt3' } ] } ] }; const entryData = { '4.1': 'opt1', '4.2': 'opt3' }; const field = form.fields[0]; const definition = getFieldDefinition('checkbox'); const value = FieldAwareValidator.getFieldValue(entryData, field, definition); assert(Array.isArray(value), 'Should return array for checkbox'); assertEqual(value.length, 2, 'Should have 2 selected values'); assert(value.includes('opt1'), 'Should include first selection'); assert(value.includes('opt3'), 'Should include second selection'); }); // Test 8: File Upload Variants test('Handles file upload variants', () => { const singleFileField = { id: '5', type: 'fileupload', label: 'Single File', multipleFiles: false }; const multiFileField = { id: '6', type: 'fileupload', label: 'Multiple Files', multipleFiles: true }; assertEqual(detectFieldVariant(singleFileField), 'single', 'Should detect single file variant'); assertEqual(detectFieldVariant(multiFileField), 'multiple', 'Should detect multiple files variant'); // Validate multiple file JSON storage const form = { id: '1', fields: [multiFileField] }; const validEntry = { '6': JSON.stringify(['file1.pdf', 'file2.jpg']) }; const validated = FieldAwareValidator.validateEntryData(validEntry, form); assert(validated['6'], 'Should accept valid JSON array for multiple files'); }); // Test 9: Submission Processing test('Processes form submission data', () => { const form = { id: '1', fields: [ { id: '1', type: 'text', label: 'Name' }, { id: '2', type: 'email', label: 'Email' } ] }; const submissionData = { 'input_1': 'John Doe', 'input_2': 'john@example.com' }; const processed = FieldAwareValidator.processSubmissionData(submissionData, form); assertEqual(processed['1'], 'John Doe', 'Should process text field'); assertEqual(processed['2'], 'john@example.com', 'Should process email field'); assert(processed.form_id === '1', 'Should include form ID'); assert(processed.status === 'active', 'Should set active status'); }); test('Processes compound field submission', () => { const form = { id: '1', fields: [ { id: '3', type: 'name', label: 'Full Name' } ] }; const submissionData = { 'input_3_2': 'John', // First name 'input_3_6': 'Doe' // Last name }; const processed = FieldAwareValidator.processSubmissionData(submissionData, form); assertEqual(processed['3.2'], 'John', 'Should process first name'); assertEqual(processed['3.6'], 'Doe', 'Should process last name'); }); test('Processes checkbox submission', () => { const form = { id: '1', fields: [ { id: '4', type: 'checkbox', label: 'Options', choices: [ { text: 'A', value: 'a' }, { text: 'B', value: 'b' }, { text: 'C', value: 'c' } ] } ] }; const submissionData = { 'input_4_1': 'a', 'input_4_2': 'c' // Changed from input_4_3 to input_4_2 for sequential numbering }; const processed = FieldAwareValidator.processSubmissionData(submissionData, form); assertEqual(processed['4.1'], 'a', 'Should process first checkbox'); assertEqual(processed['4.2'], 'c', 'Should process second checkbox'); }); // Test 10: Unknown Field Types test('Handles unknown field types gracefully', () => { const fields = [ { id: '1', type: 'custom_field_type', label: 'Custom Field' } ]; const validated = FieldAwareValidator.validateFormFields(fields); assertEqual(validated.length, 1, 'Should allow unknown field types'); assert(validated[0]._unknown === true, 'Should mark as unknown'); }); // Test 11: Validation Summary test('Generates validation summary', () => { const form = { id: '1', fields: [ { id: '1', type: 'text', label: 'Text', isRequired: true }, { id: '2', type: 'address', label: 'Address', conditionalLogic: { actionType: 'show', logicType: 'all', rules: [] } }, { id: '3', type: 'checkbox', label: 'Options', choices: [] }, { id: '4', type: 'unknown_type', label: 'Unknown' } ] }; const summary = FieldAwareValidator.getValidationSummary(form); assertEqual(summary.totalFields, 4, 'Should count total fields'); assertEqual(summary.requiredFields, 1, 'Should count required fields'); assertEqual(summary.conditionalFields, 1, 'Should count conditional fields'); assertEqual(summary.compoundFields, 1, 'Should count compound fields'); assertEqual(summary.arrayFields, 1, 'Should count array fields'); assert(summary.unknownTypes.includes('unknown_type'), 'Should track unknown types'); }); // Print test results console.log('\n' + '='.repeat(50)); console.log('📊 Test Results:'); console.log(`✅ Passed: ${passedTests}`); console.log(`❌ Failed: ${failedTests}`); if (failedTests > 0) { console.log('\n❌ Failed Tests:'); errors.forEach(({ test, error }) => { console.log(` - ${test}: ${error}`); }); process.exit(1); } else { console.log('\n🎉 All field validation tests passed!'); process.exit(0); }

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/GravityKit/gravity-mcp'

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