]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: test for legacy component compat
authorEvan You <yyx990803@gmail.com>
Thu, 29 Apr 2021 19:51:37 +0000 (15:51 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 29 Apr 2021 19:51:37 +0000 (15:51 -0400)
packages/runtime-core/src/compat/__tests__/componentAsync.spec.ts [new file with mode: 0644]
packages/runtime-core/src/compat/__tests__/componentFunctional.spec.ts [new file with mode: 0644]
packages/runtime-core/src/compat/__tests__/vModel.spec.ts [new file with mode: 0644]
packages/runtime-core/src/compat/component.ts
packages/runtime-core/src/compat/componentAsync.ts [new file with mode: 0644]
packages/runtime-core/src/compat/componentFunctional.ts [new file with mode: 0644]
packages/runtime-core/src/compat/instance.ts

diff --git a/packages/runtime-core/src/compat/__tests__/componentAsync.spec.ts b/packages/runtime-core/src/compat/__tests__/componentAsync.spec.ts
new file mode 100644 (file)
index 0000000..1f63033
--- /dev/null
@@ -0,0 +1,62 @@
+import Vue from '@vue/compat'
+import {
+  DeprecationTypes,
+  deprecationData,
+  toggleDeprecationWarning
+} from '../compatConfig'
+
+beforeEach(() => {
+  toggleDeprecationWarning(true)
+  Vue.configureCompat({
+    MODE: 2,
+    GLOBAL_MOUNT: 'suppress-warning'
+  })
+})
+
+afterEach(() => {
+  toggleDeprecationWarning(false)
+  Vue.configureCompat({ MODE: 3 })
+})
+
+const timeout = (n: number) => new Promise(r => setTimeout(r, n))
+
+describe('COMPONENT_ASYNC', () => {
+  test('resolve/reject', async () => {
+    let resolve: any
+    const comp = (r: any) => {
+      resolve = r
+    }
+    const vm = new Vue({
+      template: `<div><comp/></div>`,
+      components: { comp }
+    }).$mount()
+    expect(vm.$el.innerHTML).toBe(`<!---->`)
+
+    resolve({ template: 'foo' })
+    await timeout(0)
+    expect(vm.$el.innerHTML).toBe(`foo`)
+
+    expect(
+      (deprecationData[DeprecationTypes.COMPONENT_ASYNC].message as Function)(
+        comp
+      )
+    ).toHaveBeenWarned()
+  })
+
+  test('Promise', async () => {
+    const comp = () => Promise.resolve({ template: 'foo' })
+    const vm = new Vue({
+      template: `<div><comp/></div>`,
+      components: { comp }
+    }).$mount()
+    expect(vm.$el.innerHTML).toBe(`<!---->`)
+    await timeout(0)
+    expect(vm.$el.innerHTML).toBe(`foo`)
+
+    expect(
+      (deprecationData[DeprecationTypes.COMPONENT_ASYNC].message as Function)(
+        comp
+      )
+    ).toHaveBeenWarned()
+  })
+})
diff --git a/packages/runtime-core/src/compat/__tests__/componentFunctional.spec.ts b/packages/runtime-core/src/compat/__tests__/componentFunctional.spec.ts
new file mode 100644 (file)
index 0000000..5eceaa1
--- /dev/null
@@ -0,0 +1,61 @@
+import Vue from '@vue/compat'
+import {
+  DeprecationTypes,
+  deprecationData,
+  toggleDeprecationWarning
+} from '../compatConfig'
+
+beforeEach(() => {
+  toggleDeprecationWarning(true)
+  Vue.configureCompat({
+    MODE: 2,
+    GLOBAL_MOUNT: 'suppress-warning'
+  })
+})
+
+afterEach(() => {
+  toggleDeprecationWarning(false)
+  Vue.configureCompat({ MODE: 3 })
+})
+
+test('COMPONENT_FUNCTIONAL', async () => {
+  const func = {
+    name: 'Func',
+    functional: true,
+    props: {
+      x: String
+    },
+    inject: ['foo'],
+    render: (h: any, { data, props, injections, slots }: any) => {
+      return h('div', { id: props.x, class: data.class }, [
+        h('div', { class: 'inject' }, injections.foo),
+        h('div', { class: 'slot' }, slots().default)
+      ])
+    }
+  }
+
+  const vm = new Vue({
+    provide() {
+      return {
+        foo: 123
+      }
+    },
+    components: {
+      func
+    },
+    template: `<func class="foo" x="foo">hello</func>`
+  }).$mount()
+
+  expect(vm.$el.id).toBe('foo')
+  expect(vm.$el.className).toBe('foo')
+  expect(vm.$el.querySelector('.inject').textContent).toBe('123')
+  expect(vm.$el.querySelector('.slot').textContent).toBe('hello')
+  expect(vm.$el.outerHTML).toMatchInlineSnapshot(
+    `"<div id=\\"foo\\" class=\\"foo\\"><div class=\\"inject\\">123</div><div class=\\"slot\\">hello</div></div>"`
+  )
+
+  expect(
+    (deprecationData[DeprecationTypes.COMPONENT_FUNCTIONAL]
+      .message as Function)(func)
+  ).toHaveBeenWarned()
+})
diff --git a/packages/runtime-core/src/compat/__tests__/vModel.spec.ts b/packages/runtime-core/src/compat/__tests__/vModel.spec.ts
new file mode 100644 (file)
index 0000000..e69de29
index 3e89924f35a98604a6ef7f5a058e4c087f2016af..b09d579a59a95e6861b858d3f328a438ca2aa5b7 100644 (file)
@@ -1,22 +1,12 @@
-import { isArray, isFunction, isObject, isPromise } from '@vue/shared'
-import { defineAsyncComponent } from '../apiAsyncComponent'
-import {
-  Component,
-  ComponentInternalInstance,
-  ComponentOptions,
-  FunctionalComponent,
-  getCurrentInstance
-} from '../component'
-import { resolveInjections } from '../componentOptions'
-import { InternalSlots } from '../componentSlots'
-import { isVNode } from '../vnode'
+import { isFunction, isObject } from '@vue/shared'
+import { Component, ComponentInternalInstance } from '../component'
 import {
   checkCompatEnabled,
-  softAssertCompatEnabled,
-  DeprecationTypes
+  DeprecationTypes,
+  softAssertCompatEnabled
 } from './compatConfig'
-import { getCompatListeners } from './instanceListeners'
-import { compatH } from './renderFn'
+import { convertLegacyAsyncComponent } from './componentAsync'
+import { convertLegacyFunctionalComponent } from './componentFunctional'
 
 export function convertLegacyComponent(
   comp: any,
@@ -56,109 +46,3 @@ export function convertLegacyComponent(
 
   return comp
 }
-
-interface LegacyAsyncOptions {
-  component: Promise<Component>
-  loading?: Component
-  error?: Component
-  delay?: number
-  timeout?: number
-}
-
-type LegacyAsyncReturnValue = Promise<Component> | LegacyAsyncOptions
-
-type LegacyAsyncComponent = (
-  resolve?: (res: LegacyAsyncReturnValue) => void,
-  reject?: (reason?: any) => void
-) => LegacyAsyncReturnValue | undefined
-
-const normalizedAsyncComponentMap = new Map<LegacyAsyncComponent, Component>()
-
-function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) {
-  if (normalizedAsyncComponentMap.has(comp)) {
-    return normalizedAsyncComponentMap.get(comp)!
-  }
-
-  // we have to call the function here due to how v2's API won't expose the
-  // options until we call it
-  let resolve: (res: LegacyAsyncReturnValue) => void
-  let reject: (reason?: any) => void
-  const fallbackPromise = new Promise<Component>((r, rj) => {
-    ;(resolve = r), (reject = rj)
-  })
-
-  const res = comp(resolve!, reject!)
-
-  let converted: Component
-  if (isPromise(res)) {
-    converted = defineAsyncComponent(() => res)
-  } else if (isObject(res) && !isVNode(res) && !isArray(res)) {
-    converted = defineAsyncComponent({
-      loader: () => res.component,
-      loadingComponent: res.loading,
-      errorComponent: res.error,
-      delay: res.delay,
-      timeout: res.timeout
-    })
-  } else if (res == null) {
-    converted = defineAsyncComponent(() => fallbackPromise)
-  } else {
-    converted = comp as any // probably a v3 functional comp
-  }
-  normalizedAsyncComponentMap.set(comp, converted)
-  return converted
-}
-
-const normalizedFunctionalComponentMap = new Map<
-  ComponentOptions,
-  FunctionalComponent
->()
-
-export const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
-  get(target, key: string) {
-    const slot = target[key]
-    return slot && slot()
-  }
-}
-
-function convertLegacyFunctionalComponent(comp: ComponentOptions) {
-  if (normalizedFunctionalComponentMap.has(comp)) {
-    return normalizedFunctionalComponentMap.get(comp)!
-  }
-
-  const legacyFn = comp.render as any
-
-  const Func: FunctionalComponent = (props, ctx) => {
-    const instance = getCurrentInstance()!
-
-    const legacyCtx = {
-      props,
-      children: instance.vnode.children || [],
-      data: instance.vnode.props || {},
-      scopedSlots: ctx.slots,
-      parent: instance.parent && instance.parent.proxy,
-      slots() {
-        return new Proxy(ctx.slots, legacySlotProxyHandlers)
-      },
-      get listeners() {
-        return getCompatListeners(instance)
-      },
-      get injections() {
-        if (comp.inject) {
-          const injections = {}
-          resolveInjections(comp.inject, {})
-          return injections
-        }
-        return {}
-      }
-    }
-    return legacyFn(compatH, legacyCtx)
-  }
-  Func.props = comp.props
-  Func.displayName = comp.name
-  // v2 functional components do not inherit attrs
-  Func.inheritAttrs = false
-
-  normalizedFunctionalComponentMap.set(comp, Func)
-  return Func
-}
diff --git a/packages/runtime-core/src/compat/componentAsync.ts b/packages/runtime-core/src/compat/componentAsync.ts
new file mode 100644 (file)
index 0000000..0a0dee7
--- /dev/null
@@ -0,0 +1,56 @@
+import { isArray, isObject, isPromise } from '@vue/shared'
+import { defineAsyncComponent } from '../apiAsyncComponent'
+import { Component } from '../component'
+import { isVNode } from '../vnode'
+
+interface LegacyAsyncOptions {
+  component: Promise<Component>
+  loading?: Component
+  error?: Component
+  delay?: number
+  timeout?: number
+}
+
+type LegacyAsyncReturnValue = Promise<Component> | LegacyAsyncOptions
+
+type LegacyAsyncComponent = (
+  resolve?: (res: LegacyAsyncReturnValue) => void,
+  reject?: (reason?: any) => void
+) => LegacyAsyncReturnValue | undefined
+
+const normalizedAsyncComponentMap = new Map<LegacyAsyncComponent, Component>()
+
+export function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) {
+  if (normalizedAsyncComponentMap.has(comp)) {
+    return normalizedAsyncComponentMap.get(comp)!
+  }
+
+  // we have to call the function here due to how v2's API won't expose the
+  // options until we call it
+  let resolve: (res: LegacyAsyncReturnValue) => void
+  let reject: (reason?: any) => void
+  const fallbackPromise = new Promise<Component>((r, rj) => {
+    ;(resolve = r), (reject = rj)
+  })
+
+  const res = comp(resolve!, reject!)
+
+  let converted: Component
+  if (isPromise(res)) {
+    converted = defineAsyncComponent(() => res)
+  } else if (isObject(res) && !isVNode(res) && !isArray(res)) {
+    converted = defineAsyncComponent({
+      loader: () => res.component,
+      loadingComponent: res.loading,
+      errorComponent: res.error,
+      delay: res.delay,
+      timeout: res.timeout
+    })
+  } else if (res == null) {
+    converted = defineAsyncComponent(() => fallbackPromise)
+  } else {
+    converted = comp as any // probably a v3 functional comp
+  }
+  normalizedAsyncComponentMap.set(comp, converted)
+  return converted
+}
diff --git a/packages/runtime-core/src/compat/componentFunctional.ts b/packages/runtime-core/src/compat/componentFunctional.ts
new file mode 100644 (file)
index 0000000..80af32a
--- /dev/null
@@ -0,0 +1,63 @@
+import {
+  ComponentOptions,
+  FunctionalComponent,
+  getCurrentInstance
+} from '../component'
+import { resolveInjections } from '../componentOptions'
+import { InternalSlots } from '../componentSlots'
+import { getCompatListeners } from './instanceListeners'
+import { compatH } from './renderFn'
+
+const normalizedFunctionalComponentMap = new Map<
+  ComponentOptions,
+  FunctionalComponent
+>()
+
+export const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
+  get(target, key: string) {
+    const slot = target[key]
+    return slot && slot()
+  }
+}
+
+export function convertLegacyFunctionalComponent(comp: ComponentOptions) {
+  if (normalizedFunctionalComponentMap.has(comp)) {
+    return normalizedFunctionalComponentMap.get(comp)!
+  }
+
+  const legacyFn = comp.render as any
+
+  const Func: FunctionalComponent = (props, ctx) => {
+    const instance = getCurrentInstance()!
+
+    const legacyCtx = {
+      props,
+      children: instance.vnode.children || [],
+      data: instance.vnode.props || {},
+      scopedSlots: ctx.slots,
+      parent: instance.parent && instance.parent.proxy,
+      slots() {
+        return new Proxy(ctx.slots, legacySlotProxyHandlers)
+      },
+      get listeners() {
+        return getCompatListeners(instance)
+      },
+      get injections() {
+        if (comp.inject) {
+          const injections = {}
+          resolveInjections(comp.inject, injections)
+          return injections
+        }
+        return {}
+      }
+    }
+    return legacyFn(compatH, legacyCtx)
+  }
+  Func.props = comp.props
+  Func.displayName = comp.name
+  // v2 functional components do not inherit attrs
+  Func.inheritAttrs = false
+
+  normalizedFunctionalComponentMap.set(comp, Func)
+  return Func
+}
index 46e3ef6313a72ed06b76ce424ffa7549638a7fb2..065e357bec58635f36a48432f14cd40542c22f3d 100644 (file)
@@ -19,7 +19,7 @@ import {
 import { off, on, once } from './instanceEventEmitter'
 import { getCompatListeners } from './instanceListeners'
 import { shallowReadonly } from '@vue/reactivity'
-import { legacySlotProxyHandlers } from './component'
+import { legacySlotProxyHandlers } from './componentFunctional'
 import { compatH } from './renderFn'
 import { createCommentVNode, createTextVNode } from '../vnode'
 import { renderList } from '../helpers/renderList'