Files
CRM_Montzar/apps/api/test/behavior.test.ts
T
MahdiandCursor 002f697dfe feat: add DISC profiles and a sales copilot as an org module
Help reps capture a customer's likely behavior style and get negotiation guidance, with the feature gated from super admin.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 21:04:26 +03:30

105 lines
4.0 KiB
TypeScript

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
analyzeCopilot,
followUpMessage,
scoreDisc,
type DiscAnswers,
} from '../../../packages/shared/src/behavior';
function all(type: DiscAnswers[string]): DiscAnswers {
return { '1': type, '2': type, '3': type, '4': type, '5': type, '6': type, '7': type, '8': type };
}
describe('امتیاز DISC', () => {
it('گزینه D فقط امتیاز D را زیاد می‌کند', () => {
const result = scoreDisc(all('D'));
assert.equal(result.scores.D, 8);
assert.equal(result.scores.I, 0);
assert.equal(result.scores.S, 0);
assert.equal(result.scores.C, 0);
assert.equal(result.label, 'D');
assert.equal(result.percents.D, 100);
});
it('هر چهار تیپ را جدا جمع می‌کند', () => {
const result = scoreDisc({ '1': 'D', '2': 'I', '3': 'S', '4': 'C', '5': 'D', '6': 'I' });
assert.deepEqual(result.scores, { D: 2, I: 2, S: 1, C: 1 });
assert.equal(result.validCount, 6);
assert.equal(result.label, 'D/I');
});
it('در تساوی نتیجه ترکیبی می‌دهد', () => {
const result = scoreDisc({ '1': 'S', '2': 'C', '3': 'S', '4': 'C' });
assert.equal(result.reliable, true);
assert.equal(result.label, 'S/C');
});
it('با کمتر از چهار پاسخ نتیجه قابل اتکا نمی‌دهد', () => {
const result = scoreDisc({ '1': 'D', '2': 'D', '3': 'D' });
assert.equal(result.reliable, false);
assert.equal(result.label, 'اطلاعات ناکافی برای نتیجه قابل اتکا');
assert.deepEqual(result.primaryTypes, []);
});
});
describe('دستیار فروش', () => {
it('برای نیاز ندارم سؤال کشف نیاز می‌دهد و قیمت نمی‌سازد', () => {
const analysis = analyzeCopilot({
topic: 'objection',
goal: 'discover_need',
customerStatement: 'فعلاً نیازی ندارم؛ همین روش فعلی برای من جواب می‌دهد.',
facts: { customerName: 'مهدی', stageName: 'سرنخ جدید' },
});
assert.equal(analysis.objection_category, 'no_need');
assert.match(analysis.recommended_question, /روش فعلی/);
assert.doesNotMatch(analysis.suggested_response, /\d{3,}/);
assert.equal(analysis.disc_context, null);
});
it('DISC تازه را راهنما می‌کند نه تشخیص قطعی', () => {
const analysis = analyzeCopilot({
topic: 'pricing',
goal: 'follow_price',
customerStatement: 'قیمتتان بالاست.',
facts: {
customerName: 'مهدی',
priceOffered: null,
disc: {
label: 'D',
primaryTypes: ['D'],
reliable: true,
assessedAt: new Date().toISOString(),
},
},
});
assert.equal(analysis.disc_context?.used, true);
assert.match(analysis.suggested_response, /کوتاه می‌گویم/);
assert.match(analysis.confidence_note, /قطعی/);
assert.match(analysis.suggested_response, /عدد یا تخفیف جدیدی پیشنهاد نمی‌شود/);
});
it('حرف کوتاه را قطعی تحلیل نمی‌کند', () => {
const analysis = analyzeCopilot({
topic: 'intro',
goal: 'discover_need',
customerStatement: 'نه',
facts: {},
});
assert.equal(analysis.needs_more_input, true);
assert.equal(analysis.suggested_response, '');
});
it('متن پیگیری قیمت را از خودش نمی‌سازد', () => {
const analysis = analyzeCopilot({
topic: 'pricing',
goal: 'follow_price',
customerStatement: 'باید فکر کنم.',
facts: { customerName: 'سارا', productName: 'منتکت' },
});
const text = followUpMessage('price_followup', { customerName: 'سارا', productName: 'منتکت' }, analysis);
assert.match(text, /منتکت/);
assert.match(text, /عدد قیمت را اینجا تکرار نمی‌کنم/);
});
});