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

CI : Add visual regression testing workflow based on Playwright and Argos. #511

Merged
merged 17 commits into from
Feb 17, 2025
Merged
48 changes: 48 additions & 0 deletions .github/workflows/argos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Argos CI Screenshots

on:
workflow_dispatch:
push:
paths:
- '**.js'
- '**.json'
- '**.yml'
- '**.yaml'
- '**.css'
branches: [master]
pull_request:
paths:
- '**.js'
- '**.json'
- '**.yml'
- '**.yaml'
- '**.css'
branches: [master]


jobs:
take-screenshots:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4

- name: Use Node.js 22.x
uses: actions/setup-node@v4
with:
node-version: 22.x

- name: Install dependencies
run: npm install

- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium

- name: Build the website
run: npm run build

- name: Take screenshots with Playwright
run: npx playwright test

- name: Upload screenshots to Argos
run: npx argos upload ./screenshots
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Playwright
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
screenshots
22 changes: 22 additions & 0 deletions argo_utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const cheerio = require("cheerio");
const fs = require("fs");

function extractSitemapPathnames(sitemapPath) {
const sitemap = fs.readFileSync(sitemapPath).toString();
const $ = cheerio.load(sitemap, { xmlMode: true });
const urls = [];
$("loc").each(function handleLoc() {
urls.push($(this).text());
});
return urls.map((url) => new URL(url).pathname);
}

// Converts a pathname to a decent screenshot name
function pathnameToArgosName(pathname) {
return pathname.replace(/^\/|\/$/g, "") || "index";
}

module.exports = {
extractSitemapPathnames,
pathnameToArgosName,
};
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,17 @@
"got": "^12.1.0"
},
"devDependencies": {
"@argos-ci/cli": "^2.5.5",
"@argos-ci/playwright": "^4.1.0",
"@babel/core": "^7.26.0",
"@babel/plugin-transform-modules-commonjs": "^7.25.9",
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.25.9",
"@babel/register": "^7.25.9",
"@playwright/test": "^1.50.0",
"@typescript-eslint/eslint-plugin": "4.22.0",
"@untitaker/hyperlink": "^0.1.32",
"cheerio": "^1.0.0",
"eslint": "^7.24.0",
"eslint-config-prettier": "^8.2.0",
"eslint-import-resolver-typescript": "^2.4.0",
Expand Down
19 changes: 19 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {devices} from '@playwright/test';
import type {PlaywrightTestConfig} from '@playwright/test';

const config: PlaywrightTestConfig = {
webServer: {
port: 3000,
command: 'yarn docusaurus serve',
},
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
],
};

export default config;
22 changes: 22 additions & 0 deletions screenshot.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* Iframes can load lazily */
iframe,
/* Avatars can be flaky due to using external sources: GitHub/Unavatar */
.avatar__photo,
/* Gifs load lazily and are animated */
img[src$='.gif'],
/* Algolia keyboard shortcuts appear with a little delay */
.DocSearch-Button-Keys>kbd,
/* The live playground preview can often display dates/counters */
[class*='playgroundPreview'] {
visibility: hidden;
}
/* Different docs last-update dates can alter layout */
.theme-last-updated,
/* Mermaid diagrams are rendered client-side and produce layout shifts */
.docusaurus-mermaid-container {
display: none;
}
/* Videos are laggy and the loading spinner is shown as change on Argos */
video {
visibility: hidden;
}
34 changes: 34 additions & 0 deletions screenshot.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const fs = require("fs");
const { test } = require("@playwright/test");
const { argosScreenshot } = require("@argos-ci/playwright");
const { extractSitemapPathnames, pathnameToArgosName } = require("./argo_utils");

// Constants
const siteUrl = 'http://localhost:3000';
const sitemapPath = './build/sitemap.xml';
const stylesheetPath = './screenshot.css';
const stylesheet = fs.readFileSync(stylesheetPath).toString();

// Wait for hydration
function waitForDocusaurusHydration() {
return document.documentElement.dataset.hasHydrated === 'true';
}

function screenshotPathname(pathname) {
test(`pathname ${pathname}`, async ({ page }) => {
const url = siteUrl + pathname;
await page.goto(url);
await page.waitForFunction(waitForDocusaurusHydration);
await page.addStyleTag({ content: stylesheet });
await argosScreenshot(page, pathnameToArgosName(pathname));
});
}

test.describe('Docusaurus site screenshots', () => {
const pathnames = extractSitemapPathnames(sitemapPath);
console.log('Pathnames to screenshot:', pathnames);
const filteredPathnames = pathnames.filter((pathname) => !pathname.includes('/1.0.0/'));
console.log('Filtered Pathnames:', filteredPathnames);

filteredPathnames.forEach(screenshotPathname);
});