fix: deletes local storage environment id on logout (#5957)

This commit is contained in:
Anshuman Pandey
2025-06-16 19:31:16 +05:30
committed by GitHub
parent 14023ca8a9
commit 6ddc91ee85
17 changed files with 614 additions and 262 deletions
@@ -1,3 +1,4 @@
import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@/lib/localStorage";
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, test, vi } from "vitest";
import { TOrganization } from "@formbricks/types/organizations";
@@ -78,6 +79,11 @@ describe("DeleteAccountModal", () => {
.spyOn(actions, "deleteUserAction")
.mockResolvedValue("deleted-user-id" as any); // the return doesn't matter here
Object.defineProperty(window, "localStorage", {
writable: true,
value: { removeItem: vi.fn() },
});
// Mock window.location.replace
Object.defineProperty(window, "location", {
writable: true,
@@ -94,6 +100,8 @@ describe("DeleteAccountModal", () => {
/>
);
const removeItemSpy = vi.spyOn(window.localStorage, "removeItem");
const input = screen.getByTestId("deleteAccountConfirmation");
fireEvent.change(input, { target: { value: mockUser.email } });
@@ -106,6 +114,7 @@ describe("DeleteAccountModal", () => {
reason: "account_deletion",
redirect: false, // Updated to match new implementation
});
expect(removeItemSpy).toHaveBeenCalledWith(FORMBRICKS_ENVIRONMENT_ID_LS);
expect(window.location.replace).toHaveBeenCalledWith("/auth/login");
expect(mockSetOpen).toHaveBeenCalledWith(false);
});
@@ -116,6 +125,11 @@ describe("DeleteAccountModal", () => {
.spyOn(actions, "deleteUserAction")
.mockResolvedValue("deleted-user-id" as any); // the return doesn't matter here
Object.defineProperty(window, "localStorage", {
writable: true,
value: { removeItem: vi.fn() },
});
Object.defineProperty(window, "location", {
writable: true,
value: { replace: vi.fn() },
@@ -137,12 +151,15 @@ describe("DeleteAccountModal", () => {
const form = screen.getByTestId("deleteAccountForm");
fireEvent.submit(form);
const removeItemSpy = vi.spyOn(window.localStorage, "removeItem");
await waitFor(() => {
expect(deleteUserAction).toHaveBeenCalled();
expect(mockSignOut).toHaveBeenCalledWith({
reason: "account_deletion",
redirect: false, // Updated to match new implementation
});
expect(removeItemSpy).toHaveBeenCalledWith(FORMBRICKS_ENVIRONMENT_ID_LS);
expect(window.location.replace).toHaveBeenCalledWith(
"https://app.formbricks.com/s/clri52y3z8f221225wjdhsoo2"
);
@@ -1,5 +1,6 @@
"use client";
import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@/lib/localStorage";
import { useSignOut } from "@/modules/auth/hooks/use-sign-out";
import { DeleteDialog } from "@/modules/ui/components/delete-dialog";
import { Input } from "@/modules/ui/components/input";
@@ -38,6 +39,8 @@ export const DeleteAccountModal = ({
setDeleting(true);
await deleteUserAction();
localStorage.removeItem(FORMBRICKS_ENVIRONMENT_ID_LS);
// Sign out with account deletion reason (no automatic redirect)
await signOutWithAudit({
reason: "account_deletion",
@@ -1,17 +1,61 @@
import { useSignOut } from "@/modules/auth/hooks/use-sign-out";
import { render } from "@testing-library/react";
import { signOut } from "next-auth/react";
import { describe, expect, test, vi } from "vitest";
import { type MockedFunction, beforeEach, describe, expect, test, vi } from "vitest";
import { ClientLogout } from "./index";
// Mock the localStorage
const mockRemoveItem = vi.fn();
Object.defineProperty(window, "localStorage", {
value: {
removeItem: mockRemoveItem,
},
});
// Mock next-auth/react
vi.mock("next-auth/react", () => ({
signOut: vi.fn(),
const mockSignOut = vi.fn();
vi.mock("@/modules/auth/hooks/use-sign-out", () => ({
useSignOut: vi.fn(),
}));
const mockUseSignOut = useSignOut as MockedFunction<typeof useSignOut>;
describe("ClientLogout", () => {
test("calls signOut on render", () => {
beforeEach(() => {
vi.clearAllMocks();
mockUseSignOut.mockReturnValue({
signOut: mockSignOut,
});
});
test("calls signOut with correct parameters on render", () => {
render(<ClientLogout />);
expect(signOut).toHaveBeenCalled();
expect(mockUseSignOut).toHaveBeenCalled();
expect(mockSignOut).toHaveBeenCalledWith({
reason: "forced_logout",
redirectUrl: "/auth/login",
redirect: false,
callbackUrl: "/auth/login",
});
});
test("handles missing userId and userEmail", () => {
render(<ClientLogout />);
expect(mockUseSignOut).toHaveBeenCalled();
expect(mockSignOut).toHaveBeenCalledWith({
reason: "forced_logout",
redirectUrl: "/auth/login",
redirect: false,
callbackUrl: "/auth/login",
});
});
test("removes environment ID from localStorage", () => {
render(<ClientLogout />);
expect(mockRemoveItem).toHaveBeenCalledWith("formbricks-environment-id");
});
test("renders null", () => {
@@ -1,11 +1,20 @@
"use client";
import { signOut } from "next-auth/react";
import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@/lib/localStorage";
import { useSignOut } from "@/modules/auth/hooks/use-sign-out";
import { useEffect } from "react";
export const ClientLogout = () => {
const { signOut: signOutWithAudit } = useSignOut();
useEffect(() => {
signOut();
localStorage.removeItem(FORMBRICKS_ENVIRONMENT_ID_LS);
signOutWithAudit({
reason: "forced_logout",
redirectUrl: "/auth/login",
redirect: false,
callbackUrl: "/auth/login",
});
});
return null;
};