Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added function for changing permalink. created performance-hints file… #136

Merged
merged 24 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
02e3ab9
added function for changing permalink. created performance-hints file…
hanna-meda Aug 23, 2024
4868e94
changed 'assert' with 'expect' after review. added @hints tag under p…
hanna-meda Aug 26, 2024
0d0256c
added 4 more scenarios with corresponding steps. refactored some parts
hanna-meda Sep 5, 2024
ebffb21
added one more final step on 3rd scenario. made changes based on late…
hanna-meda Sep 12, 2024
4878d82
improved method that inserts hardcoded data in DB
hanna-meda Sep 13, 2024
1b5656b
Updated step definitions and fixed lints
jeawhanlee Sep 18, 2024
bfc1b98
Added new general step definitions
jeawhanlee Sep 18, 2024
fb1bb62
Added new wp cli command support
jeawhanlee Sep 18, 2024
8b5807e
Updated the way we log out and use wp cli for wp rocket cleanup with …
jeawhanlee Sep 18, 2024
4553c1b
Introduced new helper plugin for disabling saas visit and changed som…
jeawhanlee Sep 18, 2024
28680b5
Remove helper plugin dependency
jeawhanlee Sep 18, 2024
f312c17
Check for seeded data existence
jeawhanlee Sep 18, 2024
ea5e0ea
Add new wp cli commands to get post data by title and update post status
jeawhanlee Sep 19, 2024
5d267bd
Bypass UI and use new commands
jeawhanlee Sep 19, 2024
70267a3
Updated step
jeawhanlee Sep 19, 2024
ff22ddb
Wait for page load to complete
jeawhanlee Sep 19, 2024
e115505
Added new helper to seed data in DB
jeawhanlee Sep 19, 2024
e9fb1ac
Used new helper and removed function with duplicate functionality
jeawhanlee Sep 19, 2024
55f01f5
Merge branch 'develop' into performance-hints
jeawhanlee Sep 19, 2024
ec77a58
Updated helper function to seed data and added new helper to check da…
jeawhanlee Sep 20, 2024
9aa0043
Removed logs from command
jeawhanlee Sep 20, 2024
30286fd
Updated to use new helpers
jeawhanlee Sep 20, 2024
972437a
Removed unused module
jeawhanlee Sep 20, 2024
a857ff4
Removed waitForLoadState function
jeawhanlee Sep 20, 2024
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"test:llimages": "$npm_package_config_testCommand --tags @llimages",
"test:lcp": "$npm_package_config_testCommand --tags @lcp",
"test:test": "$npm_package_config_testCommand --tags @test",
"test:performancehints": "$npm_package_config_testCommand --tags @performancehints",
"healthcheck": "ts-node healthcheck.ts",
"wp-env": "wp-env"
},
Expand Down
53 changes: 53 additions & 0 deletions src/features/performance-hints.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@setup @performancehints
Feature: Clear lcp/performance hints data tests

Background:
Given I am logged in
And plugin is installed 'new_release'
And plugin is activated

Scenario: C16387 - Should clear performance hints data when click clear PH in admin bar
Given performance hints data added to DB
When clear performance hints is clicked in admin bar
Then data is removed from the performance hints tables

Scenario: C16389 - Should clear performance hints when change permalinks
Given performance hints data added to DB
When permalink structure is changed to '/%postname%'
Then data is removed from the performance hints tables

Scenario: C16390 - Should clear performance hints when switch theme
Given performance hints data added to DB
And switching the theme
Then data is removed from the performance hints tables
Then theme 'Twenty Twenty' is activated

Scenario: Should clear performance hints of the current URL
Given I log out
And I visit beacon driven page 'atf-lrc-1' with browser dimension 1600 x 700
And I visit beacon driven page 'atf-lrc-2' with browser dimension 1600 x 700
And data for 'atf-lrc-1' present in the performance hints tables
And data for 'atf-lrc-2' present in the performance hints tables
And I am logged in
And I go to 'atf-lrc-1'
When clear performance hints for this URL is clicked in admin bar
Then data for 'atf-lrc-1' is removed from the performance hints tables
Then data for 'atf-lrc-2' present in the performance hints tables

Scenario: C16388 - Should clear performance hints of the URL when edited
Given I log out
And I visit beacon driven page 'atf-lrc-1' with browser dimension 1600 x 700
And data for 'atf-lrc-1' present in the performance hints tables
And I am logged in
And I go to 'atf-lrc-1'
When I edit the content of post
Then data for 'atf-lrc-1' is removed from the performance hints tables

