更新说明文档
大石头 authored at 2022-04-16 15:30:05
3.20 KiB
YuQue
import { test, expect } from '@playwright/test';
import { collectErrors, expectNoErrors } from './helpers';

/** 文章详情页走查:正文/元信息/上下篇/目录锚点一致性/产品页分流 */
const DETAILS = [
  { name: 'core-nav', path: '/core/nav' },
  { name: 'events-talk004', path: '/events/night_talks_004' },
  { name: 'xcode-mysql', path: '/xcode/mysql' },
];
const PRODUCTS = ['/blood/stardust', '/blood/antjob', '/blood/newlife'];

test.describe('文章详情页', () => {
  test.beforeEach(async () => { await new Promise((r) => setTimeout(r, 300)); });

  for (const d of DETAILS) {
    test(`${d.path} 加载:标题/正文/面包屑/无错误`, async ({ page }) => {
      const errors = collectErrors(page);
      const resp = await page.goto(d.path);
      expect(resp!.status(), `${d.path} 返回 ${resp!.status()}`).toBe(200);
      await expect(page.locator('.crumb')).toBeVisible();
      await expect(page.locator('.article-header h1')).not.toBeEmpty();
      await expect(page.locator('.article-content')).toBeVisible();
      expectNoErrors(errors);
    });

    test(`${d.path} 目录锚点一致性(若存在 toc)`, async ({ page }) => {
      await page.goto(d.path);
      const toc = page.locator('.toc');
      const n = await toc.count();
      if (n === 0) return; // 无 toc 属数据决定,跳过
      // 校验 toc 每个 #href 在正文存在对应 id,否则 scrollspy 失效
      const missing = await page.evaluate(() => {
        const out: String[] = [];
        document.querySelectorAll('.toc a[href^="#"]').forEach((a) => {
          const id = a.getAttribute('href')!.slice(1);
          if (!document.getElementById(id)) out.push(id);
        });
        return out;
      });
      expect(missing, `${d.path} 目录锚点指向不存在的 id:${missing}`).toEqual([]);
    });
  }

  test('详情元信息与上下篇结构完整', async ({ page }) => {
    await page.goto('/core/nav');
    const nav = page.locator('.article-nav');
    if ((await nav.count()) > 0) {
      // 上下篇卡内标题不为空(有内容才显示整卡)
      const titles = await page.locator('.article-nav .nav-title').allTextContents();
      expect(titles.join('').trim().length, '上下篇卡片存在但标题为空').toBeGreaterThan(0);
    }
  });

  for (const p of PRODUCTS) {
    test(`产品/宣传页 ${p} 渲染:正文容器存在且无错误`, async ({ page }) => {
      const errors = collectErrors(page);
      const resp = await page.goto(p);
      expect(resp!.status(), `${p} 返回 ${resp!.status()}`).toBe(200);
      await page.waitForTimeout(500);
      // 可能是 product 分流(.product-page) 或普通文章(.article-content)
      const isProduct = (await page.locator('.product-page').count()) > 0;
      const isNormal = (await page.locator('.article-content').count()) > 0;
      expect(isProduct || isNormal, `${p} 无任何正文容器`).toBe(true);
      if (isProduct) {
        // product 全宽:不应有目录/上下篇/文章头部
        expect(await page.locator('.toc').count(), 'product 页不应有目录').toBe(0);
        expect(await page.locator('.article-nav').count(), 'product 页不应有上下篇').toBe(0);
      }
      expectNoErrors(errors);
    });
  }
});