import { describe, expect, it, vi } from "vitest"; import { dispatch } from "../src/vue-helper"; /** * 创建模拟的 Vue ComponentPublicInstance */ function createMockInstance( componentName?: string, parent?: any, root?: any, ) { const instance = { $parent: parent ?? null, $root: root ?? null, $options: { componentName: componentName ?? null }, $emit: vi.fn(), }; return instance; } describe("dispatch", () => { it("应在祖先组件中找到匹配的 componentName 并触发事件", () => { const targetComponentName = "FormComponent"; const grandParent = createMockInstance(targetComponentName); const parent = createMockInstance(undefined, grandParent, grandParent); const thisArg = createMockInstance(undefined, parent, grandParent); // thisArg.$parent → parent, parent.$parent → grandParent parent.$parent = grandParent; dispatch(thisArg, targetComponentName as any, "validate", { field: "name" }); expect(grandParent.$emit).toHaveBeenCalledWith("validate", { field: "name" }); }); it("应从直接父组件开始查找", () => { const targetComponentName = "DirectParent"; const parent = createMockInstance(targetComponentName); const thisArg = createMockInstance(undefined, parent, parent); dispatch(thisArg, targetComponentName as any, "change", null); expect(parent.$emit).toHaveBeenCalledWith("change", null); }); it("当没有父组件且有根组件时,应从根组件开始查找", () => { const targetComponentName = "RootComponent"; const root = createMockInstance(targetComponentName); const thisArg = createMockInstance(undefined, null, root); dispatch(thisArg, targetComponentName as any, "init", undefined); expect(root.$emit).toHaveBeenCalledWith("init", undefined); }); it("当 thisArg.$parent 和 thisArg.$root 都为 null 时,应直接返回", () => { const thisArg = createMockInstance(undefined, null, null); const targetComponentName = "FormComponent" as any; // 不应抛出错误 expect(() => dispatch(thisArg, targetComponentName, "validate", {})).not.toThrow(); }); it("当找不到匹配的祖先组件时,不应触发任何事件", () => { const grandParent = createMockInstance("OtherComponent"); const parent = createMockInstance("AnotherComponent", grandParent, grandParent); const thisArg = createMockInstance(undefined, parent, grandParent); dispatch(thisArg, "NonExistentComponent" as any, "validate", {}); expect(grandParent.$emit).not.toHaveBeenCalled(); expect(parent.$emit).not.toHaveBeenCalled(); }); it("应在多级父组件链中找到匹配的祖先", () => { const targetComponentName = "GrandGrandParent"; const grandGrandParent = createMockInstance(targetComponentName); const grandParent = createMockInstance("GrandParent", grandGrandParent, grandGrandParent); const parent = createMockInstance("Parent", grandParent, grandGrandParent); const thisArg = createMockInstance(undefined, parent, grandGrandParent); dispatch(thisArg, targetComponentName as any, "submit", { data: "test" }); expect(grandGrandParent.$emit).toHaveBeenCalledWith("submit", { data: "test" }); expect(grandParent.$emit).not.toHaveBeenCalled(); expect(parent.$emit).not.toHaveBeenCalled(); }); it("找到的第一个匹配组件后,不应继续向上查找", () => { const targetComponentName = "Parent"; const grandParent = createMockInstance(targetComponentName); // 更上层也有同名组件 const parent = createMockInstance(targetComponentName, grandParent, grandParent); const thisArg = createMockInstance(undefined, parent, grandParent); dispatch(thisArg, targetComponentName as any, "click", true); // 应该触发 parent 的事件,而非 grandParent 的 expect(parent.$emit).toHaveBeenCalledWith("click", true); expect(grandParent.$emit).not.toHaveBeenCalled(); }); it("应支持传递各种类型的参数", () => { const parent = createMockInstance("Target"); const thisArg = createMockInstance(undefined, parent, parent); // 字符串 dispatch(thisArg, "Target" as any, "event1", "hello"); expect(parent.$emit).toHaveBeenCalledWith("event1", "hello"); // 数字 dispatch(thisArg, "Target" as any, "event2", 42); expect(parent.$emit).toHaveBeenCalledWith("event2", 42); // 数组 dispatch(thisArg, "Target" as any, "event3", [1, 2, 3]); expect(parent.$emit).toHaveBeenCalledWith("event3", [1, 2, 3]); // undefined dispatch(thisArg, "Target" as any, "event4", undefined); expect(parent.$emit).toHaveBeenCalledWith("event4", undefined); }); it("当祖先组件的 componentName 为 undefined 时,应跳过继续查找", () => { const targetComponentName = "TargetComponent"; const grandParent = createMockInstance(targetComponentName); // parent 没有 componentName const parent = createMockInstance(undefined, grandParent, grandParent); const thisArg = createMockInstance(undefined, parent, grandParent); dispatch(thisArg, targetComponentName as any, "save", {}); expect(grandParent.$emit).toHaveBeenCalledWith("save", {}); }); });