1
- import { createActionAllowList , getMatchRegex , processRawAllowList , maskActionName } from './allowedDictionary'
1
+ import { ACTION_NAME_PLACEHOLDER } from '../getActionNameFromElement'
2
+ import {
3
+ createActionAllowList ,
4
+ processRawAllowList ,
5
+ maskActionName ,
6
+ tokenize ,
7
+ isBrowserSupported ,
8
+ } from './allowedDictionary'
2
9
import type { AllowedDictionary } from './allowedDictionary'
3
10
4
11
const TEST_STRINGS = {
5
12
COMPLEX_MIXED : 'test-user-name:💥$$$, test-user-id:hello>=42@world?' ,
6
13
PARAGRAPH_MIXED : 'This is a test paragraph with various symbols: 💥, $$$, 123, and more.' ,
7
14
}
8
15
16
+ const LANGUAGES_TEST_STRINGS = {
17
+ FRENCH_MIXED_SENTENCE : "C'est un test avec des mots français et des symboles: 💥, $$$, 123, et plus. Bonjour!" ,
18
+ SPANISH_MIXED_SENTENCE : 'Este es un test con palabras en español y símbolos: 💥, $$$, 123, y más. ¡Hola!' ,
19
+ GERMAN_MIXED_SENTENCE : 'Das ist ein Test mit deutschen Wörtern und Symbolen: 💥, $$$, 123, und mehr. Hallo!' ,
20
+ ITALIAN_MIXED_SENTENCE : 'Questo è un test con parole in italiano e simboli: 💥, $$$, 123, e altro. Ciao!' ,
21
+ PORTUGUESE_MIXED_SENTENCE : 'Este é um teste com palavras em português e símbolos: 💥, $$$, 123, e mais. Olá!' ,
22
+ }
23
+ if ( isBrowserSupported ( ) ) {
24
+ describe ( 'Test tokenize' , ( ) => {
25
+ it ( 'should handle emojis when Browser supports unicode regex' , ( ) => {
26
+ const paragraphMixedTokens = tokenize ( TEST_STRINGS . PARAGRAPH_MIXED )
27
+ expect ( paragraphMixedTokens ) . toContain ( '💥' )
28
+ expect ( paragraphMixedTokens ) . not . toContain ( '$$$' )
29
+ expect ( paragraphMixedTokens ) . not . toContain ( '123' )
30
+ } )
31
+
32
+ /**
33
+ * This test is to ensure that the match regex is working as expected in all browsers.
34
+ * With unicode regex, we can support symbols and emojis OOTB.
35
+ * But in older versions of browsers, we need to use a minimal fallback regex which does
36
+ * not support many symbols, to avoid bloating the bundle size.
37
+ *
38
+ * Only European languages (Except Russian) are tested here.
39
+ * We can't test Russian because it's not supported by the fallback regex.
40
+ * Asian languages are not supported by our current tokenizer strategy.
41
+ */
42
+ it ( 'Tokenized results matches words and symbols in TEST_STRINGS' , ( ) => {
43
+ const paragraphMixedTokens = tokenize ( TEST_STRINGS . PARAGRAPH_MIXED )
44
+ const expectedParagraphMixed = [ 'This' , 'is' , 'a' , 'test' , 'paragraph' , 'with' , 'various' , 'symbols' , 'and' , 'more' ]
45
+ expectedParagraphMixed . forEach ( ( expected ) => {
46
+ expect ( paragraphMixedTokens ) . toContain ( expected )
47
+ } )
48
+ const frenchTokens = tokenize ( LANGUAGES_TEST_STRINGS . FRENCH_MIXED_SENTENCE )
49
+ const expectedFrench = [
50
+ 'C' ,
51
+ 'est' ,
52
+ 'un' ,
53
+ 'test' ,
54
+ 'avec' ,
55
+ 'des' ,
56
+ 'mots' ,
57
+ 'français' ,
58
+ 'et' ,
59
+ 'des' ,
60
+ 'symboles' ,
61
+ 'et' ,
62
+ 'plus' ,
63
+ 'Bonjour' ,
64
+ ]
65
+ expectedFrench . forEach ( ( expected ) => {
66
+ expect ( frenchTokens ) . toContain ( expected )
67
+ } )
68
+
69
+ const spanishTokens = tokenize ( LANGUAGES_TEST_STRINGS . SPANISH_MIXED_SENTENCE )
70
+ const expectedSpanish = [
71
+ 'Este' ,
72
+ 'es' ,
73
+ 'un' ,
74
+ 'test' ,
75
+ 'con' ,
76
+ 'palabras' ,
77
+ 'en' ,
78
+ 'español' ,
79
+ 'y' ,
80
+ 'símbolos' ,
81
+ 'y' ,
82
+ 'más' ,
83
+ 'Hola' ,
84
+ ]
85
+ expectedSpanish . forEach ( ( expected ) => {
86
+ expect ( spanishTokens ) . toContain ( expected )
87
+ } )
88
+
89
+ const germanTokens = tokenize ( LANGUAGES_TEST_STRINGS . GERMAN_MIXED_SENTENCE )
90
+ const expectedGerman = [
91
+ 'Das' ,
92
+ 'ist' ,
93
+ 'ein' ,
94
+ 'Test' ,
95
+ 'mit' ,
96
+ 'deutschen' ,
97
+ 'Wörtern' ,
98
+ 'und' ,
99
+ 'Symbolen' ,
100
+ 'und' ,
101
+ 'mehr' ,
102
+ 'Hallo' ,
103
+ ]
104
+ expectedGerman . forEach ( ( expected ) => {
105
+ expect ( germanTokens ) . toContain ( expected )
106
+ } )
107
+
108
+ const portugueseTokens = tokenize ( LANGUAGES_TEST_STRINGS . PORTUGUESE_MIXED_SENTENCE )
109
+ const expectedPortuguese = [
110
+ 'Este' ,
111
+ 'é' ,
112
+ 'um' ,
113
+ 'teste' ,
114
+ 'com' ,
115
+ 'palavras' ,
116
+ 'em' ,
117
+ 'português' ,
118
+ 'e' ,
119
+ 'símbolos' ,
120
+ 'e' ,
121
+ 'mais' ,
122
+ 'Olá' ,
123
+ ]
124
+ expectedPortuguese . forEach ( ( expected ) => {
125
+ expect ( portugueseTokens ) . toContain ( expected )
126
+ } )
127
+ } )
128
+ } )
129
+ }
130
+
9
131
describe ( 'createActionAllowList' , ( ) => {
10
132
beforeAll ( ( ) => {
11
133
window . $DD_ALLOW = new Set ( [ TEST_STRINGS . COMPLEX_MIXED , TEST_STRINGS . PARAGRAPH_MIXED ] )
@@ -15,10 +137,18 @@ describe('createActionAllowList', () => {
15
137
window . $DD_ALLOW = undefined
16
138
} )
17
139
18
- it ( 'should create an action name dictionary' , ( ) => {
140
+ it ( 'should create an action name dictionary and clear it ' , ( ) => {
19
141
const actionNameDictionary = createActionAllowList ( )
20
- expect ( actionNameDictionary . allowlist . size ) . toBe ( 20 )
142
+ if ( ! isBrowserSupported ( ) ) {
143
+ expect ( actionNameDictionary . allowlist . size ) . toBe ( 0 )
144
+ expect ( actionNameDictionary . rawStringIterator ) . toBeDefined ( )
145
+ return
146
+ }
147
+ expect ( actionNameDictionary . allowlist . size ) . toBeGreaterThan ( 0 )
21
148
expect ( actionNameDictionary . rawStringIterator ) . toBeDefined ( )
149
+ actionNameDictionary . clear ( )
150
+ expect ( actionNameDictionary . allowlist . size ) . toBe ( 0 )
151
+ expect ( actionNameDictionary . rawStringIterator ) . toBeUndefined ( )
22
152
} )
23
153
24
154
it ( 'should handle when $DD_ALLOW is undefined and redefined later' , ( ) => {
@@ -30,76 +160,52 @@ describe('createActionAllowList', () => {
30
160
// Trigger the observer manually
31
161
window . $DD_ALLOW_OBSERVERS ?. forEach ( ( observer ) => observer ( ) )
32
162
expect ( actionNameDictionary . rawStringIterator ) . toBeDefined ( )
163
+ actionNameDictionary . clear ( )
33
164
} )
34
165
} )
35
166
36
- describe ( 'actionNameDictionary processing' , ( ) => {
37
- let actionNameDictionary : AllowedDictionary
38
- let clearActionNameDictionary : ( ) => void
39
-
40
- beforeEach ( ( ) => {
41
- window . $DD_ALLOW = new Set ( [ TEST_STRINGS . COMPLEX_MIXED , TEST_STRINGS . PARAGRAPH_MIXED ] )
42
- actionNameDictionary = createActionAllowList ( )
43
- clearActionNameDictionary = actionNameDictionary . clear
44
- } )
45
-
46
- afterEach ( ( ) => {
47
- window . $DD_ALLOW = undefined
48
- clearActionNameDictionary ( )
49
- } )
50
-
51
- it ( 'MATCH_REGEX matches words and symbols in TEST_STRINGS' , ( ) => {
52
- expect ( TEST_STRINGS . COMPLEX_MIXED . match ( getMatchRegex ( ) ) ) . toEqual (
53
- jasmine . arrayContaining ( [ 'test' , 'user' , 'name' , '💥$$$' , 'test' , 'user' , 'id' , 'hello' , '>=42' , 'world' ] )
54
- )
55
- expect ( TEST_STRINGS . PARAGRAPH_MIXED . match ( getMatchRegex ( ) ) ) . toEqual (
56
- jasmine . arrayContaining ( [
57
- 'This' ,
58
- 'is' ,
59
- 'a' ,
60
- 'test' ,
61
- 'paragraph' ,
62
- 'with' ,
63
- 'various' ,
64
- 'symbols' ,
65
- '💥' ,
66
- '$$$' ,
67
- '123' ,
68
- 'and' ,
69
- 'more' ,
70
- ] )
71
- )
72
- } )
73
-
74
- it ( 'initializes allowlist with normalized words from $DD_ALLOW' , ( ) => {
75
- // EMOJI and EMOJI_WITH_NUMBERS
76
- expect ( actionNameDictionary . allowlist . has ( '123' ) ) . toBeTrue ( )
77
- // COMPLEX_MIXED
78
- expect ( actionNameDictionary . allowlist . has ( 'test' ) ) . toBeTrue ( )
79
- expect ( actionNameDictionary . allowlist . has ( 'hello' ) ) . toBeTrue ( )
80
- expect ( actionNameDictionary . allowlist . has ( '>=42' ) ) . toBeTrue ( )
81
- expect ( actionNameDictionary . allowlist . has ( 'world' ) ) . toBeTrue ( )
167
+ if ( isBrowserSupported ( ) ) {
168
+ describe ( 'actionNameDictionary processing' , ( ) => {
169
+ let actionNameDictionary : AllowedDictionary
170
+ let clearActionNameDictionary : ( ) => void
171
+
172
+ beforeEach ( ( ) => {
173
+ window . $DD_ALLOW = new Set ( [ TEST_STRINGS . COMPLEX_MIXED , TEST_STRINGS . PARAGRAPH_MIXED ] )
174
+ actionNameDictionary = createActionAllowList ( )
175
+ clearActionNameDictionary = actionNameDictionary . clear
176
+ } )
177
+
178
+ afterEach ( ( ) => {
179
+ window . $DD_ALLOW = undefined
180
+ clearActionNameDictionary ( )
181
+ } )
182
+
183
+ it ( 'initializes allowlist with normalized words from $DD_ALLOW' , ( ) => {
184
+ expect ( actionNameDictionary . allowlist . has ( 'test' ) ) . toBeTrue ( )
185
+ expect ( actionNameDictionary . allowlist . has ( 'hello' ) ) . toBeTrue ( )
186
+ expect ( actionNameDictionary . allowlist . has ( 'world' ) ) . toBeTrue ( )
187
+ } )
188
+
189
+ it ( 'updates dictionary when $DD_ALLOW changes' , ( ) => {
190
+ const initialAllowlistSize = actionNameDictionary . allowlist . size
191
+
192
+ // Simulate a change in $DD_ALLOW
193
+ window . $DD_ALLOW ?. add ( 'new-Word' )
194
+ window . $DD_ALLOW ?. add ( 'another-Word' )
195
+ // Trigger the observer manually
196
+ processRawAllowList ( window . $DD_ALLOW , actionNameDictionary )
197
+
198
+ // Verify dictionary is updated with new words
199
+ expect ( actionNameDictionary . allowlist . has ( 'word' ) ) . toBeTrue ( )
200
+ expect ( actionNameDictionary . allowlist . has ( 'new' ) ) . toBeTrue ( )
201
+ expect ( actionNameDictionary . allowlist . has ( 'another' ) ) . toBeTrue ( )
202
+ // Old words should still be present
203
+ expect ( actionNameDictionary . allowlist . size ) . toBe ( initialAllowlistSize + 3 )
204
+ } )
82
205
} )
206
+ }
83
207
84
- it ( 'updates dictionary when $DD_ALLOW changes' , ( ) => {
85
- expect ( actionNameDictionary . allowlist . size ) . toBe ( 20 )
86
-
87
- // Simulate a change in $DD_ALLOW
88
- window . $DD_ALLOW ?. add ( 'new-Word' )
89
- window . $DD_ALLOW ?. add ( 'another-Word' )
90
- // Trigger the observer manually
91
- processRawAllowList ( window . $DD_ALLOW , actionNameDictionary )
92
-
93
- // Verify dictionary is updated with new words
94
- expect ( actionNameDictionary . allowlist . has ( 'word' ) ) . toBeTrue ( )
95
- expect ( actionNameDictionary . allowlist . has ( 'new' ) ) . toBeTrue ( )
96
- expect ( actionNameDictionary . allowlist . has ( 'another' ) ) . toBeTrue ( )
97
- // Old words should still be present
98
- expect ( actionNameDictionary . allowlist . size ) . toBe ( 23 )
99
- } )
100
- } )
101
-
102
- describe ( 'maskActionName' , ( ) => {
208
+ describe ( 'createActionNameDictionary and maskActionName' , ( ) => {
103
209
let actionNameDictionary : AllowedDictionary
104
210
let clearActionNameDictionary : ( ) => void
105
211
@@ -122,12 +228,22 @@ describe('maskActionName', () => {
122
228
} )
123
229
124
230
it ( 'masks words not in allowlist (with dictionary from $DD_ALLOW)' , ( ) => {
231
+ let expected = 'test-💥-xxxxxx-xxx'
232
+ if ( ! isBrowserSupported ( ) ) {
233
+ expected = ACTION_NAME_PLACEHOLDER
234
+ }
235
+
125
236
const testString1 = maskActionName ( 'test-💥-$>=123-pii' , actionNameDictionary . allowlist )
126
237
expect ( testString1 . masked ) . toBeTrue ( )
127
- expect ( testString1 . name ) . toBe ( 'test-💥-***-***' )
238
+ expect ( testString1 . name ) . toBe ( expected )
239
+
240
+ expected = 'test-xxxxxx*hello xxxx'
241
+ if ( ! isBrowserSupported ( ) ) {
242
+ expected = ACTION_NAME_PLACEHOLDER
243
+ }
128
244
const testString2 = maskActionName ( 'test-💥+123*hello wild' , actionNameDictionary . allowlist )
129
245
expect ( testString2 . masked ) . toBeTrue ( )
130
- expect ( testString2 . name ) . toBe ( 'test-****hello ***' )
246
+ expect ( testString2 . name ) . toBe ( expected )
131
247
} )
132
248
133
249
it ( 'handles empty string' , ( ) => {
0 commit comments