From 8e7e12e2840dada32745ad82dd383960cb34b147 Mon Sep 17 00:00:00 2001 From: macaul_b Date: Tue, 25 Nov 2025 16:28:07 +0100 Subject: [PATCH 1/4] testing ghwf for changes --- .github/workflows/visualdiffs.yml | 47 ++++++++++++++++++ package-lock.json | 81 +++++++++++++++++++++++++++++-- package.json | 3 ++ scripts/visualDiff.cjs | 41 ++++++++++++++++ 4 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/visualdiffs.yml create mode 100644 scripts/visualDiff.cjs diff --git a/.github/workflows/visualdiffs.yml b/.github/workflows/visualdiffs.yml new file mode 100644 index 0000000..2071f5a --- /dev/null +++ b/.github/workflows/visualdiffs.yml @@ -0,0 +1,47 @@ +name: Visual URL Comparison + +on: + pull_request: + +jobs: + visual-diff: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install deps for PR build + run: npm ci + + - name: Install Playwright browsers + run: npx playwright install --with-deps + + - name: Build PR version + run: npm run build + + - name: Serve PR build + run: npm run preview -- --port 3001 & + + # Checkout base branch and build + - name: Checkout base branch + run: | + git fetch origin ${{ github.event.pull_request.base.ref }} --depth=1 + git checkout FETCH_HEAD + + - name: Install deps for base build + run: npm ci + + - name: Build base version + run: npm run build + + - name: preview vite build + run: npm run preview -- --port 3002 & + + # Run screenshot diff script + - name: Run visual diff + run: node scripts/visualDiff.cjs + + - uses: actions/upload-artifact@v4 + with: + name: visual-diffs + path: diffs diff --git a/package-lock.json b/package-lock.json index 00c00fb..63a725c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,9 @@ "eslint": "^9.33.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^5.2.0", + "pixelmatch": "^7.1.0", + "playwright": "^1.57.0", + "pngjs": "^7.0.0", "tailwindcss": "^4.1.16", "vite": "^7.1.2" } @@ -15887,6 +15890,19 @@ "node": ">= 6" } }, + "node_modules/pixelmatch": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.1.0.tgz", + "integrity": "sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^7.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -16024,6 +16040,63 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.57.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -20769,9 +20842,9 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "license": "Apache-2.0", "peer": true, "bin": { @@ -20779,7 +20852,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { diff --git a/package.json b/package.json index 27f6553..2e5f7fc 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,9 @@ "eslint": "^9.33.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^5.2.0", + "pixelmatch": "^7.1.0", + "playwright": "^1.57.0", + "pngjs": "^7.0.0", "tailwindcss": "^4.1.16", "vite": "^7.1.2" } diff --git a/scripts/visualDiff.cjs b/scripts/visualDiff.cjs new file mode 100644 index 0000000..ea1bdc2 --- /dev/null +++ b/scripts/visualDiff.cjs @@ -0,0 +1,41 @@ +const { chromium } = require("playwright"); +const pixelmatch = require("pixelmatch").default || require("pixelmatch"); +const { PNG } = require("pngjs"); +const fs = require("fs"); + +const urls = [{ name: "home", path: "/" }]; + +async function run() { + if (!fs.existsSync("diffs")) fs.mkdirSync("diffs"); + + const browser = await chromium.launch(); + + for (const { name, path } of urls) { + const page = await browser.newPage(); + + // PR screenshot + await page.goto(`http://localhost:5173${path}`); + await page.screenshot({ path: `diffs/${name}_pr.png`, fullPage: true }); + + // Base screenshot + await page.goto(`http://localhost:5173${path}`); + await page.screenshot({ path: `diffs/${name}_base.png`, fullPage: true }); + + // Compare + const img1 = PNG.sync.read(fs.readFileSync(`diffs/${name}_base.png`)); + const img2 = PNG.sync.read(fs.readFileSync(`diffs/${name}_pr.png`)); + const out = new PNG({ width: img1.width, height: img1.height }); + + pixelmatch(img1.data, img2.data, out.data, img1.width, img1.height, { + threshold: 0.3, + }); + + fs.writeFileSync(`diffs/${name}_diff.png`, PNG.sync.write(out)); + + await page.close(); + } + + await browser.close(); +} + +run(); From 9f3d78afbf81e9bba7312599e9afc23284bd4e66 Mon Sep 17 00:00:00 2001 From: macaul_b Date: Tue, 25 Nov 2025 16:36:53 +0100 Subject: [PATCH 2/4] test ghwf --- src/components/OptimadeClient/OptimadeMetadata.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/OptimadeClient/OptimadeMetadata.jsx b/src/components/OptimadeClient/OptimadeMetadata.jsx index 9869203..086c321 100644 --- a/src/components/OptimadeClient/OptimadeMetadata.jsx +++ b/src/components/OptimadeClient/OptimadeMetadata.jsx @@ -14,7 +14,9 @@ export default function OptimadeMetadata({ child }) {

FAQs

-

What is the OPTIMADE Client?

