diff --git a/package.json b/package.json index b4edd9ed58..eb889d6842 100644 --- a/package.json +++ b/package.json @@ -181,6 +181,7 @@ "@emotion/server": "^11.11.0", "@ianvs/prettier-plugin-sort-imports": "^4.2.1", "@madccc/duplicate-package-checker-webpack-plugin": "^1.0.0", + "@microflash/rehype-figure": "^2.1.0", "@npmcli/run-script": "^7.0.4", "@octokit/rest": "^20.1.0", "@qixian.cs/github-contributors-list": "^2.0.1", @@ -314,13 +315,14 @@ "react-router-dom": "^6.22.3", "react-sticky-box": "^2.0.5", "regenerator-runtime": "^0.14.1", + "rehype-stringify": "^10.0.0", "remark": "^15.0.1", "remark-cli": "^12.0.0", "remark-gfm": "^4.0.0", - "remark-html": "^16.0.1", "remark-lint": "^9.1.2", "remark-lint-no-undefined-references": "^4.2.1", "remark-preset-lint-recommended": "^6.1.3", + "remark-rehype": "^11.1.0", "runes2": "^1.1.4", "semver": "^7.6.0", "sharp": "^0.33.3", diff --git a/scripts/visual-regression/build.ts b/scripts/visual-regression/build.ts index df62573031..5ece812f8c 100644 --- a/scripts/visual-regression/build.ts +++ b/scripts/visual-regression/build.ts @@ -6,17 +6,17 @@ import os from 'os'; import path from 'path'; import { Readable } from 'stream'; import { finished } from 'stream/promises'; +import simpleGit from 'simple-git'; import chalk from 'chalk'; import fse from 'fs-extra'; import difference from 'lodash/difference'; import minimist from 'minimist'; import pixelmatch from 'pixelmatch'; import { PNG } from 'pngjs'; -import { remark } from 'remark'; -import remarkGfm from 'remark-gfm'; -import remarkHtml from 'remark-html'; import sharp from 'sharp'; +import markdown2Html from './convert'; + const ROOT_DIR = process.cwd(); const ALI_OSS_BUCKET = 'antd-visual-diff'; @@ -119,36 +119,100 @@ async function downloadBaseSnapshots(ref: string, targetDir: string) { }); } +interface IImageDesc { + src: string; + alt: string; +} + +function getMdImageTag(desc: IImageDesc, extraCaption?: boolean) { + const { src, alt } = desc; + if (!extraCaption || !alt) { + // in md2html report, we use `@microflash/rehype-figure` to generate a figure + return `![${alt}](${src})`; + } + // show caption with image in github markdown comment + return `![${alt}](${src}) ${alt}`; +} + interface IBadCase { type: 'removed' | 'changed'; filename: string; + /** + * compare target file + */ + targetFilename?: string; /** * 0 - 1 */ weight: number; } -function md2Html(md: string) { - return remark().use(remarkGfm).use(remarkHtml).processSync(md).toString(); -} +const git = simpleGit(); -function parseArgs() { +async function parseArgs() { // parse args from -- --pr-id=123 --base_ref=feature const argv = minimist(process.argv.slice(2)); const prId = argv['pr-id']; assert(prId, 'Missing --pr-id'); const baseRef = argv['base-ref']; assert(baseRef, 'Missing --base-ref'); + + const { latest } = await git.log(); + return { prId, baseRef, + currentRef: latest?.hash.slice(0, 8) || '', }; } +function generateLineReport( + badCase: IBadCase, + publicPath: string, + currentRef: string, + extraCaption?: boolean, +) { + const { filename, type, targetFilename } = badCase; + + let lineHTMLReport = ''; + if (type === 'changed') { + lineHTMLReport += '| '; + lineHTMLReport += [ + // add ref as query to avoid github cache image object + getMdImageTag({ + src: `${publicPath}/images/base/${filename}?ref=${currentRef}`, + alt: targetFilename || '', + }, extraCaption), + getMdImageTag({ + src: `${publicPath}/images/current/${filename}?ref=${currentRef}`, + alt: filename, + }, extraCaption), + getMdImageTag({ + src: `${publicPath}/images/diff/${filename}?ref=${currentRef}`, + alt: '', + }, extraCaption), + ].join(' | '); + lineHTMLReport += ' |\n'; + } else if (type === 'removed') { + lineHTMLReport += '| '; + lineHTMLReport += [ + getMdImageTag({ + src: `${publicPath}/images/base/${filename}?ref=${currentRef}`, + alt: targetFilename || '', + }, extraCaption), + `⛔️⛔️⛔️ Missing ⛔️⛔️⛔️`, + `🚨🚨🚨 Removed 🚨🚨🚨`, + ].join(' | '); + lineHTMLReport += ' |\n'; + } + return lineHTMLReport; +} + function generateReport( badCases: IBadCase[], targetBranch: string, targetRef: string, + currentRef: string, prId: string, ): [string, string] { const reportDirname = path.basename(REPORT_DIR); @@ -174,15 +238,15 @@ function generateReport( '', ].join('\n'); - return [mdStr, md2Html(mdStr)]; + return [mdStr, markdown2Html(mdStr)]; } let reportMdStr = ` ${commonHeader} ${fullReport} -| Image name | Expected | Actual | Diff | -| --- | --- | --- | --- | +| Expected (Branch ${targetBranch}) | Actual (Current PR) | Diff | +| --- | --- | --- | `.trim(); reportMdStr += '\n'; @@ -192,44 +256,33 @@ ${fullReport} let diffCount = 0; for (const badCase of badCases) { - const { filename, type } = badCase; - let lineReportMdStr = ''; - if (type === 'changed') { - lineReportMdStr += '| '; - lineReportMdStr += [ - `\`${badCase.filename}\``, - `![${targetBranch}: ${targetRef}](${publicPath}/images/base/${filename})`, - `![current: pr-${prId}](${publicPath}/images/current/${filename})`, - `![diff](${publicPath}/images/diff/${filename})`, - ].join(' | '); - lineReportMdStr += ' |\n'; - } else if (type === 'removed') { - lineReportMdStr += '| '; - lineReportMdStr += [ - `\`${badCase.filename}\``, - `![${targetBranch}: ${targetRef}](${publicPath}/images/base/${filename})`, - `⛔️⛔️⛔️ Missing ⛔️⛔️⛔️`, - `🚨🚨🚨 Removed 🚨🚨🚨`, - ].join(' | '); - lineReportMdStr += ' |\n'; - } - diffCount += 1; if (diffCount <= 10) { - reportMdStr += lineReportMdStr; + // 将图片下方增加文件名 + reportMdStr += generateLineReport( + badCase, + publicPath, + currentRef, + true, + ); } - fullVersionMd += lineReportMdStr; + fullVersionMd += generateLineReport( + badCase, + publicPath, + currentRef, + false, + ); } reportMdStr += addonFullReportDesc; // convert fullVersionMd to html - return [reportMdStr, md2Html(fullVersionMd)]; + return [reportMdStr, markdown2Html(fullVersionMd)]; } async function boot() { - const { prId, baseRef: targetBranch = 'master' } = parseArgs(); + const { prId, baseRef: targetBranch = 'master', currentRef } = await parseArgs(); const baseImgSourceDir = path.resolve(ROOT_DIR, `./imageSnapshots-${targetBranch}`); @@ -324,6 +377,7 @@ async function boot() { badCases.push({ type: 'changed', filename: compareImgName, + targetFilename: baseImgName, weight: mismatchedPxPercent, }); } else { @@ -340,6 +394,7 @@ async function boot() { badCases, targetBranch, targetCommitSha, + currentRef, prId, ); await fse.writeFile(path.join(REPORT_DIR, './report.md'), reportMdStr); diff --git a/scripts/visual-regression/convert.ts b/scripts/visual-regression/convert.ts new file mode 100644 index 0000000000..7105767a88 --- /dev/null +++ b/scripts/visual-regression/convert.ts @@ -0,0 +1,17 @@ +/* eslint-disable compat/compat */ +/* eslint-disable no-console, no-await-in-loop, import/no-extraneous-dependencies, no-restricted-syntax */ +import { remark } from 'remark'; +import remarkGfm from 'remark-gfm'; +import remarkRehype from 'remark-rehype'; +import rehypeStringify from 'rehype-stringify'; +import rehypeFigure from '@microflash/rehype-figure'; + +export default function markdown2Html(content: string) { + return remark() + .use(remarkGfm) + .use(remarkRehype) + .use(rehypeFigure) + .use(rehypeStringify) + .processSync(content) + .toString(); +} diff --git a/scripts/visual-regression/report-template.html b/scripts/visual-regression/report-template.html index 34f36beb7f..a523870703 100644 --- a/scripts/visual-regression/report-template.html +++ b/scripts/visual-regression/report-template.html @@ -12,6 +12,16 @@ padding-bottom: 16px; } + /* figcaption */ + figcaption { + color: #a3a3a3; + text-align: center; + } + + figure { + cursor: pointer; + } + /* Table Styles */ table { width: 100%; @@ -33,12 +43,7 @@ th, td { - width: 10%; - } - - th+th, - td+td { - width: 30%; + width: 33%; } th { @@ -89,6 +94,22 @@ {{reportContent}} + + - \ No newline at end of file + diff --git a/typings/custom-typings.d.ts b/typings/custom-typings.d.ts index 5db6d572f5..2a12b0bd35 100644 --- a/typings/custom-typings.d.ts +++ b/typings/custom-typings.d.ts @@ -25,3 +25,5 @@ declare module '@npmcli/run-script' { [key: string]: string | string[] | boolean | NodeJS.ProcessEnv; }): Promise; } + +declare module '@microflash/rehype-figure';