Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
tools: composer
coverage: none

- name: Install Composer dependencies
run: composer install --no-dev --no-scripts --optimize-autoloader

- name: Install npm dependencies
run: npm ci
env:
Expand All @@ -55,6 +58,14 @@ jobs:
echo "exit_code=$?" >> $GITHUB_OUTPUT
continue-on-error: false

- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-test-results-${{ matrix.shardIndex }}
path: ${{ inputs.path-to-results || 'test-results/' }}
if-no-files-found: ignore
retention-days: 2

- name: Check test results
if: steps.playwright-tests.outcome == 'failure'
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ assets/js/
.env
tmp/
.phpunit.result.cache
test-results/
!tests/phpunit/hello-elementor
157 changes: 157 additions & 0 deletions tests/playwright/pages/settings-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { APIRequestContext, type Page, type TestInfo, expect } from '@playwright/test';
import WpAdminPage from './wp-admin-page.ts';

export default class SettingsPage extends WpAdminPage {
async gotoSettingsPage(): Promise<void> {
await this.page.goto( '/wp-admin/admin.php?page=hello-elementor-settings' );
await this.page.waitForSelector( 'h4:has-text("Advanced theme settings")', { timeout: 10000 } );
}

getTab( tabName: string ) {
return this.page.getByRole( 'tab', { name: tabName } );
}

getTabPanel( tabName: string ) {
return this.page.getByRole( 'tabpanel', { name: tabName } );
}

async createNewBasicPost(): Promise<string> {
const request: APIRequestContext = this.page.context().request,
postDataInitial = {
title: 'Playwright Test Page - Uninitialized',
content: 'This is a test content for the post.',
status: 'publish' as const,
excerpt: 'This is a test excerpt that should appear in meta description.',
},
postId = await this.apiRequests.create( request, 'pages', postDataInitial ),
postDataUpdated = {
title: `Playwright Test Page #${ postId }`,
};

await this.apiRequests.create( request, `pages/${ postId }`, postDataUpdated );

return postId;
}

async clickTab( tabName: string ): Promise<void> {
await this.getTab( tabName ).click();
await this.page.waitForSelector( `[role="tabpanel"]:visible`, { timeout: 5000 } );
}

getCheckboxByIndex( index: number ) {
return this.page.locator( 'input[type="checkbox"]' ).nth( index );
}

getCheckboxBySetting( settingName: string ) {
const settingMap: { [key: string]: number } = {
'Disable description meta tag': 0,
'Disable skip links': 1,
'Disable theme header and footer': 0,
'Hide page title': 1,
'Deregister Hello reset.css': 0,
'Deregister Hello theme.css': 1,
};
return this.page.locator( `input[type="checkbox"]` ).nth( settingMap[ settingName ] || 0 );
}

async toggleSetting( settingName: string ): Promise<boolean> {
const checkbox = this.getCheckboxBySetting( settingName );
await checkbox.click();
await this.waitForSaveNotification();
return await checkbox.isChecked();
}

async toggleCheckboxByIndex( index: number ): Promise<boolean> {
const checkbox = this.getCheckboxByIndex( index );
await checkbox.click();
await this.waitForSaveNotification();
return await checkbox.isChecked();
}

async waitForSaveNotification(): Promise<void> {
await expect( this.page.locator( '[role="alert"]:has-text("Settings Saved")' ).first() ).toBeVisible( { timeout: 5000 } );
}

async closeSaveNotification(): Promise<void> {
const notification = this.page.locator( '[role="alert"]:has-text("Settings Saved")' );
if ( await notification.isVisible() ) {
await notification.getByRole( 'button', { name: 'Close' } ).click();
}
}

async openChangelog(): Promise<void> {
await this.page.getByRole( 'link', { name: 'Changelog' } ).click();
await expect( this.page.locator( 'h4:has-text("Changelog")' ) ).toBeVisible( { timeout: 5000 } );
}

async closeChangelogWithEscape(): Promise<void> {
await this.page.keyboard.press( 'Escape' );
await expect( this.page.locator( '[role="dialog"]' ) ).not.toBeVisible( { timeout: 3000 } );
}

async closeChangelogByClickingOutside(): Promise<void> {
await this.page.locator( '[role="dialog"]' ).click( { position: { x: 5, y: 5 } } );
await expect( this.page.locator( '[role="dialog"]' ) ).not.toBeVisible( { timeout: 3000 } );
}

async getChangelogVersions(): Promise<string[]> {
const versionElements = this.page.locator( 'h6[class*="MuiTypography"]:regex("\\d+\\.\\d+\\.\\d+ - \\d{4}-\\d{2}-\\d{2}")' );
return await versionElements.allTextContents();
}

async hasWarningAlert(): Promise<boolean> {
return await this.page.locator( '[role="alert"]:has-text("Be Careful")' ).isVisible();
}

async getSettingDescription( settingName: string ): Promise<string> {
const descriptionElement = this.page.locator( `h6:has-text("${ settingName }") ~ * p:has-text("What it does:")` );
return await descriptionElement.textContent() || '';
}

async getSettingTip( settingName: string ): Promise<string> {
const tipElement = this.page.locator( `h6:has-text("${ settingName }") ~ * p:has-text("Tip:")` );
return await tipElement.textContent() || '';
}

async getSettingCode( settingName: string ): Promise<string> {
const codeElement = this.page.locator( `h6:has-text("${ settingName }") ~ * code` );
return await codeElement.textContent() || '';
}

async waitForPageLoad(): Promise<void> {
await this.page.waitForSelector( 'h4:has-text("Advanced theme settings")', { timeout: 10000 } );
await this.page.waitForSelector( '[role="tablist"]', { timeout: 5000 } );
await this.page.waitForSelector( '[role="tabpanel"]:visible', { timeout: 5000 } );
await this.page.waitForTimeout( 1000 );
}

async resetToDefaults(): Promise<void> {
const tabs = [
'SEO and accessibility',
'Structure and layout',
'CSS and styling control',
];

for ( const tabName of tabs ) {
await this.clickTab( tabName );

const checkboxes = this.page.locator( 'input[type="checkbox"]' );
const count = await checkboxes.count();

for ( let i = 0; i < count; i++ ) {
const checkbox = checkboxes.nth( i );
const isChecked = await checkbox.isChecked();

let shouldBeChecked = false;
if ( 'Structure and layout' === tabName && 1 === i ) {
shouldBeChecked = true;
}

if ( isChecked !== shouldBeChecked ) {
await checkbox.click();
await this.waitForSaveNotification();
}
}
}
}
}
60 changes: 60 additions & 0 deletions tests/playwright/tests/settings/description-meta-tag.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { parallelTest as test } from '../../parallelTest.ts';
import { expect } from '@playwright/test';
import SettingsPage from '../../pages/settings-page.ts';

