351 lines
11 KiB
JavaScript
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");
|
|
});
|
|
});
|