mirror of
https://github.com/gethomepage/homepage.git
synced 2026-02-07 16:30:52 +08:00
Add some auth tests
This commit is contained in:
100
src/__tests__/pages/api/auth/[...nextauth].test.js
Normal file
100
src/__tests__/pages/api/auth/[...nextauth].test.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const { nextAuthMock } = vi.hoisted(() => ({
|
||||||
|
nextAuthMock: vi.fn((options) => ({ options })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("next-auth", () => ({
|
||||||
|
default: nextAuthMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("pages/api/auth/[...nextauth]", () => {
|
||||||
|
const originalEnv = process.env;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.resetModules();
|
||||||
|
nextAuthMock.mockClear();
|
||||||
|
process.env = { ...originalEnv };
|
||||||
|
delete process.env.NEXTAUTH_SECRET;
|
||||||
|
delete process.env.NEXTAUTH_URL;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("configures no providers when auth is disabled", async () => {
|
||||||
|
const mod = await import("pages/api/auth/[...nextauth]");
|
||||||
|
|
||||||
|
expect(nextAuthMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mod.default.options.providers).toEqual([]);
|
||||||
|
expect(mod.default.options.pages?.signIn).toBe("/auth/signin");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("maps HOMEPAGE_AUTH_SECRET and HOMEPAGE_EXTERNAL_URL to NextAuth envs", async () => {
|
||||||
|
process.env.HOMEPAGE_AUTH_SECRET = "secret";
|
||||||
|
process.env.HOMEPAGE_EXTERNAL_URL = "https://homepage.example";
|
||||||
|
|
||||||
|
const mod = await import("pages/api/auth/[...nextauth]");
|
||||||
|
|
||||||
|
expect(process.env.NEXTAUTH_SECRET).toBe("secret");
|
||||||
|
expect(process.env.NEXTAUTH_URL).toBe("https://homepage.example");
|
||||||
|
expect(mod.default.options.secret).toBe("secret");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws when auth is enabled but required settings are missing", async () => {
|
||||||
|
process.env.HOMEPAGE_AUTH_ENABLED = "true";
|
||||||
|
|
||||||
|
await expect(import("pages/api/auth/[...nextauth]")).rejects.toThrow(
|
||||||
|
/OIDC auth is enabled but required settings are missing/i,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("builds an OIDC provider when enabled and maps profile fields", async () => {
|
||||||
|
process.env.HOMEPAGE_AUTH_ENABLED = "true";
|
||||||
|
process.env.HOMEPAGE_OIDC_ISSUER = "https://issuer.example/";
|
||||||
|
process.env.HOMEPAGE_OIDC_CLIENT_ID = "client-id";
|
||||||
|
process.env.HOMEPAGE_OIDC_CLIENT_SECRET = "client-secret";
|
||||||
|
process.env.HOMEPAGE_AUTH_SECRET = "auth-secret";
|
||||||
|
process.env.HOMEPAGE_EXTERNAL_URL = "https://homepage.example";
|
||||||
|
process.env.HOMEPAGE_OIDC_NAME = "My OIDC";
|
||||||
|
process.env.HOMEPAGE_OIDC_SCOPE = "openid email";
|
||||||
|
|
||||||
|
const mod = await import("pages/api/auth/[...nextauth]");
|
||||||
|
const [provider] = mod.default.options.providers;
|
||||||
|
|
||||||
|
expect(provider).toMatchObject({
|
||||||
|
id: "homepage-oidc",
|
||||||
|
name: "My OIDC",
|
||||||
|
type: "oauth",
|
||||||
|
idToken: true,
|
||||||
|
issuer: "https://issuer.example",
|
||||||
|
wellKnown: "https://issuer.example/.well-known/openid-configuration",
|
||||||
|
clientId: "client-id",
|
||||||
|
clientSecret: "client-secret",
|
||||||
|
});
|
||||||
|
expect(provider.authorization.params.scope).toBe("openid email");
|
||||||
|
|
||||||
|
expect(
|
||||||
|
provider.profile({
|
||||||
|
sub: "sub",
|
||||||
|
preferred_username: "user",
|
||||||
|
email: "user@example.com",
|
||||||
|
picture: "https://example.com/p.png",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id: "sub",
|
||||||
|
name: "user",
|
||||||
|
email: "user@example.com",
|
||||||
|
image: "https://example.com/p.png",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
provider.profile({
|
||||||
|
id: "id",
|
||||||
|
name: "name",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id: "id",
|
||||||
|
name: "name",
|
||||||
|
email: null,
|
||||||
|
image: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
72
src/__tests__/pages/auth/signin.test.jsx
Normal file
72
src/__tests__/pages/auth/signin.test.jsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// @vitest-environment jsdom
|
||||||
|
|
||||||
|
import { render, screen, waitFor } from "@testing-library/react";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const { getSettingsMock } = vi.hoisted(() => ({
|
||||||
|
getSettingsMock: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("utils/config/config", () => ({
|
||||||
|
getSettings: getSettingsMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { getProviders } from "next-auth/react";
|
||||||
|
import SignInPage, { getServerSideProps } from "pages/auth/signin";
|
||||||
|
|
||||||
|
describe("pages/auth/signin", () => {
|
||||||
|
it("renders an error state when no providers are configured", async () => {
|
||||||
|
render(
|
||||||
|
<SignInPage
|
||||||
|
providers={{}}
|
||||||
|
settings={{
|
||||||
|
theme: "dark",
|
||||||
|
color: "slate",
|
||||||
|
title: "Homepage",
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText("Authentication not configured")).toBeInTheDocument();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(document.documentElement.classList.contains("dark")).toBe(true);
|
||||||
|
expect(document.documentElement.classList.contains("scheme-dark")).toBe(true);
|
||||||
|
expect(document.documentElement.classList.contains("theme-slate")).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders provider buttons when providers are available", () => {
|
||||||
|
render(
|
||||||
|
<SignInPage
|
||||||
|
providers={{
|
||||||
|
oidc: { id: "oidc", name: "OIDC" },
|
||||||
|
}}
|
||||||
|
settings={{
|
||||||
|
theme: "light",
|
||||||
|
color: "emerald",
|
||||||
|
title: "My Dashboard",
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText("Sign in")).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("button", { name: /login via oidc/i })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("getServerSideProps returns providers and settings", async () => {
|
||||||
|
getProviders.mockResolvedValueOnce({ foo: { id: "foo", name: "Foo" } });
|
||||||
|
getSettingsMock.mockReturnValueOnce({ theme: "dark" });
|
||||||
|
|
||||||
|
const res = await getServerSideProps({});
|
||||||
|
|
||||||
|
expect(getProviders).toHaveBeenCalled();
|
||||||
|
expect(getSettingsMock).toHaveBeenCalled();
|
||||||
|
expect(res).toEqual({
|
||||||
|
props: {
|
||||||
|
providers: { foo: { id: "foo", name: "Foo" } },
|
||||||
|
settings: { theme: "dark" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -8,6 +8,12 @@ afterEach(() => {
|
|||||||
if (typeof document !== "undefined") cleanup();
|
if (typeof document !== "undefined") cleanup();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Avoid NextAuth client-side fetches during unit tests.
|
||||||
|
vi.mock("next-auth/react", () => ({
|
||||||
|
SessionProvider: ({ children }) => children ?? null,
|
||||||
|
getProviders: vi.fn(async () => ({})),
|
||||||
|
}));
|
||||||
|
|
||||||
// implement a couple of common formatters mocked in next-i18next
|
// implement a couple of common formatters mocked in next-i18next
|
||||||
vi.mock("next-i18next", () => ({
|
vi.mock("next-i18next", () => ({
|
||||||
// Keep app/page components importable in unit tests.
|
// Keep app/page components importable in unit tests.
|
||||||
|
|||||||
Reference in New Issue
Block a user