fix: viewing forkable apps routing to login for non signed in users (#19966)
This commit is contained in:
parent
8a63c703af
commit
d1efd7f199
|
|
@ -140,7 +140,9 @@ export function PageMenu(props: AppViewerHeaderProps) {
|
|||
workspaceId={workspaceID}
|
||||
/>
|
||||
)}
|
||||
<PrimaryCTA className="t--back-to-editor--mobile" url={props.url} />
|
||||
{isOpen && (
|
||||
<PrimaryCTA className="t--back-to-editor--mobile" url={props.url} />
|
||||
)}
|
||||
{!hideWatermark && (
|
||||
<a
|
||||
className="flex hover:no-underline"
|
||||
|
|
|
|||
166
app/client/src/pages/AppViewer/PrimaryCTA.test.tsx
Normal file
166
app/client/src/pages/AppViewer/PrimaryCTA.test.tsx
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
import React from "react";
|
||||
import { render, screen, cleanup } from "@testing-library/react";
|
||||
import "@testing-library/jest-dom/extend-expect";
|
||||
import { Provider } from "react-redux";
|
||||
import { ThemeProvider } from "styled-components";
|
||||
import { lightTheme } from "selectors/themeSelectors";
|
||||
import PrimaryCTA from "./PrimaryCTA";
|
||||
import configureStore from "redux-mock-store";
|
||||
|
||||
jest.mock("react-router", () => ({
|
||||
...jest.requireActual("react-router"),
|
||||
useLocation: () => ({
|
||||
pathname: "/app/test-3/page1-63cccd44463c535b9fbc297c",
|
||||
search: "?fork=true",
|
||||
}),
|
||||
}));
|
||||
|
||||
const mockDispatch = jest.fn();
|
||||
jest.mock("react-redux", () => ({
|
||||
...jest.requireActual("react-redux"),
|
||||
useDispatch: () => mockDispatch,
|
||||
}));
|
||||
|
||||
export const initialState: any = {
|
||||
entities: {
|
||||
pageList: {
|
||||
applicationId: 1,
|
||||
currentPageId: 1,
|
||||
pages: [
|
||||
{
|
||||
pageId: 1,
|
||||
slug: "pageSlug",
|
||||
},
|
||||
],
|
||||
},
|
||||
datasources: {
|
||||
list: [],
|
||||
},
|
||||
actions: [],
|
||||
canvasWidgets: {
|
||||
main_component: {},
|
||||
},
|
||||
},
|
||||
ui: {
|
||||
appTheming: {
|
||||
selectedTheme: {
|
||||
properties: {
|
||||
colors: {
|
||||
primaryColor: "",
|
||||
},
|
||||
borderRadius: {
|
||||
appBorderRadius: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
applications: {
|
||||
currentApplication: {
|
||||
id: "605c435a91dea93f0eaf91b8",
|
||||
name: "My Application",
|
||||
slug: "my-application",
|
||||
workspaceId: "",
|
||||
evaluationVersion: 1,
|
||||
appIsExample: false,
|
||||
gitApplicationMetadata: undefined,
|
||||
applicationVersion: 2,
|
||||
forkingEnabled: true,
|
||||
isPublic: true,
|
||||
},
|
||||
userWorkspaces: [],
|
||||
},
|
||||
theme: {
|
||||
theme: {
|
||||
colors: {
|
||||
applications: {
|
||||
iconColor: "#f2f2f2",
|
||||
},
|
||||
success: {
|
||||
main: "#e2e2e2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
users: {
|
||||
currentUser: undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
const state = JSON.parse(JSON.stringify(initialState));
|
||||
const mockStore = configureStore();
|
||||
|
||||
export function getStore(action?: string) {
|
||||
switch (action) {
|
||||
case "SET_CURRENT_USER_DETAILS":
|
||||
state.ui.users.currentUser = {
|
||||
username: "test",
|
||||
};
|
||||
break;
|
||||
}
|
||||
return mockStore(state);
|
||||
}
|
||||
|
||||
export const fetchApplicationMockResponse = {
|
||||
responseMeta: {
|
||||
status: 200,
|
||||
success: true,
|
||||
},
|
||||
data: {
|
||||
application: {
|
||||
id: "605c435a91dea93f0eaf91b8",
|
||||
name: "My Application",
|
||||
slug: "my-application",
|
||||
workspaceId: "",
|
||||
evaluationVersion: 1,
|
||||
appIsExample: false,
|
||||
gitApplicationMetadata: undefined,
|
||||
applicationVersion: 2,
|
||||
forkingEnabled: true,
|
||||
isPublic: true,
|
||||
},
|
||||
pages: [
|
||||
{
|
||||
id: "605c435a91dea93f0eaf91ba",
|
||||
name: "Page1",
|
||||
isDefault: true,
|
||||
slug: "page-1",
|
||||
},
|
||||
{
|
||||
id: "605c435a91dea93f0eaf91bc",
|
||||
name: "Page2",
|
||||
isDefault: false,
|
||||
slug: "page-2",
|
||||
},
|
||||
],
|
||||
workspaceId: "",
|
||||
},
|
||||
};
|
||||
|
||||
describe("App viewer fork button", () => {
|
||||
afterEach(cleanup);
|
||||
|
||||
it("Fork modal trigger should not be displayed until user details are fetched", () => {
|
||||
render(
|
||||
<Provider store={getStore()}>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<PrimaryCTA />
|
||||
</ThemeProvider>
|
||||
</Provider>,
|
||||
);
|
||||
expect(screen.queryAllByTestId("fork-modal-trigger").length).toEqual(0);
|
||||
expect(mockDispatch).toHaveBeenCalledTimes(0);
|
||||
mockDispatch.mockClear();
|
||||
});
|
||||
it("Fork modal trigger should be displayed when user details are defined and user is not anonymous", () => {
|
||||
render(
|
||||
<Provider store={getStore("SET_CURRENT_USER_DETAILS")}>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<PrimaryCTA />
|
||||
</ThemeProvider>
|
||||
</Provider>,
|
||||
);
|
||||
expect(screen.queryAllByTestId("fork-modal-trigger").length).toEqual(1);
|
||||
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||
mockDispatch.mockClear();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useMemo } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import Button from "./AppViewerButton";
|
||||
import { AUTH_LOGIN_URL } from "constants/routes";
|
||||
|
|
@ -21,7 +21,6 @@ import {
|
|||
import { getCurrentUser } from "selectors/usersSelectors";
|
||||
import { ANONYMOUS_USERNAME } from "constants/userConstants";
|
||||
import ForkApplicationModal from "pages/Applications/ForkApplicationModal";
|
||||
import { getAllApplications } from "actions/applicationActions";
|
||||
import { viewerURL } from "RouteBuilder";
|
||||
import { useHistory } from "react-router";
|
||||
import { useHref } from "pages/Editor/utils";
|
||||
|
|
@ -44,7 +43,6 @@ type Props = {
|
|||
|
||||
function PrimaryCTA(props: Props) {
|
||||
const { className, url } = props;
|
||||
const dispatch = useDispatch();
|
||||
const currentUser = useSelector(getCurrentUser);
|
||||
const currentPageID = useSelector(getCurrentPageId);
|
||||
const selectedTheme = useSelector(getSelectedAppTheme);
|
||||
|
|
@ -56,7 +54,9 @@ function PrimaryCTA(props: Props) {
|
|||
|
||||
const appViewerURL = useHref(viewerURL, {
|
||||
pageId: currentPageID,
|
||||
suffix: "fork",
|
||||
params: {
|
||||
fork: "true",
|
||||
},
|
||||
});
|
||||
|
||||
// get the fork url
|
||||
|
|
@ -94,6 +94,7 @@ function PrimaryCTA(props: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
if (!currentUser) return;
|
||||
if (
|
||||
currentApplication?.forkingEnabled &&
|
||||
currentApplication?.isPublic &&
|
||||
|
|
@ -104,7 +105,7 @@ function PrimaryCTA(props: Props) {
|
|||
borderRadius={selectedTheme.properties.borderRadius.appBorderRadius}
|
||||
buttonColor={selectedTheme.properties.colors.primaryColor}
|
||||
buttonVariant="PRIMARY"
|
||||
className="t--fork-app"
|
||||
className={`t--fork-app w-full md:w-auto ${className}`}
|
||||
icon="fork"
|
||||
onClick={() => {
|
||||
history.push(forkURL);
|
||||
|
|
@ -126,9 +127,9 @@ function PrimaryCTA(props: Props) {
|
|||
}
|
||||
buttonColor={selectedTheme.properties.colors.primaryColor}
|
||||
buttonVariant="PRIMARY"
|
||||
className="t--fork-app"
|
||||
className={`t--fork-app w-full md:w-auto ${className}`}
|
||||
data-testid="fork-modal-trigger"
|
||||
icon="fork"
|
||||
onClick={() => dispatch(getAllApplications())}
|
||||
text={createMessage(FORK_APP)}
|
||||
/>
|
||||
}
|
||||
|
|
@ -157,6 +158,8 @@ function PrimaryCTA(props: Props) {
|
|||
}, [
|
||||
url,
|
||||
canEdit,
|
||||
forkURL,
|
||||
currentUser?.username,
|
||||
selectedTheme.properties.colors.primaryColor,
|
||||
selectedTheme.properties.borderRadius.appBorderRadius,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useMemo, useEffect } from "react";
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getUserApplicationsWorkspaces } from "selectors/applicationSelectors";
|
||||
import { hasCreateNewAppPermission } from "@appsmith/utils/permissionHelpers";
|
||||
|
|
@ -15,7 +15,6 @@ import {
|
|||
import { StyledDialog, ButtonWrapper, SpinnerWrapper } from "./ForkModalStyles";
|
||||
import { getIsFetchingApplications } from "selectors/applicationSelectors";
|
||||
import { useLocation } from "react-router";
|
||||
import { matchViewerForkPath } from "constants/routes";
|
||||
import { Colors } from "constants/Colors";
|
||||
import {
|
||||
CANCEL,
|
||||
|
|
@ -26,6 +25,7 @@ import {
|
|||
FORK_APP_MODAL_SUCCESS_TITLE,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { getAllApplications } from "actions/applicationActions";
|
||||
import history from "utils/history";
|
||||
|
||||
type ForkApplicationModalProps = {
|
||||
applicationId: string;
|
||||
|
|
@ -48,16 +48,9 @@ function ForkApplicationModal(props: ForkApplicationModalProps) {
|
|||
(state: AppState) => state.ui.applications.forkingApplication,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userWorkspaces.length) {
|
||||
dispatch(getAllApplications());
|
||||
}
|
||||
}, [userWorkspaces.length]);
|
||||
|
||||
const isFetchingApplications = useSelector(getIsFetchingApplications);
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const showBasedOnURL = matchViewerForkPath(pathname);
|
||||
const location = useLocation();
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
|
||||
const forkApplication = () => {
|
||||
dispatch({
|
||||
|
|
@ -98,12 +91,35 @@ function ForkApplicationModal(props: ForkApplicationModalProps) {
|
|||
? createMessage(FORK_APP_MODAL_EMPTY_TITLE)
|
||||
: createMessage(FORK_APP_MODAL_SUCCESS_TITLE);
|
||||
|
||||
const handleClose = () => {
|
||||
if (!props.setModalClose) {
|
||||
const url = new URL(window.location.href);
|
||||
if (url.searchParams.has("fork")) {
|
||||
url.searchParams.delete("fork");
|
||||
history.push(url.toString().slice(url.origin.length));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpen = () => {
|
||||
if (!props.setModalClose) {
|
||||
const url = new URL(window.location.href);
|
||||
if (!url.searchParams.has("fork")) {
|
||||
url.searchParams.append("fork", "true");
|
||||
history.push(url.toString().slice(url.origin.length));
|
||||
}
|
||||
dispatch(getAllApplications());
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledDialog
|
||||
canOutsideClickClose
|
||||
className={"fork-modal"}
|
||||
headerIcon={{ name: "fork-2", bgColor: Colors.GEYSER_LIGHT }}
|
||||
isOpen={isModalOpen || showBasedOnURL}
|
||||
isOpen={isModalOpen || queryParams.has("fork")}
|
||||
onClose={handleClose}
|
||||
onOpening={handleOpen}
|
||||
setModalClose={setModalClose}
|
||||
title={modalHeading}
|
||||
trigger={props.trigger}
|
||||
|
|
@ -136,7 +152,10 @@ function ForkApplicationModal(props: ForkApplicationModalProps) {
|
|||
<Button
|
||||
category={Category.secondary}
|
||||
disabled={forkingApplication}
|
||||
onClick={() => setModalClose && setModalClose(false)}
|
||||
onClick={() => {
|
||||
setModalClose && setModalClose(false);
|
||||
handleClose();
|
||||
}}
|
||||
size={Size.large}
|
||||
tag="button"
|
||||
text={createMessage(CANCEL)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user