import { ExperimentalFeature } from '@datadog/browser-core'
import { mockExperimentalFeatures } from '../../../../core/test'
import { appendElement, mockRumConfiguration } from '../../../test'
import { NodePrivacyLevel } from '../privacyConstants'
import { getNodeSelfPrivacyLevel } from '../privacy'
import { getActionNameFromElement } from './getActionNameFromElement'
import { ActionNameSource } from './actionNameConstants'
const defaultConfiguration = mockRumConfiguration()
describe('getActionNameFromElement', () => {
it('extracts the textual content of an element', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement('
'),
defaultConfiguration
)
expect(name).toBe('Foo bar')
expect(nameSource).toBe('text_content')
})
it('extracts the text of an input button', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(''),
defaultConfiguration
)
expect(name).toBe('Click')
expect(nameSource).toBe('text_content')
})
it('extracts the alt text of an image', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement('
'),
defaultConfiguration
)
expect(name).toBe('bar')
expect(nameSource).toBe('standard_attribute')
})
it('extracts the title text of an image', () => {
const { name, nameSource } = getActionNameFromElement(appendElement('
'), defaultConfiguration)
expect(name).toBe('foo')
expect(nameSource).toBe('standard_attribute')
})
it('extracts the text of an aria-label attribute', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(''),
defaultConfiguration
)
expect(name).toBe('Foo')
expect(nameSource).toBe('standard_attribute')
})
it('gets the parent element textual content if everything else fails', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement('Foo
![]()
'),
defaultConfiguration
)
expect(name).toBe('Foo')
expect(nameSource).toBe('text_content')
})
it("doesn't get the value of a text input", () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(''),
defaultConfiguration
)
expect(name).toBe('')
expect(nameSource).toBe('blank')
})
it("doesn't get the value of a password input", () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(''),
defaultConfiguration
)
expect(name).toBe('')
expect(nameSource).toBe('blank')
})
it('limits the { name, nameSource } length to a reasonable size', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(
'Foooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz
'
),
defaultConfiguration
)
expect(name).toBe(
'Foooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]'
)
expect(nameSource).toBe('text_content')
})
it('normalize white spaces', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement('foo\tbar\n\n baz
'),
defaultConfiguration
)
expect(name).toBe('foo bar baz')
expect(nameSource).toBe('text_content')
})
it('should correctly compute whitespace for
and elements', () => {
const testCases = [
{ element: appendElement('
hello
world
'), expected: 'hello world' },
{ element: appendElement(''), expected: 'hello world' },
{ element: appendElement(''), expected: 'hello world !' },
{ element: appendElement(''), expected: 'hello world ! !' },
]
testCases.forEach(({ element, expected }) => {
const { name, nameSource } = getActionNameFromElement(element, defaultConfiguration)
expect(name).toBe(expected)
expect(nameSource).toBe('text_content')
})
})
it('should introduce whitespace for block-level display values', () => {
mockExperimentalFeatures([ExperimentalFeature.USE_TREE_WALKER_FOR_ACTION_NAME])
const testCases = [
{ display: 'block', expected: 'space' },
{ display: 'inline-block', expected: 'no-space' },
{ display: 'flex', expected: 'space' },
{ display: 'inline-flex', expected: 'no-space' },
{ display: 'grid', expected: 'space' },
{ display: 'inline-grid', expected: 'no-space' },
{ display: 'list-item', expected: 'space' },
{ display: 'table', expected: 'space' },
{ display: 'table-caption', expected: 'space' },
{ display: 'inline', expected: 'no-space' },
{ display: 'none', expected: 'nothing' },
]
testCases.forEach(({ display, expected }) => {
const element = appendElement(
``
)
const { name, nameSource } = getActionNameFromElement(element, defaultConfiguration)
switch (expected) {
case 'space':
expect(name).toBe('foo bar')
expect(nameSource).toBe('text_content')
break
case 'no-space':
expect(name).toBe('foobar')
expect(nameSource).toBe('text_content')
break
case 'nothing':
expect(name).toBe('')
expect(nameSource).toBe('blank')
break
}
})
})
it('ignores the inline script textual content', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement("b
"),
defaultConfiguration
)
expect(name).toBe('b')
expect(nameSource).toBe('text_content')
})
it('extracts text from SVG elements', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(''),
defaultConfiguration
)
expect(name).toBe('foo bar')
expect(nameSource).toBe('text_content')
})
it('extracts text from an associated label', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('label text')
expect(nameSource).toBe('text_content')
})
it('extracts text from a parent label', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('foo bar')
expect(nameSource).toBe('text_content')
})
it('extracts text from the first OPTION element when clicking on a SELECT', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('foo')
expect(nameSource).toBe('text_content')
})
it('extracts text from a aria-labelledby associated element', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('label text')
expect(nameSource).toBe('text_content')
})
it('extracts text from multiple aria-labelledby associated elements', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('label text')
expect(nameSource).toBe('text_content')
})
it('extracts text from a BUTTON element', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('foo')
expect(nameSource).toBe('text_content')
})
it('extracts text from a role=button element', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('foo')
expect(nameSource).toBe('text_content')
})
it('limits the recursion to the 10th parent', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('')
expect(nameSource).toBe('blank')
})
it('limits the recursion to the BODY element', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
ignored
`),
defaultConfiguration
)
expect(name).toBe('')
expect(nameSource).toBe('blank')
})
it('limits the recursion to a FORM element', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('')
expect(nameSource).toBe('blank')
})
it('extracts the { name, nameSource } from a parent FORM element', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('foo')
expect(nameSource).toBe('standard_attribute')
})
it('extracts the whole textual content of a button', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
`),
defaultConfiguration
)
expect(name).toBe('foo bar')
expect(nameSource).toBe('text_content')
})
it('ignores the textual content of contenteditable elements', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
ignored
ignored
`),
defaultConfiguration
)
expect(name).toBe('')
expect(nameSource).toBe('blank')
})
it('extracts the { name, nameSource } from attributes of contenteditable elements', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`
ignored
ignored
`),
defaultConfiguration
)
expect(name).toBe('foo')
expect(nameSource).toBe('standard_attribute')
})
it('computes an action name on SVG elements', () => {
const { name, nameSource } = getActionNameFromElement(
appendElement(`