Skip to content

Commit 9384255

Browse files
authored
Propagate measurement ID change to analytics-exp (#4459)
1 parent 74bf520 commit 9384255

File tree

6 files changed

+42
-22
lines changed

6 files changed

+42
-22
lines changed

packages-exp/analytics-exp/src/factory.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,7 @@ import {
2222
MinimalDynamicConfig,
2323
Analytics
2424
} from '@firebase/analytics-types-exp';
25-
import {
26-
insertScriptTag,
27-
getOrCreateDataLayer,
28-
wrapOrCreateGtag,
29-
findGtagScriptOnPage
30-
} from './helpers';
25+
import { getOrCreateDataLayer, wrapOrCreateGtag } from './helpers';
3126
import { AnalyticsError, ERROR_FACTORY } from './errors';
3227
import { _FirebaseInstallationsInternal } from '@firebase/installations-types-exp';
3328
import { areCookiesEnabled, isBrowserExtension } from '@firebase/util';
@@ -60,9 +55,9 @@ export let initializationPromisesMap: {
6055
* wait on all these to be complete in order to determine if it can selectively
6156
* wait for only certain initialization (FID) promises or if it must wait for all.
6257
*/
63-
let dynamicConfigPromisesList: Array<Promise<
64-
DynamicConfig | MinimalDynamicConfig
65-
>> = [];
58+
let dynamicConfigPromisesList: Array<
59+
Promise<DynamicConfig | MinimalDynamicConfig>
60+
> = [];
6661

6762
/**
6863
* Maps fetched measurementIds to appId. Populated when the app's dynamic config
@@ -214,10 +209,6 @@ export function factory(
214209
// Steps here should only be done once per page: creation or wrapping
215210
// of dataLayer and global gtag function.
216211

217-
// Detect if user has already put the gtag <script> tag on this page.
218-
if (!findGtagScriptOnPage()) {
219-
insertScriptTag(dataLayerName);
220-
}
221212
getOrCreateDataLayer(dataLayerName);
222213

223214
const { wrappedGtag, gtagCore } = wrapOrCreateGtag(
@@ -239,7 +230,8 @@ export function factory(
239230
dynamicConfigPromisesList,
240231
measurementIdToAppId,
241232
installations,
242-
gtagCoreFunction
233+
gtagCoreFunction,
234+
dataLayerName
243235
);
244236

245237
const analyticsInstance: AnalyticsService = new AnalyticsService(app);

packages-exp/analytics-exp/src/helpers.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ describe('Gtag wrapping functions', () => {
5757

5858
it('insertScriptIfNeeded inserts script tag', () => {
5959
expect(findGtagScriptOnPage()).to.be.null;
60-
insertScriptTag('customDataLayerName');
60+
insertScriptTag('customDataLayerName', fakeMeasurementId);
6161
const scriptTag = findGtagScriptOnPage();
6262
expect(scriptTag).to.not.be.null;
6363
expect(scriptTag!.src).to.contain(`l=customDataLayerName`);
64+
expect(scriptTag!.src).to.contain(`id=${fakeMeasurementId}`);
6465
});
6566

6667
describe('wrapOrCreateGtag() when user has not previously inserted a gtag script tag on this page', () => {

packages-exp/analytics-exp/src/helpers.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,14 @@ export function promiseAllSettled<T>(
4343
* Inserts gtag script tag into the page to asynchronously download gtag.
4444
* @param dataLayerName Name of datalayer (most often the default, "_dataLayer").
4545
*/
46-
export function insertScriptTag(dataLayerName: string): void {
46+
export function insertScriptTag(
47+
dataLayerName: string,
48+
measurementId: string
49+
): void {
4750
const script = document.createElement('script');
4851
// We are not providing an analyticsId in the URL because it would trigger a `page_view`
4952
// without fid. We will initialize ga-id using gtag (config) command together with fid.
50-
script.src = `${GTAG_URL}?l=${dataLayerName}`;
53+
script.src = `${GTAG_URL}?l=${dataLayerName}&id=${measurementId}`;
5154
script.async = true;
5255
document.head.appendChild(script);
5356
}

packages-exp/analytics-exp/src/index.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ describe('FirebaseAnalytics instance tests', () => {
100100
);
101101
});
102102
it('Warns if config has no apiKey but does have a measurementId', async () => {
103+
// Since this is a warning and doesn't block the rest of initialization
104+
// all the async stuff needs to be stubbed and cleaned up.
103105
const warnStub = stub(console, 'warn');
106+
const docStub = stub(document, 'createElement');
107+
stubFetch(200, { measurementId: fakeMeasurementId });
104108
const app = getFakeApp({
105109
appId: fakeAppParams.appId,
106110
measurementId: fakeMeasurementId
@@ -115,7 +119,11 @@ describe('FirebaseAnalytics instance tests', () => {
115119
`Falling back to the measurement ID ${fakeMeasurementId}`
116120
);
117121
warnStub.restore();
122+
docStub.restore();
123+
fetchStub.restore();
118124
idbOpenStub.restore();
125+
delete window['gtag'];
126+
delete window['dataLayer'];
119127
});
120128
it('Throws if creating an instance with already-used appId', () => {
121129
const app = getFakeApp(fakeAppParams);
@@ -206,6 +214,7 @@ describe('FirebaseAnalytics instance tests', () => {
206214
afterEach(() => {
207215
delete window['gtag'];
208216
delete window['dataLayer'];
217+
removeGtagScript();
209218
fetchStub.restore();
210219
clock.restore();
211220
warnStub.restore();

packages-exp/analytics-exp/src/initialize-analytics.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { DynamicConfig } from '@firebase/analytics-types-exp';
2828
import { FirebaseApp } from '@firebase/app-types-exp';
2929
import { Deferred } from '@firebase/util';
3030
import { _FirebaseInstallationsInternal } from '@firebase/installations-types-exp';
31+
import { removeGtagScript } from '../testing/gtag-script-util';
3132

3233
const fakeMeasurementId = 'abcd-efgh-ijkl';
3334
const fakeFid = 'fid-1234-zyxw';
@@ -60,6 +61,7 @@ describe('initializeIds()', () => {
6061
});
6162
afterEach(() => {
6263
fetchStub.restore();
64+
removeGtagScript();
6365
});
6466
it('gets FID and measurement ID and calls gtag config with them', async () => {
6567
stubFetch();
@@ -68,7 +70,8 @@ describe('initializeIds()', () => {
6870
dynamicPromisesList,
6971
measurementIdToAppId,
7072
fakeInstallations,
71-
gtagStub
73+
gtagStub,
74+
'dataLayer'
7275
);
7376
expect(gtagStub).to.be.calledWith(GtagCommand.CONFIG, fakeMeasurementId, {
7477
'firebase_id': fakeFid,
@@ -83,7 +86,8 @@ describe('initializeIds()', () => {
8386
dynamicPromisesList,
8487
measurementIdToAppId,
8588
fakeInstallations,
86-
gtagStub
89+
gtagStub,
90+
'dataLayer'
8791
);
8892
const dynamicPromiseResult = await dynamicPromisesList[0];
8993
expect(dynamicPromiseResult.measurementId).to.equal(fakeMeasurementId);
@@ -96,7 +100,8 @@ describe('initializeIds()', () => {
96100
dynamicPromisesList,
97101
measurementIdToAppId,
98102
fakeInstallations,
99-
gtagStub
103+
gtagStub,
104+
'dataLayer'
100105
);
101106
expect(measurementIdToAppId[fakeMeasurementId]).to.equal(fakeAppId);
102107
});
@@ -108,7 +113,8 @@ describe('initializeIds()', () => {
108113
dynamicPromisesList,
109114
measurementIdToAppId,
110115
fakeInstallations,
111-
gtagStub
116+
gtagStub,
117+
'dataLayer'
112118
);
113119
expect(consoleStub.args[0][1]).to.include(fakeMeasurementId);
114120
expect(consoleStub.args[0][1]).to.include('old-measurement-id');

packages-exp/analytics-exp/src/initialize-analytics.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
validateIndexedDBOpenable
3131
} from '@firebase/util';
3232
import { ERROR_FACTORY, AnalyticsError } from './errors';
33+
import { findGtagScriptOnPage, insertScriptTag } from './helpers';
3334

3435
async function validateIndexedDB(): Promise<boolean> {
3536
if (!isIndexedDBAvailable()) {
@@ -74,7 +75,8 @@ export async function initializeAnalytics(
7475
>,
7576
measurementIdToAppId: { [key: string]: string },
7677
installations: _FirebaseInstallationsInternal,
77-
gtagCore: Gtag
78+
gtagCore: Gtag,
79+
dataLayerName: string
7880
): Promise<string> {
7981
const dynamicConfigPromise = fetchDynamicConfigWithRetry(app);
8082
// Once fetched, map measurementIds to appId, for ease of lookup in wrapped gtag function.
@@ -113,6 +115,11 @@ export async function initializeAnalytics(
113115
fidPromise
114116
]);
115117

118+
// Detect if user has already put the gtag <script> tag on this page.
119+
if (!findGtagScriptOnPage()) {
120+
insertScriptTag(dataLayerName, dynamicConfig.measurementId);
121+
}
122+
116123
// This command initializes gtag.js and only needs to be called once for the entire web app,
117124
// but since it is idempotent, we can call it multiple times.
118125
// We keep it together with other initialization logic for better code structure.
@@ -131,6 +138,8 @@ export async function initializeAnalytics(
131138

132139
// It should be the first config command called on this GA-ID
133140
// Initialize this GA-ID and set FID on it using the gtag config API.
141+
// Note: This will trigger a page_view event unless 'send_page_view' is set to false in
142+
// `configProperties`.
134143
gtagCore(GtagCommand.CONFIG, dynamicConfig.measurementId, configProperties);
135144
return dynamicConfig.measurementId;
136145
}

0 commit comments

Comments
 (0)