Merge branch 'develop' into fix/cw-4085
This commit is contained in:
@@ -145,3 +145,34 @@ export const extractFilenameFromUrl = url => {
|
||||
return match ? match[1] : url;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalizes a comma/newline separated list of domains
|
||||
* @param {string} domains - The comma/newline separated list of domains
|
||||
* @returns {string} - The normalized list of domains
|
||||
* - Converts newlines to commas
|
||||
* - Trims whitespace
|
||||
* - Lowercases entries
|
||||
* - Removes empty values
|
||||
* - De-duplicates while preserving original order
|
||||
*/
|
||||
export const sanitizeAllowedDomains = domains => {
|
||||
if (!domains) return '';
|
||||
|
||||
const tokens = domains
|
||||
.replace(/\r\n/g, '\n')
|
||||
.replace(/\s*\n\s*/g, ',')
|
||||
.split(',')
|
||||
.map(d => d.trim().toLowerCase())
|
||||
.filter(d => d.length > 0);
|
||||
|
||||
// De-duplicate while preserving order using Set and filter index
|
||||
const seen = new Set();
|
||||
const unique = tokens.filter(d => {
|
||||
if (seen.has(d)) return false;
|
||||
seen.add(d);
|
||||
return true;
|
||||
});
|
||||
|
||||
return unique.join(',');
|
||||
};
|
||||
|
||||
@@ -124,6 +124,7 @@ export const getConditionOptions = ({
|
||||
customAttributes,
|
||||
inboxes,
|
||||
languages,
|
||||
labels,
|
||||
statusFilterOptions,
|
||||
teams,
|
||||
type,
|
||||
@@ -150,6 +151,7 @@ export const getConditionOptions = ({
|
||||
country_code: countries,
|
||||
message_type: messageTypeOptions,
|
||||
priority: priorityOptions,
|
||||
labels: generateConditionOptions(labels, 'title'),
|
||||
};
|
||||
|
||||
return conditionFilterMaps[type];
|
||||
|
||||
@@ -78,3 +78,18 @@ export const sanitizeVariableSearchKey = (searchKey = '') => {
|
||||
.replace(/,/g, '') // remove commas
|
||||
.trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert underscore-separated string to title case.
|
||||
* Eg. "round_robin" => "Round Robin"
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
export const formatToTitleCase = str => {
|
||||
return (
|
||||
str
|
||||
?.replace(/_/g, ' ')
|
||||
.replace(/\b\w/g, l => l.toUpperCase())
|
||||
.trim() || ''
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ const FEATURE_HELP_URLS = {
|
||||
team_management: 'https://chwt.app/hc/teams',
|
||||
webhook: 'https://chwt.app/hc/webhooks',
|
||||
billing: 'https://chwt.app/pricing',
|
||||
saml: 'https://chwt.app/hc/saml',
|
||||
};
|
||||
|
||||
export function getHelpUrlForFeature(featureName) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
timeStampAppendedURL,
|
||||
getHostNameFromURL,
|
||||
extractFilenameFromUrl,
|
||||
sanitizeAllowedDomains,
|
||||
} from '../URLHelper';
|
||||
|
||||
describe('#URL Helpers', () => {
|
||||
@@ -318,4 +319,32 @@ describe('#URL Helpers', () => {
|
||||
).toBe('file.doc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitizeAllowedDomains', () => {
|
||||
it('returns empty string for falsy input', () => {
|
||||
expect(sanitizeAllowedDomains('')).toBe('');
|
||||
expect(sanitizeAllowedDomains(null)).toBe('');
|
||||
expect(sanitizeAllowedDomains(undefined)).toBe('');
|
||||
});
|
||||
|
||||
it('trims whitespace and converts newlines to commas', () => {
|
||||
const input = ' example.com \n foo.bar\nbar.baz ';
|
||||
expect(sanitizeAllowedDomains(input)).toBe('example.com,foo.bar,bar.baz');
|
||||
});
|
||||
|
||||
it('handles Windows newlines and mixed spacing', () => {
|
||||
const input = ' example.com\r\n\tfoo.bar , bar.baz ';
|
||||
expect(sanitizeAllowedDomains(input)).toBe('example.com,foo.bar,bar.baz');
|
||||
});
|
||||
|
||||
it('removes empty values from repeated commas', () => {
|
||||
const input = ',,example.com,,foo.bar,,';
|
||||
expect(sanitizeAllowedDomains(input)).toBe('example.com,foo.bar');
|
||||
});
|
||||
|
||||
it('lowercases entries and de-duplicates preserving order', () => {
|
||||
const input = 'Example.com,FOO.bar,example.com,Bar.Baz,foo.BAR';
|
||||
expect(sanitizeAllowedDomains(input)).toBe('example.com,foo.bar,bar.baz');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
convertToCategorySlug,
|
||||
convertToPortalSlug,
|
||||
sanitizeVariableSearchKey,
|
||||
formatToTitleCase,
|
||||
} from '../commons';
|
||||
|
||||
describe('#createPendingMessage', () => {
|
||||
@@ -115,3 +116,51 @@ describe('sanitizeVariableSearchKey', () => {
|
||||
expect(sanitizeVariableSearchKey()).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatToTitleCase', () => {
|
||||
it('converts underscore-separated string to title case', () => {
|
||||
expect(formatToTitleCase('round_robin')).toBe('Round Robin');
|
||||
});
|
||||
|
||||
it('converts single word to title case', () => {
|
||||
expect(formatToTitleCase('priority')).toBe('Priority');
|
||||
});
|
||||
|
||||
it('converts multiple underscores to title case', () => {
|
||||
expect(formatToTitleCase('auto_assignment_policy')).toBe(
|
||||
'Auto Assignment Policy'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles already capitalized words', () => {
|
||||
expect(formatToTitleCase('HIGH_PRIORITY')).toBe('HIGH PRIORITY');
|
||||
});
|
||||
|
||||
it('handles mixed case with underscores', () => {
|
||||
expect(formatToTitleCase('first_Name_last')).toBe('First Name Last');
|
||||
});
|
||||
|
||||
it('handles empty string', () => {
|
||||
expect(formatToTitleCase('')).toBe('');
|
||||
});
|
||||
|
||||
it('handles null input', () => {
|
||||
expect(formatToTitleCase(null)).toBe('');
|
||||
});
|
||||
|
||||
it('handles undefined input', () => {
|
||||
expect(formatToTitleCase(undefined)).toBe('');
|
||||
});
|
||||
|
||||
it('handles string without underscores', () => {
|
||||
expect(formatToTitleCase('hello')).toBe('Hello');
|
||||
});
|
||||
|
||||
it('handles string with numbers', () => {
|
||||
expect(formatToTitleCase('priority_1_high')).toBe('Priority 1 High');
|
||||
});
|
||||
|
||||
it('handles leading and trailing underscores', () => {
|
||||
expect(formatToTitleCase('_leading_trailing_')).toBe('Leading Trailing');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -218,6 +218,7 @@ describe('templateHelper', () => {
|
||||
expect(result.header).toEqual({
|
||||
media_url: '',
|
||||
media_type: 'document',
|
||||
media_name: '',
|
||||
});
|
||||
expect(result.body).toEqual({
|
||||
1: '',
|
||||
|
||||
@@ -51,6 +51,11 @@ export const buildTemplateParameters = (template, hasMediaHeaderValue) => {
|
||||
if (!allVariables.header) allVariables.header = {};
|
||||
allVariables.header.media_url = '';
|
||||
allVariables.header.media_type = headerComponent.format.toLowerCase();
|
||||
|
||||
// For document templates, include media_name field for filename support
|
||||
if (headerComponent.format.toLowerCase() === 'document') {
|
||||
allVariables.header.media_name = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Process button variables
|
||||
|
||||
Reference in New Issue
Block a user