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

Show promo for setting up Google Ads on the product feed tabs #2539 #2641

Merged
merged 39 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
57b4adb
intial commit
kt-12 Oct 10, 2024
3922ab1
CreateCampaignNotice component
kt-12 Oct 12, 2024
711160d
Add CreateCampaignNotice to the product-feed page
kt-12 Oct 12, 2024
656c4a8
check for google ads connection
kt-12 Oct 12, 2024
0170b7d
Get Campaign Notice on prodict feed E2E
kt-12 Oct 14, 2024
7b963a4
fix bug
kt-12 Oct 14, 2024
cefcd7a
fix e2e account connection
kt-12 Oct 14, 2024
25365e5
add more spaces.
kt-12 Oct 14, 2024
a3b00cb
remove unused dashboard variable.
kt-12 Oct 14, 2024
df22e4a
remove incorrect comment
kt-12 Oct 14, 2024
15123b0
remove unused fulfill's
kt-12 Oct 14, 2024
120d5fd
Avoid querying campaigns in useAdsCampaigns before connection
joemcgill Oct 15, 2024
922edb4
Fix E2E tests
joemcgill Oct 15, 2024
c19de71
replace child with text content
kt-12 Oct 18, 2024
1536e4b
simplyfy logic
kt-12 Oct 18, 2024
4631978
simplify logic
kt-12 Oct 18, 2024
6181299
slight rearragment
kt-12 Oct 18, 2024
c65f071
change variable name
kt-12 Oct 18, 2024
59ac898
remove alaising
kt-12 Oct 18, 2024
1b739a6
remove class name
kt-12 Oct 18, 2024
6d90bb9
fix jest error
kt-12 Oct 21, 2024
199675d
Fix notice styling
joemcgill Oct 21, 2024
71a823c
Fix CSS lint issues
joemcgill Oct 21, 2024
80016a2
Review styles.
asvinb Oct 22, 2024
81aba99
Merge branch 'feature/2460-google-ads-value-prop' into feature/2539-s…
kt-12 Oct 23, 2024
05aecee
Merge branch 'feature/2539-show-promo' of https://github.com/woocomme…
kt-12 Oct 23, 2024
165543c
Convert notice to a Flex component in the CardFooter
joemcgill Oct 24, 2024
51dfbca
this could be null so return []
kt-12 Oct 25, 2024
3f824c0
update condition here
kt-12 Oct 25, 2024
660ad11
only fetch campaigns from fully connected account
kt-12 Oct 25, 2024
052a386
match loading attributes with that of google ads account api status
kt-12 Oct 25, 2024
77dd887
remove button click
kt-12 Oct 25, 2024
30237f3
revert back changes to useAdsCampaigns
kt-12 Oct 28, 2024
65972ac
fix e2e
kt-12 Oct 28, 2024
2954cf6
update e2e
kt-12 Oct 30, 2024
09146cf
add page reload
kt-12 Oct 30, 2024
c1bfc5b
remove unused variable.
kt-12 Oct 30, 2024
33ebb24
remove unused css
kt-12 Oct 30, 2024
616756f
remove unused mock. use page.goto instead or reload and rearrage tests
kt-12 Oct 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions js/src/dashboard/all-programs-table-card/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,11 @@ const AllProgramsTableCard = ( props ) => {
const { formatAmount } = useAdsCurrency();
const { data: finalCountryCodesData } =
useTargetAudienceFinalCountryCodes();
const { data: adsCampaignsData } = useAdsCampaigns();
const { data: adsCampaignsData, loaded: adsCampaignsLoaded } =
useAdsCampaigns();
const map = useCountryKeyNameMap();

if ( ! finalCountryCodesData || ! adsCampaignsData ) {
if ( ! finalCountryCodesData || ! adsCampaignsLoaded ) {
return <AppSpinner />;
}

Expand Down
2 changes: 1 addition & 1 deletion js/src/dashboard/all-programs-table-card/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe( 'AllProgramsTableCard', () => {
let mockedCampaigns = [];

useAdsCampaigns.mockImplementation( () => {
return { data: mockedCampaigns };
return { data: mockedCampaigns, loaded: true };
} );

mockCampaigns = ( ...campaigns ) => {
Expand Down
31 changes: 20 additions & 11 deletions js/src/hooks/useAdsCampaigns.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
* Internal dependencies
*/
import { STORE_KEY } from '.~/data';
import { glaData } from '.~/constants';
import useGoogleAdsAccount from '.~/hooks/useGoogleAdsAccount';
import useIsEqualRefValue from '.~/hooks/useIsEqualRefValue';
import { GOOGLE_ADS_ACCOUNT_STATUS } from '.~/constants';

const selectorName = 'getAdsCampaigns';

Expand All @@ -30,19 +31,22 @@
*/
const useAdsCampaigns = ( ...query ) => {
const queryRefValue = useIsEqualRefValue( query );
const {
googleAdsAccount,
hasGoogleAdsConnection,
hasFinishedResolution,
isResolving,
} = useGoogleAdsAccount();

return useSelect(
( select ) => {
// TODO: ideally adsSetupComplete should be retrieved from API endpoint
// and then put into wp-data.
// With that in place, then we don't need to depend on glaData
// which requires force reload using window.location.href.
const { adsSetupComplete } = glaData;

if ( ! adsSetupComplete ) {
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
if (
! hasGoogleAdsConnection ||
kt-12 marked this conversation as resolved.
Show resolved Hide resolved
googleAdsAccount?.status !== GOOGLE_ADS_ACCOUNT_STATUS.CONNECTED

Check warning on line 45 in js/src/hooks/useAdsCampaigns.js

View check run for this annotation

Codecov / codecov/patch

js/src/hooks/useAdsCampaigns.js#L45

Added line #L45 was not covered by tests
) {
return {
loading: false,
loaded: true,
loading: isResolving,
loaded: hasFinishedResolution,
data: [],
};
}
Expand All @@ -62,7 +66,12 @@
data,
};
},
[ queryRefValue ]
[
hasFinishedResolution,
hasGoogleAdsConnection,
isResolving,
queryRefValue,
]
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Flex, FlexItem } from '@wordpress/components';

/**
* Internal dependencies
*/
import AddPaidCampaignButton from '.~/components/paid-ads/add-paid-campaign-button';
import useMCProductStatistics from '.~/hooks/useMCProductStatistics';
import useAdsCampaigns from '.~/hooks/useAdsCampaigns';
import './index.scss';

const CreateCampaignNotice = () => {
const { data: products } = useMCProductStatistics();
const { loaded: campaignsLoaded, data: campaigns } = useAdsCampaigns();

if (
! products?.statistics?.active ||
! campaignsLoaded ||
campaigns?.length > 0

Check warning on line 22 in js/src/product-feed/product-statistics/create-campaign-notice/index.js

View check run for this annotation

Codecov / codecov/patch

js/src/product-feed/product-statistics/create-campaign-notice/index.js#L22

Added line #L22 was not covered by tests
) {
return null;
}

return (

Check warning on line 27 in js/src/product-feed/product-statistics/create-campaign-notice/index.js

View check run for this annotation

Codecov / codecov/patch

js/src/product-feed/product-statistics/create-campaign-notice/index.js#L27

Added line #L27 was not covered by tests
<Flex className="gla-ads-inline-notice">
<FlexItem>
<p>
{ __(
'You have approved products. Create a Google Ads campaign to reach more customers and drive more sales.',
'google-listings-and-ads'
) }
</p>
<AddPaidCampaignButton
isSmall={ false }
eventProps={ {
context: 'product-feed-overview-promotion',
} }
>
{ __( 'Create Campaign', 'google-listings-and-ads' ) }
</AddPaidCampaignButton>
</FlexItem>
</Flex>
);
};

export default CreateCampaignNotice;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.gla-ads-inline-notice {
background-color: #f0f6fc;
border-left: none;
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
margin-bottom: $grid-unit-20;
padding: $grid-unit-20 $grid-unit-30;

p {
margin-top: 0;
}
}
3 changes: 3 additions & 0 deletions js/src/product-feed/product-statistics/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import SyncStatus from '.~/product-feed/product-statistics/status-box/sync-statu
import SyncProductStatistics from '.~/product-feed/product-statistics/status-box/sync-product-statistics';
import FeedStatus from '.~/product-feed/product-statistics/status-box/feed-status';
import AccountStatus from '.~/product-feed/product-statistics/status-box/account-status';
import CreateCampaignNotice from '.~/product-feed/product-statistics/create-campaign-notice';
import Text from '.~/components/app-text';
import AppSpinner from '.~/components/app-spinner';
import './index.scss';
Expand Down Expand Up @@ -133,7 +134,9 @@ const ProductStatistics = () => {
</SummaryList>
) }
</CardBody>

<CardFooter gap={ 0 }>
<CreateCampaignNotice />
<FeedStatus />
<SyncStatus />
<AccountStatus />
Expand Down
206 changes: 206 additions & 0 deletions tests/e2e/specs/product-feed/product-feed-campaign-notice.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/**
* External dependencies
*/
import { expect, test } from '@playwright/test';
/**
* Internal dependencies
*/
import { clearOnboardedMerchant, setOnboardedMerchant } from '../../utils/api';
import ProductFeedPage from '../../utils/pages/product-feed';
import SetupAdsAccountsPage from '../../utils/pages/setup-ads/setup-ads-accounts.js';
import { LOAD_STATE } from '../../utils/constants';

test.use( { storageState: process.env.ADMINSTATE } );

test.describe.configure( { mode: 'serial' } );

/**
* @type {import('../../utils/pages/setup-ads/setup-ads-accounts').default} setupAdsAccounts
*/
let setupAdsAccounts = null;

/**
* @type {import('../../utils/pages/product-feed').default} productFeedPage
*/
let productFeedPage = null;

/**
* @type {import('@playwright/test').Page} page
*/
let page = null;

test.describe( 'Product Feed Page', () => {
test.beforeAll( async ( { browser } ) => {
page = await browser.newPage();
productFeedPage = new ProductFeedPage( page );
setupAdsAccounts = new SetupAdsAccountsPage( page );
await Promise.all( [
setupAdsAccounts.mockAdsAccountsResponse( [] ),
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
productFeedPage.mockRequests(),
setOnboardedMerchant(),
] );
} );

test.afterAll( async () => {
await clearOnboardedMerchant();
await page.close();
} );

test.describe( 'No campaign', () => {
test.beforeAll( async () => {
await productFeedPage.fulfillAdsCampaignsRequest( [] );
await productFeedPage.goto();
} );

test( 'No active product and no campaign; Do not display campaign notice', async () => {
await expect(
page.getByRole( 'heading', { level: 1, name: 'Product Feed' } )
).toBeVisible();

await productFeedPage.fulfillProductStatisticsRequest( {
timestamp: 1695011644,
statistics: {
active: 0,
expiring: 0,
pending: 0,
disapproved: 0,
not_synced: 1137,
},
scheduled_sync: 0,
loading: false,
} );

await page.reload();
await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED );
eason9487 marked this conversation as resolved.
Show resolved Hide resolved

await expect(
page.getByRole( 'heading', {
name: 'Overview',
} )
).toBeVisible();

await expect(
await productFeedPage.getActiveProductValueElement()
).toBeVisible();

await expect(
( await productFeedPage.getActiveProductValue() ).trim()
).toBe( '0' );

await expect(
await productFeedPage.getCampaignNoticeSection()
).not.toBeVisible();
} );

test( 'Has active product but no campaign; Display campaign notice', async () => {
await productFeedPage.fulfillProductStatisticsRequest( {
timestamp: 1695011644,
statistics: {
active: 1,
expiring: 0,
pending: 0,
disapproved: 0,
not_synced: 1137,
},
scheduled_sync: 0,
loading: false,
} );

await page.reload();
await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED );

await expect(
await productFeedPage.getActiveProductValueElement()
).toBeVisible();

await expect(
( await productFeedPage.getActiveProductValue() ).trim()
).toBe( '1' );

const noticeSection =
await productFeedPage.getCampaignNoticeSection();
const createCampaignButton =
await productFeedPage.getInNoticeCreateCampaignButton();

await expect( noticeSection ).toBeVisible();
await expect( createCampaignButton ).toBeVisible();
await createCampaignButton.click();
await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED );
await expect(
page.getByRole( 'heading', {
level: 1,
name: 'Set up your accounts',
} )
).toBeVisible();

await productFeedPage.goto();
await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED );

await expect(
page.getByRole( 'heading', { level: 1, name: 'Product Feed' } )
).toBeVisible();
eason9487 marked this conversation as resolved.
Show resolved Hide resolved
} );
} );

test.describe( 'Has campaign', () => {
test.beforeAll( async () => {
await productFeedPage.fulfillAdsCampaignsRequest( [
{
id: 111111111,
name: 'Test Campaign',
status: 'enabled',
type: 'performance_max',
amount: 1,
country: 'US',
targeted_locations: [ 'US' ],
},
] );
} );

test( 'Has active product and a campaign; Do not display campaign notice', async () => {
await productFeedPage.goto();
await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED );

await expect(
await productFeedPage.getActiveProductValueElement()
).toBeVisible();

await expect(
( await productFeedPage.getActiveProductValue() ).trim()
).toBe( '1' );

await expect(
await productFeedPage.getCampaignNoticeSection()
).not.toBeVisible();
} );

test( 'Has campaign but no active product; Do not display campaign notice', async () => {
await productFeedPage.fulfillProductStatisticsRequest( {
timestamp: 1695011644,
statistics: {
active: 0,
expiring: 0,
pending: 0,
disapproved: 0,
not_synced: 1137,
},
scheduled_sync: 0,
loading: false,
} );
await page.reload();
await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED );

await expect(
await productFeedPage.getActiveProductValueElement()
).toBeVisible();

await expect(
( await productFeedPage.getActiveProductValue() ).trim()
).toBe( '0' );

await expect(
await productFeedPage.getCampaignNoticeSection()
).not.toBeVisible();
} );
} );
} );
3 changes: 3 additions & 0 deletions tests/e2e/specs/setup-mc/step-4-complete-campaign.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@ test.describe( 'Complete your campaign', () => {
test( 'should not see the Free Ad Credit section if the account is not eligible', async () => {
await setupAdsAccountPage.mockAdsAccountConnected();
await completeCampaign.goto();
await completeCampaign.clickCreatePaidAdButton();
await completeCampaign.clickSkipPaidAdsCreationButon();
await completeCampaign.fulfillAdsCampaignsRequest( [] );
await setupAdsAccountPage.awaitAdsConnectionResponse();

// Check we are on the correct page.
Expand Down
Loading