From a10daf123ad73e0d827deaa2daf81fb5835e9702 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 13 May 2024 11:06:41 -0400 Subject: [PATCH 1/8] Ensure 'update' string reaches gtagCore. Include the consent string ('update') in gtagCore calls that update consent. --- packages/analytics/src/helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/analytics/src/helpers.ts b/packages/analytics/src/helpers.ts index 7f9582900c2..0fbb934eab2 100644 --- a/packages/analytics/src/helpers.ts +++ b/packages/analytics/src/helpers.ts @@ -304,8 +304,8 @@ function wrapGtag( gtagParams as GtagConfigOrEventParams ); } else if (command === GtagCommand.CONSENT) { - const [gtagParams] = args; - gtagCore(GtagCommand.CONSENT, 'update', gtagParams as ConsentSettings); + const [consentString, gtagParams] = args; + gtagCore(GtagCommand.CONSENT, consentString, gtagParams as ConsentSettings); } else if (command === GtagCommand.GET) { const [measurementId, fieldName, callback] = args; gtagCore( From 6b09d6b9e514e87c119d3e000d8489c230982b9a Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 13 May 2024 11:23:59 -0400 Subject: [PATCH 2/8] Changeset --- .changeset/cold-brooms-run.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cold-brooms-run.md diff --git a/.changeset/cold-brooms-run.md b/.changeset/cold-brooms-run.md new file mode 100644 index 00000000000..9ee6a5d1760 --- /dev/null +++ b/.changeset/cold-brooms-run.md @@ -0,0 +1,5 @@ +--- +'@firebase/analytics': minor +--- + +Analytics - fixed an issue where setConsent was clobbering the consentSettings before passing them to the gtag implementation. From 3f20114fc20ec56ddb657c32a86c30ee6a77f374 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 13 May 2024 11:29:49 -0400 Subject: [PATCH 3/8] format. --- packages/analytics/src/helpers.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/analytics/src/helpers.ts b/packages/analytics/src/helpers.ts index 0fbb934eab2..bceb1c3a8f6 100644 --- a/packages/analytics/src/helpers.ts +++ b/packages/analytics/src/helpers.ts @@ -305,7 +305,11 @@ function wrapGtag( ); } else if (command === GtagCommand.CONSENT) { const [consentString, gtagParams] = args; - gtagCore(GtagCommand.CONSENT, consentString, gtagParams as ConsentSettings); + gtagCore( + GtagCommand.CONSENT, + consentString, + gtagParams as ConsentSettings + ); } else if (command === GtagCommand.GET) { const [measurementId, fieldName, callback] = args; gtagCore( From 60ddfd5fcd9ef2818fbc9e7e40fcc7a194005345 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Mon, 13 May 2024 11:33:11 -0400 Subject: [PATCH 4/8] patch, not minor --- .changeset/cold-brooms-run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/cold-brooms-run.md b/.changeset/cold-brooms-run.md index 9ee6a5d1760..acedccf16cb 100644 --- a/.changeset/cold-brooms-run.md +++ b/.changeset/cold-brooms-run.md @@ -1,5 +1,5 @@ --- -'@firebase/analytics': minor +'@firebase/analytics': patch --- Analytics - fixed an issue where setConsent was clobbering the consentSettings before passing them to the gtag implementation. From 6e63d10b81b6212553c1141bc017fe252ac0061f Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Tue, 14 May 2024 18:54:44 -0400 Subject: [PATCH 5/8] added higher fidelity tests to helpers.test.ts --- packages/analytics/src/helpers.test.ts | 97 ++++++++++++++++++++------ 1 file changed, 75 insertions(+), 22 deletions(-) diff --git a/packages/analytics/src/helpers.test.ts b/packages/analytics/src/helpers.test.ts index b35a3f6867b..095072a4c9c 100644 --- a/packages/analytics/src/helpers.test.ts +++ b/packages/analytics/src/helpers.test.ts @@ -175,10 +175,11 @@ describe('Gtag wrapping functions', () => { 'gtag' ); window['dataLayer'] = []; - (window['gtag'] as Gtag)(GtagCommand.EVENT, 'purchase', { + const eventObject = { 'transaction_id': 'abcd123', 'send_to': 'some_group' - }); + }; + (window['gtag'] as Gtag)(GtagCommand.EVENT, 'purchase', eventObject); expect((window['dataLayer'] as DataLayer).length).to.equal(0); initPromise1.resolve(fakeMeasurementId); // Resolves first initialization promise. @@ -187,8 +188,12 @@ describe('Gtag wrapping functions', () => { initPromise2.resolve('other-measurement-id'); // Resolves second initialization promise. await Promise.all([initPromise1, initPromise2]); // Wait for resolution of Promise.all() await promiseAllSettled(fakeDynamicConfigPromises); - - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('event'); + expect(data['1']).to.equal('purchase'); + expect(data['2']).to.equal(eventObject); }); it( @@ -208,10 +213,11 @@ describe('Gtag wrapping functions', () => { 'gtag' ); window['dataLayer'] = []; - (window['gtag'] as Gtag)(GtagCommand.EVENT, 'purchase', { + const eventObject = { 'transaction_id': 'abcd123', 'send_to': [fakeMeasurementId, 'some_group'] - }); + }; + (window['gtag'] as Gtag)(GtagCommand.EVENT, 'purchase', eventObject); expect((window['dataLayer'] as DataLayer).length).to.equal(0); initPromise1.resolve(); // Resolves first initialization promise. @@ -221,7 +227,12 @@ describe('Gtag wrapping functions', () => { await Promise.all([initPromise1, initPromise2]); // Wait for resolution of Promise.all() await promiseAllSettled(fakeDynamicConfigPromises); - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('event'); + expect(data['1']).to.equal('purchase'); + expect(data['2']).to.equal(eventObject); } ); @@ -242,9 +253,10 @@ describe('Gtag wrapping functions', () => { 'gtag' ); window['dataLayer'] = []; - (window['gtag'] as Gtag)(GtagCommand.EVENT, 'purchase', { + const eventObject = { 'transaction_id': 'abcd123' - }); + }; + (window['gtag'] as Gtag)(GtagCommand.EVENT, 'purchase', eventObject); expect((window['dataLayer'] as DataLayer).length).to.equal(0); initPromise1.resolve(); // Resolves first initialization promise. @@ -253,7 +265,12 @@ describe('Gtag wrapping functions', () => { initPromise2.resolve(); // Resolves second initialization promise. await Promise.all([initPromise1, initPromise2]); // Wait for resolution of Promise.all() - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('event'); + expect(data['1']).to.equal('purchase'); + expect(data['2']).to.equal(eventObject); } ); @@ -274,17 +291,23 @@ describe('Gtag wrapping functions', () => { 'gtag' ); window['dataLayer'] = []; - (window['gtag'] as Gtag)(GtagCommand.EVENT, 'purchase', { + const eventObject = { 'transaction_id': 'abcd123', 'send_to': fakeMeasurementId - }); + }; + (window['gtag'] as Gtag)(GtagCommand.EVENT, 'purchase', eventObject); expect((window['dataLayer'] as DataLayer).length).to.equal(0); initPromise1.resolve(); // Resolves first initialization promise. await promiseAllSettled(fakeDynamicConfigPromises); await Promise.all([initPromise1]); // Wait for resolution of Promise.all() - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('event'); + expect(data['1']).to.equal('purchase'); + expect(data['2']).to.equal(eventObject); } ); @@ -307,8 +330,13 @@ describe('Gtag wrapping functions', () => { 'gtag' ); window['dataLayer'] = []; - (window['gtag'] as Gtag)(GtagCommand.SET, { 'language': 'en' }); - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const eventObject = { 'language': 'en' }; + (window['gtag'] as Gtag)(GtagCommand.SET, eventObject); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('set'); + expect(data['1']).to.equal(eventObject); }); it('new window.gtag function does not wait when sending "consent" calls', async () => { @@ -329,7 +357,12 @@ describe('Gtag wrapping functions', () => { 'update', consentParameters ); - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('consent'); + expect(data['1']).to.equal('update'); + expect(data['2']).to.equal(consentParameters); }); it('new window.gtag function does not wait when sending "get" calls', async () => { @@ -347,7 +380,13 @@ describe('Gtag wrapping functions', () => { 'client_id', clientId => console.log(clientId) ); - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('get'); + expect(data['1']).to.equal(fakeMeasurementId); + expect(data['2']).to.equal('client_id'); + expect(data['3']).to.not.be.undefined; }); it('new window.gtag function does not wait when sending an unknown command', async () => { @@ -360,7 +399,11 @@ describe('Gtag wrapping functions', () => { ); window['dataLayer'] = []; (window['gtag'] as Gtag)('new-command-from-gtag-team', fakeMeasurementId); - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('new-command-from-gtag-team'); + expect(data['1']).to.equal(fakeMeasurementId); }); it('new window.gtag function waits for initialization promise when sending "config" calls', async () => { @@ -383,19 +426,29 @@ describe('Gtag wrapping functions', () => { expect((window['dataLayer'] as DataLayer).length).to.equal(0); await Promise.all([initPromise1]); // Wait for resolution of Promise.all() - + console.log('DEDB datalayer:', window['dataLayer']); expect((window['dataLayer'] as DataLayer).length).to.equal(1); }); it('new window.gtag function does not wait when sending "config" calls if there are no pending initialization promises', async () => { wrapOrCreateGtag({}, fakeDynamicConfigPromises, {}, 'dataLayer', 'gtag'); window['dataLayer'] = []; - (window['gtag'] as Gtag)(GtagCommand.CONFIG, fakeMeasurementId, { + const eventObject = { 'transaction_id': 'abcd123' - }); + }; + (window['gtag'] as Gtag)( + GtagCommand.CONFIG, + fakeMeasurementId, + eventObject + ); await promiseAllSettled(fakeDynamicConfigPromises); await Promise.resolve(); // Config call is always chained onto initialization promise list, even if empty. - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('config'); + expect(data['1']).to.equal(fakeMeasurementId); + expect(data['2']).to.equal(eventObject); }); }); From 5ea29898d6e39028e9c7c3f34f108890cecf6a31 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Wed, 15 May 2024 09:45:53 -0400 Subject: [PATCH 6/8] two more tests. --- packages/analytics/src/helpers.test.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/analytics/src/helpers.test.ts b/packages/analytics/src/helpers.test.ts index 095072a4c9c..e47fa828a22 100644 --- a/packages/analytics/src/helpers.test.ts +++ b/packages/analytics/src/helpers.test.ts @@ -416,9 +416,14 @@ describe('Gtag wrapping functions', () => { 'gtag' ); window['dataLayer'] = []; - (window['gtag'] as Gtag)(GtagCommand.CONFIG, fakeMeasurementId, { + const eventObject = { 'language': 'en' - }); + }; + (window['gtag'] as Gtag)( + GtagCommand.CONFIG, + fakeMeasurementId, + eventObject + ); expect((window['dataLayer'] as DataLayer).length).to.equal(0); initPromise1.resolve(fakeMeasurementId); @@ -426,8 +431,12 @@ describe('Gtag wrapping functions', () => { expect((window['dataLayer'] as DataLayer).length).to.equal(0); await Promise.all([initPromise1]); // Wait for resolution of Promise.all() - console.log('DEDB datalayer:', window['dataLayer']); - expect((window['dataLayer'] as DataLayer).length).to.equal(1); + const dataLayer = window['dataLayer'] as DataLayer; + expect(dataLayer.length).to.equal(1); + const data = dataLayer[0]; + expect(data['0']).to.equal('config'); + expect(data['1']).to.equal(fakeMeasurementId); + expect(data['2']).to.equal(eventObject); }); it('new window.gtag function does not wait when sending "config" calls if there are no pending initialization promises', async () => { From 8d25c9818c75b51d7cb8a519996452f298f8ae65 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Wed, 15 May 2024 09:46:03 -0400 Subject: [PATCH 7/8] renamed consentString to consentAction. --- packages/analytics/src/helpers.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/analytics/src/helpers.ts b/packages/analytics/src/helpers.ts index bceb1c3a8f6..2e9a46e03b2 100644 --- a/packages/analytics/src/helpers.ts +++ b/packages/analytics/src/helpers.ts @@ -304,10 +304,11 @@ function wrapGtag( gtagParams as GtagConfigOrEventParams ); } else if (command === GtagCommand.CONSENT) { - const [consentString, gtagParams] = args; + const [consentAction, gtagParams] = args; + // consentAction can be one of 'default' or 'update'. gtagCore( GtagCommand.CONSENT, - consentString, + consentAction, gtagParams as ConsentSettings ); } else if (command === GtagCommand.GET) { From 5e07a73522662ce64dcff9f04b91cf7666477bb0 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Wed, 15 May 2024 13:25:23 -0400 Subject: [PATCH 8/8] Index by number not string --- packages/analytics/src/helpers.test.ts | 58 +++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/packages/analytics/src/helpers.test.ts b/packages/analytics/src/helpers.test.ts index e47fa828a22..ff06ba3ea6c 100644 --- a/packages/analytics/src/helpers.test.ts +++ b/packages/analytics/src/helpers.test.ts @@ -191,9 +191,9 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('event'); - expect(data['1']).to.equal('purchase'); - expect(data['2']).to.equal(eventObject); + expect(data[0]).to.equal('event'); + expect(data[1]).to.equal('purchase'); + expect(data[2]).to.equal(eventObject); }); it( @@ -230,9 +230,9 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('event'); - expect(data['1']).to.equal('purchase'); - expect(data['2']).to.equal(eventObject); + expect(data[0]).to.equal('event'); + expect(data[1]).to.equal('purchase'); + expect(data[2]).to.equal(eventObject); } ); @@ -268,9 +268,9 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('event'); - expect(data['1']).to.equal('purchase'); - expect(data['2']).to.equal(eventObject); + expect(data[0]).to.equal('event'); + expect(data[1]).to.equal('purchase'); + expect(data[2]).to.equal(eventObject); } ); @@ -305,9 +305,9 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('event'); - expect(data['1']).to.equal('purchase'); - expect(data['2']).to.equal(eventObject); + expect(data[0]).to.equal('event'); + expect(data[1]).to.equal('purchase'); + expect(data[2]).to.equal(eventObject); } ); @@ -335,8 +335,8 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('set'); - expect(data['1']).to.equal(eventObject); + expect(data[0]).to.equal('set'); + expect(data[1]).to.equal(eventObject); }); it('new window.gtag function does not wait when sending "consent" calls', async () => { @@ -360,9 +360,9 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('consent'); - expect(data['1']).to.equal('update'); - expect(data['2']).to.equal(consentParameters); + expect(data[0]).to.equal('consent'); + expect(data[1]).to.equal('update'); + expect(data[2]).to.equal(consentParameters); }); it('new window.gtag function does not wait when sending "get" calls', async () => { @@ -383,10 +383,10 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('get'); - expect(data['1']).to.equal(fakeMeasurementId); - expect(data['2']).to.equal('client_id'); - expect(data['3']).to.not.be.undefined; + expect(data[0]).to.equal('get'); + expect(data[1]).to.equal(fakeMeasurementId); + expect(data[2]).to.equal('client_id'); + expect(data[3]).to.not.be.undefined; }); it('new window.gtag function does not wait when sending an unknown command', async () => { @@ -402,8 +402,8 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('new-command-from-gtag-team'); - expect(data['1']).to.equal(fakeMeasurementId); + expect(data[0]).to.equal('new-command-from-gtag-team'); + expect(data[1]).to.equal(fakeMeasurementId); }); it('new window.gtag function waits for initialization promise when sending "config" calls', async () => { @@ -434,9 +434,9 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('config'); - expect(data['1']).to.equal(fakeMeasurementId); - expect(data['2']).to.equal(eventObject); + expect(data[0]).to.equal('config'); + expect(data[1]).to.equal(fakeMeasurementId); + expect(data[2]).to.equal(eventObject); }); it('new window.gtag function does not wait when sending "config" calls if there are no pending initialization promises', async () => { @@ -455,9 +455,9 @@ describe('Gtag wrapping functions', () => { const dataLayer = window['dataLayer'] as DataLayer; expect(dataLayer.length).to.equal(1); const data = dataLayer[0]; - expect(data['0']).to.equal('config'); - expect(data['1']).to.equal(fakeMeasurementId); - expect(data['2']).to.equal(eventObject); + expect(data[0]).to.equal('config'); + expect(data[1]).to.equal(fakeMeasurementId); + expect(data[2]).to.equal(eventObject); }); });