mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-02 04:02:20 +00:00
fix: use gtag get for checkout attribution (#8930)
## Summary
Replace checkout attribution GA identity sourcing from
`window.__ga_identity__` with GA4 `gtag('get', ...)` calls keyed by
remote config measurement ID.
## Changes
- **What**:
- Add typed global `gtag` get definitions and shared GA field types.
- Fetch `client_id`, `session_id`, and `session_number` via `gtag('get',
measurementId, field, callback)` with timeout-based fallback.
- Normalize numeric GA values to strings before emitting checkout
attribution metadata.
- Update checkout attribution tests to mock `gtag` retrieval and verify
requested fields + numeric normalization.
- Add `ga_measurement_id` to remote config typings.
## Review Focus
Validate the `gtag('get', ...)` retrieval path and failure handling
(`undefined` fallback on timeout/errors) and confirm analytics field
names match GA4 expectations.
## Screenshots (if applicable)
N/A
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8930-fix-use-gtag-get-for-checkout-attribution-30a6d73d365081dcb773da945daceee6)
by [Unito](https://www.unito.io)
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import { GtmTelemetryProvider } from './GtmTelemetryProvider'
|
||||
|
||||
describe('GtmTelemetryProvider', () => {
|
||||
beforeEach(() => {
|
||||
window.__CONFIG__ = {}
|
||||
window.dataLayer = undefined
|
||||
window.gtag = undefined
|
||||
document.head.innerHTML = ''
|
||||
})
|
||||
|
||||
it('injects the GTM runtime script', () => {
|
||||
window.__CONFIG__ = {
|
||||
gtm_container_id: 'GTM-TEST123'
|
||||
}
|
||||
|
||||
new GtmTelemetryProvider()
|
||||
|
||||
const gtmScript = document.querySelector(
|
||||
'script[src="https://www.googletagmanager.com/gtm.js?id=GTM-TEST123"]'
|
||||
)
|
||||
|
||||
expect(gtmScript).not.toBeNull()
|
||||
expect(window.dataLayer?.[0]).toMatchObject({
|
||||
event: 'gtm.js'
|
||||
})
|
||||
})
|
||||
|
||||
it('bootstraps gtag when a GA measurement id exists', () => {
|
||||
window.__CONFIG__ = {
|
||||
ga_measurement_id: 'G-TEST123'
|
||||
}
|
||||
|
||||
new GtmTelemetryProvider()
|
||||
|
||||
const gtagScript = document.querySelector(
|
||||
'script[src="https://www.googletagmanager.com/gtag/js?id=G-TEST123"]'
|
||||
)
|
||||
const dataLayer = window.dataLayer as unknown[]
|
||||
|
||||
expect(gtagScript).not.toBeNull()
|
||||
expect(typeof window.gtag).toBe('function')
|
||||
expect(dataLayer).toHaveLength(2)
|
||||
expect(Array.from(dataLayer[0] as IArguments)[0]).toBe('js')
|
||||
expect(Array.from(dataLayer[1] as IArguments)).toEqual([
|
||||
'config',
|
||||
'G-TEST123',
|
||||
{
|
||||
send_page_view: false
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('does not inject duplicate gtag scripts across repeated init', () => {
|
||||
window.__CONFIG__ = {
|
||||
ga_measurement_id: 'G-TEST123'
|
||||
}
|
||||
|
||||
new GtmTelemetryProvider()
|
||||
new GtmTelemetryProvider()
|
||||
|
||||
const gtagScripts = document.querySelectorAll(
|
||||
'script[src="https://www.googletagmanager.com/gtag/js?id=G-TEST123"]'
|
||||
)
|
||||
|
||||
expect(gtagScripts).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
@@ -22,13 +22,21 @@ export class GtmTelemetryProvider implements TelemetryProvider {
|
||||
if (typeof window === 'undefined') return
|
||||
|
||||
const gtmId = window.__CONFIG__?.gtm_container_id
|
||||
if (!gtmId) {
|
||||
if (gtmId) {
|
||||
this.initializeGtm(gtmId)
|
||||
} else {
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
console.warn('[GTM] No GTM ID configured, skipping initialization')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const measurementId = window.__CONFIG__?.ga_measurement_id
|
||||
if (measurementId) {
|
||||
this.bootstrapGtag(measurementId)
|
||||
}
|
||||
}
|
||||
|
||||
private initializeGtm(gtmId: string): void {
|
||||
window.dataLayer = window.dataLayer || []
|
||||
|
||||
window.dataLayer.push({
|
||||
@@ -44,6 +52,38 @@ export class GtmTelemetryProvider implements TelemetryProvider {
|
||||
this.initialized = true
|
||||
}
|
||||
|
||||
private bootstrapGtag(measurementId: string): void {
|
||||
window.dataLayer = window.dataLayer || []
|
||||
|
||||
if (typeof window.gtag !== 'function') {
|
||||
function gtag() {
|
||||
// gtag queue shape is dataLayer.push(arguments)
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
;(window.dataLayer as unknown[] | undefined)?.push(arguments)
|
||||
}
|
||||
|
||||
window.gtag = gtag as Window['gtag']
|
||||
}
|
||||
|
||||
const gtagScriptSrc = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`
|
||||
const existingGtagScript = document.querySelector(
|
||||
`script[src="${gtagScriptSrc}"]`
|
||||
)
|
||||
|
||||
if (!existingGtagScript) {
|
||||
const script = document.createElement('script')
|
||||
script.async = true
|
||||
script.src = gtagScriptSrc
|
||||
document.head.insertBefore(script, document.head.firstChild)
|
||||
}
|
||||
|
||||
const gtag = window.gtag
|
||||
if (typeof gtag !== 'function') return
|
||||
|
||||
gtag('js', new Date())
|
||||
gtag('config', measurementId, { send_page_view: false })
|
||||
}
|
||||
|
||||
private pushEvent(event: string, properties?: Record<string, unknown>): void {
|
||||
if (!this.initialized) return
|
||||
window.dataLayer?.push({ event, ...properties })
|
||||
|
||||
Reference in New Issue
Block a user