@capillarytech/creatives-library 8.0.169 → 8.0.170-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app.js CHANGED
@@ -26,6 +26,10 @@ import './styles/main.scss';
26
26
  import pathConfig from './config/path';
27
27
  import ignoredErrorMessages from './utils/ignoredErrorMessages';
28
28
  import App from './containers/App';
29
+ import { setupNewrelicWebVitals } from './utils/commonUtils';
30
+
31
+ // Setup New Relic WebVitals tracking
32
+ setupNewrelicWebVitals();
29
33
 
30
34
  // const browserHistory = useRouterHistory(createHistory)({
31
35
  // basename: pathConfig.publicPath,
@@ -1,3 +1,5 @@
1
+ export const HOSTNAME_PATTERN = /^(?:([^.]+)\.)?(?:([^.]+)\.)?intouch\.capillarytech\.com$/;
2
+
1
3
  export const GET_SIDEBAR_REQUEST = 'app/App/GET_SIDEBAR_REQUEST';
2
4
  export const GET_SIDEBAR_SUCCESS = 'app/App/GET_SIDEBAR_SUCCESS';
3
5
  export const GET_SIDEBAR_FAILURE = 'app/App/GET_SIDEBAR_FAILURE';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.169",
4
+ "version": "8.0.170-beta.2",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -18,6 +18,7 @@
18
18
  "@bugsnag/plugin-react": "7.2.1",
19
19
  "@capillarytech/cap-ui-utils": "3.0.4",
20
20
  "@capillarytech/vulcan-react-sdk": "^2.3.5",
21
+ "@newrelic/browser-agent": "^1.293.0",
21
22
  "@mailupinc/bee-plugin": "^1.2.0",
22
23
  "babel-cli": "^6.26.0",
23
24
  "chalk": "^2.4.2",
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { FormattedMessage } from 'react-intl';
3
3
  import get from 'lodash/get';
4
4
  import { convert } from "html-to-text";
5
+ import { BrowserAgent } from '@newrelic/browser-agent';
5
6
  import { EMBEDDED } from "../v2Containers/Whatsapp/constants";
