Back to Repositories

Validating Template Scope Compilation in uni-app

This test suite evaluates the compiler’s scope handling in uni-app, focusing on v-for directives and conditional rendering. It verifies the correct transformation of Vue template syntax to miniprogram compatible code while maintaining proper variable scoping and data binding.

Test Coverage Overview

The test suite provides comprehensive coverage of scope-related functionality in the uni-app compiler:
  • v-for directive implementation and nested v-for scenarios
  • Integration of v-for with conditional rendering (v-if/v-else)
  • Complex data binding and event handling within loops
  • Multiple levels of nested scope handling
  • Edge cases involving scope isolation and variable accessibility

Implementation Analysis

The testing approach employs Jest framework features to validate compiler transformations. It uses assertion patterns to compare input Vue templates with expected miniprogram output and runtime functions. The tests verify template compilation, scope maintenance, and proper variable binding across different nesting levels.

Technical Details

Testing infrastructure includes:
  • Jest test runner for execution
  • Custom assert utility for template comparison
  • Template transformation validation
  • Runtime function verification
  • Scope isolation checking

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices through systematic validation of compiler behavior. It includes granular test cases, clear input/output patterns, and comprehensive coverage of edge cases. The organization follows a logical progression from simple to complex scenarios, ensuring thorough validation of scope-related functionality.

dcloudio/uni-app

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

            
import { assert } from './testUtils'

