Back to Repositories

Testing Vue Component Import Transformations in dcloudio/uni-app

This test suite evaluates the functionality of Vue component imports and transformations in the uni-app framework, focusing on global and local component registration patterns. It verifies the correct handling of component imports, dynamic loading, and proper integration with the mini-program architecture.

Test Coverage Overview

The test suite provides comprehensive coverage of Vue component import scenarios in both global and local contexts.

Key areas tested include:
  • Basic component registration and transformation
  • TypeScript support for component imports
  • Easycom component integration
  • Unplugin-vue-components compatibility
  • PascalCase component naming conventions
  • Setup syntax component handling

Implementation Analysis

The testing approach utilizes Jest’s describe/test structure to organize test cases into logical groups for global and local component scenarios. The implementation leverages mock functions for path resolution and dynamic imports, ensuring isolated testing of transformation logic.

Key patterns include component name normalization, path resolution, and AST-based code analysis for accurate component registration detection.

Technical Details

Testing tools and configuration:
  • Jest as the primary testing framework
  • AST parsing for JavaScript/TypeScript analysis
  • Mock implementations for file system operations
  • Custom utilities for path normalization
  • Transform functions for dynamic import handling

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices through comprehensive test case organization and thorough edge case coverage.

Notable practices include:
  • Modular test case organization
  • Consistent use of mock functions
  • Clear test case descriptions
  • Comprehensive assertion patterns
  • Isolated component transformation testing

dcloudio/uni-app

packages/uni-cli-shared/__tests__/usingComponents.spec.ts

            
import path from 'path'
import type { ResolvedId } from 'rollup'
import { normalizePath } from '../src/utils'
import { findUsingComponents } from '../src/json/mp/jsonFile'
import { parseProgram } from '../src/mp/ast'
import {
  parseMainDescriptor,
  transformDynamicImports,
  updateMiniProgramComponentsByMainFilename,
  updateMiniProgramGlobalComponents,
} from '../src/mp/usingComponents'
const inputDir = normalizePath(path.resolve('/usr/xxx/projects/test/src'))

function normalizeComponentName(name: string) {
  return name
}
async function resolve(id: string, importer?: string) {
  return {
    id: normalizePath(importer ? path.resolve(path.dirname(importer), id) : id),
  } as ResolvedId
}

function dynamicImport(name: string, source: string) {
  return `const ${name} = ()=>import('${source}')`
}

