From 3780a9fe84b210f65d87f019f226a13b2fcc245b Mon Sep 17 00:00:00 2001 From: CodingOnStar Date: Mon, 31 Aug 2026 22:53:31 +0800 Subject: [PATCH] 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. --- .../home/__tests__/home-trending.spec.tsx | 66 +++++++++++++++++++ .../marketplace/home/home-trending.tsx | 7 +- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/web/app/components/plugins/marketplace/home/__tests__/home-trending.spec.tsx b/web/app/components/plugins/marketplace/home/__tests__/home-trending.spec.tsx index e8be4b4b2f9..878969c9302 100644 --- a/web/app/components/plugins/marketplace/home/__tests__/home-trending.spec.tsx +++ b/web/app/components/plugins/marketplace/home/__tests__/home-trending.spec.tsx @@ -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() + + 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() + } + }) }) diff --git a/web/app/components/plugins/marketplace/home/home-trending.tsx b/web/app/components/plugins/marketplace/home/home-trending.tsx index 6448631b9f7..b67c76f6a3d 100644 --- a/web/app/components/plugins/marketplace/home/home-trending.tsx +++ b/web/app/components/plugins/marketplace/home/home-trending.tsx @@ -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, )