describe('compiler: scope', () => {
  test('v-for', () => {
    assert(
      `<view v-for="item in items" :key="item.id" :class="{red: item.isRed}" @longpress="longpress" @click="onClick(item)">{{item.title}}</view>`,
      `<view wx:for="{{a}}" wx:for-item="item" wx:key="b" class="{{[item.c && 'red']}}" bindlongpress="{{b}}" bindtap="{{item.d}}">{{item.a}}</view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _t(item.title), b: item.id, c: item.isRed ? 1 : '', d: _o($event => _ctx.onClick(item)) }; }), b: _o(_ctx.longpress) }
}`
    )
  })
  test('v-for + v-for', () => {
    assert(
      `<view v-for="item in items" :key="item.id">{{item.title}}{{handle(foo)}}<view v-for="item1 in item.list" :key="item1.id" @click="onClick(item)" @longpress="longpress(item1)">{{item.id}}{{item1.title}}</view></view>`,
      `<view wx:for="{{a}}" wx:for-item="item" wx:key="e">{{item.a}}{{b}}<view wx:for="{{item.b}}" wx:for-item="item1" wx:key="b" bindtap="{{item.d}}" bindlongpress="{{item1.c}}">{{item.c}}{{item1.a}}</view></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _t(item.title), b: _f(item.list, (item1, k1, i1) => { return { a: _t(item1.title), b: item1.id, c: _o($event => _ctx.longpress(item1)) }; }), c: _t(item.id), d: _o($event => _ctx.onClick(item)), e: item.id }; }), b: _t(_ctx.handle(_ctx.foo)) }
}`
    )
    assert(
      `<view v-for="item in items"><view v-for="item1 in item1" :data-id="item.id" :data-title="item1.title"/></view>`,
      `<view wx:for="{{a}}" wx:for-item="item"><view wx:for="{{item.a}}" wx:for-item="item1" data-id="{{item.b}}" data-title="{{item1.a}}"/></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items, (item, k0, i0) => { return { a: _f(_ctx.item1, (item1, k1, i1) => { return { a: item1.title }; }), b: item.id }; }) }
}`
    )
    assert(
      `<view v-for="(item,weekIndex) in weeks" :key="weekIndex" :data-id="item.id"><view v-for="(weeks,weeksIndex) in item" :key="weeksIndex" :data-id="weeks.id"/></view>`,
      `<view wx:for="{{a}}" wx:for-item="item" wx:key="b" data-id="{{item.c}}"><view wx:for="{{item.a}}" wx:for-item="weeks" wx:key="a" data-id="{{weeks.b}}"/></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.weeks, (item, weekIndex, i0) => { return { a: _f(item, (weeks, weeksIndex, i1) => { return { a: weeksIndex, b: weeks.id }; }), b: weekIndex, c: item.id }; }) }
}`
    )
    assert(
      `<view v-for="x in 2"><view v-for="y in 2">{{x+y}}</view></view>`,
      `<view wx:for="{{a}}" wx:for-item="x"><view wx:for="{{x.a}}" wx:for-item="y">{{y.a}}</view></view>`,
      `(_ctx, _cache) => {
  return { a: _f(2, (x, k0, i0) => { return { a: _f(2, (y, k1, i1) => { return { a: _t(x + y) }; }) }; }) }
}`
    )
  })
  test('v-for + v-if', () => {
    assert(
      `<view v-for="item in items"><view v-if="true" :data-id="id"></view></view>`,
      `<view wx:for="{{a}}" wx:for-item="item"><view wx:if="{{true}}" data-id="{{item.a}}"></view></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items, (item, k0, i0) => { return true ? { a: _ctx.id } : {}; }) }
}`
    )
  })
  test('v-for + v-for + v-if', () => {
    assert(
      `<view v-for="s in items1"><view v-for="r in items2"><text v-if="s == 2">s</text></view></view>`,
      `<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{s.a}}" wx:for-item="r"><text wx:if="{{s.b}}">s</text></view></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items1, (s, k0, i0) => { return { a: _f(_ctx.items2, (r, k1, i1) => { return s == 2 ? {} : {}; }), b: s == 2 }; }) }
}`
    )
    assert(
      `<view v-for="s in items1"><view v-for="r in items2"><text v-if="a == 2">s</text></view></view>`,
      `<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{s.a}}" wx:for-item="r"><text wx:if="{{b}}">s</text></view></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items1, (s, k0, i0) => { return { a: _f(_ctx.items2, (r, k1, i1) => { return _ctx.a == 2 ? {} : {}; }) }; }), b: _ctx.a == 2 }
}`
    )
  })
  test('v-for + v-for + v-if + v-else-if', () => {
    assert(
      `<view v-for="s in items1"><view v-for="r in items2"><text v-if="a == 2">a</text><text v-else-if="s == 3">s</text></view></view>`,
      `<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{s.a}}" wx:for-item="r"><text wx:if="{{b}}">a</text><text wx:elif="{{s.b}}">s</text></view></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items1, (s, k0, i0) => { return { a: _f(_ctx.items2, (r, k1, i1) => { return _ctx.a == 2 ? {} : s == 3 ? {} : {}; }), b: s == 3 }; }), b: _ctx.a == 2 }
}`
    )
    assert(
      `<view v-for="s in items1"><view v-for="r in items2"><text v-if="a == 2">a</text><text v-else-if="b == 3">s</text></view></view>`,
      `<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{s.a}}" wx:for-item="r"><text wx:if="{{b}}">a</text><text wx:elif="{{c}}">s</text></view></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items1, (s, k0, i0) => { return { a: _f(_ctx.items2, (r, k1, i1) => { return _ctx.a == 2 ? {} : _ctx.b == 3 ? {} : {}; }) }; }), b: _ctx.a == 2, c: _ctx.b == 3 }
}`
    )
  })
  test('v-for + v-for + v-for + v-if', () => {
    assert(
      `<view v-for="s in items1"><view v-for="r in items2"><view v-for="t in items3"><text v-if="s == 2">s</text></view></view></view>`,
      `<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{s.a}}" wx:for-item="r"><view wx:for="{{r.a}}" wx:for-item="t"><text wx:if="{{s.b}}">s</text></view></view></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items1, (s, k0, i0) => { return { a: _f(_ctx.items2, (r, k1, i1) => { return { a: _f(_ctx.items3, (t, k2, i2) => { return s == 2 ? {} : {}; }) }; }), b: s == 2 }; }) }
}`
    )
    assert(
      `<view v-for="s in items1"><view v-for="r in items2"><view v-for="t in items3"><text v-if="r == 2">s</text></view></view></view>`,
      `<view wx:for="{{a}}" wx:for-item="s"><view wx:for="{{s.a}}" wx:for-item="r"><view wx:for="{{r.a}}" wx:for-item="t"><text wx:if="{{r.b}}">s</text></view></view></view>`,
      `(_ctx, _cache) => {
  return { a: _f(_ctx.items1, (s, k0, i0) => { return { a: _f(_ctx.items2, (r, k1, i1) => { return { a: _f(_ctx.items3, (t, k2, i2) => { return r == 2 ? {} : {}; }), b: r == 2 }; }) }; }) }
}`
    )
  })
  test('v-if', () => {
    assert(
      `<view v-if="ok">{{ok}}</view><view v-else-if="ok1">{{ok1}}</view><view v-else-if="ok2">{{ok2}}</view><view v-else>{{ok3}}</view>`,
      `<view wx:if="{{a}}">{{b}}</view><view wx:elif="{{c}}">{{d}}</view><view wx:elif="{{e}}">{{f}}</view><view wx:else>{{g}}</view>`,
      `(_ctx, _cache) => {
  return _e({ a: _ctx.ok }, _ctx.ok ? { b: _t(_ctx.ok) } : _ctx.ok1 ? { d: _t(_ctx.ok1) } : _ctx.ok2 ? { f: _t(_ctx.ok2) } : { g: _t(_ctx.ok3) }, { c: _ctx.ok1, e: _ctx.ok2 })
}`
    )
  })
  test('v-if + v-for', () => {
    assert(
      `<view v-if="ok"><view v-for="item in items" :key="item.id" :data-title="item.title" :data-foo="foo" @click="onClick"/></view><view v-else-if="ok1"><view v-for="item in items" :key="item.id" :data-title="item.title" :data-foo="foo" @click="onClick"/></view><view v-else><view v-for="item in items" :key="item.id" :data-title="item.title" :data-foo="foo" @click="onClick"/></view>`,
      `<view wx:if="{{a}}"><view wx:for="{{b}}" wx:for-item="item" wx:key="a" data-title="{{item.b}}" data-foo="{{c}}" bindtap="{{d}}"/></view><view wx:elif="{{e}}"><view wx:for="{{f}}" wx:for-item="item" wx:key="a" data-title="{{item.b}}" data-foo="{{g}}" bindtap="{{h}}"/></view><view wx:else><view wx:for="{{i}}" wx:for-item="item" wx:key="a" data-title="{{item.b}}" data-foo="{{j}}" bindtap="{{k}}"/></view>`,
      `(_ctx, _cache) => {
  return _e({ a: _ctx.ok }, _ctx.ok ? { b: _f(_ctx.items, (item, k0, i0) => { return { a: item.id, b: item.title }; }), c: _ctx.foo, d: _o(_ctx.onClick) } : _ctx.ok1 ? { f: _f(_ctx.items, (item, k0, i0) => { return { a: item.id, b: item.title }; }), g: _ctx.foo, h: _o(_ctx.onClick) } : { i: _f(_ctx.items, (item, k0, i0) => { return { a: item.id, b: item.title }; }), j: _ctx.foo, k: _o(_ctx.onClick) }, { e: _ctx.ok1 })
}`
    )
  })
})