From c0df58ec36787c318cf1ab3e37f573e4c2993cae Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Thu, 31 Aug 2023 15:42:30 +0800 Subject: [PATCH 01/17] Add more helper methods for mocking requests --- tests/e2e/utils/mock-requests.js | 74 ++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/e2e/utils/mock-requests.js b/tests/e2e/utils/mock-requests.js index 406ccf1caa..996bbddec5 100644 --- a/tests/e2e/utils/mock-requests.js +++ b/tests/e2e/utils/mock-requests.js @@ -64,6 +64,16 @@ export default class MockRequests { await this.fulfillRequest( /\/wc\/gla\/jetpack\/connected\b/, payload ); } + /** + * Fulfill the request to connect Jetpack. + * + * @param {Object} payload + * @return {Promise} + */ + async fulfillConnectJetPack( payload ) { + await this.fulfillRequest( /\/wc\/gla\/jetpack\/connect\b/, payload ); + } + /** * Fulfill the Google Connection request. * @@ -74,6 +84,16 @@ export default class MockRequests { await this.fulfillRequest( /\/wc\/gla\/google\/connected\b/, payload ); } + /** + * Fulfill the request to connect Google. + * + * @param {Object} payload + * @return {Promise} + */ + async fulfillConnectGoogle( payload ) { + await this.fulfillRequest( /\/wc\/gla\/google\/connect\b/, payload ); + } + /** * Fulfill the Ads Connection request. * @@ -93,4 +113,58 @@ export default class MockRequests { async fulfillSettingsSync( payload ) { await this.fulfillRequest( /\/wc\/gla\/mc\/settings\/sync\b/, payload ); } + + /** + * Mock the request to connect Jetpack + */ + async mockJetpackConnect( url ) { + await this.fulfillConnectJetPack( { url } ); + } + + /** + * Mock Jetpack as connected + */ + async mockJetpackConnected( displayName = 'John', email = 'mail@example.com' ) { + await this.fulfillJetPackConnection( { + active: 'yes', + owner: 'yes', + displayName, + email, + } ); + } + + /** + * Mock the request to connect Google + */ + async mockGoogleConnect( url ) { + await this.fulfillConnectGoogle( { url } ); + } + + /** + * Mock Google as connected + */ + async mockGoogleConnected( email = 'mail@example.com' ) { + await this.fulfillGoogleConnection( { + active: 'yes', + email, + scope: [ + 'https:\/\/www.googleapis.com\/auth\/content', + 'https:\/\/www.googleapis.com\/auth\/adwords', + 'https:\/\/www.googleapis.com\/auth\/userinfo.email', + 'https:\/\/www.googleapis.com\/auth\/siteverification.verify_only', + 'openid', + ], + } ); + } + + /** + * Mock Google as not connected + */ + async mockGoogleNotConnected() { + await this.fulfillGoogleConnection( { + active: 'no', + email: '', + scope: [], + } ); + } } From 6b48f46372b2e16ac6db80f771991b25dbb11985 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Thu, 31 Aug 2023 15:44:35 +0800 Subject: [PATCH 02/17] Add "Set Up Accounts" page --- .../pages/setup-mc/step-1-set-up-accounts.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js diff --git a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js new file mode 100644 index 0000000000..33f0da2d43 --- /dev/null +++ b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js @@ -0,0 +1,38 @@ +/** + * Internal dependencies + */ +import MockRequests from '../../mock-requests'; + +/** + * Set up accounts page object class. + */ +export default class SetUpAccountsPage extends MockRequests { + /** + * @param {import('@playwright/test').Page} page + */ + constructor( page ) { + super( page ); + this.page = page; + } + + /** + * Close the current page. + * + * @return {Promise} + */ + async closePage() { + await this.page.close(); + } + + /** + * Go to the set up mc page. + * + * @return {Promise} + */ + async goto() { + await this.page.goto( + '/wp-admin/admin.php?page=wc-admin&path=%2Fgoogle%2Fsetup-mc', + { waitUntil: 'networkidle' } + ); + } +} From 7269f254263849b29d86dc916f25d203e58159ed Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Thu, 31 Aug 2023 15:49:23 +0800 Subject: [PATCH 03/17] Rewrite the existing setup accounts test to use the shared page Thanks to @jorgemd24, using the shared page for testing significantly decrease the testing time from 50s to 25s when using 1 worker. --- .../specs/setup-mc/step-1-accounts.test.js | 179 ++++++++---------- 1 file changed, 74 insertions(+), 105 deletions(-) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 4d39804779..6d0af01125 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -1,3 +1,8 @@ +/** + * Internal dependencies + */ +import SetUpAccountsPage from '../../utils/pages/setup-mc/step-1-set-up-accounts.js'; + /** * External dependencies */ @@ -5,19 +10,30 @@ const { test, expect } = require( '@playwright/test' ); test.use( { storageState: process.env.ADMINSTATE } ); -test.describe( 'Merchant who is getting started', () => { - test.beforeEach( async ( { page } ) => { - // Go to the setup page short way - directly via URL. - await page.goto( - '/wp-admin/admin.php?page=wc-admin&path=%2Fgoogle%2Fsetup-mc' - ); - await page.waitForLoadState( 'networkidle' ); +test.describe.configure( { mode: 'serial' } ); + +/** + * @type {import('../../utils/pages/setup-mc/step-1-set-up-accounts.js').default} setUpAccountsPage + */ +let setUpAccountsPage = null; + +/** + * @type {import('@playwright/test').Page} page + */ +let page = null; + +test.describe( 'Set up accounts', () => { + test.beforeAll( async ( { browser } ) => { + page = await browser.newPage(); + setUpAccountsPage = new SetUpAccountsPage( page ); + await setUpAccountsPage.goto(); } ); - test( 'should see accounts step header, "Connect your WordPress.com account" & connect button', async ( { - page, - } ) => { - // Wait for API calls and the page to render. + test.afterAll( async () => { + await setUpAccountsPage.closePage(); + } ); + + test( 'should see accounts step header, "Connect your WordPress.com account" & connect button', async () => { await expect( page.getByRole( 'heading', { name: 'Set up your accounts' } ) ).toBeVisible(); @@ -33,105 +49,58 @@ test.describe( 'Merchant who is getting started', () => { ).toBeEnabled(); } ); - test( 'after clicking the "Connect your WordPress.com account" button, should send an API request to connect Jetpack, and redirect to the returned URL', async ( { - page, - baseURL, - } ) => { - // Mock Jetpack connect - await page.route( /\/wc\/gla\/jetpack\/connect\b/, ( route ) => - route.fulfill( { - content: 'application/json', - headers: { 'Access-Control-Allow-Origin': '*' }, - body: JSON.stringify( { - url: baseURL + 'auth_url', - } ), - } ) - ); - - // Click the enabled connect button. - page.locator( "//button[text()='Connect'][not(@disabled)]" ).click(); - await page.waitForLoadState( 'networkidle' ); - - // Expect the user to be redirected - await page.waitForURL( baseURL + 'auth_url' ); - } ); -} ); + test.describe( 'Connect WordPress.com account', () => { + test( 'should send an API request to connect Jetpack, and redirect to the returned URL', async ( { baseURL } ) => { + // Mock Jetpack connect + setUpAccountsPage.mockJetpackConnect( baseURL + 'auth_url' ); -test.describe( 'Merchant with Jetpack connected & Google not connected', () => { - test.beforeEach( async ( { page } ) => { - // Mock Jetpack as connected - await page.route( /\/wc\/gla\/jetpack\/connected\b/, ( route ) => - route.fulfill( { - content: 'application/json', - headers: { 'Access-Control-Allow-Origin': '*' }, - body: JSON.stringify( { - active: 'yes', - owner: 'yes', - displayName: 'testUser', - email: 'mail@example.com', - } ), - } ) - ); - - // Mock google as not connected. - // When pending even WPORG will not render yet. - // If not mocked will fail and render nothing, - // as Jetpack is mocked only on the client-side. - await page.route( /\/wc\/gla\/google\/connected\b/, ( route ) => - route.fulfill( { - content: 'application/json', - headers: { 'Access-Control-Allow-Origin': '*' }, - body: JSON.stringify( { - active: 'no', - email: '', - } ), - } ) - ); - - // Go to the setup page short way - directly via URL. - await page.goto( - '/wp-admin/admin.php?page=wc-admin&path=%2Fgoogle%2Fsetup-mc' - ); - - // Wait for API calls and the page to render. - await page.waitForLoadState( 'networkidle' ); - page.getByRole( 'heading', { name: 'Set up your accounts' } ); + // Click the enabled connect button. + page.locator( "//button[text()='Connect'][not(@disabled)]" ).click(); + await page.waitForLoadState( 'networkidle' ); + + // Expect the user to be redirected + await page.waitForURL( baseURL + 'auth_url' ); + } ); } ); - test( 'should see their WPORG email, "Google" title & connect button', async ( { - page, - } ) => { - await expect( page.getByText( 'mail@example.com' ) ).toBeVisible(); + test.describe( 'Connect Google account', () => { + test.beforeAll( async () => { + // Mock Jetpack as connected + await setUpAccountsPage.mockJetpackConnected( 'Test user', 'mail@example.com' ); - await expect( - page.getByText( 'Google', { exact: true } ) - ).toBeVisible(); + // Mock google as not connected. + // When pending even WPORG will not render yet. + // If not mocked will fail and render nothing, + // as Jetpack is mocked only on the client-side. + await setUpAccountsPage.mockGoogleNotConnected(); - expect( - page.getByRole( 'button', { name: 'Connect' } ).first() - ).toBeEnabled(); - } ); + setUpAccountsPage.goto(); + + page.getByRole( 'heading', { name: 'Set up your accounts' } ); + } ); + + test( 'should see their WPORG email, "Google" title & connect button', async () => { + await expect( page.getByText( 'mail@example.com' ) ).toBeVisible(); + + await expect( + page.getByText( 'Google', { exact: true } ) + ).toBeVisible(); + + expect( + page.getByRole( 'button', { name: 'Connect' } ).first() + ).toBeEnabled(); + } ); + + test( 'after clicking the "Connect your Google account" button should send an API request to connect Google account, and redirect to the returned URL', async ( { baseURL } ) => { + // Mock google connect. + await setUpAccountsPage.mockGoogleConnect( baseURL + 'google_auth' ); + + // Click the enabled connect button + page.locator( "//button[text()='Connect'][not(@disabled)]" ).click(); + await page.waitForLoadState( 'networkidle' ); - test( 'after clicking the "Connect your Google account" button should send an API request to connect Google account, and redirect to the returned URL', async ( { - page, - baseURL, - } ) => { - // Mock google connect. - await page.route( /\/wc\/gla\/google\/connect\b/, ( route ) => - route.fulfill( { - content: 'application/json', - headers: { 'Access-Control-Allow-Origin': '*' }, - body: JSON.stringify( { - url: baseURL + 'google_auth', - } ), - } ) - ); - - // Click the enabled connect button - page.locator( "//button[text()='Connect'][not(@disabled)]" ).click(); - await page.waitForLoadState( 'networkidle' ); - - // Expect the user to be redirected - await page.waitForURL( baseURL + 'google_auth' ); + // Expect the user to be redirected + await page.waitForURL( baseURL + 'google_auth' ); + } ); } ); } ); From f1fe0405c35da1ec533555088e479dc5f0196ef2 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Fri, 1 Sep 2023 18:35:49 +0800 Subject: [PATCH 04/17] Add more mock requests --- tests/e2e/utils/mock-requests.js | 139 +++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 5 deletions(-) diff --git a/tests/e2e/utils/mock-requests.js b/tests/e2e/utils/mock-requests.js index 996bbddec5..a59730be15 100644 --- a/tests/e2e/utils/mock-requests.js +++ b/tests/e2e/utils/mock-requests.js @@ -18,9 +18,10 @@ export default class MockRequests { * @param {Object} payload The payload to send. * @return {Promise} */ - async fulfillRequest( url, payload ) { + async fulfillRequest( url, payload, status = 200 ) { await this.page.route( url, ( route ) => route.fulfill( { + status, content: 'application/json', headers: { 'Access-Control-Allow-Origin': '*' }, body: JSON.stringify( payload ), @@ -54,6 +55,47 @@ export default class MockRequests { ); } + /** + * Fulfill the MC accounts request. + * + * @param {Object} payload + * @return {Promise} + */ + async fulfillMCAccounts( payload, status = 200 ) { + await this.fulfillRequest( + /\/wc\/gla\/mc\/accounts\b/, + payload, + status + ); + } + + /** + * Fulfill the MC accounts claim-overwrite request. + * + * @param {Object} payload + * @return {Promise} + */ + async fulfillMCAccountsClaimOverwrite( payload, status = 200 ) { + await this.fulfillRequest( + /\/wc\/gla\/mc\/accounts\/claim-overwrite\b/, + payload, + status + ); + } + + /** + * Fulfill the MC connection request. + * + * @param {Object} payload + * @return {Promise} + */ + async fulfillMCConnection( payload ) { + await this.fulfillRequest( + /\/wc\/gla\/mc\/connection\b/, + payload + ); + } + /** * Fulfill the JetPack Connection request. * @@ -122,7 +164,7 @@ export default class MockRequests { } /** - * Mock Jetpack as connected + * Mock Jetpack as connected. */ async mockJetpackConnected( displayName = 'John', email = 'mail@example.com' ) { await this.fulfillJetPackConnection( { @@ -134,14 +176,14 @@ export default class MockRequests { } /** - * Mock the request to connect Google + * Mock the request to connect Google. */ async mockGoogleConnect( url ) { await this.fulfillConnectGoogle( { url } ); } /** - * Mock Google as connected + * Mock Google as connected. */ async mockGoogleConnected( email = 'mail@example.com' ) { await this.fulfillGoogleConnection( { @@ -158,7 +200,7 @@ export default class MockRequests { } /** - * Mock Google as not connected + * Mock Google as not connected. */ async mockGoogleNotConnected() { await this.fulfillGoogleConnection( { @@ -167,4 +209,91 @@ export default class MockRequests { scope: [], } ); } + + /** + * Mock MC as connected. + */ + async mockMCConnected( id = 1234 ) { + await this.fulfillMCConnection( { + id, + status: 'connected', + } ); + } + + /** + * Mock MC as not connected. + */ + async mockMCNotConnected() { + await this.fulfillMCConnection( { + id: 0, + status: 'disconnected', + } ); + } + + /** + * Mock MC has accounts. + */ + async mockMCHasAccounts() { + await this.fulfillMCAccounts( [ + { + id: 12345, + subaccount: true, + name: 'MC Account 1', + domain: 'https:\/\/example.com', + }, + { + id: 23456, + subaccount: true, + name: 'MC Account 2', + domain: 'https:\/\/example.com', + }, + ] ); + } + + /** + * Mock MC has no accounts. + */ + async mockMCHasNoAccounts() { + await this.fulfillMCAccounts( [] ); + } + + /** + * Mock MC create account where the website is not claimed. + */ + async mockMCCreateAccountWebsiteNotClaimed( id = 12345 ) { + await this.fulfillMCAccounts( { + id, + subaccount: null, + name: null, + domain: null, + } ); + } + + /** + * Mock MC create account where the website is claimed. + */ + async mockMCCreateAccountWebsiteClaimed( id = 12345, websiteUrl = 'example.com' ) { + await this.fulfillMCAccounts( + { + message: 'Website already claimed, use overwrite to complete the process.', + id, + website_url: websiteUrl, + }, + 403 + ); + } + + /** + * Mock MC accounts claim overwrite. + */ + async mockMCAccountsClaimOverwrite( id = 12345 ) { + await this.fulfillMCAccountsClaimOverwrite( + { + id, + subaccount: null, + name: null, + domain: null, + }, + ); + } } From 26100e515380a93a9dccbf5164519dc3a1e9b842 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Fri, 1 Sep 2023 18:36:27 +0800 Subject: [PATCH 05/17] Add functions to get elements in set up accounts page --- .../pages/setup-mc/step-1-set-up-accounts.js | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js index 33f0da2d43..078980b65d 100644 --- a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js +++ b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js @@ -35,4 +35,177 @@ export default class SetUpAccountsPage extends MockRequests { { waitUntil: 'networkidle' } ); } + + /** + * Get "Create account" button. + * + * @return {import('@playwright/test').Locator} Get "Create account" button. + */ + getCreateAccountButton() { + return this.page.getByRole( 'button', { + name: 'Create account', + exact: true + } ); + } + + /** + * Get MC "Create account" button from the page. + * + * @return {import('@playwright/test').Locator} Get MC "Create account" button from the page. + */ + getMCCreateAccountButtonFromPage() { + const button = this.getCreateAccountButton(); + return button.locator( ':scope.is-secondary' ); + } + + /** + * Get MC "Create account" button from the modal. + * + * @return {import('@playwright/test').Locator} Get MC "Create account" button from the modal. + */ + getMCCreateAccountButtonFromModal() { + const button = this.getCreateAccountButton(); + return button.locator( ':scope.is-primary' ); + } + + /** + * Get .gla-account-card__title class. + * + * @return {import('@playwright/test').Locator} Get .gla-account-card__title class. + */ + getCardTitleClass() { + return this.page.locator( '.gla-account-card__title' ); + } + + /** + * Get .gla-account-card__description class. + * + * @return {import('@playwright/test').Locator} Get .gla-account-card__description class. + */ + getCardDescriptionClass() { + return this.page.locator( '.gla-account-card__description' ); + } + + /** + * Get Jetpack description row. + * + * @return {import('@playwright/test').Locator} Get Jetpack description row. + */ + getJetpackDescriptionRow() { + return this.getCardDescriptionClass().first(); + } + + /** + * Get Google description row. + * + * @return {import('@playwright/test').Locator} Get Google description row. + */ + getGoogleDescriptionRow() { + return this.getCardDescriptionClass().nth( 1 ); + } + + /** + * Get Merchant Center description row. + * + * @return {import('@playwright/test').Locator} Get Merchant Center description row. + */ + getMCDescriptionRow() { + return this.getCardDescriptionClass().nth( 2 ); + } + + /** + * Get Google Merchant Center title. + * + * @return {import('@playwright/test').Locator} Get Google Merchant Center title. + */ + getMCTitleRow() { + return this.getCardTitleClass().nth( 2 ); + } + + /** + * Get modal header. + * + * @return {import('@playwright/test').Locator} Get modal header. + */ + getModalHeader() { + return this.page.locator( '.components-modal__header' ); + } + + /** + * Get modal checkbox. + * + * @return {import('@playwright/test').Locator} Get modal checkbox. + */ + getModalCheckbox() { + return this.page.getByRole( 'checkbox' ); + } + + /** + * Get .gla-connected-icon-label class. + * + * @return {import('@playwright/test').Locator} Get .gla-connected-icon-label class. + */ + getConnectedLabelClass() { + return this.page.locator( '.gla-connected-icon-label' ); + } + + /** + * Get Jetpack connected label. + * + * @return {import('@playwright/test').Locator} Get Jetpack connected label. + */ + getJetpackConnectedLabel() { + return this.getConnectedLabelClass().first(); + } + + /** + * Get Google connected label. + * + * @return {import('@playwright/test').Locator} Get Google connected label. + */ + getGoogleConnectedLabel() { + return this.getConnectedLabelClass().nth( 1 ); + } + + /** + * Get Merchant Center connected label. + * + * @return {import('@playwright/test').Locator} Get Merchant Center connected label. + */ + getMCConnectedLabel() { + return this.getConnectedLabelClass().nth( 2 ); + } + + /** + * Get "Reclaim my URL" button. + * + * @return {import('@playwright/test').Locator} Get "Reclaim my URL" button. + */ + getReclaimMyURLButton() { + return this.page.getByRole( 'button', { + name: 'Reclaim my URL', + exact: true + } ); + } + + /** + * Get "Switch account" button. + * + * @return {import('@playwright/test').Locator} Get "Switch account" button. + */ + getSwitchAccountButton() { + return this.page.getByRole( 'button', { + name: 'Switch account', + exact: true + } ); + } + + /** + * Get reclaiming URL input. + * + * @return {import('@playwright/test').Locator} Get reclaiming URL input. + */ + getReclaimingURLInput() { + return this.page.locator( 'input#inspector-input-control-0' ); + } } From 075de21bbaa658df6bddd2c0a032b7284b8c6fc7 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Fri, 1 Sep 2023 18:37:15 +0800 Subject: [PATCH 06/17] Add tests for connecting merchant center --- .../specs/setup-mc/step-1-accounts.test.js | 159 +++++++++++++++++- 1 file changed, 152 insertions(+), 7 deletions(-) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 6d0af01125..a3f2aedf0f 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -26,7 +26,6 @@ test.describe( 'Set up accounts', () => { test.beforeAll( async ( { browser } ) => { page = await browser.newPage(); setUpAccountsPage = new SetUpAccountsPage( page ); - await setUpAccountsPage.goto(); } ); test.afterAll( async () => { @@ -34,6 +33,8 @@ test.describe( 'Set up accounts', () => { } ); test( 'should see accounts step header, "Connect your WordPress.com account" & connect button', async () => { + await setUpAccountsPage.goto(); + await expect( page.getByRole( 'heading', { name: 'Set up your accounts' } ) ).toBeVisible(); @@ -44,7 +45,7 @@ test.describe( 'Set up accounts', () => { ) ).toBeVisible(); - expect( + await expect( page.getByRole( 'button', { name: 'Connect' } ).first() ).toBeEnabled(); } ); @@ -52,7 +53,7 @@ test.describe( 'Set up accounts', () => { test.describe( 'Connect WordPress.com account', () => { test( 'should send an API request to connect Jetpack, and redirect to the returned URL', async ( { baseURL } ) => { // Mock Jetpack connect - setUpAccountsPage.mockJetpackConnect( baseURL + 'auth_url' ); + await setUpAccountsPage.mockJetpackConnect( baseURL + 'auth_url' ); // Click the enabled connect button. page.locator( "//button[text()='Connect'][not(@disabled)]" ).click(); @@ -66,7 +67,7 @@ test.describe( 'Set up accounts', () => { test.describe( 'Connect Google account', () => { test.beforeAll( async () => { // Mock Jetpack as connected - await setUpAccountsPage.mockJetpackConnected( 'Test user', 'mail@example.com' ); + await setUpAccountsPage.mockJetpackConnected( 'Test user', 'jetpack@example.com' ); // Mock google as not connected. // When pending even WPORG will not render yet. @@ -74,19 +75,20 @@ test.describe( 'Set up accounts', () => { // as Jetpack is mocked only on the client-side. await setUpAccountsPage.mockGoogleNotConnected(); - setUpAccountsPage.goto(); + await setUpAccountsPage.goto(); page.getByRole( 'heading', { name: 'Set up your accounts' } ); } ); test( 'should see their WPORG email, "Google" title & connect button', async () => { - await expect( page.getByText( 'mail@example.com' ) ).toBeVisible(); + const jetpackEmailRow = setUpAccountsPage.getJetpackDescriptionRow(); + await expect( jetpackEmailRow ).toContainText( 'jetpack@example.com' ); await expect( page.getByText( 'Google', { exact: true } ) ).toBeVisible(); - expect( + await expect( page.getByRole( 'button', { name: 'Connect' } ).first() ).toBeEnabled(); } ); @@ -103,4 +105,147 @@ test.describe( 'Set up accounts', () => { await page.waitForURL( baseURL + 'google_auth' ); } ); } ); + + test.describe( 'Connect Merchant Center account', () => { + test.beforeAll( async () => { + + await Promise.all( [ + // Mock Jetpack as connected. + setUpAccountsPage.mockJetpackConnected( 'Test user', 'jetpack@example.com' ), + + // Mock google as connected. + setUpAccountsPage.mockGoogleConnected( 'google@example.com' ), + + // Mock merchant center as not connected. + setUpAccountsPage.mockMCNotConnected(), + ] ); + } ); + + test.describe( 'Create a new Merchant Center account', () => { + test.beforeAll( async () => { + // Mock merchant center has no accounts + await setUpAccountsPage.mockMCHasNoAccounts(); + await setUpAccountsPage.goto(); + } ); + + test( 'should see their WPORG email, Google email, "Google Merchant Center" title & "Create account" button', async () => { + const jetpackEmailRow = setUpAccountsPage.getJetpackDescriptionRow(); + await expect( jetpackEmailRow ).toContainText( 'jetpack@example.com' ); + + const googleEmailRow = setUpAccountsPage.getGoogleDescriptionRow(); + await expect( googleEmailRow ).toContainText( 'google@example.com' ); + + const mcTitleRow = setUpAccountsPage.getMCTitleRow(); + await expect( mcTitleRow ).toContainText( 'Google Merchant Center' ); + + const createAccountButton = setUpAccountsPage.getMCCreateAccountButtonFromPage(); + await expect( createAccountButton ).toBeEnabled(); + } ); + + 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( 'domcontentloaded' ); + + 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(); + } ); + + test.describe( 'click "Create account" button from the modal', () => { + test( 'should see Merchant Center "Connected" when the website is not claimed', async ( { baseURL } ) => { + await Promise.all( [ + // Mock Merchant Center create accounts + setUpAccountsPage.mockMCCreateAccountWebsiteNotClaimed(), + + // Mock Merchant Center as connected with ID 12345 + setUpAccountsPage.mockMCConnected( 12345 ), + ] ); + + const createAccountButtonFromModal = setUpAccountsPage.getMCCreateAccountButtonFromModal(); + await createAccountButtonFromModal.click(); + await page.waitForLoadState( 'networkidle' ); + const mcConnectedLabel = setUpAccountsPage.getMCConnectedLabel(); + await expect( mcConnectedLabel ).toContainText( 'Connected' ); + + const host = new URL( baseURL ).host; + const mcDescriptionRow = setUpAccountsPage.getMCDescriptionRow(); + await expect( mcDescriptionRow ).toContainText( `${host} (12345)` ); + } ); + + test.describe( 'when the website is already claimed', () => { + test( 'should see "Reclaim my URL" button, "Switch account" button, and site URL input', async ( { baseURL } ) => { + const host = new URL( baseURL ).host; + + await Promise.all( [ + // Mock merchant center has no accounts + setUpAccountsPage.mockMCHasNoAccounts(), + + // Mock Merchant Center as not connected + setUpAccountsPage.mockMCNotConnected(), + ] ); + + await setUpAccountsPage.goto(); + + // Mock Merchant Center create accounts + await setUpAccountsPage.mockMCCreateAccountWebsiteClaimed( 12345, host ); + + // Click "Create account" button from the page. + const createAccountButton = setUpAccountsPage.getMCCreateAccountButtonFromPage(); + await createAccountButton.click(); + await page.waitForLoadState( 'domcontentloaded' ); + + // Check the checkbox to accept ToS. + const modalCheckbox = setUpAccountsPage.getModalCheckbox(); + await modalCheckbox.click(); + + // Click "Create account" button from the modal. + const createAccountButtonFromModal = setUpAccountsPage.getMCCreateAccountButtonFromModal(); + await createAccountButtonFromModal.click(); + await page.waitForLoadState( 'networkidle' ); + + const reclaimButton = setUpAccountsPage.getReclaimMyURLButton(); + await expect( reclaimButton ).toBeVisible(); + + const switchAccountButton = setUpAccountsPage.getSwitchAccountButton(); + await expect( switchAccountButton ).toBeVisible(); + + const reclaimingURLInput = setUpAccountsPage.getReclaimingURLInput(); + await expect( reclaimingURLInput ).toHaveValue( baseURL ); + } ); + + test( 'click "Reclaim my URL" should send a claim overwrite request and see Merchant Center "Connected" ', async ( { baseURL } ) => { + await Promise.all( [ + // Mock Merchant Center accounts claim overwrite + setUpAccountsPage.mockMCAccountsClaimOverwrite( 12345 ), + + // Mock Merchant Center as connected with ID 12345 + setUpAccountsPage.mockMCConnected( 12345 ), + ] ); + + const reclaimButton = setUpAccountsPage.getReclaimMyURLButton(); + await reclaimButton.click(); + await page.waitForLoadState( 'networkidle' ); + + const mcConnectedLabel = setUpAccountsPage.getMCConnectedLabel(); + await expect( mcConnectedLabel ).toContainText( 'Connected' ); + + const host = new URL( baseURL ).host; + const mcDescriptionRow = setUpAccountsPage.getMCDescriptionRow(); + await expect( mcDescriptionRow ).toContainText( `${host} (12345)` ); + } ); + } ); + } ); + } ); + } ); } ); From 0ac9d82d289328a112a5a191ce8036ad38a351da Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Mon, 4 Sep 2023 11:34:48 +0800 Subject: [PATCH 07/17] Rename "email row" to "description row" --- tests/e2e/specs/setup-mc/step-1-accounts.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index a3f2aedf0f..4ca400adc5 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -81,8 +81,8 @@ test.describe( 'Set up accounts', () => { } ); test( 'should see their WPORG email, "Google" title & connect button', async () => { - const jetpackEmailRow = setUpAccountsPage.getJetpackDescriptionRow(); - await expect( jetpackEmailRow ).toContainText( 'jetpack@example.com' ); + const jetpackDescriptionRow = setUpAccountsPage.getJetpackDescriptionRow(); + await expect( jetpackDescriptionRow ).toContainText( 'jetpack@example.com' ); await expect( page.getByText( 'Google', { exact: true } ) @@ -129,11 +129,11 @@ test.describe( 'Set up accounts', () => { } ); test( 'should see their WPORG email, Google email, "Google Merchant Center" title & "Create account" button', async () => { - const jetpackEmailRow = setUpAccountsPage.getJetpackDescriptionRow(); - await expect( jetpackEmailRow ).toContainText( 'jetpack@example.com' ); + const jetpackDescriptionRow = setUpAccountsPage.getJetpackDescriptionRow(); + await expect( jetpackDescriptionRow ).toContainText( 'jetpack@example.com' ); - const googleEmailRow = setUpAccountsPage.getGoogleDescriptionRow(); - await expect( googleEmailRow ).toContainText( 'google@example.com' ); + const googleDescriptionRow = setUpAccountsPage.getGoogleDescriptionRow(); + await expect( googleDescriptionRow ).toContainText( 'google@example.com' ); const mcTitleRow = setUpAccountsPage.getMCTitleRow(); await expect( mcTitleRow ).toContainText( 'Google Merchant Center' ); From eb9046d29054045b254da9a0322c33e2c25227fa Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Mon, 4 Sep 2023 12:24:33 +0800 Subject: [PATCH 08/17] Fix JSLint errors --- .../specs/setup-mc/step-1-accounts.test.js | 265 +++++++++++------- tests/e2e/utils/mock-requests.js | 67 +++-- .../pages/setup-mc/step-1-set-up-accounts.js | 6 +- 3 files changed, 218 insertions(+), 120 deletions(-) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 4ca400adc5..9a944cec81 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -51,12 +51,16 @@ test.describe( 'Set up accounts', () => { } ); test.describe( 'Connect WordPress.com account', () => { - test( 'should send an API request to connect Jetpack, and redirect to the returned URL', async ( { baseURL } ) => { + test( 'should send an API request to connect Jetpack, and redirect to the returned URL', async ( { + baseURL, + } ) => { // Mock Jetpack connect await setUpAccountsPage.mockJetpackConnect( baseURL + 'auth_url' ); // Click the enabled connect button. - page.locator( "//button[text()='Connect'][not(@disabled)]" ).click(); + page.locator( + "//button[text()='Connect'][not(@disabled)]" + ).click(); await page.waitForLoadState( 'networkidle' ); // Expect the user to be redirected @@ -67,7 +71,10 @@ test.describe( 'Set up accounts', () => { test.describe( 'Connect Google account', () => { test.beforeAll( async () => { // Mock Jetpack as connected - await setUpAccountsPage.mockJetpackConnected( 'Test user', 'jetpack@example.com' ); + await setUpAccountsPage.mockJetpackConnected( + 'Test user', + 'jetpack@example.com' + ); // Mock google as not connected. // When pending even WPORG will not render yet. @@ -81,8 +88,12 @@ test.describe( 'Set up accounts', () => { } ); test( 'should see their WPORG email, "Google" title & connect button', async () => { - const jetpackDescriptionRow = setUpAccountsPage.getJetpackDescriptionRow(); - await expect( jetpackDescriptionRow ).toContainText( 'jetpack@example.com' ); + const jetpackDescriptionRow = + setUpAccountsPage.getJetpackDescriptionRow(); + + await expect( jetpackDescriptionRow ).toContainText( + 'jetpack@example.com' + ); await expect( page.getByText( 'Google', { exact: true } ) @@ -93,12 +104,18 @@ test.describe( 'Set up accounts', () => { ).toBeEnabled(); } ); - test( 'after clicking the "Connect your Google account" button should send an API request to connect Google account, and redirect to the returned URL', async ( { baseURL } ) => { + test( 'after clicking the "Connect your Google account" button should send an API request to connect Google account, and redirect to the returned URL', async ( { + baseURL, + } ) => { // Mock google connect. - await setUpAccountsPage.mockGoogleConnect( baseURL + 'google_auth' ); + await setUpAccountsPage.mockGoogleConnect( + baseURL + 'google_auth' + ); // Click the enabled connect button - page.locator( "//button[text()='Connect'][not(@disabled)]" ).click(); + page.locator( + "//button[text()='Connect'][not(@disabled)]" + ).click(); await page.waitForLoadState( 'networkidle' ); // Expect the user to be redirected @@ -108,10 +125,12 @@ test.describe( 'Set up accounts', () => { test.describe( 'Connect Merchant Center account', () => { test.beforeAll( async () => { - await Promise.all( [ // Mock Jetpack as connected. - setUpAccountsPage.mockJetpackConnected( 'Test user', 'jetpack@example.com' ), + setUpAccountsPage.mockJetpackConnected( + 'Test user', + 'jetpack@example.com' + ), // Mock google as connected. setUpAccountsPage.mockGoogleConnected( 'google@example.com' ), @@ -129,32 +148,45 @@ test.describe( 'Set up accounts', () => { } ); test( 'should see their WPORG email, Google email, "Google Merchant Center" title & "Create account" button', async () => { - const jetpackDescriptionRow = setUpAccountsPage.getJetpackDescriptionRow(); - await expect( jetpackDescriptionRow ).toContainText( 'jetpack@example.com' ); - - const googleDescriptionRow = setUpAccountsPage.getGoogleDescriptionRow(); - await expect( googleDescriptionRow ).toContainText( 'google@example.com' ); + const jetpackDescriptionRow = + setUpAccountsPage.getJetpackDescriptionRow(); + await expect( jetpackDescriptionRow ).toContainText( + 'jetpack@example.com' + ); + + const googleDescriptionRow = + setUpAccountsPage.getGoogleDescriptionRow(); + await expect( googleDescriptionRow ).toContainText( + 'google@example.com' + ); const mcTitleRow = setUpAccountsPage.getMCTitleRow(); - await expect( mcTitleRow ).toContainText( 'Google Merchant Center' ); + await expect( mcTitleRow ).toContainText( + 'Google Merchant Center' + ); - const createAccountButton = setUpAccountsPage.getMCCreateAccountButtonFromPage(); + const createAccountButton = + setUpAccountsPage.getMCCreateAccountButtonFromPage(); await expect( createAccountButton ).toBeEnabled(); } ); 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() + const createAccountButton = + setUpAccountsPage.getMCCreateAccountButtonFromPage(); + await createAccountButton.click(); await page.waitForLoadState( 'domcontentloaded' ); const modalHeader = setUpAccountsPage.getModalHeader(); - await expect( modalHeader ).toContainText( 'Create Google Merchant Center Account' ); + await expect( modalHeader ).toContainText( + 'Create Google Merchant Center Account' + ); const modalCheckbox = setUpAccountsPage.getModalCheckbox(); await expect( modalCheckbox ).toBeEnabled(); - const createAccountButtonFromModal = setUpAccountsPage.getMCCreateAccountButtonFromModal(); + const createAccountButtonFromModal = + setUpAccountsPage.getMCCreateAccountButtonFromModal(); await expect( createAccountButtonFromModal ).toBeDisabled(); // Click the checkbox of accepting ToS, the create account button will be enabled. @@ -162,90 +194,133 @@ test.describe( 'Set up accounts', () => { await expect( createAccountButtonFromModal ).toBeEnabled(); } ); - test.describe( 'click "Create account" button from the modal', () => { - test( 'should see Merchant Center "Connected" when the website is not claimed', async ( { baseURL } ) => { - await Promise.all( [ - // Mock Merchant Center create accounts - setUpAccountsPage.mockMCCreateAccountWebsiteNotClaimed(), - - // Mock Merchant Center as connected with ID 12345 - setUpAccountsPage.mockMCConnected( 12345 ), - ] ); - - const createAccountButtonFromModal = setUpAccountsPage.getMCCreateAccountButtonFromModal(); - await createAccountButtonFromModal.click(); - await page.waitForLoadState( 'networkidle' ); - const mcConnectedLabel = setUpAccountsPage.getMCConnectedLabel(); - await expect( mcConnectedLabel ).toContainText( 'Connected' ); - - const host = new URL( baseURL ).host; - const mcDescriptionRow = setUpAccountsPage.getMCDescriptionRow(); - await expect( mcDescriptionRow ).toContainText( `${host} (12345)` ); - } ); - - test.describe( 'when the website is already claimed', () => { - test( 'should see "Reclaim my URL" button, "Switch account" button, and site URL input', async ( { baseURL } ) => { - const host = new URL( baseURL ).host; - - await Promise.all( [ - // Mock merchant center has no accounts - setUpAccountsPage.mockMCHasNoAccounts(), - - // Mock Merchant Center as not connected - setUpAccountsPage.mockMCNotConnected(), - ] ); - - await setUpAccountsPage.goto(); - - // Mock Merchant Center create accounts - await setUpAccountsPage.mockMCCreateAccountWebsiteClaimed( 12345, host ); - - // Click "Create account" button from the page. - const createAccountButton = setUpAccountsPage.getMCCreateAccountButtonFromPage(); - await createAccountButton.click(); - await page.waitForLoadState( 'domcontentloaded' ); - - // Check the checkbox to accept ToS. - const modalCheckbox = setUpAccountsPage.getModalCheckbox(); - await modalCheckbox.click(); - - // Click "Create account" button from the modal. - const createAccountButtonFromModal = setUpAccountsPage.getMCCreateAccountButtonFromModal(); - await createAccountButtonFromModal.click(); - await page.waitForLoadState( 'networkidle' ); - - const reclaimButton = setUpAccountsPage.getReclaimMyURLButton(); - await expect( reclaimButton ).toBeVisible(); - - const switchAccountButton = setUpAccountsPage.getSwitchAccountButton(); - await expect( switchAccountButton ).toBeVisible(); - - const reclaimingURLInput = setUpAccountsPage.getReclaimingURLInput(); - await expect( reclaimingURLInput ).toHaveValue( baseURL ); - } ); - - test( 'click "Reclaim my URL" should send a claim overwrite request and see Merchant Center "Connected" ', async ( { baseURL } ) => { + test.describe( + 'click "Create account" button from the modal', + () => { + test( 'should see Merchant Center "Connected" when the website is not claimed', async ( { + baseURL, + } ) => { await Promise.all( [ - // Mock Merchant Center accounts claim overwrite - setUpAccountsPage.mockMCAccountsClaimOverwrite( 12345 ), + // Mock Merchant Center create accounts + setUpAccountsPage.mockMCCreateAccountWebsiteNotClaimed(), // Mock Merchant Center as connected with ID 12345 setUpAccountsPage.mockMCConnected( 12345 ), ] ); - const reclaimButton = setUpAccountsPage.getReclaimMyURLButton(); - await reclaimButton.click(); + const createAccountButtonFromModal = + setUpAccountsPage.getMCCreateAccountButtonFromModal(); + await createAccountButtonFromModal.click(); await page.waitForLoadState( 'networkidle' ); - - const mcConnectedLabel = setUpAccountsPage.getMCConnectedLabel(); - await expect( mcConnectedLabel ).toContainText( 'Connected' ); + const mcConnectedLabel = + setUpAccountsPage.getMCConnectedLabel(); + await expect( mcConnectedLabel ).toContainText( + 'Connected' + ); const host = new URL( baseURL ).host; - const mcDescriptionRow = setUpAccountsPage.getMCDescriptionRow(); - await expect( mcDescriptionRow ).toContainText( `${host} (12345)` ); + const mcDescriptionRow = + setUpAccountsPage.getMCDescriptionRow(); + await expect( mcDescriptionRow ).toContainText( + `${ host } (12345)` + ); } ); - } ); - } ); + + test.describe( + 'when the website is already claimed', + () => { + test( 'should see "Reclaim my URL" button, "Switch account" button, and site URL input', async ( { + baseURL, + } ) => { + const host = new URL( baseURL ).host; + + await Promise.all( [ + // Mock merchant center has no accounts + setUpAccountsPage.mockMCHasNoAccounts(), + + // Mock Merchant Center as not connected + setUpAccountsPage.mockMCNotConnected(), + ] ); + + await setUpAccountsPage.goto(); + + // Mock Merchant Center create accounts + await setUpAccountsPage.mockMCCreateAccountWebsiteClaimed( + 12345, + host + ); + + // Click "Create account" button from the page. + const createAccountButton = + setUpAccountsPage.getMCCreateAccountButtonFromPage(); + await createAccountButton.click(); + await page.waitForLoadState( + 'domcontentloaded' + ); + + // Check the checkbox to accept ToS. + const modalCheckbox = + setUpAccountsPage.getModalCheckbox(); + await modalCheckbox.click(); + + // Click "Create account" button from the modal. + const createAccountButtonFromModal = + setUpAccountsPage.getMCCreateAccountButtonFromModal(); + await createAccountButtonFromModal.click(); + await page.waitForLoadState( 'networkidle' ); + + const reclaimButton = + setUpAccountsPage.getReclaimMyURLButton(); + await expect( reclaimButton ).toBeVisible(); + + const switchAccountButton = + setUpAccountsPage.getSwitchAccountButton(); + await expect( + switchAccountButton + ).toBeVisible(); + + const reclaimingURLInput = + setUpAccountsPage.getReclaimingURLInput(); + await expect( reclaimingURLInput ).toHaveValue( + baseURL + ); + } ); + + test( 'click "Reclaim my URL" should send a claim overwrite request and see Merchant Center "Connected"', async ( { + baseURL, + } ) => { + await Promise.all( [ + // Mock Merchant Center accounts claim overwrite + setUpAccountsPage.mockMCAccountsClaimOverwrite( + 12345 + ), + + // Mock Merchant Center as connected with ID 12345 + setUpAccountsPage.mockMCConnected( 12345 ), + ] ); + + const reclaimButton = + setUpAccountsPage.getReclaimMyURLButton(); + await reclaimButton.click(); + await page.waitForLoadState( 'networkidle' ); + + const mcConnectedLabel = + setUpAccountsPage.getMCConnectedLabel(); + await expect( mcConnectedLabel ).toContainText( + 'Connected' + ); + + const host = new URL( baseURL ).host; + const mcDescriptionRow = + setUpAccountsPage.getMCDescriptionRow(); + await expect( mcDescriptionRow ).toContainText( + `${ host } (12345)` + ); + } ); + } + ); + } + ); } ); } ); } ); diff --git a/tests/e2e/utils/mock-requests.js b/tests/e2e/utils/mock-requests.js index a59730be15..3fbcf36dbb 100644 --- a/tests/e2e/utils/mock-requests.js +++ b/tests/e2e/utils/mock-requests.js @@ -14,8 +14,9 @@ export default class MockRequests { /** * Fulfill a request with a payload. * - * @param {RegExp|string} url The url to fulfill. + * @param {RegExp|string} url The url to fulfill. * @param {Object} payload The payload to send. + * @param {number} status The HTTP status in the response. * @return {Promise} */ async fulfillRequest( url, payload, status = 200 ) { @@ -59,6 +60,7 @@ export default class MockRequests { * Fulfill the MC accounts request. * * @param {Object} payload + * @param {number} status * @return {Promise} */ async fulfillMCAccounts( payload, status = 200 ) { @@ -73,6 +75,7 @@ export default class MockRequests { * Fulfill the MC accounts claim-overwrite request. * * @param {Object} payload + * @param {number} status * @return {Promise} */ async fulfillMCAccountsClaimOverwrite( payload, status = 200 ) { @@ -90,10 +93,7 @@ export default class MockRequests { * @return {Promise} */ async fulfillMCConnection( payload ) { - await this.fulfillRequest( - /\/wc\/gla\/mc\/connection\b/, - payload - ); + await this.fulfillRequest( /\/wc\/gla\/mc\/connection\b/, payload ); } /** @@ -158,6 +158,8 @@ export default class MockRequests { /** * Mock the request to connect Jetpack + * + * @param {string} url */ async mockJetpackConnect( url ) { await this.fulfillConnectJetPack( { url } ); @@ -165,8 +167,14 @@ export default class MockRequests { /** * Mock Jetpack as connected. + * + * @param {string} displayName + * @param {string} email */ - async mockJetpackConnected( displayName = 'John', email = 'mail@example.com' ) { + async mockJetpackConnected( + displayName = 'John', + email = 'mail@example.com' + ) { await this.fulfillJetPackConnection( { active: 'yes', owner: 'yes', @@ -177,6 +185,8 @@ export default class MockRequests { /** * Mock the request to connect Google. + * + * @param {string} url */ async mockGoogleConnect( url ) { await this.fulfillConnectGoogle( { url } ); @@ -184,16 +194,18 @@ export default class MockRequests { /** * Mock Google as connected. + * + * @param {string} email */ async mockGoogleConnected( email = 'mail@example.com' ) { await this.fulfillGoogleConnection( { active: 'yes', email, scope: [ - 'https:\/\/www.googleapis.com\/auth\/content', - 'https:\/\/www.googleapis.com\/auth\/adwords', - 'https:\/\/www.googleapis.com\/auth\/userinfo.email', - 'https:\/\/www.googleapis.com\/auth\/siteverification.verify_only', + 'https://www.googleapis.com/auth/content', + 'https://www.googleapis.com/auth/adwords', + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/siteverification.verify_only', 'openid', ], } ); @@ -212,6 +224,8 @@ export default class MockRequests { /** * Mock MC as connected. + * + * @param {number} id */ async mockMCConnected( id = 1234 ) { await this.fulfillMCConnection( { @@ -239,13 +253,13 @@ export default class MockRequests { id: 12345, subaccount: true, name: 'MC Account 1', - domain: 'https:\/\/example.com', + domain: 'https://example.com', }, { id: 23456, subaccount: true, name: 'MC Account 2', - domain: 'https:\/\/example.com', + domain: 'https://example.com', }, ] ); } @@ -259,6 +273,8 @@ export default class MockRequests { /** * Mock MC create account where the website is not claimed. + * + * @param {number} id */ async mockMCCreateAccountWebsiteNotClaimed( id = 12345 ) { await this.fulfillMCAccounts( { @@ -271,11 +287,18 @@ export default class MockRequests { /** * Mock MC create account where the website is claimed. + * + * @param {number} id + * @param {string} websiteUrl */ - async mockMCCreateAccountWebsiteClaimed( id = 12345, websiteUrl = 'example.com' ) { + async mockMCCreateAccountWebsiteClaimed( + id = 12345, + websiteUrl = 'example.com' + ) { await this.fulfillMCAccounts( { - message: 'Website already claimed, use overwrite to complete the process.', + message: + 'Website already claimed, use overwrite to complete the process.', id, website_url: websiteUrl, }, @@ -285,15 +308,15 @@ export default class MockRequests { /** * Mock MC accounts claim overwrite. + * + * @param {number} id */ async mockMCAccountsClaimOverwrite( id = 12345 ) { - await this.fulfillMCAccountsClaimOverwrite( - { - id, - subaccount: null, - name: null, - domain: null, - }, - ); + await this.fulfillMCAccountsClaimOverwrite( { + id, + subaccount: null, + name: null, + domain: null, + } ); } } diff --git a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js index 078980b65d..459dda79ad 100644 --- a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js +++ b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js @@ -44,7 +44,7 @@ export default class SetUpAccountsPage extends MockRequests { getCreateAccountButton() { return this.page.getByRole( 'button', { name: 'Create account', - exact: true + exact: true, } ); } @@ -184,7 +184,7 @@ export default class SetUpAccountsPage extends MockRequests { getReclaimMyURLButton() { return this.page.getByRole( 'button', { name: 'Reclaim my URL', - exact: true + exact: true, } ); } @@ -196,7 +196,7 @@ export default class SetUpAccountsPage extends MockRequests { getSwitchAccountButton() { return this.page.getByRole( 'button', { name: 'Switch account', - exact: true + exact: true, } ); } From 79f4a20e8b3577878dfe82fd4f4c4d577ea3ebe0 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Mon, 4 Sep 2023 20:58:58 +0800 Subject: [PATCH 09/17] Add e2e test for connecting an existing merchant center account --- .../specs/setup-mc/step-1-accounts.test.js | 144 ++++++++++++++++++ .../pages/setup-mc/step-1-set-up-accounts.js | 111 ++++++++++++++ 2 files changed, 255 insertions(+) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 9a944cec81..65ff0de4ee 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -322,5 +322,149 @@ test.describe( 'Set up accounts', () => { } ); } ); + + test.describe( 'Merchant Center has existing accounts', () => { + test.beforeAll( async () => { + await Promise.all( [ + // Mock merchant center as not connected. + setUpAccountsPage.mockMCNotConnected(), + + // Mock merchant center has accounts + setUpAccountsPage.fulfillMCAccounts( [ + { + id: 12345, + subaccount: true, + name: 'MC Account 1', + domain: 'https://example.com', + }, + { + id: 23456, + subaccount: true, + name: 'MC Account 2', + domain: 'https://example.com', + }, + ] ), + ] ); + + await setUpAccountsPage.goto(); + } ); + + test.describe( 'connect to an existing account', () => { + test( 'should see "Select an existing account" title', async () => { + const selectAccountTitle = + setUpAccountsPage.getSelectExistingMCAccountTitle(); + await expect( selectAccountTitle ).toContainText( + 'Select an existing account' + ); + } ); + + test( 'should see "Or, create a new Merchant Center account" text', async () => { + const mcFooter = setUpAccountsPage.getMCCardFooter(); + await expect( mcFooter ).toContainText( + 'Or, create a new Merchant Center account' + ); + } ); + + test( 'should see "Connect" button', async () => { + const connectButton = setUpAccountsPage.getConnectButton(); + await expect( connectButton ).toBeEnabled(); + } ); + + test( 'select MC Account 2 and click "Connect" button should see Merchant Center "Connected"', async ( { + baseURL, + } ) => { + await Promise.all( [ + // Mock Merchant Center create accounts + setUpAccountsPage.mockMCCreateAccountWebsiteNotClaimed(), + + // Mock Merchant Center as connected with ID 12345 + setUpAccountsPage.mockMCConnected( 23456 ), + ] ); + + // Select MC Account 2 from the options + const mcAccountsSelect = + setUpAccountsPage.getMCAccountsSelect(); + await mcAccountsSelect.selectOption( { + label: 'MC Account 2 ・ https://example.com (23456)', + } ); + + // Click connect button + const connectButton = setUpAccountsPage.getConnectButton(); + await connectButton.click(); + await page.waitForLoadState( 'networkidle' ); + + const mcConnectedLabel = + setUpAccountsPage.getMCConnectedLabel(); + await expect( mcConnectedLabel ).toContainText( + 'Connected' + ); + + const host = new URL( baseURL ).host; + const mcDescriptionRow = + setUpAccountsPage.getMCDescriptionRow(); + await expect( mcDescriptionRow ).toContainText( + `${ host } (23456)` + ); + } ); + } ); + + test.describe( + 'click "Or, create a new Merchant Center account"', + () => { + test.beforeAll( async () => { + await Promise.all( [ + // Mock merchant center as not connected. + setUpAccountsPage.mockMCNotConnected(), + + // Mock merchant center has accounts + setUpAccountsPage.fulfillMCAccounts( [ + { + id: 12345, + subaccount: true, + name: 'MC Account 1', + domain: 'https://example.com', + }, + { + id: 23456, + subaccount: true, + name: 'MC Account 2', + domain: 'https://example.com', + }, + ] ), + ] ); + + await setUpAccountsPage.goto(); + } ); + + 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( 'domcontentloaded' ); + + 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(); + } ); + } + ); + } ); } ); } ); diff --git a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js index 459dda79ad..3ecc45bd6d 100644 --- a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js +++ b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js @@ -122,6 +122,15 @@ export default class SetUpAccountsPage extends MockRequests { return this.getCardTitleClass().nth( 2 ); } + /** + * Get modal. + * + * @return {import('@playwright/test').Locator} Get modal. + */ + getModal() { + return this.page.locator( '.components-modal__content' ); + } + /** * Get modal header. * @@ -140,6 +149,24 @@ export default class SetUpAccountsPage extends MockRequests { return this.page.getByRole( 'checkbox' ); } + /** + * Get modal primary button. + * + * @return {import('@playwright/test').Locator} Get modal primary button. + */ + getModalPrimaryButton() { + return this.getModal().locator( 'button.is-primary' ); + } + + /** + * Get modal secondary button. + * + * @return {import('@playwright/test').Locator} Get modal secondary button. + */ + getModalSecondaryButton() { + return this.getModal().locator( 'button.is-secondary' ); + } + /** * Get .gla-connected-icon-label class. * @@ -208,4 +235,88 @@ export default class SetUpAccountsPage extends MockRequests { getReclaimingURLInput() { return this.page.locator( 'input#inspector-input-control-0' ); } + + /** + * Get sub section title row. + * + * @return {import('@playwright/test').Locator} Get sub section title row. + */ + getSubSectionTitleRow() { + return this.page.locator( '.wcdl-subsection-title' ); + } + + /** + * Get section footer row. + * + * @return {import('@playwright/test').Locator} Get section footer row. + */ + getSectionFooterRow() { + return this.page.locator( '.wcdl-section-card-footer' ); + } + + /** + * Get select existing Merchant Center account title. + * + * @return {import('@playwright/test').Locator} Get select existing Merchant Center account title. + */ + getSelectExistingMCAccountTitle() { + return this.getSubSectionTitleRow().nth( 3 ); + } + + /** + * Get MC accounts select element. + * + * @return {import('@playwright/test').Locator} Get select MC accounts select element. + */ + getMCAccountsSelect() { + return this.page.locator( 'select[id*="inspector-select-control"]' ); + } + + /** + * Get "Connect" button. + * + * @return {import('@playwright/test').Locator} Get "Connect" button. + */ + getConnectButton() { + return this.page.getByRole( 'button', { + name: 'Connect', + exact: true, + } ); + } + + /** + * Get account cards. + * + * @return {import('@playwright/test').Locator} Get account cards. + */ + getAccountCards() { + return this.page.locator( '.gla-account-card' ); + } + + /** + * Get Merchant Center account card. + * + * @return {import('@playwright/test').Locator} Get Merchant Center account card. + */ + getMCAccountCard() { + return this.getAccountCards().nth( 2 ); + } + + /** + * Get Merchant Center card footer. + * + * @return {import('@playwright/test').Locator} Get Merchant Center card footer. + */ + getMCCardFooter() { + return this.getMCAccountCard().locator( '.wcdl-section-card-footer' ); + } + + /** + * Get Merchant Center card footer button. + * + * @return {import('@playwright/test').Locator} Get Merchant Center card footer button. + */ + getMCCardFooterButton() { + return this.getMCCardFooter().getByRole( 'button' ); + } } From c8af807b392ed522fa7955c4de8cabd2f9169ce3 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Wed, 6 Sep 2023 11:07:22 +0800 Subject: [PATCH 10/17] Add assertions in tests without assertions --- tests/e2e/specs/setup-mc/step-1-accounts.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 65ff0de4ee..11442fd364 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -65,6 +65,8 @@ test.describe( 'Set up accounts', () => { // Expect the user to be redirected await page.waitForURL( baseURL + 'auth_url' ); + + expect( page.url() ).toMatch( baseURL + 'auth_url' ); } ); } ); @@ -120,6 +122,8 @@ test.describe( 'Set up accounts', () => { // Expect the user to be redirected await page.waitForURL( baseURL + 'google_auth' ); + + expect( page.url() ).toMatch( baseURL + 'google_auth' ); } ); } ); From 69722d4515c3f43b8eb4d254ef630083a6d29d19 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Wed, 6 Sep 2023 11:45:50 +0800 Subject: [PATCH 11/17] Remove unused line We already test this heading at the top of the test. --- tests/e2e/specs/setup-mc/step-1-accounts.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 11442fd364..5e79706327 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -85,8 +85,6 @@ test.describe( 'Set up accounts', () => { await setUpAccountsPage.mockGoogleNotConnected(); await setUpAccountsPage.goto(); - - page.getByRole( 'heading', { name: 'Set up your accounts' } ); } ); test( 'should see their WPORG email, "Google" title & connect button', async () => { From 4e948e457558347854394d0ce76b466d94c54d52 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Wed, 6 Sep 2023 12:07:13 +0800 Subject: [PATCH 12/17] Move 'domcontentloaded' and 'networkidle' to constants --- tests/e2e/global-setup.js | 14 ++++++++++--- .../dashboard/edit-free-listings.test.js | 6 ++++-- tests/e2e/specs/get-started.test.js | 9 ++++++-- .../specs/setup-mc/step-1-accounts.test.js | 21 ++++++++++--------- tests/e2e/utils/constants.js | 4 ++++ tests/e2e/utils/customer.js | 3 ++- tests/e2e/utils/pages/dashboard.js | 3 ++- .../pages/setup-mc/step-1-set-up-accounts.js | 3 ++- 8 files changed, 43 insertions(+), 20 deletions(-) create mode 100644 tests/e2e/utils/constants.js diff --git a/tests/e2e/global-setup.js b/tests/e2e/global-setup.js index cef4fa7c63..aa755c9f5c 100644 --- a/tests/e2e/global-setup.js +++ b/tests/e2e/global-setup.js @@ -1,5 +1,13 @@ -const { chromium, expect } = require( '@playwright/test' ); +/** + * Internal dependencies + */ const { admin } = require( './config/default.json' ).users; +const { LOAD_STATE } = require( './utils/constants' ); + +/** + * External dependencies + */ +const { chromium, expect } = require( '@playwright/test' ); const fs = require( 'fs' ); /* eslint-disable no-console */ @@ -48,9 +56,9 @@ module.exports = async ( config ) => { .locator( 'input[name="pwd"]' ) .fill( admin.password ); await adminPage.locator( 'text=Log In' ).click(); - await adminPage.waitForLoadState( 'networkidle' ); + await adminPage.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); await adminPage.goto( `/wp-admin` ); - await adminPage.waitForLoadState( 'domcontentloaded' ); + await adminPage.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED ); await expect( adminPage.locator( 'div.wrap > h1' ) ).toHaveText( 'Dashboard' diff --git a/tests/e2e/specs/dashboard/edit-free-listings.test.js b/tests/e2e/specs/dashboard/edit-free-listings.test.js index 87a8e60858..0def13c2e9 100644 --- a/tests/e2e/specs/dashboard/edit-free-listings.test.js +++ b/tests/e2e/specs/dashboard/edit-free-listings.test.js @@ -2,10 +2,12 @@ * External dependencies */ import { expect, test } from '@playwright/test'; + /** * Internal dependencies */ import { clearOnboardedMerchant, setOnboardedMerchant } from '../../utils/api'; +import { LOAD_STATE } from '../../utils/constants'; import { checkSnackBarMessage } from '../../utils/page'; import DashboardPage from '../../utils/pages/dashboard.js'; import EditFreeListingsPage from '../../utils/pages/edit-free-listings.js'; @@ -55,7 +57,7 @@ test.describe( 'Edit Free Listings', () => { test( 'Edit Free Listings should show modal', async () => { await dashboardPage.clickEditFreeListings(); - await page.waitForLoadState( 'domcontentloaded' ); + await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED ); const continueToEditButton = await dashboardPage.getContinueToEditButton(); @@ -66,7 +68,7 @@ test.describe( 'Edit Free Listings', () => { test( 'Continue to edit Free listings', async () => { await dashboardPage.clickContinueToEditButton(); - await page.waitForLoadState( 'domcontentloaded' ); + await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED ); } ); test( 'Check recommended shipping settings', async () => { diff --git a/tests/e2e/specs/get-started.test.js b/tests/e2e/specs/get-started.test.js index f58f5c427b..bf2ba8f231 100644 --- a/tests/e2e/specs/get-started.test.js +++ b/tests/e2e/specs/get-started.test.js @@ -1,3 +1,8 @@ +/** + * Internal dependencies + */ +import { LOAD_STATE } from '../utils/constants'; + /** * External dependencies */ @@ -22,13 +27,13 @@ test( 'Merchant who is getting started clicks on the Marketing > GLA link, click // the submenu is now opened, the GLA sub menu item is now visible to the user, // we can call `click` now. await page.getByRole( 'link', { name: 'Google Listings & Ads' } ).click(); - await page.waitForLoadState( 'networkidle' ); + await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); await expect( page ).toHaveTitle( /Google Listings & Ads/ ); // click on the call-to-action button. await page.getByText( 'Start listing products →' ).first().click(); - await page.waitForLoadState( 'networkidle' ); + await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); // Check we are in the Setup MC page. await expect( diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 5e79706327..39bf1d2072 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -1,7 +1,8 @@ /** * Internal dependencies */ -import SetUpAccountsPage from '../../utils/pages/setup-mc/step-1-set-up-accounts.js'; +import SetUpAccountsPage from '../../utils/pages/setup-mc/step-1-set-up-accounts'; +import { LOAD_STATE } from '../../utils/constants'; /** * External dependencies @@ -61,7 +62,7 @@ test.describe( 'Set up accounts', () => { page.locator( "//button[text()='Connect'][not(@disabled)]" ).click(); - await page.waitForLoadState( 'networkidle' ); + await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); // Expect the user to be redirected await page.waitForURL( baseURL + 'auth_url' ); @@ -116,7 +117,7 @@ test.describe( 'Set up accounts', () => { page.locator( "//button[text()='Connect'][not(@disabled)]" ).click(); - await page.waitForLoadState( 'networkidle' ); + await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); // Expect the user to be redirected await page.waitForURL( baseURL + 'google_auth' ); @@ -177,7 +178,7 @@ test.describe( 'Set up accounts', () => { const createAccountButton = setUpAccountsPage.getMCCreateAccountButtonFromPage(); await createAccountButton.click(); - await page.waitForLoadState( 'domcontentloaded' ); + await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED ); const modalHeader = setUpAccountsPage.getModalHeader(); await expect( modalHeader ).toContainText( @@ -213,7 +214,7 @@ test.describe( 'Set up accounts', () => { const createAccountButtonFromModal = setUpAccountsPage.getMCCreateAccountButtonFromModal(); await createAccountButtonFromModal.click(); - await page.waitForLoadState( 'networkidle' ); + await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); const mcConnectedLabel = setUpAccountsPage.getMCConnectedLabel(); await expect( mcConnectedLabel ).toContainText( @@ -257,7 +258,7 @@ test.describe( 'Set up accounts', () => { setUpAccountsPage.getMCCreateAccountButtonFromPage(); await createAccountButton.click(); await page.waitForLoadState( - 'domcontentloaded' + LOAD_STATE.DOM_CONTENT_LOADED ); // Check the checkbox to accept ToS. @@ -269,7 +270,7 @@ test.describe( 'Set up accounts', () => { const createAccountButtonFromModal = setUpAccountsPage.getMCCreateAccountButtonFromModal(); await createAccountButtonFromModal.click(); - await page.waitForLoadState( 'networkidle' ); + await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); const reclaimButton = setUpAccountsPage.getReclaimMyURLButton(); @@ -304,7 +305,7 @@ test.describe( 'Set up accounts', () => { const reclaimButton = setUpAccountsPage.getReclaimMyURLButton(); await reclaimButton.click(); - await page.waitForLoadState( 'networkidle' ); + await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); const mcConnectedLabel = setUpAccountsPage.getMCConnectedLabel(); @@ -393,7 +394,7 @@ test.describe( 'Set up accounts', () => { // Click connect button const connectButton = setUpAccountsPage.getConnectButton(); await connectButton.click(); - await page.waitForLoadState( 'networkidle' ); + await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); const mcConnectedLabel = setUpAccountsPage.getMCConnectedLabel(); @@ -443,7 +444,7 @@ test.describe( 'Set up accounts', () => { const mcFooterButton = setUpAccountsPage.getMCCardFooterButton(); await mcFooterButton.click(); - await page.waitForLoadState( 'domcontentloaded' ); + await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED ); const modalHeader = setUpAccountsPage.getModalHeader(); await expect( modalHeader ).toContainText( diff --git a/tests/e2e/utils/constants.js b/tests/e2e/utils/constants.js new file mode 100644 index 0000000000..3460f444b2 --- /dev/null +++ b/tests/e2e/utils/constants.js @@ -0,0 +1,4 @@ +export const LOAD_STATE = { + NETWORK_IDLE: 'networkidle', + DOM_CONTENT_LOADED: 'domcontentloaded', +}; diff --git a/tests/e2e/utils/customer.js b/tests/e2e/utils/customer.js index 6418f14e93..4e1bff04ca 100644 --- a/tests/e2e/utils/customer.js +++ b/tests/e2e/utils/customer.js @@ -12,6 +12,7 @@ const { expect } = require( '@playwright/test' ); /** * Internal dependencies */ +import { LOAD_STATE } from './constants'; const config = require( '../config/default.json' ); /** @@ -30,7 +31,7 @@ export async function singleProductAddToCart( page, productID ) { ).toBeVisible(); // Wait till all tracking event request have been sent after page reloaded. - await page.waitForLoadState( 'networkidle' ); + await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); } /** diff --git a/tests/e2e/utils/pages/dashboard.js b/tests/e2e/utils/pages/dashboard.js index 925426330a..5f2d5d44af 100644 --- a/tests/e2e/utils/pages/dashboard.js +++ b/tests/e2e/utils/pages/dashboard.js @@ -1,6 +1,7 @@ /** * Internal dependencies */ +import { LOAD_STATE } from '../constants'; import MockRequests from '../mock-requests'; /** @@ -84,7 +85,7 @@ export default class DashboardPage extends MockRequests { async goto() { await this.page.goto( '/wp-admin/admin.php?page=wc-admin&path=%2Fgoogle%2Fdashboard', - { waitUntil: 'domcontentloaded' } + { waitUntil: LOAD_STATE.DOM_CONTENT_LOADED } ); } diff --git a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js index 3ecc45bd6d..a80b12b4d2 100644 --- a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js +++ b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js @@ -1,6 +1,7 @@ /** * Internal dependencies */ +import { LOAD_STATE } from '../../constants'; import MockRequests from '../../mock-requests'; /** @@ -32,7 +33,7 @@ export default class SetUpAccountsPage extends MockRequests { async goto() { await this.page.goto( '/wp-admin/admin.php?page=wc-admin&path=%2Fgoogle%2Fsetup-mc', - { waitUntil: 'networkidle' } + { waitUntil: LOAD_STATE.NETWORK_IDLE } ); } From d9ce9f664587a4442ad44ea7728b74774d3acbb8 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Wed, 6 Sep 2023 12:09:40 +0800 Subject: [PATCH 13/17] Rename test description to make it more accurate --- tests/e2e/specs/setup-mc/step-1-accounts.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 39bf1d2072..85ab3af622 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -143,7 +143,7 @@ test.describe( 'Set up accounts', () => { ] ); } ); - test.describe( 'Create a new Merchant Center account', () => { + test.describe( 'Merchant Center has no existing accounts', () => { test.beforeAll( async () => { // Mock merchant center has no accounts await setUpAccountsPage.mockMCHasNoAccounts(); From 0584880454edf9c303b41426e6a8c5b82e126387 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Wed, 6 Sep 2023 13:20:19 +0800 Subject: [PATCH 14/17] Add more asserttions on texts and buttons. --- .../specs/setup-mc/step-1-accounts.test.js | 52 ++++++++++++++++++- .../pages/setup-mc/step-1-set-up-accounts.js | 30 +++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 85ab3af622..37a37daa26 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -49,6 +49,19 @@ test.describe( 'Set up accounts', () => { await expect( page.getByRole( 'button', { name: 'Connect' } ).first() ).toBeEnabled(); + + const wpAccountCard = setUpAccountsPage.getWPAccountCard(); + await expect( wpAccountCard ).toBeEnabled(); + await expect( wpAccountCard ).toContainText( 'WordPress.com' ); + + const googleAccountCard = setUpAccountsPage.getGoogleAccountCard(); + await expect( googleAccountCard.getByRole( 'button' ) ).toBeDisabled(); + + const mcAccountCard = setUpAccountsPage.getMCAccountCard(); + await expect( mcAccountCard.getByRole( 'button' ) ).toBeDisabled(); + + const continueButton = setUpAccountsPage.getContinueButton(); + await expect( continueButton ).toBeDisabled(); } ); test.describe( 'Connect WordPress.com account', () => { @@ -96,13 +109,23 @@ test.describe( 'Set up accounts', () => { 'jetpack@example.com' ); + const googleAccountCard = setUpAccountsPage.getGoogleAccountCard(); + await expect( - page.getByText( 'Google', { exact: true } ) + googleAccountCard.getByText( 'Google', { exact: true } ) ).toBeVisible(); await expect( - page.getByRole( 'button', { name: 'Connect' } ).first() + googleAccountCard + .getByRole( 'button', { name: 'Connect' } ) + .first() ).toBeEnabled(); + + const mcAccountCard = setUpAccountsPage.getMCAccountCard(); + await expect( mcAccountCard.getByRole( 'button' ) ).toBeDisabled(); + + const continueButton = setUpAccountsPage.getContinueButton(); + await expect( continueButton ).toBeDisabled(); } ); test( 'after clicking the "Connect your Google account" button should send an API request to connect Google account, and redirect to the returned URL', async ( { @@ -171,6 +194,9 @@ test.describe( 'Set up accounts', () => { const createAccountButton = setUpAccountsPage.getMCCreateAccountButtonFromPage(); await expect( createAccountButton ).toBeEnabled(); + + const continueButton = setUpAccountsPage.getContinueButton(); + await expect( continueButton ).toBeDisabled(); } ); test( 'click "Create account" button should see the modal of confirmation of creating account', async () => { @@ -227,6 +253,10 @@ test.describe( 'Set up accounts', () => { await expect( mcDescriptionRow ).toContainText( `${ host } (12345)` ); + + const continueButton = + setUpAccountsPage.getContinueButton(); + await expect( continueButton ).toBeEnabled(); } ); test.describe( @@ -287,6 +317,10 @@ test.describe( 'Set up accounts', () => { await expect( reclaimingURLInput ).toHaveValue( baseURL ); + + const continueButton = + setUpAccountsPage.getContinueButton(); + await expect( continueButton ).toBeDisabled(); } ); test( 'click "Reclaim my URL" should send a claim overwrite request and see Merchant Center "Connected"', async ( { @@ -319,6 +353,10 @@ test.describe( 'Set up accounts', () => { await expect( mcDescriptionRow ).toContainText( `${ host } (12345)` ); + + const continueButton = + setUpAccountsPage.getContinueButton(); + await expect( continueButton ).toBeEnabled(); } ); } ); @@ -373,6 +411,12 @@ test.describe( 'Set up accounts', () => { await expect( connectButton ).toBeEnabled(); } ); + test( 'should see "Continue" button is disabled', async () => { + const continueButton = + setUpAccountsPage.getContinueButton(); + await expect( continueButton ).toBeDisabled(); + } ); + test( 'select MC Account 2 and click "Connect" button should see Merchant Center "Connected"', async ( { baseURL, } ) => { @@ -408,6 +452,10 @@ test.describe( 'Set up accounts', () => { await expect( mcDescriptionRow ).toContainText( `${ host } (23456)` ); + + const continueButton = + setUpAccountsPage.getContinueButton(); + await expect( continueButton ).toBeEnabled(); } ); } ); diff --git a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js index a80b12b4d2..e08d2b1cc6 100644 --- a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js +++ b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js @@ -294,6 +294,24 @@ export default class SetUpAccountsPage extends MockRequests { return this.page.locator( '.gla-account-card' ); } + /** + * Get WordPress account card. + * + * @return {import('@playwright/test').Locator} Get WordPress account card. + */ + getWPAccountCard() { + return this.getAccountCards().first(); + } + + /** + * Get Google account card. + * + * @return {import('@playwright/test').Locator} Get Google account card. + */ + getGoogleAccountCard() { + return this.getAccountCards().nth( 1 ); + } + /** * Get Merchant Center account card. * @@ -320,4 +338,16 @@ export default class SetUpAccountsPage extends MockRequests { getMCCardFooterButton() { return this.getMCCardFooter().getByRole( 'button' ); } + + /** + * Get "Continue" button. + * + * @return {import('@playwright/test').Locator} Get "Continue" button. + */ + getContinueButton() { + return this.page.getByRole( 'button', { + name: 'Continue', + exact: true, + } ); + } } From 2464cd9bae26cf9a831f9c34ad672ca8615a25c5 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Wed, 6 Sep 2023 13:26:24 +0800 Subject: [PATCH 15/17] Fix JS lint errors --- tests/e2e/specs/setup-mc/step-1-accounts.test.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 37a37daa26..8f494e1881 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -300,7 +300,9 @@ test.describe( 'Set up accounts', () => { const createAccountButtonFromModal = setUpAccountsPage.getMCCreateAccountButtonFromModal(); await createAccountButtonFromModal.click(); - await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); + await page.waitForLoadState( + LOAD_STATE.NETWORK_IDLE + ); const reclaimButton = setUpAccountsPage.getReclaimMyURLButton(); @@ -339,7 +341,9 @@ test.describe( 'Set up accounts', () => { const reclaimButton = setUpAccountsPage.getReclaimMyURLButton(); await reclaimButton.click(); - await page.waitForLoadState( LOAD_STATE.NETWORK_IDLE ); + await page.waitForLoadState( + LOAD_STATE.NETWORK_IDLE + ); const mcConnectedLabel = setUpAccountsPage.getMCConnectedLabel(); @@ -492,7 +496,9 @@ test.describe( 'Set up accounts', () => { const mcFooterButton = setUpAccountsPage.getMCCardFooterButton(); await mcFooterButton.click(); - await page.waitForLoadState( LOAD_STATE.DOM_CONTENT_LOADED ); + await page.waitForLoadState( + LOAD_STATE.DOM_CONTENT_LOADED + ); const modalHeader = setUpAccountsPage.getModalHeader(); await expect( modalHeader ).toContainText( From 2388e294eae9248dcc0b04ae05b04a9689373fcd Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Wed, 6 Sep 2023 13:51:09 +0800 Subject: [PATCH 16/17] Add tests for FAQ panels --- .../specs/setup-mc/step-1-accounts.test.js | 29 +++++++++++++++++++ .../pages/setup-mc/step-1-set-up-accounts.js | 27 +++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 8f494e1881..2ed9881af3 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -64,6 +64,35 @@ test.describe( 'Set up accounts', () => { await expect( continueButton ).toBeDisabled(); } ); + test.describe( 'FAQ panels', () => { + test( 'should see two questions in FAQ', async () => { + const faqTitles = setUpAccountsPage.getFAQPanelTitle(); + await expect( faqTitles ).toHaveCount( 2 ); + } ); + + test( 'should not see FAQ rows when FAQ titles are not clicked', async () => { + const faqRows = setUpAccountsPage.getFAQPanelRow(); + await expect( faqRows ).toHaveCount( 0 ); + } ); + + test( 'should see one FAQ rows when the first FAQ title is clicked', async () => { + const faqTitle = setUpAccountsPage.getFAQPanelTitle().first(); + await faqTitle.click(); + const faqRow = setUpAccountsPage.getFAQPanelRow(); + await expect( faqRow ).toBeVisible(); + } ); + + test( 'should see two FAQ rows when two FAQ titles are clicked', async () => { + const faqTitle2 = setUpAccountsPage.getFAQPanelTitle().nth( 1 ); + await faqTitle2.click(); + const faqRows = setUpAccountsPage.getFAQPanelRow(); + await expect( faqRows ).toHaveCount( 2 ); + for ( const faqRow of await faqRows.all() ) { + await expect( faqRow ).toBeVisible(); + } + } ); + } ); + test.describe( 'Connect WordPress.com account', () => { test( 'should send an API request to connect Jetpack, and redirect to the returned URL', async ( { baseURL, diff --git a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js index e08d2b1cc6..36af5e8098 100644 --- a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js +++ b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js @@ -350,4 +350,31 @@ export default class SetUpAccountsPage extends MockRequests { exact: true, } ); } + + /** + * Get FAQ panel. + * + * @return {import('@playwright/test').Locator} Get FAQ panel. + */ + getFAQPanel() { + return this.page.locator( '.gla-faqs-panel' ); + } + + /** + * Get FAQ panel title. + * + * @return {import('@playwright/test').Locator} Get FAQ panel title. + */ + getFAQPanelTitle() { + return this.getFAQPanel().locator( '.components-panel__body-title' ); + } + + /** + * Get FAQ panel row. + * + * @return {import('@playwright/test').Locator} Get FAQ panel row. + */ + getFAQPanelRow() { + return this.getFAQPanel().locator( '.components-panel__row' ); + } } From d5ef12d57ddd557645d71954a4a1e97867f12979 Mon Sep 17 00:00:00 2001 From: Ian Yu-Hsun Lin Date: Wed, 6 Sep 2023 15:01:56 +0800 Subject: [PATCH 17/17] Add tests for links on the setup accounts page --- .../specs/setup-mc/step-1-accounts.test.js | 28 +++++++++++++++++++ .../pages/setup-mc/step-1-set-up-accounts.js | 26 +++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/tests/e2e/specs/setup-mc/step-1-accounts.test.js b/tests/e2e/specs/setup-mc/step-1-accounts.test.js index 2ed9881af3..8b36519628 100644 --- a/tests/e2e/specs/setup-mc/step-1-accounts.test.js +++ b/tests/e2e/specs/setup-mc/step-1-accounts.test.js @@ -553,4 +553,32 @@ test.describe( 'Set up accounts', () => { ); } ); } ); + + test.describe( 'Links', () => { + test( 'should contain the correct URL for "Google Merchant Center Help" link', async () => { + await setUpAccountsPage.goto(); + const link = setUpAccountsPage.getMCHelpLink(); + await expect( link ).toBeVisible(); + await expect( link ).toHaveAttribute( + 'href', + 'https://support.google.com/merchants/topic/9080307' + ); + } ); + + test( 'should contain the correct URL for CSS Partners link', async () => { + const cssPartersLink = + 'https://comparisonshoppingpartners.withgoogle.com/find_a_partner/'; + const link = setUpAccountsPage.getCSSPartnersLink(); + await expect( link ).toBeVisible(); + await expect( link ).toHaveAttribute( 'href', cssPartersLink ); + + const faqTitle2 = setUpAccountsPage.getFAQPanelTitle().nth( 1 ); + await faqTitle2.click(); + const linkInFAQ = setUpAccountsPage.getCSSPartnersLink( + 'Please find more information here' + ); + await expect( linkInFAQ ).toBeVisible(); + await expect( linkInFAQ ).toHaveAttribute( 'href', cssPartersLink ); + } ); + } ); } ); diff --git a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js index 36af5e8098..3f87e9be52 100644 --- a/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js +++ b/tests/e2e/utils/pages/setup-mc/step-1-set-up-accounts.js @@ -377,4 +377,30 @@ export default class SetUpAccountsPage extends MockRequests { getFAQPanelRow() { return this.getFAQPanel().locator( '.components-panel__row' ); } + + /** + * Get link of Google Merchant Center Help. + * + * @return {import('@playwright/test').Locator} Get link of Google Merchant Center Help. + */ + getMCHelpLink() { + return this.page.getByRole( 'link', { + name: 'Google Merchant Center Help', + exact: true, + } ); + } + + /** + * Get link of CSS partners. + * + * @param {string} name + * + * @return {import('@playwright/test').Locator} Get link of CSS partners. + */ + getCSSPartnersLink( name = 'here' ) { + return this.page.getByRole( 'link', { + name, + exact: true, + } ); + } }