mirror of
https://gh.wpcy.net/https://github.com/elementor/hello-theme.git
synced 2026-04-25 11:52:21 +08:00
412 lines
13 KiB
TypeScript
412 lines
13 KiB
TypeScript
import { type APIRequestContext, type Page, Response, type TestInfo } from '@playwright/test';
|
|
import BasePage from './base-page.ts';
|
|
import EditorPage from './editor-page.ts';
|
|
import { ElementorType, WindowType } from '../types/types.ts';
|
|
import { wpCli } from '../assets/wp-cli.ts';
|
|
import ApiRequests from '../assets/api-requests.ts';
|
|
let elementor: ElementorType;
|
|
|
|
export default class WpAdminPage extends BasePage {
|
|
protected readonly apiRequests: ApiRequests;
|
|
|
|
constructor( page: Page, testInfo: TestInfo, apiRequests: ApiRequests ) {
|
|
super( page, testInfo );
|
|
this.apiRequests = apiRequests;
|
|
}
|
|
|
|
/**
|
|
* Go to the WordPress dashboard.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async gotoDashboard(): Promise<void> {
|
|
await this.page.goto( '/wp-admin' );
|
|
}
|
|
|
|
/**
|
|
* If not logged in, log in to WordPress. Otherwise, go to the WordPress dashboard.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async login(): Promise<void> {
|
|
await this.gotoDashboard();
|
|
|
|
const loggedIn = await this.page.$( 'text=Dashboard' );
|
|
|
|
if ( loggedIn ) {
|
|
return;
|
|
}
|
|
|
|
await this.page.waitForSelector( 'text=Log In' );
|
|
await this.page.fill( 'input[name="log"]', process.env.USERNAME );
|
|
await this.page.fill( 'input[name="pwd"]', process.env.PASSWORD );
|
|
await this.page.click( 'text=Log In' );
|
|
await this.page.waitForSelector( 'text=Dashboard' );
|
|
}
|
|
|
|
/**
|
|
* Log in to WordPress with custom credentials.
|
|
*
|
|
* @param {string} username - The username to log in with.
|
|
* @param {string} password - The password to log in with.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async customLogin( username: string, password: string ): Promise<void> {
|
|
await this.gotoDashboard();
|
|
const loggedIn = await this.page.$( 'text=Dashboard' );
|
|
|
|
if ( loggedIn ) {
|
|
await this.page.hover( '#wp-admin-bar-top-secondary' );
|
|
await this.page.click( '#wp-admin-bar-logout > a' );
|
|
}
|
|
|
|
await this.page.fill( 'input[name="log"]', username );
|
|
await this.page.fill( 'input[name="pwd"]', password );
|
|
await this.page.locator( 'text=Log In' ).last().click();
|
|
await this.page.waitForSelector( 'text=Dashboard' );
|
|
}
|
|
|
|
/**
|
|
* Open a new Elementor page.
|
|
*
|
|
* @param {boolean} setWithApi - Optional. Whether to create the page with the API. Default is true.
|
|
* @param {boolean} setPageName - Optional. Whether to set the page name. Default is true.
|
|
*
|
|
* @return {Promise<EditorPage>} A promise that resolves to the new editor page instance.
|
|
*/
|
|
async openNewPage( setWithApi: boolean = true, setPageName: boolean = true ): Promise<EditorPage> {
|
|
if ( setWithApi ) {
|
|
await this.createNewPostWithAPI();
|
|
} else {
|
|
await this.createNewPostFromDashboard( setPageName );
|
|
}
|
|
|
|
await this.page.waitForLoadState( 'load', { timeout: 20000 } );
|
|
await this.waitForPanel();
|
|
await this.closeAnnouncementsIfVisible();
|
|
|
|
return new EditorPage( this.page, this.testInfo );
|
|
}
|
|
|
|
/**
|
|
* Create a new page with the API and open it in Elementor.
|
|
*
|
|
* @return {Promise<string>} A promise that resolves to the created page ID.
|
|
*/
|
|
async createNewPostWithAPI(): Promise<string> {
|
|
const request: APIRequestContext = this.page.context().request,
|
|
postDataInitial = {
|
|
title: 'Playwright Test Page - Uninitialized',
|
|
content: '',
|
|
},
|
|
postId = await this.apiRequests.create( request, 'pages', postDataInitial ),
|
|
postDataUpdated = {
|
|
title: `Playwright Test Page #${ postId }`,
|
|
};
|
|
|
|
await this.apiRequests.create( request, `pages/${ postId }`, postDataUpdated );
|
|
await this.page.goto( `/wp-admin/post.php?post=${ postId }&action=elementor` );
|
|
|
|
return postId;
|
|
}
|
|
|
|
/**
|
|
* Create a new page from the WordPress dashboard.
|
|
*
|
|
* @param {boolean} setPageName - Whether to set the page name.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async createNewPostFromDashboard( setPageName: boolean ): Promise<void> {
|
|
if ( ! await this.page.$( '.e-overview__create > a' ) ) {
|
|
await this.gotoDashboard();
|
|
}
|
|
|
|
await this.page.click( '.e-overview__create > a' );
|
|
|
|
if ( ! setPageName ) {
|
|
return;
|
|
}
|
|
|
|
await this.setPageName();
|
|
}
|
|
|
|
/**
|
|
* Set the page name.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async setPageName(): Promise<void> {
|
|
await this.page.locator( '#elementor-panel-footer-settings' ).click();
|
|
|
|
const pageId = await this.page.evaluate( () => elementor.config.initialDocument.id );
|
|
await this.page.locator( '.elementor-control-post_title input' ).fill( `Playwright Test Page #${ pageId }` );
|
|
|
|
await this.page.locator( '#elementor-panel-footer-saver-options' ).click();
|
|
await this.page.locator( '#elementor-panel-footer-sub-menu-item-save-draft' ).click();
|
|
await this.page.locator( '#elementor-panel-header-add-button' ).click();
|
|
}
|
|
|
|
/**
|
|
* Convert the current page from Gutenberg to Elementor.
|
|
*
|
|
* @return {Promise<EditorPage>} A promise that resolves to the editor page instance.
|
|
*/
|
|
async convertFromGutenberg(): Promise<EditorPage> {
|
|
await Promise.all( [
|
|
this.page.waitForResponse( async ( response ) => await this.blockUrlResponse( response ) ),
|
|
this.page.click( '#elementor-switch-mode' ),
|
|
] );
|
|
|
|
await this.page.waitForURL( '**/post.php?post=*&action=elementor' );
|
|
await this.page.waitForLoadState( 'load', { timeout: 20000 } );
|
|
await this.waitForPanel();
|
|
|
|
await this.closeAnnouncementsIfVisible();
|
|
|
|
return new EditorPage( this.page, this.testInfo );
|
|
}
|
|
|
|
/**
|
|
* Get the response status for the API request.
|
|
*
|
|
* @param {Response} response - The response object.
|
|
*
|
|
* @return {Promise<boolean>} A promise that resolves to true if the response is a valid REST/JSON request with a 200 status.
|
|
*/
|
|
async blockUrlResponse( response: Response ): Promise<boolean> {
|
|
const isRestRequest = response.url().includes( 'rest_route=%2Fwp%2Fv2%2Fpages%2' ); // For local testing
|
|
const isJsonRequest = response.url().includes( 'wp-json/wp/v2/pages' ); // For CI testing
|
|
return ( isJsonRequest || isRestRequest ) && 200 === response.status();
|
|
}
|
|
|
|
/**
|
|
* Wait for the Elementor editor panel to finish loading.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async waitForPanel(): Promise<void> {
|
|
await this.page.waitForSelector( '.elementor-panel-loading', { state: 'detached' } );
|
|
await this.page.waitForSelector( '#elementor-loading', { state: 'hidden' } );
|
|
}
|
|
|
|
/**
|
|
* Activate and deactivate Elementor experiments.
|
|
*
|
|
* TODO: The testing environment isn't clean between tests - Use with caution!
|
|
*
|
|
* @param {Object} experiments - Experiments settings ( `{ experiment_id: true / false }` );
|
|
* @param {(boolean|string)=} oldUrl - Optional. Whether to use the old URL structure. Default is false.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async setExperiments( experiments: { [ n: string ]: boolean | string }, oldUrl: boolean = false ): Promise<void> {
|
|
if ( oldUrl ) {
|
|
await this.page.goto( '/wp-admin/admin.php?page=elementor#tab-experiments' );
|
|
await this.page.click( '#elementor-settings-tab-experiments' );
|
|
} else {
|
|
await this.page.goto( '/wp-admin/admin.php?page=elementor-settings#tab-experiments' );
|
|
}
|
|
|
|
const prefix = 'e-experiment';
|
|
|
|
for ( const [ id, state ] of Object.entries( experiments ) ) {
|
|
const selector = `#${ prefix }-${ id }`;
|
|
|
|
// Try to make the element visible - Since some experiments may be hidden for the user,
|
|
// but actually exist and need to be tested.
|
|
await this.page.evaluate( ( el ) => {
|
|
const element: HTMLElement = document.querySelector( el );
|
|
|
|
if ( element ) {
|
|
element.style.display = 'block';
|
|
}
|
|
}, `.elementor_experiment-${ id }` );
|
|
|
|
await this.page.selectOption( selector, state ? 'active' : 'inactive' );
|
|
|
|
// Click to confirm any experiment that has dependencies.
|
|
await this.confirmExperimentModalIfOpen();
|
|
}
|
|
|
|
await this.page.click( '#submit' );
|
|
}
|
|
|
|
/**
|
|
* Reset all Elementor experiments to their default settings.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async resetExperiments(): Promise<void> {
|
|
await this.page.goto( '/wp-admin/admin.php?page=elementor-settings#tab-experiments' );
|
|
await this.page.getByRole( 'button', { name: 'default' } ).click();
|
|
}
|
|
|
|
/**
|
|
* Set site language.
|
|
*
|
|
* @param {string} language - The site language to set.
|
|
* @param {string|null} userLanguage - Optional. The user language to set. Default is null.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async setSiteLanguage( language: string, userLanguage: string = null ): Promise<void> {
|
|
let languageCheck = language;
|
|
|
|
if ( 'he_IL' === language ) {
|
|
languageCheck = 'he-IL';
|
|
} else if ( '' === language ) {
|
|
languageCheck = 'en_US';
|
|
}
|
|
|
|
await this.page.goto( '/wp-admin/options-general.php' );
|
|
|
|
const isLanguageActive = await this.page.locator( 'html[lang=' + languageCheck + ']' ).isVisible();
|
|
|
|
if ( ! isLanguageActive ) {
|
|
await this.page.selectOption( '#WPLANG', language );
|
|
await this.page.locator( '#submit' ).click();
|
|
}
|
|
|
|
const userProfileLanguage = null !== userLanguage ? userLanguage : language;
|
|
await this.setUserLanguage( userProfileLanguage );
|
|
}
|
|
|
|
/**
|
|
* Set user language.
|
|
*
|
|
* @param {string} language - The language to set.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async setUserLanguage( language: string ): Promise<void> {
|
|
await this.page.goto( 'wp-admin/profile.php' );
|
|
await this.page.selectOption( '[name="locale"]', language );
|
|
await this.page.locator( '#submit' ).click();
|
|
}
|
|
|
|
/**
|
|
* Confirm the Elementor experiment modal if it's open.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async confirmExperimentModalIfOpen(): Promise<void> {
|
|
const dialogButton = this.page.locator( '.dialog-type-confirm .dialog-confirm-ok' );
|
|
|
|
if ( await dialogButton.isVisible() ) {
|
|
await dialogButton.click();
|
|
|
|
// Clicking the confirm button - "Activate" or "Deactivate" - will immediately save the existing experiments,
|
|
// so we need to wait for the page to save and reload before we continue on to set any more experiments.
|
|
await this.page.waitForLoadState( 'load' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the active WordPress theme.
|
|
*
|
|
* @return {Promise<string>} The name of the active WordPress theme.
|
|
*/
|
|
async getActiveTheme(): Promise<string> {
|
|
const request: APIRequestContext = this.page.context().request;
|
|
const themeData = await this.apiRequests.getTheme( request, 'active' );
|
|
return themeData[ 0 ].stylesheet;
|
|
}
|
|
|
|
async activateTheme( theme: string ) {
|
|
await wpCli( `wp theme activate ${ theme }` );
|
|
}
|
|
|
|
/**
|
|
* Enable uploading SVG files.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async enableAdvancedUploads(): Promise<void> {
|
|
await this.page.goto( '/wp-admin/admin.php?page=elementor-settings#tab-advanced' );
|
|
await this.page.locator( 'select[name="elementor_unfiltered_files_upload"]' ).selectOption( '1' );
|
|
await this.page.getByRole( 'button', { name: 'Save Changes' } ).click();
|
|
}
|
|
|
|
/**
|
|
* Disable uploading SVG files.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async disableAdvancedUploads(): Promise<void> {
|
|
await this.page.goto( '/wp-admin/admin.php?page=elementor-settings#tab-advanced' );
|
|
await this.page.locator( 'select[name="elementor_unfiltered_files_upload"]' ).selectOption( '' );
|
|
await this.page.getByRole( 'button', { name: 'Save Changes' } ).click();
|
|
}
|
|
|
|
/**
|
|
* Close the Elementor announcements if they are visible.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async closeAnnouncementsIfVisible(): Promise<void> {
|
|
if ( await this.page.locator( '#e-announcements-root' ).count() > 0 ) {
|
|
await this.page.evaluate( ( selector ) => document.getElementById( selector ).remove(), 'e-announcements-root' );
|
|
}
|
|
let window: WindowType;
|
|
await this.page.evaluate( () => {
|
|
// @ts-ignore editor session is on the window object
|
|
const editorSessionId = window.EDITOR_SESSION_ID;
|
|
window.sessionStorage.setItem( 'ai_promotion_introduction_editor_session_key', editorSessionId );
|
|
} );
|
|
}
|
|
|
|
/**
|
|
* Edit the page with Elementor.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async editWithElementor(): Promise<void> {
|
|
await this.page.getByRole( 'link', { name: ' Edit with Elementor' } ).click();
|
|
}
|
|
|
|
/**
|
|
* Close the block editor popup if it's visible.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async closeBlockEditorPopupIfVisible(): Promise<void> {
|
|
await this.page.locator( '#elementor-switch-mode-button' ).waitFor();
|
|
if ( await this.page.getByRole( 'button', { name: 'Close' } ).isVisible() ) {
|
|
await this.page.getByRole( 'button', { name: 'Close' } ).click();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open a new WordPress page.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async openNewWordpressPage(): Promise<void> {
|
|
await this.page.goto( '/wp-admin/post-new.php?post_type=page' );
|
|
await this.closeBlockEditorPopupIfVisible();
|
|
}
|
|
|
|
/**
|
|
* Hide the WordPress admin bar.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async hideAdminBar(): Promise<void> {
|
|
await this.page.goto( '/wp-admin/profile.php' );
|
|
await this.page.locator( '#admin_bar_front' ).uncheck();
|
|
await this.page.locator( '#submit' ).click();
|
|
}
|
|
|
|
/**
|
|
* Show the WordPress admin bar.
|
|
*
|
|
* @return {Promise<void>}
|
|
*/
|
|
async showAdminBar(): Promise<void> {
|
|
await this.page.goto( '/wp-admin/profile.php' );
|
|
await this.page.locator( '#admin_bar_front' ).check();
|
|
await this.page.locator( '#submit' ).click();
|
|
}
|
|
}
|