mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-23 18:50:06 +08:00
test: handle error
This commit is contained in:
parent
aa56e9ab7c
commit
3e8e61c1c1
@ -21,9 +21,15 @@ interface VisualDiffConfig {
|
||||
openTriggerClassName?: string;
|
||||
}
|
||||
|
||||
interface PageVisitConfig {
|
||||
mdPath: string;
|
||||
theme: string;
|
||||
enableCssVar: boolean;
|
||||
}
|
||||
|
||||
const themes = ['default', 'dark', 'compact'];
|
||||
|
||||
async function retrieveDemoUrl(mdPath: string) {
|
||||
function retrieveDemoUrl(mdPath: string) {
|
||||
// ~demos/button-demo-basic
|
||||
return mdPath
|
||||
.replace(/^components\//, '')
|
||||
@ -31,6 +37,13 @@ async function retrieveDemoUrl(mdPath: string) {
|
||||
.replace(/\//g, '-');
|
||||
}
|
||||
|
||||
function retrieveCaptureImgName(config: PageVisitConfig) {
|
||||
// components/affix/demo/basic.md -> affix-basic
|
||||
const { mdPath, theme, enableCssVar } = config;
|
||||
const demoUrl = retrieveDemoUrl(mdPath);
|
||||
return `${demoUrl.replace('-demo', '')}.${theme}${enableCssVar ? '.css-var' : ''}.png`;
|
||||
}
|
||||
|
||||
async function retrieveConfig(mdPath: string): Promise<VisualDiffConfig | void> {
|
||||
const mdDir = path.dirname(mdPath);
|
||||
const configDir = path.join(mdDir, '..', '__tests__');
|
||||
@ -64,12 +77,6 @@ async function createSiteServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
interface PageVisitConfig {
|
||||
mdPath: string;
|
||||
theme: string;
|
||||
enableCssVar: boolean;
|
||||
}
|
||||
|
||||
class BrowserAuto {
|
||||
private browser: Browser | null = null;
|
||||
|
||||
@ -86,8 +93,7 @@ class BrowserAuto {
|
||||
deviceScaleFactor: 2,
|
||||
});
|
||||
|
||||
// 要截屏 6 张,需要测试一下要不要拆分为独立任务,还是按照 demoPath 一次性截屏 6 张
|
||||
this.context.setDefaultTimeout(5000 * 6);
|
||||
this.context.setDefaultTimeout(5000);
|
||||
|
||||
await fse.ensureDir(this.outputDir);
|
||||
await fse.emptyDir(this.outputDir);
|
||||
@ -97,29 +103,32 @@ class BrowserAuto {
|
||||
await fse.writeFile(errorFilePath, '');
|
||||
}
|
||||
|
||||
async appendErrorLog(errorData: any) {
|
||||
async appendErrorLog(errorData: {
|
||||
filename: string;
|
||||
error: string;
|
||||
}) {
|
||||
const errorFilePath = path.join(this.outputDir, 'error.jsonl');
|
||||
const errorLine = JSON.stringify({
|
||||
...errorData,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
await fs.promises.writeFile(errorFilePath, `${errorLine}\n`);
|
||||
console.log('Error filename:', errorData.filename);
|
||||
await fs.promises.appendFile(errorFilePath, `${errorLine}\n`);
|
||||
}
|
||||
|
||||
// 执行截屏
|
||||
async captureScreenshots(config: PageVisitConfig) {
|
||||
async captureScreenshots(config: PageVisitConfig, imgName: string) {
|
||||
if (!this.context) return;
|
||||
const { mdPath, theme, enableCssVar } = config;
|
||||
|
||||
const page = await this.context.newPage();
|
||||
|
||||
await this.visitDemoPage(page, mdPath, theme, enableCssVar);
|
||||
await this.visitDemoPage(page, config, imgName);
|
||||
|
||||
return page?.close();
|
||||
}
|
||||
|
||||
private async visitDemoPage(page: Page, mdPath: string, theme: string, enableCssVar: boolean) {
|
||||
const demoUrl = await retrieveDemoUrl(mdPath);
|
||||
private async visitDemoPage(page: Page, config: PageVisitConfig, imgName: string) {
|
||||
const { mdPath, theme, enableCssVar } = config;
|
||||
const demoUrl = retrieveDemoUrl(mdPath);
|
||||
const options = await retrieveConfig(mdPath);
|
||||
if (!options) {
|
||||
loglevel.info('Skip for: %s', mdPath);
|
||||
@ -144,10 +153,10 @@ class BrowserAuto {
|
||||
const pageUrl = `http://localhost:${port}/~demos/${demoUrl.toLowerCase()}?${query.toString()}`;
|
||||
|
||||
await page.goto(pageUrl);
|
||||
// TODO: 需要禁用掉页面中的各种采集和埋点请求,避免干扰
|
||||
// TODO: Need to disable various data collection and tracking requests in the page to avoid interference
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// 禁用掉所有的动画
|
||||
// disabled animation
|
||||
await page.addStyleTag({
|
||||
content: '*{animation: none!important;}',
|
||||
});
|
||||
@ -176,10 +185,6 @@ class BrowserAuto {
|
||||
// await page.click(`.${options.openTriggerClassName}`);
|
||||
// }
|
||||
|
||||
// ~demos/button-demo-basic -> button-basic
|
||||
const imgName = `${demoUrl.replace('-demo', '')}.${theme}${enableCssVar ? '.css-var' : ''}.png`;
|
||||
|
||||
// 保存截图到 ./result 目录
|
||||
await page.screenshot({
|
||||
path: path.join(this.outputDir, imgName),
|
||||
animations: 'disabled',
|
||||
@ -190,7 +195,6 @@ class BrowserAuto {
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭浏览器
|
||||
async close() {
|
||||
await this.browser?.close();
|
||||
}
|
||||
@ -225,7 +229,7 @@ function parseArgs(): {
|
||||
// npm run visual-diff:capture -- --component=space --loglevel=info --server-only --shard=1/2 --max-workers=2
|
||||
(async () => {
|
||||
const args = parseArgs();
|
||||
const { serverOnly, component, logLevel = 'error', shard } = args;
|
||||
const { serverOnly, component, logLevel = 'info', shard } = args;
|
||||
loglevel.setLevel(logLevel);
|
||||
|
||||
loglevel.info(`Args: ${JSON.stringify(args)}`);
|
||||
@ -251,13 +255,15 @@ function parseArgs(): {
|
||||
loglevel.info(`Shard ${current}/${total}: ${mdPaths.length}/${originLens} mds`);
|
||||
}
|
||||
|
||||
const task = async (visitConfig: PageVisitConfig) => {
|
||||
const visitTask = async (visitConfig: PageVisitConfig) => {
|
||||
const imgName = retrieveCaptureImgName(visitConfig);
|
||||
try {
|
||||
await handler.captureScreenshots(visitConfig);
|
||||
// ~demos/button-demo-basic -> button-basic
|
||||
await handler.captureScreenshots(visitConfig, imgName);
|
||||
} catch (err) {
|
||||
const errorData = {
|
||||
filename: visitConfig.mdPath,
|
||||
error: (err as Error).message,
|
||||
filename: imgName,
|
||||
error: `Capture failed: ${(err as Error).message}`,
|
||||
};
|
||||
await handler.appendErrorLog(errorData);
|
||||
loglevel.error(`Error: ${errorData.error}`);
|
||||
@ -278,11 +284,13 @@ function parseArgs(): {
|
||||
await pAll(
|
||||
visitConfigs.map((visitConfig, i) => async () => {
|
||||
loglevel.info(
|
||||
`处理 ${i + 1}/${visitConfigs.length}: ${visitConfig.mdPath} / ${visitConfig.theme} / ${visitConfig.enableCssVar}`,
|
||||
`Task ${i + 1}/${visitConfigs.length}: ${visitConfig.mdPath} / ${visitConfig.theme} / ${visitConfig.enableCssVar}`,
|
||||
);
|
||||
const now = performance.now();
|
||||
await task(visitConfig);
|
||||
loglevel.info(`处理 ${i + 1}/${visitConfigs.length} 完成,耗时 ${performance.now() - now}ms`);
|
||||
await visitTask(visitConfig);
|
||||
loglevel.info(
|
||||
`Task ${i + 1}/${visitConfigs.length} finished, time: ${performance.now() - now}ms`,
|
||||
);
|
||||
}),
|
||||
{ concurrency: 10 },
|
||||
);
|
||||
|
@ -95,6 +95,23 @@ async function downloadFile(url: string, destPath: string) {
|
||||
await finished(body.pipe(fs.createWriteStream(destPath)));
|
||||
}
|
||||
|
||||
async function readErrorJsonl(filePath: string) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return {};
|
||||
}
|
||||
const content = await fs.promises.readFile(filePath, 'utf-8');
|
||||
const result: Record<string, string> = {};
|
||||
for (const lineStr of content.split('\n').filter(Boolean)) {
|
||||
try {
|
||||
const line = JSON.parse(lineStr);
|
||||
result[line.filename] = line.error;
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function getBranchLatestRef(branchName: string) {
|
||||
const baseImageRefUrl = `${ossDomain}/${branchName}/visual-regression-ref.txt`;
|
||||
// get content from baseImageRefText
|
||||
@ -145,6 +162,7 @@ interface IBadCase {
|
||||
* 0 - 1
|
||||
*/
|
||||
weight: number;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
const git = simpleGit();
|
||||
@ -175,7 +193,7 @@ function generateLineReport(
|
||||
currentRef: string,
|
||||
extraCaption?: boolean,
|
||||
) {
|
||||
const { filename, type, targetFilename } = badCase;
|
||||
const { filename, type, targetFilename, reason } = badCase;
|
||||
|
||||
let lineHTMLReport = '';
|
||||
if (type === 'changed') {
|
||||
@ -216,7 +234,7 @@ function generateLineReport(
|
||||
extraCaption,
|
||||
),
|
||||
`⛔️⛔️⛔️ Missing ⛔️⛔️⛔️`,
|
||||
`🚨🚨🚨 Removed 🚨🚨🚨`,
|
||||
reason || `🚨🚨🚨 Removed 🚨🚨🚨`,
|
||||
].join(' | ');
|
||||
lineHTMLReport += ' |\n';
|
||||
} else if (type === 'added') {
|
||||
@ -347,6 +365,8 @@ async function boot() {
|
||||
|
||||
const currentImgSourceDir = path.resolve(ROOT_DIR, './imageSnapshots');
|
||||
|
||||
const errorHashMap = await readErrorJsonl(path.resolve(currentImgSourceDir, 'error.jsonl'));
|
||||
|
||||
// save diff images(x3) to reportDir
|
||||
const diffImgReportDir = path.resolve(REPORT_DIR, './images/diff');
|
||||
const baseImgReportDir = path.resolve(REPORT_DIR, './images/base');
|
||||
@ -391,6 +411,7 @@ async function boot() {
|
||||
type: 'removed',
|
||||
filename: compareImgName,
|
||||
weight: 1,
|
||||
reason: errorHashMap[compareImgName],
|
||||
} as IBadCase;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user