Files
MeshChatX/tests/frontend/AboutPage.test.js
2026-01-05 11:47:35 -06:00

222 lines
8.1 KiB
JavaScript

import { mount } from "@vue/test-utils";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import AboutPage from "@/components/about/AboutPage.vue";
import ElectronUtils from "@/js/ElectronUtils";
import DialogUtils from "@/js/DialogUtils";
describe("AboutPage.vue", () => {
let axiosMock;
beforeEach(() => {
vi.useFakeTimers();
axiosMock = {
get: vi.fn().mockImplementation(() => Promise.resolve({ data: {} })),
post: vi.fn().mockImplementation(() => Promise.resolve({ data: {} })),
};
window.axios = axiosMock;
window.URL.createObjectURL = vi.fn();
window.URL.revokeObjectURL = vi.fn();
// Default electron mock
window.electron = {
getMemoryUsage: vi.fn().mockResolvedValue(null),
electronVersion: vi.fn().mockReturnValue("1.0.0"),
chromeVersion: vi.fn().mockReturnValue("1.0.0"),
nodeVersion: vi.fn().mockReturnValue("1.0.0"),
appVersion: vi.fn().mockResolvedValue("1.0.0"),
};
});
afterEach(() => {
vi.useRealTimers();
delete window.axios;
delete window.electron;
});
const mountAboutPage = () => {
return mount(AboutPage, {
global: {
mocks: {
$t: (key, params) => {
if (params) {
return `${key} ${JSON.stringify(params)}`;
}
return key;
},
},
stubs: {
MaterialDesignIcon: true,
},
},
});
};
it("fetches app info and config on mount", async () => {
const appInfo = {
version: "1.0.0",
rns_version: "0.1.0",
lxmf_version: "0.2.0",
python_version: "3.11.0",
reticulum_config_path: "/path/to/config",
database_path: "/path/to/db",
database_file_size: 1024,
dependencies: {
aiohttp: "3.8.1",
cryptography: "3.4.8",
},
};
const config = {
identity_hash: "hash1",
lxmf_address_hash: "hash2",
};
axiosMock.get.mockImplementation((url) => {
if (url === "/api/v1/app/info") return Promise.resolve({ data: { app_info: appInfo } });
if (url === "/api/v1/config") return Promise.resolve({ data: { config: config } });
if (url === "/api/v1/database/health")
return Promise.resolve({
data: {
database: {
quick_check: "ok",
journal_mode: "wal",
page_size: 4096,
page_count: 100,
freelist_pages: 5,
estimated_free_bytes: 20480,
},
},
});
if (url === "/api/v1/database/snapshots") return Promise.resolve({ data: [] });
return Promise.reject(new Error("Not found"));
});
const wrapper = mountAboutPage();
wrapper.vm.showAdvanced = true;
await vi.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick(); // Extra tick for multiple async calls
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
expect(axiosMock.get).toHaveBeenCalledWith("/api/v1/app/info");
expect(axiosMock.get).toHaveBeenCalledWith("/api/v1/config");
expect(wrapper.text()).toContain("MeshChatX");
expect(wrapper.text()).toContain("Reticulum Network Stack");
expect(wrapper.text()).toContain("hash1");
expect(wrapper.text()).toContain("hash2");
// Check for Dependency Chain section
expect(wrapper.text()).toContain("Dependency Chain");
expect(wrapper.text()).toContain("Lightweight Extensible Message Format");
expect(wrapper.text()).toContain("Reticulum Network Stack");
// Check for dependencies
expect(wrapper.text()).toContain("Backend Stack");
expect(wrapper.text()).toContain("aiohttp");
expect(wrapper.text()).toContain("3.8.1");
});
it("displays Electron memory usage when running in Electron", async () => {
vi.spyOn(ElectronUtils, "isElectron").mockReturnValue(true);
const getMemoryUsageSpy = vi.spyOn(ElectronUtils, "getMemoryUsage").mockResolvedValue({
private: 1000,
residentSet: 2000,
});
const appInfo = {
version: "1.0.0",
};
axiosMock.get.mockImplementation((url) => {
if (url === "/api/v1/app/info") return Promise.resolve({ data: { app_info: appInfo } });
if (url === "/api/v1/config") return Promise.resolve({ data: { config: {} } });
if (url === "/api/v1/database/health") return Promise.resolve({ data: { database: {} } });
if (url === "/api/v1/database/snapshots") return Promise.resolve({ data: [] });
return Promise.reject(new Error("Not found"));
});
const wrapper = mountAboutPage();
wrapper.vm.showAdvanced = true;
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
expect(getMemoryUsageSpy).toHaveBeenCalled();
expect(wrapper.vm.electronMemoryUsage).not.toBeNull();
expect(wrapper.text()).toContain("Environment Information");
});
it("handles shutdown action", async () => {
const confirmSpy = vi.spyOn(DialogUtils, "confirm").mockResolvedValue(true);
const axiosPostSpy = axiosMock.post.mockResolvedValue({ data: { message: "Shutting down..." } });
const shutdownSpy = vi.spyOn(ElectronUtils, "shutdown").mockImplementation(() => {});
vi.spyOn(ElectronUtils, "isElectron").mockReturnValue(true);
const wrapper = mountAboutPage();
wrapper.vm.appInfo = { version: "1.0.0" };
await wrapper.vm.$nextTick();
await wrapper.vm.shutdown();
expect(confirmSpy).toHaveBeenCalled();
expect(axiosPostSpy).toHaveBeenCalledWith("/api/v1/app/shutdown");
expect(shutdownSpy).toHaveBeenCalled();
});
it("updates app info periodically", async () => {
axiosMock.get.mockResolvedValue({
data: {
app_info: {},
config: {},
database: {
quick_check: "ok",
journal_mode: "wal",
page_size: 4096,
page_count: 100,
freelist_pages: 5,
estimated_free_bytes: 20480,
},
},
});
mountAboutPage();
expect(axiosMock.get).toHaveBeenCalledTimes(5); // info, config, health, snapshots, backups
vi.advanceTimersByTime(5000);
expect(axiosMock.get).toHaveBeenCalledTimes(6); // +1 from updateInterval
vi.advanceTimersByTime(5000);
expect(axiosMock.get).toHaveBeenCalledTimes(7); // +2 from updateInterval
});
it("handles vacuum database action", async () => {
axiosMock.get.mockResolvedValue({
data: {
app_info: {},
config: {},
database: {
quick_check: "ok",
journal_mode: "wal",
page_size: 4096,
page_count: 100,
freelist_pages: 5,
estimated_free_bytes: 20480,
},
},
});
axiosMock.post.mockResolvedValue({ data: { message: "Vacuum success" } });
const wrapper = mountAboutPage();
await wrapper.vm.$nextTick();
// Find vacuum button (it's the second button in the database health section)
// Or we can just call the method directly to be sure
await wrapper.vm.vacuumDatabase();
expect(axiosMock.post).toHaveBeenCalledWith("/api/v1/database/vacuum");
expect(wrapper.vm.databaseActionMessage).toBe("Vacuum success");
});
});