Skip to content

Commit ce564c5

Browse files
committed
fix: remove fallback for older version browsers
1 parent 052a04b commit ce564c5

File tree

5 files changed

+308
-119
lines changed

5 files changed

+308
-119
lines changed

packages/rum-core/src/domain/action/actionCollection.spec.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { createHooks } from '../hooks'
1111
import type { RumMutationRecord } from '../../browser/domMutationObservable'
1212
import type { ActionContexts } from './actionCollection'
1313
import { startActionCollection } from './actionCollection'
14+
import { ACTION_NAME_PLACEHOLDER } from './getActionNameFromElement'
15+
import { isBrowserSupported } from './privacy/allowedDictionary'
1416

1517
describe('actionCollection', () => {
1618
const lifeCycle = new LifeCycle()
@@ -206,8 +208,13 @@ describe('actionCollection', () => {
206208
startClocks: { relative: 0 as RelativeTime, timeStamp: 0 as TimeStamp },
207209
type: ActionType.CUSTOM,
208210
})
211+
212+
let expectedName = 'foo bar xxx'
213+
if(!isBrowserSupported()) {
214+
expectedName = ACTION_NAME_PLACEHOLDER
215+
}
209216

210-
expect((rawRumEvents[0].rawRumEvent as RawRumActionEvent).action.target.name).toBe('foo bar ***')
217+
expect((rawRumEvents[0].rawRumEvent as RawRumActionEvent).action.target.name).toBe(expectedName)
211218
expect((rawRumEvents[0].rawRumEvent as RawRumActionEvent)._dd?.action?.name_source).toBe('mask_disallowed')
212219
})
213220

@@ -229,7 +236,12 @@ describe('actionCollection', () => {
229236
startClocks: { relative: 0 as RelativeTime, timeStamp: 0 as TimeStamp },
230237
type: ActionType.CLICK,
231238
})
232-
expect((rawRumEvents[0].rawRumEvent as RawRumActionEvent).action.target.name).toBe('foo bar ***')
239+
240+
let expectedName = 'foo bar xxx'
241+
if(!isBrowserSupported()) {
242+
expectedName = ACTION_NAME_PLACEHOLDER
243+
}
244+
expect((rawRumEvents[0].rawRumEvent as RawRumActionEvent).action.target.name).toBe(expectedName)
233245
expect((rawRumEvents[0].rawRumEvent as RawRumActionEvent)._dd?.action?.name_source).toBe('mask_disallowed')
234246
})
235247
})

packages/rum-core/src/domain/action/actionCollection.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,10 @@ function processAction(
9393
action: AutoAction | CustomAction,
9494
actionNameDictionary: AllowedDictionary
9595
): RawRumEventCollectedData<RawRumActionEvent> {
96-
const { name: updatedName, masked } = maskActionName(action.name, actionNameDictionary.allowlist)
96+
const { name: updatedName, masked } =
97+
isAutoAction(action) && action.nameSource === ActionNameSource.MASK_PLACEHOLDER
98+
? { name: action.name, masked: false }
99+
: maskActionName(action.name, actionNameDictionary.allowlist)
97100
const autoActionProperties = isAutoAction(action)
98101
? {
99102
action: {

packages/rum-core/src/domain/action/privacy/allowedDictionary.spec.ts

Lines changed: 186 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,133 @@
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'
29
import type { AllowedDictionary } from './allowedDictionary'
310

411
const TEST_STRINGS = {
512
COMPLEX_MIXED: 'test-user-name:💥$$$, test-user-id:hello>=42@world?',
613
PARAGRAPH_MIXED: 'This is a test paragraph with various symbols: 💥, $$$, 123, and more.',
714
}
815

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+
9131
describe('createActionAllowList', () => {
10132
beforeAll(() => {
11133
window.$DD_ALLOW = new Set([TEST_STRINGS.COMPLEX_MIXED, TEST_STRINGS.PARAGRAPH_MIXED])
@@ -15,10 +137,18 @@ describe('createActionAllowList', () => {
15137
window.$DD_ALLOW = undefined
16138
})
17139

18-
it('should create an action name dictionary', () => {
140+
it('should create an action name dictionary and clear it', () => {
19141
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)
21148
expect(actionNameDictionary.rawStringIterator).toBeDefined()
149+
actionNameDictionary.clear()
150+
expect(actionNameDictionary.allowlist.size).toBe(0)
151+
expect(actionNameDictionary.rawStringIterator).toBeUndefined()
22152
})
23153

24154
it('should handle when $DD_ALLOW is undefined and redefined later', () => {
@@ -30,76 +160,52 @@ describe('createActionAllowList', () => {
30160
// Trigger the observer manually
31161
window.$DD_ALLOW_OBSERVERS?.forEach((observer) => observer())
32162
expect(actionNameDictionary.rawStringIterator).toBeDefined()
163+
actionNameDictionary.clear()
33164
})
34165
})
35166

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+
})
82205
})
206+
}
83207

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', () => {
103209
let actionNameDictionary: AllowedDictionary
104210
let clearActionNameDictionary: () => void
105211

@@ -122,12 +228,22 @@ describe('maskActionName', () => {
122228
})
123229

124230
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+
125236
const testString1 = maskActionName('test-💥-$>=123-pii', actionNameDictionary.allowlist)
126237
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+
}
128244
const testString2 = maskActionName('test-💥+123*hello wild', actionNameDictionary.allowlist)
129245
expect(testString2.masked).toBeTrue()
130-
expect(testString2.name).toBe('test-****hello ***')
246+
expect(testString2.name).toBe(expected)
131247
})
132248

133249
it('handles empty string', () => {

0 commit comments

Comments
 (0)