// @vitest-environment jsdom
import { test, describe, expect, vi, beforeEach, afterEach } from "vitest";
import "vitest-dom/extend-expect";
import { createRoutesStub } from "react-router";
import { render, screen, waitFor, cleanup } from "@testing-library/react";
import Index, { loader, action } from "../_index";
import * as auth from "~/lib/auth";
// Mock isAuthEnabled to control test behavior
vi.mock("~/lib/auth", async () => {
const actual = await vi.importActual("~/lib/auth");
return {
...actual,
isAuthEnabled: vi.fn().mockReturnValue(true)
};
});
describe("Index route", () => {
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
test("renders index route", async () => {
// Set isAuthEnabled to true for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const RemixStub = createRoutesStub([
{
path: "/",
Component: Index,
loader: () => ({ user: { authenticated: false }, authEnabled: true }),
},
]);
const { container } = render();
// Check if component renders at all
expect(container).toBeInTheDocument();
// Wait for content to load
await waitFor(() => {
expect(
screen.getByText("Welcome to Counterscale"),
).toBeInTheDocument();
});
expect(
screen.getByText("Enter your password to access the dashboard"),
).toBeInTheDocument();
expect(screen.getByText("Sign In")).toBeInTheDocument();
});
test("renders authenticated state", async () => {
// Set isAuthEnabled to true for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const RemixStub = createRoutesStub([
{
path: "/",
Component: Index,
loader: () => ({ user: { authenticated: true }, authEnabled: true }),
},
]);
render();
await waitFor(() => {
expect(
screen.getByText("Welcome to Counterscale"),
).toBeInTheDocument();
});
expect(
screen.getByText("Continue to access your analytics dashboard."),
).toBeInTheDocument();
expect(screen.getByText("Go to Dashboard")).toBeInTheDocument();
expect(
screen.getByRole("link", { name: "Go to Dashboard" }),
).toHaveAttribute("href", "/dashboard");
});
});
describe("loader function", () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
test("should return user when authenticated", async () => {
const mockUser = { authenticated: true };
const getUserSpy = vi
.spyOn(auth, "getUser")
.mockResolvedValue(mockUser);
// Mock isAuthEnabled for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const mockRequest = new Request("http://localhost/");
const mockContext = {
cloudflare: {
env: {
CF_PASSWORD_HASH: "$2b$12$test.hash.value",
CF_JWT_SECRET: "test-secret",
},
},
};
const result = await loader({
request: mockRequest,
context: mockContext,
params: {},
});
expect(getUserSpy).toHaveBeenCalledWith(
mockRequest,
mockContext.cloudflare.env,
);
expect(result).toEqual({ user: mockUser, authEnabled: true });
});
test("should return { authenticated: false } user when not authenticated", async () => {
const getUserSpy = vi
.spyOn(auth, "getUser")
.mockResolvedValue({ authenticated: false });
// Mock isAuthEnabled for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const mockRequest = new Request("http://localhost/");
const mockContext = {
cloudflare: {
env: {
CF_PASSWORD_HASH: "$2b$12$test.hash.value",
CF_JWT_SECRET: "test-secret",
},
},
};
const result = await loader({
request: mockRequest,
context: mockContext,
params: {},
});
expect(getUserSpy).toHaveBeenCalledWith(
mockRequest,
mockContext.cloudflare.env,
);
expect(result).toEqual({ user: { authenticated: false }, authEnabled: true });
});
});
describe("action function", () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
test("should return error when password is missing", async () => {
// Mock isAuthEnabled to return true for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const formData = new FormData();
const mockRequest = {
formData: vi.fn().mockResolvedValue(formData),
} as unknown as Request;
const mockContext = {
cloudflare: {
env: {
CF_PASSWORD_HASH: "$2b$12$test.hash.value",
CF_JWT_SECRET: "test-secret",
},
},
};
const result = await action({
request: mockRequest,
context: mockContext,
params: {},
});
expect(result).toEqual({ error: "Password is required" });
});
test("should return error when password is empty string", async () => {
// Mock isAuthEnabled to return true for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const formData = new FormData();
formData.set("password", "");
const mockRequest = {
formData: vi.fn().mockResolvedValue(formData),
} as unknown as Request;
const mockContext = {
cloudflare: {
env: {
CF_PASSWORD_HASH: "$2b$12$test.hash.value",
CF_JWT_SECRET: "test-secret",
},
},
};
const result = await action({
request: mockRequest,
context: mockContext,
params: {},
});
expect(result).toEqual({ error: "Password is required" });
});
test("should return error when password is not a string", async () => {
// Mock isAuthEnabled to return true for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const formData = new FormData();
formData.set("password", "123" as any);
// Mock formData.get to return a non-string value
const mockFormData = {
get: vi.fn().mockReturnValue(123),
};
const mockRequest = {
formData: vi.fn().mockResolvedValue(mockFormData),
} as unknown as Request;
const mockContext = {
cloudflare: {
env: {
CF_PASSWORD_HASH: "$2b$12$test.hash.value",
CF_JWT_SECRET: "test-secret",
},
},
};
const result = await action({
request: mockRequest,
context: mockContext,
params: {},
});
expect(result).toEqual({ error: "Password is required" });
});
test("should call login and return redirect when password is valid", async () => {
// Mock isAuthEnabled to return true for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const mockRedirect = { status: 302, headers: new Headers() };
const loginSpy = vi
.spyOn(auth, "login")
.mockResolvedValue(mockRedirect as any);
const formData = new FormData();
formData.set("password", "correct-password");
const mockRequest = {
formData: vi.fn().mockResolvedValue(formData),
} as unknown as Request;
const mockContext = {
cloudflare: {
env: {
CF_PASSWORD_HASH: "$2b$12$test.hash.value",
CF_JWT_SECRET: "test-secret",
},
},
};
const result = await action({
request: mockRequest,
context: mockContext,
params: {},
});
expect(loginSpy).toHaveBeenCalledWith(
mockRequest,
"correct-password",
mockContext.cloudflare.env,
);
expect(result).toBe(mockRedirect);
});
test("should return error when login throws an error", async () => {
// Mock isAuthEnabled to return true for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const loginSpy = vi
.spyOn(auth, "login")
.mockRejectedValue(new Error("Invalid password"));
const formData = new FormData();
formData.set("password", "wrong-password");
const mockRequest = {
formData: vi.fn().mockResolvedValue(formData),
} as unknown as Request;
const mockContext = {
cloudflare: {
env: {
CF_PASSWORD_HASH: "$2b$12$test.hash.value",
CF_JWT_SECRET: "test-secret",
},
},
};
const result = await action({
request: mockRequest,
context: mockContext,
params: {},
});
expect(loginSpy).toHaveBeenCalledWith(
mockRequest,
"wrong-password",
mockContext.cloudflare.env,
);
expect(result).toEqual({ error: "Invalid password" });
});
test("should handle login rejection gracefully", async () => {
// Mock isAuthEnabled to return true for this test
vi.mocked(auth.isAuthEnabled).mockReturnValue(true);
const loginSpy = vi
.spyOn(auth, "login")
.mockRejectedValue("Some other error");
const formData = new FormData();
formData.set("password", "any-password");
const mockRequest = {
formData: vi.fn().mockResolvedValue(formData),
} as unknown as Request;
const mockContext = {
cloudflare: {
env: {
CF_PASSWORD_HASH: "$2b$12$test.hash.value",
CF_JWT_SECRET: "test-secret",
},
},
};
const result = await action({
request: mockRequest,
context: mockContext,
params: {},
});
expect(loginSpy).toHaveBeenCalledWith(
mockRequest,
"any-password",
mockContext.cloudflare.env,
);
expect(result).toEqual({ error: "Invalid password" });
});
});