mirror of
https://github.com/elementor/hello-theme.git
synced 2026-07-29 15:07:33 +08:00
Some checks failed
Build / Build theme (push) Failing after 1s
Lint / ESLint (push) Failing after 0s
PHPUnit / File Diff (push) Failing after 1s
Lint / PHPCS (push) Failing after 41s
PHPUnit / PHPUnit - WordPress 6.8 - PHP version 7.4 (push) Has been skipped
PHPUnit / PHPUnit - WordPress 6.9 - PHP version 7.4 (push) Has been skipped
PHPUnit / PHPUnit - WordPress latest - PHP version 7.4 (push) Has been skipped
PHPUnit / PHPUnit - WordPress nightly - PHP version 7.4 (push) Has been skipped
PHPUnit / PHPUnit - WordPress 6.8 - PHP version 8.0 (push) Has been skipped
PHPUnit / PHPUnit - WordPress 6.9 - PHP version 8.0 (push) Has been skipped
PHPUnit / PHPUnit - WordPress latest - PHP version 8.0 (push) Has been skipped
PHPUnit / PHPUnit - WordPress nightly - PHP version 8.0 (push) Has been skipped
PHPUnit / PHPUnit - WordPress 6.8 - PHP version 8.1 (push) Has been skipped
PHPUnit / PHPUnit - WordPress 6.9 - PHP version 8.1 (push) Has been skipped
PHPUnit / PHPUnit - WordPress latest - PHP version 8.1 (push) Has been skipped
PHPUnit / PHPUnit - WordPress nightly - PHP version 8.1 (push) Has been skipped
PHPUnit / PHPUnit - WordPress 6.8 - PHP version 8.2 (push) Has been skipped
PHPUnit / PHPUnit - WordPress 6.9 - PHP version 8.2 (push) Has been skipped
PHPUnit / PHPUnit - WordPress latest - PHP version 8.2 (push) Has been skipped
PHPUnit / PHPUnit - WordPress nightly - PHP version 8.2 (push) Has been skipped
PHPUnit / PHPUnit - WordPress 6.8 - PHP version 8.3 (push) Has been skipped
PHPUnit / PHPUnit - WordPress 6.9 - PHP version 8.3 (push) Has been skipped
PHPUnit / PHPUnit - WordPress latest - PHP version 8.3 (push) Has been skipped
PHPUnit / PHPUnit - WordPress nightly - PHP version 8.3 (push) Has been skipped
PHPUnit / PHPUnit - Test Results (push) Successful in 2s
* Setup prettier * Fix lint/format issues
464 lines
13 KiB
TypeScript
464 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();
|
|
}
|
|
}
|