137 lines
5.0 KiB
JavaScript
137 lines
5.0 KiB
JavaScript
/**
|
|
* Unit tests for the WhatsApp template-parameter selection (#647 follow-up).
|
|
*
|
|
* Pins:
|
|
* - parseTemplateParams sanitizes unknown / non-string / duplicate keys,
|
|
* and falls back to the default 5-slot shape on empty / malformed input.
|
|
* - buildComponents emits ONLY the listed slots, in the listed order, so
|
|
* a 2-parameter template (event_name + gallery_link) sends exactly 2
|
|
* positional values — the reporter's exact case from issue #647.
|
|
* - The legacy 5-slot default still works unchanged for installs that
|
|
* haven't reconfigured.
|
|
*/
|
|
const {
|
|
buildComponents,
|
|
parseTemplateParams,
|
|
DEFAULT_TEMPLATE_PARAMS,
|
|
} = require('../../src/services/whatsappProcessor');
|
|
|
|
const baseData = {
|
|
customer_name: 'Aisha',
|
|
event_name: 'Wedding 2026',
|
|
gallery_link: 'https://picpeak.example/wedding-2026',
|
|
gallery_password: 'StrongPass!',
|
|
expiry_date: '2026-12-31T00:00:00Z',
|
|
};
|
|
|
|
describe('parseTemplateParams', () => {
|
|
test('returns the default 5-slot shape for empty / null / undefined input', () => {
|
|
expect(parseTemplateParams('')).toEqual(DEFAULT_TEMPLATE_PARAMS);
|
|
expect(parseTemplateParams(null)).toEqual(DEFAULT_TEMPLATE_PARAMS);
|
|
expect(parseTemplateParams(undefined)).toEqual(DEFAULT_TEMPLATE_PARAMS);
|
|
});
|
|
|
|
test('returns the default shape for malformed JSON', () => {
|
|
expect(parseTemplateParams('{not json')).toEqual(DEFAULT_TEMPLATE_PARAMS);
|
|
});
|
|
|
|
test('returns the default shape when JSON parses to a non-array', () => {
|
|
expect(parseTemplateParams('"event_name"')).toEqual(DEFAULT_TEMPLATE_PARAMS);
|
|
expect(parseTemplateParams('{"a":1}')).toEqual(DEFAULT_TEMPLATE_PARAMS);
|
|
});
|
|
|
|
test('preserves the reporter\'s 2-slot shape', () => {
|
|
const out = parseTemplateParams(JSON.stringify(['event_name', 'gallery_link']));
|
|
expect(out).toEqual(['event_name', 'gallery_link']);
|
|
});
|
|
|
|
test('drops unknown slot keys', () => {
|
|
const out = parseTemplateParams(JSON.stringify([
|
|
'event_name', 'unknown_slot', 'gallery_link', '__proto__',
|
|
]));
|
|
expect(out).toEqual(['event_name', 'gallery_link']);
|
|
});
|
|
|
|
test('drops duplicate slot keys (first wins)', () => {
|
|
const out = parseTemplateParams(JSON.stringify([
|
|
'event_name', 'gallery_link', 'event_name',
|
|
]));
|
|
expect(out).toEqual(['event_name', 'gallery_link']);
|
|
});
|
|
|
|
test('drops non-string entries', () => {
|
|
const out = parseTemplateParams(JSON.stringify([
|
|
'event_name', 42, null, { a: 1 }, 'gallery_link',
|
|
]));
|
|
expect(out).toEqual(['event_name', 'gallery_link']);
|
|
});
|
|
|
|
test('falls back to default when every entry is invalid', () => {
|
|
const out = parseTemplateParams(JSON.stringify([
|
|
'unknown_a', 'unknown_b', null, 7,
|
|
]));
|
|
expect(out).toEqual(DEFAULT_TEMPLATE_PARAMS);
|
|
});
|
|
|
|
test('also accepts an already-parsed array (defensive)', () => {
|
|
const out = parseTemplateParams(['event_name', 'gallery_link']);
|
|
expect(out).toEqual(['event_name', 'gallery_link']);
|
|
});
|
|
});
|
|
|
|
describe('buildComponents', () => {
|
|
test('legacy default shape emits 5 positional values, gallery_ready order', () => {
|
|
const out = buildComponents(baseData, 'en_US');
|
|
expect(out).toHaveLength(5);
|
|
expect(out[0]).toBe('Aisha');
|
|
expect(out[1]).toBe('Wedding 2026');
|
|
expect(out[2]).toBe('https://picpeak.example/wedding-2026');
|
|
expect(out[3]).toBe('🔒 Password: StrongPass!');
|
|
// expiry date is locale-formatted but always non-empty for a valid date
|
|
expect(out[4]).toMatch(/\d{2}/);
|
|
});
|
|
|
|
test('reporter\'s 2-slot shape — event_name + gallery_link, in that order', () => {
|
|
const out = buildComponents(baseData, 'ar', ['event_name', 'gallery_link']);
|
|
expect(out).toEqual(['Wedding 2026', 'https://picpeak.example/wedding-2026']);
|
|
});
|
|
|
|
test('reorder: gallery_link first, event_name second', () => {
|
|
const out = buildComponents(baseData, 'en_US', ['gallery_link', 'event_name']);
|
|
expect(out).toEqual(['https://picpeak.example/wedding-2026', 'Wedding 2026']);
|
|
});
|
|
|
|
test('empty slot list emits an empty components array (admin opted into nothing)', () => {
|
|
const out = buildComponents(baseData, 'en_US', []);
|
|
expect(out).toEqual([]);
|
|
});
|
|
|
|
test('password_line uses the locale-specific label when included', () => {
|
|
const out = buildComponents(baseData, 'ar', ['password_line']);
|
|
expect(out).toEqual(['🔒 كلمة المرور: StrongPass!']);
|
|
});
|
|
|
|
test('password_line is empty when no real password is set', () => {
|
|
const out = buildComponents(
|
|
{ ...baseData, gallery_password: '' },
|
|
'en_US',
|
|
['password_line'],
|
|
);
|
|
expect(out).toEqual(['']);
|
|
});
|
|
|
|
test('password_line is empty for the "No password required" sentinel', () => {
|
|
const out = buildComponents(
|
|
{ ...baseData, gallery_password: 'No password required' },
|
|
'en_US',
|
|
['password_line'],
|
|
);
|
|
expect(out).toEqual(['']);
|
|
});
|
|
|
|
test('omits expiry_date when omitted from the slot list', () => {
|
|
const out = buildComponents(baseData, 'en_US', ['event_name']);
|
|
expect(out).toEqual(['Wedding 2026']);
|
|
});
|
|
});
|