fix(web): dual-write marketplace banner impressions

Clicks already go to Amplitude and __marketplaceTracking__. Impressions
only hit Amplitude, so marketplace.dify.ai never recorded views.
This commit is contained in:
CodingOnStar 2026-08-31 22:53:31 +08:00
parent 42f7088d9d
commit 3780a9fe84
2 changed files with 71 additions and 2 deletions

View File

@ -3,12 +3,18 @@ import { act, fireEvent, render, screen, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { trackEvent } from '@/app/components/base/amplitude'
import { trackMarketplaceSiteEvent } from '@/utils/marketplace-site-track'
import HomeTrending from '../home-trending'
vi.mock('@/app/components/base/amplitude', () => ({
trackEvent: vi.fn(),
}))
vi.mock('@/utils/marketplace-site-track', () => ({
rememberMarketplaceSiteReferrer: vi.fn(),
trackMarketplaceSiteEvent: vi.fn(),
}))
vi.mock('#i18n', async () => {
const { withSelectorKey } = await import('@/test/i18n-mock')
return {
@ -110,6 +116,7 @@ const banners: PluginBanner[] = [
]
const mockTrackEvent = vi.mocked(trackEvent)
const mockTrackMarketplaceSiteEvent = vi.mocked(trackMarketplaceSiteEvent)
beforeEach(() => {
vi.clearAllMocks()
@ -746,4 +753,63 @@ describe('HomeTrending', () => {
style_type: 'event',
})
})
it('dual-writes banner impressions to Amplitude and marketplace site tracking', () => {
vi.useFakeTimers()
const observers: Array<{ callback: IntersectionObserverCallback }> = []
class MockIntersectionObserver {
disconnect = vi.fn()
observe = vi.fn()
root: Element | Document | null = null
rootMargin = '0px'
takeRecords = () => []
thresholds = [0.5]
unobserve = vi.fn()
constructor(callback: IntersectionObserverCallback) {
observers.push({ callback })
}
}
vi.stubGlobal('IntersectionObserver', MockIntersectionObserver)
try {
const blogBanner = banners[1]
if (!blogBanner) throw new Error('Expected a blog banner fixture')
render(<HomeTrending banners={[blogBanner]} isMarketplacePlatform page="plugins" />)
const observer = observers.at(-1)
if (!observer) throw new Error('Expected IntersectionObserver to be registered')
act(() => {
observer.callback(
[
{
intersectionRatio: 0.5,
isIntersecting: true,
} as IntersectionObserverEntry,
],
{} as IntersectionObserver,
)
})
act(() => {
vi.advanceTimersByTime(1000)
})
const properties = {
banner_id: 'blog',
sort: 1,
page: 'plugins',
language: 'en',
style_type: 'blog',
}
expect(mockTrackEvent).toHaveBeenCalledWith('marketplace_banner_impression', properties)
expect(mockTrackMarketplaceSiteEvent).toHaveBeenCalledWith(
'marketplace_banner_impression',
properties,
)
} finally {
vi.useRealTimers()
}
})
})

View File

@ -11,6 +11,7 @@ import { cn } from '@langgenius/dify-ui/cn'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from '#i18n'
import { trackEvent } from '@/app/components/base/amplitude'
import { trackMarketplaceSiteEvent } from '@/utils/marketplace-site-track'
import TrendingNavigation from './home-trending-navigation'
import { HomeBannerSlide } from './home-trending-slides'
import styles from './home-trending.module.css'
@ -50,13 +51,15 @@ function TrackedBannerSlide({
useBannerViewability(
slideRef,
() => {
trackEvent('marketplace_banner_impression', {
const properties = {
banner_id: banner.id,
sort: banner.sort,
page,
language: banner.language,
style_type: banner.style_type,
})
}
trackEvent('marketplace_banner_impression', properties)
trackMarketplaceSiteEvent('marketplace_banner_impression', properties)
},
isActive,
)