Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions src/features/enable-all-features.feature
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@smoke @local @setup
@smoke @local @setup
Comment thread
hanna-meda marked this conversation as resolved.
Outdated
Feature: C1205 - Enabling all WP Rocket features should not throw any fatal errors

Background:
Expand All @@ -8,7 +8,7 @@ Feature: C1205 - Enabling all WP Rocket features should not throw any fatal erro

Scenario: Enable all features
When I go to 'wp-admin/options-general.php?page=wprocket#dashboard'
And I enable all settings
And I enable all settings with text inputs
Comment thread
hanna-meda marked this conversation as resolved.
Outdated
And I log out
Then page loads successfully
When I log in
Expand Down
9 changes: 8 additions & 1 deletion src/support/steps/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,13 @@ When('I enable all settings', async function (this: ICustomWorld) {
await this.utils.enableAllOptions();
});

/**
* Executes the step to enable all settings and validates text inputs.
*/
When('I enable all settings with text inputs', async function (this: ICustomWorld) {
await this.utils.enableAllOptionsWithTextInputs();
});

/**
* Executes the step to log out.
*/
Expand Down Expand Up @@ -514,4 +521,4 @@ Then('page navigated to the new page {string}', async function (this: ICustomWor
const url = `${WP_BASE_URL}/${path}`;
const regex = new RegExp(url);
await expect(this.page).toHaveURL(regex);
});
});
150 changes: 147 additions & 3 deletions utils/page-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
* @requires {@link ../config/wp.config}
* @requires {@link ./configurations}
*/
import type {Page} from '@playwright/test';
import type { Dialog, Page } from '@playwright/test';
import type {Sections} from '../src/common/sections';
import type {Locators, Selector, Pickle} from './types';
import type {Locators, Selector, Pickle, SectionId} from './types';
import {expect} from "@playwright/test";
import { ICustomWorld } from '../src/common/custom-world';
import fs from "fs/promises";
Expand Down Expand Up @@ -567,6 +567,150 @@ export class PageUtils {

}

/**
* Enables all options and ensures every visible text input accepts arbitrary values without native validation dialogs.
*
* @return {Promise<void>}
*/
public enableAllOptionsWithTextInputs = async (): Promise<void> => {
await this.enableAllOptions();
await this.validateTextInputsWithoutDialogs();
}

/**
* Iterates over every visible text field in each section, types a test value, presses Enter,
* and fails if a browser dialog appears (e.g. "Please enter a valid URL").
*
* The original field values are restored after each check, and form submissions are suppressed to avoid unwanted saves.
*
* @param {string} testValue - Value to use while validating the inputs.
*
* @return {Promise<void>}
*/
private validateTextInputsWithoutDialogs = async (testValue: string = 'test'): Promise<void> => {
const sectionIds: SectionId[] = [
'dashboard',
'cache',
'file_optimization',
'media',
'preload',
'advanced_cache',
'database',
'page_cdn',
'heartbeat',
'addons',
];
const allowedInputTypes = new Set(['', 'text', 'search', 'url', 'email']);
const dialogMessages: string[] = [];
let activeField = '';

await this.gotoWpr();
await this.suppressSettingsFormSubmit();
Comment thread
hanna-meda marked this conversation as resolved.
Outdated

const handleDialog = async (dialog: Dialog): Promise<void> => {
dialogMessages.push(`${dialog.message()}${activeField ? ` (Field: ${activeField})` : ''}`);
await dialog.dismiss();
};

this.page.on('dialog', handleDialog);

try {
for (const sectionId of sectionIds) {
const navLocator = this.page.locator(`#wpr-nav-${sectionId}`);
if (!await navLocator.isVisible()) {
continue;
}

await navLocator.scrollIntoViewIfNeeded();
await navLocator.click();
await this.page.locator(`#${sectionId}`).waitFor({ state: 'visible' });

const textInputs = this.page
.locator(`#${sectionId}`)
.locator('textarea, input[type="text"], input[type="search"], input[type="url"], input:not([type])');
Comment thread
hanna-meda marked this conversation as resolved.

const fieldCount = await textInputs.count();

for (let index = 0; index < fieldCount; index++) {
const field = textInputs.nth(index);

if (!await field.isVisible() || await field.isDisabled() || !await field.isEditable()) {
continue;
}

const tagName = await field.evaluate((el) => el.tagName.toLowerCase());
if (tagName === 'input') {
const typeAttr = (await field.getAttribute('type'))?.toLowerCase() ?? '';
if (!allowedInputTypes.has(typeAttr)) {
continue;
}
}

const fieldId = await field.getAttribute('id');
const fieldName = await field.getAttribute('name');
activeField = `${sectionId}:${fieldId ?? fieldName ?? `field-${index}`}`;

const originalValue = await field.inputValue();

await field.fill(testValue);
await field.press('Enter', { noWaitAfter: true });
await this.page.waitForTimeout(100);
await field.fill(originalValue);
}
}
} finally {
this.page.off('dialog', handleDialog);
activeField = '';
await this.resumeSettingsFormSubmit();
Comment thread
hanna-meda marked this conversation as resolved.
Outdated
}

if (dialogMessages.length > 0) {
throw new Error(`Unexpected validation dialog(s) triggered while interacting with text inputs: ${dialogMessages.join(' | ')}`);
}
Comment thread
jeawhanlee marked this conversation as resolved.
Outdated
}

/**
* Prevents the WP Rocket settings form from submitting while we simulate Enter presses on inputs.
*
* @return {Promise<void>}
*/
private suppressSettingsFormSubmit = async (): Promise<void> => {
Comment thread
hanna-meda marked this conversation as resolved.
Outdated
await this.page.waitForSelector('form[id$="_options"]');
await this.page.evaluate(() => {
const form = document.querySelector('form[id$="_options"]');
const win = window as typeof window & { wprSubmitInterceptor?: (event: Event) => void };

if (!form || win.wprSubmitInterceptor) {
return;
}

win.wprSubmitInterceptor = (event: Event): void => {
event.preventDefault();
};

form.addEventListener('submit', win.wprSubmitInterceptor, true);
});
}

/**
* Restores the default submit behavior for the WP Rocket settings form.
*
* @return {Promise<void>}
*/
private resumeSettingsFormSubmit = async (): Promise<void> => {
Comment thread
hanna-meda marked this conversation as resolved.
Outdated
await this.page.evaluate(() => {
const form = document.querySelector('form[id$="_options"]');
const win = window as typeof window & { wprSubmitInterceptor?: (event: Event) => void };

if (!form || !win.wprSubmitInterceptor) {
return;
}

form.removeEventListener('submit', win.wprSubmitInterceptor, true);
delete win.wprSubmitInterceptor;
});
}

/**
* Performs setting import action in WP Rocket.
*
Expand Down Expand Up @@ -794,4 +938,4 @@ export class PageUtils {
await scrollPage;
});
}
}
}
Loading