describe('transformVueComponentImports', () => {
  describe('global', () => {
    const filename = '/usr/xxx/projects/test/src/main.js'
    test(`basic`, async () => {
      const source = `
import { createSSRApp } from 'vue'
import App from './App.vue'
import ComponentA from './components/component-a.vue'
import ComponentB from './components/component-b.vue'
export function createApp() {
  const app = createSSRApp(App)
  app.component('component-a',ComponentA)
  app.component('component-b',ComponentB)
  return {
    app
  }
}
`
      const { imports } = await updateMiniProgramGlobalComponents(
        filename,
        parseProgram(source, filename, {}),
        {
          inputDir,
          resolve,
          normalizeComponentName,
        }
      )

      expect(findUsingComponents('app')).toMatchObject({
        'component-a': '/components/component-a',
        'component-b': '/components/component-b',
      })
      expect(imports.length).toBe(2)
      const { code } = await transformDynamicImports(source, imports, {
        dynamicImport,
      })
      expect(code).toContain(`import App from './App.vue'`)
      expect(code).toContain(
        `const ComponentA = ()=>import('${inputDir}/components/component-a.vue')`
      )
      expect(code).toContain(
        `const ComponentB = ()=>import('${inputDir}/components/component-b.vue')`
      )
    })
    test(`ts`, async () => {
      const source = `
import { createSSRApp } from 'vue'
import App from './App.vue'
import ComponentA from './components/component-a.vue'
import ComponentB from './components/component-b.vue'
export function createApp() {
  const app = createSSRApp(App)
  app.provide('UseRequestConfigContext', {
    requestMethod: (param: any) => {},
  })
  app.component('component-a',ComponentA)
  app.component('component-b',ComponentB)
  return {
    app
  }
}
`
      const tsFilename = filename.replace('.js', '.ts')
      const { imports } = await updateMiniProgramGlobalComponents(
        tsFilename,
        parseProgram(source, tsFilename, {}),
        {
          inputDir,
          resolve,
          normalizeComponentName,
        }
      )

      expect(findUsingComponents('app')).toMatchObject({
        'component-a': '/components/component-a',
        'component-b': '/components/component-b',
      })
      expect(imports.length).toBe(2)
      const { code } = await transformDynamicImports(source, imports, {
        dynamicImport,
      })
      expect(code).toContain(`import App from './App.vue'`)
      expect(code).toContain(
        `const ComponentA = ()=>import('${inputDir}/components/component-a.vue')`
      )
      expect(code).toContain(
        `const ComponentB = ()=>import('${inputDir}/components/component-b.vue')`
      )
    })
  })
  describe('local', () => {
    const filename = '/usr/xxx/projects/test/src/pages/index/index.vue'

    async function testLocal(
      source: string,
      usingComponents: Record<string, string>,
      code: string
    ) {
      const ast = parseProgram(source, filename, {})
      const { imports } = await parseMainDescriptor(filename, ast, resolve)
      updateMiniProgramComponentsByMainFilename(
        filename,
        inputDir,
        normalizeComponentName
      )
      expect(findUsingComponents('pages/index/index')).toMatchObject(
        usingComponents
      )
      expect(
        (await transformDynamicImports(source, imports, { dynamicImport })).code
      ).toContain(code)
    }

    test(`basic`, async () => {
      await testLocal(
        `import test1 from "${inputDir}/components/test1.vue";
      const _sfc_main = {
        components: {
          test1
        }
      };
      const __BINDING_COMPONENTS__ = '{"test1":{"name":"_component_test1","type":"unknown"}}';
      function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
        return {};
      }
      import "${filename}?vue&type=style&index=0&lang.css";
      import _export_sfc from "plugin-vue:export-helper";
      export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
  `,
        {
          test1: '/components/test1',
        },
        `const test1 = ()=>import('${inputDir}/components/test1.vue')`
      )
    })

    test(`easycom`, async () => {
      await testLocal(
        `import test1 from "../../components/test1.vue";
        import MyComponentName from "../../components/test1.vue";
        const _sfc_main = {
          components: {
            test1,
            MyComponentName
          }
        };
        const __BINDING_COMPONENTS__ = '{"test":{"name":"_easycom_test","type":"unknown"},"test1":{"name":"_component_test1","type":"unknown"},"MyComponentName":{"name":"_component_MyComponentName","type":"unknown"},"my-component-name":{"name":"_component_my_component_name","type":"unknown"}}';
        import _easycom_test from "${inputDir}/components/test/test.vue";
        if (!Math) {
          (_easycom_test)();
        }
        function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
          return {};
        }
        import "${inputDir}/pages/index/index.vue?vue&type=style&index=0&lang.css";
        import _export_sfc from "plugin-vue:export-helper";
        export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
    `,
        {
          test: '/components/test/test',
          test1: '/components/test1',
          'my-component-name': '/components/test1',
        },
        `const _easycom_test = ()=>import('${inputDir}/components/test/test.vue')`
      )
    })

    test(`unplugin-vue-components`, async () => {
      await testLocal(
        `import { ref, watch } from 'vue';/* unplugin-vue-components disabled */import __unplugin_components_0 from '${inputDir}/components/test1.vue';
        const _sfc_main = {
          setup() {
            const visible = ref(false);
            watch(visible, () => {
              console.log("parent visible change");
            });
            return {
              visible
            };
          }
        };
        import { o as _o, resolveComponent as _resolveComponent, p as _p } from "vue";
        const __BINDING_COMPONENTS__ = '{"Test1":{"name":"_component_Test","type":"unknown"}}';
        if (!Array) {const _component_Test = __unplugin_components_0;Math.max.call(null, _component_Test);}
        function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
          return {};
        }
        import _export_sfc from "plugin-vue:export-helper";
        export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
    `,
        {
          test1: '/components/test1',
        },
        `const __unplugin_components_0 = ()=>import('${inputDir}/components/test1.vue')`
      )
    })

    test(`PascalCase`, async () => {
      await testLocal(
        `import test1 from "../../components/test1.vue";
        import MyComponentName from "../../components/test1.vue";
        const _sfc_main = {
          components: {
            test1,
            MyComponentName
          }
        };
        const __BINDING_COMPONENTS__ = '{"test1":{"name":"_component_test1","type":"unknown"},"MyComponentName":{"name":"_component_MyComponentName","type":"unknown"},"my-component-name":{"name":"_component_my_component_name","type":"unknown"}}';
        function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
          return {};
        }
        import "${inputDir}/pages/index/index.vue?vue&type=style&index=0&lang.css";
        import _export_sfc from "plugin-vue:export-helper";
        export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
    `,
        {
          test1: '/components/test1',
          'my-component-name': '/components/test1',
        },
        `const MyComponentName = ()=>import('${inputDir}/components/test1.vue')`
      )
    })

    test(`setup`, async () => {
      await testLocal(
        `import { defineComponent as _defineComponent } from "vue";
        const __BINDING_COMPONENTS__ = '{"test1":{"name":"test1","type":"setup"},"MyComponentName":{"name":"MyComponentName","type":"setup"},"my-component-name":{"name":"MyComponentName","type":"setup"}}';
        if (!Math) {
          (test1+MyComponentName+MyComponentName)();
        }
        import test1 from "../../components/test1.vue";
        import MyComponentName from "../../components/test1.vue";
        const _sfc_main = /* @__PURE__ */ _defineComponent({
          setup(__props) {
            return (_ctx, _cache) => {
              return {};
            };
          }
        });
        import "${inputDir}/pages/index/index.vue?vue&type=style&index=0&lang.css";
        export default _sfc_main;
    `,
        {
          test1: '/components/test1',
          'my-component-name': '/components/test1',
        },
        `const MyComponentName = ()=>import('${inputDir}/components/test1.vue')`
      )
    })
    test(`setup with easycom`, async () => {
      await testLocal(
        `import { defineComponent as _defineComponent } from "vue";
        const __BINDING_COMPONENTS__ = '{"test":{"name":"_easycom_test","type":"unknown"},"test1":{"name":"test1","type":"setup"},"MyComponentName":{"name":"MyComponentName","type":"setup"},"my-component-name":{"name":"MyComponentName","type":"setup"}}';
        import _easycom_test from "${inputDir}/components/test/test.vue";
        if (!Math) {
          (_easycom_test+test1+MyComponentName+MyComponentName)();
        }
        import test1 from "../../components/test1.vue";
        import MyComponentName from "../../components/test1.vue";
        const _sfc_main = /* @__PURE__ */ _defineComponent({
          setup(__props) {
            return (_ctx, _cache) => {
              return {};
            };
          }
        });
        import "${inputDir}/pages/index/index.vue?vue&type=style&index=0&lang.css";
        export default _sfc_main;
    `,
        {
          test: '/components/test/test',
          test1: '/components/test1',
          'my-component-name': '/components/test1',
        },
        `const _easycom_test = ()=>import('${inputDir}/components/test/test.vue')`
      )
    })
  })
})