Skip to content

Commit

Permalink
Merge pull request #2523 from woocommerce/release/2.8.2
Browse files Browse the repository at this point in the history
Release 2.8.2
  • Loading branch information
eason9487 authored Aug 14, 2024
2 parents 6e5b4bc + c581e5e commit 4cab1dc
Show file tree
Hide file tree
Showing 36 changed files with 1,133 additions and 435 deletions.
11 changes: 11 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
*** Google for WooCommerce Changelog ***

= 2.8.2 - 2024-08-14 =
* Fix - Disconnecting all accounts when WPCOM connection is not granted.
* Fix - Error when Google Merchant Center account is undefined while checking the notification service enabled property.
* Tweak - Label campaigns for the web version and the WooCommerce Mobile app.
* Tweak - Update FAQS in Getting Started page.
* Tweak - Update WP.org plugin FAQs.
* Tweak - Update WPORG plugin page header image.
* Tweak - Update get started page.
* Tweak - WC 9.2.0 compatibility.
* Update - Block validation to support error context.

= 2.8.1 - 2024-08-06 =
* Add - Enable labeling of Ads campaigns.
* Tweak - Update doc links references.
Expand Down
6 changes: 3 additions & 3 deletions google-listings-and-ads.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Plugin Name: Google for WooCommerce
* Plugin URL: https://wordpress.org/plugins/google-listings-and-ads/
* Description: Native integration with Google that allows merchants to easily display their products across Google’s network.
* Version: 2.8.1
* Version: 2.8.2
* Author: WooCommerce
* Author URI: https://woocommerce.com/
* Text Domain: google-listings-and-ads
Expand All @@ -13,7 +13,7 @@
* Requires PHP Architecture: 64 bits
* Requires Plugins: woocommerce
* WC requires at least: 6.9
* WC tested up to: 9.1
* WC tested up to: 9.2.0
* Woo:
*
* @package WooCommerce\Admin
Expand All @@ -30,7 +30,7 @@

defined( 'ABSPATH' ) || exit;

define( 'WC_GLA_VERSION', '2.8.1' ); // WRCS: DEFINED_VERSION.
define( 'WC_GLA_VERSION', '2.8.2' ); // WRCS: DEFINED_VERSION.
define( 'WC_GLA_MIN_PHP_VER', '7.4' );
define( 'WC_GLA_MIN_WC_VER', '6.9' );

Expand Down
20 changes: 15 additions & 5 deletions js/src/blocks/product-date-time-field/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
useValidation,
} from '@woocommerce/product-editor';
import { Flex, FlexBlock } from '@wordpress/components';
import { isWcVersion } from '@woocommerce/settings'; // eslint-disable-line import/no-unresolved

/**
* Internal dependencies
Expand All @@ -21,11 +22,19 @@ import styles from './editor.module.scss';
* @typedef {import('../types.js').ProductBasicAttributes} ProductBasicAttributes
*/

