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

/** 搜索结果页走查:全局/分类内、回显、空态、结果、提交跳转 */
const KW = encodeURIComponent('序列化');
const KW_RAW = '序列化';
const isDesktop = (w: number) => w > 900;

test.describe('搜索结果页', () => {
  test.beforeEach(async () => { await new Promise((r) => setTimeout(r, 300)); });

  test('全局搜索有结果:计数/条目/关键词回显/无错误', async ({ page }) => {
    const errors = collectErrors(page);
    const resp = await page.goto(`/search-${KW}`);
    expect(resp!.status(), `搜索页返回 ${resp!.status()}`).toBe(200);
    // 回显关键词(搜索页大搜索框,区别于头部小搜索框)
    await expect(page.locator('.search-hero input[name="key"]')).toHaveValue(KW_RAW);
    // 结果条目
    const n = await page.locator('.search-result-item').count();
    expect(n, '关键词"序列化"应命中结果').toBeGreaterThan(0);
    // 高亮 mark 或标题含关键词
    const marks = await page.locator('.sr-title mark').count();
    expect(marks, '结果标题应有 <mark> 高亮').toBeGreaterThan(0);
    expectNoErrors(errors);
  });

  test('搜索无结果:空态提示可见', async ({ page }) => {
    const resp = await page.goto(`/search-${encodeURIComponent('zzz不存在的词条')}`);
    expect(resp!.status()).toBe(200);
    await expect(page.locator('.search-empty')).toBeVisible();
  });

  test('分类内搜索:/core-关键词 命中本分类结果', async ({ page }) => {
    const resp = await page.goto(`/core-${KW}`);
    expect(resp!.status(), `分类内搜索返回 ${resp!.status()}`).toBe(200);
    // 分类内搜索框回显关键词且 data-action=/core-
    await expect(page.locator('.search-hero input[name="key"]')).toHaveValue(KW_RAW);
  });

  test('首页搜索提交跳转 /search-key(site.js 拦截)', async ({ page }) => {
    if (!isDesktop(page.viewportSize()!.width)) return; // 移动 header 搜索框隐藏
    await page.goto('/');
    const input = page.locator('.site-header input[name="key"]');
    await expect(input).toBeVisible();
    await input.fill('XCode');
    await input.press('Enter');
    await page.waitForURL(/search-XCode/);
  });

  test('分类列表页头搜索提交跳转 /{code}-key', async ({ page }) => {
    if (!isDesktop(page.viewportSize()!.width)) return;
    await page.goto('/core');
    const form = page.locator('.head-search');
    if ((await form.count()) === 0) return;
    const input = form.locator('input[name="key"]');
    await input.fill('缓冲');
    await input.press('Enter');
    await page.waitForURL(/\/core-%E7%BC%93%E5%86%B2/);
  });

  test('搜索词长尾:标题与摘要高亮同时存在', async ({ page }) => {
    await page.goto(`/search-${KW}`);
    const descMark = await page.locator('.sr-desc mark').count();
    const titleMark = await page.locator('.sr-title mark').count();
    expect(titleMark + descMark, '至少标题或摘要高亮').toBeGreaterThan(0);
  });

  test('空格多关键词:拆词 AND 页面正常、回显原文,标题摘要关键词有逐词高亮', async ({ page }) => {
    // 空格分隔多词走 /search-词1%20词2;本地/部署后新版已是拆词 AND(词间 And、词内多字段 Or)
    // 命中词可能仅在正文(摘要不显示),故仅当标题/摘要出现关键词时才要求有 <mark> 逐词高亮
    const words = '代理 星尘';
    const wordList = words.split(' ');
    const errors = collectErrors(page);
    const resp = await page.goto(`/search-${encodeURIComponent(words)}`);
    expect(resp!.status(), `多关键词搜索页返回 ${resp!.status()}`).toBe(200);
    await expect(page.locator('.search-hero input[name="key"]')).toHaveValue(words);

    const n = await page.locator('.search-result-item').count();
    if (n > 0) {
      // 标题或摘要中是否出现了任一关键词(命中词可能只在正文)
      const shown = await page.locator('.sr-title, .sr-desc').evaluateAll(
        (els, ws) => els.some((el) => ws.some((w) => el.textContent!.includes(w))), wordList);
      if (shown) {
        const marks = await page.locator('.sr-title mark, .sr-desc mark').count();
        expect(marks, '标题/摘要出现关键词时应逐词 <mark> 高亮').toBeGreaterThan(0);
      }
    }
    expectNoErrors(errors);
  });
});