import { test, request } from '@playwright/test';
/**
* HTTP 层全站审计(node 客户端不受生产 Bot/UA 降级影响,稳定返回新版)
* 逐页检查结构完整性,输出审计指标
*/
const PAGES: Array<[String, String]> = [
['首页', '/'],
['分类core', '/core'],
['分类xcode', '/xcode'],
['分类cube', '/cube'],
['分类blood', '/blood'],
['分类iot', '/iot'],
['分类tech', '/tech'],
['分类events', '/events'],
['core分页2', '/core-2'],
['详情nav', '/core/nav'],
['详情talk', '/events/night_talks_004'],
['产品newlife', '/blood/newlife'],
['产品stardust', '/blood/stardust'],
['搜索结果', '/search-%E5%BA%8F%E5%88%97%E5%8C%96'],
['搜索空态', '/search-%E4%B8%8D%E5%AD%98%E5%9C%A8%E7%9A%84%E8%AF%8D%E6%9D%A1zz'],
['分类内搜索', '/core-%E5%BA%8F%E5%88%97%E5%8C%96'],
];
test.describe('生产全站 HTML 审计', () => {
for (const [name, path] of PAGES) {
test(`审计 ${name}`, async () => {
const ctx = await request.newContext({ extraHTTPHeaders: { 'cache-control': 'no-cache' } });
const resp = await ctx.get(`https://newlifex.com${path}`);
const status = resp.status();
const ct = resp.headers()['content-type'] || '';
const body = ct.includes('html') ? await resp.text() : '';
const has = (re: RegExp) => re.test(body);
const count = (re: RegExp) => (body.match(re) || []).length;
const title = (body.match(/<title>([^<]*)<\/title>/) || [])[1]?.trim() || '';
const isNew = has(/f-grid/) && has(/学无先后达者为师/);
const isLimited = has(/访问太频繁/);
const out: String[] = [
`status=${status}`,
`NEW=${isNew}`,
isLimited ? 'LIMITED' : '',
`title=${title.slice(0, 40)}`,
];
if (isNew) {
// 新版才做结构指标
out.push(`articleCard=${count(/class="[^"]*article-card/g)}`);
out.push(`badgeTop=${count(/badge-top/g)}`);
out.push(`pagination=${has(/class="[^"]*pagination/)}`);
out.push(`mark=${count(/<mark>/g)}`);
out.push(`toc=${has(/class="[^"]*toc/)}`);
out.push(`projectCard=${count(/class="[^"]*project-card/g)}`);
out.push(`bookCard=${count(/class="[^"]*book-card/g)}`);
out.push(`latestItem=${count(/class="[^"]*latest-item/g)}`);
out.push(`hero=${has(/class="[^"]*hero/)}`);
out.push(`searchEmpty=${has(/search-empty/)}`);
out.push(`crumb=${has(/class="[^"]*crumb/)}`);
}
// eslint-disable-next-line no-console
console.log(`>>> ${name} ${path}\n ${out.join(' | ')}`);
await ctx.dispose();
await new Promise((r) => setTimeout(r, 900));
});
}
});
|