Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connect MC Account within combo card #2639

Open
wants to merge 80 commits into
base: feature/2509-consolidate-google-account-cards
Choose a base branch
from

Conversation

asvinb
Copy link
Collaborator

@asvinb asvinb commented Oct 3, 2024

Changes proposed in this Pull Request:

Closes #2597 .

Replace this with a good description of your changes & reasoning.

Detailed test instructions:

  1. Go through the onboarding process and make sure you have at least one MC account.
  2. The card to connect your MC account should be displayed where you can connect a MC account
  3. The user should also be able to create a new account or disconnect their MC account.
  4. Designs: https://www.figma.com/design/fqR0EHi63lWahRcVTKCcba/Google-Listings-%26-Ads-v2.x?node-id=7447-22280&node-type=frame&t=tKJkit0BKc6985PH-0

Additional details:

  • There are no designs to reclaim a URL and existing components have been used as is.

Changelog entry

@github-actions github-actions bot added the changelog: update Big changes to something that wasn't broken. label Oct 3, 2024
Copy link

codecov bot commented Oct 3, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 62.7%. Comparing base (4f5bb47) to head (17e7c84).

Additional details and impacted files

Impacted file tree graph

@@                               Coverage Diff                               @@
##           feature/2509-consolidate-google-account-cards   #2639     +/-   ##
===============================================================================
- Coverage                                           62.7%   62.7%   -0.0%     
===============================================================================
  Files                                                325     325             
  Lines                                               5162    5161      -1     
  Branches                                            1265    1265             
===============================================================================
- Hits                                                3239    3238      -1     
  Misses                                              1746    1746             
  Partials                                             177     177             