async function resolveValidationMessage( inputRef ) {
async function resolveValidationMessage( inputRef, context ) {
const input = inputRef.current;

if ( ! input.validity.valid ) {
return input.validationMessage;
// compatibility-code "WC < 9.2"
if ( isWcVersion( '9.2.0', '<' ) ) {
return input.validationMessage;
}

return {
message: input.validationMessage,
context,
};
}
}

Expand All @@ -35,8 +44,9 @@ async function resolveValidationMessage( inputRef ) {
* @param {Object} props React props.
* @param {ProductBasicAttributes} props.attributes
* @param {ProductEditorBlockContext} props.context
* @param {string} props.clientId
*/
export default function Edit( { attributes, context } ) {
export default function Edit( { attributes, context, clientId } ) {
const { property } = attributes;
const blockProps = useWooBlockProps( attributes );
const [ value, setValue ] = useProductEntityProp( property, {
Expand Down Expand Up @@ -76,11 +86,11 @@ export default function Edit( { attributes, context } ) {
};

const dateValidation = useValidation( `${ property }-date`, () =>
resolveValidationMessage( dateInputRef )
resolveValidationMessage( dateInputRef, clientId )
);

const timeValidation = useValidation( `${ property }-time`, () =>
resolveValidationMessage( timeInputRef )
resolveValidationMessage( timeInputRef, clientId )
);

return (
Expand Down
4 changes: 2 additions & 2 deletions js/src/components/enable-new-product-sync-notice.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const EnableNewProductSyncNotice = () => {
// Do not render if already switch to new product sync.
if (
! hasGoogleMCAccountFinishedResolution ||
! googleMCAccount.notification_service_enabled ||
googleMCAccount.wpcom_rest_api_status
! googleMCAccount?.notification_service_enabled ||
googleMCAccount?.wpcom_rest_api_status
) {
return null;
}
Expand Down
116 changes: 116 additions & 0 deletions js/src/components/enable-new-product-sync-notice.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* External dependencies
*/
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';

/**
* Internal dependencies
*/
import useGoogleMCAccount from '.~/hooks/useGoogleMCAccount';
import EnableNewProductSyncNotice from './enable-new-product-sync-notice';

jest.mock( '.~/hooks/useGoogleMCAccount' );

describe( 'Enable New Product Sync Notice', () => {
it( 'should render the notice if the account has not switched to new product sync', () => {
useGoogleMCAccount.mockImplementation( () => {
return {
hasFinishedResolution: true,
googleMCAccount: {
notification_service_enabled: true,
wpcom_rest_api_status: null,
},
};
} );

const { getByText, getByRole } = render(
<EnableNewProductSyncNotice />
);

expect(
getByText(
'We will soon transition to a new and improved method for synchronizing product data with Google.'
)
).toBeInTheDocument();

const button = getByRole( 'button', {
name: /Get early access/i,
} );

expect( button ).toBeEnabled();
} );
it( 'should not render the notice if the account has switched to new product sync', () => {
useGoogleMCAccount.mockImplementation( () => {
return {
hasFinishedResolution: true,
googleMCAccount: {
notification_service_enabled: true,
wpcom_rest_api_status: 'approved',
},
};
} );

const { queryByText, queryByRole } = render(
<EnableNewProductSyncNotice />
);

expect(
queryByText(
'We will soon transition to a new and improved method for synchronizing product data with Google.'
)
).not.toBeInTheDocument();

expect(
queryByRole( 'button', { name: /Get early access/i } )
).not.toBeInTheDocument();
} );
it( 'should not render the notice if the notification service is not enabled', () => {
useGoogleMCAccount.mockImplementation( () => {
return {
hasFinishedResolution: true,
googleMCAccount: {
notification_service_enabled: false,
wpcom_rest_api_status: 'approved',
},
};
} );

const { queryByText, queryByRole } = render(
<EnableNewProductSyncNotice />
);

expect(
queryByText(
'We will soon transition to a new and improved method for synchronizing product data with Google.'
)
).not.toBeInTheDocument();

expect(
queryByRole( 'button', { name: /Get early access/i } )
).not.toBeInTheDocument();
} );

it( 'should not render the notice if googleMCAccount is undefined because the google account is not connected or missing scopes', () => {
useGoogleMCAccount.mockImplementation( () => {
return {
hasFinishedResolution: true,
googleMCAccount: undefined,
};
} );

const { queryByText, queryByRole } = render(
<EnableNewProductSyncNotice />
);

expect(
queryByText(
'We will soon transition to a new and improved method for synchronizing product data with Google.'
)
).not.toBeInTheDocument();

expect(
queryByRole( 'button', { name: /Get early access/i } )
).not.toBeInTheDocument();
} );
} );
1 change: 1 addition & 0 deletions js/src/css/abstracts/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ $gla-width-small: 250px;
$gla-width-medium: 300px;
$gla-width-medium-large: 360px;
$gla-width-mobile: 480px;
$gla-width-medium-big: 550px;
$gla-width-large: 600px;
17 changes: 17 additions & 0 deletions js/src/data/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from './constants';
import { handleApiError } from '.~/utils/handleError';
import { adaptAdsCampaign } from './adapters';
import { isWCIos, isWCAndroid } from '.~/utils/isMobileApp';

/**
* @typedef {import('.~/data/types.js').AssetEntityGroupUpdateBody} AssetEntityGroupUpdateBody
Expand Down Expand Up @@ -523,6 +524,13 @@ export function* disconnectAllAccounts() {
type: TYPES.DISCONNECT_ACCOUNTS_ALL,
};
} catch ( error ) {
// Skip any error related to revoking WPCOM token.
if ( error.errors[ `${ API_NAMESPACE }/rest-api/authorize` ] ) {
return {
type: TYPES.DISCONNECT_ACCOUNTS_ALL,
};
}

handleApiError(
error,
__(
Expand Down Expand Up @@ -798,13 +806,22 @@ export function* saveTargetAudience( targetAudience ) {
* @throws { { message: string } } Will throw an error if the campaign creation fails.
*/
export function* createAdsCampaign( amount, countryCodes ) {
let label = 'wc-web';

if ( isWCIos() ) {
label = 'wc-ios';
} else if ( isWCAndroid() ) {
label = 'wc-android';
}

try {
const createdCampaign = yield apiFetch( {
path: `${ API_NAMESPACE }/ads/campaigns`,
method: 'POST',
data: {
amount,
targeted_locations: countryCodes,
label,
},
} );

Expand Down
92 changes: 92 additions & 0 deletions js/src/data/test/createAdsCampaign.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* External dependencies
*/
import { renderHook } from '@testing-library/react-hooks';
import apiFetch from '@wordpress/api-fetch';

/**
* Internal dependencies
*/
import { useAppDispatch } from '.~/data';

jest.mock( '@wordpress/api-fetch', () => {
const impl = jest.fn().mockName( '@wordpress/api-fetch' );
impl.use = jest.fn().mockName( 'apiFetch.use' );
return impl;
} );

describe( 'createAdsCampaign', () => {
let navigatorGetter;

const mockFetch = jest
.fn()
.mockResolvedValue( { targeted_locations: [ 'ES' ] } );

beforeEach( () => {
jest.clearAllMocks();
apiFetch.mockImplementation( ( args ) => {
return mockFetch( args );
} );

navigatorGetter = jest.spyOn( window.navigator, 'userAgent', 'get' );
} );

it( 'If the user agent is not set to wc-ios or wc-android, the label should default to wc-web', async () => {
const { result } = renderHook( () => useAppDispatch() );

await result.current.createAdsCampaign( 100, [ 'ES' ] );

expect( mockFetch ).toHaveBeenCalledTimes( 1 );
expect( mockFetch ).toHaveBeenCalledWith( {
path: '/wc/gla/ads/campaigns',
method: 'POST',
data: {
amount: 100,
targeted_locations: [ 'ES' ],
label: 'wc-web',
},
} );
} );

it( 'If the user agent is set to wc-ios the label should be wc-ios', async () => {
navigatorGetter.mockReturnValue(
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 wc-ios/19.7.1'
);

const { result } = renderHook( () => useAppDispatch() );

await result.current.createAdsCampaign( 100, [ 'ES' ] );

expect( mockFetch ).toHaveBeenCalledTimes( 1 );
expect( mockFetch ).toHaveBeenCalledWith( {
path: '/wc/gla/ads/campaigns',
method: 'POST',
data: {
amount: 100,
targeted_locations: [ 'ES' ],
label: 'wc-ios',
},
} );
} );

it( 'If the user agent is set to wc-android the label should be wc-android', async () => {
navigatorGetter.mockReturnValue(
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 wc-android/19.7.1'
);

const { result } = renderHook( () => useAppDispatch() );

await result.current.createAdsCampaign( 100, [ 'ES' ] );

expect( mockFetch ).toHaveBeenCalledTimes( 1 );
expect( mockFetch ).toHaveBeenCalledWith( {
path: '/wc/gla/ads/campaigns',
method: 'POST',
data: {
amount: 100,
targeted_locations: [ 'ES' ],
label: 'wc-android',
},
} );
} );
} );
Loading

0 comments on commit 4cab1dc

Please sign in to comment.