Skip to content

Commit

Permalink
Merge branch 'release' of github.com:appsmithorg/appsmith into feat/3…
Browse files Browse the repository at this point in the history
…7854-request-integration
  • Loading branch information
AmanAgarwal041 committed Dec 6, 2024
2 parents 5a2adf0 + fd9efb8 commit f3d906a
Show file tree
Hide file tree
Showing 123 changed files with 5,020 additions and 398 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ describe("EditableName", () => {

await userEvent.click(document.body);

expect(getByRole("tooltip").textContent).toEqual(validationError);
expect(getByRole("tooltip").textContent).toEqual("");

expect(exitEditing).toHaveBeenCalled();
expect(onNameSave).not.toHaveBeenCalledWith(invalidTitle);
Expand Down Expand Up @@ -187,7 +187,7 @@ describe("EditableName", () => {
});

fireEvent.focusOut(inputElement);
expect(getByRole("tooltip").textContent).toEqual(validationError);
expect(getByRole("tooltip").textContent).toEqual("");
expect(exitEditing).toHaveBeenCalled();
expect(onNameSave).not.toHaveBeenCalledWith(invalidTitle);
});
Expand Down
16 changes: 14 additions & 2 deletions app/client/src/IDE/Components/EditableName/EditableName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import React, {
useRef,
useState,
} from "react";
import { Spinner, Text, Tooltip } from "@appsmith/ads";
import { Spinner, Text as ADSText, Tooltip } from "@appsmith/ads";
import { useEventCallback, useEventListener } from "usehooks-ts";
import { usePrevious } from "@mantine/hooks";
import { useNameEditor } from "./useNameEditor";
import styled from "styled-components";