Flag Coverage Δ
js-unit-tests 62.7% <100.0%> (-<0.1%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
js/src/hooks/useAutoCreateAdsMCAccounts.js 93.6% <100.0%> (-0.1%) ⬇️

@asvinb asvinb marked this pull request as ready for review October 8, 2024 12:35
@asvinb
Copy link
Collaborator Author

asvinb commented Oct 8, 2024

@joemcgill Can you review PR please? The failing unit tests are from the parent branch.

@joemcgill joemcgill linked an issue Oct 8, 2024 that may be closed by this pull request
5 tasks
Copy link
Collaborator

@joemcgill joemcgill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @asvinb This is looking pretty good, but I've got a few questions/suggestions inline.

Comment on lines 45 to 90
if ( ! isConnected && resultConnectMC.response?.status === 409 ) {
return (
<SwitchUrlCard
id={ resultConnectMC.error.id }
message={ resultConnectMC.error.message }
claimedUrl={ resultConnectMC.error.claimed_url }
newUrl={ resultConnectMC.error.new_url }
onSelectAnotherAccount={ resultConnectMC.reset }
/>
);
}

if (
! isConnected &&
( resultConnectMC.response?.status === 403 ||
resultCreateAccount.response?.status === 403 )
) {
return (
<ReclaimUrlCard
id={
resultConnectMC.error?.id || resultCreateAccount.error?.id
}
websiteUrl={
resultConnectMC.error?.website_url ||
resultCreateAccount.error?.website_url
}
onSwitchAccount={ () => {
resultConnectMC.reset();
resultCreateAccount.reset();
} }
/>
);
}

if (
! isConnected &&
( resultCreateAccount.loading ||
resultCreateAccount.response?.status === 503 )
) {
return (
<CreatingCard
retryAfter={ resultCreateAccount.error?.retry_after }
onRetry={ handleCreateAccount }
/>
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This duplicates a lot of logic directly from js/src/components/google-mc-account-card/connect-mc/index.js. Curious if we could abstract some of this into a shared component and avoid the redundant isConnected check in the process?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indicator={ getIndicator() }
/>

<ConnectMC />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than making this a sibling component of the AccountCard above, I would have expected ConnectMC to return a Section.Card.Body component that is passed as a child of the AccountCard above.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joemcgill We synced in Slack about that. We'll have a group of cars, so it'll be siblings.

Copy link
Collaborator

@joemcgill joemcgill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few questions, but no blockers. Marking this as approved and ready for QA/WooCR

{ ! isConnected && ! isConnecting && (
<AppButton
isSecondary
disabled={ ! value }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what circumstance would this be visible and disabled?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joemcgill This will be visible only when we need to connect a new account. I removed the disabled prop since we will always have a value set.

Comment on lines +47 to +51
useEffect( () => {
if ( isGoogleMCReady ) {
setAccountID( googleMCAccount.id );
}
}, [ googleMCAccount, isGoogleMCReady ] );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand why we need to save the MC account separately as component state here rather than using googleMcaccount.id directly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joemcgill We need to save the value set from the select in case the user needs to connect to a different acccount.

( existingAccount ) => existingAccount.id === googleMCAccount.id
);

if ( ! accountIdExists && isConnected ) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clever workaround. I assume this is for a newly created account that has not yet shown up in the API response for existing accounts? If so, adding that context to the inline doc would be helpful.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resultCreateAccount,
onRetry,
} ) => {
if ( resultConnectMC.response?.status === 409 ) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker, but I think using raw response status codes like this is not very readable. Curious if we could create a utility to map these to more human friendly, or at least add inline comments that makes it easy to understand what a 409 indicates.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some inline comments, just describing what are the error statuses. More details can be found in the utils function: https://github.com/woocommerce/google-listings-and-ads/pull/2639/files#diff-289f2d84147885af5105af45f5b616e4057a4490f0fe42b5cf487ca942fdfcefR6-R7

Base automatically changed from feature/2567-kickoff-mc-ads-account-creation to feature/2509-consolidate-google-account-cards October 28, 2024 06:50
@ankitguptaindia
Copy link
Member

QA Report-

Testing Environment -

  • WordPress: 6.6.2
  • Theme active on store: Twenty Twenty-Four Version: 1.2
  • WooCommerce - Version 9.3.3
  • PHP: 8.3
  • Web Server: Nginx
  • Browser: Chrome - Version 130
  • OS: macOS Sonoma 14.6.1

Test Results: Tested multiple scenarios related to MC account creation and connection with the store, including cases where Google accounts have multiple MC accounts, a single MC account, connection when the URL is already claimed, connection when the URL doesn’t need to be claimed, and creating a new MC account from the options. All cases were found to be working.

Functional Demo / Screencast

Connection when Google account has only single MC account:

when.account.has.only.One.MC.account.avaaible.mp4

Creating New account when Google Account has single MC account:

creeatung.new.account.mp4

Creating account when Google Accounts has multiple MC accounts:

Creating.new.account.when.user.has.already.multiple.MC.accounts.mp4

Connection with existing MC account when Google has multiple MC accounts:

Connecting.when.multiple.MC.accounts.availble.mp4

@@ -39,7 +56,7 @@ const ConnectedGoogleComboAccountCard = () => {
<AccountCard
appearance={ APPEARANCE.GOOGLE }
alignIcon="top"
className="gla-google-combo-account-card--connected"
className="gla-google-combo-account-card gla-google-combo-account-card--connected"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the gla-google-combo-account-card to be consistent with the other cards.

const MerchantCenterSelect = ( { isConnected, ...rest } ) => {
const { data: existingAccounts } = useExistingGoogleMCAccounts();
const { googleMCAccount } = useGoogleMCAccount();
const domain = new URL( getSetting( 'homeUrl' ) ).host;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about moving this variable into the below if block?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing! Updated @eason9487

Comment on lines 105 to 107
getExistingGoogleMCAccounts.shouldInvalidate = ( action ) => {
return action.type === TYPES.RECEIVE_ACCOUNTS_GOOGLE_MC;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this invalidation is trying to refetch the existing accounts so that it can have the newly created account. If so, after the following findings, maybe this could be removed since it usually won't work.

* we found that when a URL is reclaimed for Merchant Center (MC), the Google API does not
* return the newly reclaimed account immediately in the list provided by the useExistingGoogleMCAccounts hook,
* even though the data in the store is invalidated. In that case, thus we end up having an account ID

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines 263 to 267
await setUpAccountsPage.mockMCConnected();
await setUpAccountsPage.mockAdsAccountConnected();

await setUpAccountsPage.mockMCHasAccounts();
await setUpAccountsPage.mockMCConnected();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated await setUpAccountsPage.mockMCConnected();

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Removed.

const createAccountButton =
setUpAccountsPage.getMCCreateAccountButtonFromPage();
await createAccountButton.click();
await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it needs to wait for load state?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out we don't need it. Removed.

Comment on lines 379 to 381
await page.waitForLoadState(
LOAD_STATE.DOM_CONTENT_LOADED
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it needs to wait for load state?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed and removed unnecessary wait for load state.

Comment on lines 503 to 509
// Mock Jetpack as connected.
setUpAccountsPage.mockJetpackConnected(),

// Mock google as connected.
setUpAccountsPage.mockGoogleConnected(
'[email protected]'
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These mocks seem to be redundant as the upper test.beforeAll block has set the same mocks and they won't invalidated when entering here.

test.describe( 'Connect Merchant Center account', () => {
test.beforeAll( async () => {
await Promise.all( [
// Mock Jetpack as connected.
setUpAccountsPage.mockJetpackConnected(),
// Mock google as connected.
setUpAccountsPage.mockGoogleConnected( '[email protected]' ),

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines 215 to 217
return this.getMCAccountCard().locator(
'select[id*="inspector-select-control"]'
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return this.getMCAccountCard().locator(
'select[id*="inspector-select-control"]'
);
return this.getMCAccountCard().getByRole( 'combobox' );

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines 590 to 600
// Mock Jetpack as connected.
setUpAccountsPage.mockJetpackConnected(),

// Mock google as connected.
setUpAccountsPage.mockGoogleConnected(
'[email protected]'
),

// Mock Google Ads as connected.
setUpAccountsPage.mockAdsAccountConnected(),
setUpAccountsPage.mockAdsStatusClaimed(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These mocks seem to be redundant.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Removed.

Comment on lines 612 to 639
test( 'should see see a modal to ensure the intention of creating a new account', async () => {
// Click 'Or, create a new Merchant Center account'
const mcFooterButton =
setUpAccountsPage.getMCCardFooterButton();
await mcFooterButton.click();
await page.waitForLoadState(
LOAD_STATE.DOM_CONTENT_LOADED
);

const modalHeader = setUpAccountsPage.getModalHeader();
await expect( modalHeader ).toContainText(
'Create Google Merchant Center Account'
);

const modalCheckbox = setUpAccountsPage.getModalCheckbox();
await expect( modalCheckbox ).toBeEnabled();

const modalPrimaryButton =
setUpAccountsPage.getModalPrimaryButton();
await expect( modalPrimaryButton ).toContainText(
'Create account'
);
await expect( modalPrimaryButton ).toBeDisabled();

// Select the checkbox, the button should be enabled.
await modalCheckbox.click();
await expect( modalPrimaryButton ).toBeEnabled();
} );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the same test is already in place.

test( 'click "Create account" button should see the modal of confirmation of creating account', async () => {
// Click the create account button
const createAccountButton =
setUpAccountsPage.getMCCreateAccountButtonFromPage();
await createAccountButton.click();
await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED );
const modalHeader = setUpAccountsPage.getModalHeader();
await expect( modalHeader ).toContainText(
'Create Google Merchant Center Account'
);
const modalCheckbox = setUpAccountsPage.getModalCheckbox();
await expect( modalCheckbox ).toBeEnabled();
const createAccountButtonFromModal =
setUpAccountsPage.getMCCreateAccountButtonFromModal();
await expect( createAccountButtonFromModal ).toBeDisabled();
// Click the checkbox of accepting ToS, the create account button will be enabled.
await modalCheckbox.click();
await expect( createAccountButtonFromModal ).toBeEnabled();
} );

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test removed @eason9487

Comment on lines 259 to 265
return this.page
.locator( '.components-card-body', {
has: this.page.locator( '.gla-account-card__title', {
hasText: 'Google',
} ),
} )
.first();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May add a CSS class for this as well like the getMCAccountCard method below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member

@eason9487 eason9487 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the original components under the js/src/components/google-mc-account-card directory are only used for the Onboarding accounts step, except for ConnectedGoogleMCAccountCard which is shared by the Setting page. Given this, extracting some code into shared components or functions and creating another ConnectedMC component would make for unnecessary redundant code and reduce maintainability.

This PR should be able to be done based on the original existing code and minimize code changes to optimally leverage codes that have been run for 2-3 years.

Suggest exploring possibilities in this direction:

  • Adjust the google-mc-account-card/connect-mc/index.js to fulfill the new UI and functionality rather than creating google-combo-account-card/connect-mc/connect-mc.js
  • Revert the extraction of AccountConnectionStatus and hasAccountConnectionIssue
  • ConnectedGoogleMCAccountCard will only be used on the Settings page and its hideAccountSwitch prop will no longer be needed
    • Remove code related to hideAccountSwitch
    • Rename to specify that this component is dedicated to the Settings page

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog: update Big changes to something that wasn't broken.
Projects
None yet
4 participants