Scenario: C16388 - Should clear performance hints of the URL when deleted
Given I log out
And I visit beacon driven page 'atf-lrc-1' with browser dimension 1600 x 700
And data for 'atf-lrc-1' present in the performance hints tables
And I am logged in
When 'atf-lrc-1' page is deleted
Then data for 'atf-lrc-1' is removed from the performance hints tables
Then untrash and republish 'atf-lrc-1' page
25 changes: 25 additions & 0 deletions src/support/steps/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,38 @@ When('I visit page {string} with browser dimension {int} x {int}', async functio
await this.utils.visitPage(page);
});

/**
* Executes the step to visit beacon driven page in a specific browser dimension.
*/
When('I visit beacon driven page {string} with browser dimension {int} x {int}', async function (this:ICustomWorld, page, width, height) {
await this.page.setViewportSize({
width: width,
height: height,
});

await this.utils.visitPage(page);

// Wait the beacon to add an attribute `beacon-complete` to true before fetching from DB.
await this.page.waitForFunction(() => {
const beacon = document.querySelector('[data-name="wpr-wpr-beacon"]');
return beacon && beacon.getAttribute('beacon-completed') === 'true';
});
});

/**
* Executes the step to scroll to the bottom of the page.
*/
When('I scroll to bottom of page', async function (this:ICustomWorld) {
await this.utils.scrollDownBottomOfAPage();
});

/**
* Executes the step to change permalink structure.
*/
When('permalink structure is changed to {string}', async function (this: ICustomWorld, structure: string) {
await this.utils.permalinkChanged(structure);
});

/**
* Executes the step to assert the presence of specific text.
*/
Expand Down
219 changes: 219 additions & 0 deletions src/support/steps/performance-hints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/**
* @fileoverview
* This module contains Cucumber step definitions using Playwright for asserting performance hints data clearing from DB,
* both ATF and LRC tables
*
* @requires {@link ../../common/custom-world}
* @requires {@link @playwright/test}
* @requires {@link @cucumber/cucumber}
*/

import { ICustomWorld } from "../../common/custom-world";
import { WP_BASE_URL } from '../../../config/wp.config';
import { When, Then, Given } from '@cucumber/cucumber';
import { dbQuery, getWPTablePrefix, getPostDataFromTitle, updatePostStatus } from "../../../utils/commands";
import { extractFromStdout, seedData } from "../../../utils/helpers";
import { expect } from "@playwright/test";

/*
* Executes step to add hardcoded data to DB: ATF & LRC tables
*/
Given('performance hints data added to DB', async function (this: ICustomWorld) {
const tablePrefix = await getWPTablePrefix();
jeawhanlee marked this conversation as resolved.
Show resolved Hide resolved
const tableNames = [
`${tablePrefix}wpr_above_the_fold`,
`${tablePrefix}wpr_lazy_render_content`
];

// Define the data to be inserted
const data = [
// eslint-disable-next-line @typescript-eslint/naming-convention
{ url: `${WP_BASE_URL}/a`, is_mobile: 0, status: 'completed' },
// eslint-disable-next-line @typescript-eslint/naming-convention
{ url: `${WP_BASE_URL}/b`, is_mobile: 0, status: 'completed' },
// eslint-disable-next-line @typescript-eslint/naming-convention
{ url: `${WP_BASE_URL}/c`, is_mobile: 0, status: 'completed' }
];

await Promise.all(tableNames.map(async (tableName) => {
await dbQuery(`TRUNCATE TABLE ${tableName}`);
}));

await seedData(tableNames, data);

const selectSql = `SELECT url, is_mobile, status FROM %s`;

// Function to insert data and fetch filtered results
// eslint-disable-next-line @typescript-eslint/naming-convention
const processTable = async (tableName: string): Promise<Array<{ url: string; is_mobile: number; status: string }>> => {
// Fetch the data from the table
const selectQuery = selectSql.replace('%s', tableName);
const resultString = await dbQuery(selectQuery);

// Convert the result
const result = await extractFromStdout(resultString);

// Filter out the rows that match the hardcoded data
const filteredResult = data.filter(hardcodedRow =>
result.some(dbRow =>
dbRow.url === hardcodedRow.url &&
Number(dbRow.is_mobile) === hardcodedRow.is_mobile &&
dbRow.status === hardcodedRow.status
)
);

// Check if the filtered result contains all the hardcoded data
expect(filteredResult.length).toBe(data.length); // Ensure all hardcoded data is found

return filteredResult;
};

// Check that data exists in tables.
await Promise.all(tableNames.map(processTable));
});

