對前端依存項充滿信心地進行升級
前端開發人員常常需要升級 npm 依存項,但這些升級可能會讓人感到害怕,並導致細微的 UI 副作用,而您的常規測試套件無法偵測到。
升級 Docusaurus 就是一個很好的例子:在不逐頁檢閱所有頁面的情況下,很難確定沒有視覺回歸。Docusaurus v3 即將推出(目前正在 測試版 中),我們希望協助您充滿信心地進行此升級。
本文介紹一個視覺回歸測試工作流程,此工作流程基於 GitHub Actions、Playwright 和 Argos。它與 Docusaurus 或 React 無直接關聯,而且可以調整,以用於其他前端應用程式和架構。
此工作流程已經在將 Docusaurus v2 升級到 v3 時進行測試,並已幫助在類似 React Native、Jest 和 Docusaurus 本身的網站上,捕獲到一些視覺回歸。
Docusaurus v3 的基礎設施有更改,而且有重大相依性升級,例如 MDX v3 和 React 18,而這些可能會產生意外的副作用。如果沒有此一工作流程,將難以注意到所有視覺回歸。這就是我們鼓勵網站擁有者考慮採用視覺回歸測試的原因,特別是對於高度自訂的網站。
工作流程概觀
整個概念相當簡單
- 在使用 GitHub Actions 於 CI 中建立你的網站
- 使用 Playwright 截取所有
sitemap.xml
頁面的螢幕擷取畫面 - 將它們上傳至 Argos
- 對 Git 分支
main
和pr-branch
都執行相同動作 - 在 Argos 並排比較螢幕擷取畫面
隨後,Argos 將會 回報視覺差異,也就是在 main
和 pr-branch
之間找到的差異,當成 GitHub 提交狀態和拉取請求註解。這樣的功能能夠協助你自動化偵測視覺回歸,並且及早發現。
Argos 會製作一份報告,參考在並排比較兩個 Git 分支網站時,所找到的所有視覺差異,而且會提供便利的 UX 讓你可以輕鬆發現差異。
查看 Docusaurus Argos 頁面,瀏覽我們自己網站的報告。
這裡有 Argos 的具體範例,回報在升級 React-Native 網站時,所發現的一個視覺回歸
工作流程實作
本節將描述工作流程中每個步驟的實作詳細資料。
你需要 註冊 Argos,並將 Argos 連接到你的 GitHub 儲存庫
相依性
此工作流程需要以下的開發相依性,且這些相依性不包含一般 Docusaurus 會用到的相依性
yarn add -D @argos-ci/cli @argos-ci/playwright @playwright/test cheerio
GitHub Action
GitHub action 負責為每個 Git 分支執行工作流程。
一個最小程度的工作流程可以用以下方法執行
name: Argos CI Screenshots
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
take-screenshots:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: current
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Install Playwright browsers
run: yarn playwright install --with-deps chromium
- name: Build the website
run: yarn docusaurus build
- name: Take screenshots with Playwright
run: yarn playwright test
- name: Upload screenshots to Argos
run: yarn argos upload ./screenshots
Playwright 設定檔
Playwright 負責截取 GitHub action 在本地先前已建置的網站之螢幕截圖。
極簡的 Playwright 設定檔 可能是
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;
Playwright 測試
Playwright 設定檔還不夠,我們還需要撰寫一個 Playwright 測試檔案來產生網站螢幕截圖。
import * as fs from 'fs';
import {test} from '@playwright/test';
import {argosScreenshot} from '@argos-ci/playwright';
import {extractSitemapPathnames, pathnameToArgosName} from './utils';
// Constants
const siteUrl = 'https://127.0.0.1:3000';
const sitemapPath = './build/sitemap.xml';
const stylesheetPath = './screenshot.css';
const stylesheet = fs.readFileSync(stylesheetPath).toString();
// Wait for hydration, requires Docusaurus v2.4.3+
// Docusaurus adds a <html data-has-hydrated="true"> once hydrated
// See https://github.com/facebook/docusaurus/pull/9256
function waitForDocusaurusHydration() {
return document.documentElement.dataset.hasHydrated === 'true';
}
function screenshotPathname(pathname: string) {
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);
pathnames.forEach(screenshotPathname);
});
我們為什麼使用 Argos 而非 Playwright 來截取螢幕截圖?
Argos 有個 Playwright 整合,可以封裝原始 Playwright 螢幕截圖 API 並提供更好的預設值,讓螢幕截圖更具有決定性。
utils.ts
裡有什麼?
此模組包含我們基於清楚起見而選擇隱藏的實作細節。
import * as cheerio from 'cheerio';
import * as fs from 'fs';
// Extract a list of pathnames, given a fs path to a sitemap.xml file
// Docusaurus generates a build/sitemap.xml file for you!
export function extractSitemapPathnames(sitemapPath: string): string[] {
const sitemap = fs.readFileSync(sitemapPath).toString();
const $ = cheerio.load(sitemap, {xmlMode: true});
const urls: string[] = [];
$('loc').each(function handleLoc() {
urls.push($(this).text());
});
return urls.map((url) => new URL(url).pathname);
}
// Converts a pathname to a decent screenshot name
export function pathnameToArgosName(pathname: string): string {
return pathname.replace(/^\/|\/$/g, '') || 'index';
}
樣式表
螢幕截圖不總是具有決定性,截取頁面的螢幕截圖兩次可能會導致細微的變異,而 Argos 會將這類變異報告為假正向視覺回歸錯誤。
因此,我們建議注入一張額外的樣式表來隱藏有問題的元素。你可能會需要根據在你自己的網站上找到出現變異的元素,新增新的 CSS 規則到這個基本樣式表。請參閱 Argos - 關於變異性測試文件 以取得詳細資料。
/* 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;
}
我們建議使用 display: none;
隱藏影響版面的不穩定 UI 元素。
例如,文件「最後更新時間」可能會轉譯成兩行以上,最終「推移」你的其他內容到更下面,導致 Argos 偵測到許多不同的像素。
範例存放庫
slorber/docusaurus-argos-example 存放庫展示了使用 Yarn monorepo 在新初始化的 Docusaurus v2 網站上實作此工作流程的完整範例。
相關的 pull 要求
- PR - 設定 GitHub Action + Playwright + Argos:實作上述極簡工作流程
- 升級 Docusaurus 從 v2 到 v3:顯示 Argos 在升級時如何捕獲 3 次視覺回歸
瀏覽 Docusaurus 儲存庫以獲取更進階的整合
讓它便宜一些
我們選擇的工具是這個視覺回歸測試工作流程的實作詳細資料。
對於 Docusaurus,我們選擇 Argos:它對我們來說很好用,並提供免費和開源方案。但是,您可以自由採用其他工具。
萬一您不介意將大型螢幕截圖儲存在 Git 中,您也可以嘗試免費的自託管Playwright Visual Comparisons,並使用 npx playwright show-report
瀏覽視覺差異。但是,我們發現使用專門的外部工具比較方便。
外部工具可能很昂貴,但通常提供免費方案,並有充足的螢幕截圖配額。您可以透過實作以下一些技巧來減少螢幕截圖使用量。
限制路徑名稱數量
基本設定包含擷取在 sitemap.xml
中找到的每個單獨路徑名稱的螢幕截圖。對於大型網站,這可能會產生許多螢幕截圖。
您可以決定過濾路徑名稱,僅擷取最關鍵的頁面的螢幕截圖。
對於 Docusaurus 網站,不要為版本化文件頁面擷取螢幕截圖
function isVersionedDocsPathname(pathname: string): boolean {
return pathname.match(/^\/docs\/((\d\.\d\.\d)|(next))\//);
}
test.describe('Docusaurus site screenshots', () => {
const pathnames = extractSitemapPathnames(sitemapPath)
.filter(isVersionedDocsPathname);
pathnames.forEach(screenshotPathname);
});
限制工作流程同時執行
實作 GitHub Actions 同時執行群組 將防止後續提交觸發多個無用的工作流程執行。工作流程將僅針對最後一次提交執行,先前提交將被自動取消。
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
有條件執行您的工作流程
不值得針對每一個提交和 pull 要求執行此工作流程。
例如,如果有人更正您的文件中的拼寫錯誤,您可能不希望截取數百個螢幕擷圖並請 Argos 指出只有修改過的頁面視覺上有所差異:嗯,這是有點預期的!
對於 Docusaurus 網站,我們僅針對具有 Argos
標籤的 pull 要求執行工作流程。
name: Argos CI Screenshots
on:
push:
branches: [main]
pull_request:
branches: [main]
types:
- opened
- synchronize
- reopened
- labeled
jobs:
take-screenshots:
if: ${{ github.ref_name == 'main' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Argos')) }}
runs-on: ubuntu-latest
steps:
# Your job steps here ...
有許多選項可以探索,例如 手動觸發工作流程 或 僅在與特定模式相符的檔案修改時。
結論
我相信視覺回歸測試在前端生態系統中未得到充分利用。
擷取全頁螢幕擷圖是垂手可得、容易設定且可以協助您抓出一般測試套件會錯過的全新錯誤類型。此技術不僅適用於 npm 套件升級,也適用於不應變更使用者介面的任何重構。
因此何不給它一個機會?
駭得開心!
另請參閱
有用的文件連結