Repository: vuejs/vue-next
File: packages/runtime-dom/__tests__/modules/class.spec.ts (TypeScript)

Code before:
      [['x', {}], 'foo x'],
      [undefined, 'foo']
    ])
  })

  test('class merge between parent and child', () => {
    const root = document.createElement('div')
    type ClassItem = {
      value: string | object | string[]
    }
    const childClass: ClassItem = { value: 'd' }

    const child = () => h('div', { class: ['c', childClass.value] })

    const parentClass: ClassItem = { value: 'b' }
    const parent = () => h(child, { class: ['a', parentClass.value] })

    render(h(parent), root)
    expect(root.children[0].className).toBe('c a d b')

    parentClass.value = 'e'
    render(h(parent), root)
    expect(root.children[0].className).toBe('c a d e')

    parentClass.value = 'f'
    render(h(parent), root)
    expect(root.children[0].className).toBe('c a f e')

    parentClass.value = { foo: true }
    childClass.value = ['bar', 'baz']
    expect(root.children[0].className).toBe('c a bar baz foo')
  })

  test('class merge between parent and child with props', () => {
    const root = document.createElement('div')
    type ClassItem = {
      value: string | object | string[]
    }
    const childClass: ClassItem = { value: 'd' }

    const child = {
      setup() {
        return () => h('div', { class: ['c', childClass.value] })
      }
    }

    const parentClass: ClassItem = { value: 'b' }
    const parent = {
      setup() {
        return () => h(child, { class: ['a', parentClass.value] })
      }

Review comment (bug):
The only difference between the 2 cases is that `child2` has an empty `props` declaration, therefore it successfully triggered class merging while the other one didn't.
I didn't see anything about this behavior in the `inheritAttrs` RFS.
Seems a bug in the current implementation?
@yyx990803 

Code after:
      [['x', { y: true, z: true }], 'foo x y z'],
      [['x', { y: true, z: false }], 'foo x y'],
      [['f', { z: true }], 'foo f z'],
      [['l', 'f', { n: true, z: true }], 'foo l f n z'],
      [['x', {}], 'foo x'],
      [undefined, 'foo']
    ])
  })

  test('class merge between parent and child', () => {
    const root = document.createElement('div')

    const childClass: ClassItem = { value: 'd' }
    const child = {
      props: {},
      render: () => h('div', { class: ['c', childClass.value] })
    }

    const parentClass: ClassItem = { value: 'b' }
    const parent = {
      props: {},
      render: () => h(child, { class: ['a', parentClass.value] })
    }

    render(h(parent), root)
    expect(root.children[0].className).toBe('c d a b')

    parentClass.value = 'e'
    // the `foo` here is just for forcing parent to be updated
    // (otherwise it's skipped since its props never change)
    render(h(parent, { foo: 1 }), root)
    expect(root.children[0].className).toBe('c d a e')

    parentClass.value = 'f'
    render(h(parent, { foo: 2 }), root)
    expect(root.children[0].className).toBe('c d a f')

    parentClass.value = { foo: true }
    childClass.value = ['bar', 'baz']
    render(h(parent, { foo: 3 }), root)
    expect(root.children[0].className).toBe('c bar baz a foo')
  })

  test('class merge between multiple nested components sharing same element', () => {
    const component1 = createComponent({
      props: {},
      render() {
        return this.$slots.default()[0]
      }
    })