Skip to content

Commit

Permalink
Merge pull request #2425 from woocommerce/add/2387-wp-consent-api-int…
Browse files Browse the repository at this point in the history
…egration

Add integration with WP Consent API
  • Loading branch information
martynmjones authored Jun 18, 2024
2 parents 22a13e8 + 210333d commit b86e92b
Show file tree
Hide file tree
Showing 6 changed files with 323 additions and 9 deletions.
61 changes: 61 additions & 0 deletions js/src/wp-consent-api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const consentMap = {
statistics: [ 'analytics_storage' ],
marketing: [ 'ad_storage', 'ad_user_data', 'ad_personalization' ],
};

const setCurrentConsentState = () => {
// eslint-disable-next-line camelcase -- `wp_has_consent` is defined by the WP Consent API plugin.
if ( typeof wp_has_consent === 'function' ) {
if ( window.wp_consent_type === undefined ) {
window.wp_consent_type = 'optin';
}

const consentState = {};

for ( const [ category, types ] of Object.entries( consentMap ) ) {
if (
// eslint-disable-next-line camelcase, no-undef -- `consent_api_get_cookie` is defined by the WP Consent API plugin.
consent_api_get_cookie(
window.consent_api.cookie_prefix + '_' + category
) !== ''
) {
// eslint-disable-next-line camelcase, no-undef -- `wp_has_consent` is defined by the WP Consent API plugin.
const hasConsent = wp_has_consent( category )
? 'granted'
: 'denied';

types.forEach( ( type ) => {
consentState[ type ] = hasConsent;
} );
}
}

if ( Object.keys( consentState ).length > 0 ) {
window.gtag( 'consent', 'update', consentState );
}
}
};

document.addEventListener( 'wp_listen_for_consent_change', ( event ) => {
const consentUpdate = {};

const types = consentMap[ Object.keys( event.detail )[ 0 ] ];
const state =
Object.values( event.detail )[ 0 ] === 'allow' ? 'granted' : 'denied';

if ( types !== undefined ) {
types.forEach( ( type ) => {
consentUpdate[ type ] = state;
} );

if ( Object.keys( consentUpdate ).length > 0 ) {
window.gtag( 'consent', 'update', consentUpdate );
}
}
} );

