Skip to content

Commit

Permalink
Merge pull request #144 from wp-media/feature/129-ll-not-applied-lcp
Browse files Browse the repository at this point in the history
Closes #129 Automate the scenario that check LL isnot applied on LCP/ATF images
  • Loading branch information
Mai-Saad authored Jan 10, 2025
2 parents 43da580 + d21bfa4 commit 7c06faa
Show file tree
Hide file tree
Showing 10 changed files with 362 additions and 19 deletions.
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'
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

0 comments on commit 7c06faa

Please sign in to comment.