import { mount } from "@vue/test-utils"; import { describe, it, expect, vi, beforeEach } from "vitest"; import InterfacesPage from "../../meshchatx/src/frontend/components/interfaces/InterfacesPage.vue"; // Mock global objects const mockAxios = { get: vi.fn(), post: vi.fn(), }; window.axios = mockAxios; const mockToast = { success: vi.fn(), error: vi.fn(), }; // We need to handle how ToastUtils is imported in the component // If it's a global or imported, we might need a different approach. // Let's assume it's available via window or we can mock the import if using vitest aliases. vi.mock("../../js/ToastUtils", () => ({ default: { success: vi.fn(), error: vi.fn(), }, })); // Mock router/route const mockRoute = { query: {}, }; const mockRouter = { push: vi.fn(), }; describe("InterfacesPage.vue", () => { beforeEach(() => { vi.clearAllMocks(); mockAxios.get.mockResolvedValue({ data: { interfaces: [], app_info: { is_reticulum_running: true } } }); }); it("loads interfaces on mount", async () => { mockAxios.get.mockImplementation((url) => { if (url.includes("interfaces")) { return Promise.resolve({ data: { interfaces: [{ name: "Test Iface", type: "TCP" }] } }); } if (url.includes("app/info")) { return Promise.resolve({ data: { app_info: { is_reticulum_running: true } } }); } return Promise.reject(); }); const wrapper = mount(InterfacesPage, { global: { mocks: { $route: mockRoute, $router: mockRouter, $t: (msg) => msg, }, stubs: ["RouterLink", "MaterialDesignIcon", "IconButton", "Interface", "ImportInterfacesModal"], }, }); await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick(); // wait for multiple awaits expect(mockAxios.get).toHaveBeenCalledWith("/api/v1/reticulum/interfaces"); expect(wrapper.vm.interfaces.length).toBe(1); }); it("tracks changes when an interface is enabled", async () => { const wrapper = mount(InterfacesPage, { global: { mocks: { $route: mockRoute, $router: mockRouter, $t: (msg) => msg, }, stubs: ["RouterLink", "MaterialDesignIcon", "IconButton", "Interface", "ImportInterfacesModal"], }, }); await wrapper.vm.enableInterface("test-iface"); expect(wrapper.vm.hasPendingInterfaceChanges).toBe(true); expect(wrapper.vm.modifiedInterfaceNames.has("test-iface")).toBe(true); }); it("clears pending changes after RNS reload", async () => { mockAxios.post.mockResolvedValue({ data: { message: "Reloaded" } }); const wrapper = mount(InterfacesPage, { global: { mocks: { $route: mockRoute, $router: mockRouter, $t: (msg) => msg, }, stubs: ["RouterLink", "MaterialDesignIcon", "IconButton", "Interface", "ImportInterfacesModal"], }, }); wrapper.vm.hasPendingInterfaceChanges = true; wrapper.vm.modifiedInterfaceNames.add("test-iface"); await wrapper.vm.reloadRns(); expect(wrapper.vm.hasPendingInterfaceChanges).toBe(false); expect(wrapper.vm.modifiedInterfaceNames.size).toBe(0); expect(mockAxios.post).toHaveBeenCalledWith("/api/v1/reticulum/reload"); }); });