ogMeta.test.js 4.4 KB
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

import {
  applyOgMeta,
  buildOgImageUrl,
  installOgMetaSync,
  resetOgMeta,
  resetOgMetaManagerState,
} from '../ogMeta'

const createRouterMock = (initialRoute = { meta: { title: '首页' }, fullPath: '/#/' }) => {
  let afterEachHook = null

  return {
    currentRoute: { value: initialRoute },
    afterEach: vi.fn(handler => {
      afterEachHook = handler
      return () => {
        afterEachHook = null
      }
    }),
    triggerAfterEach(route) {
      this.currentRoute.value = route
      afterEachHook?.(route)
    },
  }
}

const flushMutationObserver = async () => {
  await new Promise(resolve => setTimeout(resolve, 0))
}

describe('ogMeta', () => {
  beforeEach(() => {
    document.head.innerHTML = `
      <meta property="og:description" content="默认描述">
      <title>美乐爱觉</title>
    `
    document.title = '美乐爱觉'
    window.location.hash = '#/'
    resetOgMetaManagerState()
  })

  afterEach(() => {
    resetOgMetaManagerState()
  })

  it('复用现有 og:description 并在 reset 时恢复初始值', () => {
    applyOgMeta({
      title: '课程详情',
      description: '课程副标题',
      image: 'https://cdn.ipadbiz.cn/mlaj/course-cover.png',
      url: 'https://example.com/#/courses/1',
    })

    const descriptionMetas = document.head.querySelectorAll('meta[property="og:description"]')

    expect(descriptionMetas).toHaveLength(1)
    expect(descriptionMetas[0].getAttribute('content')).toBe('课程副标题')
    expect(document.head.querySelector('meta[property="og:title"]')?.getAttribute('content')).toBe(
      '课程详情'
    )
    expect(
      document.head.querySelector('meta[property="og:image"]')?.getAttribute('content')
    ).toContain('imageMogr2/thumbnail/400x/strip/quality/70')

    resetOgMeta()

    expect(
      document.head.querySelector('meta[property="og:description"]')?.getAttribute('content')
    ).toBe('默认描述')
    expect(document.head.querySelector('meta[property="og:title"]')).toBeNull()
    expect(document.head.querySelector('meta[property="og:image"]')).toBeNull()
    expect(document.head.querySelector('meta[property="og:url"]')).toBeNull()
  })

  it('在路由切换和 document.title 变化时同步 og:title 与 og:url', async () => {
    const router = createRouterMock({
      meta: { title: '课程详情' },
      fullPath: '/courses/1',
    })

    const teardown = installOgMetaSync(router)

    await flushMutationObserver()

    expect(document.head.querySelector('meta[property="og:title"]')?.getAttribute('content')).toBe(
      '美乐爱觉'
    )
    expect(document.head.querySelector('meta[property="og:url"]')?.getAttribute('content')).toBe(
      window.location.href
    )

    window.location.hash = '#/courses/2'
    router.triggerAfterEach({
      meta: { title: '课程详情' },
      fullPath: '/courses/2',
    })

    await flushMutationObserver()

    expect(document.head.querySelector('meta[property="og:title"]')?.getAttribute('content')).toBe(
      '课程详情'
    )
    expect(document.head.querySelector('meta[property="og:url"]')?.getAttribute('content')).toBe(
      window.location.href
    )

    document.title = '高阶课程'
    await flushMutationObserver()

    expect(document.head.querySelector('meta[property="og:title"]')?.getAttribute('content')).toBe(
      '高阶课程'
    )
    expect(
      document.head.querySelector('meta[property="og:description"]')?.getAttribute('content')
    ).toBe('高阶课程')

    teardown()
  })

  it('支持通过异步 resolver 合并描述与图片', async () => {
    const router = createRouterMock({
      meta: { title: '课程详情' },
      fullPath: '/courses/1',
    })

    installOgMetaSync(router, {
      resolver: vi.fn(() =>
        Promise.resolve({
          description: '课程副标题',
          image: 'https://cdn.ipadbiz.cn/mlaj/course-cover.png',
        })
      ),
    })

    await flushMutationObserver()

    expect(
      document.head.querySelector('meta[property="og:description"]')?.getAttribute('content')
    ).toBe('课程副标题')
    expect(
      document.head.querySelector('meta[property="og:image"]')?.getAttribute('content')
    ).toContain('imageMogr2/thumbnail/400x/strip/quality/70')
  })

  it('为非 cdn.ipadbiz.cn 图片保持原始地址', () => {
    expect(buildOgImageUrl('https://example.com/image.png')).toBe('https://example.com/image.png')
  })
})