interface EditableTextProps {
name: string;
Expand All @@ -29,6 +30,10 @@ interface EditableTextProps {
inputTestId?: string;
}

export const Text = styled(ADSText)`
min-width: 3ch;
`;

export const EditableName = ({
exitEditing,
icon,
Expand Down Expand Up @@ -72,10 +77,15 @@ export const EditableName = ({
const nameError = validate(editableName);

if (editableName === name) {
// No change detected
exitWithoutSaving();
} else if (nameError === null) {
// Save the new name
exitEditing();
onNameSave(editableName);
} else {
// Exit edit mode and revert name
exitWithoutSaving();
}
}, [
editableName,
Expand Down Expand Up @@ -119,7 +129,9 @@ export const EditableName = ({
useEventListener(
"focusout",
function handleFocusOut() {
if (isEditing) {
const input = inputRef.current;

if (input) {
attemptSave();
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,10 @@
& :global(.ads-v2-select > .rc-select-selector) {
min-width: unset;
}

/* Remove this once the config in DB is updated to use Section and Zone (Twilio, Airtable) */
& :global(.ar-form-info-text) {
max-width: unset;
}
/* Removable section ends here */
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const UQIEditorForm = () => {

return (
<Flex
alignItems="center"
data-testid="t--uqi-editor-form"
flexDirection="column"
overflowY="scroll"
Expand Down
11 changes: 9 additions & 2 deletions app/client/src/ce/sagas/NavigationSagas.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { fork, put, select, call } from "redux-saga/effects";
import { fork, put, select, call, take } from "redux-saga/effects";
import type { RouteChangeActionPayload } from "actions/focusHistoryActions";
import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity";
import log from "loglevel";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import { getRecentEntityIds } from "selectors/globalSearchSelectors";
import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import {
ReduxActionTypes,
type ReduxAction,
} from "ee/constants/ReduxActionConstants";
import { getCurrentThemeDetails } from "selectors/themeSelectors";
import type { BackgroundTheme } from "sagas/ThemeSaga";
import { changeAppBackground } from "sagas/ThemeSaga";
Expand Down Expand Up @@ -86,6 +89,10 @@ function* clearErrors() {
}

function* watchForTrackableUrl(payload: RouteChangeActionPayload) {
yield take([
ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
]);
const oldPathname = payload.prevLocation.pathname;
const newPathname = payload.location.pathname;
const isOldPathTrackable: boolean = yield call(
Expand Down
30 changes: 23 additions & 7 deletions app/client/src/ce/sagas/userSagas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,11 @@ function* initTrackers(currentUser: User) {
}
}

export function* runUserSideEffectsSaga() {
function* restartUserTracking() {
const currentUser: User = yield select(getCurrentUser);
const { enableTelemetry } = currentUser;
const isAirgappedInstance = isAirgapped();

if (enableTelemetry) {
yield fork(initTrackers, currentUser);
}

const isFFFetched: boolean = yield select(getFeatureFlagsFetched);

if (!isFFFetched) {
Expand All @@ -169,13 +165,33 @@ export function* runUserSideEffectsSaga() {

if (!isAirgappedInstance) {
// We need to stop and start tracking activity to ensure that the tracking from previous session is not carried forward
UsagePulse.stopTrackingActivity();
UsagePulse.startTrackingActivity(
yield call(UsagePulse.stopTrackingActivity);

if (currentUser?.isAnonymous) {
yield take([
ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
]);
}

yield call(
UsagePulse.startTrackingActivity,
enableTelemetry && getAppsmithConfigs().segment.enabled,
currentUser?.isAnonymous ?? false,
isFreeLicense,
);
}
}

export function* runUserSideEffectsSaga() {
const currentUser: User = yield select(getCurrentUser);
const { enableTelemetry } = currentUser;

if (enableTelemetry) {
yield fork(initTrackers, currentUser);
}

yield fork(restartUserTracking);

if (currentUser.emptyInstance) {
history.replace(SETUP);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export const StyledFormInfo = styled.span<{ config?: ControlProps }>`
? "5px"
: "0px"};
line-height: 16px;
max-width: 270px;
overflow: hidden;
break-word: break-all;
`;

const FormSubtitleText = styled.span<{ config?: ControlProps }>`
Expand Down Expand Up @@ -177,7 +180,9 @@ function FormLabel(props: FormLabelProps) {
//Wrapper on styled <span/>
function FormInfoText(props: FormLabelProps) {
return (
<StyledFormInfo config={props.config}>{props.children}</StyledFormInfo>
<StyledFormInfo className="ar-form-info-text" config={props.config}>
{props.children}
</StyledFormInfo>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
GitImportStep,
GitOpsTab,
GitSettingsTab,
} from "../../enums";
} from "../../constants/enums";
import type {
GitSingleArtifactAPIResponsesReduxState,
GitSingleArtifactUIReduxState,
Expand Down
165 changes: 165 additions & 0 deletions app/client/src/git/components/DisconnectModal/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import DisconnectModal from ".";

jest.mock("ee/utils/AnalyticsUtil", () => ({
logEvent: jest.fn(),
}));

describe("DisconnectModal", () => {
const defaultProps = {
isModalOpen: true,
disconnectingApp: {
id: "app123",
name: "TestApp",
},
closeModal: jest.fn(),
onBackClick: jest.fn(),
onDisconnect: jest.fn(),
};

afterEach(() => {
jest.clearAllMocks();
});

it("should render the modal when isModalOpen is true", () => {
render(<DisconnectModal {...defaultProps} />);
expect(screen.getByTestId("t--disconnect-git-modal")).toBeInTheDocument();
});

it("should not render the modal when isModalOpen is false", () => {
render(<DisconnectModal {...defaultProps} isModalOpen={false} />);
expect(
screen.queryByTestId("t--disconnect-git-modal"),
).not.toBeInTheDocument();
});

it("should display the correct modal header", () => {
render(<DisconnectModal {...defaultProps} />);
expect(screen.getByText("Revoke access to TestApp")).toBeInTheDocument();
});

it("should display the correct instruction text", () => {
render(<DisconnectModal {...defaultProps} />);
expect(
screen.getByText("Type “TestApp” in the input box to revoke access."),
).toBeInTheDocument();
});

it("should update appName state when input changes", () => {
render(<DisconnectModal {...defaultProps} />);
const input = screen.getByLabelText("Application name");

fireEvent.change(input, { target: { value: "TestApp" } });
expect(input).toHaveValue("TestApp");
});

it("should enable Revoke button when appName matches disconnectingApp.name", () => {
render(<DisconnectModal {...defaultProps} />);
const input = screen.getByLabelText("Application name");
const revokeButton = document.getElementsByClassName(
"t--git-revoke-button",
)[0];

expect(revokeButton).toBeDisabled();

fireEvent.change(input, { target: { value: "TestApp" } });
expect(revokeButton).toBeEnabled();
});

it("should disable Revoke button when appName does not match disconnectingApp.name", () => {
render(<DisconnectModal {...defaultProps} />);
const input = screen.getByLabelText("Application name");
const revokeButton = document.getElementsByClassName(
"t--git-revoke-button",
)[0];

fireEvent.change(input, { target: { value: "WrongAppName" } });
expect(revokeButton).toBeDisabled();
});

it("should call onBackClick when Go Back button is clicked", () => {
render(<DisconnectModal {...defaultProps} />);
const goBackButton = document.getElementsByClassName(
"t--git-revoke-back-button",
)[0];

fireEvent.click(goBackButton);
expect(defaultProps.onBackClick).toHaveBeenCalledTimes(1);
});

it("should call onDisconnect when Revoke button is clicked", () => {
render(<DisconnectModal {...defaultProps} />);
const input = screen.getByLabelText("Application name");
const revokeButton = document.getElementsByClassName(
"t--git-revoke-button",
)[0];

fireEvent.change(input, { target: { value: "TestApp" } });
fireEvent.click(revokeButton);

expect(defaultProps.onDisconnect).toHaveBeenCalledTimes(1);
});

it("should disable Revoke button when isRevoking is true", () => {
const { rerender } = render(<DisconnectModal {...defaultProps} />);
const input = screen.getByLabelText("Application name");
const revokeButton = document.getElementsByClassName(
"t--git-revoke-button",
)[0];

fireEvent.change(input, { target: { value: "TestApp" } });
expect(revokeButton).toBeEnabled();

fireEvent.click(revokeButton);
// Rerender to reflect state change
rerender(<DisconnectModal {...defaultProps} />);

expect(defaultProps.onDisconnect).toHaveBeenCalledTimes(1);
expect(revokeButton).toBeDisabled();
});

it("should log analytics event on input blur", () => {
render(<DisconnectModal {...defaultProps} />);
const input = screen.getByLabelText("Application name");

fireEvent.change(input, { target: { value: "SomeValue" } });
fireEvent.blur(input);

expect(AnalyticsUtil.logEvent).toHaveBeenCalledWith(
"GS_MATCHING_REPO_NAME_ON_GIT_DISCONNECT_MODAL",
{
value: "SomeValue",
expecting: "TestApp",
},
);
});

it("should display callout with non-reversible message and learn more link", () => {
render(<DisconnectModal {...defaultProps} />);
expect(
screen.getByText(
"This action is non-reversible. Please proceed with caution.",
),
).toBeInTheDocument();
const learnMoreLink = screen.getByText("Learn more").parentElement;

expect(learnMoreLink).toBeInTheDocument();
expect(learnMoreLink).toHaveAttribute(
"href",
"https://docs.appsmith.com/advanced-concepts/version-control-with-git/disconnect-the-git-repository",
);
});

it("should not call onDisconnect when Revoke button is clicked and appName does not match", () => {
render(<DisconnectModal {...defaultProps} />);
const revokeButton = document.getElementsByClassName(
"t--git-revoke-button",
)[0];

fireEvent.click(revokeButton);
expect(defaultProps.onDisconnect).not.toHaveBeenCalled();
});
});
Loading

0 comments on commit f3d906a

Please sign in to comment.