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>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
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, /عدد قیمت را اینجا تکرار نمیکنم/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user