Skip to main content
Glama
field-dependencies.test.js8.82 kB
/** * Unit tests for DependencyTracker class * Tests dependency scanning for conditional logic, calculations, merge tags */ import test from 'node:test'; import assert from 'node:assert'; import { DependencyTracker } from '../field-operations/field-dependencies.js'; // Sample form with various dependencies const createTestForm = () => ({ id: 1, title: 'Test Form', fields: [ { id: 1, type: 'text', label: 'Name', conditionalLogic: { enabled: true, rules: [ { fieldId: 2, operator: 'is', value: 'yes' } ] } }, { id: 2, type: 'radio', label: 'Subscribe', choices: [ { text: 'Yes', value: 'yes' }, { text: 'No', value: 'no' } ] }, { id: 3, type: 'number', label: 'Price', enableCalculation: true, calculationFormula: '{Quantity:4} * 10 + {Tax:5}' }, { id: 4, type: 'number', label: 'Quantity' }, { id: 5, type: 'number', label: 'Tax' }, { id: 6, type: 'text', label: 'Dynamic Field', allowsPrepopulate: true, inputName: 'dynamic_param' }, { id: 7, type: 'html', content: 'Total: {Price:3}' }, { id: 8, type: 'text', label: 'Referrer', defaultValue: '{dynamic_param}' } ], notifications: { notification_1: { name: 'Admin Notification', subject: 'New submission from {Name:1}', message: 'Name: {Name:1}\nQuantity: {Quantity:4}\nPrice: {Price:3}', to: '{Email:9}' } }, confirmations: { confirmation_1: { name: 'Thank You', type: 'message', message: 'Thank you {Name:1}, your total is {Price:3}' } } }); test('DependencyTracker - scanConditionalLogic', async (t) => { const tracker = new DependencyTracker(); await t.test('finds conditional logic dependencies', () => { const form = createTestForm(); const dependencies = { conditionalLogic: [] }; tracker.scanConditionalLogic(form, 2, dependencies); assert.strictEqual(dependencies.conditionalLogic.length, 1); assert.strictEqual(dependencies.conditionalLogic[0].field_id, 1); assert.strictEqual(dependencies.conditionalLogic[0].field_label, 'Name'); assert.strictEqual(dependencies.conditionalLogic[0].rule_count, 1); }); await t.test('handles no conditional logic dependencies', () => { const form = createTestForm(); const dependencies = { conditionalLogic: [] }; tracker.scanConditionalLogic(form, 999, dependencies); assert.strictEqual(dependencies.conditionalLogic.length, 0); }); }); test('DependencyTracker - scanCalculations', async (t) => { const tracker = new DependencyTracker(); await t.test('finds calculation formula dependencies', () => { const form = createTestForm(); const dependencies = { calculations: [] }; tracker.scanCalculations(form, 4, dependencies); assert.strictEqual(dependencies.calculations.length, 1); assert.strictEqual(dependencies.calculations[0].field_id, 3); assert.strictEqual(dependencies.calculations[0].field_label, 'Price'); assert.ok(dependencies.calculations[0].formula.includes('{Quantity:4}')); }); await t.test('finds multiple field references in formula', () => { const form = createTestForm(); const dependencies = { calculations: [] }; // Check for field 5 (Tax) tracker.scanCalculations(form, 5, dependencies); assert.strictEqual(dependencies.calculations.length, 1); assert.strictEqual(dependencies.calculations[0].field_id, 3); assert.ok(dependencies.calculations[0].matches.some(m => m.includes(':5'))); }); }); test('DependencyTracker - scanMergeTags', async (t) => { const tracker = new DependencyTracker(); await t.test('finds merge tags in notifications', () => { const form = createTestForm(); const dependencies = { mergeTags: [] }; tracker.scanMergeTags(form, 1, dependencies); // Should find in subject and message const notificationTags = dependencies.mergeTags.filter(d => d.location === 'notification'); assert.strictEqual(notificationTags.length, 2); assert.ok(notificationTags.some(d => d.field === 'subject')); assert.ok(notificationTags.some(d => d.field === 'message')); }); await t.test('finds merge tags in confirmations', () => { const form = createTestForm(); const dependencies = { mergeTags: [] }; tracker.scanMergeTags(form, 1, dependencies); const confirmationTags = dependencies.mergeTags.filter(d => d.location === 'confirmation'); assert.strictEqual(confirmationTags.length, 1); assert.strictEqual(confirmationTags[0].field, 'message'); }); await t.test('finds merge tags in field content', () => { const form = createTestForm(); const dependencies = { mergeTags: [] }; tracker.scanMergeTags(form, 3, dependencies); const fieldTags = dependencies.mergeTags.filter(d => d.location === 'field'); assert.strictEqual(fieldTags.length, 1); assert.strictEqual(fieldTags[0].field_id, 7); assert.strictEqual(fieldTags[0].property, 'content'); }); }); test('DependencyTracker - scanDynamicPopulation', async (t) => { const tracker = new DependencyTracker(); await t.test('finds dynamic population references', () => { const form = createTestForm(); const dependencies = { dynamicPopulation: [] }; tracker.scanDynamicPopulation(form, 6, dependencies); // Should find field 8 using the parameter and field 6 accepting it assert.strictEqual(dependencies.dynamicPopulation.length, 2); const reference = dependencies.dynamicPopulation.find(d => d.field_id === 8); assert.ok(reference); assert.strictEqual(reference.parameter, 'dynamic_param'); assert.strictEqual(reference.usage, 'default_value'); const accepts = dependencies.dynamicPopulation.find(d => d.usage === 'accepts_population'); assert.ok(accepts); assert.strictEqual(accepts.field_id, 6); }); }); test('DependencyTracker - scanFormDependencies', async (t) => { const tracker = new DependencyTracker(); await t.test('scans all dependency types', () => { const form = createTestForm(); const dependencies = tracker.scanFormDependencies(form, 1); // Field 1 should have dependencies in confirmations and notifications assert.ok(dependencies.mergeTags.length > 0); assert.strictEqual(dependencies.conditionalLogic.length, 0); // Field 1 doesn't appear in conditional logic assert.strictEqual(dependencies.calculations.length, 0); // Field 1 not in calculations }); await t.test('returns comprehensive dependency report', () => { const form = createTestForm(); const dependencies = tracker.scanFormDependencies(form, 4); // Field 4 (Quantity) is used in calculations and notifications assert.ok(dependencies.calculations.length > 0); assert.ok(dependencies.mergeTags.length > 0); }); }); test('DependencyTracker - hasBreakingDependencies', async (t) => { const tracker = new DependencyTracker(); await t.test('detects breaking dependencies', () => { const dependencies = { conditionalLogic: [{ field_id: 1 }], calculations: [], mergeTags: [], dynamicPopulation: [] }; assert.strictEqual(tracker.hasBreakingDependencies(dependencies), true); }); await t.test('detects no breaking dependencies', () => { const dependencies = { conditionalLogic: [], calculations: [], mergeTags: [], dynamicPopulation: [{ usage: 'accepts_population' }] }; assert.strictEqual(tracker.hasBreakingDependencies(dependencies), false); }); }); test('DependencyTracker - generateDependencySummary', async (t) => { const tracker = new DependencyTracker(); await t.test('generates summary for multiple dependencies', () => { const dependencies = { conditionalLogic: [{ field_id: 1 }, { field_id: 2 }], calculations: [{ field_id: 3 }], mergeTags: [{ location: 'notification' }], dynamicPopulation: [] }; const summary = tracker.generateDependencySummary(dependencies); assert.ok(summary.includes('2 conditional logic rule(s)')); assert.ok(summary.includes('1 calculation formula(s)')); assert.ok(summary.includes('1 merge tag reference(s)')); }); await t.test('generates summary for no dependencies', () => { const dependencies = { conditionalLogic: [], calculations: [], mergeTags: [], dynamicPopulation: [] }; const summary = tracker.generateDependencySummary(dependencies); assert.strictEqual(summary, 'No dependencies found'); }); });

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