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

Closes #129 Automate the scenario that check LL isnot applied on LCP/ATF images #144

Merged
merged 24 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
aceccd2
tmp
Miraeld Sep 18, 2024
73e6d87
Adds Imagify steps
Miraeld Sep 18, 2024
3ed8cef
Merge branch 'develop' into feature/129-ll-not-applied-lcp
Miraeld Sep 19, 2024
c588e74
Update steps
Miraeld Sep 19, 2024
221ea39
modify scenario steps
Khadreal Sep 20, 2024
dcbd67a
Add new cli step to delete plugin to avoid conflict :closes: #129
Khadreal Sep 20, 2024
349e318
Add new step, fixed clear cache timeout error
Khadreal Sep 20, 2024
511a359
Improve scenario one -- #129
Khadreal Sep 23, 2024
d6fa3e3
fix url issue
Khadreal Sep 23, 2024
38bbdec
:feat: Add steps for imagify template and enable new scenario #129
Khadreal Sep 23, 2024
5694b41
Split scenario into multiple scenarios
Khadreal Sep 23, 2024
e2f881d
PR modifications
Khadreal Sep 26, 2024
1218be4
Revert deleted backstop.json file
Khadreal Sep 30, 2024
e6fdb1c
Removed unused step definition
jeawhanlee Oct 2, 2024
8cd3ba0
Merge conde conflict
Khadreal Jan 3, 2025
479daee
Modify steps to include background images
Khadreal Jan 7, 2025
6656a3b
:sparkles: Make code anti-DRY
Khadreal Jan 7, 2025
1f5446e
Move function to helper file
Khadreal Jan 9, 2025
a24f6a2
Modify scenario name
Khadreal Jan 9, 2025
51576aa
Merge branch 'develop' into feature/129-ll-not-applied-lcp
Khadreal Jan 9, 2025
b884006
Clean up, fix error
Khadreal Jan 9, 2025
86e530d
Change test tag, add more step to lcp test
Khadreal Jan 9, 2025
2ac30d7
Remove redundant step and add Youtube lazyload option
Khadreal Jan 10, 2025
d21bfa4
Merge branch 'develop' into feature/129-ll-not-applied-lcp
Mai-Saad Jan 10, 2025
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
3 changes: 1 addition & 2 deletions backstop.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
"height": 1080
}
],
"scenarios": [
],
"scenarios": [],
"paths": {
"bitmaps_reference": "backstop_data/bitmaps_reference",
"bitmaps_test": "backstop_data/bitmaps_test",
Expand Down
19 changes: 17 additions & 2 deletions config/wp.config.sample.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ScenarioUrls from "./scenarioUrls.json";
/**
* The default WordPress admin user configuration for both local and live environments.
* @constant
Expand All @@ -15,6 +16,16 @@ const WP_ADMIN_USER = {

} as const;

/**
* The default Imagify settings information
*
* @constant
* @type {{ apiKey: string }}
*/
const IMAGIFY_INFOS = {
apiKey: ''
} as const;

/**
* Extracted environment variables related to WordPress configuration.
* Uses default values if environment variables are not set.
Expand Down Expand Up @@ -62,7 +73,9 @@ const {
* mobile?: boolean
* }
* }}
*/
*/
const scriptName = process.env.npm_lifecycle_event;
const SCENARIO_URLS = ScenarioUrls[scriptName];

/**
* Exported WordPress environment configuration.
Expand Down Expand Up @@ -102,5 +115,7 @@ export {
WP_SSH_USERNAME,
WP_SSH_ADDRESS,
WP_SSH_KEY,
WP_SSH_ROOT_DIR
WP_SSH_ROOT_DIR,
SCENARIO_URLS,
IMAGIFY_INFOS
};
46 changes: 46 additions & 0 deletions src/features/ll-lcp.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@lcp @delaylcp @setup
Feature: Lazyload with LCP

Background:
Given I am logged in
And plugin is installed 'new_release'
And plugin 'wp-rocket' is activated
When I go to 'wp-admin/options-general.php?page=wprocket#dashboard'
And I save settings 'media' 'lazyloadCssBgImg'
Khadreal marked this conversation as resolved.
Show resolved Hide resolved
And I save settings 'media' 'lazyload'
And I save settings 'media' 'lazyloadIframes'
And I save settings 'media' 'lazyloadYoutube'

Scenario: Should Exclude LCP/ATF from Lazyload
When I log out
And I visit the urls for 'desktop'
When I am logged in
And I clear cache
And I log out
And I visit the urls and check for lazyload
Then lcp and atf images are not written to LL format

Scenario: Should exclude next-gen lcp/atf from LL
Given I install plugin 'imagify'
And plugin 'imagify' is activated
When I am logged in
And Imagify is set up
When I log out
And I visit page 'lcp_with_imagify' and check for lcp
When I am logged in
And I clear cache
And I log out
And I visit the 'lcp_with_imagify' and check lcp-atf are not lazyloaded
Then lcp and atf images are not written to LL format

Scenario: Should exclude Imagify next-gen lcp/atf from LL
When I am logged in
And display next-gen is enabled on imagify
When I log out
And I visit page 'lcp_with_imagify' and check for lcp
When I am logged in
And I clear cache
And I log out
And I visit the 'lcp_with_imagify' and check lcp-atf are not lazyloaded
Then lcp and atf images are not written to LL format

2 changes: 2 additions & 0 deletions src/support/steps/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,10 @@ When('I clear cache', async function (this:ICustomWorld) {
await this.utils.gotoWpr();

this.sections.set('dashboard');

const cacheButton = this.page.locator('p:has-text("This action will clear") + a').first();
await cacheButton.click();

await expect(this.page.getByText('WP Rocket: Cache cleared.')).toBeVisible();
});

Expand Down
31 changes: 31 additions & 0 deletions src/support/steps/imagify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ICustomWorld } from "../../common/custom-world";

import { Given } from '@cucumber/cucumber';
import { IMAGIFY_INFOS } from "../../../config/wp.config";
import {expect} from "@playwright/test";

Given('Imagify is set up', async function (this: ICustomWorld) {
await this.utils.gotoImagify();

// Check if the API key input field exists on the page
const apiKeyInput = await this.page.$('input#api_key');

if (apiKeyInput) {
// Fill the API key input field with the API key from the config
await this.page.fill('input#api_key', IMAGIFY_INFOS.apiKey);
// Click the submit button to save the changes
await this.page.click('div.submit.imagify-clearfix input#submit');
}
});
Given('display next-gen is enabled on imagify', async function (this: ICustomWorld) {
// Go to Imagify setting page
await this.utils.gotoImagify();

// Check the 'Display images in Next-Gen format on the site' checkbox
await this.page.click('label[for="imagify_display_nextgen"]');

// Click the submit button to save the changes
await this.page.click('input#submit');

await expect(this.page.getByText('Settings saved.')).toBeVisible();
});
199 changes: 186 additions & 13 deletions src/support/steps/lcp-beacon-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,82 @@
* @requires {@link @playwright/test}
* @requires {@link @cucumber/cucumber}
*/
import { ICustomWorld } from "../../common/custom-world";
import { expect } from "@playwright/test";
import { When, Then } from "@cucumber/cucumber";
import { LcpData, Row } from "../../../utils/types";

import { dbQuery, getWPTablePrefix } from "../../../utils/commands";
import { extractFromStdout } from "../../../utils/helpers";
import { WP_BASE_URL } from '../../../config/wp.config';
import {ICustomWorld} from "../../common/custom-world";
import {expect} from "@playwright/test";
import {Then, When} from "@cucumber/cucumber";
import {LcpData, LLImagesData, Row, SinglePageLCPImages} from "../../../utils/types";

import {dbQuery, getWPTablePrefix} from "../../../utils/commands";
import {checkLcpOrViewport, extractFromStdout} from "../../../utils/helpers";
import {WP_BASE_URL} from '../../../config/wp.config';
import fs from 'fs/promises';

let data: string,
truthy: boolean = true,
failMsg: string,
jsonData: Record<string, { lcp: string[]; viewport: string[]; enabled: boolean, comment: string; }>,
isDbResultAvailable: boolean = true;
isDbResultAvailable: boolean = true,
lcpLLImages: LLImagesData = {},
singlePageLcp : SinglePageLCPImages = {url: '', lcp: '', viewport: ''};

const actual: LcpData = {};

/**
* Executes step to visit page based on the templates and get check for lazyload.
*/
When('I visit the urls and check for lazyload', async function (this: ICustomWorld) {
const resultFile: string = './src/support/results/expectedResultsDesktop.json';

await this.page.setViewportSize({
width: 1600,
height: 700
});

data = await fs.readFile(resultFile, 'utf8');
jsonData = JSON.parse(data);

// Visit page.
for (const key in jsonData) {
if ( jsonData[key].enabled === true ) {
// Visit the page url.
await this.utils.visitPage(key);

lcpLLImages = await this.page.evaluate((url) => {
const images = document.querySelectorAll('img'),
result = {},
allElements = document.querySelectorAll('*');

Array.from(images).forEach((img) => {
result[`${url}_img`] = {
src: img.getAttribute('src'),
type: 'image',
url: url,
lazyloaded: img.classList.contains('lazyloaded')
}
});

Array.from(allElements).forEach((element) => {
const computedStyle = window.getComputedStyle(element);
const backgroundImage = computedStyle.backgroundImage;

if (backgroundImage && backgroundImage !== 'none') {
const bgUrl = backgroundImage.replace(/^url\(['"]?/, '').replace(/['"]?\)$/, '');

result[`${url}_bg`] = {
type: 'background',
src: bgUrl,
url: url,
lazyloaded: element.classList.contains('data-rocket-lazy-bg'),
};
}
})


return result;
}, key);
}
}
});
/**
* Executes step to visit page based on the form factor(desktop/mobile) and get the LCP/ATF data from DB.
*/
Expand Down Expand Up @@ -71,7 +129,7 @@ When('I visit the urls for {string}', async function (this: ICustomWorld, formFa
await this.page.waitForFunction(() => {
const beacon = document.querySelector('[data-name="wpr-wpr-beacon"]');
return beacon && beacon.getAttribute('beacon-completed') === 'true';
}, { timeout: 900000 });
}, { timeout: 100000 });

if (formFactor !== 'desktop') {
isMobile = 1;
Expand Down Expand Up @@ -152,22 +210,137 @@ Then('lcp and atf should be as expected for {string}', async function (this: ICu
expect(truthy).toBeTruthy();
});

let lcpImages: Array<{ src: string; fetchpriority: string | boolean; lazyloaded: string | boolean }> = [];

Then('lcp image should have fetchpriority', async function (this: ICustomWorld) {
truthy= false;

const imageWithFetchPriority = await this.page.evaluate(() => {
lcpImages = await this.page.evaluate(() => {
const images = document.querySelectorAll('img');
return Array.from(images).map(img => ({
src: img.getAttribute('src'),
fetchpriority: img.getAttribute('fetchpriority') || false
fetchpriority: img.getAttribute('fetchpriority') || false,
lazyloaded: img.classList.contains('lazyloaded')
}));
});

for (const image of imageWithFetchPriority) {
for (const image of lcpImages) {
if(image.src === '/wp-content/rocket-test-data/images/600px-Mapang-test.gif' && image.fetchpriority !== false) {
truthy = true
}
}

expect(truthy).toBeTruthy();
});

/**
* Executes the step to assert that LCP & ATF aren't lazyloaded.
*
* @returns {Promise<void>}
*/
Then('lcp and atf images are not written to LL format', async function (this: ICustomWorld) {
// Reset truthy to true here.
truthy = true;

// Iterate over the data
for (const key in jsonData) {
if (Object.hasOwnProperty.call(jsonData, key) && jsonData[key].enabled === true) {
const expected = jsonData[key];

const lcpResult = await checkLcpOrViewport(lcpLLImages, key, 'LCP', expected.lcp);
if (lcpResult && !lcpResult.isValid) {
truthy = false;
failMsg += lcpResult.errorMessages.join('');
}

const viewportResult = await checkLcpOrViewport(lcpLLImages, key, 'Viewport', expected.viewport);
if (viewportResult && !viewportResult.isValid) {
truthy = false;
failMsg += viewportResult.errorMessages.join('');
}
}
}

// Fail test when there is expectation mismatch.
expect(truthy).toBeTruthy();
});

When('I visit the {string} and check lcp-atf are not lazyloaded', async function (this: ICustomWorld, url: string) {
// Reset truthy to true here.
truthy = true;

await this.page.setViewportSize({
width: 1600,
height: 700
});

await this.utils.visitPage(url);

const allImages = await this.page.evaluate((url) => {
const images = document.querySelectorAll('img'),
result = [];

Array.from(images).forEach((img) => {
result.push({
src: img.getAttribute('src'),
url: url,
lazyloaded: img.classList.contains('lazyloaded')
})
});

return result;
}, url);

allImages.forEach((image) => {
if(singlePageLcp.lcp.includes(image.src) && image.lazyloaded ) {
truthy = false;
}

if(singlePageLcp.viewport.includes(image.src) && image.lazyloaded ) {
truthy = false;
}
});

// Fail test when there is expectation mismatch.
expect(truthy).toBeTruthy();
});

/**
* Executes the step to visit page in a specific browser dimension.
*/
When('I visit page {string} and check for lcp', async function (this:ICustomWorld, page) {

const tablePrefix: string = await getWPTablePrefix();

await this.page.setViewportSize({
width: 1600,
height: 700,
});

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';
}, { timeout: 100000 });

// Get the LCP/ATF from the DB
const sql = `SELECT lcp, viewport
FROM ${tablePrefix}wpr_above_the_fold
WHERE url LIKE "%${page}%"
AND is_mobile = 0`;
const result = await dbQuery(sql);
const resultFromStdout = await extractFromStdout(result);

// If no DB result, set assertion var to false, fail msg and skip the loop.
if (!resultFromStdout || resultFromStdout.length === 0) {
isDbResultAvailable = false;
}

singlePageLcp = {
url: page,
lcp: resultFromStdout[0].lcp,
viewport: resultFromStdout[0].viewport
}
});
Loading
Loading