Skip to content

Commit

Permalink
feat: visual polling
Browse files Browse the repository at this point in the history
  • Loading branch information
etowahadams committed Nov 2, 2023
1 parent 7210083 commit d8920b8
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 8 deletions.
98 changes: 90 additions & 8 deletions e2e/perf.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { test, expect, type Page } from '@playwright/test';

import { test, expect, type Page, Locator } from '@playwright/test';
import { PNG } from 'pngjs';
import pixelmatch from 'pixelmatch';
import * as fs from 'fs';

const jsonString = fs.readFileSync('./e2e/spec.json', 'utf-8');
Expand All @@ -10,17 +11,98 @@ function delay(time: number) {
});
}

test('changes editor spec', async ({ page }) => {
await page.goto('/');
/**
* Compares two PNG files and returns true if they are the same.
*/
function isPngSame(newImg: Buffer, oldImgPath: string) {
const img1 = PNG.sync.read(newImg);
const img2 = PNG.sync.read(fs.readFileSync(oldImgPath));
// check if the images have the same dimensions
if (img1.width !== img2.width || img1.height !== img2.height) return false;

const { width, height } = img1;
const diff = new PNG({ width, height });
const pixeldifference = pixelmatch(img1.data, img2.data, diff.data, width, height, { threshold: 0.1 });
// only write to file if there is a difference in the images
return pixeldifference === 0;
}

/**
* This function changes the editor spec by pasting the given JSON string.
*/
async function changeEditorSpec(page: Page, jsonString: string) {
await page.evaluate(jsonString => {
navigator.clipboard.writeText(jsonString);

}, jsonString);
await delay(1000);
await page.mouse.click(200, 200);
await page.getByRole('textbox', { name: 'Editor content;Press Alt+F1 for Accessibility Options.' }).press('Control+a');
await page.getByRole('textbox', { name: 'Editor content;Press Alt+F1 for Accessibility Options.' }).press('Control+KeyA');
await page.keyboard.press('Backspace');
// await delay(1000);
await page.getByRole('textbox', { name: 'Editor content;Press Alt+F1 for Accessibility Options.' }).press('Meta+v');

await delay(100);
await page.mouse.click(200, 200, { button: 'right' });
await delay(100); // this is needed to wait for the context menu to appear
await page.getByRole('menuitem', { name: 'Paste' }).click();
}

/**
* This function polls until the screenshot of the given component matches the expected screenshot.
*/
async function pollUntilScreenshotMatchesExpected(
component: Locator,
page: Page,
expectedScreenshotPath: string,
timeout: number
) {
let screenshotMatchesExpected = false;
let timeElapsed = 0;
while (!screenshotMatchesExpected && timeElapsed < timeout) {
const screenshot = await component.screenshot();

screenshotMatchesExpected = isPngSame(screenshot, expectedScreenshotPath);

if (!screenshotMatchesExpected) {
await page.waitForTimeout(50); // wait 50ms before polling again
timeElapsed += 50;
}
}
return timeElapsed;
}

test('changes editor spec', async ({ page, context }) => {
await context.grantPermissions(['clipboard-read', 'clipboard-write']);
await page.goto('/');
await changeEditorSpec(page, jsonString);
// wait for network to go idle
await page.waitForLoadState('networkidle');
const gosComponent = page.getByLabel('Gosling visualization');

const matchTime = await pollUntilScreenshotMatchesExpected(
gosComponent,
page,
'e2e/perf.spec.ts-snapshots/changes-editor-spec-1-chromium-darwin.png',
10000
);

const totalBlockingTime = await page.evaluate(() => {
return new Promise((resolve) => {
let totalBlockingTime = 0
new PerformanceObserver(function (list) {
const perfEntries = list.getEntries()
for (const perfEntry of perfEntries) {
totalBlockingTime += perfEntry.duration - 50
}
perfEntries.forEach((entry) => console.log(entry.toJSON()))
resolve(totalBlockingTime)
}).observe({ type: 'longtask', buffered: true })

// Resolve promise if there haven't been long tasks
setTimeout(() => resolve(totalBlockingTime), 5000)
})
})

console.log('blocking', parseFloat(totalBlockingTime as string)) // 0
console.log(matchTime);
await delay(10000);

});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
"@types/d3-selection": "^2.0.0",
"@types/lodash-es": "^4.17.5",
"@types/node": "^18.6.2",
"@types/pixelmatch": "^5.2.5",
"@types/pngjs": "^6.0.3",
"@types/pubsub-js": "^1.8.2",
"@types/rbush": "^3.0.0",
"@types/react": "^18.2.0",
Expand Down Expand Up @@ -115,7 +117,9 @@
"jsoncrush": "^1.1.6",
"knip": "^2.30.0",
"npm-run-all": "^4.1.5",
"pixelmatch": "^5.3.0",
"pixi.js": "^6.3.0",
"pngjs": "^7.0.0",
"prettier": "^2.0.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
31 changes: 31 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,20 @@
resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz#e4a932069db47bb3eabeb0b305502d01586fa90d"
integrity sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==

"@types/pixelmatch@^5.2.5":
version "5.2.5"
resolved "https://registry.yarnpkg.com/@types/pixelmatch/-/pixelmatch-5.2.5.tgz#65eb7f7076a93cc003a504e363d0bf403ecbeede"
integrity sha512-di/HknmWA+KNjlLczJiLft9g1mHJZl5qGAXtDct8KsJg8KPrXKJa8Avumj53fgdJOBbfHABYhp3EjyitmKPdBg==
dependencies:
"@types/node" "*"

"@types/pngjs@^6.0.3":
version "6.0.3"
resolved "https://registry.yarnpkg.com/@types/pngjs/-/pngjs-6.0.3.tgz#33bfafdae1e6803624357be49f27ea70581a199e"
integrity sha512-F/WaGVKEZ1XYFlEtsWtqWm92vRfQdOqSSTBPj07BRDKnDtRhCw50DpwEQtrrDwEZUoAZAzv2FaalZiNV/54BoQ==
dependencies:
"@types/node" "*"

"@types/prop-types@*":
version "15.7.5"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
Expand Down Expand Up @@ -5341,6 +5355,13 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==

pixelmatch@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-5.3.0.tgz#5e5321a7abedfb7962d60dbf345deda87cb9560a"
integrity sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==
dependencies:
pngjs "^6.0.0"

pixi.js@^6.3.0:
version "6.5.9"
resolved "https://registry.yarnpkg.com/pixi.js/-/pixi.js-6.5.9.tgz#c85fb0f7efa303c17d8edc2698ca4b1545b23ab0"
Expand Down Expand Up @@ -5413,6 +5434,16 @@ [email protected]:
optionalDependencies:
fsevents "2.3.2"

pngjs@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821"
integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==

pngjs@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-7.0.0.tgz#a8b7446020ebbc6ac739db6c5415a65d17090e26"
integrity sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==

postcss@^8.4.27:
version "8.4.31"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
Expand Down

0 comments on commit d8920b8

Please sign in to comment.