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