Skip to content

自定义比较 css 匹配器

自定义匹配器是一个函数,接收两个参数。

自定义匹配器

下面我们自定义一个匹配器,用来比较两个 css 字符串达到的效果是否一样。

ts
import {
  matcherErrorMessage,
  matcherHint,
  printDiffOrStringify,
  printExpected,
  printReceived,
  printWithType
} from "jest-matcher-utils"

// 自定义匹配器约定的返回类型
interface MatcherResult {
  // 匹配结果
  pass: boolean
  // 匹配失败时给出的相关信息
  message: () => string
}

const removeFormat = (str: string) => str.trim().replace(/[;\s]+/g, "")

// 自定义匹配器
// 把两个 CSS 字符串中的“分号”和“空白”去掉后进行比对,若相等则表示匹配成功

// 匹配器是一个函数
export const cssMatcher = (received: string, expected: string): MatcherResult => {
  const receivedWithoutFormat = removeFormat(received)
  const expectedWithoutFormat = removeFormat(expected)
  const pass = receivedWithoutFormat === expectedWithoutFormat
  // 利用 `jest-matcher-utils` 生成更有可读性的匹配失败信息
  // 比较不同
  const diffMessage = printDiffOrStringify(
    expectedWithoutFormat, // 断言的值
    receivedWithoutFormat, // 接收传入的值
    "Expected CSS", // 断言的值 label
    "Received CSS",// 接收传入的值 label
    true // 为 true 代表会深度比较对象或数组的每个属性或元素。
    // 为 false 只会显示对象或数组的高层结构,不会展开所有嵌套结构
  )
  // matcherHint 第一个参数传入匹配器的名字
  const passMessage = matcherHint(".not.toMatchCss") +
    "\n\n" +
    "Two CSS classes should not be equal while ignoring white-space and semicolon (using ===):\n" +
    diffMessage

  const failMessage = matcherHint(".toMatchCss") +
    "\n\n" +
    "Two CSS classes should be equal while ignoring white-space and semicolon (using ===):\n" +
    diffMessage
  // message 的作用是输出错误信息,并给予提示
  // 不使用 not,pass 为 true 代表通过,不会输出 message。否则输出 message。
  // 使用 not,pass 为 true 被视为不通过,会调用 message 函数,输出 mesage。
  return { pass, message: () => (pass ? passMessage : failMessage) }
}

注册匹配器

ts
import {cssMatcher} from '../customMater/cssMatcher'

// 扩展匹配器
expect.extend({ 
  toMatchCss: cssMatcher,
  toBeFoo(received, expected) {
    const { isNot } = this
    return {
      pass: received === 'foo',
      message: () => `${received} is ${isNot ? ' not' : ''} foo`
    }
  }
})

describe('判断生成 css 是否相同', () => {
  const generatedCss = `
    .clay {
      background-color: #f87171;
    }
  `
  const expectedCss = `.clay { background-color: #f87171; }`
  // .skip 跳过这个测试。因为肯定不通过。
  test.skip('test', () => {
    expect(generatedCss).toBe(expectedCss);
  })
  // 在验证前格式化字符串。但是比较麻烦
  test('test2', () => {
    expect(generatedCss.trim().replace(/[;\s]+/g, ""))
      .toBe(`.clay { background-color: #f87171; }`.trim().replace(/[;\s]+/g, ""))
  })
  // 使用我们的自定义匹配器
  test('test3', () => {
    expect(generatedCss).toMatchCss(expectedCss)
  })
});

ts 支持

官网说建一个 vitest.d.ts 文件,写入如下类型,然后在 tsconfig.json 里包含该声明文件即可。但实际会发现会把原有的类型全部变为 any。

ts
interface CustomMatchers<R = unknown> {
  toMatchCss(): R
}

declare module 'vitest' {
  interface Assertion<T = any> extends CustomMatchers<T> {}
  interface AsymmetricMatchersContaining extends CustomMatchers {}
}

Released under the MIT License.