mirror of
https://github.com/logos-co/logos-web.git
synced 2026-08-30 21:51:09 +00:00
Button labels were built from textContent, so a card that stacks a heading above its CTA produced run-together names like "Explore the TechGet Started". Build the label from innerText instead and join its rendered lines with the " - " separator already used by hand-written event names.
177 lines
4.9 KiB
TypeScript
177 lines
4.9 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
TRACKABLE_BUTTON_SELECTOR,
|
|
buildButtonClickEventName,
|
|
buildButtonClickEventData,
|
|
getButtonTrackingLabel,
|
|
getTrackableButton,
|
|
shouldTrackButtonClick,
|
|
} from '../umami-button-tracking'
|
|
|
|
interface FakeButtonOptions {
|
|
readonly ariaLabel?: string
|
|
readonly eventName?: string
|
|
readonly disabled?: boolean
|
|
readonly excluded?: boolean
|
|
readonly excludedAncestor?: boolean
|
|
readonly id?: string
|
|
readonly innerText?: string
|
|
readonly name?: string
|
|
readonly nestedEventName?: string
|
|
readonly textContent?: string
|
|
readonly title?: string
|
|
}
|
|
|
|
const createButton = ({
|
|
ariaLabel,
|
|
disabled = false,
|
|
eventName,
|
|
excluded = false,
|
|
excludedAncestor = false,
|
|
id = '',
|
|
innerText,
|
|
name,
|
|
nestedEventName,
|
|
textContent = '',
|
|
title,
|
|
}: FakeButtonOptions = {}) => {
|
|
const attributes = new Map<string, string>()
|
|
|
|
if (ariaLabel) attributes.set('aria-label', ariaLabel)
|
|
if (eventName) attributes.set('data-umami-event-name', eventName)
|
|
if (excluded) attributes.set('data-umami-button-tracking', 'off')
|
|
if (name) attributes.set('name', name)
|
|
if (title) attributes.set('title', title)
|
|
|
|
return {
|
|
classList: {
|
|
contains: (className: string) => className === 'disabled' && disabled,
|
|
},
|
|
closest: (selector: string) =>
|
|
selector === '[data-umami-button-tracking="off"]' && excludedAncestor
|
|
? {}
|
|
: null,
|
|
disabled,
|
|
getAttribute: (name: string) => attributes.get(name) ?? null,
|
|
id,
|
|
innerText,
|
|
querySelector: (selector: string) =>
|
|
selector === '[data-umami-event-name]' && nestedEventName
|
|
? {
|
|
getAttribute: (name: string) =>
|
|
name === 'data-umami-event-name' ? nestedEventName : null,
|
|
}
|
|
: null,
|
|
textContent,
|
|
} as unknown as HTMLElement
|
|
}
|
|
|
|
describe('getTrackableButton', () => {
|
|
it('finds the closest button or link target', () => {
|
|
const button = createButton()
|
|
const target = {
|
|
closest: (selector: string) =>
|
|
selector === TRACKABLE_BUTTON_SELECTOR ? button : null,
|
|
} as unknown as Element
|
|
|
|
expect(getTrackableButton(target)).toBe(button)
|
|
})
|
|
|
|
it('returns null for non-DOM targets', () => {
|
|
expect(getTrackableButton(null)).toBeNull()
|
|
expect(getTrackableButton({})).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('shouldTrackButtonClick', () => {
|
|
it('tracks enabled buttons and links by default', () => {
|
|
expect(shouldTrackButtonClick(createButton())).toBe(true)
|
|
})
|
|
|
|
it('skips disabled and explicitly excluded elements', () => {
|
|
expect(shouldTrackButtonClick(createButton({ disabled: true }))).toBe(false)
|
|
expect(shouldTrackButtonClick(createButton({ excluded: true }))).toBe(false)
|
|
expect(
|
|
shouldTrackButtonClick(createButton({ excludedAncestor: true }))
|
|
).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('getButtonTrackingLabel', () => {
|
|
it('uses an explicit Umami event name before other labels', () => {
|
|
expect(
|
|
getButtonTrackingLabel(
|
|
createButton({
|
|
eventName: 'Run a Node',
|
|
id: 'node-card',
|
|
textContent: 'Run a node in less than 10 minutes Using CLI',
|
|
})
|
|
)
|
|
).toBe('Run a Node')
|
|
})
|
|
|
|
it('uses id before visible text', () => {
|
|
expect(
|
|
getButtonTrackingLabel(
|
|
createButton({ id: 'join-cta', textContent: 'Join the movement' })
|
|
)
|
|
).toBe('join-cta')
|
|
})
|
|
|
|
it('uses a nested explicit event name for generated interactive wrappers', () => {
|
|
expect(
|
|
getButtonTrackingLabel(
|
|
createButton({
|
|
nestedEventName: 'Open map cluster - 2 circles',
|
|
textContent: '2',
|
|
})
|
|
)
|
|
).toBe('Open map cluster - 2 circles')
|
|
})
|
|
|
|
it('joins the rendered lines of stacked copy with a dash', () => {
|
|
expect(
|
|
getButtonTrackingLabel(
|
|
createButton({
|
|
innerText: 'Explore the Tech\nGet Started',
|
|
textContent: 'Explore the TechGet Started',
|
|
})
|
|
)
|
|
).toBe('Explore the Tech - Get Started')
|
|
})
|
|
|
|
it('falls back through text, aria-label, name, and title', () => {
|
|
expect(
|
|
getButtonTrackingLabel(createButton({ textContent: ' Learn more ' }))
|
|
).toBe('Learn more')
|
|
expect(
|
|
getButtonTrackingLabel(createButton({ ariaLabel: 'Open menu' }))
|
|
).toBe('Open menu')
|
|
expect(getButtonTrackingLabel(createButton({ name: 'newsletter' }))).toBe(
|
|
'newsletter'
|
|
)
|
|
expect(
|
|
getButtonTrackingLabel(createButton({ title: 'External link' }))
|
|
).toBe('External link')
|
|
expect(getButtonTrackingLabel(null)).toBeNull()
|
|
expect(getButtonTrackingLabel(createButton())).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('buildButtonClickEventName', () => {
|
|
it('returns the extracted label', () => {
|
|
expect(
|
|
buildButtonClickEventName(createButton({ textContent: 'Subscribe' }))
|
|
).toBe('Subscribe')
|
|
})
|
|
})
|
|
|
|
describe('buildButtonClickEventData', () => {
|
|
it('keeps pathname context in event data', () => {
|
|
expect(buildButtonClickEventData('/builders-hub')).toEqual({
|
|
source: '/builders-hub',
|
|
})
|
|
})
|
|
})
|