From c1cc257912b35b0683ea560fa1efa275b14d1432 Mon Sep 17 00:00:00 2001 From: martynmjones Date: Wed, 13 Mar 2024 07:40:30 +0000 Subject: [PATCH 01/25] Setup global site tag in head --- includes/class-wc-google-gtag-js.php | 71 +++++++++++++++------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index b0f3e4a5..2d121645 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -49,10 +49,40 @@ public function __construct( $settings = array() ) { $this->map_hooks(); // Setup frontend scripts + add_action( 'wp_head', array( $this, 'setup_site_tag' ), 2 ); add_action( 'wp_enqueue_scripts', array( $this, 'enquque_tracker' ), 5 ); add_action( 'wp_footer', array( $this, 'inline_script_data' ) ); } + /** + * Setup the global site tag as early as possible on the page + * + * @return void + */ + public function setup_site_tag() { + $tracker_function = self::tracker_function_name(); + $default_consents = json_encode( self::get_consent_modes() ); + $ga_id = self::get( 'ga_id' ); + $developer_id = self::DEVELOPER_ID; + $config = $this->get_site_tag_config(); + + echo << + + + HTML; + } + /** * Register tracker scripts and its inline config. * We need to execute tracker.js w/ `gtag` configuration before any trackable action may happen. @@ -60,41 +90,28 @@ public function __construct( $settings = array() ) { * @return void */ public function enquque_tracker(): void { - wp_enqueue_script( + // Although we're not enqueuing the Google Tag Manager script the WordPress way, it still + // needs to be registered to prevent WooCommerce core from enqueueing the script. + wp_register_script( 'google-tag-manager', 'https://www.googletagmanager.com/gtag/js?id=' . self::get( 'ga_id' ), array(), null, - array( - 'strategy' => 'async', - ) ); - // tracker.js needs to be executed ASAP, the remaining bits for main.js could be deffered, - // but to reduce the traffic, we ship it all together. + wp_enqueue_script( $this->script_handle, Plugin::get_instance()->get_js_asset_url( 'main.js' ), array( ...Plugin::get_instance()->get_js_asset_dependencies( 'main' ), - 'google-tag-manager', ), Plugin::get_instance()->get_js_asset_version( 'main' ), true ); - // Provide tracker's configuration. - wp_add_inline_script( - $this->script_handle, - sprintf( - 'var wcgai = {config: %s};', - wp_json_encode( $this->get_analytics_config() ) - ), - 'before' - ); } /** - * Feed classic tracking with event data via inline script. - * Make sure it's added at the bottom of the page, so all the data is collected. + * Add all event data via an inline script in the footer to ensure all the data is collected in time. * * @return void */ @@ -112,7 +129,7 @@ public function inline_script_data(): void { wp_add_inline_script( $this->data_script_handle, sprintf( - 'wcgai.trackClassicPages( %s );', + 'var wcgaiData = %s;', $this->get_script_data() ) ); @@ -214,10 +231,8 @@ public static function tracker_function_name(): string { * * @return array */ - public function get_analytics_config(): array { - $defaults = array( - 'gtag_id' => self::get( 'ga_id' ), - 'tracker_function_name' => self::tracker_function_name(), + public function get_site_tag_config(): string { + return json_encode( array( 'track_404' => 'yes' === self::get( 'ga_404_tracking_enabled' ), 'allow_google_signals' => 'yes' === self::get( 'ga_support_display_advertising' ), 'logged_in' => is_user_logged_in(), @@ -228,15 +243,7 @@ public function get_analytics_config(): array { 'custom_map' => array( 'dimension1' => 'logged_in', ), - 'events' => self::get_enabled_events(), - 'identifier' => self::get( 'ga_product_identifier' ), - 'consent_modes' => self::get_consent_modes(), - ); - - $config = apply_filters( 'woocommerce_ga_gtag_config', $defaults ); - $config['developer_id'] = self::DEVELOPER_ID; - - return $config; + ) ); } /** From 0a3af1ed87601e61339e91c7eacfb759873addf6 Mon Sep 17 00:00:00 2001 From: martynmjones Date: Wed, 13 Mar 2024 10:03:50 +0000 Subject: [PATCH 02/25] Wait until DOMContentLoaded before tracking --- assets/js/src/config.js | 6 ++- assets/js/src/index.js | 16 +++++-- assets/js/src/integrations/blocks.js | 68 ++++++++++++++------------- assets/js/src/integrations/classic.js | 36 ++++++-------- assets/js/src/tracker/index.js | 32 +++---------- includes/class-wc-google-gtag-js.php | 10 +++- 6 files changed, 80 insertions(+), 88 deletions(-) diff --git a/assets/js/src/config.js b/assets/js/src/config.js index 0477cb8b..10d3e3b6 100644 --- a/assets/js/src/config.js +++ b/assets/js/src/config.js @@ -1,2 +1,4 @@ -/* global wcgai */ -export const config = wcgai.config; +/* global ga4wData */ +export const config = () => { + return window.ga4wData; +}; diff --git a/assets/js/src/index.js b/assets/js/src/index.js index 7f198bf2..b09e81be 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -1,6 +1,12 @@ -// Initialize tracking for classic WooCommerce pages -import { trackClassicPages } from './integrations/classic'; -window.wcgai.trackClassicPages = trackClassicPages; +import { config } from './config'; +import { classicTracking } from './integrations/classic'; +import { blocksTracking } from './integrations/blocks'; -// Initialize tracking for Block based WooCommerce pages -import './integrations/blocks'; +document.addEventListener( 'DOMContentLoaded', () => { + if ( ! config() ) { + throw new Error( 'Google Analytics for WooCommerce: Configuration and tracking data not found.' ); + } + + classicTracking(); + blocksTracking(); +}); \ No newline at end of file diff --git a/assets/js/src/integrations/blocks.js b/assets/js/src/integrations/blocks.js index d9503f1e..a103b345 100644 --- a/assets/js/src/integrations/blocks.js +++ b/assets/js/src/integrations/blocks.js @@ -3,44 +3,46 @@ import { addUniqueAction } from '../utils'; import { tracker } from '../tracker'; import { ACTION_PREFIX, NAMESPACE } from '../constants'; -addUniqueAction( - `${ ACTION_PREFIX }-product-render`, - NAMESPACE, - tracker.eventHandler( 'view_item' ) -); +export const blocksTracking = () => { + addUniqueAction( + `${ ACTION_PREFIX }-product-render`, + NAMESPACE, + tracker.eventHandler( 'view_item' ) + ); -addUniqueAction( - `${ ACTION_PREFIX }-cart-remove-item`, - NAMESPACE, - tracker.eventHandler( 'remove_from_cart' ) -); + addUniqueAction( + `${ ACTION_PREFIX }-cart-remove-item`, + NAMESPACE, + tracker.eventHandler( 'remove_from_cart' ) + ); -addUniqueAction( - `${ ACTION_PREFIX }-checkout-render-checkout-form`, - NAMESPACE, - tracker.eventHandler( 'begin_checkout' ) -); + addUniqueAction( + `${ ACTION_PREFIX }-checkout-render-checkout-form`, + NAMESPACE, + tracker.eventHandler( 'begin_checkout' ) + ); -// These actions only works for All Products Block -addUniqueAction( - `${ ACTION_PREFIX }-cart-add-item`, - NAMESPACE, - ( { product } ) => { - tracker.eventHandler( 'add_to_cart' )( { product } ); - } -); + // These actions only works for All Products Block + addUniqueAction( + `${ ACTION_PREFIX }-cart-add-item`, + NAMESPACE, + ( { product } ) => { + tracker.eventHandler( 'add_to_cart' )( { product } ); + } + ); -addUniqueAction( - `${ ACTION_PREFIX }-product-list-render`, - NAMESPACE, - tracker.eventHandler( 'view_item_list' ) -); + addUniqueAction( + `${ ACTION_PREFIX }-product-list-render`, + NAMESPACE, + tracker.eventHandler( 'view_item_list' ) + ); -addUniqueAction( - `${ ACTION_PREFIX }-product-view-link`, - NAMESPACE, - tracker.eventHandler( 'select_content' ) -); + addUniqueAction( + `${ ACTION_PREFIX }-product-view-link`, + NAMESPACE, + tracker.eventHandler( 'select_content' ) + ); +}; /** * Remove additional actions added by WooCommerce Core which are either diff --git a/assets/js/src/integrations/classic.js b/assets/js/src/integrations/classic.js index 020155be..53d20ce0 100644 --- a/assets/js/src/integrations/classic.js +++ b/assets/js/src/integrations/classic.js @@ -1,5 +1,6 @@ import { tracker } from '../tracker'; import { getProductFromID } from '../utils'; +import { config } from '../config'; /** * The Google Analytics integration for classic WooCommerce pages @@ -12,35 +13,28 @@ import { getProductFromID } from '../utils'; * To be executed once data set is complete, and `document` is ready. * * It also handles some Block events that are not fired reliably for `woocommerce/all-products` block. - * - * @param {Object} data - The tracking data from the current page load, containing the following properties: - * @param {Object} data.events - An object containing the events to be instantly tracked. - * @param {Object} data.cart - The cart object. - * @param {Object[]} data.products - An array of all product from the current page. - * @param {Object} data.product - The single product object. - * @param {Object} data.added_to_cart - The product added to cart. - * @param {Object} data.order - The order object. */ -export function trackClassicPages( { - events, - cart, - products, - product, - added_to_cart: addedToCart, - order, -} ) { - // Instantly track the events listed in the `events` object. - const eventData = { - storeCart: cart, +export function classicTracking() { + const { + events, + cart, products, product, + addedToCart, order, - }; + } = config(); + + // Instantly track the events listed in the `events` object. Object.values( events ?? {} ).forEach( ( eventName ) => { if ( eventName === 'add_to_cart' ) { tracker.eventHandler( eventName )( { product: addedToCart } ); } else { - tracker.eventHandler( eventName )( eventData ); + tracker.eventHandler( eventName )( { + storeCart: cart, + products, + product, + order, + } ); } } ); diff --git a/assets/js/src/tracker/index.js b/assets/js/src/tracker/index.js index 671418c8..5115428e 100644 --- a/assets/js/src/tracker/index.js +++ b/assets/js/src/tracker/index.js @@ -19,30 +19,6 @@ class Tracker { throw new Error( 'Cannot instantiate more than one Tracker' ); } instance = this; - - window.dataLayer = window.dataLayer || []; - - function gtag() { - window.dataLayer.push( arguments ); - } - - window[ config.tracker_function_name ] = gtag; - - // Set up default consent state, denying all for EEA visitors. - for ( const mode of config.consent_modes || [] ) { - gtag( 'consent', 'default', mode ); - } - - gtag( 'js', new Date() ); - gtag( 'set', `developer_id.${ config.developer_id }`, true ); - gtag( 'config', config.gtag_id, { - allow_google_signals: config.allow_google_signals, - link_attribution: config.link_attribution, - anonymize_ip: config.anonymize_ip, - logged_in: config.logged_in, - linker: config.linker, - custom_map: config.custom_map, - } ); } /** @@ -53,6 +29,10 @@ class Tracker { * @throws {Error} If the event name is not supported. */ eventHandler( name ) { + if ( ! config() ) { + throw new Error( 'Google Analytics for WooCommerce: eventHandler called too early' ); + } + /* eslint import/namespace: [ 'error', { allowComputed: true } ] */ const formatter = formatters[ name ]; if ( typeof formatter !== 'function' ) { @@ -61,8 +41,8 @@ class Tracker { return function trackerEventHandler( data ) { const eventData = formatter( data ); - if ( config.events.includes( name ) && eventData ) { - window[ config.tracker_function_name ]( + if ( config().settings.events.includes( name ) && eventData ) { + window[ config().settings.tracker_function_name ]( 'event', name, eventData diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index 2d121645..a8fbefab 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -116,6 +116,14 @@ public function enquque_tracker(): void { * @return void */ public function inline_script_data(): void { + $this->set_script_data( + 'settings', + array( + 'tracker_function_name' => self::tracker_function_name(), + 'events' => $this->get_enabled_events() + ) + ); + wp_register_script( $this->data_script_handle, '', @@ -129,7 +137,7 @@ public function inline_script_data(): void { wp_add_inline_script( $this->data_script_handle, sprintf( - 'var wcgaiData = %s;', + 'var ga4wData = %s;', $this->get_script_data() ) ); From ac06b6dcb8df09a790148afbe6d4f13d370f3726 Mon Sep 17 00:00:00 2001 From: martynmjones Date: Wed, 13 Mar 2024 10:22:02 +0000 Subject: [PATCH 03/25] CS --- assets/js/src/config.js | 3 +- assets/js/src/index.js | 14 +++--- assets/js/src/integrations/classic.js | 9 +--- assets/js/src/tracker/index.js | 4 +- includes/class-wc-google-gtag-js.php | 62 ++++++++++++++------------- 5 files changed, 45 insertions(+), 47 deletions(-) diff --git a/assets/js/src/config.js b/assets/js/src/config.js index 10d3e3b6..1d5db7ce 100644 --- a/assets/js/src/config.js +++ b/assets/js/src/config.js @@ -1,4 +1,3 @@ -/* global ga4wData */ export const config = () => { - return window.ga4wData; + return window.ga4wData; // esling-disable-line no-unused-vars }; diff --git a/assets/js/src/index.js b/assets/js/src/index.js index b09e81be..94cade1b 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -3,10 +3,12 @@ import { classicTracking } from './integrations/classic'; import { blocksTracking } from './integrations/blocks'; document.addEventListener( 'DOMContentLoaded', () => { - if ( ! config() ) { - throw new Error( 'Google Analytics for WooCommerce: Configuration and tracking data not found.' ); - } + if ( ! config() ) { + throw new Error( + 'Google Analytics for WooCommerce: Configuration and tracking data not found.' + ); + } - classicTracking(); - blocksTracking(); -}); \ No newline at end of file + classicTracking(); + blocksTracking(); +} ); diff --git a/assets/js/src/integrations/classic.js b/assets/js/src/integrations/classic.js index 53d20ce0..12cb1b44 100644 --- a/assets/js/src/integrations/classic.js +++ b/assets/js/src/integrations/classic.js @@ -15,14 +15,7 @@ import { config } from '../config'; * It also handles some Block events that are not fired reliably for `woocommerce/all-products` block. */ export function classicTracking() { - const { - events, - cart, - products, - product, - addedToCart, - order, - } = config(); + const { events, cart, products, product, addedToCart, order } = config(); // Instantly track the events listed in the `events` object. Object.values( events ?? {} ).forEach( ( eventName ) => { diff --git a/assets/js/src/tracker/index.js b/assets/js/src/tracker/index.js index 5115428e..af45365e 100644 --- a/assets/js/src/tracker/index.js +++ b/assets/js/src/tracker/index.js @@ -30,7 +30,9 @@ class Tracker { */ eventHandler( name ) { if ( ! config() ) { - throw new Error( 'Google Analytics for WooCommerce: eventHandler called too early' ); + throw new Error( + 'Google Analytics for WooCommerce: eventHandler called too early' + ); } /* eslint import/namespace: [ 'error', { allowComputed: true } ] */ diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index a8fbefab..df67b284 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -60,27 +60,28 @@ public function __construct( $settings = array() ) { * @return void */ public function setup_site_tag() { - $tracker_function = self::tracker_function_name(); - $default_consents = json_encode( self::get_consent_modes() ); - $ga_id = self::get( 'ga_id' ); - $developer_id = self::DEVELOPER_ID; - $config = $this->get_site_tag_config(); - - echo << - - - HTML; + // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript + printf( + ' + + ', + esc_js( self::get( 'ga_id' ) ), + esc_js( self::tracker_function_name() ), + esc_js( self::DEVELOPER_ID ), + json_encode( self::get_consent_modes() ), + json_encode( $this->get_site_tag_config() ) + ); + // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedScript } /** @@ -97,6 +98,7 @@ public function enquque_tracker(): void { 'https://www.googletagmanager.com/gtag/js?id=' . self::get( 'ga_id' ), array(), null, + false ); wp_enqueue_script( @@ -120,7 +122,7 @@ public function inline_script_data(): void { 'settings', array( 'tracker_function_name' => self::tracker_function_name(), - 'events' => $this->get_enabled_events() + 'events' => $this->get_enabled_events(), ) ); @@ -239,19 +241,19 @@ public static function tracker_function_name(): string { * * @return array */ - public function get_site_tag_config(): string { - return json_encode( array( - 'track_404' => 'yes' === self::get( 'ga_404_tracking_enabled' ), - 'allow_google_signals' => 'yes' === self::get( 'ga_support_display_advertising' ), - 'logged_in' => is_user_logged_in(), - 'linker' => array( + public function get_site_tag_config(): array { + return array( + 'track_404' => 'yes' === self::get( 'ga_404_tracking_enabled' ), + 'allow_google_signals' => 'yes' === self::get( 'ga_support_display_advertising' ), + 'logged_in' => is_user_logged_in(), + 'linker' => array( 'domains' => ! empty( self::get( 'ga_linker_cross_domains' ) ) ? array_map( 'esc_js', explode( ',', self::get( 'ga_linker_cross_domains' ) ) ) : array(), 'allow_incoming' => 'yes' === self::get( 'ga_linker_allow_incoming_enabled' ), ), - 'custom_map' => array( + 'custom_map' => array( 'dimension1' => 'logged_in', ), - ) ); + ); } /** From d064dfb55f56c85227bb7168f35e00c3bedaf566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Wytr=C4=99bowicz?= Date: Wed, 13 Mar 2024 17:55:14 +0100 Subject: [PATCH 04/25] Initialize instantly, if the document is already loaded. --- assets/js/src/index.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/assets/js/src/index.js b/assets/js/src/index.js index 94cade1b..e17b4b40 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -2,7 +2,16 @@ import { config } from './config'; import { classicTracking } from './integrations/classic'; import { blocksTracking } from './integrations/blocks'; -document.addEventListener( 'DOMContentLoaded', () => { +// Wait for DOMContentLoaded to make sure event data is in place. +if ( document.readyState === 'loading' ) { + document.addEventListener( + 'DOMContentLoaded', + eventuallyInitializeTracking + ); +} else { + eventuallyInitializeTracking(); +} +function eventuallyInitializeTracking() { if ( ! config() ) { throw new Error( 'Google Analytics for WooCommerce: Configuration and tracking data not found.' @@ -11,4 +20,4 @@ document.addEventListener( 'DOMContentLoaded', () => { classicTracking(); blocksTracking(); -} ); +} From f3fe7b4b1babf89ee4c2d98c905c4b468e968d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Wytr=C4=99bowicz?= Date: Wed, 13 Mar 2024 17:58:39 +0100 Subject: [PATCH 05/25] Add code doc explaining why we wait to add block actions --- assets/js/src/integrations/blocks.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/js/src/integrations/blocks.js b/assets/js/src/integrations/blocks.js index a103b345..6217a5ca 100644 --- a/assets/js/src/integrations/blocks.js +++ b/assets/js/src/integrations/blocks.js @@ -3,6 +3,7 @@ import { addUniqueAction } from '../utils'; import { tracker } from '../tracker'; import { ACTION_PREFIX, NAMESPACE } from '../constants'; +// We add actions asynchronosly, to make sure handlares will have the config available. export const blocksTracking = () => { addUniqueAction( `${ ACTION_PREFIX }-product-render`, @@ -44,7 +45,7 @@ export const blocksTracking = () => { ); }; -/** +/* * Remove additional actions added by WooCommerce Core which are either * not supported by Google Analytics for WooCommerce or are redundant * since Google retired Universal Analytics. From 9ed093f64b13a2dedc3bc1dfb2848b4cc84d5325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Wytr=C4=99bowicz?= Date: Wed, 13 Mar 2024 18:02:33 +0100 Subject: [PATCH 06/25] Enqueue tagmanager in regular way, It's loaded async anyway, so we don't need to hack to have it in place for the tracker definition. --- includes/class-wc-google-gtag-js.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index df67b284..06ae010b 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -63,11 +63,10 @@ public function setup_site_tag() { // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript printf( ' - ', + WC_Google_Analytics_Integration::get_instance()->get_js_asset_url( 'main.js' ) + ); + }, + 9999 + ); + break; + } + } +} ); From 25bd09c659d83b8390f2a9840bdb1241cadeae0e Mon Sep 17 00:00:00 2001 From: martynmjones Date: Fri, 22 Mar 2024 09:58:24 +0000 Subject: [PATCH 15/25] Update related products add to cart selector --- tests/e2e/utils/customer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/utils/customer.js b/tests/e2e/utils/customer.js index 1097159a..31d806a9 100644 --- a/tests/e2e/utils/customer.js +++ b/tests/e2e/utils/customer.js @@ -66,8 +66,8 @@ export async function variableProductAddToCart( page, productID ) { */ export async function relatedProductAddToCart( page ) { const addToCart = ( await page.locator( '.related.products' ).isVisible() ) - ? '.related.products .add_to_cart_button' - : '.wp-block-woocommerce-related-products .add_to_cart_button'; + ? '.related.products .add_to_cart_button.product_type_simple' + : '.wp-block-woocommerce-related-products .add_to_cart_button.product_type_simple'; const addToCartButton = await page.locator( addToCart ).first(); await addToCartButton.click(); From 582e41b756e90d38c8b3c1285e057d12536b8e99 Mon Sep 17 00:00:00 2001 From: martynmjones Date: Fri, 22 Mar 2024 10:02:43 +0000 Subject: [PATCH 16/25] Wrap tag setup in wp_print_inline_script_tag --- includes/class-wc-google-gtag-js.php | 38 +++++++++++++--------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index 7c263481..b23619b5 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -60,27 +60,25 @@ public function __construct( $settings = array() ) { * @return void */ public function setup_site_tag() { - // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript - printf( - ' - ', - esc_js( self::get( 'ga_id' ) ), - esc_js( self::tracker_function_name() ), - esc_js( self::DEVELOPER_ID ), - json_encode( self::get_consent_modes() ), - json_encode( $this->get_site_tag_config() ) + wp_print_inline_script_tag( + sprintf( + '/** Google Analytics for WooCommerce (gtag.js) */ + window.dataLayer = window.dataLayer || []; + function %2$s(){dataLayer.push(arguments);} + // Set up default consent state. + for ( const mode of %4$s || [] ) { + %2$s( "consent", "default", mode ); + } + %2$s("js", new Date()); + %2$s("set", "developer_id.%3$s", true); + %2$s("config", "%1$s", %5$s);', + esc_js( self::get( 'ga_id' ) ), + esc_js( self::tracker_function_name() ), + esc_js( self::DEVELOPER_ID ), + json_encode( self::get_consent_modes() ), + json_encode( $this->get_site_tag_config() ) + ) ); - // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedScript } /** From d1629c246c091e21411fee7a1acbe3d791a46e88 Mon Sep 17 00:00:00 2001 From: martynmjones Date: Fri, 22 Mar 2024 10:15:55 +0000 Subject: [PATCH 17/25] CS fixes --- tests/e2e/specs/js-scripts/load-order.test.js | 18 ++-- tests/e2e/test-snippets/test-snippets.php | 85 ++++++++++--------- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/tests/e2e/specs/js-scripts/load-order.test.js b/tests/e2e/specs/js-scripts/load-order.test.js index 88def41f..5f75d7f9 100644 --- a/tests/e2e/specs/js-scripts/load-order.test.js +++ b/tests/e2e/specs/js-scripts/load-order.test.js @@ -18,18 +18,22 @@ test.describe( 'JavaScript file position', () => { await clearSettings(); } ); - test( 'Tracking is functional if main.js is loaded in the header', async ( { page } ) => { + test( 'Tracking is functional if main.js is loaded in the header', async ( { + page, + } ) => { const event = trackGtagEvent( page, 'view_item_list' ); await page.goto( 'shop?move_mainjs_to=head' ); await expect( - page.locator( 'head #woocommerce-google-analytics-integration-head-js' ) + page.locator( + 'head #woocommerce-google-analytics-integration-head-js' + ) ).toBeAttached(); await expect( page.locator( '#woocommerce-google-analytics-integration-js' ) - ).toHaveCount(0); + ).toHaveCount( 0 ); await event.then( ( request ) => { const data = getEventData( request, 'view_item_list' ); @@ -37,13 +41,17 @@ test.describe( 'JavaScript file position', () => { } ); } ); - test( 'Tracking is functional if main.js is loaded after the inline script data', async ( { page } ) => { + test( 'Tracking is functional if main.js is loaded after the inline script data', async ( { + page, + } ) => { const event = trackGtagEvent( page, 'view_item_list' ); await page.goto( 'shop?move_mainjs_to=after_inline_data' ); await expect( - page.locator( '#woocommerce-google-analytics-integration-data-js-after + #woocommerce-google-analytics-integration-js' ) + page.locator( + '#woocommerce-google-analytics-integration-data-js-after + #woocommerce-google-analytics-integration-js' + ) ).toBeAttached(); await event.then( ( request ) => { diff --git a/tests/e2e/test-snippets/test-snippets.php b/tests/e2e/test-snippets/test-snippets.php index 7704859e..2f3b06f2 100644 --- a/tests/e2e/test-snippets/test-snippets.php +++ b/tests/e2e/test-snippets/test-snippets.php @@ -35,46 +35,51 @@ function ( $modes ) { * script is loaded. This is important because some third-party plugins will * change the load order in unexpected ways which has previously caused problems. */ -add_action( 'wp_enqueue_scripts', function() { - if ( isset( $_GET['move_mainjs_to'] ) ) { - // main.js is a dependency of the inline data script so we need to make sure it doesn't load - add_filter( - 'script_loader_src', - function ( $src, $handle ) { - if ( $handle === WC_Google_Gtag_JS::get_instance()->script_handle ) { - $src = ''; - } - return $src; - }, - 10, - 2 - ); +add_action( + 'wp_enqueue_scripts', + function () { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( isset( $_GET['move_mainjs_to'] ) ) { + // main.js is a dependency of the inline data script so we need to make sure it doesn't load + add_filter( + 'script_loader_src', + function ( $src, $handle ) { + if ( $handle === WC_Google_Gtag_JS::get_instance()->script_handle ) { + $src = ''; + } + return $src; + }, + 10, + 2 + ); - switch( $_GET['move_mainjs_to'] ) { - case 'head': - wp_enqueue_script( - WC_Google_Gtag_JS::get_instance()->script_handle .'-head', - WC_Google_Analytics_Integration::get_instance()->get_js_asset_url( 'main.js' ), - array( - ...WC_Google_Analytics_Integration::get_instance()->get_js_asset_dependencies( 'main' ), - 'google-tag-manager', - ), - WC_Google_Analytics_Integration::get_instance()->get_js_asset_version( 'main' ), - false - ); - break; - case 'after_inline_data': - add_action( - 'wp_footer', - function() { - printf( - '', - WC_Google_Analytics_Integration::get_instance()->get_js_asset_url( 'main.js' ) - ); - }, - 9999 - ); - break; + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + switch ( $_GET['move_mainjs_to'] ) { + case 'head': + wp_enqueue_script( + WC_Google_Gtag_JS::get_instance()->script_handle . '-head', + WC_Google_Analytics_Integration::get_instance()->get_js_asset_url( 'main.js' ), + array( + ...WC_Google_Analytics_Integration::get_instance()->get_js_asset_dependencies( 'main' ), + 'google-tag-manager', + ), + WC_Google_Analytics_Integration::get_instance()->get_js_asset_version( 'main' ), + false + ); + break; + case 'after_inline_data': + add_action( + 'wp_footer', + function () { + printf( + '', // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript + WC_Google_Analytics_Integration::get_instance()->get_js_asset_url( 'main.js' ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ); + }, + 9999 + ); + break; + } } } -} ); +); From 4d6f371edce123fcfd2be312e496a43b1376b503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Wytr=C4=99bowicz?= Date: Tue, 26 Mar 2024 21:41:28 +0100 Subject: [PATCH 18/25] Fix the convention for JS block comments Address https://github.com/woocommerce/woocommerce-google-analytics-integration/pull/398#discussion_r1539301224 --- includes/class-wc-google-gtag-js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index b23619b5..816d8504 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -62,7 +62,7 @@ public function __construct( $settings = array() ) { public function setup_site_tag() { wp_print_inline_script_tag( sprintf( - '/** Google Analytics for WooCommerce (gtag.js) */ + '/* Google Analytics for WooCommerce (gtag.js) */ window.dataLayer = window.dataLayer || []; function %2$s(){dataLayer.push(arguments);} // Set up default consent state. From cdb60c08a9cb62f351f5d324472ad62b6eef7105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Wytr=C4=99bowicz?= Date: Tue, 26 Mar 2024 22:15:19 +0100 Subject: [PATCH 19/25] Rename event handler-related function names to more accurately reflect what they are. Address https://github.com/woocommerce/woocommerce-google-analytics-integration/pull/398#discussion_r1539261209 --- assets/js/src/index.js | 8 ++++---- assets/js/src/integrations/blocks.js | 14 +++++++------- assets/js/src/integrations/classic.js | 18 +++++++++--------- assets/js/src/tracker/index.js | 12 ++++++------ 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/assets/js/src/index.js b/assets/js/src/index.js index b1c61719..9da81ac2 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -1,4 +1,4 @@ -import { createTracker } from './tracker'; +import { setupEventHandlers } from './tracker'; import { classicTracking } from './integrations/classic'; import { blocksTracking } from './integrations/blocks'; @@ -17,8 +17,8 @@ function eventuallyInitializeTracking() { 'Google Analytics for WooCommerce: Configuration and tracking data not found.' ); } - const eventHandler = createTracker( window.ga4w.settings ); + const getEventHandler = setupEventHandlers( window.ga4w.settings ); - classicTracking( eventHandler, window.ga4w.data ); - blocksTracking( eventHandler ); + classicTracking( getEventHandler, window.ga4w.data ); + blocksTracking( getEventHandler ); } diff --git a/assets/js/src/integrations/blocks.js b/assets/js/src/integrations/blocks.js index b663ee31..30d6c64b 100644 --- a/assets/js/src/integrations/blocks.js +++ b/assets/js/src/integrations/blocks.js @@ -3,23 +3,23 @@ import { addUniqueAction } from '../utils'; import { ACTION_PREFIX, NAMESPACE } from '../constants'; // We add actions asynchronosly, to make sure handlers will have the config available. -export const blocksTracking = ( eventHandler ) => { +export const blocksTracking = ( getEventHandler ) => { addUniqueAction( `${ ACTION_PREFIX }-product-render`, NAMESPACE, - eventHandler( 'view_item' ) + getEventHandler( 'view_item' ) ); addUniqueAction( `${ ACTION_PREFIX }-cart-remove-item`, NAMESPACE, - eventHandler( 'remove_from_cart' ) + getEventHandler( 'remove_from_cart' ) ); addUniqueAction( `${ ACTION_PREFIX }-checkout-render-checkout-form`, NAMESPACE, - eventHandler( 'begin_checkout' ) + getEventHandler( 'begin_checkout' ) ); // These actions only works for All Products Block @@ -27,20 +27,20 @@ export const blocksTracking = ( eventHandler ) => { `${ ACTION_PREFIX }-cart-add-item`, NAMESPACE, ( { product } ) => { - eventHandler( 'add_to_cart' )( { product } ); + getEventHandler( 'add_to_cart' )( { product } ); } ); addUniqueAction( `${ ACTION_PREFIX }-product-list-render`, NAMESPACE, - eventHandler( 'view_item_list' ) + getEventHandler( 'view_item_list' ) ); addUniqueAction( `${ ACTION_PREFIX }-product-view-link`, NAMESPACE, - eventHandler( 'select_content' ) + getEventHandler( 'select_content' ) ); }; diff --git a/assets/js/src/integrations/classic.js b/assets/js/src/integrations/classic.js index 4c1f8e5e..566bab12 100644 --- a/assets/js/src/integrations/classic.js +++ b/assets/js/src/integrations/classic.js @@ -12,7 +12,7 @@ import { getProductFromID } from '../utils'; * * It also handles some Block events that are not fired reliably for `woocommerce/all-products` block. * - * @param {Function} eventHandler + * @param {Function} getEventHandler * @param {Object} data - The tracking data from the current page load, containing the following properties: * @param {Object} data.events - An object containing the events to be instantly tracked. * @param {Object} data.cart - The cart object. @@ -22,15 +22,15 @@ import { getProductFromID } from '../utils'; * @param {Object} data.order - The order object. */ export function classicTracking( - eventHandler, + getEventHandler, { events, cart, products, product, added_to_cart: addedToCart, order } ) { // Instantly track the events listed in the `events` object. Object.values( events ?? {} ).forEach( ( eventName ) => { if ( eventName === 'add_to_cart' ) { - eventHandler( eventName )( { product: addedToCart } ); + getEventHandler( eventName )( { product: addedToCart } ); } else { - eventHandler( eventName )( { + getEventHandler( eventName )( { storeCart: cart, products, product, @@ -65,7 +65,7 @@ export function classicTracking( return; } - eventHandler( 'add_to_cart' )( { product: productToHandle } ); + getEventHandler( 'add_to_cart' )( { product: productToHandle } ); }; /** @@ -87,7 +87,7 @@ export function classicTracking( * @param {HTMLElement|Object} element - The HTML element clicked on to trigger this event */ function removeFromCartHandler( element ) { - eventHandler( 'remove_from_cart' )( { + getEventHandler( 'remove_from_cart' )( { product: getProductFromID( parseInt( element.target.dataset.product_id ), products, @@ -154,7 +154,7 @@ export function classicTracking( return; } - eventHandler( 'select_content' )( { + getEventHandler( 'select_content' )( { product: getProductFromID( parseInt( productId ), products, @@ -202,7 +202,7 @@ export function classicTracking( if ( isAddToCartButton ) { // Add to cart. - eventHandler( 'add_to_cart' )( { + getEventHandler( 'add_to_cart' )( { product: getProductFromID( parseInt( productId ), products, @@ -211,7 +211,7 @@ export function classicTracking( } ); } else if ( viewLink || button || nameLink ) { // Product image or add-to-cart-like button. - eventHandler( 'select_content' )( { + getEventHandler( 'select_content' )( { product: getProductFromID( parseInt( productId ), products, diff --git a/assets/js/src/tracker/index.js b/assets/js/src/tracker/index.js index 1cd1fa51..3156cd08 100644 --- a/assets/js/src/tracker/index.js +++ b/assets/js/src/tracker/index.js @@ -6,32 +6,32 @@ import * as formatters from './data-formatting'; * @param {Object} settings - The settings object. * @param {Array} settings.events - The list of supported events. * @param {string} settings.tracker_function_name - The name of the global function to call for tracking. - * @return {function(string): Function} - A function to create event handlers for specific events. + * @return {function(string): Function} - A function to get event handlers for specific events. */ -export function createTracker( { +export function setupEventHandlers( { events, tracker_function_name: trackerFunctionName, } ) { /** - * Creates and returns an event handler for a specified event name. + * Returns an event handler for a specified event name. * * @param {string} eventName The name of the event. * @return {function(*): void} Function for processing and tracking the event. * @throws {Error} If the event name is not supported. */ - function eventHandler( eventName ) { + function getEventHandler( eventName ) { /* eslint import/namespace: [ 'error', { allowComputed: true } ] */ const formatter = formatters[ eventName ]; if ( typeof formatter !== 'function' ) { throw new Error( `Event ${ eventName } is not supported.` ); } - return function trackerEventHandler( data ) { + return function eventHandler( data ) { const eventData = formatter( data ); if ( events.includes( eventName ) && eventData ) { window[ trackerFunctionName ]( 'event', eventName, eventData ); } }; } - return eventHandler; + return getEventHandler; } From 867f69657d7a1e48713ab49ab0cb56d594ede9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Wytr=C4=99bowicz?= Date: Wed, 27 Mar 2024 19:33:52 +0100 Subject: [PATCH 20/25] Unify `self::` to `$this->` reference in instance methods Address https://github.com/woocommerce/woocommerce-google-analytics-integration/pull/398/files#r1539297700 --- includes/class-wc-google-gtag-js.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index 816d8504..790ba554 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -72,10 +72,10 @@ function %2$s(){dataLayer.push(arguments);} %2$s("js", new Date()); %2$s("set", "developer_id.%3$s", true); %2$s("config", "%1$s", %5$s);', - esc_js( self::get( 'ga_id' ) ), - esc_js( self::tracker_function_name() ), + esc_js( $this->get( 'ga_id' ) ), + esc_js( $this->tracker_function_name() ), esc_js( self::DEVELOPER_ID ), - json_encode( self::get_consent_modes() ), + json_encode( $this->get_consent_modes() ), json_encode( $this->get_site_tag_config() ) ) ); @@ -132,9 +132,9 @@ public function inline_script_data(): void { $this->get_script_data(), wp_json_encode( array( - 'tracker_function_name' => self::tracker_function_name(), + 'tracker_function_name' => $this->tracker_function_name(), 'events' => $this->get_enabled_events(), - 'identifier' => self::get( 'ga_product_identifier' ), + 'identifier' => $this->get( 'ga_product_identifier' ), ), ), ) @@ -241,12 +241,12 @@ public function get_site_tag_config(): array { return apply_filters( 'woocommerce_ga_gtag_config', array( - 'track_404' => 'yes' === self::get( 'ga_404_tracking_enabled' ), - 'allow_google_signals' => 'yes' === self::get( 'ga_support_display_advertising' ), + 'track_404' => 'yes' === $this->get( 'ga_404_tracking_enabled' ), + 'allow_google_signals' => 'yes' === $this->get( 'ga_support_display_advertising' ), 'logged_in' => is_user_logged_in(), 'linker' => array( - 'domains' => ! empty( self::get( 'ga_linker_cross_domains' ) ) ? array_map( 'esc_js', explode( ',', self::get( 'ga_linker_cross_domains' ) ) ) : array(), - 'allow_incoming' => 'yes' === self::get( 'ga_linker_allow_incoming_enabled' ), + 'domains' => ! empty( $this->get( 'ga_linker_cross_domains' ) ) ? array_map( 'esc_js', explode( ',', $this->get( 'ga_linker_cross_domains' ) ) ) : array(), + 'allow_incoming' => 'yes' === $this->get( 'ga_linker_allow_incoming_enabled' ), ), 'custom_map' => array( 'dimension1' => 'logged_in', From 89487e643cb47a2030be2de9160d3b011532dd18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Wytr=C4=99bowicz?= Date: Wed, 27 Mar 2024 19:39:46 +0100 Subject: [PATCH 21/25] Use `static::` to reference `DEVELOPER_ID` in parent class, to avoid `Constant from class 'WC_Abstract_Google_Analytics_JS' referenced through child. PHP6606` notice --- includes/class-wc-google-gtag-js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index 790ba554..e2e97602 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -74,7 +74,7 @@ function %2$s(){dataLayer.push(arguments);} %2$s("config", "%1$s", %5$s);', esc_js( $this->get( 'ga_id' ) ), esc_js( $this->tracker_function_name() ), - esc_js( self::DEVELOPER_ID ), + esc_js( static::DEVELOPER_ID ), json_encode( $this->get_consent_modes() ), json_encode( $this->get_site_tag_config() ) ) From dacaabcf29aff66f310fd71f90cd5f335a7d5285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Wytr=C4=99bowicz?= Date: Thu, 28 Mar 2024 21:56:19 +0100 Subject: [PATCH 22/25] Use the data if ready, wait for our own event to use it, warn if still not available after the document is fully loaded. Make the extension work, regardless of the order and timing of `woocommerce-google-analytics-integration` and `woocommerce-google-analytics-integration-data`scripts. Addresses https://github.com/woocommerce/woocommerce-google-analytics-integration/pull/398#discussion_r1539204297 Make it work with Autoptimize "Aggregate inline JS" option, address https://github.com/woocommerce/woocommerce-google-analytics-integration/pull/398#issuecomment-2025156082 --- assets/js/src/index.js | 34 +++++++++++++++++----------- includes/class-wc-google-gtag-js.php | 2 +- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/assets/js/src/index.js b/assets/js/src/index.js index 9da81ac2..1ef34fb8 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -2,23 +2,31 @@ import { setupEventHandlers } from './tracker'; import { classicTracking } from './integrations/classic'; import { blocksTracking } from './integrations/blocks'; -// Wait for DOMContentLoaded to make sure event data is in place. -if ( document.readyState === 'loading' ) { - document.addEventListener( - 'DOMContentLoaded', - eventuallyInitializeTracking - ); +// Wait for 'ga4w:ready' event if `window.ga4w` is not there yet. +if ( window.ga4w ) { + initializeTracking(); } else { - eventuallyInitializeTracking(); -} -function eventuallyInitializeTracking() { - if ( ! window.ga4w ) { - throw new Error( - 'Google Analytics for WooCommerce: Configuration and tracking data not found.' - ); + document.addEventListener( 'ga4w:ready', initializeTracking ); + + // Warn if there is still nothing after the document is fully loded. + if ( document.readyState === 'complete' ) { + warnIfDataMissing(); + } else { + window.addEventListener( 'load', warnIfDataMissing ); } +} +function initializeTracking() { const getEventHandler = setupEventHandlers( window.ga4w.settings ); classicTracking( getEventHandler, window.ga4w.data ); blocksTracking( getEventHandler ); } + +function warnIfDataMissing() { + if ( ! window.ga4w ) { + // eslint-disable-next-line no-console -- It's not an error, as one may load the script later, but we'd like to warn developers if it's about to be missing. + console.warn( + 'Google Analytics for WooCommerce: Configuration and tracking data not found after the page was fully loaded. Make sure the `woocommerce-google-analytics-integration-data` script gets eventually loaded.' + ); + } +} diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index e2e97602..0c3135a4 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -128,7 +128,7 @@ public function inline_script_data(): void { wp_add_inline_script( $this->data_script_handle, sprintf( - 'var ga4w = { data: %1$s, settings: %2$s };', + 'window.ga4w = { data: %1$s, settings: %2$s }; document.dispatchEvent(new Event("ga4w:ready"));', $this->get_script_data(), wp_json_encode( array( From 77067d8f0812af6071867af11ed7867bf8e280f6 Mon Sep 17 00:00:00 2001 From: martynmjones Date: Wed, 3 Apr 2024 19:31:46 +0100 Subject: [PATCH 23/25] Move gtag and dataLayer setup to registered inline script --- includes/class-wc-google-gtag-js.php | 71 ++++++++++++++++------------ 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index 0c3135a4..ed0fc748 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -16,7 +16,10 @@ class WC_Google_Gtag_JS extends WC_Abstract_Google_Analytics_JS { /** @var string $script_handle Handle for the front end JavaScript file */ public $script_handle = 'woocommerce-google-analytics-integration'; - /** @var string $script_handle Handle for the event data inline script */ + /** @var string $gtag_script_handle Handle for the gtag setup script */ + public $gtag_script_handle = 'woocommerce-google-analytics-integration-gtag'; + + /** @var string $data_script_handle Handle for the event data inline script */ public $data_script_handle = 'woocommerce-google-analytics-integration-data'; /** @var string $script_data Data required for frontend event tracking */ @@ -49,38 +52,10 @@ public function __construct( $settings = array() ) { $this->map_hooks(); // Setup frontend scripts - add_action( 'wp_head', array( $this, 'setup_site_tag' ), 2 ); add_action( 'wp_enqueue_scripts', array( $this, 'enquque_tracker' ), 5 ); add_action( 'wp_footer', array( $this, 'inline_script_data' ) ); } - /** - * Setup the global site tag as early as possible on the page - * - * @return void - */ - public function setup_site_tag() { - wp_print_inline_script_tag( - sprintf( - '/* Google Analytics for WooCommerce (gtag.js) */ - window.dataLayer = window.dataLayer || []; - function %2$s(){dataLayer.push(arguments);} - // Set up default consent state. - for ( const mode of %4$s || [] ) { - %2$s( "consent", "default", mode ); - } - %2$s("js", new Date()); - %2$s("set", "developer_id.%3$s", true); - %2$s("config", "%1$s", %5$s);', - esc_js( $this->get( 'ga_id' ) ), - esc_js( $this->tracker_function_name() ), - esc_js( static::DEVELOPER_ID ), - json_encode( $this->get_consent_modes() ), - json_encode( $this->get_site_tag_config() ) - ) - ); - } - /** * Register tracker scripts. * @@ -93,10 +68,46 @@ public function enquque_tracker(): void { array(), null, array( - 'strategy' => 'async', + 'strategy' => 'async', + ) + ); + + wp_register_script( + $this->gtag_script_handle, + '', + array(), + null, + array( + 'in_footer' => false ) ); + wp_add_inline_script( + $this->gtag_script_handle, + apply_filters( + 'woocommerce_gtag_snippet', + sprintf( + '/* Google Analytics for WooCommerce (gtag.js) */ + window.dataLayer = window.dataLayer || []; + function %2$s(){dataLayer.push(arguments);} + // Set up default consent state. + for ( const mode of %4$s || [] ) { + %2$s( "consent", "default", mode ); + } + %2$s("js", new Date()); + %2$s("set", "developer_id.%3$s", true); + %2$s("config", "%1$s", %5$s);', + esc_js( $this->get( 'ga_id' ) ), + esc_js( $this->tracker_function_name() ), + esc_js( static::DEVELOPER_ID ), + json_encode( $this->get_consent_modes() ), + json_encode( $this->get_site_tag_config() ) + ) + ) + ); + + wp_enqueue_script( $this->gtag_script_handle ); + wp_enqueue_script( $this->script_handle, Plugin::get_instance()->get_js_asset_url( 'main.js' ), From 757b3e828c0714036a7df7be76c03ed9a9b58491 Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Thu, 4 Apr 2024 18:22:51 +0100 Subject: [PATCH 24/25] PHPCS and add snippet to bypass WooCommerce dependency in e2e tests --- includes/class-wc-google-gtag-js.php | 11 +---------- tests/e2e/test-snippets/test-snippets.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index cabb9454..3a95a49e 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -70,7 +70,7 @@ private function register_scripts(): void { array(), null, array( - 'strategy' => 'async', + 'strategy' => 'async', ) ); @@ -133,15 +133,6 @@ public function enquque_tracker(): void { // tracker.js needs to be executed ASAP, the remaining bits for main.js could be deffered, // but to reduce the traffic, we ship it all together. wp_enqueue_script( $this->script_handle ); - // Provide tracker's configuration. - wp_add_inline_script( - $this->script_handle, - sprintf( - 'var wcgai = {config: %s};', - wp_json_encode( $this->get_analytics_config() ) - ), - 'before' - ); } /** diff --git a/tests/e2e/test-snippets/test-snippets.php b/tests/e2e/test-snippets/test-snippets.php index 6dcc3e47..01fc94bc 100644 --- a/tests/e2e/test-snippets/test-snippets.php +++ b/tests/e2e/test-snippets/test-snippets.php @@ -100,3 +100,18 @@ function () { } } ); + +/** + * Snippet to bypass the WooCommerce dependency in Google Listings & Ads because + * in wp-env WooCommerce is installed in the directory woocommerce-trunk-nightly + */ +add_action( + 'wp_plugin_dependencies_slug', + function( $slug ) { + if ( 'woocommerce' === $slug ) { + $slug = ''; + } + + return $slug; + } +); From 5d587a329826b4e985d907bcfd5e40a99b69e0c7 Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Thu, 4 Apr 2024 18:25:28 +0100 Subject: [PATCH 25/25] Fix WC bypass snippet --- tests/e2e/test-snippets/test-snippets.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/test-snippets/test-snippets.php b/tests/e2e/test-snippets/test-snippets.php index 01fc94bc..a0ff0af8 100644 --- a/tests/e2e/test-snippets/test-snippets.php +++ b/tests/e2e/test-snippets/test-snippets.php @@ -107,7 +107,7 @@ function () { */ add_action( 'wp_plugin_dependencies_slug', - function( $slug ) { + function ( $slug ) { if ( 'woocommerce' === $slug ) { $slug = ''; }