test.describe.serial( 'Description Meta Tag Setting - Behavior Tests', () => {
let postId: string;

test.beforeEach( async ( { page, apiRequests }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );
postId = await settingsPage.createNewBasicPost();
await settingsPage.gotoSettingsPage();
await settingsPage.waitForPageLoad();
await settingsPage.clickTab( 'SEO and accessibility' );
} );

test( 'should not show meta description on page when the "Disable description meta tag" setting is enabled', async ( { page, apiRequests }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );
const metaCheckbox = settingsPage.getCheckboxBySetting( 'Disable description meta tag' );

const isChecked = await metaCheckbox.isChecked();
if ( ! isChecked ) {
await settingsPage.toggleSetting( 'Disable description meta tag' );
}

await page.goto( `/?p=${ postId }` );

const metaDescription = page.locator( 'meta[name="description"]' );
await expect( metaDescription ).not.toBeAttached();
} );

test( 'should show meta description on page when the "Disable description meta tag" setting is disabled', async ( { page, apiRequests }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );
const metaCheckbox = settingsPage.getCheckboxBySetting( 'Disable description meta tag' );

const isChecked = await metaCheckbox.isChecked();
if ( isChecked ) {
await settingsPage.toggleSetting( 'Disable description meta tag' );
}

await page.goto( `/?p=${ postId }` );

const metaDescription = page.locator( 'meta[name="description"]' );
await expect( metaDescription ).toBeAttached();
} );

