import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { automationUsesAdvanced, formConditionValues, matchFormFieldCondition, splitAutomationBranches, planAutomationActions, splitFormAnswer, type AutomationStep, } from '@mento/shared'; import { formatFormAnswers, pickRoundRobin, renderAutomationTemplate, } from '../src/automations/automation-engine.util'; const KEY = 'coop_type'; const answers = (raw: string): Record => ({ [KEY]: raw }); function cond(id: string, value: string): AutomationStep { return { id, kind: 'condition', conditionField: 'form_field', conditionKey: KEY, conditionOp: 'contains', conditionValue: value, }; } function act(id: string): AutomationStep { return { id, kind: 'action', action: 'create_activity' }; } const BSJ: AutomationStep[] = [ cond('c1', 'بنکداری'), act('a1'), cond('c2', 'آنلاین شاپ'), act('a2'), cond('c3', 'ویترین نقره'), act('a3'), ]; function run(steps: AutomationStep[], raw: string, mode: 'branches' | 'stop' = 'branches') { const values = answers(raw); return planAutomationActions(steps, mode, (s) => matchFormFieldCondition(s.conditionOp, values[s.conditionKey ?? ''], s.conditionValue), ).map((s) => s.id); } describe('اتوماسیون شاخه‌ای فرم', () => { it('چندانتخابی را می‌شکند', () => { assert.deepEqual(splitFormAnswer('بنکداری, آنلاین شاپ'), ['بنکداری', 'آنلاین شاپ']); assert.deepEqual(splitFormAnswer('الف، ب'), ['الف', 'ب']); assert.deepEqual(splitFormAnswer(''), []); assert.deepEqual(splitFormAnswer(undefined), []); }); it('عملگرهای فیلد فرم', () => { assert.equal(matchFormFieldCondition('contains', 'بنکداری, ویترین نقره', 'ویترین نقره'), true); assert.equal(matchFormFieldCondition('contains', 'بنکداری', 'آنلاین شاپ'), false); assert.equal(matchFormFieldCondition('not_contains', 'بنکداری', 'آنلاین شاپ'), true); assert.equal(matchFormFieldCondition('eq', 'بنکداری', 'بنکداری'), true); assert.equal(matchFormFieldCondition('eq', 'بنکداری, آنلاین شاپ', 'بنکداری'), false); assert.equal(matchFormFieldCondition('is_set', 'x', ''), true); assert.equal(matchFormFieldCondition('is_empty', '', ''), true); }); it('شرط با چند گزینهٔ انتخابی', () => { const many = ['آزاد', 'کارگر']; assert.equal(matchFormFieldCondition('contains', 'آزاد, کارگر, کارمند دولتی', many), true); assert.equal(matchFormFieldCondition('contains', 'آزاد', many), false); assert.equal(matchFormFieldCondition('contains_any', 'کارگر', many), true); assert.equal(matchFormFieldCondition('contains_any', 'کارمند دولتی', many), false); assert.equal(matchFormFieldCondition('not_contains', 'کارمند دولتی', many), true); assert.equal(matchFormFieldCondition('not_contains', 'کارگر', many), false); assert.equal(matchFormFieldCondition('eq', 'کارگر, آزاد', many), true); assert.equal(matchFormFieldCondition('eq', 'آزاد, کارگر, کارمند دولتی', many), false); assert.equal(matchFormFieldCondition('neq', 'آزاد', many), true); assert.deepEqual(formConditionValues({ conditionValue: 'آزاد', conditionValues: [] }), ['آزاد']); assert.deepEqual(formConditionValues({ conditionValue: 'x', conditionValues: many }), many); }); it('هر گزینه فقط شاخهٔ خودش را اجرا می‌کند', () => { assert.deepEqual(run(BSJ, 'بنکداری'), ['a1']); assert.deepEqual(run(BSJ, 'آنلاین شاپ'), ['a2']); assert.deepEqual(run(BSJ, 'ویترین نقره'), ['a3']); }); it('چند تیک = چند فعالیت', () => { assert.deepEqual(run(BSJ, 'بنکداری, ویترین نقره'), ['a1', 'a3']); assert.deepEqual(run(BSJ, 'بنکداری, آنلاین شاپ, ویترین نقره'), ['a1', 'a2', 'a3']); assert.deepEqual(run(BSJ, ''), []); }); it('شرط‌های پشت‌سرهم با «و» ترکیب می‌شوند', () => { const steps = [cond('c1', 'بنکداری'), cond('c2', 'آنلاین شاپ'), act('a1'), act('a2')]; assert.deepEqual(run(steps, 'بنکداری'), []); assert.deepEqual(run(steps, 'بنکداری, آنلاین شاپ'), ['a1', 'a2']); }); it('حالت stop رفتار قدیمی را حفظ می‌کند', () => { assert.deepEqual(run(BSJ, 'آنلاین شاپ', 'stop'), []); assert.deepEqual(run(BSJ, 'بنکداری, آنلاین شاپ, ویترین نقره', 'stop'), ['a1', 'a2', 'a3']); assert.deepEqual( planAutomationActions([act('a0')], undefined, () => false).map((s) => s.id), ['a0'], ); }); it('تفکیک شاخه‌ها برای نمایش موازی', () => { assert.deepEqual(splitAutomationBranches(BSJ), { prefixEnd: 0, branches: [ { start: 0, end: 2 }, { start: 2, end: 4 }, { start: 4, end: 6 }, ], }); const mixed = [act('a0'), cond('c1', 'x'), cond('c2', 'y'), act('a1')]; assert.deepEqual(splitAutomationBranches(mixed), { prefixEnd: 1, branches: [{ start: 1, end: 4 }], }); assert.deepEqual(splitAutomationBranches([act('a0')]), { prefixEnd: 1, branches: [] }); }); it('تشخیص نیاز به اتوماسیون حرفه‌ای', () => { assert.equal(automationUsesAdvanced([act('a0')], 'stop'), false); assert.equal(automationUsesAdvanced([act('a0')], 'branches'), true); assert.equal(automationUsesAdvanced(BSJ, 'stop'), true); assert.equal( automationUsesAdvanced([{ ...act('a0'), assignMode: 'team_round_robin' }], undefined), true, ); }); it('تقسیم نوبتی مساوی و پایدار', () => { const team = ['u3', 'u1', 'u2']; const picks = [1, 2, 3, 4, 5, 6].map((n) => pickRoundRobin(team, n)); assert.deepEqual(picks, ['u1', 'u2', 'u3', 'u1', 'u2', 'u3']); assert.equal(pickRoundRobin([], 1), null); assert.equal(pickRoundRobin(['u1', 'u1'], 2), 'u1'); }); it('جایگذاری متغیرها و خلاصهٔ پاسخ‌ها', () => { const text = renderAutomationTemplate('تماس با {{title}} ({{mobile}}) — {{field.coop_type}} از {{form}}', { title: 'علی', mobile: '0912', formName: 'BSJ', formValues: answers('بنکداری'), }); assert.equal(text, 'تماس با علی (0912) — بنکداری از BSJ'); assert.equal( formatFormAnswers([{ key: KEY, label: 'نوع همکاری' }], { [KEY]: 'بنکداری', extra: 'x' }), 'نوع همکاری: بنکداری\nextra: x', ); }); });