Skip to main content
Glama
field-operations-integration.test.jsโ€ข11.5 kB
/** * Integration tests for field operations with real API calls * Tests the complete flow from MCP tool calls to API interactions */ import { fieldOperationHandlers } from '../field-operations/index.js'; import { testConfig, TestFormManager } from '../config/test-config.js'; import GravityFormsClient from '../gravity-forms-client.js'; import fieldRegistry from '../field-definitions/field-registry.js'; import FieldAwareValidator from '../config/field-validation.js'; console.log('๐Ÿงช Field Operations Integration Tests\n'); // Skip tests if no credentials if (!process.env.TEST_GF_CONSUMER_KEY || !process.env.TEST_GF_CONSUMER_SECRET) { console.log('โš ๏ธ Skipping integration tests - missing test credentials'); console.log('Set TEST_GF_CONSUMER_KEY and TEST_GF_CONSUMER_SECRET to run tests'); process.exit(0); } let apiClient; let testFormManager; let fieldOperations; let testForm; /** * Setup test environment */ async function setup() { try { // Initialize API client with test configuration apiClient = new GravityFormsClient({ GRAVITY_FORMS_CONSUMER_KEY: process.env.TEST_GF_CONSUMER_KEY, GRAVITY_FORMS_CONSUMER_SECRET: process.env.TEST_GF_CONSUMER_SECRET, GRAVITY_FORMS_BASE_URL: process.env.TEST_GF_URL || 'http://localhost:10003' }); const validation = await apiClient.initialize(); if (!validation.available) { throw new Error('Test API not available: ' + validation.error); } // Create test form manager testFormManager = new TestFormManager(apiClient, testConfig); // Initialize field operations const validator = new FieldAwareValidator(); fieldOperations = { fieldManager: { addField: async (formId, fieldType, properties, position) => { // Mock field manager for integration test const form = await apiClient.getForm(formId); const nextId = Math.max(...form.fields.map(f => parseInt(f.id) || 0), 0) + 1; const fieldDef = fieldRegistry[fieldType]; if (!fieldDef) { throw new Error(`Unknown field type: ${fieldType}`); } const field = { id: nextId, type: fieldType, label: properties.label || fieldDef.label || 'Untitled', isRequired: properties.isRequired || false, ...properties }; // Add field to form form.fields.push(field); await apiClient.updateForm(form); return { success: true, field, form_id: formId, position: { index: form.fields.length - 1 } }; }, updateField: async (formId, fieldId, properties) => { const form = await apiClient.getForm(formId); const fieldIndex = form.fields.findIndex(f => f.id == fieldId); if (fieldIndex === -1) { throw new Error(`Field ${fieldId} not found`); } const originalField = { ...form.fields[fieldIndex] }; form.fields[fieldIndex] = { ...originalField, ...properties, id: originalField.id }; await apiClient.updateForm(form); return { success: true, field: form.fields[fieldIndex], changes: { before: originalField, after: form.fields[fieldIndex] }, warnings: { dependencies: [] } }; }, deleteField: async (formId, fieldId, options = {}) => { const form = await apiClient.getForm(formId); const field = form.fields.find(f => f.id == fieldId); if (!field) { throw new Error(`Field ${fieldId} not found`); } // Remove field form.fields = form.fields.filter(f => f.id != fieldId); await apiClient.updateForm(form); return { success: true, deleted_field: { id: field.id, type: field.type, label: field.label }, dependencies: {}, actions_taken: [] }; } }, dependencyTracker: { scanFormDependencies: () => ({ conditionalLogic: [] }), hasBreakingDependencies: () => false }, positionEngine: { calculatePosition: (fields, config) => fields.length }, config: testConfig }; console.log('โœ… Test environment initialized'); } catch (error) { console.error('โŒ Setup failed:', error.message); process.exit(1); } } /** * Create test form */ async function createTestForm() { try { testForm = await testFormManager.createTestForm('FieldOpsIntegration', [ { id: 1, type: 'text', label: 'Name', isRequired: true }, { id: 2, type: 'email', label: 'Email' } ]); console.log(`โœ… Created test form: ${testForm.id}`); return testForm; } catch (error) { console.error('โŒ Failed to create test form:', error.message); throw error; } } /** * Test gf_add_field tool */ async function testAddField() { console.log('\n๐Ÿ”ง Testing gf_add_field...'); try { const result = await fieldOperationHandlers.gf_add_field({ form_id: testForm.id, field_type: 'textarea', properties: { label: 'Comments', placeholder: 'Enter your comments here', isRequired: false }, position: { mode: 'append' } }, fieldOperations); if (result.success) { console.log(` โœ… Added field: ${result.field.label} (ID: ${result.field.id})`); console.log(` ๐Ÿ“ Position: ${result.position.index}`); } else { console.log(` โŒ Failed: ${result.error}`); return false; } // Verify field was actually added const updatedForm = await apiClient.getForm(testForm.id); const addedField = updatedForm.fields.find(f => f.label === 'Comments'); if (addedField) { console.log(' โœ… Field verified in form'); return true; } else { console.log(' โŒ Field not found in form'); return false; } } catch (error) { console.log(` โŒ Error: ${error.message}`); return false; } } /** * Test gf_update_field tool */ async function testUpdateField() { console.log('\n๐Ÿ”ง Testing gf_update_field...'); try { // Get current form to find a field to update const form = await apiClient.getForm(testForm.id); const fieldToUpdate = form.fields.find(f => f.label === 'Email'); if (!fieldToUpdate) { console.log(' โŒ No field to update found'); return false; } const result = await fieldOperationHandlers.gf_update_field({ form_id: testForm.id, field_id: fieldToUpdate.id, properties: { label: 'Email Address', isRequired: true, placeholder: 'your@email.com' } }, fieldOperations); if (result.success) { console.log(` โœ… Updated field: ${result.field.label}`); console.log(` ๐Ÿ“ Changes: ${Object.keys(result.changes.after).length} properties`); } else { console.log(` โŒ Failed: ${result.error}`); return false; } // Verify changes const updatedForm = await apiClient.getForm(testForm.id); const updatedField = updatedForm.fields.find(f => f.id == fieldToUpdate.id); if (updatedField && updatedField.label === 'Email Address') { console.log(' โœ… Update verified in form'); return true; } else { console.log(' โŒ Update not found in form'); return false; } } catch (error) { console.log(` โŒ Error: ${error.message}`); return false; } } /** * Test gf_list_field_types tool */ async function testListFieldTypes() { console.log('\n๐Ÿ”ง Testing gf_list_field_types...'); try { const result = await fieldOperationHandlers.gf_list_field_types({ category: 'standard', include_variants: false }, fieldOperations); if (result.success) { console.log(` โœ… Listed ${result.total} field types`); console.log(` ๐Ÿ“‹ Categories: ${result.categories.join(', ')}`); // Check for expected field types const hasText = result.field_types.some(f => f.type === 'text'); const hasEmail = result.field_types.some(f => f.type === 'email'); if (hasText && hasEmail) { console.log(' โœ… Expected field types found'); return true; } else { console.log(' โŒ Missing expected field types'); return false; } } else { console.log(` โŒ Failed: ${result.error}`); return false; } } catch (error) { console.log(` โŒ Error: ${error.message}`); return false; } } /** * Test gf_delete_field tool */ async function testDeleteField() { console.log('\n๐Ÿ”ง Testing gf_delete_field...'); try { // Get current form to find a field to delete const form = await apiClient.getForm(testForm.id); const fieldToDelete = form.fields.find(f => f.label === 'Comments'); if (!fieldToDelete) { console.log(' โŒ No field to delete found'); return false; } const result = await fieldOperationHandlers.gf_delete_field({ form_id: testForm.id, field_id: fieldToDelete.id, force: true }, fieldOperations); if (result.success) { console.log(` โœ… Deleted field: ${result.deleted_field.label}`); } else { console.log(` โŒ Failed: ${result.error}`); return false; } // Verify field was deleted const updatedForm = await apiClient.getForm(testForm.id); const deletedField = updatedForm.fields.find(f => f.id == fieldToDelete.id); if (!deletedField) { console.log(' โœ… Deletion verified in form'); return true; } else { console.log(' โŒ Field still exists in form'); return false; } } catch (error) { console.log(` โŒ Error: ${error.message}`); return false; } } /** * Cleanup test resources */ async function cleanup() { try { if (testFormManager && testForm) { await testFormManager.deleteTestForm(testForm.id); console.log('\n๐Ÿงน Test form cleaned up'); } } catch (error) { console.log('\nโš ๏ธ Cleanup warning:', error.message); } } /** * Run all tests */ async function runTests() { console.log('๐Ÿš€ Starting Field Operations Integration Tests\n'); await setup(); await createTestForm(); const results = []; // Run all tests results.push(await testAddField()); results.push(await testUpdateField()); results.push(await testListFieldTypes()); results.push(await testDeleteField()); await cleanup(); // Report results const passed = results.filter(r => r).length; const total = results.length; console.log('\n๐Ÿ“Š Integration Test Results:'); console.log(` โœ… Passed: ${passed}/${total}`); console.log(` โŒ Failed: ${total - passed}/${total}`); if (passed === total) { console.log('\n๐ŸŽ‰ All integration tests passed!'); process.exit(0); } else { console.log('\nโš ๏ธ Some integration tests failed'); process.exit(1); } } // Handle cleanup on exit process.on('SIGINT', cleanup); process.on('SIGTERM', cleanup); // Run tests runTests().catch(error => { console.error('\n๐Ÿ’ฅ Test runner error:', error); cleanup().finally(() => process.exit(1)); });

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