6
7
  import {
7
8
  EDIT,
@@ -18,6 +19,8 @@ import {
18
19
  import { GLOBAL_CONVERT_OPTIONS } from "../v2Components/FormBuilder/constants";
19
20
  import { checkSupport, extractNames } from "./tagValidations";
20
21
  import { SMS_TRAI_VAR } from '../v2Containers/SmsTrai/Edit/constants';
22
+ import appConfig from '../../app-config';
23
+ import { HOSTNAME_PATTERN } from '../containers/App/constants';
21
24
  export const apiMessageFormatHandler = (id, fallback) => (
22
25
  <FormattedMessage id={id} defaultMessage={fallback} />
23
26
  );
@@ -429,3 +432,50 @@ export const validateInAppContent = async (formData, options) => {
429
432
  // Errors already reported via the 'onError' callback passed to _validatePlatformSpecificContent
430
433
  return false;
431
434
  };
435
+
436
+ // New Relic WebVitals tracking setup
437
+ const {
438
+ newrelic: { enabled = false, environments = {}, licenseKey },
439
+ } = appConfig;
440
+
441
+ const getNewrelicAppId = () => {
442
+ const hostname = window.location.hostname;
443
+ const match = hostname.match(HOSTNAME_PATTERN);
444
+ const environment = match?.[2] ?? match?.[1] ?? 'nightly';
445
+ return environments[environment]?.appId;
446
+ };
447
+
448
+ export const setupNewrelicWebVitals = () => {
449
+ if (process.env.NODE_ENV === 'production' && enabled) {
450
+ const options = {
451
+ info: {
452
+ applicationID: getNewrelicAppId(),
453
+ licenseKey: licenseKey,
454
+ },
455
+ init: {
456
+ ajax: { enabled: true },
457
+ distributed_tracing: {
458
+ enabled: true,
459
+ exclude_newrelic_header: true,
460
+ },
461
+ generic_events: { enabled: true },
462
+ jserrors: { enabled: true },
463
+ logging: { enabled: false },
464
+ metrics: { enabled: true },
465
+ page_action: { enabled: true },
466
+ page_view_timing: { enabled: true },
467
+ performance: {
468
+ capture_detail: true,
469
+ },
470
+ privacy: { cookies_enabled: true },
471
+ session_replay: { enabled: false },
472
+ session_trace: { enabled: true },
473
+ soft_navigations: { enabled: true },
474
+ spa: { enabled: true },
475
+ user_actions: { enabled: true },
476
+ },
477
+ };
478
+ // The agent loader code executes immediately on instantiation.
479
+ new BrowserAgent(options);
480
+ }
481
+ };
@@ -0,0 +1,546 @@
1
+ const { mockBrowserAgent } = require('../../../__mocks__/newRelicMock');
2
+ import { setupNewrelicWebVitals } from '../commonUtils';
3
+
4
+ describe('commonUtils - New Relic Integration', () => {
5
+ beforeEach(() => {
6
+ process.env.NODE_ENV = 'production';
7
+ mockBrowserAgent.reset();
8
+ delete window.location;
9
+ });
10
+
11
+ afterAll(() => {
12
+ process.env.NODE_ENV = 'test';
13
+ });
14
+
15
+ describe('setupNewrelicWebVitals', () => {
16
+ it('should initialize BrowserAgent with correct options - localhost', () => {
17
+ Object.defineProperty(window, 'location', {
18
+ value: { hostname: 'localhost' },
19
+ writable: true,
20
+ configurable: true,
21
+ });
22
+ setupNewrelicWebVitals();
23
+
24
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
25
+
26
+ const expectedOptions = {
27
+ info: {
28
+ applicationID: '718411553',
29
+ licenseKey: '082da40fff',
30
+ },
31
+ init: {
32
+ ajax: { enabled: true },
33
+ distributed_tracing: {
34
+ enabled: true,
35
+ exclude_newrelic_header: true,
36
+ },
37
+ generic_events: { enabled: true },
38
+ jserrors: { enabled: true },
39
+ logging: { enabled: false },
40
+ metrics: { enabled: true },
41
+ page_action: { enabled: true },
42
+ page_view_timing: { enabled: true },
43
+ performance: {
44
+ capture_detail: true,
45
+ },
46
+ privacy: { cookies_enabled: true },
47
+ session_replay: { enabled: false },
48
+ session_trace: { enabled: true },
49
+ soft_navigations: { enabled: true },
50
+ spa: { enabled: true },
51
+ user_actions: { enabled: true },
52
+ },
53
+ };
54
+
55
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
56
+ });
57
+
58
+ it('should initialize BrowserAgent with correct options - nightly', () => {
59
+ Object.defineProperty(window, 'location', {
60
+ value: { hostname: 'nightly.intouch.capillarytech.com' },
61
+ writable: true,
62
+ configurable: true,
63
+ });
64
+ setupNewrelicWebVitals();
65
+
66
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
67
+
68
+ const expectedOptions = {
69
+ info: {
70
+ applicationID: '718411553',
71
+ licenseKey: '082da40fff',
72
+ },
73
+ init: {
74
+ ajax: { enabled: true },
75
+ distributed_tracing: {
76
+ enabled: true,
77
+ exclude_newrelic_header: true,
78
+ },
79
+ generic_events: { enabled: true },
80
+ jserrors: { enabled: true },
81
+ logging: { enabled: false },
82
+ metrics: { enabled: true },
83
+ page_action: { enabled: true },
84
+ page_view_timing: { enabled: true },
85
+ performance: {
86
+ capture_detail: true,
87
+ },
88
+ privacy: { cookies_enabled: true },
89
+ session_replay: { enabled: false },
90
+ session_trace: { enabled: true },
91
+ soft_navigations: { enabled: true },
92
+ spa: { enabled: true },
93
+ user_actions: { enabled: true },
94
+ },
95
+ };
96
+
97
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
98
+ });
99
+
100
+ it('should initialize BrowserAgent with correct options - staging', () => {
101
+ Object.defineProperty(window, 'location', {
102
+ value: { hostname: 'staging.intouch.capillarytech.com' },
103
+ writable: true,
104
+ configurable: true,
105
+ });
106
+ setupNewrelicWebVitals();
107
+
108
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
109
+
110
+ const expectedOptions = {
111
+ info: {
112
+ applicationID: '718413027',
113
+ licenseKey: '082da40fff',
114
+ },
115
+ init: {
116
+ ajax: { enabled: true },
117
+ distributed_tracing: {
118
+ enabled: true,
119
+ exclude_newrelic_header: true,
120
+ },
121
+ generic_events: { enabled: true },
122
+ jserrors: { enabled: true },
123
+ logging: { enabled: false },
124
+ metrics: { enabled: true },
125
+ page_action: { enabled: true },
126
+ page_view_timing: { enabled: true },
127
+ performance: {
128
+ capture_detail: true,
129
+ },
130
+ privacy: { cookies_enabled: true },
131
+ session_replay: { enabled: false },
132
+ session_trace: { enabled: true },
133
+ soft_navigations: { enabled: true },
134
+ spa: { enabled: true },
135
+ user_actions: { enabled: true },
136
+ },
137
+ };
138
+
139
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
140
+ });
141
+
142
+ it('should initialize BrowserAgent with correct options - ushc', () => {
143
+ Object.defineProperty(window, 'location', {
144
+ value: { hostname: 'ushc.intouch.capillarytech.com' },
145
+ writable: true,
146
+ configurable: true,
147
+ });
148
+ setupNewrelicWebVitals();
149
+
150
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
151
+
152
+ const expectedOptions = {
153
+ info: {
154
+ applicationID: '718413124',
155
+ licenseKey: '082da40fff',
156
+ },
157
+ init: {
158
+ ajax: { enabled: true },
159
+ distributed_tracing: {
160
+ enabled: true,
161
+ exclude_newrelic_header: true,
162
+ },
163
+ generic_events: { enabled: true },
164
+ jserrors: { enabled: true },
165
+ logging: { enabled: false },
166
+ metrics: { enabled: true },
167
+ page_action: { enabled: true },
168
+ page_view_timing: { enabled: true },
169
+ performance: {
170
+ capture_detail: true,
171
+ },
172
+ privacy: { cookies_enabled: true },
173
+ session_replay: { enabled: false },
174
+ session_trace: { enabled: true },
175
+ soft_navigations: { enabled: true },
176
+ spa: { enabled: true },
177
+ user_actions: { enabled: true },
178
+ },
179
+ };
180
+
181
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
182
+ });
183
+
184
+ it('should initialize BrowserAgent with correct options - apac', () => {
185
+ Object.defineProperty(window, 'location', {
186
+ value: { hostname: 'apac.intouch.capillarytech.com' },
187
+ writable: true,
188
+ configurable: true,
189
+ });
190
+ setupNewrelicWebVitals();
191
+
192
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
193
+
194
+ const expectedOptions = {
195
+ info: {
196
+ applicationID: '718413134',
197
+ licenseKey: '082da40fff',
198
+ },
199
+ init: {
200
+ ajax: { enabled: true },
201
+ distributed_tracing: {
202
+ enabled: true,
203
+ exclude_newrelic_header: true,
204
+ },
205
+ generic_events: { enabled: true },
206
+ jserrors: { enabled: true },
207
+ logging: { enabled: false },
208
+ metrics: { enabled: true },
209
+ page_action: { enabled: true },
210
+ page_view_timing: { enabled: true },
211
+ performance: {
212
+ capture_detail: true,
213
+ },
214
+ privacy: { cookies_enabled: true },
215
+ session_replay: { enabled: false },
216
+ session_trace: { enabled: true },
217
+ soft_navigations: { enabled: true },
218
+ spa: { enabled: true },
219
+ user_actions: { enabled: true },
220
+ },
221
+ };
222
+
223
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
224
+ });
225
+
226
+ it('should initialize BrowserAgent with correct options - north-america', () => {
227
+ Object.defineProperty(window, 'location', {
228
+ value: { hostname: 'north-america.intouch.capillarytech.com' },
229
+ writable: true,
230
+ configurable: true,
231
+ });
232
+ setupNewrelicWebVitals();
233
+
234
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
235
+
236
+ const expectedOptions = {
237
+ info: {
238
+ applicationID: '718413144',
239
+ licenseKey: '082da40fff',
240
+ },
241
+ init: {
242
+ ajax: { enabled: true },
243
+ distributed_tracing: {
244
+ enabled: true,
245
+ exclude_newrelic_header: true,
246
+ },
247
+ generic_events: { enabled: true },
248
+ jserrors: { enabled: true },
249
+ logging: { enabled: false },
250
+ metrics: { enabled: true },
251
+ page_action: { enabled: true },
252
+ page_view_timing: { enabled: true },
253
+ performance: {
254
+ capture_detail: true,
255
+ },
256
+ privacy: { cookies_enabled: true },
257
+ session_replay: { enabled: false },
258
+ session_trace: { enabled: true },
259
+ soft_navigations: { enabled: true },
260
+ spa: { enabled: true },
261
+ user_actions: { enabled: true },
262
+ },
263
+ };
264
+
265
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
266
+ });
267
+
268
+ it('should initialize BrowserAgent with correct options - apac2', () => {
269
+ Object.defineProperty(window, 'location', {
270
+ value: { hostname: 'apac2.intouch.capillarytech.com' },
271
+ writable: true,
272
+ configurable: true,
273
+ });
274
+ setupNewrelicWebVitals();
275
+
276
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
277
+
278
+ const expectedOptions = {
279
+ info: {
280
+ applicationID: '718413155',
281
+ licenseKey: '082da40fff',
282
+ },
283
+ init: {
284
+ ajax: { enabled: true },
285
+ distributed_tracing: {
286
+ enabled: true,
287
+ exclude_newrelic_header: true,
288
+ },
289
+ generic_events: { enabled: true },
290
+ jserrors: { enabled: true },
291
+ logging: { enabled: false },
292
+ metrics: { enabled: true },
293
+ page_action: { enabled: true },
294
+ page_view_timing: { enabled: true },
295
+ performance: {
296
+ capture_detail: true,
297
+ },
298
+ privacy: { cookies_enabled: true },
299
+ session_replay: { enabled: false },
300
+ session_trace: { enabled: true },
301
+ soft_navigations: { enabled: true },
302
+ spa: { enabled: true },
303
+ user_actions: { enabled: true },
304
+ },
305
+ };
306
+
307
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
308
+ });
309
+
310
+ it('should initialize BrowserAgent with correct options - tata', () => {
311
+ Object.defineProperty(window, 'location', {
312
+ value: { hostname: 'tata.intouch.capillarytech.com' },
313
+ writable: true,
314
+ configurable: true,
315
+ });
316
+ setupNewrelicWebVitals();
317
+
318
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
319
+
320
+ const expectedOptions = {
321
+ info: {
322
+ applicationID: '718413165',
323
+ licenseKey: '082da40fff',
324
+ },
325
+ init: {
326
+ ajax: { enabled: true },
327
+ distributed_tracing: {
328
+ enabled: true,
329
+ exclude_newrelic_header: true,
330
+ },
331
+ generic_events: { enabled: true },
332
+ jserrors: { enabled: true },
333
+ logging: { enabled: false },
334
+ metrics: { enabled: true },
335
+ page_action: { enabled: true },
336
+ page_view_timing: { enabled: true },
337
+ performance: {
338
+ capture_detail: true,
339
+ },
340
+ privacy: { cookies_enabled: true },
341
+ session_replay: { enabled: false },
342
+ session_trace: { enabled: true },
343
+ soft_navigations: { enabled: true },
344
+ spa: { enabled: true },
345
+ user_actions: { enabled: true },
346
+ },
347
+ };
348
+
349
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
350
+ });
351
+
352
+ it('should initialize BrowserAgent with correct options - sea', () => {
353
+ Object.defineProperty(window, 'location', {
354
+ value: { hostname: 'sea.intouch.capillarytech.com' },
355
+ writable: true,
356
+ configurable: true,
357
+ });
358
+ setupNewrelicWebVitals();
359
+
360
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
361
+
362
+ const expectedOptions = {
363
+ info: {
364
+ applicationID: '718413175',
365
+ licenseKey: '082da40fff',
366
+ },
367
+ init: {
368
+ ajax: { enabled: true },
369
+ distributed_tracing: {
370
+ enabled: true,
371
+ exclude_newrelic_header: true,
372
+ },
373
+ generic_events: { enabled: true },
374
+ jserrors: { enabled: true },
375
+ logging: { enabled: false },
376
+ metrics: { enabled: true },
377
+ page_action: { enabled: true },
378
+ page_view_timing: { enabled: true },
379
+ performance: {
380
+ capture_detail: true,
381
+ },
382
+ privacy: { cookies_enabled: true },
383
+ session_replay: { enabled: false },
384
+ session_trace: { enabled: true },
385
+ soft_navigations: { enabled: true },
386
+ spa: { enabled: true },
387
+ user_actions: { enabled: true },
388
+ },
389
+ };
390
+
391
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
392
+ });
393
+
394
+ it('should initialize BrowserAgent with correct options - eu', () => {
395
+ Object.defineProperty(window, 'location', {
396
+ value: { hostname: 'eu.intouch.capillarytech.com' },
397
+ writable: true,
398
+ configurable: true,
399
+ });
400
+ setupNewrelicWebVitals();
401
+
402
+ expect(mockBrowserAgent).toHaveBeenCalledTimes(1);
403
+
404
+ const expectedOptions = {
405
+ info: {
406
+ applicationID: '718413185',
407
+ licenseKey: '082da40fff',
408
+ },
409
+ init: {
410
+ ajax: { enabled: true },
411
+ distributed_tracing: {
412
+ enabled: true,
413
+ exclude_newrelic_header: true,
414
+ },
415
+ generic_events: { enabled: true },
416
+ jserrors: { enabled: true },
417
+ logging: { enabled: false },
418
+ metrics: { enabled: true },
419
+ page_action: { enabled: true },
420
+ page_view_timing: { enabled: true },
421
+ performance: {
422
+ capture_detail: true,
423
+ },
424
+ privacy: { cookies_enabled: true },
425
+ session_replay: { enabled: false },
426
+ session_trace: { enabled: true },
427
+ soft_navigations: { enabled: true },
428
+ spa: { enabled: true },
429
+ user_actions: { enabled: true },
430
+ },
431
+ };
432
+
433
+ expect(mockBrowserAgent).toHaveBeenCalledWith(expectedOptions);
434
+ });
435
+
436
+ it('should not initialize BrowserAgent in development environment', () => {
437
+ process.env.NODE_ENV = 'development';
438
+
439
+ Object.defineProperty(window, 'location', {
440
+ value: { hostname: 'nightly.intouch.capillarytech.com' },
441
+ writable: true,
442
+ configurable: true,
443
+ });
444
+
445
+ setupNewrelicWebVitals();
446
+
447
+ expect(mockBrowserAgent).not.toHaveBeenCalled();
448
+ });
449
+ });
450
+
451
+ describe('setupNewrelicWebVitals with disabled config', () => {
452
+ beforeEach(() => {
453
+ jest.resetModules();
454
+ jest.doMock('../../../app-config', () => ({
455
+ newrelic: {
456
+ enabled: false,
457
+ licenseKey: '082da40fff',
458
+ environments: {
459
+ nightly: {
460
+ appId: '718411553',
461
+ },
462
+ },
463
+ },
464
+ }));
465
+ });
466
+
467
+ afterEach(() => {
468
+ jest.resetModules();
469
+ });
470
+
471
+ it('should not initialize BrowserAgent when New Relic is disabled in production', () => {
472
+ process.env.NODE_ENV = 'production';
473
+
474
+ Object.defineProperty(window, 'location', {
475
+ value: { hostname: 'nightly.intouch.capillarytech.com' },
476
+ writable: true,
477
+ configurable: true,
478
+ });
479
+
480
+ const { setupNewrelicWebVitals: setupWithDisabledConfig } = require('../commonUtils');
481
+ setupWithDisabledConfig();
482
+
483
+ expect(mockBrowserAgent).not.toHaveBeenCalled();
484
+ });
485
+
486
+ it('should not initialize BrowserAgent when New Relic is disabled in development', () => {
487
+ process.env.NODE_ENV = 'development';
488
+
489
+ Object.defineProperty(window, 'location', {
490
+ value: { hostname: 'nightly.intouch.capillarytech.com' },
491
+ writable: true,
492
+ configurable: true,
493
+ });
494
+
495
+ const { setupNewrelicWebVitals: setupWithDisabledConfig } = require('../commonUtils');
496
+ setupWithDisabledConfig();
497
+
498
+ expect(mockBrowserAgent).not.toHaveBeenCalled();
499
+ });
500
+ });
501
+
502
+ describe('setupNewrelicWebVitals with default config values', () => {
503
+ beforeEach(() => {
504
+ jest.resetModules();
505
+ jest.doMock('../../../app-config', () => ({
506
+ newrelic: {
507
+ licenseKey: '082da40fff',
508
+ },
509
+ }));
510
+ });
511
+
512
+ afterEach(() => {
513
+ jest.resetModules();
514
+ });
515
+
516
+ it('should not initialize BrowserAgent when enabled is missing (defaults to false)', () => {
517
+ process.env.NODE_ENV = 'production';
518
+
519
+ Object.defineProperty(window, 'location', {
520
+ value: { hostname: 'nightly.intouch.capillarytech.com' },
521
+ writable: true,
522
+ configurable: true,
523
+ });
524
+
525
+ const { setupNewrelicWebVitals: setupWithDefaults } = require('../commonUtils');
526
+ setupWithDefaults();
527
+
528
+ expect(mockBrowserAgent).not.toHaveBeenCalled();
529
+ });
530
+
531
+ it('should handle missing environments object (defaults to empty object)', () => {
532
+ process.env.NODE_ENV = 'production';
533
+
534
+ Object.defineProperty(window, 'location', {
535
+ value: { hostname: 'nightly.intouch.capillarytech.com' },
536
+ writable: true,
537
+ configurable: true,
538
+ });
539
+
540
+ const { setupNewrelicWebVitals: setupWithDefaults } = require('../commonUtils');
541
+
542
+ expect(() => setupWithDefaults()).not.toThrow();
543
+ expect(mockBrowserAgent).not.toHaveBeenCalled();
544
+ });
545
+ });
546
+ });
@@ -20,7 +20,7 @@ import globalMessages from '../../v2Containers/Cap/messages';
20
20
  import whatsappMsg from '../../v2Containers/Whatsapp/messages';
21
21
  import messages from './messages';
22
22
  import './index.scss';
23
- import { isUrl, isValidText } from '../../v2Containers/Line/Container/Wrapper/utils';
23
+ import { isUrl, isValidWhatsappCtaText } from '../../v2Containers/Line/Container/Wrapper/utils';
24
24
  import TagList from '../../v2Containers/TagList';
25
25
  import {
26
26
  CTA_OPTIONS,
@@ -144,7 +144,7 @@ export const CapWhatsappCTA = (props) => {
144
144
  const onButtonTextChange = ({ target }) => {
145
145
  const { value, id } = target;
146
146
  let errorMessage = '';
147
- if (!isValidText(value)) {
147
+ if (!isValidWhatsappCtaText(value)) {
148
148
  errorMessage = formatMessage(messages.ctaButtonErrorMessage);
149
149
  }
150
150
  setButtonError(errorMessage);
@@ -34,7 +34,7 @@ import {
34
34
  KARIX_GUPSHUP_URL_OPTIONS, TWILIO_URL_OPTIONS, DYNAMIC_URL, STATIC_URL,
35
35
  } from "../CapWhatsappCTA/constants";
36
36
  import globalMessages from '../../v2Containers/Cap/messages';
37
- import { isUrl, isValidText } from '../../v2Containers/Line/Container/Wrapper/utils';
37
+ import { isUrl, isValidWhatsappCtaText } from '../../v2Containers/Line/Container/Wrapper/utils';
38
38
  import { HOST_TWILIO } from "../../v2Containers/Whatsapp/constants";
39
39
  import "./index.scss";
40
40
 
@@ -119,7 +119,7 @@ export const CapWhatsappCarouselButton = (props) => {
119
119
 
120
120
  const onButtonTextChange = (value, buttonIndex) => {
121
121
  let errorMessage = '';
122
- if (!isValidText(value)) {
122
+ if (!isValidWhatsappCtaText(value)) {
123
123
  errorMessage = formatMessage(capWhatsappCTAMsg.ctaButtonErrorMessage);
124
124
  }
125
125
  onValueChange(buttonIndex, [{fieldName: 'text', value}, {fieldName: 'textError', value: errorMessage}]);
@@ -188,7 +188,7 @@ const useEmailWrapper = ({
188
188
  CapNotification.error(message);
189
189
  }
190
190
  }
191
- }, [isUploading, formatMessage, templatesActions, stopTimerGA, handleZipUploadError, showNextStep]);
191
+ }, [isUploading, formatMessage, stopTimerGA, handleZipUploadError, showNextStep]);
192
192
 
193
193
  const handleEdmDefaultTemplateSelection = useCallback((id) => {
194
194
  const data = find(CmsTemplates, { _id: id });
@@ -251,7 +251,7 @@ const useEmailWrapper = ({
251
251
  getCmsTemplatesInProgress,
252
252
  modeContent.id,
253
253
  SelectedEdmDefaultTemplate,
254
- templatesActions,
254
+ // templatesActions,
255
255
  handleEdmDefaultTemplateSelection
256
256
  ]);
257
257
 
@@ -5,7 +5,7 @@ import { initialReducer } from '../../../../../initialReducer';
5
5
  import { mountWithIntl } from '../../../../../helpers/intl-enzym-test-helpers';
6
6
  import { LineWrapper } from '../index';
7
7
  import { mockData } from './mockData';
8
- import {isValidText} from '../utils';
8
+ import {isValidText, isValidWhatsappCtaText} from '../utils';
9
9
  import history from '../../../../../utils/history';
10
10
 
11
11
  let store;
@@ -92,5 +92,60 @@ describe('line wrapper test/>', () => {
92
92
  expect(isValidText(emptyString)).toBe(true);
93
93
  });
94
94
  });
95
+
96
+ describe('isValidWhatsappCtaText', () => {
97
+ it('should return false for non-string input', () => {
98
+ expect(isValidWhatsappCtaText(123)).toBe(false);
99
+ expect(isValidWhatsappCtaText(null)).toBe(false);
100
+ expect(isValidWhatsappCtaText(undefined)).toBe(false);
101
+ expect(isValidWhatsappCtaText({})).toBe(false);
102
+ expect(isValidWhatsappCtaText([])).toBe(false);
103
+ expect(isValidWhatsappCtaText(true)).toBe(false);
104
+ });
105
+
106
+ it('should return true for multilingual and symbol text', () => {
107
+ const valid = '確認一下 Привет مرحبا मान्य 123 @#&!?.:, -+/\\\'"=&₹€£$^(){}';
108
+ expect(isValidWhatsappCtaText(valid)).toBe(true);
109
+ });
110
+
111
+ it('should return true for full allowed punctuation set', () => {
112
+ const allowed = 'A1 z9 @#&!?.:, -+/\\\'"=&₹€£$^(){}';
113
+ expect(isValidWhatsappCtaText(allowed)).toBe(true);
114
+ });
115
+
116
+ it('should return false for whitespace-only', () => {
117
+ expect(isValidWhatsappCtaText(' ')).toBe(false);
118
+ expect(isValidWhatsappCtaText('\t\t')).toBe(false);
119
+ });
120
+
121
+ it('should return true for leading and trailing spaces with valid text', () => {
122
+ expect(isValidWhatsappCtaText(' Hi')).toBe(true);
123
+ expect(isValidWhatsappCtaText('Hi ')).toBe(true);
124
+ });
125
+
126
+ it('should return false for control or markdown chars', () => {
127
+ expect(isValidWhatsappCtaText('Hello\nWorld')).toBe(false);
128
+ expect(isValidWhatsappCtaText('Hello\tWorld')).toBe(false);
129
+ expect(isValidWhatsappCtaText('*bold*')).toBe(false);
130
+ expect(isValidWhatsappCtaText('_under_')).toBe(false);
131
+ expect(isValidWhatsappCtaText('~strike~')).toBe(false);
132
+ expect(isValidWhatsappCtaText('`code`')).toBe(false);
133
+ });
134
+
135
+ it('should return false for emojis, flags, ZWJ sequences and VS16', () => {
136
+ expect(isValidWhatsappCtaText('Heart ❤️')).toBe(false);
137
+ expect(isValidWhatsappCtaText('Flag 🇮🇳')).toBe(false);
138
+ expect(isValidWhatsappCtaText('Family 👨‍👩‍👧‍👦')).toBe(false);
139
+ expect(isValidWhatsappCtaText('Sun ☀️')).toBe(false);
140
+ });
141
+
142
+ it('should return false for mixed valid text with emoji inside', () => {
143
+ expect(isValidWhatsappCtaText('Hello @# hi ❤️')).toBe(false);
144
+ });
145
+
146
+ it('should return true for backslash usage', () => {
147
+ expect(isValidWhatsappCtaText('C:\\Path')).toBe(true);
148
+ });
149
+ });
95
150
 
96
151
  });
@@ -78,3 +78,12 @@ export const isValidText = (text) => {
78
78
  return regex.test(text);
79
79
  };
80
80
 
81
+ // WhatsApp CTA button text validation - whitelist approach
82
+ // Allow: all letters and numbers (any language), regular space, and safe symbols
83
+ // Safe symbols: @ # & ! ? . : , - + / \ ' " = & ₹ € £ $ ^ ( ) { }
84
+ export const isValidWhatsappCtaText = (text) => {
85
+ if (typeof text !== 'string') return false;
86
+ if (!/\S/.test(text)) return false; // disallow whitespace-only
87
+ const regex = /^[\p{L}\p{M}\p{N} @#&!?.:,\-+/\\'"=&₹€£$^(){}]*$/u;
88
+ return regex.test(text);
89
+ };