Skip to content

Commit

Permalink
Jetpack E2E: add Social Connection spec using Tumblr. (Automattic#81073)
Browse files Browse the repository at this point in the history
* packages/calypso-e2e/src/secrets/types.ts
packages/calypso-e2e/src/secrets/secrets-manager.ts
packages/calypso-e2e/src/secrets/encrypted.enc
- update secrets with new social connections category

packages/calypso-e2e/src/lib/pages/marketing-page.ts
- add methods to handle social connections

packages/calypso-e2e/src/types/rest-api-client.types.ts
- new methods to get and delete publicize connections

test/e2e/specs/tools/social-connections__tumblr.ts
- add new spec to connect and disconnect social connection

* packages/calypso-e2e/src/lib/pages/marketing-page.ts
- change return signature of validateSocialConnected method.

* test/e2e/specs/tools/social-connections__tumblr.ts
- replace Object.forEach with for...of loops

* test/e2e/specs/tools/social-connections__tumblr.ts
- replace sidebar navigation with direct visits to the page.

packages/calypso-e2e/src/lib/pages/marketing-page.ts
- add a `waitForLoadState` wait for the popup.

* packages/calypso-e2e/src/rest-api-client.ts
- add JSDoc

* test/e2e/specs/tools/social-connections__tumblr.ts
- add exception for private sites
  • Loading branch information
worldomonation authored Aug 28, 2023
1 parent 675ef33 commit 8fed7df
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 4 deletions.
90 changes: 86 additions & 4 deletions packages/calypso-e2e/src/lib/pages/marketing-page.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { Page } from 'playwright';
import { getCalypsoURL } from '../../data-helper';
import { clickNavTab } from '../../element-helper';

type MarketingPageTab =
| 'Marketing Tools'
| 'Traffic'
| 'Connections'
| 'Sharing Buttons'
| 'Business Tools';
type SocialConnection = 'Facebook' | 'LinkedIn' | 'Tumblr' | 'Mastodon' | 'Instagram Business';

const selectors = {
// Traffic tab
websiteMetaTextArea: '#advanced_seo_front_page_description',
Expand All @@ -25,12 +34,20 @@ export class MarketingPage {
}

/**
* Given a string, clicks on the tab matching the string at top of the page.
* Navigates directly to the Marketing page for the site.
*
* @param {string} name Name of the tab to click on the top of the page.
* @returns {Promise<void>} No return value.
* @param {string} siteSlug Site slug.
*/
async visit( siteSlug: string ) {
await this.page.goto( getCalypsoURL( `marketing/tools/${ siteSlug }` ) );
}

/**
* Click on the tab name matching the given parameter `name`.
*
* @param {MarketingPageTab} name Name of the tab to click on the top of the page.
*/
async clickTab( name: string ): Promise< void > {
async clickTab( name: MarketingPageTab ) {
await clickNavTab( this.page, name );
}

Expand Down Expand Up @@ -66,4 +83,69 @@ export class MarketingPage {
await this.page.click( selectors.seoPreviewPaneCloseButton );
await this.page.waitForSelector( selectors.seoPreviewButton );
}

/* Social Connectisons */

/**
* Clicks on the Connection button for specified Social service.
*
* @param {SocialConnection} target Social service.
*/
async clickSocialConnectButton( target: SocialConnection ): Promise< Page > {
// Set up a handler for the popup promise.
const popupPromise = this.page.waitForEvent( 'popup' );

await this.page
.getByRole( 'main' )
.getByRole( 'listitem' )
.filter( { hasText: target } )
.getByRole( 'button', { name: 'Connect' } )
.click();

return await popupPromise;
}

/**
* Validates the specified Social service button now is connected.
*
* @param {SocialConnection} target Social service.
* @throws {Error} If the social connection was not made for any reason.
*/
async validateSocialConnected( target: SocialConnection ): Promise< void > {
await this.page
.getByRole( 'main' )
.getByRole( 'listitem' )
.filter( { hasText: target } )
.getByRole( 'button', { name: 'Disconnect' } )
.waitFor();
}

/**
* Tailored method to set up the Tumblr connection.
*
* @param {Page} popup Pointer to the popup Page object.
* @param param1 Keyed object parameter.
* @param {string} param1.username Tumblr username.
* @param {string} param1.password Tumblr password.
*/
async setupTumblr( popup: Page, { username, password }: { username: string; password: string } ) {
// Wait for the page load to complete. Otherwise, a `Cannot POST /login` error
// is shown.
await popup.waitForLoadState( 'networkidle' );

// Fill in the email and password.
await popup.getByRole( 'textbox', { name: 'email' } ).fill( username );
await popup.getByPlaceholder( 'Password' ).fill( password );

// Log in.
await popup.getByRole( 'button', { name: 'Log in' } ).click();

// Click on Tumblr side's "Allow" button.
const popupClosePromise = popup.waitForEvent( 'close' );
await popup.getByRole( 'button', { name: 'Allow' } ).click();
await popupClosePromise;

// Click on the Calypso side's "Connect" button.
await this.page.getByRole( 'dialog' ).getByRole( 'button', { name: 'Connect' } ).click();
}
}
61 changes: 61 additions & 0 deletions packages/calypso-e2e/src/rest-api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
PluginParams,
AllDomainsResponse,
DomainData,
PublicizeConnectionDeletedResponse,
PublicizeConnection,
} from './types';
import type { Roles } from './lib';
import type {
Expand Down Expand Up @@ -1132,4 +1134,63 @@ export class RestAPIClient {

return response;
}

/* Publicize */

/**
* Returns an array of existing publicize (social) connections.
*
* @param {number} siteID Site ID.
* @returns {Promise<Array<PublicizeConnection>>} Array of Publicize connections.
*/
async getAllPublicizeConnections( siteID: number ): Promise< Array< PublicizeConnection > > {
const params: RequestParams = {
method: 'get',
headers: {
Authorization: await this.getAuthorizationHeader( 'bearer' ),
'Content-Type': this.getContentTypeHeader( 'json' ),
},
};

const response = await this.sendRequest(
this.getRequestURL( '1.1', `/sites/${ siteID }/publicize-connections` ),
params
);

if ( response.hasOwnProperty( 'error' ) ) {
throw new Error(
`${ ( response as ErrorResponse ).error }: ${ ( response as ErrorResponse ).message }`
);
}

return response[ 'connections' ];
}

/**
* Given siteID and connectionID, deletes the connection.
*
* @param {number} siteID Site ID.
* @param {number} connectionID Publicize connection ID.
* @returns {Promise<PublicizeConnectionDeletedResponse>} Confirmation of connection being deleted.
*/
async deletePublicizeConnection(
siteID: number,
connectionID: number
): Promise< PublicizeConnectionDeletedResponse > {
const params: RequestParams = {
method: 'post',
headers: {
Authorization: await this.getAuthorizationHeader( 'bearer' ),
'Content-Type': this.getContentTypeHeader( 'json' ),
},
};

return await this.sendRequest(
this.getRequestURL(
'1.1',
`/sites/${ siteID }/publicize-connections/${ connectionID }/delete`
),
params
);
}
}
Binary file modified packages/calypso-e2e/src/secrets/encrypted.enc
Binary file not shown.
6 changes: 6 additions & 0 deletions packages/calypso-e2e/src/secrets/secrets-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ export class SecretsManager {
martechTosUploadCredentials: {
bearer_token: 'FAKE_VALUE',
},
socialAccounts: {
tumblr: {
username: 'FAKE_VALUE',
password: 'FAKE_VALUE',
},
},
mailosaur: {
apiKey: 'FAKE_VALUE',
inviteInboxId: 'FAKE_VALUE',
Expand Down
6 changes: 6 additions & 0 deletions packages/calypso-e2e/src/secrets/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export interface Secrets {
martechTosUploadCredentials: {
bearer_token: string;
};
socialAccounts: {
tumblr: {
username: string;
password: string;
};
};
mailosaur: {
apiKey: string;
inviteInboxId: string;
Expand Down
12 changes: 12 additions & 0 deletions packages/calypso-e2e/src/types/rest-api-client.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,18 @@ export interface JetpackSearchResponse {
// Lots more of course -- add as needed!
}

export interface PublicizeConnection {
ID: number;
site_ID: number;
label: string;
external_ID: string;
}

export interface PublicizeConnectionDeletedResponse {
ID: number;
deleted: boolean;
}

/* Error Responses */

export interface BearerTokenErrorResponse {
Expand Down
85 changes: 85 additions & 0 deletions test/e2e/specs/tools/social-connections__tumblr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* @group jetpack-wpcom-integration
*/

import {
envToFeatureKey,
getTestAccountByFeature,
envVariables,
DataHelper,
MarketingPage,
RestAPIClient,
TestAccount,
SecretsManager,
} from '@automattic/calypso-e2e';
import { Page, Browser } from 'playwright';
import { skipDescribeIf } from '../../jest-helpers';

declare const browser: Browser;

/**
* Sets up a Tumblr social connection for the site.
*
* Note, Private sites do not support Social/Publicize connections.
*
* Keywords: Social Connections, Marketing, Jetpack, Tumblr, Publicize
*/
skipDescribeIf( envVariables.ATOMIC_VARIATION === 'private' )(
DataHelper.createSuiteTitle( 'Social Connections: Set up Tumblr' ),
function () {
let page: Page;
let popup: Page;

let testAccount: TestAccount;
let restAPIClient: RestAPIClient;
let marketingPage: MarketingPage;

beforeAll( async () => {
page = await browser.newPage();

const features = envToFeatureKey( envVariables );
const accountName = getTestAccountByFeature( features );
testAccount = new TestAccount( accountName );
await testAccount.authenticate( page );

restAPIClient = new RestAPIClient( testAccount.credentials );

// Check whether a Tumblr connection exists.
const connections = await restAPIClient.getAllPublicizeConnections(
testAccount.credentials.testSites?.primary.id as number
);

// If it does, remove the connection.
for ( const connection of connections ) {
if ( connection.label === 'Tumblr' ) {
console.info(
`Removing existing connection for Tumblr for accountName ${ accountName }.`
);
await restAPIClient.deletePublicizeConnection( connection.site_ID, connection.ID );
}
}

marketingPage = new MarketingPage( page );
} );

it( 'Navigate to Tools > Marketing page', async function () {
await marketingPage.visit( testAccount.getSiteURL( { protocol: false } ) );
} );

it( 'Click on Connections tab', async function () {
await marketingPage.clickTab( 'Connections' );
} );

it( 'Click on the "Connect" button for Tumblr', async function () {
popup = await marketingPage.clickSocialConnectButton( 'Tumblr' );
} );

it( 'Set up Tumblr', async function () {
await marketingPage.setupTumblr( popup, SecretsManager.secrets.socialAccounts.tumblr );
} );

it( 'Tumblr is connected', async function () {
await marketingPage.validateSocialConnected( 'Tumblr' );
} );
}
);

0 comments on commit 8fed7df

Please sign in to comment.