Back to Repositories

Testing Class Binding Transformations in uni-app Compiler

This test suite examines the class transformation functionality in the uni-app compiler, focusing on static and dynamic class bindings in view components. The tests verify various class binding syntaxes and their correct compilation for miniprogram platforms.

Test Coverage Overview

The test suite provides comprehensive coverage of class binding scenarios in uni-app components:

  • Static class name handling and normalization
  • Dynamic v-bind:class implementations
  • Combined static and dynamic class bindings
  • Object and array syntax variations
  • Complex expressions and conditional class applications

Implementation Analysis

The testing approach uses Jest’s describe/test structure with custom assert utilities to validate class transformations. Tests systematically verify the compiler’s handling of different class binding patterns, ensuring correct template-to-function compilation and proper runtime behavior.

Particular attention is paid to template syntax transformation and the generation of appropriate JavaScript runtime code.

Technical Details

Testing infrastructure includes:

  • Custom assert utility for comparing input/output pairs
  • Jest test framework integration
  • TypeScript implementation for type safety
  • Specialized compiler transformation validation
  • Runtime code generation verification

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Systematic test organization by feature category
  • Comprehensive edge case coverage
  • Clear input/output validation patterns
  • Consistent test structure and naming
  • Granular test cases for specific functionality

dcloudio/uni-app

packages/uni-mp-compiler/__tests__/class.spec.ts

            
import { assert } from './testUtils'