When('clear performance hints is clicked in admin bar', async function (this: ICustomWorld) {
await this.page.locator('#wp-admin-bar-wp-rocket').hover();
await this.page.waitForSelector('#wp-admin-bar-clear-performance-hints', { state: 'visible' });
await this.page.locator('#wp-admin-bar-clear-performance-hints').click();
await this.page.waitForSelector('text=WP Rocket: Critical images and Lazy Render data was cleared!', { state: 'visible' });
});

When('clear performance hints for this URL is clicked in admin bar', async function (this: ICustomWorld) {
await this.page.locator('#wp-admin-bar-wp-rocket').hover();
await this.page.waitForSelector('#wp-admin-bar-clear-performance-hints-data-url', { state: 'visible' });
await this.page.locator('#wp-admin-bar-clear-performance-hints-data-url').click();
await this.page.waitForLoadState('load', { timeout: 30000 });
Mai-Saad marked this conversation as resolved.
Show resolved Hide resolved
});

/*
* Executes the step to check all data has been cleared from ATF & LRC tables
* (home URL ignored as its the quickest to re-appear on prewarmup)
*/
Then('data is removed from the performance hints tables', async function (this: ICustomWorld) {
const tablePrefix = await getWPTablePrefix();
const tables = [`${tablePrefix}wpr_above_the_fold`, `${tablePrefix}wpr_lazy_render_content`];

// Helper function to check if data is removed from a table
const verifyTableIsEmpty = async (tableName: string): Promise<void> => {
// Construct SQL query to select all rows
const selectSql = `SELECT * FROM ${tableName}`;
const result = await dbQuery(selectSql);
const resultFromStdout = await extractFromStdout(result);

// URLs to check
const urlsToDelete = [
`${WP_BASE_URL}/a`,
`${WP_BASE_URL}/b`,
`${WP_BASE_URL}/c`
];

// Filter out any URL that exists in the dataset and adds it to the constant.
const missingUrls = urlsToDelete.filter(url => {
return !resultFromStdout.some(item => item.url === url);
});

expect(urlsToDelete.length).toBe(missingUrls.length);
};

// Verify both tables
for (const table of tables) {
await verifyTableIsEmpty(table);
}

});

/*
* Executes the step to check data has been cleared from ATF & LRC tables for specific URL
*/
Then('data for {string} is removed from the performance hints tables', async function (this: ICustomWorld, permalink: string) {
const tablePrefix = await getWPTablePrefix();
const tables = [`${tablePrefix}wpr_above_the_fold`, `${tablePrefix}wpr_lazy_render_content`];

// Helper function to check if the permalink is removed from a table
const verifyPermalinkRemoved = async (tableName: string): Promise<void> => {
// Construct SQL query to check if the permalink exists in the table
const selectSql = `SELECT * FROM ${tableName} WHERE url LIKE "%${permalink}%"`;
const result = await dbQuery(selectSql);
const resultFromStdout = await extractFromStdout(result);

// Check if any rows are returned that match the permalink
expect(resultFromStdout.length).toBe(0);
};

// Verify both tables
for (const table of tables) {
await verifyPermalinkRemoved(table);
}
});

/*
* Executes the step to check data still exists in the ATF & LRC tables for specific URL
*/
Then('data for {string} present in the performance hints tables', async function (this: ICustomWorld, permalink: string) {
const tablePrefix = await getWPTablePrefix();
const tables = [`${tablePrefix}wpr_above_the_fold`, `${tablePrefix}wpr_lazy_render_content`];

// Helper function to check if the permalink still exists in a table
const verifyPermalinkExists = async (tableName: string): Promise<void> => {
// Construct SQL query to check if the permalink exists in the table
const selectSql = `SELECT * FROM ${tableName} WHERE url LIKE "%${permalink}%"`;
const result = await dbQuery(selectSql);
const resultFromStdout = await extractFromStdout(result);

// Check if any rows are returned that match the permalink
expect(resultFromStdout.length).toBeGreaterThan(0); // Fail test if permalink is not found
};

// Verify both tables
for (const table of tables) {
await verifyPermalinkExists(table);
}
});

