Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,41 +76,48 @@ class BabelInteractionTracking {

const { useAccessibilityLabel } = this.config;

const tryContent = () => {
const content = getContent?.();
if (content && content.length > 0) {
return content;
const index = handlerArgs
? handlerArgs.find(x => typeof x === 'number') || 0
: 0;

// Picks the value at `index` (positions map to compound component parts).
// Values can come from runtime expressions, so a value that doesn't resolve
// to a usable name returns null and lets the next naming source take over.
const getIndexedValue = (values: unknown): string | null => {
if (!Array.isArray(values) || values.length === 0) {
return null;
}

// Fail-safe in case the our 'index' value turns out to not be a real index
const value =
index + 1 > values.length || index < 0
? values[0]
: values[index];

if (
(typeof value === 'string' && value.length > 0) ||
(typeof value === 'number' && Number.isFinite(value))
) {
return String(value);
}

return null;
};

const getAccessibilityLabel = () =>
useAccessibilityLabel && accessibilityLabel
? accessibilityLabel
: null;

const index = handlerArgs
? handlerArgs.find(x => typeof x === 'number') || 0
: 0;
useAccessibilityLabel ? getIndexedValue(accessibilityLabel) : null;

// Order: actionName → actionNameAttribute → accessibilityLabel → content
const selectedContent =
actionName ||
Object.values(attrs)[0] ||
getAccessibilityLabel() ||
tryContent();
const output =
getIndexedValue(actionName) ??
getIndexedValue(Object.values(attrs)[0]) ??
getAccessibilityLabel() ??
getIndexedValue(getContent?.());

if (!selectedContent) {
if (output === null) {
return componentName;
}

// Fail-safe in case the our 'index' value turns out to not be a real index
const output =
index + 1 > selectedContent.length || index < 0
? selectedContent[0]
: selectedContent[index];

return options.useNamePrefix
? `${componentName} ("${output}")`
: output;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,83 @@ describe('DdBabelInteractionTracking.wrapRumAction', () => {
);
});
});

describe('DdBabelInteractionTracking target name resolution', () => {
const mockAddAction = jest.fn().mockResolvedValue(undefined);

beforeEach(() => {
mockAddAction.mockClear();
DdBabelInteractionTracking.config = {
trackInteractions: true,
useAccessibilityLabel: true
};
DdBabelInteractionTracking.attachRumInstance({
addAction: mockAddAction
} as any);
});

const getTargetName = (targetObject: Record<string, any>) => {
DdBabelInteractionTracking.wrapRumAction(jest.fn(), RumActionType.TAP, {
...mockTargetObject,
options: { useContent: true, useNamePrefix: false },
...targetObject
} as any)();
return mockAddAction.mock.calls[0][1];
};

it('should use the actionNameAttribute value', () => {
expect(getTargetName({ testID: ['save_button'] })).toBe('save_button');
});

it('should skip undefined and empty actionNameAttribute values', () => {
expect(
getTargetName({
testID: [undefined, ''],
accessibilityLabel: ['Save']
})
).toBe('Save');
});

it('should skip empty dd-action-name and fall back to actionNameAttribute', () => {
expect(
getTargetName({ 'dd-action-name': [], testID: ['save_button'] })
).toBe('save_button');
});

it('should fall back to content when no attribute resolves', () => {
expect(
getTargetName({
testID: [null],
accessibilityLabel: [undefined],
getContent: () => ['Save content']
})
).toBe('Save content');
});

it('should fall back to the component name when nothing resolves', () => {
expect(getTargetName({ testID: [undefined] })).toBe('Button');
});

it('should stringify numeric values', () => {
expect(getTargetName({ testID: [42] })).toBe('42');
});

it('should keep positions when resolving the indexed value', () => {
expect(
getTargetName({
testID: ['first', undefined, 'third'],
handlerArgs: [2]
})
).toBe('third');
});

it('should fall back to the next source when the indexed value is unresolved', () => {
expect(
getTargetName({
testID: ['first', undefined, 'third'],
accessibilityLabel: ['a11y_first', 'a11y_second'],
handlerArgs: [1]
})
).toBe('a11y_second');
});
});
Loading
Loading