describe('compiler: transform class', () => {
  test(`static class`, () => {
    assert(
      `<view class="foo"/>`,
      `<view class="foo"/>`,
      `(_ctx, _cache) => {
  return {}
}`
    )
    assert(
      `<view class="foo bar"/>`,
      `<view class="foo bar"/>`,
      `(_ctx, _cache) => {
  return {}
}`
    )
    assert(
      `<view class="foo  bar"/>`,
      `<view class="foo bar"/>`,
      `(_ctx, _cache) => {
  return {}
}`
    )
    assert(
      `<view class="foo
bar
"/>`,
      `<view class="foo bar"/>`,
      `(_ctx, _cache) => {
  return {}
}`
    )
  })
  test('v-bind:class basic', () => {
    assert(
      `<view :class="foo"/>`,
      `<view class="{{a}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.foo) }
}`
    )
    assert(
      `<view :class="foo | bar"/>`,
      `<view class="{{a}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.foo | _ctx.bar) }
}`
    )
  })
  test('v-bind:class basic + class ', () => {
    assert(
      `<view :class="foo" class="bar"/>`,
      `<view class="{{[a, 'bar']}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.foo) }
}`
    )
    assert(
      `<view class="bar" :class="foo"/>`,
      `<view class="{{['bar', a]}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.foo) }
}`
    )
  })
  test('v-bind:class object syntax', () => {
    assert(
      `<view :class="{ red: \`\${isRed}\` }"/>`,
      `<view class="{{[a && 'red']}}"/>`,
      `(_ctx, _cache) => {
  return { a: \`\${_ctx.isRed}\` ? 1 : '' }
}`
    )
    assert(
      `<view :class="{ a: 1, b: 0, c: true, d: false, e: null, f: undefined, g: ok, h: handle(ok), i: ok>1, j, [k]:1, [l]:m, ...n, ...{a:true}, ...{b:o} }"/>`,
      `<view class="{{['a', 'c', a && 'g', b && 'h', c && 'i', d && 'j', e, g && f, h, i, j]}}"/>`,
      `(_ctx, _cache) => {
  return { a: _ctx.ok ? 1 : '', b: _ctx.handle(_ctx.ok) ? 1 : '', c: _ctx.ok > 1 ? 1 : '', d: _ctx.j ? 1 : '', e: _ctx.k, f: _ctx.l, g: _ctx.m ? 1 : '', h: _n(_ctx.n), i: _n({ a: true }), j: _n({ b: _ctx.o }) }
}`
    )
  })
  test('v-bind:class object syntax + class', () => {
    assert(
      `<view :class="{ red: isRed }" class="foo  bar"/>`,
      `<view class="{{[a && 'red', 'foo', 'bar']}}"/>`,
      `(_ctx, _cache) => {
  return { a: _ctx.isRed ? 1 : '' }
}`
    )
    assert(
      `<view class="foo  bar" :class="{ red: isRed }"/>`,
      `<view class="{{['foo', 'bar', a && 'red']}}"/>`,
      `(_ctx, _cache) => {
  return { a: _ctx.isRed ? 1 : '' }
}`
    )
    assert(
      `<view :class="{ a: 1, b: 0, c: true, d: false, e: null, f: undefined, g: ok, h: handle(ok), i: ok>1, j, [k]:1, [l]:m, ...n, ...{a:true}, ...{b:o} }" class="foo  bar"/>`,
      `<view class="{{['a', 'c', a && 'g', b && 'h', c && 'i', d && 'j', e, g && f, h, i, j, 'foo', 'bar']}}"/>`,
      `(_ctx, _cache) => {
  return { a: _ctx.ok ? 1 : '', b: _ctx.handle(_ctx.ok) ? 1 : '', c: _ctx.ok > 1 ? 1 : '', d: _ctx.j ? 1 : '', e: _ctx.k, f: _ctx.l, g: _ctx.m ? 1 : '', h: _n(_ctx.n), i: _n({ a: true }), j: _n({ b: _ctx.o }) }
}`
    )
    assert(
      `<view class="foo  bar" :class="{ a: 1, b: 0, c: true, d: false, e: null, f: undefined, g: ok, h: handle(ok), i: ok>1, j, [k]:1, [l]:m, ...n, ...{a:true}, ...{b:o} }"/>`,
      `<view class="{{['foo', 'bar', 'a', 'c', a && 'g', b && 'h', c && 'i', d && 'j', e, g && f, h, i, j]}}"/>`,
      `(_ctx, _cache) => {
  return { a: _ctx.ok ? 1 : '', b: _ctx.handle(_ctx.ok) ? 1 : '', c: _ctx.ok > 1 ? 1 : '', d: _ctx.j ? 1 : '', e: _ctx.k, f: _ctx.l, g: _ctx.m ? 1 : '', h: _n(_ctx.n), i: _n({ a: true }), j: _n({ b: _ctx.o }) }
}`
    )
  })
  test('v-bind:class array syntax', () => {
    assert(
      `<view :class="[classA, \`\${classB}\`]"/>`,
      `<view class="{{[a, b]}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.classA), b: _n(\`\${_ctx.classB}\`) }
}`
    )
    assert(
      `<view :class="[classA, classB]"/>`,
      `<view class="{{[a, b]}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.classA), b: _n(_ctx.classB) }
}`
    )
    assert(
      `<view :class="[classA, classB, { classC: isC, classD: isD }, 'classE', isF ? 'classF' : '', isG && 'classG', ...classH, ...[classI,classJ], handle(classK) ]"/>`,
      `<view class="{{[a, b, c, 'classE', d, e, f, g, h]}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.classA), b: _n(_ctx.classB), c: _n({ classC: _ctx.isC, classD: _ctx.isD }), d: _n(_ctx.isF ? 'classF' : ''), e: _n(_ctx.isG && 'classG'), f: _n(_ctx.classH), g: _n([_ctx.classI, _ctx.classJ]), h: _n(_ctx.handle(_ctx.classK)) }
}`
    )
  })
  test('v-bind:class array syntax + class', () => {
    assert(
      `<view :class="[classA, classB]" class="foo  bar"/>`,
      `<view class="{{[a, b, 'foo', 'bar']}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.classA), b: _n(_ctx.classB) }
}`
    )
    assert(
      `<view class="foo  bar" :class="[classA, classB]"/>`,
      `<view class="{{['foo', 'bar', a, b]}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.classA), b: _n(_ctx.classB) }
}`
    )
    assert(
      `<view :class="[classA, classB, { classC: isC, classD: isD }, 'classE', isF ? 'classF' : '', isG && 'classG', ...classH, ...[classI,classJ], handle(classK) ]" class="foo  bar"/>`,
      `<view class="{{[a, b, c, 'classE', d, e, f, g, h, 'foo', 'bar']}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.classA), b: _n(_ctx.classB), c: _n({ classC: _ctx.isC, classD: _ctx.isD }), d: _n(_ctx.isF ? 'classF' : ''), e: _n(_ctx.isG && 'classG'), f: _n(_ctx.classH), g: _n([_ctx.classI, _ctx.classJ]), h: _n(_ctx.handle(_ctx.classK)) }
}`
    )
    assert(
      `<view class="foo  bar" :class="[classA, classB, { classC: isC, classD: isD }, 'classE', isF ? 'classF' : '', isG && 'classG', ...classH, ...[classI,classJ], handle(classK) ]"/>`,
      `<view class="{{['foo', 'bar', a, b, c, 'classE', d, e, f, g, h]}}"/>`,
      `(_ctx, _cache) => {
  return { a: _n(_ctx.classA), b: _n(_ctx.classB), c: _n({ classC: _ctx.isC, classD: _ctx.isD }), d: _n(_ctx.isF ? 'classF' : ''), e: _n(_ctx.isG && 'classG'), f: _n(_ctx.classH), g: _n([_ctx.classI, _ctx.classJ]), h: _n(_ctx.handle(_ctx.classK)) }
}`
    )
  })
})