142 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /* eslint-disable import/no-commonjs */
 | |
| const Vue = require('vue');
 | |
| const VTU = require('@vue/test-utils');
 | |
| const { installCompat: installVTUCompat, fullCompatConfig } = require('vue-test-utils-compat');
 | |
| 
 | |
| function getComponentName(component) {
 | |
|   if (!component) {
 | |
|     return undefined;
 | |
|   }
 | |
| 
 | |
|   return (
 | |
|     component.name ||
 | |
|     getComponentName(component.extends) ||
 | |
|     component.mixins?.find((mixin) => getComponentName(mixin))
 | |
|   );
 | |
| }
 | |
| 
 | |
| function isLegacyExtendedComponent(component) {
 | |
|   return Reflect.has(component, 'super') && component.super.extend({}).super === component.super;
 | |
| }
 | |
| function unwrapLegacyVueExtendComponent(selector) {
 | |
|   return isLegacyExtendedComponent(selector) ? selector.options : selector;
 | |
| }
 | |
| 
 | |
| if (global.document) {
 | |
|   const compatConfig = {
 | |
|     MODE: 2,
 | |
| 
 | |
|     GLOBAL_MOUNT: 'suppress-warning',
 | |
|     GLOBAL_EXTEND: 'suppress-warning',
 | |
|     GLOBAL_PROTOTYPE: 'suppress-warning',
 | |
|     RENDER_FUNCTION: 'suppress-warning',
 | |
| 
 | |
|     INSTANCE_DESTROY: 'suppress-warning',
 | |
|     INSTANCE_DELETE: 'suppress-warning',
 | |
| 
 | |
|     INSTANCE_ATTRS_CLASS_STYLE: 'suppress-warning',
 | |
|     INSTANCE_CHILDREN: 'suppress-warning',
 | |
|     INSTANCE_SCOPED_SLOTS: 'suppress-warning',
 | |
|     INSTANCE_LISTENERS: 'suppress-warning',
 | |
|     INSTANCE_EVENT_EMITTER: 'suppress-warning',
 | |
|     INSTANCE_EVENT_HOOKS: 'suppress-warning',
 | |
|     INSTANCE_SET: 'suppress-warning',
 | |
|     GLOBAL_OBSERVABLE: 'suppress-warning',
 | |
|     GLOBAL_SET: 'suppress-warning',
 | |
|     COMPONENT_FUNCTIONAL: 'suppress-warning',
 | |
|     COMPONENT_V_MODEL: 'suppress-warning',
 | |
|     COMPONENT_ASYNC: 'suppress-warning',
 | |
|     CUSTOM_DIR: 'suppress-warning',
 | |
|     OPTIONS_BEFORE_DESTROY: 'suppress-warning',
 | |
|     OPTIONS_DATA_MERGE: 'suppress-warning',
 | |
|     OPTIONS_DATA_FN: 'suppress-warning',
 | |
|     OPTIONS_DESTROYED: 'suppress-warning',
 | |
|     ATTR_FALSE_VALUE: 'suppress-warning',
 | |
| 
 | |
|     COMPILER_V_ON_NATIVE: 'suppress-warning',
 | |
|     COMPILER_V_BIND_OBJECT_ORDER: 'suppress-warning',
 | |
| 
 | |
|     CONFIG_WHITESPACE: 'suppress-warning',
 | |
|     CONFIG_OPTION_MERGE_STRATS: 'suppress-warning',
 | |
|     PRIVATE_APIS: 'suppress-warning',
 | |
|     WATCH_ARRAY: 'suppress-warning',
 | |
|   };
 | |
| 
 | |
|   let compatH;
 | |
|   Vue.config.compilerOptions.whitespace = 'preserve';
 | |
|   Vue.createApp({
 | |
|     compatConfig: {
 | |
|       MODE: 3,
 | |
|       RENDER_FUNCTION: 'suppress-warning',
 | |
|     },
 | |
|     render(h) {
 | |
|       compatH = h;
 | |
|     },
 | |
|   }).mount(document.createElement('div'));
 | |
| 
 | |
|   Vue.configureCompat(compatConfig);
 | |
|   installVTUCompat(VTU, fullCompatConfig, compatH);
 | |
|   VTU.config.global.renderStubDefaultSlot = true;
 | |
| 
 | |
|   const noop = () => {};
 | |
| 
 | |
|   VTU.config.plugins.createStubs = ({ name, component: rawComponent, registerStub }) => {
 | |
|     const component = unwrapLegacyVueExtendComponent(rawComponent);
 | |
|     const hyphenatedName = name.replace(/\B([A-Z])/g, '-$1').toLowerCase();
 | |
| 
 | |
|     const stub = Vue.defineComponent({
 | |
|       name: getComponentName(component),
 | |
|       props: component.props,
 | |
|       model: component.model,
 | |
|       methods: Object.fromEntries(
 | |
|         Object.entries(component.methods ?? {}).map(([key]) => [key, noop]),
 | |
|       ),
 | |
|       render() {
 | |
|         const {
 | |
|           $slots: slots = {},
 | |
|           $scopedSlots: scopedSlots = {},
 | |
|           $parent: parent,
 | |
|           $vnode: vnode,
 | |
|         } = this;
 | |
| 
 | |
|         const hasStaticDefaultSlot = 'default' in slots && !('default' in scopedSlots);
 | |
|         const isTheOnlyChild = parent?.$.subTree === vnode;
 | |
|         // this condition should be altered when https://github.com/vuejs/vue-test-utils/pull/2068 is merged
 | |
|         // and our codebase will be updated to include it (@vue/test-utils@1.3.6 I assume)
 | |
|         const shouldRenderAllSlots = !hasStaticDefaultSlot && isTheOnlyChild;
 | |
| 
 | |
|         const renderSlotByName = (slotName) => {
 | |
|           const slot = scopedSlots[slotName] || slots[slotName];
 | |
|           let result;
 | |
|           if (typeof slot === 'function') {
 | |
|             try {
 | |
|               result = slot({});
 | |
|             } catch {
 | |
|               // intentionally blank
 | |
|             }
 | |
|           } else {
 | |
|             result = slot;
 | |
|           }
 | |
|           return result;
 | |
|         };
 | |
| 
 | |
|         const slotContents = shouldRenderAllSlots
 | |
|           ? [...new Set([...Object.keys(slots), ...Object.keys(scopedSlots)])]
 | |
|               .map(renderSlotByName)
 | |
|               .filter(Boolean)
 | |
|           : renderSlotByName('default');
 | |
| 
 | |
|         return Vue.h(`${hyphenatedName || 'anonymous'}-stub`, this.$props, slotContents);
 | |
|       },
 | |
|     });
 | |
| 
 | |
|     if (typeof component === 'function') {
 | |
|       component()?.then?.((resolvedComponent) => {
 | |
|         registerStub({ source: resolvedComponent.default, stub });
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     return stub;
 | |
|   };
 | |
| }
 |