@capillarytech/creatives-library 8.0.314 → 8.0.316-alpha.0
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/package.json +1 -1
- package/services/tests/getSchema.test.js +95 -0
- package/utils/tests/common.test.js +265 -323
- package/utils/tests/commonUtils.test.js +581 -0
- package/utils/tests/messageUtils.test.js +95 -0
- package/utils/tests/smsCharCount.test.js +304 -0
- package/utils/tests/smsCharCountV2.test.js +213 -10
- package/v2Containers/InApp/index.js +26 -4
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test suite for legacy smsCharCount utility (app/utils/smsCharCount.js)
|
|
3
|
+
* Note: The actual smsCharCount.js file is referenced from this module path
|
|
4
|
+
*/
|
|
5
|
+
import { updateCharCount, checkUnicode } from '../smsCharCount';
|
|
6
|
+
|
|
7
|
+
describe('SMS Character Count - Utility Functions', () => {
|
|
8
|
+
describe('updateCharCount function', () => {
|
|
9
|
+
it('should return object with totalLength, msgCount, charLeft properties', () => {
|
|
10
|
+
const result = updateCharCount('Hello');
|
|
11
|
+
expect(result).toHaveProperty('totalLength');
|
|
12
|
+
expect(result).toHaveProperty('msgCount');
|
|
13
|
+
expect(result).toHaveProperty('charLeft');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should handle simple ASCII message', () => {
|
|
17
|
+
const result = updateCharCount('Hello World');
|
|
18
|
+
expect(typeof result.msgCount).toBe('number');
|
|
19
|
+
expect(result.msgCount).toBe(1);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should count exactly 160 7-bit characters as single SMS', () => {
|
|
23
|
+
const msg160 = 'A'.repeat(160);
|
|
24
|
+
const result = updateCharCount(msg160);
|
|
25
|
+
expect(result.msgCount).toBe(1);
|
|
26
|
+
expect(result.charLeft).toBe(0);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should split 161 7-bit characters into 2 SMS', () => {
|
|
30
|
+
const msg161 = 'A'.repeat(161);
|
|
31
|
+
const result = updateCharCount(msg161);
|
|
32
|
+
expect(result.msgCount).toBe(2);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should count 153 7-bit characters per SMS after first', () => {
|
|
36
|
+
const msg = 'A'.repeat(313); // 160 + 153
|
|
37
|
+
const result = updateCharCount(msg);
|
|
38
|
+
// Math.ceil(313 / 153) calculates multipart for total length
|
|
39
|
+
expect(result.msgCount).toBe(Math.ceil(313 / 153));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should split 154 7-bit characters into 2 SMS (160 + 153 boundary)', () => {
|
|
43
|
+
const msg = 'A'.repeat(314); // 160 + 154
|
|
44
|
+
const result = updateCharCount(msg);
|
|
45
|
+
expect(result.msgCount).toBe(3);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should handle Unicode characters correctly', () => {
|
|
49
|
+
const msg = '你好';
|
|
50
|
+
const result = updateCharCount(msg);
|
|
51
|
+
expect(result.msgCount).toBe(1);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should count exactly 70 Unicode characters as single SMS', () => {
|
|
55
|
+
const msg70Unicode = '你'.repeat(70);
|
|
56
|
+
const result = updateCharCount(msg70Unicode);
|
|
57
|
+
expect(result.msgCount).toBe(1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should split 71 Unicode characters into 2 SMS', () => {
|
|
61
|
+
const msg71Unicode = '你'.repeat(71);
|
|
62
|
+
const result = updateCharCount(msg71Unicode);
|
|
63
|
+
expect(result.msgCount).toBe(2);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should count 67 Unicode characters per SMS after first', () => {
|
|
67
|
+
const msg = '你'.repeat(137); // 70 + 67
|
|
68
|
+
const result = updateCharCount(msg);
|
|
69
|
+
// Math.ceil(137 / 67) calculates multipart for total length
|
|
70
|
+
expect(result.msgCount).toBe(Math.ceil(137 / 67));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle 2-byte GSM-7 extended characters', () => {
|
|
74
|
+
// Characters like \f, ^, {, }, \, [, ~, ], |, € count as 2 bytes
|
|
75
|
+
const msg = 'A\fB'; // \f is 2-byte
|
|
76
|
+
const result = updateCharCount(msg);
|
|
77
|
+
expect(result).toHaveProperty('msgCount');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should handle mixed 7-bit and extended GSM-7 characters', () => {
|
|
81
|
+
const msg = 'Hello^World{Test}';
|
|
82
|
+
const result = updateCharCount(msg);
|
|
83
|
+
expect(result.msgCount).toBe(1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should handle form feed characters', () => {
|
|
87
|
+
const msg = 'Hello\fWorld';
|
|
88
|
+
const result = updateCharCount(msg);
|
|
89
|
+
expect(result).toHaveProperty('msgCount');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should handle newline characters', () => {
|
|
93
|
+
const msg = 'Hello\nWorld';
|
|
94
|
+
const result = updateCharCount(msg);
|
|
95
|
+
expect(result).toHaveProperty('msgCount');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should handle HTML entities like &', () => {
|
|
99
|
+
const msg = 'Hello & World';
|
|
100
|
+
const result = updateCharCount(msg);
|
|
101
|
+
expect(result).toHaveProperty('msgCount');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should handle non-breaking space character', () => {
|
|
105
|
+
const msg = 'Hello\u00a0World';
|
|
106
|
+
const result = updateCharCount(msg);
|
|
107
|
+
expect(result).toHaveProperty('msgCount');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should handle carriage return characters', () => {
|
|
111
|
+
const msg = 'Hello\r\nWorld';
|
|
112
|
+
const result = updateCharCount(msg);
|
|
113
|
+
expect(result).toHaveProperty('msgCount');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should handle empty or minimal content', () => {
|
|
117
|
+
const result = updateCharCount('A');
|
|
118
|
+
expect(result.msgCount).toBe(1);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should handle very long messages', () => {
|
|
122
|
+
const msg = 'A'.repeat(1000);
|
|
123
|
+
const result = updateCharCount(msg);
|
|
124
|
+
expect(result.msgCount).toBe(Math.ceil(1000 / 153));
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should handle messages with special GSM-7 characters', () => {
|
|
128
|
+
const specialChars = '@£$¥èéùìòÇØø';
|
|
129
|
+
const result = updateCharCount(specialChars);
|
|
130
|
+
expect(result).toHaveProperty('msgCount');
|
|
131
|
+
expect(result.msgCount).toBe(1);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should handle euro symbol', () => {
|
|
135
|
+
const msg = '€'.repeat(10);
|
|
136
|
+
const result = updateCharCount(msg);
|
|
137
|
+
expect(result).toHaveProperty('msgCount');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should return props array with totalLength, msgcount, charLeft', () => {
|
|
141
|
+
const result = updateCharCount('Test');
|
|
142
|
+
// Result should contain labels and counts
|
|
143
|
+
expect(Array.isArray(result)).toBe(false);
|
|
144
|
+
expect(result).toBeInstanceOf(Object);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should correctly calculate characters left in current SMS', () => {
|
|
148
|
+
const msg = 'A'.repeat(100);
|
|
149
|
+
const result = updateCharCount(msg);
|
|
150
|
+
expect(result.charLeft).toBe(160 - 100);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should handle mixed ASCII and special characters', () => {
|
|
154
|
+
const msg = 'Hello[{World}]€Test';
|
|
155
|
+
const result = updateCharCount(msg);
|
|
156
|
+
expect(result.msgCount).toBeGreaterThanOrEqual(1);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('checkUnicode function', () => {
|
|
161
|
+
it('should return false for ASCII-only text', () => {
|
|
162
|
+
expect(checkUnicode('Hello World 123')).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should return true for Unicode characters', () => {
|
|
166
|
+
expect(checkUnicode('你好')).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should return true for mixed ASCII and Unicode', () => {
|
|
170
|
+
expect(checkUnicode('Hello你好')).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should return false for supported GSM-7 extended characters', () => {
|
|
174
|
+
expect(checkUnicode('@£$¥èéù')).toBe(false);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should return false for empty string', () => {
|
|
178
|
+
expect(checkUnicode('')).toBe(false);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should return false for GSM-7 extended escape characters', () => {
|
|
182
|
+
expect(checkUnicode('\f^{}[]~|€')).toBe(false);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should return true for Japanese characters', () => {
|
|
186
|
+
expect(checkUnicode('こんにちは')).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should return true for Korean characters', () => {
|
|
190
|
+
expect(checkUnicode('안녕하세요')).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should return true for Cyrillic characters', () => {
|
|
194
|
+
expect(checkUnicode('Привет')).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should return false for all supported extended ASCII', () => {
|
|
198
|
+
const supported = 'èéùìòÇØøÅåΔΦΓΛΩΠΨΣΘΞÆæßÉÄÖÑÜäöñüࣥ¤§¿€';
|
|
199
|
+
expect(checkUnicode(supported)).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should return true for emoji', () => {
|
|
203
|
+
expect(checkUnicode('Hello 🌍')).toBe(true);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should return false for numeric characters', () => {
|
|
207
|
+
expect(checkUnicode('0123456789')).toBe(false);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should return false for special ASCII characters', () => {
|
|
211
|
+
expect(checkUnicode('!@#$%^&*()_+-=[]{}|;:,.<>?')).toBe(false);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should handle newline in check', () => {
|
|
215
|
+
expect(checkUnicode('Hello\nWorld')).toBe(false);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should handle carriage return in check', () => {
|
|
219
|
+
expect(checkUnicode('Hello\r\nWorld')).toBe(false);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should handle form feed in check', () => {
|
|
223
|
+
expect(checkUnicode('Hello\fWorld')).toBe(false);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should handle non-breaking space in check', () => {
|
|
227
|
+
expect(checkUnicode('Hello\u00a0World')).toBe(false);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should return true when any non-GSM7 character is present', () => {
|
|
231
|
+
expect(checkUnicode('ABCDEFGHIJKLMNOPQRSTUVWXYZ你')).toBe(true);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should handle mixed supported and unsupported Unicode', () => {
|
|
235
|
+
expect(checkUnicode('è 你好 €')).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should return false for message with only spaces', () => {
|
|
239
|
+
expect(checkUnicode(' ')).toBe(false);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should return true for message with tabs (tabs are not in GSM-7 charset)', () => {
|
|
243
|
+
// Tab character (\t) is not in the GSM-7 character set, so it returns true
|
|
244
|
+
expect(checkUnicode('Hello\t\tWorld')).toBe(true);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe('Integration Tests', () => {
|
|
249
|
+
it('should correctly identify and count Unicode message', () => {
|
|
250
|
+
const msg = 'Hello你好World';
|
|
251
|
+
const isUnicode = checkUnicode(msg);
|
|
252
|
+
const counts = updateCharCount(msg);
|
|
253
|
+
|
|
254
|
+
expect(isUnicode).toBe(true);
|
|
255
|
+
if (msg.length > 70) {
|
|
256
|
+
expect(counts.msgCount).toBeGreaterThan(1);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('should correctly identify and count ASCII message', () => {
|
|
261
|
+
const msg = 'Hello World This is a test message';
|
|
262
|
+
const isUnicode = checkUnicode(msg);
|
|
263
|
+
const counts = updateCharCount(msg);
|
|
264
|
+
|
|
265
|
+
expect(isUnicode).toBe(false);
|
|
266
|
+
if (msg.length > 160) {
|
|
267
|
+
expect(counts.msgCount).toBeGreaterThan(1);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should handle long Unicode messages correctly', () => {
|
|
272
|
+
const msg = '你好'.repeat(50); // 100 characters
|
|
273
|
+
const isUnicode = checkUnicode(msg);
|
|
274
|
+
const counts = updateCharCount(msg);
|
|
275
|
+
|
|
276
|
+
expect(isUnicode).toBe(true);
|
|
277
|
+
expect(counts.msgCount).toBeGreaterThan(1);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should handle long ASCII messages correctly', () => {
|
|
281
|
+
const msg = 'A'.repeat(300);
|
|
282
|
+
const isUnicode = checkUnicode(msg);
|
|
283
|
+
const counts = updateCharCount(msg);
|
|
284
|
+
|
|
285
|
+
expect(isUnicode).toBe(false);
|
|
286
|
+
expect(counts.msgCount).toBeGreaterThan(1);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should handle boundary case: exactly 160 ASCII characters', () => {
|
|
290
|
+
const msg = 'A'.repeat(160);
|
|
291
|
+
const counts = updateCharCount(msg);
|
|
292
|
+
|
|
293
|
+
expect(counts.msgCount).toBe(1);
|
|
294
|
+
expect(counts.charLeft).toBe(0);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should handle boundary case: exactly 70 Unicode characters', () => {
|
|
298
|
+
const msg = '你'.repeat(70);
|
|
299
|
+
const counts = updateCharCount(msg);
|
|
300
|
+
|
|
301
|
+
expect(counts.msgCount).toBe(1);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -1,12 +1,215 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { updateCharCount } from '../smsCharCountV2';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
import { updateCharCount, checkUnicode } from '../smsCharCountV2';
|
|
3
|
+
import * as Api from '../../services/api';
|
|
4
|
+
|
|
5
|
+
jest.mock('../../services/api');
|
|
6
|
+
|
|
7
|
+
describe('smsCharCountV2', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
jest.clearAllMocks();
|
|
10
|
+
// Setup default mock to return resolved promise
|
|
11
|
+
Api.getUnsubscribeUrl.mockResolvedValue({
|
|
12
|
+
response: { response: '' },
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('checkUnicode', () => {
|
|
17
|
+
it('should return false for 7-bit ASCII characters', () => {
|
|
18
|
+
const result = checkUnicode('Hello');
|
|
19
|
+
expect(result).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should return true for Unicode characters', () => {
|
|
23
|
+
const result = checkUnicode('你好');
|
|
24
|
+
expect(result).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should return false for empty string', () => {
|
|
28
|
+
const result = checkUnicode('');
|
|
29
|
+
expect(result).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should return false for 7-bit extended characters', () => {
|
|
33
|
+
const result = checkUnicode('\f^{}');
|
|
34
|
+
expect(result).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should return true when mixed with Unicode', () => {
|
|
38
|
+
const result = checkUnicode('Hello世界');
|
|
39
|
+
expect(result).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should return false for special 7-bit GSM characters', () => {
|
|
43
|
+
const result = checkUnicode('@£$¥');
|
|
44
|
+
expect(result).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should return false for escape sequences', () => {
|
|
48
|
+
const result = checkUnicode('\\[~]|€');
|
|
49
|
+
expect(result).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('updateCharCount', () => {
|
|
54
|
+
it('should return basic SMS details for simple 7-bit message', () => {
|
|
55
|
+
const result = updateCharCount('Hello');
|
|
56
|
+
expect(result).toHaveProperty('unicode', false);
|
|
57
|
+
expect(result).toHaveProperty('parts', 1);
|
|
58
|
+
expect(result).toHaveProperty('chars_sms', 160);
|
|
59
|
+
expect(result).toHaveProperty('optoutUrlPresent', false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should return SMS details for simple Unicode message', () => {
|
|
63
|
+
const result = updateCharCount('你好');
|
|
64
|
+
expect(result.unicode).toBe(true);
|
|
65
|
+
expect(result.parts).toBe(1);
|
|
66
|
+
expect(result.chars_sms).toBe(70);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should detect 7-bit messages up to 160 characters', () => {
|
|
70
|
+
const msg = 'A'.repeat(160);
|
|
71
|
+
const result = updateCharCount(msg);
|
|
72
|
+
expect(result.unicode).toBe(false);
|
|
73
|
+
expect(result.parts).toBe(1);
|
|
74
|
+
expect(result.chars_sms).toBe(160);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should calculate multipart for 7-bit messages over 160 chars', () => {
|
|
78
|
+
const msg = 'A'.repeat(161);
|
|
79
|
+
const result = updateCharCount(msg);
|
|
80
|
+
expect(result.unicode).toBe(false);
|
|
81
|
+
expect(result.parts).toBe(2);
|
|
82
|
+
expect(result.chars_sms).toBe(153);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should calculate correct parts for large 7-bit messages', () => {
|
|
86
|
+
const msg = 'A'.repeat(500);
|
|
87
|
+
const result = updateCharCount(msg);
|
|
88
|
+
expect(result.unicode).toBe(false);
|
|
89
|
+
expect(result.parts).toBe(Math.ceil(500 / 153));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should detect Unicode messages up to 70 characters', () => {
|
|
93
|
+
const msg = '你'.repeat(70);
|
|
94
|
+
const result = updateCharCount(msg);
|
|
95
|
+
expect(result.unicode).toBe(true);
|
|
96
|
+
expect(result.parts).toBe(1);
|
|
97
|
+
expect(result.chars_sms).toBe(70);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should calculate multipart for Unicode messages over 70 chars', () => {
|
|
101
|
+
const msg = '你'.repeat(71);
|
|
102
|
+
const result = updateCharCount(msg);
|
|
103
|
+
expect(result.unicode).toBe(true);
|
|
104
|
+
expect(result.parts).toBe(2);
|
|
105
|
+
expect(result.chars_sms).toBe(67);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should handle message with optout tag', () => {
|
|
109
|
+
const result = updateCharCount('Hello{{optout}}');
|
|
110
|
+
expect(result.optoutUrlPresent).toBe(true);
|
|
111
|
+
expect(result.parts).toBe(1);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should handle message without optout tag', () => {
|
|
115
|
+
const res = updateCharCount('Hello');
|
|
116
|
+
expect(res.optoutUrlPresent).toBe(false);
|
|
117
|
+
expect(res.parts).toBe(1);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should handle multiple optout tags', () => {
|
|
121
|
+
const result = updateCharCount('Hello{{optout}}World{{optout}}');
|
|
122
|
+
expect(result.optoutUrlPresent).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should handle default empty string parameter', () => {
|
|
126
|
+
const result = updateCharCount();
|
|
127
|
+
expect(result.chars_used).toBe(0);
|
|
128
|
+
expect(result.parts).toBe(1);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should count 7-bit extended characters as 2 bytes', () => {
|
|
132
|
+
const result = updateCharCount('A\fB'); // \f is 2-byte GSM-7 character
|
|
133
|
+
expect(result.unicode).toBe(false);
|
|
134
|
+
expect(result.char_list).toBeDefined();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should handle isUnicodeEnabled parameter', () => {
|
|
138
|
+
const result = updateCharCount('Hello', true);
|
|
139
|
+
expect(result).toBeDefined();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should handle isUnicodeEnabled as false', () => {
|
|
143
|
+
const result = updateCharCount('Hello', false);
|
|
144
|
+
expect(result).toBeDefined();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should return char_list with React elements', () => {
|
|
148
|
+
const result = updateCharCount('Hi');
|
|
149
|
+
expect(Array.isArray(result.char_list)).toBe(true);
|
|
150
|
+
expect(result.char_list.length).toBe(2);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should include responseState in return object', () => {
|
|
154
|
+
const result = updateCharCount('Test');
|
|
155
|
+
expect(result).toHaveProperty('responseState');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should handle optout URL when available from API', () => {
|
|
159
|
+
Api.getUnsubscribeUrl.mockResolvedValueOnce({
|
|
160
|
+
response: { response: 'https://example.com/unsub' },
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const result = updateCharCount('Hello{{optout}}');
|
|
164
|
+
expect(result.optoutUrlPresent).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should handle API error gracefully', () => {
|
|
168
|
+
Api.getUnsubscribeUrl.mockRejectedValueOnce(new Error('API failed'));
|
|
169
|
+
|
|
170
|
+
const result = updateCharCount('Hello{{optout}}');
|
|
171
|
+
expect(result).toBeDefined();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should calculate correct char count with 7-bit extended chars', () => {
|
|
175
|
+
const result = updateCharCount('Test[Message]');
|
|
176
|
+
expect(result.unicode).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should handle all GSM-7 alphabet characters', () => {
|
|
180
|
+
const gsm7Chars = '@£$¥èéùìòÇ';
|
|
181
|
+
const result = checkUnicode(gsm7Chars);
|
|
182
|
+
expect(result).toBe(false);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should handle whitespace and newlines', () => {
|
|
186
|
+
const result = updateCharCount('Line1\nLine2');
|
|
187
|
+
expect(result.unicode).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should maintain char_list integrity for multipart messages', () => {
|
|
191
|
+
const msg = 'A'.repeat(200);
|
|
192
|
+
const result = updateCharCount(msg);
|
|
193
|
+
expect(result.char_list.length).toBe(200);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should handle message with only special characters', () => {
|
|
197
|
+
const result = updateCharCount('!@#$%^&*()');
|
|
198
|
+
expect(result.unicode).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should calculate correct part boundaries for 7-bit', () => {
|
|
202
|
+
const msg = 'A'.repeat(153);
|
|
203
|
+
const result = updateCharCount(msg);
|
|
204
|
+
expect(result.parts).toBe(1);
|
|
205
|
+
expect(result.chars_sms).toBe(160);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should calculate correct part boundaries for Unicode', () => {
|
|
209
|
+
const msg = '你'.repeat(67);
|
|
210
|
+
const result = updateCharCount(msg);
|
|
211
|
+
expect(result.parts).toBe(1);
|
|
212
|
+
expect(result.chars_sms).toBe(70);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
12
215
|
});
|
|
@@ -68,6 +68,7 @@ import { HTML_EDITOR_VARIANTS } from "../../v2Components/HtmlEditor/constants";
|
|
|
68
68
|
import { INAPP_EDITOR_TYPES } from "../InAppWrapper/constants";
|
|
69
69
|
import InappAdvanced from "../InappAdvance/index";
|
|
70
70
|
import CapDeviceContent from "../../v2Components/CapDeviceContent";
|
|
71
|
+
import capDeviceContentMessages from "../../v2Components/CapDeviceContent/messages";
|
|
71
72
|
import { ErrorInfoNote } from "../../v2Components/ErrorInfoNote";
|
|
72
73
|
|
|
73
74
|
let editContent = {};
|
|
@@ -1277,16 +1278,29 @@ export const InApp = (props) => {
|
|
|
1277
1278
|
return '';
|
|
1278
1279
|
};
|
|
1279
1280
|
|
|
1281
|
+
/** Same rules as CapDeviceContent onTitleChange / onTemplateMessageChange */
|
|
1282
|
+
const computeInAppTemplateFieldError = (value) => {
|
|
1283
|
+
let error = templateDescErrorHandler(value);
|
|
1284
|
+
if (value === '') {
|
|
1285
|
+
error = formatMessage(capDeviceContentMessages.emptyTemplateMessageErrorMessage);
|
|
1286
|
+
}
|
|
1287
|
+
return error;
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1280
1290
|
const onCopyTitleAndContent = () => {
|
|
1281
1291
|
if (panes === ANDROID) {
|
|
1282
1292
|
clearDeviceErrors(ANDROID);
|
|
1283
1293
|
setTitleAndroid(titleIos);
|
|
1284
1294
|
setTemplateMessageAndroid(templateMessageIos);
|
|
1295
|
+
setTemplateTitleErrorAndroid(computeInAppTemplateFieldError(titleIos));
|
|
1296
|
+
setTemplateMessageErrorAndroid(computeInAppTemplateFieldError(templateMessageIos));
|
|
1285
1297
|
setInAppImageSrcAndroid(inAppImageSrcIos);
|
|
1286
1298
|
} else {
|
|
1287
1299
|
clearDeviceErrors(IOS);
|
|
1288
1300
|
setTitleIos(titleAndroid);
|
|
1289
1301
|
setTemplateMessageIos(templateMessageAndroid);
|
|
1302
|
+
setTemplateTitleErrorIos(computeInAppTemplateFieldError(titleAndroid));
|
|
1303
|
+
setTemplateMessageErrorIos(computeInAppTemplateFieldError(templateMessageAndroid));
|
|
1290
1304
|
setInAppImageSrcIos(inAppImageSrcAndroid);
|
|
1291
1305
|
}
|
|
1292
1306
|
};
|
|
@@ -1295,12 +1309,20 @@ export const InApp = (props) => {
|
|
|
1295
1309
|
const tag = `{{${value}}}`;
|
|
1296
1310
|
if (panes === ANDROID) {
|
|
1297
1311
|
clearDeviceErrors(ANDROID);
|
|
1298
|
-
|
|
1299
|
-
|
|
1312
|
+
const nextTitle = index === 0 ? titleAndroid + tag : titleAndroid;
|
|
1313
|
+
const nextMessage = index === 0 ? templateMessageAndroid : templateMessageAndroid + tag;
|
|
1314
|
+
if (index === 0) setTitleAndroid(nextTitle);
|
|
1315
|
+
else setTemplateMessageAndroid(nextMessage);
|
|
1316
|
+
setTemplateTitleErrorAndroid(computeInAppTemplateFieldError(nextTitle));
|
|
1317
|
+
setTemplateMessageErrorAndroid(computeInAppTemplateFieldError(nextMessage));
|
|
1300
1318
|
} else {
|
|
1301
1319
|
clearDeviceErrors(IOS);
|
|
1302
|
-
|
|
1303
|
-
|
|
1320
|
+
const nextTitle = index === 0 ? titleIos + tag : titleIos;
|
|
1321
|
+
const nextMessage = index === 0 ? templateMessageIos : templateMessageIos + tag;
|
|
1322
|
+
if (index === 0) setTitleIos(nextTitle);
|
|
1323
|
+
else setTemplateMessageIos(nextMessage);
|
|
1324
|
+
setTemplateTitleErrorIos(computeInAppTemplateFieldError(nextTitle));
|
|
1325
|
+
setTemplateMessageErrorIos(computeInAppTemplateFieldError(nextMessage));
|
|
1304
1326
|
}
|
|
1305
1327
|
};
|
|
1306
1328
|
|