+

+ What is1232132 the OPTIMADE Client? +

This is a friendly client to search through databases and other implementations exposing an OPTIMADE RESTful API. To get more From f5e2155c1992481b8845e3a33bcfa6e185cdca04 Mon Sep 17 00:00:00 2001 From: macaul_b Date: Tue, 25 Nov 2025 16:40:17 +0100 Subject: [PATCH 3/4] fix --- scripts/visualDiff.cjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/visualDiff.cjs b/scripts/visualDiff.cjs index ea1bdc2..35ed81c 100644 --- a/scripts/visualDiff.cjs +++ b/scripts/visualDiff.cjs @@ -14,11 +14,11 @@ async function run() { const page = await browser.newPage(); // PR screenshot - await page.goto(`http://localhost:5173${path}`); + await page.goto(`http://localhost:3001${path}`); await page.screenshot({ path: `diffs/${name}_pr.png`, fullPage: true }); // Base screenshot - await page.goto(`http://localhost:5173${path}`); + await page.goto(`http://localhost:3002${path}`); await page.screenshot({ path: `diffs/${name}_base.png`, fullPage: true }); // Compare From e177b57fca0377701f849c57f0b9a76deb741a6b Mon Sep 17 00:00:00 2001 From: macaul_b Date: Tue, 25 Nov 2025 16:47:56 +0100 Subject: [PATCH 4/4] fix2 --- .github/workflows/visualdiffs.yml | 27 ++++++++++++++++++++++++--- scripts/visualDiff.cjs | 22 +++++++++++++++++----- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/.github/workflows/visualdiffs.yml b/.github/workflows/visualdiffs.yml index 2071f5a..49ac58c 100644 --- a/.github/workflows/visualdiffs.yml +++ b/.github/workflows/visualdiffs.yml @@ -16,12 +16,16 @@ jobs: - name: Install Playwright browsers run: npx playwright install --with-deps + # Build and serve PR version - name: Build PR version run: npm run build - name: Serve PR build run: npm run preview -- --port 3001 & + - name: Wait for PR server + run: npx wait-on http://localhost:3001 + # Checkout base branch and build - name: Checkout base branch run: | @@ -34,13 +38,30 @@ jobs: - name: Build base version run: npm run build - - name: preview vite build + - name: Serve base build run: npm run preview -- --port 3002 & - # Run screenshot diff script + - name: Wait for base server + run: npx wait-on http://localhost:3002 + + # Run visual diff and capture Markdown output - name: Run visual diff - run: node scripts/visualDiff.cjs + id: generate-diff + run: | + node scripts/visualDiff.cjs > diff.md + echo "comment<> $GITHUB_OUTPUT + cat diff.md >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # Post visual diffs as a PR comment + - name: Comment visual diffs on PR + uses: peter-evans/create-or-update-comment@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.pull_request.number }} + body: ${{ steps.generate-diff.outputs.comment }} + # Upload raw diff artifacts - uses: actions/upload-artifact@v4 with: name: visual-diffs diff --git a/scripts/visualDiff.cjs b/scripts/visualDiff.cjs index 35ed81c..2979c1f 100644 --- a/scripts/visualDiff.cjs +++ b/scripts/visualDiff.cjs @@ -10,32 +10,44 @@ async function run() { const browser = await chromium.launch(); + let mdOutput = "## Visual Diff Results\n\n"; + for (const { name, path } of urls) { const page = await browser.newPage(); // PR screenshot await page.goto(`http://localhost:3001${path}`); - await page.screenshot({ path: `diffs/${name}_pr.png`, fullPage: true }); + const prPath = `diffs/${name}_pr.png`; + await page.screenshot({ path: prPath, fullPage: true }); // Base screenshot await page.goto(`http://localhost:3002${path}`); - await page.screenshot({ path: `diffs/${name}_base.png`, fullPage: true }); + const basePath = `diffs/${name}_base.png`; + await page.screenshot({ path: basePath, fullPage: true }); // Compare - const img1 = PNG.sync.read(fs.readFileSync(`diffs/${name}_base.png`)); - const img2 = PNG.sync.read(fs.readFileSync(`diffs/${name}_pr.png`)); + const img1 = PNG.sync.read(fs.readFileSync(basePath)); + const img2 = PNG.sync.read(fs.readFileSync(prPath)); const out = new PNG({ width: img1.width, height: img1.height }); pixelmatch(img1.data, img2.data, out.data, img1.width, img1.height, { threshold: 0.3, }); - fs.writeFileSync(`diffs/${name}_diff.png`, PNG.sync.write(out)); + const diffPath = `diffs/${name}_diff.png`; + fs.writeFileSync(diffPath, PNG.sync.write(out)); + + // Encode diff as base64 for PR comment + const base64 = fs.readFileSync(diffPath).toString("base64"); + mdOutput += `### ${name}\n![${name} diff](data:image/png;base64,${base64})\n\n`; await page.close(); } await browser.close(); + + // Output markdown to stdout for GitHub Action step + console.log(mdOutput); } run();