From 49b7d1d77f27b870e931156140f0df17f68da15b Mon Sep 17 00:00:00 2001 From: Rahul Barwal Date: Mon, 20 May 2024 15:56:25 +0530 Subject: [PATCH] fix: hide Create app from templates in airgapped image (#33581) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description On homepage create new application from template should not be available to airgapped image. Includes unit tests for `WorkspaceAction.test.tsx` Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.Templates" ### :mag: Cypress test results > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: > Commit: c7461fd1a6c8dfbfd945ec552125f49e846ebd83 > Cypress dashboard url: Click here! ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No --- .../ce/pages/Applications/WorkspaceAction.tsx | 20 +- .../tests/WorkspaceAction.test.tsx | 265 ++++++++++++++++++ 2 files changed, 277 insertions(+), 8 deletions(-) create mode 100644 app/client/src/ce/pages/Applications/tests/WorkspaceAction.test.tsx diff --git a/app/client/src/ce/pages/Applications/WorkspaceAction.tsx b/app/client/src/ce/pages/Applications/WorkspaceAction.tsx index e3fe9c4c37..275bcb6581 100644 --- a/app/client/src/ce/pages/Applications/WorkspaceAction.tsx +++ b/app/client/src/ce/pages/Applications/WorkspaceAction.tsx @@ -20,6 +20,7 @@ import type { Workspace } from "@appsmith/constants/workspaceConstants"; import { getIsCreatingApplicationByWorkspaceId } from "@appsmith/selectors/applicationSelectors"; import { getIsFetchingApplications } from "@appsmith/selectors/selectedWorkspaceSelectors"; import { hasCreateNewAppPermission } from "@appsmith/utils/permissionHelpers"; +import { isAirgapped } from "@appsmith/utils/airgapHelpers"; export interface WorkspaceActionProps { workspace: Workspace; @@ -45,6 +46,7 @@ function WorkspaceAction({ const isCreatingApplication = Boolean( useSelector(getIsCreatingApplicationByWorkspaceId(workspace.id)), ); + const isAirgappedInstance = isAirgapped(); const openActionMenu = useCallback(() => { setIsActionMenuOpen(true); @@ -93,14 +95,16 @@ function WorkspaceAction({ {} - onStartFromTemplate(workspaceId)} - startIcon="layout-2-line" - > - {createMessage(NEW_APP_FROM_TEMPLATE)} - + {!isAirgappedInstance && ( + onStartFromTemplate(workspaceId)} + startIcon="layout-2-line" + > + {createMessage(NEW_APP_FROM_TEMPLATE)} + + )} {enableImportExport && hasCreateNewApplicationPermission && ( + render( + + + + + , + ); + +describe("WorkspaceAction", () => { + const workspace = { + id: "663a0815de1fdf7aa1618918", + userPermissions: [ + "publish:workspaceApplications", + "delete:workspace", + "manage:workspaceApplications", + "delete:workspaceDatasources", + "export:workspaceApplications", + "read:workspaceDatasources", + "read:workspaceApplications", + "inviteUsers:workspace", + "read:workspaces", + "manage:workspaceDatasources", + "create:datasources", + "delete:workspaceApplications", + "manage:workspaces", + "create:applications", + ], + } as Workspace; + + it("1. should render the WorkspaceAction component", () => { + const { getByText } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + expect( + getByText(createMessage(WORKSPACE_ACTION_BUTTON)), + ).toBeInTheDocument(); + }); + + it("2. should call onCreateNewApplication when 'Create New App' is selected", () => { + const { getByTestId, getByText } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + fireEvent.click(getByText(createMessage(WORKSPACE_ACTION_BUTTON))); + fireEvent.click(getByTestId(locators.testId.createNewApp)); + + expect(onCreateNewApplication).toHaveBeenCalledWith(workspace.id); + }); + + it("3. should call onStartFromTemplate when 'Start from Template' is selected", () => { + const { getByTestId, getByText } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + fireEvent.click(getByText(createMessage(WORKSPACE_ACTION_BUTTON))); + fireEvent.click(getByTestId(locators.testId.createAppFromTemplates)); + + expect(onStartFromTemplate).toHaveBeenCalledWith(workspace.id); + }); + + it("4. should call setSelectedWorkspaceIdForImportApplication when 'Import App' is selected", () => { + const { getByTestId, getByText } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + fireEvent.click(getByText(createMessage(WORKSPACE_ACTION_BUTTON))); + fireEvent.click(getByTestId(locators.testId.importApp)); + + expect(setSelectedWorkspaceIdForImportApplication).toHaveBeenCalledWith( + workspace.id, + ); + }); + + it("5. should not render anything if create app permission is unavailable", () => { + //setup + const existingPermissions = workspace.userPermissions; + + // execute test + workspace.userPermissions = ["manage:workspaces"]; + const { queryByText } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + expect(queryByText(createMessage(WORKSPACE_ACTION_BUTTON))).toBeNull(); + + // reset the changes + workspace.userPermissions = existingPermissions; + }); +}); + +jest.mock("@appsmith/utils/airgapHelpers", () => ({ + isAirgapped: jest.fn(), +})); + +const mockIsAirGapped = (val: boolean) => { + /* eslint-disable @typescript-eslint/no-var-requires */ + const { isAirgapped } = require("@appsmith/utils/airgapHelpers"); + isAirgapped.mockImplementation(() => val); +}; + +describe("[Airgap] WorkspaceAction", () => { + const workspace = { + id: "663a0815de1fdf7aa1618918", + userPermissions: [ + "publish:workspaceApplications", + "delete:workspace", + "manage:workspaceApplications", + "delete:workspaceDatasources", + "export:workspaceApplications", + "read:workspaceDatasources", + "read:workspaceApplications", + "inviteUsers:workspace", + "read:workspaces", + "manage:workspaceDatasources", + "create:datasources", + "delete:workspaceApplications", + "manage:workspaces", + "create:applications", + ], + } as Workspace; + + beforeEach(() => mockIsAirGapped(true)); + + it("1. [Airgap] should render the WorkspaceAction component", () => { + const { getByText } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + expect( + getByText(createMessage(WORKSPACE_ACTION_BUTTON)), + ).toBeInTheDocument(); + }); + + it("2. [Airgap] should call onCreateNewApplication when 'Create New App' is selected", () => { + const { getByTestId, getByText } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + fireEvent.click(getByText(createMessage(WORKSPACE_ACTION_BUTTON))); + fireEvent.click(getByTestId(locators.testId.createNewApp)); + + expect(onCreateNewApplication).toHaveBeenCalledWith(workspace.id); + }); + + it("3. [Airgap] should not have 'Start from Template'", () => { + const { getByText, queryByTestId } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + fireEvent.click(getByText(createMessage(WORKSPACE_ACTION_BUTTON))); + expect(queryByTestId(locators.testId.createAppFromTemplates)).toBeNull(); + }); + + it("4. [Airgap] should call setSelectedWorkspaceIdForImportApplication when 'Import App' is selected", () => { + const { getByTestId, getByText } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + fireEvent.click(getByText(createMessage(WORKSPACE_ACTION_BUTTON))); + fireEvent.click(getByTestId(locators.testId.importApp)); + + expect(setSelectedWorkspaceIdForImportApplication).toHaveBeenCalledWith( + workspace.id, + ); + }); + + it("5. [Airgap] should not render anything if create app permission is unavailable", () => { + //setup + const existingPermissions = workspace.userPermissions; + + // execute test + workspace.userPermissions = ["manage:workspaces"]; + const { queryByText } = renderWorkspaceActionComponent( + workspace, + workspace.id, + ); + + expect(queryByText(createMessage(WORKSPACE_ACTION_BUTTON))).toBeNull(); + + // reset the changes + workspace.userPermissions = existingPermissions; + }); +}); + +const baseStoreForSpec = { + ...unitTestBaseMockStore, + ui: { + ...unitTestBaseMockStore.ui, + applications: { + creatingApplication: {}, + }, + workspaces: { + loadingStates: { + isFetchAllRoles: false, + isSavingWorkspaceInfo: false, + isFetchingWorkspaces: false, + isFetchingEntities: false, + isDeletingWorkspace: false, + }, + workspaceRoles: [], + searchEntities: {}, + }, + selectedWorkspace: { + packages: [], + loadingStates: { + isFetchingApplications: false, + isFetchingAllUsers: false, + isFetchingCurrentWorkspace: false, + }, + }, + }, +};