fix: hide Create app from templates in airgapped image (#33581)

## 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"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/9156749330>
> Commit: c7461fd1a6c8dfbfd945ec552125f49e846ebd83
> Cypress dashboard url: <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=9156749330&attempt=1"
target="_blank">Click here!</a>

<!-- end of auto-generated comment: Cypress test results  -->




## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No
This commit is contained in:
Rahul Barwal 2024-05-20 15:56:25 +05:30 committed by GitHub
parent 0f90f75740
commit 49b7d1d77f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 277 additions and 8 deletions

View File

@ -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({
</MenuItem>
{<Divider className="!block mb-[2px]" />}
<MenuItem
data-testid="t--workspace-action-create-app-from-template"
disabled={!hasCreateNewApplicationPermission}
onSelect={() => onStartFromTemplate(workspaceId)}
startIcon="layout-2-line"
>
{createMessage(NEW_APP_FROM_TEMPLATE)}
</MenuItem>
{!isAirgappedInstance && (
<MenuItem
data-testid="t--workspace-action-create-app-from-template"
disabled={!hasCreateNewApplicationPermission}
onSelect={() => onStartFromTemplate(workspaceId)}
startIcon="layout-2-line"
>
{createMessage(NEW_APP_FROM_TEMPLATE)}
</MenuItem>
)}
{enableImportExport && hasCreateNewApplicationPermission && (
<MenuItem

View File

@ -0,0 +1,265 @@
import {
WORKSPACE_ACTION_BUTTON,
createMessage,
} from "@appsmith/constants/messages";
import type { Workspace } from "@appsmith/constants/workspaceConstants";
import "@testing-library/jest-dom";
import { fireEvent, render } from "@testing-library/react";
import "jest-styled-components";
import { unitTestBaseMockStore } from "layoutSystems/common/dropTarget/unitTestUtils";
import React from "react";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";
import { lightTheme } from "selectors/themeSelectors";
import { ThemeProvider } from "styled-components";
import WorkspaceAction from "../WorkspaceAction";
const mockStore = configureStore([]);
const locators = {
testId: {
createNewApp: "t--workspace-action-create-app",
createAppFromTemplates: "t--workspace-action-create-app-from-template",
importApp: "t--workspace-import-app",
},
};
const onCreateNewApplication = jest.fn();
const onStartFromTemplate = jest.fn();
const setSelectedWorkspaceIdForImportApplication = jest.fn();
const renderWorkspaceActionComponent = (
workspace: Workspace,
workspaceId: string,
) =>
render(
<Provider store={mockStore(baseStoreForSpec)}>
<ThemeProvider theme={lightTheme}>
<WorkspaceAction
enableImportExport
isMobile={false}
onCreateNewApplication={onCreateNewApplication}
onStartFromTemplate={onStartFromTemplate}
setSelectedWorkspaceIdForImportApplication={
setSelectedWorkspaceIdForImportApplication
}
workspace={workspace}
workspaceId={workspaceId}
/>
</ThemeProvider>
</Provider>,
);
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,
},
},
},
};