* fix: correct admin password banner message to require both password length and 2FA The banner message incorrectly used 'or' implying only one condition was needed, but the code requires BOTH a password of at least 15 characters AND 2FA enabled. Updated the message to clearly state both requirements and added a hint that users need to log out and log back in after updating their security settings. Fixes #9527 Co-Authored-By: unknown <> * fix: auto-sign-out INACTIVE_ADMIN users after enabling 2FA When an INACTIVE_ADMIN user enables 2FA, automatically sign them out so they can log back in with refreshed session role, dismissing the banner. This matches the existing behavior for password changes. Co-Authored-By: unknown <> * feat: add dynamic admin banner message based on inactiveAdminReason Co-Authored-By: unknown <> * Remove and add various localization strings * Update common.json * Add cookie consent checkbox message and remove entries * test: add unit tests for AdminPasswordBanner and inactiveAdminReason logic Co-Authored-By: unknown <> * fix: add expires field to session mock to fix type check Co-Authored-By: unknown <> * fix: wrap CALENDSO_ENCRYPTION_KEY mutations in try/finally to prevent env state leaks Addresses Cubic AI review feedback (confidence 9/10): when a test fails early, the CALENDSO_ENCRYPTION_KEY env var was not being restored, which could leak state into subsequent tests. Wrapped the env mutation in try/finally blocks to guarantee cleanup. Co-Authored-By: bot_apk <apk@cognition.ai> * fix: add missing 'expires' field to buildSession in AdminPasswordBanner test The Session type requires 'expires' to be a string, but buildSession was not providing it, causing a type error caught by CI type-check. Co-Authored-By: bot_apk <apk@cognition.ai> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: bot_apk <apk@cognition.ai>
92 lines
3.0 KiB
TypeScript
92 lines
3.0 KiB
TypeScript
import { render, screen } from "@testing-library/react";
|
|
import type { ReactNode } from "react";
|
|
import type { SessionContextValue } from "next-auth/react";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import AdminPasswordBanner from "./AdminPasswordBanner";
|
|
|
|
vi.mock("@calcom/lib/hooks/useLocale", () => ({
|
|
useLocale: () => ({
|
|
t: (key: string) => key,
|
|
}),
|
|
}));
|
|
|
|
vi.mock("next/link", () => ({
|
|
default: ({ href, children, ...props }: { href: string; children: ReactNode }) => (
|
|
<a href={href} {...props}>
|
|
{children}
|
|
</a>
|
|
),
|
|
}));
|
|
|
|
vi.mock("@calcom/ui/components/top-banner", () => ({
|
|
TopBanner: ({ text, actions }: { text: string; actions?: ReactNode }) => (
|
|
<div>
|
|
<span>{text}</span>
|
|
{actions}
|
|
</div>
|
|
),
|
|
}));
|
|
|
|
function buildSession(overrides?: Partial<NonNullable<SessionContextValue["data"]>>): NonNullable<
|
|
SessionContextValue["data"]
|
|
> {
|
|
return {
|
|
expires: "2099-01-01T00:00:00.000Z",
|
|
hasValidLicense: true,
|
|
upId: "usr_test",
|
|
profileId: 1,
|
|
user: {
|
|
id: 1,
|
|
uuid: "uuid_test",
|
|
email: "test@example.com",
|
|
name: "Test User",
|
|
username: "testuser",
|
|
role: "INACTIVE_ADMIN",
|
|
inactiveAdminReason: "both",
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("AdminPasswordBanner", () => {
|
|
it("does not render for non-INACTIVE_ADMIN users", () => {
|
|
const data = buildSession({ user: { ...buildSession().user, role: "USER" } });
|
|
const { container } = render(<AdminPasswordBanner data={data} />);
|
|
expect(container).toBeEmptyDOMElement();
|
|
});
|
|
|
|
it("renders 2FA-only message and links to 2FA settings", () => {
|
|
const data = buildSession({ user: { ...buildSession().user, inactiveAdminReason: "2fa" } });
|
|
|
|
render(<AdminPasswordBanner data={data} />);
|
|
|
|
expect(screen.getByText("invalid_admin_password_2fa_only")).toBeInTheDocument();
|
|
const link = screen.getByRole("link");
|
|
expect(link).toHaveAttribute("href", "/settings/security/two-factor-auth");
|
|
expect(link).toHaveTextContent("enable_2fa");
|
|
});
|
|
|
|
it("renders password-only message and links to password settings", () => {
|
|
const data = buildSession({ user: { ...buildSession().user, inactiveAdminReason: "password" } });
|
|
|
|
render(<AdminPasswordBanner data={data} />);
|
|
|
|
expect(screen.getByText("invalid_admin_password_password_only")).toBeInTheDocument();
|
|
const link = screen.getByRole("link");
|
|
expect(link).toHaveAttribute("href", "/settings/security/password");
|
|
expect(link).toHaveTextContent("change_password_admin");
|
|
});
|
|
|
|
it("defaults to both message and password settings when reason is missing", () => {
|
|
const data = buildSession({ user: { ...buildSession().user, inactiveAdminReason: undefined } });
|
|
|
|
render(<AdminPasswordBanner data={data} />);
|
|
|
|
expect(screen.getByText("invalid_admin_password")).toBeInTheDocument();
|
|
const link = screen.getByRole("link");
|
|
expect(link).toHaveAttribute("href", "/settings/security/password");
|
|
expect(link).toHaveTextContent("change_password_admin");
|
|
});
|
|
});
|