From 19b9206a90591c5785e427e46a07f3130c2308f5 Mon Sep 17 00:00:00 2001 From: Edwin Takahashi Date: Fri, 8 Sep 2023 23:36:27 +0900 Subject: [PATCH 1/2] packages/calypso-e2e/src/rest-api-client.ts - implement new method to see if there are posts on the site. test/e2e/specs/tools/advertising__promote.ts - stop creating new post unnecessarily --- packages/calypso-e2e/src/rest-api-client.ts | 28 +++++++++++++++++++ .../src/types/rest-api-client.types.ts | 2 ++ test/e2e/specs/tools/advertising__promote.ts | 26 ++++++++++++----- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/packages/calypso-e2e/src/rest-api-client.ts b/packages/calypso-e2e/src/rest-api-client.ts index d74bca72e06c86..996a12a6ad5d0a 100644 --- a/packages/calypso-e2e/src/rest-api-client.ts +++ b/packages/calypso-e2e/src/rest-api-client.ts @@ -42,6 +42,7 @@ import type { JetpackSearchResponse, JetpackSearchParams, Subscriber, + SitePostState, } from './types'; import type { BodyInit, HeadersInit, RequestInit } from 'node-fetch'; @@ -635,6 +636,33 @@ export class RestAPIClient { /* Posts */ + /** + * Given a siteID, checks whether any posts exists of a given state. + * + * @param {number} siteID Site ID. + * @param param1 Keyed object parameter. + * @param {SitePostState} param1.state State of the published post. + */ + async siteHasPost( + siteID: number, + { state = 'publish' }: { state: SitePostState } + ): Promise< boolean > { + 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 }/post-counts/post` ), + params + ); + + return response.counts.all[ state ] !== 0 ? true : false; + } + /** * Creates a post on the site. * diff --git a/packages/calypso-e2e/src/types/rest-api-client.types.ts b/packages/calypso-e2e/src/types/rest-api-client.types.ts index b84e3ced768c8d..085e8cae0f85b4 100644 --- a/packages/calypso-e2e/src/types/rest-api-client.types.ts +++ b/packages/calypso-e2e/src/types/rest-api-client.types.ts @@ -1,5 +1,7 @@ /* Parameter Interfaces */ +export type SitePostState = 'publish' | 'draft' | 'private'; + export interface AccountDetails { userID: number; username: string; diff --git a/test/e2e/specs/tools/advertising__promote.ts b/test/e2e/specs/tools/advertising__promote.ts index 5e569bc8574213..d075f378aea8c7 100644 --- a/test/e2e/specs/tools/advertising__promote.ts +++ b/test/e2e/specs/tools/advertising__promote.ts @@ -50,16 +50,23 @@ skipDescribeIf( envVariables.ATOMIC_VARIATION === 'private' )( const accountName = getTestAccountByFeature( envToFeatureKey( envVariables ) ); testAccount = new TestAccount( accountName ); - // Createa a new test post before starting the test, to ensure at least one - // available post. restAPIClient = new RestAPIClient( testAccount.credentials ); - newPostDetails = await restAPIClient.createPost( + + // Createa a new test post before starting the test if site has no published post. + const hasPosts = await restAPIClient.siteHasPost( testAccount.credentials.testSites?.primary.id as number, - { - title: pageTitle, - } + { state: 'publish' } ); + if ( ! hasPosts ) { + newPostDetails = await restAPIClient.createPost( + testAccount.credentials.testSites?.primary.id as number, + { + title: pageTitle, + } + ); + } + await testAccount.authenticate( page ); advertisingPage = new AdvertisingPage( page ); @@ -75,11 +82,12 @@ skipDescribeIf( envVariables.ATOMIC_VARIATION === 'private' )( } ); it( 'Click on Promote for the first post', async function () { - await advertisingPage.clickButtonByNameOnRow( 'Promote', { postTitle: pageTitle } ); + await advertisingPage.clickButtonByNameOnRow( 'Promote', { row: 1 } ); } ); it( 'Land in Blaze campaign landing page', async function () { await page.waitForURL( /advertising/ ); + blazeCampaignPage = new BlazeCampaignPage( page ); } ); @@ -102,6 +110,10 @@ skipDescribeIf( envVariables.ATOMIC_VARIATION === 'private' )( } ); afterAll( async function () { + if ( ! newPostDetails ) { + return; + } + await restAPIClient.deletePost( testAccount.credentials.testSites?.primary.id as number, newPostDetails.ID From c597382d4859ef377923268268e1f712013d1ebf Mon Sep 17 00:00:00 2001 From: Edwin Takahashi Date: Mon, 11 Sep 2023 22:40:25 +0900 Subject: [PATCH 2/2] packages/calypso-e2e/src/rest-api-client.ts - remove unnecessary ternary packages/calypso-e2e/src/types/rest-api-client.types.ts - add new type to type check the response from PostCounts endpoint. --- packages/calypso-e2e/src/rest-api-client.ts | 43 +++---------------- .../src/types/rest-api-client.types.ts | 18 +++++++- 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/packages/calypso-e2e/src/rest-api-client.ts b/packages/calypso-e2e/src/rest-api-client.ts index 996a12a6ad5d0a..3ccf3944fd3d50 100644 --- a/packages/calypso-e2e/src/rest-api-client.ts +++ b/packages/calypso-e2e/src/rest-api-client.ts @@ -12,6 +12,7 @@ import { PublicizeConnectionDeletedResponse, PublicizeConnection, SubscriberDeletedResponse, + PostCountsResponse, } from './types'; import type { Roles } from './lib'; import type { @@ -80,7 +81,6 @@ export class RestAPIClient { /** * Constructs an instance of the API client. - * * @param {AccountCredentials} credentials User credentials. * @param {string} [bearerToken] BearerToken for the user. */ @@ -94,7 +94,6 @@ export class RestAPIClient { * * If the token has been previously obtained, this method returns the value. * Otherwise, an API call is made to obtain the bearer token and the resulting value is returned - * * @returns {Promise} String representing the bearer token. * @throws {Error} If the API responded with a success status of false. */ @@ -133,7 +132,6 @@ export class RestAPIClient { /** * Returns the appropriate authorization header. - * * @returns {Promise} Authorization header in the requested scheme. * @throws {Error} If a scheme not yet implemented is requested. */ @@ -148,7 +146,6 @@ export class RestAPIClient { /** * Returns the formatted Content-Type header string. - * * @returns {string} Content-Type header string. */ private getContentTypeHeader( value: 'json' ): string { @@ -157,7 +154,6 @@ export class RestAPIClient { /** * Returns a fully constructed URL object pointing to the request endpoint. - * * @param {EndpointVersions} version Version of the API to use. * @param {string} endpoint REST API path. * @param {EndpointNamespace} [namespace] REST API namespace. @@ -175,7 +171,6 @@ export class RestAPIClient { /** * Sends the request to the endpoint, then returns the decoded JSON. - * * @param {URL} url URL of the endpoint. * @param {RequestParams} params Parameters for the request. * @returns {Promise} Decoded JSON response. @@ -193,9 +188,8 @@ export class RestAPIClient { * This method returns an array of DomainData objects, where * each object exposes a few key pieces of data from * the response JSON: - * - domain - * - blog id - * + * - domain + * - blog id * @returns {Promise} JSON array of sites. * @throws {Error} If API responded with an error. */ @@ -221,7 +215,6 @@ export class RestAPIClient { /** * Given parameters, create a new site. - * * @param {NewSiteParams} newSiteParams Details for the new site. * @returns {Promise} Confirmation details for the new site. * @throws {ErrorResponse} If API responded with an error. @@ -269,7 +262,6 @@ export class RestAPIClient { * * Otherwise the active subscription must be first cancelled * or else the REST API will throw a HTTP 403 status. - * * @param { {id: number, domain: string}} targetSite Details for the target site to be deleted. * @returns {SiteDeletionResponse | null} Null if deletion was unsuccessful or not performed. SiteDeletionResponse otherwise. */ @@ -323,7 +315,6 @@ export class RestAPIClient { /** * Creates a user invite. - * * @param {number} siteID ID of the site where a new invite will be created. * @param param0 Keyed object parameter. * @param {string[]} param0.email List of emails to send invites to. @@ -464,7 +455,6 @@ export class RestAPIClient { /** * Returns the account information for the user authenticated * via the bearer token. - * * @returns {Promise} Response containing user details. * @throws {Error} If API responded with an error. */ @@ -490,7 +480,6 @@ export class RestAPIClient { /** * Updates the user's settings. - * * @param {SettingsParams} details Key/value attributes to be set for the user. * @returns { { [key: string]: string | number } } Generic object. * @throws {Error} If an unknown attribute or invalid value for a known attribute was provided. @@ -526,7 +515,6 @@ export class RestAPIClient { * The userID, username and email of the account that is * authenticated via the bearer token is checked against the * supplied parameters. - * * @param { AccountDetails} expectedAccountDetails Details of the accounts to be closed. * @returns {Promise} True if account closure was successful. False otherwise. */ @@ -588,7 +576,6 @@ export class RestAPIClient { /** * Returns Calypso preferences for the user. - * * @returns {Promise} JSON response containing Calypso preferences. */ async getCalypsoPreferences(): Promise< CalypsoPreferencesResponse > { @@ -607,7 +594,6 @@ export class RestAPIClient { /** * Gets the latest posts from blogs a user follows. - * * @returns {Promise} An Array of posts. * @throws {Error} If API responded with an error. */ @@ -638,7 +624,6 @@ export class RestAPIClient { /** * Given a siteID, checks whether any posts exists of a given state. - * * @param {number} siteID Site ID. * @param param1 Keyed object parameter. * @param {SitePostState} param1.state State of the published post. @@ -655,17 +640,16 @@ export class RestAPIClient { }, }; - const response = await this.sendRequest( + const response: PostCountsResponse = await this.sendRequest( this.getRequestURL( '1.1', `/sites/${ siteID }/post-counts/post` ), params ); - return response.counts.all[ state ] !== 0 ? true : false; + return response.counts.all[ state ] !== 0; } /** * Creates a post on the site. - * * @param {number} siteID Target site ID. * @param {NewPostParams} details Details of the new post. */ @@ -695,7 +679,6 @@ export class RestAPIClient { /** * Deletes a post denoted by postID from the site. - * * @param {number} siteID Target site ID. * @param {number} postID Target post ID. */ @@ -726,7 +709,6 @@ export class RestAPIClient { /** * Creates a comment on the given post. - * * @param {number} siteID Target site ID. * @param {number} postID Target post ID. * @param {string} comment Details of the new comment. @@ -763,7 +745,6 @@ export class RestAPIClient { /** * Deletes a given comment from a site. - * * @param {number} siteID Target site ID. * @param {number} commentID Target comment ID. * @returns {Promise< any >} Decoded JSON response. @@ -794,7 +775,6 @@ export class RestAPIClient { /** * Method to perform two similar operations - like and unlike a comment. - * * @param {'like'|'unlike'} action Action to perform on the comment. * @param {number} siteID Target site ID. * @param {number} commentID Target comment ID. @@ -846,7 +826,6 @@ export class RestAPIClient { /** * Uploads a media file. - * * @param {number} siteID Target site ID. * @param param1 Optional object parameter. * @param {TestFile} param1.media Local media file to be uploaded. @@ -906,7 +885,6 @@ export class RestAPIClient { /** * Clears the shopping cart. - * * @param {number} siteID Site that has the shopping cart. * @throws {Error} If the user doesn't have access to the siteID. * @returns {{success:true}} If the request was successful. @@ -938,7 +916,6 @@ export class RestAPIClient { /** * Gets a list of plugins installed in a site. - * * @param {number} siteID Target site ID. * @returns {Promise} An Array of plugins. * @throws {Error} If API responded with an error. @@ -968,7 +945,6 @@ export class RestAPIClient { /** * Modifies a plugin installed in a site. - * * @param {number} siteID Target site ID. * @param {string} pluginID Plugin ID. * @param {PluginResponse} details Key/value attributes to be set for the user. @@ -1005,7 +981,6 @@ export class RestAPIClient { /** * Finds a plugin by name, deactivates it, and removes it from the site. - * * @returns {Promise} Null if plugin removal was unsuccessful or not performed. PluginRemovalResponse otherwise. * @throws {Error} If API responded with an error. */ @@ -1047,7 +1022,6 @@ export class RestAPIClient { * * As noted in the comments, this method is quite overloaded as its outcome * differs depending on the current state of the widget (activate/deactivated). - * * @param {number} siteID ID of the target site. * @param {string} widgetID ID of the target widget. */ @@ -1087,7 +1061,6 @@ export class RestAPIClient { /** * Returns the list of widgets for a siteID. - * * @param {number} siteID ID of the target site. * @returns {AllWidgetsResponse} Array of Widgets object describing the list of widgets on the site. */ @@ -1116,7 +1089,6 @@ export class RestAPIClient { /** * Deletes or deactivates all widgets for a given site. - * * @param {number} siteID ID of the target site. */ async deleteAllWidgets( siteID: number ): Promise< void > { @@ -1130,7 +1102,6 @@ export class RestAPIClient { /** * Execute a primitive Jetpack site search request. * Useful for checking if something has been indexed yet. - * * @param {number} siteId ID of the target site. * @param {JetpackSearchParams} searchParams The search parameters. */ @@ -1169,7 +1140,6 @@ export class RestAPIClient { /** * Returns an array of existing publicize (social) connections. - * * @param {number} siteID Site ID. * @returns {Promise>} Array of Publicize connections. */ @@ -1198,7 +1168,6 @@ export class RestAPIClient { /** * Given siteID and connectionID, deletes the connection. - * * @param {number} siteID Site ID. * @param {number} connectionID Publicize connection ID. * @returns {Promise} Confirmation of connection being deleted. @@ -1228,7 +1197,6 @@ export class RestAPIClient { /** * Given a site ID, returns the list of newsletter subscribers. - * * @param {number} siteID Site ID to return list of users for. */ async getAllSubscribers( siteID: number ): Promise< Subscriber[] > { @@ -1252,7 +1220,6 @@ export class RestAPIClient { /** * Given a siteID and email address of the subscribed user to delete, * removes the subscribed user. - * * @param {number} siteID Site ID where the user is subscribed. * @param {string} email Email address of the subscriber to delete. */ diff --git a/packages/calypso-e2e/src/types/rest-api-client.types.ts b/packages/calypso-e2e/src/types/rest-api-client.types.ts index 085e8cae0f85b4..fc43a4625717bf 100644 --- a/packages/calypso-e2e/src/types/rest-api-client.types.ts +++ b/packages/calypso-e2e/src/types/rest-api-client.types.ts @@ -1,6 +1,6 @@ /* Parameter Interfaces */ -export type SitePostState = 'publish' | 'draft' | 'private'; +export type SitePostState = 'publish' | 'draft' | 'trash' | 'future'; export interface AccountDetails { userID: number; @@ -225,6 +225,22 @@ export interface SubscriberDeletedResponse { deleted: true; } +export interface PostCountsResponse { + counts: { + all: { + publish: number; + future: number; + draft: number; + trash: number; + }; + mine: { + draft: number; + publish: number; + trash: number; + }; + }; +} + /* Error Responses */ export interface BearerTokenErrorResponse {