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('
Foo
bar
'), 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('bar'), 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('
hello

world

'), expected: 'hello world' }, { element: appendElement('
hello

world
!

'), expected: 'hello world !' }, { element: appendElement('
hello world
!

!

'), 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( `
foo
bar
` ) 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('foo bar'), defaultConfiguration ) expect(name).toBe('foo bar') expect(nameSource).toBe('text_content') }) it('extracts text from an associated label', () => { const { name, nameSource } = getActionNameFromElement( appendElement(`
ignored
`), 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(`
ignored
`), 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(`
ignored
ignored
`), defaultConfiguration ) expect(name).toBe('label text') expect(nameSource).toBe('text_content') }) it('extracts text from a BUTTON element', () => { const { name, nameSource } = getActionNameFromElement( appendElement(`
ignored
`), defaultConfiguration ) expect(name).toBe('foo') expect(nameSource).toBe('text_content') }) it('extracts text from a role=button element', () => { const { name, nameSource } = getActionNameFromElement( appendElement(`
ignored
foo
`), defaultConfiguration ) expect(name).toBe('foo') expect(nameSource).toBe('text_content') }) it('limits the recursion to the 10th parent', () => { const { name, nameSource } = getActionNameFromElement( appendElement(`
ignored
`), 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(`
ignored
`), defaultConfiguration ) expect(name).toBe('') expect(nameSource).toBe('blank') }) it('extracts the { name, nameSource } from a parent FORM element', () => { const { name, nameSource } = getActionNameFromElement( appendElement(`
ignored
`), 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(`