UNPKG

@esmx/router-vue

Version:

Vue integration for @esmx/router - A universal router that works seamlessly with both Vue 2.7+ and Vue 3

457 lines (456 loc) 14.1 kB
import { Router, RouterMode } from "@esmx/router"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { createApp, defineComponent, h, nextTick } from "vue"; import { RouterLink } from "./router-link.mjs"; import { useProvideRouter } from "./use.mjs"; describe("router-link.ts - RouterLink Component", () => { let router; let app; let container; beforeEach(async () => { container = document.createElement("div"); container.id = "test-app"; document.body.appendChild(container); const routes = [ { path: "/", component: defineComponent({ name: "Home", template: "<div>Home Page</div>" }), meta: { title: "Home" } }, { path: "/about", component: defineComponent({ name: "About", template: "<div>About Page</div>" }), meta: { title: "About" } }, { path: "/contact", component: defineComponent({ name: "Contact", template: "<div>Contact Page</div>" }), meta: { title: "Contact" } } ]; router = new Router({ root: "#test-app", routes, mode: RouterMode.memory, base: new URL("http://localhost:3000/") }); await router.replace("/"); await nextTick(); }); afterEach(async () => { if (app) { app.unmount(); } if (router) { router.destroy(); } if (container.parentNode) { container.parentNode.removeChild(container); } await nextTick(); }); describe("Component Definition", () => { it("should have correct component name", () => { expect(RouterLink.name).toBe("RouterLink"); }); it("should have properly configured props", () => { const props = RouterLink.props; expect(props.to).toBeDefined(); expect(props.to.required).toBe(true); expect(props.type).toBeDefined(); expect(props.type.default).toBe("push"); expect(props.exact).toBeDefined(); expect(props.exact.default).toBe("include"); expect(props.tag).toBeDefined(); expect(props.tag.default).toBe("a"); expect(props.event).toBeDefined(); expect(props.event.default).toBe("click"); expect(props.replace).toBeDefined(); expect(props.replace.default).toBe(false); }); it("should have setup function defined", () => { expect(RouterLink.setup).toBeDefined(); expect(typeof RouterLink.setup).toBe("function"); }); }); describe("Component Rendering", () => { it("should render basic router link", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h(RouterLink, { to: "/about" }, () => "About Link"); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const linkElement = container.querySelector("a"); expect(linkElement).toBeTruthy(); expect(linkElement == null ? void 0 : linkElement.textContent).toBe("About Link"); }); it("should render with custom tag", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: "/contact", tag: "button" }, () => "Contact Button" ); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const buttonElement = container.querySelector("button"); expect(buttonElement).toBeTruthy(); expect(buttonElement == null ? void 0 : buttonElement.textContent).toBe("Contact Button"); }); it("should render with active class when route matches", async () => { await router.push("/about"); await nextTick(); const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: "/about", activeClass: "active-link" }, () => "Current Page" ); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const linkElement = container.querySelector("a"); expect(linkElement).toBeTruthy(); expect(linkElement == null ? void 0 : linkElement.classList.contains("active-link")).toBe(true); }); it("should handle different navigation types", async () => { var _a, _b; const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h("div", [ h( RouterLink, { to: "/about", type: "push" }, () => "Push Link" ), h( RouterLink, { to: "/contact", type: "replace" }, () => "Replace Link" ) ]); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const links = container.querySelectorAll("a"); expect(links).toHaveLength(2); expect((_a = links[0]) == null ? void 0 : _a.textContent).toBe("Push Link"); expect((_b = links[1]) == null ? void 0 : _b.textContent).toBe("Replace Link"); }); }); describe("Navigation Functionality", () => { it("should navigate when clicked", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: "/about" }, () => "Navigate to About" ); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const linkElement = container.querySelector("a"); expect(linkElement).toBeTruthy(); const clickPromise = new Promise((resolve) => { router.afterEach(() => resolve()); }); linkElement == null ? void 0 : linkElement.click(); await clickPromise; await nextTick(); expect(router.route.path).toBe("/about"); }); it("should handle custom events", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: "/contact", event: "mouseenter" }, () => "Hover to Navigate" ); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const linkElement = container.querySelector("a"); expect(linkElement).toBeTruthy(); const navigationPromise = new Promise((resolve) => { router.afterEach(() => resolve()); }); const event = new MouseEvent("mouseenter", { bubbles: true }); linkElement == null ? void 0 : linkElement.dispatchEvent(event); await navigationPromise; await nextTick(); expect(router.route.path).toBe("/contact"); }); it("should handle object-based route navigation", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: { path: "/about", query: { tab: "info" } } }, () => "About with Query" ); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const linkElement = container.querySelector("a"); expect(linkElement).toBeTruthy(); const navigationPromise = new Promise((resolve) => { router.afterEach(() => resolve()); }); linkElement == null ? void 0 : linkElement.click(); await navigationPromise; await nextTick(); expect(router.route.path).toBe("/about"); expect(router.route.query.tab).toBe("info"); }); }); describe("Props Validation", () => { it("should accept string as to prop", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h(RouterLink, { to: "/about" }, () => "String Route"); } }); expect(() => { app = createApp(TestApp); app.mount(container); }).not.toThrow(); }); it("should accept object as to prop", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: { path: "/contact" } }, () => "Object Route" ); } }); expect(() => { app = createApp(TestApp); app.mount(container); }).not.toThrow(); }); it("should handle array of events", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: "/about", event: ["click", "keydown"] }, () => "Multi Event Link" ); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const linkElement = container.querySelector("a"); expect(linkElement).toBeTruthy(); const clickPromise = new Promise((resolve) => { router.afterEach(() => resolve()); }); linkElement == null ? void 0 : linkElement.click(); await clickPromise; await nextTick(); expect(router.route.path).toBe("/about"); await router.push("/"); await nextTick(); const keydownPromise = new Promise((resolve) => { router.afterEach(() => resolve()); }); const keyEvent = new KeyboardEvent("keydown", { key: "Enter" }); linkElement == null ? void 0 : linkElement.dispatchEvent(keyEvent); await keydownPromise; await nextTick(); expect(router.route.path).toBe("/about"); }); }); describe("Error Handling", () => { it("should throw error when router context is missing", () => { const TestApp = defineComponent({ setup() { return () => h(RouterLink, { to: "/about" }, () => "No Router"); } }); expect(() => { app = createApp(TestApp); app.mount(container); }).toThrow(); }); }); describe("Slot Rendering", () => { it("should render default slot content", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: "/about" }, { default: () => h( "span", { class: "link-text" }, "Custom Content" ) } ); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const spanElement = container.querySelector("span.link-text"); expect(spanElement).toBeTruthy(); expect(spanElement == null ? void 0 : spanElement.textContent).toBe("Custom Content"); }); it("should render complex slot content", async () => { const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: "/contact" }, { default: () => [ h("i", { class: "icon" }, "\u2192"), h("span", "Contact Us") ] } ); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const iconElement = container.querySelector("i.icon"); const spanElement = container.querySelector("span"); expect(iconElement).toBeTruthy(); expect(spanElement).toBeTruthy(); expect(iconElement == null ? void 0 : iconElement.textContent).toBe("\u2192"); expect(spanElement == null ? void 0 : spanElement.textContent).toBe("Contact Us"); }); }); describe("Active State Management", () => { it("should apply active class with exact matching", async () => { var _a, _b; await router.push("/about"); await nextTick(); const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h("div", [ h( RouterLink, { to: "/about", exact: "exact", activeClass: "exact-active" }, () => "Exact Match" ), h( RouterLink, { to: "/about/sub", exact: "exact", activeClass: "exact-active" }, () => "Not Exact" ) ]); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const links = container.querySelectorAll("a"); expect((_a = links[0]) == null ? void 0 : _a.classList.contains("exact-active")).toBe(true); expect((_b = links[1]) == null ? void 0 : _b.classList.contains("exact-active")).toBe(false); }); it("should apply active class with include matching", async () => { await router.push("/about"); await nextTick(); const TestApp = defineComponent({ setup() { useProvideRouter(router); return () => h( RouterLink, { to: "/about", exact: "include", activeClass: "include-active" }, () => "Include Match" ); } }); app = createApp(TestApp); app.mount(container); await nextTick(); const linkElement = container.querySelector("a"); expect(linkElement == null ? void 0 : linkElement.classList.contains("include-active")).toBe( true ); }); }); });