if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', setCurrentConsentState );
} else {
setCurrentConsentState();
}
21 changes: 20 additions & 1 deletion src/Google/GlobalSiteTag.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,23 @@ function () {

$this->assets_handler->register( $gtag_events );

$wp_consent_api = new ScriptWithBuiltDependenciesAsset(
'gla-wp-consent-api',
'js/build/wp-consent-api',
"{$this->get_root_dir()}/js/build/wp-consent-api.asset.php",
new BuiltScriptDependencyArray(
[
'dependencies' => [ 'wp-consent-api' ],
'version' => $this->get_version(),
]
)
);

$this->assets_handler->register( $wp_consent_api );

add_action(
'wp_footer',
function () use ( $gtag_events ) {
function () use ( $gtag_events, $wp_consent_api ) {
$gtag_events->add_localization(
'glaGtagData',
[
Expand All @@ -204,6 +218,10 @@ function () use ( $gtag_events ) {

$this->register_js_for_fast_refresh_dev();
$this->assets_handler->enqueue( $gtag_events );

if ( ! class_exists( '\WC_Google_Gtag_JS' ) && function_exists( 'wp_has_consent' ) ) {
$this->assets_handler->enqueue( $wp_consent_api );
}
}
);
}
Expand Down Expand Up @@ -293,6 +311,7 @@ protected function get_consent_mode_config() {
ad_user_data: 'denied',
ad_personalization: 'denied',
region: ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IS', 'IE', 'IT', 'LV', 'LI', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'GB', 'CH'],
wait_for_update: 500,
} );";
/**
* Filters the default gtag consent mode configuration.
Expand Down
3 changes: 3 additions & 0 deletions tests/e2e/bin/test-env-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ wp-env run tests-cli wp theme activate twentytwentytwo
echo -e 'Install WooCommerce \n'
wp-env run tests-cli -- wp plugin install woocommerce --activate

echo -e 'Install WP Consent API \n'
wp-env run tests-cli -- wp plugin install wp-consent-api --activate

echo -e 'Activate Google Listings and Ads \n'
wp-env run tests-cli -- wp plugin activate google-listings-and-ads

Expand Down
222 changes: 222 additions & 0 deletions tests/e2e/specs/gtag-events/wp-consent-api.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/**
* External dependencies
*/
const { test, expect } = require( '@playwright/test' );

/**
* Internal dependencies
*/

import { setConversionID, clearConversionID } from '../../utils/api';

/**
* This is a clone from Google Analytics for WooCommerce plugin, version 2.1.1.
* https://github.com/woocommerce/woocommerce-google-analytics-integration/blob/a7d972826d7370eefaf1c21f02139218cd9adc71/tests/e2e/specs/js-scripts/wp-consent-api.test.js
*/
test.describe( 'WP Consent API Integration', () => {
test.beforeAll( async () => {
await setConversionID();
} );

test.afterAll( async () => {
await clearConversionID();
} );

test( 'window.wp_consent_type is set to `optin`', async ( { page } ) => {
await page.goto( 'shop' );

const consentType = await page.evaluate( () => window.wp_consent_type );
await expect( consentType ).toEqual( 'optin' );
} );

test( 'Consent update granting `analytics_storage` is sent when WP Consent API `statistics` category is `allowed`', async ( {
page,
} ) => {
await page.goto( 'shop?consent_default=denied' );
await page.evaluate( () =>
window.wp_set_consent( 'statistics', 'allow' )
);

const dataLayer = await page.evaluate( () => window.dataLayer );
const consentState = dataLayer.filter( ( i ) => i[ 0 ] === 'consent' );

await expect( consentState.length ).toEqual( 2 );

await expect( consentState[ 0 ] ).toEqual( {
0: 'consent',
1: 'default',
2: expect.objectContaining( { analytics_storage: 'denied' } ),
} );

await expect( consentState[ 1 ] ).toEqual( {
0: 'consent',
1: 'update',
2: { analytics_storage: 'granted' },
} );
} );

test( 'Consent update granting `ad_storage`, `ad_user_data`, `ad_personalization` is sent when WP Consent API `marketing` category is `allowed`', async ( {
page,
} ) => {
await page.goto( 'shop?consent_default=denied' );
await page.evaluate( () =>
window.wp_set_consent( 'marketing', 'allow' )
);

const dataLayer = await page.evaluate( () => window.dataLayer );
const consentState = dataLayer.filter( ( i ) => i[ 0 ] === 'consent' );

await expect( consentState.length ).toEqual( 2 );

await expect( consentState[ 0 ] ).toEqual( {
0: 'consent',
1: 'default',
2: expect.objectContaining( {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
} ),
} );

await expect( consentState[ 1 ] ).toEqual( {
0: 'consent',
1: 'update',
2: {
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
},
} );
} );

test( 'Consent update denying `analytics_storage` is sent when WP Consent API `statistics` category is `denied`', async ( {
page,
} ) => {
await page.goto( 'shop' );
await page.evaluate( () =>
window.wp_set_consent( 'statistics', 'deny' )
);

const dataLayer = await page.evaluate( () => window.dataLayer );
const consentState = dataLayer.filter( ( i ) => i[ 0 ] === 'consent' );

await expect( consentState.length ).toEqual( 2 );

await expect( consentState[ 0 ] ).toEqual( {
0: 'consent',
1: 'default',
2: expect.objectContaining( { analytics_storage: 'granted' } ),
} );

await expect( consentState[ 1 ] ).toEqual( {
0: 'consent',
1: 'update',
2: { analytics_storage: 'denied' },
} );
} );

test( 'Consent update denying `ad_storage`, `ad_user_data`, `ad_personalization` is sent when WP Consent API `marketing` category is `denied`', async ( {
page,
} ) => {
await page.goto( 'shop' );
await page.evaluate( () =>
window.wp_set_consent( 'marketing', 'deny' )
);

const dataLayer = await page.evaluate( () => window.dataLayer );
const consentState = dataLayer.filter( ( i ) => i[ 0 ] === 'consent' );

await expect( consentState.length ).toEqual( 2 );

await expect( consentState[ 0 ] ).toEqual( {
0: 'consent',
1: 'default',
2: expect.objectContaining( {
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
} ),
} );

await expect( consentState[ 1 ] ).toEqual( {
0: 'consent',
1: 'update',
2: {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
},
} );
} );

test( 'Consent state is sent as update when page is loaded', async ( {
page,
} ) => {
await page.goto( 'shop?consent_default=denied' );
await page.evaluate( () =>
window.wp_set_consent( 'marketing', 'allow' )
);
// Go to a new page to confirm that the consent state is maintained across page loads
await page.goto( '/?consent_default=denied' );

const dataLayer = await page.evaluate( () => window.dataLayer );
const consentState = dataLayer.filter( ( i ) => i[ 0 ] === 'consent' );

await expect( consentState.length ).toEqual( 2 );

await expect( consentState[ 0 ] ).toEqual( {
0: 'consent',
1: 'default',
2: expect.objectContaining( {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
} ),
} );

await expect( consentState[ 1 ] ).toEqual( {
0: 'consent',
1: 'update',
2: {
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
},
} );
} );

test( 'Consent state is sent as update when page is loaded if the default is set to `granted`', async ( {
page,
} ) => {
await page.goto( 'shop' );
await page.evaluate( () =>
window.wp_set_consent( 'statistics', 'deny' )
);
await page.goto( 'shop' );

const dataLayer = await page.evaluate( () => window.dataLayer );
const consentState = dataLayer.filter( ( i ) => i[ 0 ] === 'consent' );

await expect( consentState.length ).toEqual( 2 );

await expect( consentState[ 0 ] ).toEqual( {
0: 'consent',
1: 'default',
2: expect.objectContaining( {
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
analytics_storage: 'granted',
} ),
} );

await expect( consentState[ 1 ] ).toEqual( {
0: 'consent',
1: 'update',
2: {
analytics_storage: 'denied',
},
} );
} );
} );
20 changes: 12 additions & 8 deletions tests/e2e/test-snippets/test-snippets.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@
*/
add_filter(
'woocommerce_gla_gtag_consent',
function( $old_config ) {
return "gtag( 'consent', 'default', {
analytics_storage: 'granted',
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
} );
";
function ( $old_config ) {
$status = 'granted';
// Optional: Set the default consent state for tests via the `consent_default` URL parameter.
if ( isset( $_GET['consent_default'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$status = sanitize_text_field( wp_unslash( $_GET['consent_default'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
return sprintf( 'gtag( "consent", "default", {
analytics_storage: "%1$s",
ad_storage: "%1$s",
ad_user_data: "%1$s",
ad_personalization: "%1$s",
} );', $status);
}
);

Expand Down
5 changes: 5 additions & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ const webpackConfig = {
'js/src/gtag-events',
'index.js'
),
'wp-consent-api': path.resolve(
process.cwd(),
'js/src/wp-consent-api',
'index.js'
),
} ),
output: {
...defaultConfig.output,
Expand Down

0 comments on commit b86e92b

Please sign in to comment.