Files
MeshChatX/tests/frontend/AuthPage.test.js

351 lines
11 KiB
JavaScript

import { mount } from "@vue/test-utils";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import AuthPage from "../../meshchatx/src/frontend/components/auth/AuthPage.vue";
describe("AuthPage.vue", () => {
let axiosMock;
let routerMock;
beforeEach(() => {
axiosMock = {
get: vi.fn().mockResolvedValue({
data: {
auth_enabled: true,
authenticated: false,
password_set: true,
},
}),
post: vi.fn().mockResolvedValue({ data: { success: true } }),
};
window.axios = axiosMock;
routerMock = {
push: vi.fn(),
};
Object.defineProperty(window, "location", {
value: {
reload: vi.fn(),
},
writable: true,
});
});
afterEach(() => {
delete window.axios;
vi.clearAllMocks();
});
const mountAuthPage = () => {
return mount(AuthPage, {
global: {
mocks: {
$router: routerMock,
},
},
});
};
it("renders login form when password is set", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: true,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await wrapper.vm.checkAuthStatus();
await wrapper.vm.$nextTick();
expect(wrapper.text()).toContain("Authentication Required");
expect(wrapper.text()).toContain("Login");
expect(wrapper.find('input[type="password"]').exists()).toBe(true);
});
it("renders setup form when password is not set", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: false,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await new Promise((resolve) => setTimeout(resolve, 100));
expect(wrapper.vm.isSetup).toBe(true);
expect(wrapper.text()).toContain("Initial Setup");
expect(wrapper.text()).toContain("Set Password");
expect(wrapper.findAll('input[type="password"]').length).toBe(2);
});
it("redirects to home when auth is disabled", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: false,
authenticated: false,
password_set: false,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await wrapper.vm.checkAuthStatus();
await wrapper.vm.$nextTick();
expect(routerMock.push).toHaveBeenCalledWith("/");
});
it("redirects to home when already authenticated", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: true,
password_set: true,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await wrapper.vm.checkAuthStatus();
await wrapper.vm.$nextTick();
expect(routerMock.push).toHaveBeenCalledWith("/");
});
it("validates password length in setup mode", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: false,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await new Promise((resolve) => setTimeout(resolve, 100));
wrapper.vm.password = "short";
wrapper.vm.confirmPassword = "short";
await wrapper.vm.$nextTick();
await wrapper.vm.handleSubmit();
await wrapper.vm.$nextTick();
expect(wrapper.vm.error).toContain("at least 8 characters");
expect(axiosMock.post).not.toHaveBeenCalled();
});
it("validates password match in setup mode", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: false,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await new Promise((resolve) => setTimeout(resolve, 100));
wrapper.vm.password = "password123";
wrapper.vm.confirmPassword = "password456";
await wrapper.vm.$nextTick();
await wrapper.vm.handleSubmit();
await wrapper.vm.$nextTick();
expect(wrapper.vm.error).toContain("do not match");
expect(axiosMock.post).not.toHaveBeenCalled();
});
it("submits setup form with valid password", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: false,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await new Promise((resolve) => setTimeout(resolve, 100));
wrapper.vm.password = "password123";
wrapper.vm.confirmPassword = "password123";
await wrapper.vm.$nextTick();
await wrapper.vm.handleSubmit();
await wrapper.vm.$nextTick();
await new Promise((resolve) => setTimeout(resolve, 100));
expect(axiosMock.post).toHaveBeenCalledWith("/api/v1/auth/setup", {
password: "password123",
});
});
it("submits login form with password", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: true,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await wrapper.vm.checkAuthStatus();
await wrapper.vm.$nextTick();
wrapper.vm.password = "password123";
await wrapper.vm.handleSubmit();
await wrapper.vm.$nextTick();
expect(axiosMock.post).toHaveBeenCalledWith("/api/v1/auth/login", {
password: "password123",
});
});
it("reloads page after successful login", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: true,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await wrapper.vm.checkAuthStatus();
await wrapper.vm.$nextTick();
wrapper.vm.password = "password123";
await wrapper.vm.handleSubmit();
await wrapper.vm.$nextTick();
expect(window.location.reload).toHaveBeenCalled();
});
it("displays error message on login failure", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: true,
},
});
axiosMock.post.mockRejectedValueOnce({
response: {
data: {
error: "Invalid password",
},
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await wrapper.vm.checkAuthStatus();
await wrapper.vm.$nextTick();
wrapper.vm.password = "wrongpassword";
await wrapper.vm.handleSubmit();
await wrapper.vm.$nextTick();
expect(wrapper.vm.error).toBe("Invalid password");
expect(wrapper.vm.isLoading).toBe(false);
});
it("displays error message on setup failure", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: false,
},
});
axiosMock.post.mockRejectedValueOnce({
response: {
data: {
error: "Setup failed",
},
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await wrapper.vm.checkAuthStatus();
await wrapper.vm.$nextTick();
wrapper.vm.password = "password123";
wrapper.vm.confirmPassword = "password123";
await wrapper.vm.handleSubmit();
await wrapper.vm.$nextTick();
expect(wrapper.vm.error).toBe("Setup failed");
expect(wrapper.vm.isLoading).toBe(false);
});
it("disables submit button when loading", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: true,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await wrapper.vm.checkAuthStatus();
await wrapper.vm.$nextTick();
wrapper.vm.isLoading = true;
await wrapper.vm.$nextTick();
const submitButton = wrapper.find('button[type="submit"]');
expect(submitButton.attributes("disabled")).toBeDefined();
});
it("disables submit button when passwords do not match in setup mode", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
auth_enabled: true,
authenticated: false,
password_set: false,
},
});
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await new Promise((resolve) => setTimeout(resolve, 100));
wrapper.vm.password = "password123";
wrapper.vm.confirmPassword = "password456";
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
const submitButton = wrapper.find('button[type="submit"]');
const disabledAttr = submitButton.attributes("disabled");
const disabledProp = submitButton.element.disabled;
expect(disabledAttr !== undefined || disabledProp === true).toBe(true);
});
it("handles network errors gracefully", async () => {
axiosMock.get.mockRejectedValueOnce(new Error("Network error"));
const wrapper = mountAuthPage();
await wrapper.vm.$nextTick();
await wrapper.vm.checkAuthStatus();
await wrapper.vm.$nextTick();
expect(wrapper.vm.error).toContain("Failed to check");
});
});