diff --git a/.gitignore b/.gitignore index f18ac6af3..62e8b751b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,8 @@ public/wasm_lib_bg.wasm src/wasm-lib/lcov.info e2e/playwright/playwright-secrets.env +e2e/playwright/temp1.png +e2e/playwright/temp2.png /test-results/ /playwright-report/ /blob-report/ diff --git a/e2e/playwright/flow-tests.spec.ts b/e2e/playwright/flow-tests.spec.ts index 148a876eb..b4fc88ff8 100644 --- a/e2e/playwright/flow-tests.spec.ts +++ b/e2e/playwright/flow-tests.spec.ts @@ -60,8 +60,13 @@ test('Basic sketch', async ({ page }) => { // click on "Start Sketch" button await u.clearCommandLogs() - await page.getByRole('button', { name: 'Start Sketch' }).click() - await u.waitForDefaultPlanesVisibilityChange() + await Promise.all([ + u.doAndWaitForImageDiff( + () => page.getByRole('button', { name: 'Start Sketch' }).click(), + 200 + ), + u.waitForDefaultPlanesVisibilityChange(), + ]) // select a plane await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter') diff --git a/e2e/playwright/test-utils.ts b/e2e/playwright/test-utils.ts index b456d8353..71da4d45c 100644 --- a/e2e/playwright/test-utils.ts +++ b/e2e/playwright/test-utils.ts @@ -1,5 +1,8 @@ import { expect, Page } from '@playwright/test' import { EngineCommand } from '../../src/lang/std/engineConnection' +import fsp from 'fs/promises' +import pixelMatch from 'pixelmatch' +import { PNG } from 'pngjs' async function waitForPageLoad(page: Page) { // wait for 'Loading stream...' spinner @@ -108,5 +111,46 @@ export function getUtils(page: Page) { await closeDebugPanel(page) } }, + doAndWaitForImageDiff: (fn: () => Promise, diffCount = 200) => + new Promise(async (resolve) => { + await page.screenshot({ + path: './e2e/playwright/temp1.png', + fullPage: true, + }) + await fn() + const isImageDiff = async () => { + await page.screenshot({ + path: './e2e/playwright/temp2.png', + fullPage: true, + }) + const screenshot1 = PNG.sync.read( + await fsp.readFile('./e2e/playwright/temp1.png') + ) + const screenshot2 = PNG.sync.read( + await fsp.readFile('./e2e/playwright/temp2.png') + ) + const actualDiffCount = pixelMatch( + screenshot1.data, + screenshot2.data, + null, + screenshot1.width, + screenshot2.height + ) + return actualDiffCount > diffCount + } + + // run isImageDiff every 50ms until it returns true or 5 seconds have passed (100 times) + let count = 0 + const interval = setInterval(async () => { + count++ + if (await isImageDiff()) { + clearInterval(interval) + resolve(true) + } else if (count > 100) { + clearInterval(interval) + resolve(false) + } + }, 50) + }), } } diff --git a/package.json b/package.json index b45f00708..d8098ad61 100644 --- a/package.json +++ b/package.json @@ -109,6 +109,8 @@ "@types/crypto-js": "^4.1.1", "@types/debounce-promise": "^3.1.8", "@types/isomorphic-fetch": "^0.0.36", + "@types/pixelmatch": "^5.2.6", + "@types/pngjs": "^6.0.4", "@types/react-modal": "^3.16.0", "@types/uuid": "^9.0.4", "@types/wait-on": "^5.3.4", @@ -127,6 +129,8 @@ "eslint-plugin-css-modules": "^2.12.0", "happy-dom": "^10.8.0", "husky": "^8.0.3", + "pixelmatch": "^5.3.0", + "pngjs": "^7.0.0", "postcss": "^8.4.31", "prettier": "^2.8.0", "setimmediate": "^1.0.5", diff --git a/yarn.lock b/yarn.lock index c04213358..fa15b3564 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2244,6 +2244,20 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/pixelmatch@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@types/pixelmatch/-/pixelmatch-5.2.6.tgz#fba6de304ac958495f27d85989f5c6bb7499a686" + integrity sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg== + dependencies: + "@types/node" "*" + +"@types/pngjs@^6.0.4": + version "6.0.4" + resolved "https://registry.yarnpkg.com/@types/pngjs/-/pngjs-6.0.4.tgz#9a457aebabd944efde1a773a0fa1d74933e8021b" + integrity sha512-atAK9xLKOnxiuArxcHovmnOUUGBZOQ3f0vCf43FnoKs6XnqiambT1kkJWmdo71IR+BoXSh+CueeFR0GfH3dTlQ== + 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" @@ -6632,6 +6646,13 @@ pirates@^4.0.1: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +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" + pkg-types@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868" @@ -6655,6 +6676,16 @@ playwright@1.39.0: 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== + portfinder@^1.0.28: version "1.0.32" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81"