When('switching the theme', async function (this: ICustomWorld) {
await this.utils.gotoThemes();
await this.page.locator('#wpbody-content > div.wrap > div.theme-browser.rendered > div > div:nth-child(2) > div.theme-id-container').hover();
await this.page.waitForSelector('#wpbody-content > div.wrap > div.theme-browser.rendered > div > div:nth-child(2) > div.theme-id-container > div > a.button.activate', { state: 'visible' });
await this.page.locator('#wpbody-content > div.wrap > div.theme-browser.rendered > div > div:nth-child(2) > div.theme-id-container > div > a.button.activate').click();
});

When ('I edit the content of post', async function (this: ICustomWorld) {
await this.page.waitForSelector('#wp-admin-bar-edit', { state: 'visible' });
await this.page.locator('#wp-admin-bar-edit').click();

// Check for 'Update' button.
const updateButton = this.page.getByRole('button', { name: 'Update', exact: true });

try {
// Wait for the 'Update' button.
await updateButton.waitFor({ state: 'visible' });
await updateButton.click();
} catch (error) {
// If 'Update' is not found, check for 'Save' button for WP version >= 6.6.2.
const saveButton = this.page.getByRole('button', { name: 'Save', exact: true });

// Wait for the 'Save' button.
await saveButton.waitFor({ state: 'visible' });
await saveButton.click();
}

await this.page.waitForSelector('[aria-label="Dismiss this notice"]', { state: 'visible' });
});

When ('{string} page is deleted', async function (this: ICustomWorld, permalink: string) {
await this.utils.gotoPages();
await this.page.locator('#post-search-input').fill(permalink);
await this.page.locator('#search-submit').click();
await this.page.locator('td.title.column-title.has-row-actions.column-primary.page-title > strong > a').hover();
await this.page.waitForSelector('div.row-actions > span.trash > a', { state: 'visible' });
await this.page.locator('div.row-actions > span.trash > a').click();
await this.page.waitForSelector('#message', { state: 'visible' });
});


Then ('untrash and republish {string} page', async function (this: ICustomWorld, permalink: string) {
const postDataStdout = await getPostDataFromTitle(permalink, 'trash', 'ID,post_title');
const postData = await extractFromStdout(postDataStdout);
await updatePostStatus(parseInt(postData[0].ID, 10), 'publish');
});
52 changes: 50 additions & 2 deletions utils/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,27 @@ export async function installRemotePlugin(url: string): Promise<void> {
* @returns {Promise<void>} - A Promise that resolves when the uninstallation is completed.
*/
export async function uninstallPlugin(plugin: string): Promise<void> {
if(await isPluginInstalled(plugin)) {
await wp(`plugin uninstall --deactivate ${plugin}`);
const plugins = plugin.split(' ');
for (const p of plugins) {
if (await isPluginInstalled(p)) {
await wp(`plugin uninstall --deactivate ${p}`);
}
}
}

/**
* Update Permalink.
*
* @function
* @name updatePermalinkStructure
* @async
* @param {string} structure - The permalink structure.
* @returns {Promise<void>} - A Promise that resolves when the permalink structure is updated.
*/
export async function updatePermalinkStructure(structure: string): Promise<void> {
await wp(`option update permalink_structure ${structure}`);
}

/**
* Executes a SQL query on the WordPress database using WP-CLI.
*
Expand Down Expand Up @@ -439,4 +455,36 @@ export async function testSshConnection(): Promise<string> {
}
}

/**
* Performs a post search action by title using wp cli.
*
* @param {string} title Post Title.
* @param {string} status Post Status.
* @param {string} fields Post fields to return.
* @return {Promise<string>} A Promise that resolves when the post search is executed.
*/
export async function getPostDataFromTitle(title: string, status: string, fields: string): Promise<string> {
const command = wrapSSHPrefix(`wp post list --post_status=${status} --post_type=page --fields=${fields} --title='${title}'
`);
console.log(command);
const result = exec(command, { silent: true });

if (result.code === 1) {
return '';
}

return result.stdout;
}

/**
* Updates post status using wp cli.
*
* @param {string} id Post ID.
* @param {string} status Post Status.
* @return {Promise<void>} A Promise that resolves when the post search is executed.
*/
export async function updatePostStatus(id: number, status: string): Promise<void> {
await wp(`post update ${id} --post_status=${status}`);
}

export default wp;
Loading
Loading