test( 'should not show meta description on home page when the "Disable description meta tag" setting is enabled', async ( { page, apiRequests }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );
const metaCheckbox = settingsPage.getCheckboxBySetting( 'Disable description meta tag' );

const isChecked = await metaCheckbox.isChecked();
if ( ! isChecked ) {
await settingsPage.toggleSetting( 'Disable description meta tag' );
}

await page.goto( '/' );

const metaDescription = page.locator( 'meta[name="description"]' );
await expect( metaDescription ).not.toBeAttached();
} );
} );
48 changes: 48 additions & 0 deletions tests/playwright/tests/settings/header-footer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { parallelTest as test } from '../../parallelTest.ts';
import { expect } from '@playwright/test';
import SettingsPage from '../../pages/settings-page.ts';

test.describe.serial( 'Header and Footer Setting - Behavior Tests', () => {
test.beforeEach( async ( { page, apiRequests }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );
await settingsPage.gotoSettingsPage();
await settingsPage.waitForPageLoad();
await settingsPage.clickTab( 'Structure and layout' );
} );

test( 'should show header and footer by default when setting is disabled', async ( { page, apiRequests }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );
const headerFooterCheckbox = settingsPage.getCheckboxBySetting( 'Disable theme header and footer' );

const isChecked = await headerFooterCheckbox.isChecked();
if ( isChecked ) {
await settingsPage.toggleSetting( 'Disable theme header and footer' );
}

await page.goto( '/' );

const header = page.locator( 'header#site-header.site-header' );
const footer = page.locator( 'footer#site-footer.site-footer' );

await expect( header ).toBeAttached();
await expect( footer ).toBeAttached();
} );

test( 'should hide header and footer when setting is enabled', async ( { page, apiRequests }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );

const headerFooterCheckbox = settingsPage.getCheckboxBySetting( 'Disable theme header and footer' );
const isChecked = await headerFooterCheckbox.isChecked();
if ( ! isChecked ) {
await settingsPage.toggleSetting( 'Disable theme header and footer' );
}

await page.goto( '/' );

const header = page.locator( 'header#site-header.site-header' );
const footer = page.locator( 'footer#site-footer.site-footer' );

await expect( header ).not.toBeAttached();
await expect( footer ).not.toBeAttached();
} );
} );
49 changes: 49 additions & 0 deletions tests/playwright/tests/settings/page-title.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { parallelTest as test } from '../../parallelTest.ts';
import { expect } from '@playwright/test';
import SettingsPage from '../../pages/settings-page.ts';

test.describe.serial( 'Page Title Setting - Behavior Tests', () => {
let postId: string;

test.beforeEach( async ( { page, apiRequests }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );
postId = await settingsPage.createNewBasicPost();
await settingsPage.gotoSettingsPage();
await settingsPage.waitForPageLoad();
await settingsPage.clickTab( 'Structure and layout' );
} );

test( 'should hide page title when setting is checked', async ( { page, apiRequests, request }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );
const pageTitleCheckbox = settingsPage.getCheckboxBySetting( 'Hide page title' );

const isChecked = await pageTitleCheckbox.isChecked();
if ( ! isChecked ) {
await settingsPage.toggleSetting( 'Hide page title' );
}

// Act - Navigate to the test post
await page.goto( `/?p=${ postId }` );

// Assert - Page title should be hidden
const pageHeader = page.locator( '.page-header h1.entry-title' );
await expect( pageHeader ).not.toBeAttached();
} );

test( 'should show page title when setting is unchecked', async ( { page, apiRequests, request }, testInfo ) => {
const settingsPage = new SettingsPage( page, testInfo, apiRequests );
const pageTitleCheckbox = settingsPage.getCheckboxBySetting( 'Hide page title' );
const isChecked = await pageTitleCheckbox.isChecked();
if ( isChecked ) {
await settingsPage.toggleSetting( 'Hide page title' );
}

// Act - Navigate to the test post
await page.goto( `/?p=${ postId }` );

// Assert - Page title should be visible
const pageHeader = page.locator( '.page-header h1.entry-title' );
await expect( pageHeader ).toBeAttached();
await expect( pageHeader ).toHaveText( 'Playwright Test Page #' + postId );
} );
} );
Loading
Loading