diff --git a/apps/web/components/apps/AppPage.tsx b/apps/web/components/apps/AppPage.tsx index b8f35d09ba..0b90ee4f8e 100644 --- a/apps/web/components/apps/AppPage.tsx +++ b/apps/web/components/apps/AppPage.tsx @@ -184,8 +184,7 @@ export const AppPage = ({ enabled: !!dependencies, }); - const disableInstall = - dependencyData.data && dependencyData.data.some((dependency) => !dependency.installed); + const disableInstall = dependencyData.data ? dependencyData.data.some((dependency) => !dependency.installed) : false; // const disableInstall = requiresGCal && !gCalInstalled.data; diff --git a/apps/web/components/apps/InstallAppButtonChild.test.tsx b/apps/web/components/apps/InstallAppButtonChild.test.tsx new file mode 100644 index 0000000000..b7d84e9a57 --- /dev/null +++ b/apps/web/components/apps/InstallAppButtonChild.test.tsx @@ -0,0 +1,103 @@ +import { render, screen } from "@testing-library/react"; +import { vi } from "vitest"; + +import { InstallAppButtonChild } from "./InstallAppButtonChild"; + +// Mock credential type +type MockCredential = { + id: number; + delegatedToId: string; + userId: number; + user: { email: string }; + key: { access_token: string }; + invalid: boolean; + teamId: null; + team: null; + delegationCredentialId: string; + type: + | `${string}_calendar` + | `${string}_messaging` + | `${string}_payment` + | `${string}_video` + | `${string}_other` + | `${string}_automation` + | `${string}_analytics` + | `${string}_crm` + | `${string}_other_calendar`; + appId: string; +}; + +// Factory function to create mock credentials +const createMockCredential = (overrides: Partial = {}): MockCredential => ({ + id: 1, + type: "google_calendar" as const, + userId: 1, + delegatedToId: "delegation-123", + teamId: null, + team: null, + user: { email: "test@example.com" }, + key: { access_token: "mock_token_123" }, + delegationCredentialId: "delegation-123", + appId: "google-calendar", + invalid: false, + ...overrides, +}); + +// Mock the useLocale hook +vi.mock("@calcom/lib/hooks/useLocale", () => ({ + useLocale: () => ({ + t: (key: string) => { + const translations = { + install_app: "Install App", + install_another: "Install Another", + start_paid_trial: "Start Free Trial", + subscribe: "Subscribe", + }; + return translations[key as keyof typeof translations] || key; + }, + }), +})); + +describe("InstallAppButtonChild", () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it("is disabled when credentials exist and multiInstall is false", () => { + const mockCredentials = [createMockCredential()]; + render(); + + const button = screen.getByTestId("install-app-button"); + expect(button).toBeDisabled(); + }); + + it("is enabled when credentials exist and multiInstall is true", () => { + const mockCredentials = [createMockCredential()]; + render(); + + const button = screen.getByTestId("install-app-button"); + expect(button).not.toBeDisabled(); + }); + + it("is disabled when disabled prop is true regardless of credentials", () => { + render(); + + const button = screen.getByTestId("install-app-button"); + expect(button).toBeDisabled(); + }); + + it("combines disabled prop with credential logic correctly", () => { + const mockCredentials = [createMockCredential()]; + render(); + + const button = screen.getByTestId("install-app-button"); + expect(button).toBeDisabled(); + }); + + it("is enabled when no blocking conditions exist", () => { + render(); + + const button = screen.getByTestId("install-app-button"); + expect(button).not.toBeDisabled(); + }); +}); diff --git a/apps/web/components/apps/InstallAppButtonChild.tsx b/apps/web/components/apps/InstallAppButtonChild.tsx index 7b5a4ff36d..5c5b324807 100644 --- a/apps/web/components/apps/InstallAppButtonChild.tsx +++ b/apps/web/components/apps/InstallAppButtonChild.tsx @@ -17,6 +17,7 @@ export const InstallAppButtonChild = ({ const { t } = useLocale(); const shouldDisableInstallation = !multiInstall ? !!(credentials && credentials.length) : false; + const isDisabled = shouldDisableInstallation || props.disabled; // Paid apps don't support team installs at the moment // Also, cal.ai(the only paid app at the moment) doesn't support team install either @@ -25,7 +26,7 @@ export const InstallAppButtonChild = ({