From 4042b8ae2a407f572258e40a9733f8be2f85f384 Mon Sep 17 00:00:00 2001
From: zzyangh <799463087@qq.com>
Date: Fri, 12 Dec 2025 17:24:15 +0800
Subject: [PATCH 1/2] [fix]: Default jump to SQL workbench adjusted to touch
after logging in
---
packages/base/src/App.tsx | 14 +--
.../src/hooks/useNavigateToWorkbench/index.ts | 86 +++++++++++++++++
.../base/src/hooks/useSessionUser/index.tsx | 28 +++++-
packages/base/src/page/BindUser/index.tsx | 93 +++++++++++++++----
packages/base/src/page/Login/index.tsx | 68 ++++++++++----
.../Nav/UserGuideModal/UserGuideModal.tsx | 51 ++--------
.../src/page/Nav/UserGuideModal/index.tsx | 19 +---
packages/base/src/store/user/index.ts | 10 +-
8 files changed, 263 insertions(+), 106 deletions(-)
create mode 100644 packages/base/src/hooks/useNavigateToWorkbench/index.ts
diff --git a/packages/base/src/App.tsx b/packages/base/src/App.tsx
index 98c2eb73a..3578b121e 100644
--- a/packages/base/src/App.tsx
+++ b/packages/base/src/App.tsx
@@ -96,8 +96,8 @@ export const Wrapper: React.FC<{
return <>{!initRenderApp && children}>;
};
function App() {
- const { token } = useSelector((state: IReduxState) => ({
- token: state.user.token
+ const { isAfterLoggingIn } = useSelector((state: IReduxState) => ({
+ isAfterLoggingIn: !state.user.isLoggingIn && !!state.user.token
}));
const dispatch = useDispatch();
const { notificationContextHolder } = useNotificationContext();
@@ -160,7 +160,9 @@ function App() {
userOperationPermissions
]);
const elements = useRoutes(
- token ? (AuthRouterConfigData as RouteObject[]) : unAuthRouterConfig
+ isAfterLoggingIn
+ ? (AuthRouterConfigData as RouteObject[])
+ : unAuthRouterConfig
);
useChangeTheme();
const themeData = useMemo(() => {
@@ -204,10 +206,10 @@ function App() {
});
}, [dispatch, fetchModuleSupportStatus, getUserBySession, updateDriverList]);
useEffect(() => {
- if (token) {
+ if (isAfterLoggingIn) {
getInitialData();
}
- }, [token, getInitialData]);
+ }, [getInitialData, isAfterLoggingIn]);
useEffect(() => {
i18n.changeLanguage(currentLanguage);
}, [currentLanguage]);
@@ -314,7 +316,7 @@ function App() {
{notificationContextHolder}
- {elements}>}>
+ {elements}>}>
{body}
diff --git a/packages/base/src/hooks/useNavigateToWorkbench/index.ts b/packages/base/src/hooks/useNavigateToWorkbench/index.ts
new file mode 100644
index 000000000..d459cdb5f
--- /dev/null
+++ b/packages/base/src/hooks/useNavigateToWorkbench/index.ts
@@ -0,0 +1,86 @@
+import { useRequest } from 'ahooks';
+import { useDispatch, useSelector } from 'react-redux';
+import { DmsApi } from '@actiontech/shared/lib/api';
+import { ResponseCode } from '@actiontech/dms-kit';
+import useRecentlySelectedZone from '@actiontech/dms-kit/es/features/useRecentlySelectedZone';
+import { IReduxState } from '../../store';
+import { updateAvailabilityZoneTips } from '../../store/availabilityZone';
+
+const useNavigateToWorkbench = () => {
+ const dispatch = useDispatch();
+
+ const availabilityZoneTips = useSelector(
+ (state: IReduxState) => state.availabilityZone.availabilityZoneTips
+ );
+
+ const { availabilityZone, updateRecentlySelectedZone } =
+ useRecentlySelectedZone();
+
+ const {
+ runAsync: getAvailabilityZoneTipsAsync,
+ loading: getAvailabilityZoneTipsLoading
+ } = useRequest(
+ () =>
+ DmsApi.GatewayService.GetGatewayTips().then((res) => {
+ if (res.data.code === ResponseCode.SUCCESS) {
+ return res.data.data;
+ }
+ return [];
+ }),
+ {
+ onSuccess: (res) => {
+ dispatch(
+ updateAvailabilityZoneTips({
+ availabilityZoneTips: res ?? []
+ })
+ );
+ },
+ manual: true
+ }
+ );
+
+ const {
+ loading: navigateToWorkbenchLoading,
+ runAsync: navigateToWorkbenchAsync
+ } = useRequest(
+ () => {
+ return DmsApi.CloudBeaverService.GetSQLQueryConfiguration().then(
+ (res) => {
+ if (res.data.code === ResponseCode.SUCCESS) {
+ return res.data.data;
+ }
+ }
+ );
+ },
+ {
+ manual: true,
+ onSuccess: (res) => {
+ if (
+ res?.enable_sql_query &&
+ res.sql_query_root_uri &&
+ res.sql_query_root_uri !== location.pathname
+ ) {
+ // 如果当前设置了可用区 并且没有最近选择的可用区记录 则设置一个默认的可用区
+ if (!!availabilityZoneTips.length && !availabilityZone) {
+ updateRecentlySelectedZone(availabilityZoneTips[0]);
+ }
+
+ // res.sql_query_root_uri !== location.pathname 防止无限刷新
+ // 因为sql_query_root_uri是不携带origin的,只有pathname。所以开发环境localhost不可以直接跳转到CB
+ // #if [PROD]
+ window.location.href = res.sql_query_root_uri;
+ // #endif
+ }
+ }
+ }
+ );
+
+ return {
+ navigateToWorkbenchLoading,
+ navigateToWorkbenchAsync,
+ getAvailabilityZoneTipsAsync,
+ getAvailabilityZoneTipsLoading
+ };
+};
+
+export default useNavigateToWorkbench;
diff --git a/packages/base/src/hooks/useSessionUser/index.tsx b/packages/base/src/hooks/useSessionUser/index.tsx
index 96edeb043..1eab24adf 100644
--- a/packages/base/src/hooks/useSessionUser/index.tsx
+++ b/packages/base/src/hooks/useSessionUser/index.tsx
@@ -4,6 +4,8 @@ import { ResponseCode } from '@actiontech/dms-kit';
import { updateUserUid } from '../../store/user';
import { useUserInfo } from '@actiontech/shared/lib/features';
import Session from '@actiontech/shared/lib/api/base/service/Session';
+import User from '@actiontech/shared/lib/api/base/service/User';
+import { GetUserSystemEnum } from '@actiontech/shared/lib/api/base/service/common.enum';
const useSessionUser = () => {
const dispatch = useDispatch();
@@ -28,10 +30,34 @@ const useSessionUser = () => {
}
});
+ const {
+ data: shouldNavigateToWorkbench,
+ runAsync: getSessionUserInfoAsync,
+ loading: getSessionUserSystemLoading
+ } = useRequest(
+ () =>
+ Session.GetUserBySession({}).then((res) => {
+ if (res.data.code === ResponseCode.SUCCESS) {
+ const uid = res.data.data?.user_uid ?? '';
+ return User.GetUser({ user_uid: uid }).then((resp) => {
+ if (resp.data.code === ResponseCode.SUCCESS) {
+ return resp.data.data?.system === GetUserSystemEnum.WORKBENCH;
+ }
+ });
+ }
+ }),
+ {
+ manual: true
+ }
+ );
+
return {
sessionUser,
getSessionUserLoading,
- getUserBySession
+ getUserBySession,
+ getSessionUserInfoAsync,
+ shouldNavigateToWorkbench,
+ getSessionUserSystemLoading
};
};
diff --git a/packages/base/src/page/BindUser/index.tsx b/packages/base/src/page/BindUser/index.tsx
index 35056ac07..f1efb2e10 100644
--- a/packages/base/src/page/BindUser/index.tsx
+++ b/packages/base/src/page/BindUser/index.tsx
@@ -1,9 +1,15 @@
-import { useCallback, useEffect, useMemo, useRef } from 'react';
+import {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ startTransition
+} from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { Typography, Form } from 'antd';
import { OauthLoginFormFields } from './index.type';
-import { updateToken } from '../../store/user';
+import { updateToken, updateIsLoggingIn } from '../../store/user';
import { ResponseCode } from '@actiontech/dms-kit';
import OAuth2 from '@actiontech/shared/lib/api/base/service/OAuth2';
import LoginLayout from '../Login/components/LoginLayout';
@@ -24,6 +30,8 @@ import {
StorageKey,
CompanyNoticeDisplayStatusEnum
} from '@actiontech/dms-kit';
+import useSessionUser from '../../hooks/useSessionUser';
+import useNavigateToWorkbench from '../../hooks/useNavigateToWorkbench';
const BindUser = () => {
const navigate = useTypedNavigate();
const { baseTheme } = useThemeStyleData();
@@ -35,28 +43,65 @@ const BindUser = () => {
}, [extractQueries]);
useBrowserVersionTips();
const loginLock = useRef(false);
+
+ const { getSessionUserInfoAsync, getSessionUserSystemLoading } =
+ useSessionUser();
+
+ const {
+ navigateToWorkbenchAsync,
+ getAvailabilityZoneTipsAsync,
+ navigateToWorkbenchLoading,
+ getAvailabilityZoneTipsLoading
+ } = useNavigateToWorkbench();
+
const concatToken = (token = '') => {
if (!token) {
return '';
}
return `Bearer ${token}`;
};
+
const navigateToTarget = useCallback(() => {
- const encodedTarget = urlParams?.target;
- if (encodedTarget) {
- const decoded = decodeURIComponent(encodedTarget);
- const [path, targetParams] = decoded.split('?');
- if (targetParams) {
- navigate(`${path}?${targetParams}`);
- } else if (path.endsWith('cloud-beaver')) {
- navigate(`${path}?${OPEN_CLOUD_BEAVER_URL_PARAM_NAME}=true`);
+ dispatch(updateIsLoggingIn(true));
+ getSessionUserInfoAsync().then((shouldNavigateToWorkbench) => {
+ if (shouldNavigateToWorkbench) {
+ // #if [ee]
+ getAvailabilityZoneTipsAsync().then(() => {
+ navigateToWorkbenchAsync().then(() => {
+ dispatch(updateIsLoggingIn(false));
+ });
+ });
+ // #else
+ navigateToWorkbenchAsync().then(() => {
+ dispatch(updateIsLoggingIn(false));
+ });
+ // #endif
} else {
- navigate(path);
+ const encodedTarget = urlParams?.target;
+ if (encodedTarget) {
+ const decoded = decodeURIComponent(encodedTarget);
+ const [path, targetParams] = decoded.split('?');
+ if (targetParams) {
+ navigate(`${path}?${targetParams}`);
+ } else if (path.endsWith('cloud-beaver')) {
+ navigate(`${path}?${OPEN_CLOUD_BEAVER_URL_PARAM_NAME}=true`);
+ } else {
+ navigate(path);
+ }
+ } else {
+ navigate(ROUTE_PATHS.BASE.HOME);
+ }
+ dispatch(updateIsLoggingIn(false));
}
- } else {
- navigate(ROUTE_PATHS.BASE.HOME);
- }
- }, [navigate, urlParams]);
+ });
+ }, [
+ dispatch,
+ getSessionUserInfoAsync,
+ getAvailabilityZoneTipsAsync,
+ navigateToWorkbenchAsync,
+ urlParams,
+ navigate
+ ]);
const login = (values: OauthLoginFormFields) => {
const oauth2Token = urlParams?.oauth2_token;
loginLock.current = true;
@@ -67,7 +112,13 @@ const BindUser = () => {
duration: 0
});
loginLock.current = false;
- navigate(ROUTE_PATHS.BASE.LOGIN.index.path);
+ // 使用startTransition的原因如下:
+ // login 函数是表单的 onFinish 回调,属于同步用户交互事件
+ // navigate 可能会触发懒加载组件(Suspense)
+ // React 18 不允许在同步事件中直接触发 Suspense,否则会抛出错误
+ startTransition(() => {
+ navigate(ROUTE_PATHS.BASE.LOGIN.index.path);
+ });
return;
}
OAuth2.BindOauth2User({
@@ -134,9 +185,15 @@ const BindUser = () => {
urlParams?.error,
urlParams?.user_exist
]);
+ const isLoading =
+ loginLock.current ||
+ getSessionUserSystemLoading ||
+ getAvailabilityZoneTipsLoading ||
+ navigateToWorkbenchLoading;
+
return (
- {
block
htmlType="submit"
className="login-btn"
- loading={loginLock.current}
+ loading={isLoading}
>
{t('dmsLogin.oauth.submitButton')}
diff --git a/packages/base/src/page/Login/index.tsx b/packages/base/src/page/Login/index.tsx
index e5faf933a..2e6bfecd0 100644
--- a/packages/base/src/page/Login/index.tsx
+++ b/packages/base/src/page/Login/index.tsx
@@ -1,7 +1,7 @@
import { message, Form, Tabs } from 'antd';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
-import { updateToken } from '../../store/user';
+import { updateToken, updateIsLoggingIn } from '../../store/user';
import LoginLayout from './components/LoginLayout';
import { EmptyBox } from '@actiontech/dms-kit';
import { useTypedNavigate, useTypedQuery } from '@actiontech/shared';
@@ -23,6 +23,9 @@ import VerificationCodeForm from './components/VerificationCodeForm';
import OAuth2LoginForm from './components/OAuth2LoginForm';
import { DmsApi } from '@actiontech/shared/lib/api';
import { useState, useMemo, useEffect } from 'react';
+import useSessionUser from '../../hooks/useSessionUser';
+import useNavigateToWorkbench from '../../hooks/useNavigateToWorkbench';
+
const Login = () => {
const { t } = useTranslation();
useBrowserVersionTips();
@@ -44,6 +47,16 @@ const Login = () => {
const navigate = useTypedNavigate();
const extractQueries = useTypedQuery();
+ const { getSessionUserInfoAsync, getSessionUserSystemLoading } =
+ useSessionUser();
+
+ const {
+ navigateToWorkbenchAsync,
+ getAvailabilityZoneTipsAsync,
+ navigateToWorkbenchLoading,
+ getAvailabilityZoneTipsLoading
+ } = useNavigateToWorkbench();
+
const { data: oauthConfig, run: getOauth2Tips } = useRequest(
() => {
return DmsApi.OAuth2Service.GetOauth2Tips().then(
@@ -64,6 +77,7 @@ const Login = () => {
const addSession = () => {
const loginFormValues = loginForm.getFieldsValue();
const verificationCodeFormValues = verificationCodeForm.getFieldsValue();
+ dispatch(updateIsLoggingIn(true));
DmsApi.SessionService.AddSession({
session: {
username: loginFormValues.username,
@@ -86,22 +100,39 @@ const Login = () => {
token
})
);
- const encodedTarget = extractQueries(
- ROUTE_PATHS.BASE.LOGIN.index
- )?.target;
- if (encodedTarget) {
- const decoded = decodeURIComponent(encodedTarget);
- const [path, targetParams] = decoded.split('?');
- if (targetParams) {
- navigate(`${path}?${targetParams}`);
- } else if (path.endsWith('cloud-beaver')) {
- navigate(`${path}?${OPEN_CLOUD_BEAVER_URL_PARAM_NAME}=true`);
+ getSessionUserInfoAsync().then((shouldNavigateToWorkbench) => {
+ if (shouldNavigateToWorkbench) {
+ // #if [ee]
+ getAvailabilityZoneTipsAsync().then(() => {
+ navigateToWorkbenchAsync().then(() => {
+ dispatch(updateIsLoggingIn(false));
+ });
+ });
+ // #else
+ navigateToWorkbenchAsync().then(() => {
+ dispatch(updateIsLoggingIn(false));
+ });
+ // #endif
} else {
- navigate(path);
+ const encodedTarget = extractQueries(
+ ROUTE_PATHS.BASE.LOGIN.index
+ )?.target;
+ if (encodedTarget) {
+ const decoded = decodeURIComponent(encodedTarget);
+ const [path, targetParams] = decoded.split('?');
+ if (targetParams) {
+ navigate(`${path}?${targetParams}`);
+ } else if (path.endsWith('cloud-beaver')) {
+ navigate(`${path}?${OPEN_CLOUD_BEAVER_URL_PARAM_NAME}=true`);
+ } else {
+ navigate(path);
+ }
+ } else {
+ navigate(ROUTE_PATHS.BASE.HOME);
+ }
+ dispatch(updateIsLoggingIn(false));
}
- } else {
- navigate(ROUTE_PATHS.BASE.HOME);
- }
+ });
}
// #if [ee]
LocalStorageWrapper.set(
@@ -169,7 +200,12 @@ const Login = () => {
hidden={allowVerificationCode}
form={loginForm}
onSubmit={login}
- loading={loading}
+ loading={
+ loading ||
+ getSessionUserSystemLoading ||
+ getAvailabilityZoneTipsLoading ||
+ navigateToWorkbenchLoading
+ }
/>
{/* #if [ee] */}
diff --git a/packages/base/src/page/Nav/UserGuideModal/UserGuideModal.tsx b/packages/base/src/page/Nav/UserGuideModal/UserGuideModal.tsx
index 48cf16d22..c38a4f35b 100644
--- a/packages/base/src/page/Nav/UserGuideModal/UserGuideModal.tsx
+++ b/packages/base/src/page/Nav/UserGuideModal/UserGuideModal.tsx
@@ -14,9 +14,7 @@ import { useDispatch } from 'react-redux';
import { updateSystemPreference } from '../../../store/user';
import UserGuideContent from './UserGuideContent';
import { Typography } from 'antd';
-import useRecentlySelectedZone from '@actiontech/dms-kit/es/features/useRecentlySelectedZone';
-import { useSelector } from 'react-redux';
-import { IReduxState } from '../../../store';
+import useNavigateToWorkbench from '../../../hooks/useNavigateToWorkbench';
const UserGuideModal: React.FC = () => {
const { t } = useTranslation();
@@ -25,47 +23,10 @@ const UserGuideModal: React.FC = () => {
const dispatch = useDispatch();
- const availabilityZoneTips = useSelector(
- (state: IReduxState) => state.availabilityZone.availabilityZoneTips
- );
-
- const { availabilityZone, updateRecentlySelectedZone } =
- useRecentlySelectedZone();
-
const [system, setSystem] = useState(GetUserSystemEnum.MANAGEMENT);
- const { loading: openCloudBeaverLoading, run: openCloudBeaver } = useRequest(
- () => {
- return DmsApi.CloudBeaverService.GetSQLQueryConfiguration().then(
- (res) => {
- if (res.data.code === ResponseCode.SUCCESS) {
- return res.data.data;
- }
- }
- );
- },
- {
- onSuccess: (res) => {
- if (
- res?.enable_sql_query &&
- res.sql_query_root_uri &&
- res.sql_query_root_uri !== location.pathname
- ) {
- // 如果当前设置了可用区 并且没有最近选择的可用区记录 则设置一个默认的可用区
- if (!!availabilityZoneTips.length && !availabilityZone) {
- updateRecentlySelectedZone(availabilityZoneTips[0]);
- }
-
- // res.sql_query_root_uri !== location.pathname 防止无限刷新
- // 因为sql_query_root_uri是不携带origin的,只有pathname。所以开发环境localhost不可以直接跳转到CB
- // #if [PROD]
- window.location.href = res.sql_query_root_uri;
- // #endif
- }
- },
- ready: systemPreference === GetUserSystemEnum.WORKBENCH
- }
- );
+ const { navigateToWorkbenchLoading, navigateToWorkbenchAsync } =
+ useNavigateToWorkbench();
const {
loading: updateCurrentUserSystemLoading,
@@ -79,7 +40,7 @@ const UserGuideModal: React.FC = () => {
}).then((res) => {
if (res.data.code === ResponseCode.SUCCESS) {
if (system === GetUserSystemEnum.WORKBENCH) {
- openCloudBeaver();
+ navigateToWorkbenchAsync();
}
}
}),
@@ -103,7 +64,7 @@ const UserGuideModal: React.FC = () => {
system={system}
onSystemChange={(e) => setSystem(e.target.value)}
onConfirm={updateCurrentUserSystem}
- loading={updateCurrentUserSystemLoading || openCloudBeaverLoading}
+ loading={updateCurrentUserSystemLoading || navigateToWorkbenchLoading}
/>
{t('dmsMenu.userGuide.description')}
@@ -114,7 +75,7 @@ const UserGuideModal: React.FC = () => {
size="large"
onClick={updateCurrentUserSystem}
className="primary-button"
- loading={updateCurrentUserSystemLoading || openCloudBeaverLoading}
+ loading={updateCurrentUserSystemLoading || navigateToWorkbenchLoading}
>
{t('dmsMenu.userGuide.confirmButton')}
diff --git a/packages/base/src/page/Nav/UserGuideModal/index.tsx b/packages/base/src/page/Nav/UserGuideModal/index.tsx
index 6f9d48ec3..a638ee06f 100644
--- a/packages/base/src/page/Nav/UserGuideModal/index.tsx
+++ b/packages/base/src/page/Nav/UserGuideModal/index.tsx
@@ -1,24 +1,7 @@
-import { useMemo } from 'react';
import UserGuideModal from './UserGuideModal';
-import queryString from 'query-string';
-import {
- EmptyBox,
- SQL_WORKBENCH_FROM_PARAM_NAME,
- ODC_WORKBENCH_NAME
-} from '@actiontech/dms-kit';
const UserGuide = () => {
- const isNotFormODC = useMemo(() => {
- const parsedQuery = queryString.parse(location.search);
-
- return parsedQuery[SQL_WORKBENCH_FROM_PARAM_NAME] !== ODC_WORKBENCH_NAME;
- }, []);
-
- return (
-
-
-
- );
+ return ;
};
export default UserGuide;
diff --git a/packages/base/src/store/user/index.ts b/packages/base/src/store/user/index.ts
index 4c3bd4675..7042e9953 100644
--- a/packages/base/src/store/user/index.ts
+++ b/packages/base/src/store/user/index.ts
@@ -26,6 +26,7 @@ type UserReduxState = {
isUserInfoFetched: boolean;
language: SupportLanguage;
systemPreference?: GetUserSystemEnum;
+ isLoggingIn: boolean;
};
const initialState: UserReduxState = {
@@ -44,7 +45,8 @@ const initialState: UserReduxState = {
StorageKey.Language,
DEFAULT_LANGUAGE
) as SupportLanguage,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
};
const user = createSlice({
@@ -118,6 +120,9 @@ const user = createSlice({
}: PayloadAction<{ systemPreference?: GetUserSystemEnum }>
) => {
state.systemPreference = systemPreference;
+ },
+ updateIsLoggingIn: (state, { payload }: PayloadAction) => {
+ state.isLoggingIn = payload;
}
}
});
@@ -131,7 +136,8 @@ export const {
updateManagementPermissions,
updateUserUid,
updateUserInfoFetchStatus,
- updateSystemPreference
+ updateSystemPreference,
+ updateIsLoggingIn
} = user.actions;
export default user.reducer;
From 3adc9ea819ce075d732910dbc8fa71dd12d66547 Mon Sep 17 00:00:00 2001
From: zzyangh <799463087@qq.com>
Date: Fri, 12 Dec 2025 17:24:32 +0800
Subject: [PATCH 2/2] [test]: Update unit tests
---
packages/base/src/App.ce.test.tsx | 3 +-
packages/base/src/App.test.tsx | 12 +-
.../useNavigateToWorkbench/index.test.tsx | 100 ++++++++++++++
.../src/hooks/useSessionUser/index.test.tsx | 20 +++
.../base/src/page/BindUser/index.ce.test.tsx | 50 ++++++-
.../base/src/page/BindUser/index.test.tsx | 117 +++++++++++++++-
.../Login/__snapshots__/index.test.tsx.snap | 8 +-
.../base/src/page/Login/index.ce.test.tsx | 37 +++++-
packages/base/src/page/Login/index.test.tsx | 125 +++++++++++++++++-
.../__tests__/UserGuideModal.test.tsx | 114 +++-------------
.../UserGuideModal/__tests__/index.test.tsx | 15 ---
packages/base/src/store/user/index.test.ts | 51 +++++--
.../base/src/testUtils/mockHooks/data.tsx | 16 +++
.../mockHooks/mockUseNavigateToWorkbench.ts | 13 ++
.../testUtils/mockHooks/mockUseSessionUser.ts | 13 ++
15 files changed, 541 insertions(+), 153 deletions(-)
create mode 100644 packages/base/src/hooks/useNavigateToWorkbench/index.test.tsx
create mode 100644 packages/base/src/testUtils/mockHooks/mockUseNavigateToWorkbench.ts
create mode 100644 packages/base/src/testUtils/mockHooks/mockUseSessionUser.ts
diff --git a/packages/base/src/App.ce.test.tsx b/packages/base/src/App.ce.test.tsx
index c7a2a8bf1..8ea0013a9 100644
--- a/packages/base/src/App.ce.test.tsx
+++ b/packages/base/src/App.ce.test.tsx
@@ -59,7 +59,8 @@ describe('test App ce', () => {
(useSelector as jest.Mock).mockImplementation((selector) => {
return selector({
user: {
- token: 'AAh32ffdswt'
+ token: 'AAh32ffdswt',
+ isLoggingIn: false
},
nav: {
modalStatus: {
diff --git a/packages/base/src/App.test.tsx b/packages/base/src/App.test.tsx
index 76b405ec1..b01430f44 100644
--- a/packages/base/src/App.test.tsx
+++ b/packages/base/src/App.test.tsx
@@ -93,7 +93,8 @@ describe('App', () => {
(useSelector as jest.Mock).mockImplementation((selector) => {
return selector({
user: {
- token: 'AAh32ffdswt'
+ token: 'AAh32ffdswt',
+ isLoggingIn: false
},
nav: {
modalStatus: {
@@ -137,7 +138,8 @@ describe('App', () => {
(useSelector as jest.Mock).mockImplementation((selector) => {
return selector({
user: {
- token: ''
+ token: '',
+ isLoggingIn: false
}
});
});
@@ -210,12 +212,16 @@ describe('App', () => {
(useSelector as jest.Mock).mockImplementation((selector) => {
return selector({
user: {
- token: ''
+ token: '',
+ isLoggingIn: false
},
nav: {
modalStatus: {
[ModalName.Company_Notice]: false
}
+ },
+ availabilityZone: {
+ availabilityZoneTips: []
}
});
});
diff --git a/packages/base/src/hooks/useNavigateToWorkbench/index.test.tsx b/packages/base/src/hooks/useNavigateToWorkbench/index.test.tsx
new file mode 100644
index 000000000..e9e86c316
--- /dev/null
+++ b/packages/base/src/hooks/useNavigateToWorkbench/index.test.tsx
@@ -0,0 +1,100 @@
+import { act, cleanup } from '@testing-library/react';
+import { superRenderHook } from '@actiontech/shared/lib/testUtil/superRender';
+import { useDispatch, useSelector } from 'react-redux';
+import {
+ baseMockApi,
+ createSpySuccessResponse
+} from '@actiontech/shared/lib/testUtil/mockApi';
+import useNavigateToWorkbench from '.';
+import { mockUseRecentlySelectedZone } from '../../testUtils/mockHooks/mockUseRecentlySelectedZone';
+import { mockGatewayTipsData } from '@actiontech/shared/lib/testUtil/mockApi/base/gateway/data';
+
+jest.mock('react-redux', () => ({
+ ...jest.requireActual('react-redux'),
+ useDispatch: jest.fn(),
+ useSelector: jest.fn()
+}));
+
+describe('useNavigateToWorkbench', () => {
+ const dispatchSpy = jest.fn();
+ let getGatewayTipsSpy: jest.SpyInstance;
+ let getSQLQueryConfigurationSpy: jest.SpyInstance;
+
+ beforeEach(() => {
+ (useDispatch as jest.Mock).mockImplementation(() => dispatchSpy);
+ (useSelector as jest.Mock).mockImplementation((selector) => {
+ return selector({
+ availabilityZone: {
+ availabilityZoneTips: mockGatewayTipsData
+ }
+ });
+ });
+ mockUseRecentlySelectedZone();
+ getGatewayTipsSpy = baseMockApi.gateway.getGatewayTips();
+ getSQLQueryConfigurationSpy = baseMockApi.cloudBeaver.getSqlQueryUrl();
+ jest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ jest.useRealTimers();
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ it('should initialize with correct default values', () => {
+ const { result } = superRenderHook(() => useNavigateToWorkbench());
+
+ expect(result.current.navigateToWorkbenchLoading).toBeFalsy();
+ expect(result.current.getAvailabilityZoneTipsLoading).toBeFalsy();
+ expect(typeof result.current.navigateToWorkbenchAsync).toBe('function');
+ expect(typeof result.current.getAvailabilityZoneTipsAsync).toBe('function');
+ });
+
+ it('should fetch availability zone tips successfully', async () => {
+ const { result } = superRenderHook(() => useNavigateToWorkbench());
+ expect(result.current.getAvailabilityZoneTipsLoading).toBeFalsy();
+
+ act(() => {
+ result.current.getAvailabilityZoneTipsAsync();
+ });
+ expect(result.current.getAvailabilityZoneTipsLoading).toBeTruthy();
+ await act(async () => jest.advanceTimersByTime(3000));
+ expect(result.current.getAvailabilityZoneTipsLoading).toBeFalsy();
+
+ expect(getGatewayTipsSpy).toHaveBeenCalledTimes(1);
+ expect(dispatchSpy).toHaveBeenCalledWith({
+ type: 'availabilityZone/updateAvailabilityZoneTips',
+ payload: { availabilityZoneTips: mockGatewayTipsData }
+ });
+ });
+
+ it('should fetch SQL query configuration successfully', async () => {
+ const mockUpdateRecentlySelectedZone = jest.fn();
+ getSQLQueryConfigurationSpy.mockImplementation(() =>
+ createSpySuccessResponse({
+ data: {
+ enable_sql_query: true,
+ sql_query_root_uri: '/cloudbeaver'
+ }
+ })
+ );
+ mockUseRecentlySelectedZone({
+ availabilityZone: undefined,
+ updateRecentlySelectedZone: mockUpdateRecentlySelectedZone
+ });
+ const { result } = superRenderHook(() => useNavigateToWorkbench());
+ expect(result.current.navigateToWorkbenchLoading).toBeFalsy();
+
+ act(() => {
+ result.current.navigateToWorkbenchAsync();
+ });
+ expect(result.current.navigateToWorkbenchLoading).toBeTruthy();
+ await act(async () => jest.advanceTimersByTime(3000));
+ expect(result.current.navigateToWorkbenchLoading).toBeFalsy();
+
+ expect(getSQLQueryConfigurationSpy).toHaveBeenCalledTimes(1);
+ expect(mockUpdateRecentlySelectedZone).toHaveBeenCalledWith(
+ mockGatewayTipsData[0]
+ );
+ });
+});
diff --git a/packages/base/src/hooks/useSessionUser/index.test.tsx b/packages/base/src/hooks/useSessionUser/index.test.tsx
index b25b02264..c787a7341 100644
--- a/packages/base/src/hooks/useSessionUser/index.test.tsx
+++ b/packages/base/src/hooks/useSessionUser/index.test.tsx
@@ -55,4 +55,24 @@ describe('useSessionUser', () => {
});
expect(result.current.getSessionUserLoading).toBeFalsy();
});
+
+ it('should get user system data', async () => {
+ const { result } = superRenderHook(() => useSessionUser(), undefined, {
+ initStore: {
+ user: { uid: 'test' }
+ }
+ });
+ expect(result.current.getSessionUserSystemLoading).toBeFalsy();
+ expect(result.current.shouldNavigateToWorkbench).toEqual(undefined);
+
+ await act(async () => {
+ result.current.getSessionUserInfoAsync();
+ await jest.advanceTimersByTime(3000);
+ });
+ expect(getUserBySessionSpy).toHaveBeenCalledTimes(1);
+ expect(getCurrentUserSpy).toHaveBeenCalledTimes(1);
+ await act(async () => jest.advanceTimersByTime(3000));
+ expect(result.current.getSessionUserSystemLoading).toBeFalsy();
+ expect(result.current.shouldNavigateToWorkbench).toBeFalsy();
+ });
});
diff --git a/packages/base/src/page/BindUser/index.ce.test.tsx b/packages/base/src/page/BindUser/index.ce.test.tsx
index 25b41458f..a1990fd3b 100644
--- a/packages/base/src/page/BindUser/index.ce.test.tsx
+++ b/packages/base/src/page/BindUser/index.ce.test.tsx
@@ -11,6 +11,8 @@ import { getBySelector } from '@actiontech/shared/lib/testUtil/customQuery';
import { eventEmitter } from '@actiontech/dms-kit/es/utils/EventEmitter';
import EmitterKey from '@actiontech/dms-kit/es/data/EmitterKey';
import { ROUTE_PATHS } from '@actiontech/dms-kit';
+import { mockUseSessionUser } from '../../testUtils/mockHooks/mockUseSessionUser';
+import { mockUseNavigateToWorkbench } from '../../testUtils/mockHooks/mockUseNavigateToWorkbench';
jest.mock('react-router-dom', () => {
return {
@@ -27,6 +29,10 @@ jest.mock('react-redux', () => ({
describe('page/BindUser-ce', () => {
const navigateSpy = jest.fn();
const dispatchSpy = jest.fn();
+ const getSessionUserInfoAsyncSpy = jest.fn(() => Promise.resolve(false));
+ const navigateToWorkbenchAsyncSpy = jest.fn(() => Promise.resolve(undefined));
+ const getAvailabilityZoneTipsAsyncSpy = jest.fn(() => Promise.resolve([]));
+
const customRender = (path = '/user/bind') => {
return baseSuperRender(, undefined, {
routerProps: { initialEntries: [path] }
@@ -38,6 +44,24 @@ describe('page/BindUser-ce', () => {
(useDispatch as jest.Mock).mockImplementation(() => dispatchSpy);
jest.useFakeTimers();
dms.mockAllApi();
+
+ getSessionUserInfoAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve(false));
+ getAvailabilityZoneTipsAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve([]));
+ navigateToWorkbenchAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve(undefined));
+
+ mockUseSessionUser({
+ getSessionUserInfoAsync: getSessionUserInfoAsyncSpy
+ });
+ mockUseNavigateToWorkbench({
+ navigateToWorkbenchAsync: navigateToWorkbenchAsyncSpy,
+ getAvailabilityZoneTipsAsync: getAvailabilityZoneTipsAsyncSpy
+ });
});
afterEach(() => {
@@ -137,13 +161,22 @@ describe('page/BindUser-ce', () => {
user_name: 'oauth2_admin',
pwd: 'oauth2_admin'
});
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
- expect(dispatchSpy).toHaveBeenCalledWith({
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
type: 'user/updateToken',
payload: {
token: 'Bearer token'
}
});
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith('/');
});
@@ -204,13 +237,22 @@ describe('page/BindUser-ce', () => {
const search = `user_exist=true&dms_token=111111`;
customRender(`/user/bind?${search}`);
await act(async () => jest.advanceTimersByTime(300));
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
- expect(dispatchSpy).toHaveBeenCalledWith({
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
type: 'user/updateToken',
payload: {
token: 'Bearer 111111'
}
});
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith('/');
});
diff --git a/packages/base/src/page/BindUser/index.test.tsx b/packages/base/src/page/BindUser/index.test.tsx
index ec1646b49..804db14b1 100644
--- a/packages/base/src/page/BindUser/index.test.tsx
+++ b/packages/base/src/page/BindUser/index.test.tsx
@@ -14,6 +14,8 @@ import {
import { ROUTE_PATHS } from '@actiontech/dms-kit';
import BindUser from '.';
+import { mockUseSessionUser } from '../../testUtils/mockHooks/mockUseSessionUser';
+import { mockUseNavigateToWorkbench } from '../../testUtils/mockHooks/mockUseNavigateToWorkbench';
jest.mock('react-router-dom', () => {
return {
@@ -30,6 +32,10 @@ jest.mock('react-redux', () => ({
describe('page/BindUser-ee', () => {
const navigateSpy = jest.fn();
const dispatchSpy = jest.fn();
+ const getSessionUserInfoAsyncSpy = jest.fn(() => Promise.resolve(false));
+ const navigateToWorkbenchAsyncSpy = jest.fn(() => Promise.resolve(undefined));
+ const getAvailabilityZoneTipsAsyncSpy = jest.fn(() => Promise.resolve([]));
+
const customRender = (path = '/user/bind') => {
return baseSuperRender(, undefined, {
routerProps: { initialEntries: [path] }
@@ -41,6 +47,24 @@ describe('page/BindUser-ee', () => {
(useDispatch as jest.Mock).mockImplementation(() => dispatchSpy);
jest.useFakeTimers();
dms.mockAllApi();
+
+ getSessionUserInfoAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve(false));
+ getAvailabilityZoneTipsAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve([]));
+ navigateToWorkbenchAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve(undefined));
+
+ mockUseSessionUser({
+ getSessionUserInfoAsync: getSessionUserInfoAsyncSpy
+ });
+ mockUseNavigateToWorkbench({
+ navigateToWorkbenchAsync: navigateToWorkbenchAsyncSpy,
+ getAvailabilityZoneTipsAsync: getAvailabilityZoneTipsAsyncSpy
+ });
});
afterEach(() => {
@@ -142,13 +166,22 @@ describe('page/BindUser-ee', () => {
pwd: 'oauth2_admin'
});
await act(async () => jest.advanceTimersByTime(300));
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
- expect(dispatchSpy).toHaveBeenCalledWith({
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
type: 'user/updateToken',
payload: {
token: 'Bearer token'
}
});
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith('/');
expect(LocalStorageWrapperSet).toHaveBeenCalled();
@@ -187,7 +220,22 @@ describe('page/BindUser-ee', () => {
await act(async () => jest.advanceTimersByTime(3000));
expect(requestFn).toHaveBeenCalled();
await act(async () => jest.advanceTimersByTime(300));
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
+ type: 'user/updateToken',
+ payload: {
+ token: 'Bearer token'
+ }
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith('/project/test');
expect(LocalStorageWrapperSet).toHaveBeenCalled();
@@ -222,7 +270,22 @@ describe('page/BindUser-ee', () => {
await act(async () => jest.advanceTimersByTime(3000));
expect(requestFn).toHaveBeenCalled();
await act(async () => jest.advanceTimersByTime(300));
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
+ type: 'user/updateToken',
+ payload: {
+ token: 'Bearer token'
+ }
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith('/project/test?active=overview');
expect(LocalStorageWrapperSet).toHaveBeenCalled();
@@ -257,7 +320,22 @@ describe('page/BindUser-ee', () => {
await act(async () => jest.advanceTimersByTime(3000));
expect(requestFn).toHaveBeenCalled();
await act(async () => jest.advanceTimersByTime(300));
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
+ type: 'user/updateToken',
+ payload: {
+ token: 'Bearer token'
+ }
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith(
'/cloud-beaver?open_cloud_beaver=true'
@@ -321,13 +399,22 @@ describe('page/BindUser-ee', () => {
const search = `user_exist=true&dms_token=111111`;
customRender(`/user/bind?${search}`);
await act(async () => jest.advanceTimersByTime(300));
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
- expect(dispatchSpy).toHaveBeenCalledWith({
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
type: 'user/updateToken',
payload: {
token: 'Bearer 111111'
}
});
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith('/');
});
@@ -339,6 +426,22 @@ describe('page/BindUser-ee', () => {
)}`;
customRender(`/user/bind?${search}`);
await act(async () => jest.advanceTimersByTime(300));
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
+ type: 'user/updateToken',
+ payload: {
+ token: 'Bearer oauth2_token_val'
+ }
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith('/project/test');
});
diff --git a/packages/base/src/page/Login/__snapshots__/index.test.tsx.snap b/packages/base/src/page/Login/__snapshots__/index.test.tsx.snap
index 39279d83c..8769033d8 100644
--- a/packages/base/src/page/Login/__snapshots__/index.test.tsx.snap
+++ b/packages/base/src/page/Login/__snapshots__/index.test.tsx.snap
@@ -804,7 +804,7 @@ exports[`page/Login-ee render login success when has location search val render
Login With Oauth2
diff --git a/packages/base/src/page/Login/index.ce.test.tsx b/packages/base/src/page/Login/index.ce.test.tsx
index d73bb0a6e..6bd16701b 100644
--- a/packages/base/src/page/Login/index.ce.test.tsx
+++ b/packages/base/src/page/Login/index.ce.test.tsx
@@ -14,6 +14,8 @@ import {
ignoreConsoleErrors,
UtilsConsoleErrorStringsEnum
} from '@actiontech/shared/lib/testUtil/common';
+import { mockUseSessionUser } from '../../testUtils/mockHooks/mockUseSessionUser';
+import { mockUseNavigateToWorkbench } from '../../testUtils/mockHooks/mockUseNavigateToWorkbench';
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
@@ -22,6 +24,9 @@ jest.mock('react-redux', () => ({
describe('page/Login-ce', () => {
const dispatchSpy = jest.fn();
+ const getSessionUserInfoAsyncSpy = jest.fn(() => Promise.resolve(true));
+ const navigateToWorkbenchAsyncSpy = jest.fn(() => Promise.resolve(undefined));
+ const getAvailabilityZoneTipsAsyncSpy = jest.fn(() => Promise.resolve([]));
ignoreConsoleErrors([
UtilsConsoleErrorStringsEnum.UNCONNECTED_FORM_COMPONENT
@@ -35,6 +40,24 @@ describe('page/Login-ce', () => {
(useDispatch as jest.Mock).mockImplementation(() => dispatchSpy);
jest.useFakeTimers();
dms.mockAllApi();
+
+ getSessionUserInfoAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve(true));
+ getAvailabilityZoneTipsAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve([]));
+ navigateToWorkbenchAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve(undefined));
+
+ mockUseSessionUser({
+ getSessionUserInfoAsync: getSessionUserInfoAsyncSpy
+ });
+ mockUseNavigateToWorkbench({
+ navigateToWorkbenchAsync: navigateToWorkbenchAsyncSpy,
+ getAvailabilityZoneTipsAsync: getAvailabilityZoneTipsAsyncSpy
+ });
});
afterEach(() => {
@@ -76,13 +99,23 @@ describe('page/Login-ce', () => {
password: 'admin'
}
});
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
- expect(dispatchSpy).toHaveBeenCalledWith({
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
type: 'user/updateToken',
payload: {
token: `Bearer ${UserInfo.token}`
}
});
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
+ expect(navigateToWorkbenchAsyncSpy).toHaveBeenCalledTimes(1);
expect(baseElement).toMatchSnapshot();
});
diff --git a/packages/base/src/page/Login/index.test.tsx b/packages/base/src/page/Login/index.test.tsx
index 2ba9efcf4..defa4d210 100644
--- a/packages/base/src/page/Login/index.test.tsx
+++ b/packages/base/src/page/Login/index.test.tsx
@@ -29,6 +29,8 @@ import {
} from '@actiontech/shared/lib/testUtil/common';
import system from '@actiontech/shared/lib/testUtil/mockApi/base/system';
import sms from '@actiontech/shared/lib/testUtil/mockApi/base/sms';
+import { mockUseSessionUser } from '../../testUtils/mockHooks/mockUseSessionUser';
+import { mockUseNavigateToWorkbench } from '../../testUtils/mockHooks/mockUseNavigateToWorkbench';
jest.mock('react-router-dom', () => {
return {
@@ -37,6 +39,7 @@ jest.mock('react-router-dom', () => {
useSearchParams: jest.fn()
};
});
+
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: jest.fn()
@@ -47,6 +50,9 @@ describe('page/Login-ee', () => {
const navigateSpy = jest.fn();
const assignMock = jest.fn();
const useSearchParamsSpy: jest.Mock = useSearchParams as jest.Mock;
+ const getSessionUserInfoAsyncSpy = jest.fn(() => Promise.resolve(false));
+ const navigateToWorkbenchAsyncSpy = jest.fn(() => Promise.resolve(undefined));
+ const getAvailabilityZoneTipsAsyncSpy = jest.fn(() => Promise.resolve([]));
let requestGetOauth2Tip: jest.SpyInstance;
let requestGetLoginBasicConfig: jest.SpyInstance;
let verifyUserLoginSpy: jest.SpyInstance;
@@ -71,6 +77,24 @@ describe('page/Login-ee', () => {
verifyUserLoginSpy = dms.verifyUserLogin();
sendSmsCodeSpy = sms.sendSmsCode();
dms.mockAllApi();
+
+ getSessionUserInfoAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve(false));
+ getAvailabilityZoneTipsAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve([]));
+ navigateToWorkbenchAsyncSpy
+ .mockClear()
+ .mockImplementation(() => Promise.resolve(undefined));
+
+ mockUseSessionUser({
+ getSessionUserInfoAsync: getSessionUserInfoAsyncSpy
+ });
+ mockUseNavigateToWorkbench({
+ navigateToWorkbenchAsync: navigateToWorkbenchAsyncSpy,
+ getAvailabilityZoneTipsAsync: getAvailabilityZoneTipsAsyncSpy
+ });
});
afterEach(() => {
@@ -231,13 +255,22 @@ describe('page/Login-ee', () => {
}
});
await act(async () => jest.advanceTimersByTime(3000));
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
- expect(dispatchSpy).toHaveBeenCalledWith({
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
type: 'user/updateToken',
payload: {
token: `Bearer ${UserInfo.token}`
}
});
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalledWith(ROUTE_PATHS.BASE.HOME);
});
@@ -305,13 +338,22 @@ describe('page/Login-ee', () => {
}
});
await act(async () => jest.advanceTimersByTime(3000));
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
- expect(dispatchSpy).toHaveBeenCalledWith({
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
type: 'user/updateToken',
payload: {
token: `Bearer ${UserInfo.token}`
}
});
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith('/index1');
expect(LocalStorageWrapperSet).toHaveBeenCalled();
@@ -321,7 +363,7 @@ describe('page/Login-ee', () => {
);
});
- it('render with other search val', async () => {
+ it('render with cloud-beaver path', async () => {
const requestLogin = dms.addSession();
const LocalStorageWrapperSet = jest.spyOn(LocalStorageWrapper, 'set');
useSearchParamsSpy.mockReturnValue([
@@ -364,13 +406,22 @@ describe('page/Login-ee', () => {
}
});
await act(async () => jest.advanceTimersByTime(3000));
- expect(dispatchSpy).toHaveBeenCalledTimes(1);
- expect(dispatchSpy).toHaveBeenCalledWith({
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
type: 'user/updateToken',
payload: {
token: `Bearer ${UserInfo.token}`
}
});
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith(
`/project/700300/cloud-beaver?${OPEN_CLOUD_BEAVER_URL_PARAM_NAME}=true`
@@ -425,6 +476,22 @@ describe('page/Login-ee', () => {
}
});
await act(async () => jest.advanceTimersByTime(3000));
+ expect(dispatchSpy).toHaveBeenCalledTimes(3);
+ expect(dispatchSpy).toHaveBeenNthCalledWith(1, {
+ type: 'user/updateIsLoggingIn',
+ payload: true
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(2, {
+ type: 'user/updateToken',
+ payload: {
+ token: `Bearer ${UserInfo.token}`
+ }
+ });
+ expect(dispatchSpy).toHaveBeenNthCalledWith(3, {
+ type: 'user/updateIsLoggingIn',
+ payload: false
+ });
+ expect(getSessionUserInfoAsyncSpy).toHaveBeenCalledTimes(1);
expect(navigateSpy).toHaveBeenCalled();
expect(navigateSpy).toHaveBeenCalledWith('/transit?from=cloudbeaver');
expect(LocalStorageWrapperSet).toHaveBeenCalled();
@@ -435,6 +502,50 @@ describe('page/Login-ee', () => {
});
});
+ it('should navigate to workbench when shouldNavigateToWorkbench is true', async () => {
+ const requestLogin = dms.addSession();
+ getSessionUserInfoAsyncSpy.mockImplementation(() => Promise.resolve(true));
+
+ requestGetOauth2Tip.mockImplementation(() =>
+ createSpySuccessResponse({
+ data: {
+ enable_oauth2: false,
+ login_tip: 'Login no Oauth2'
+ }
+ })
+ );
+
+ requestGetLoginBasicConfig.mockImplementation(() =>
+ createSpySuccessResponse({
+ data: {
+ login_button_text: '登录',
+ disable_user_pwd_login: false
+ }
+ })
+ );
+
+ const { baseElement } = customRender();
+ await act(async () => jest.advanceTimersByTime(0));
+ await act(async () => jest.advanceTimersByTime(3300));
+
+ fireEvent.change(getBySelector('#username', baseElement), {
+ target: { value: 'admin' }
+ });
+ await act(async () => jest.advanceTimersByTime(300));
+ fireEvent.change(getBySelector('#password', baseElement), {
+ target: { value: 'admin' }
+ });
+ await act(async () => jest.advanceTimersByTime(300));
+ fireEvent.click(screen.getByText('登 录'));
+ await act(async () => jest.advanceTimersByTime(300));
+ expect(verifyUserLoginSpy).toHaveBeenCalledTimes(1);
+ await act(async () => jest.advanceTimersByTime(3000));
+ expect(requestLogin).toHaveBeenCalledTimes(1);
+ await act(async () => jest.advanceTimersByTime(3000));
+ expect(getAvailabilityZoneTipsAsyncSpy).toHaveBeenCalledTimes(1);
+ expect(navigateToWorkbenchAsyncSpy).toHaveBeenCalledTimes(1);
+ });
+
it('should enable login button when user is admin regardless of disable_user_pwd_login setting', async () => {
requestGetOauth2Tip.mockImplementation(() =>
createSpySuccessResponse({
diff --git a/packages/base/src/page/Nav/UserGuideModal/__tests__/UserGuideModal.test.tsx b/packages/base/src/page/Nav/UserGuideModal/__tests__/UserGuideModal.test.tsx
index aab173f68..22605c568 100644
--- a/packages/base/src/page/Nav/UserGuideModal/__tests__/UserGuideModal.test.tsx
+++ b/packages/base/src/page/Nav/UserGuideModal/__tests__/UserGuideModal.test.tsx
@@ -67,101 +67,15 @@ describe('UserGuideModal', () => {
expect(screen.queryByText('请选择默认进入的页面')).not.toBeInTheDocument();
});
- it('should open CloudBeaver automatically when systemPreference is WORKBENCH', async () => {
- mockUseCurrentUser({
- systemPreference: GetUserSystemEnum.WORKBENCH
- });
-
- getSQLQueryConfigurationSpy.mockImplementation(() =>
- createSpySuccessResponse({
- data: {
- enable_sql_query: true,
- sql_query_root_uri: '/cloudbeaver'
- }
- })
- );
-
- superRender();
-
- await act(async () => jest.advanceTimersByTime(3000));
-
- expect(getSQLQueryConfigurationSpy).toHaveBeenCalled();
- });
-
- it('should set default zone when zone options is not null and availabilityZone is undefined', async () => {
- mockUseCurrentUser({
- systemPreference: GetUserSystemEnum.WORKBENCH
- });
-
- (useSelector as jest.Mock).mockImplementation((selector) => {
- return selector({
- availabilityZone: {
- availabilityZoneTips: [
- {
- name: 'test',
- uid: 'test'
- }
- ]
- }
- });
- });
-
- const mockUpdateRecentlySelectedZone = jest.fn();
- mockUseRecentlySelectedZone({
- availabilityZone: undefined,
- updateRecentlySelectedZone: mockUpdateRecentlySelectedZone
- });
-
- getSQLQueryConfigurationSpy.mockImplementation(() =>
- createSpySuccessResponse({
- data: {
- enable_sql_query: true,
- sql_query_root_uri: '/cloudbeaver'
- }
- })
- );
+ it('should call updateCurrentUser API and CloudBeaver API when confirm button is clicked with WORKBENCH system', async () => {
+ mockUseCurrentUser({ systemPreference: undefined });
superRender();
- await act(async () => jest.advanceTimersByTime(3000));
-
- expect(getSQLQueryConfigurationSpy).toHaveBeenCalled();
- expect(mockUpdateRecentlySelectedZone).toHaveBeenCalledTimes(1);
- });
-
- it('should not open CloudBeaver automatically when systemPreference is WORKBENCH and sql_query_root_uri is the same as location.pathname', async () => {
- Object.defineProperty(window, 'location', {
- value: {
- ...originLocation,
- pathname: '/cloudbeaver'
- },
- writable: true
- });
-
- mockUseCurrentUser({
- systemPreference: GetUserSystemEnum.WORKBENCH
- });
-
- getSQLQueryConfigurationSpy.mockImplementation(() =>
- createSpySuccessResponse({
- data: {
- enable_sql_query: true,
- sql_query_root_uri: '/cloudbeaver'
- }
- })
+ const workbenchRadio = screen.getByDisplayValue(
+ GetUserSystemEnum.WORKBENCH
);
-
- superRender();
-
- await act(async () => jest.advanceTimersByTime(3000));
-
- expect(getSQLQueryConfigurationSpy).toHaveBeenCalled();
- });
-
- it('should call updateCurrentUser API when confirm button is clicked with MANAGEMENT system', async () => {
- mockUseCurrentUser({ systemPreference: undefined });
-
- superRender();
+ fireEvent.click(workbenchRadio);
const confirmButton = screen.getByText('确认并进入');
fireEvent.click(confirmButton);
@@ -170,24 +84,26 @@ describe('UserGuideModal', () => {
expect(updateCurrentUserSpy).toHaveBeenCalledWith({
current_user: {
- system: GetUserSystemEnum.MANAGEMENT
+ system: GetUserSystemEnum.WORKBENCH
}
});
+ expect(getSQLQueryConfigurationSpy).toHaveBeenCalledTimes(1);
+
expect(mockDispatch).toHaveBeenCalledWith(
- updateSystemPreference({ systemPreference: GetUserSystemEnum.MANAGEMENT })
+ updateSystemPreference({ systemPreference: GetUserSystemEnum.WORKBENCH })
);
});
- it('should call updateCurrentUser API and CloudBeaver API when confirm button is clicked with WORKBENCH system', async () => {
+ it('should call updateCurrentUser API when confirm button is clicked with MANAGEMENT system', async () => {
mockUseCurrentUser({ systemPreference: undefined });
superRender();
- const workbenchRadio = screen.getByDisplayValue(
- GetUserSystemEnum.WORKBENCH
+ const managementRadio = screen.getByDisplayValue(
+ GetUserSystemEnum.MANAGEMENT
);
- fireEvent.click(workbenchRadio);
+ fireEvent.click(managementRadio);
const confirmButton = screen.getByText('确认并进入');
fireEvent.click(confirmButton);
@@ -196,12 +112,12 @@ describe('UserGuideModal', () => {
expect(updateCurrentUserSpy).toHaveBeenCalledWith({
current_user: {
- system: GetUserSystemEnum.WORKBENCH
+ system: GetUserSystemEnum.MANAGEMENT
}
});
expect(mockDispatch).toHaveBeenCalledWith(
- updateSystemPreference({ systemPreference: GetUserSystemEnum.WORKBENCH })
+ updateSystemPreference({ systemPreference: GetUserSystemEnum.MANAGEMENT })
);
});
});
diff --git a/packages/base/src/page/Nav/UserGuideModal/__tests__/index.test.tsx b/packages/base/src/page/Nav/UserGuideModal/__tests__/index.test.tsx
index f8eb2eb33..247d005ba 100644
--- a/packages/base/src/page/Nav/UserGuideModal/__tests__/index.test.tsx
+++ b/packages/base/src/page/Nav/UserGuideModal/__tests__/index.test.tsx
@@ -7,10 +7,6 @@ import {
import { useDispatch, useSelector } from 'react-redux';
import UserGuide from '../index';
import { mockUseRecentlySelectedZone } from '../../../../testUtils/mockHooks/mockUseRecentlySelectedZone';
-import {
- SQL_WORKBENCH_FROM_PARAM_NAME,
- ODC_WORKBENCH_NAME
-} from '@actiontech/dms-kit';
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
@@ -47,15 +43,4 @@ describe('UserGuide', () => {
expect(baseElement).toMatchSnapshot();
expect(screen.getByText('请选择默认进入的页面')).toBeInTheDocument();
});
-
- it('should render null when location form ODC', async () => {
- Object.defineProperty(window, 'location', {
- value: {
- search: `?${SQL_WORKBENCH_FROM_PARAM_NAME}=${ODC_WORKBENCH_NAME}`
- },
- writable: true
- });
- superRender();
- expect(screen.queryByText('请选择默认进入的页面')).not.toBeInTheDocument();
- });
});
diff --git a/packages/base/src/store/user/index.test.ts b/packages/base/src/store/user/index.test.ts
index 5de8fc3a6..ef96c401d 100644
--- a/packages/base/src/store/user/index.test.ts
+++ b/packages/base/src/store/user/index.test.ts
@@ -7,7 +7,8 @@ import reducers, {
updateBindProjects,
updateManagementPermissions,
updateLanguage,
- updateSystemPreference
+ updateSystemPreference,
+ updateIsLoggingIn
} from '.';
import { IReduxState } from '..';
import { LocalStorageWrapper } from '@actiontech/dms-kit';
@@ -33,7 +34,8 @@ describe('store user', () => {
role: '',
isUserInfoFetched: false,
language: SupportLanguage.zhCN,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
};
it('should update token when dispatch updateToken action', () => {
@@ -54,7 +56,8 @@ describe('store user', () => {
managementPermissions: [],
role: '',
isUserInfoFetched: false,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
});
});
@@ -76,7 +79,8 @@ describe('store user', () => {
managementPermissions: [],
role: '',
isUserInfoFetched: false,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
});
});
@@ -99,7 +103,8 @@ describe('store user', () => {
managementPermissions: [],
role: '',
isUserInfoFetched: false,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
});
});
@@ -122,7 +127,8 @@ describe('store user', () => {
managementPermissions: [],
role: SystemRole.admin,
isUserInfoFetched: false,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
});
});
@@ -144,7 +150,8 @@ describe('store user', () => {
managementPermissions: [],
role: '',
isUserInfoFetched: false,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
});
});
@@ -161,7 +168,8 @@ describe('store user', () => {
managementPermissions: [],
role: '',
isUserInfoFetched: true,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
});
});
@@ -191,7 +199,8 @@ describe('store user', () => {
managementPermissions: [],
role: '',
isUserInfoFetched: false,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
});
});
@@ -214,7 +223,8 @@ describe('store user', () => {
managementPermissions: mockManagementPermissions,
role: '',
isUserInfoFetched: false,
- systemPreference: undefined
+ systemPreference: undefined,
+ isLoggingIn: false
});
});
@@ -236,7 +246,26 @@ describe('store user', () => {
managementPermissions: [],
role: '',
isUserInfoFetched: false,
- systemPreference: GetUserSystemEnum.MANAGEMENT
+ systemPreference: GetUserSystemEnum.MANAGEMENT,
+ isLoggingIn: false
+ });
+ });
+
+ it('should update systemPreference when dispatch updateIsLoggingIn action', () => {
+ const newState = reducers(state, updateIsLoggingIn(true));
+ expect(newState).not.toBe(state);
+ expect(newState).toEqual({
+ username: '',
+ uid: '',
+ token: '',
+ theme: SupportTheme.LIGHT,
+ language: SupportLanguage.zhCN,
+ bindProjects: [],
+ managementPermissions: [],
+ role: '',
+ isUserInfoFetched: false,
+ systemPreference: undefined,
+ isLoggingIn: true
});
});
});
diff --git a/packages/base/src/testUtils/mockHooks/data.tsx b/packages/base/src/testUtils/mockHooks/data.tsx
index d572c2146..7fce6174b 100644
--- a/packages/base/src/testUtils/mockHooks/data.tsx
+++ b/packages/base/src/testUtils/mockHooks/data.tsx
@@ -13,3 +13,19 @@ export const mockUseRecentlySelectedZoneData = {
verifyRecentlySelectedZoneRecord: jest.fn(),
clearRecentlySelectedZone: jest.fn()
};
+
+export const mockUseSessionUserData = {
+ sessionUser: undefined,
+ getSessionUserLoading: false,
+ getUserBySession: jest.fn(),
+ getSessionUserInfoAsync: jest.fn(() => Promise.resolve(false)),
+ shouldNavigateToWorkbench: false,
+ getSessionUserSystemLoading: false
+};
+
+export const mockUseNavigateToWorkbenchData = {
+ navigateToWorkbenchAsync: jest.fn(() => Promise.resolve(undefined)),
+ getAvailabilityZoneTipsAsync: jest.fn(() => Promise.resolve([])),
+ navigateToWorkbenchLoading: false,
+ getAvailabilityZoneTipsLoading: false
+};
diff --git a/packages/base/src/testUtils/mockHooks/mockUseNavigateToWorkbench.ts b/packages/base/src/testUtils/mockHooks/mockUseNavigateToWorkbench.ts
new file mode 100644
index 000000000..dc5a7cfbe
--- /dev/null
+++ b/packages/base/src/testUtils/mockHooks/mockUseNavigateToWorkbench.ts
@@ -0,0 +1,13 @@
+import * as useNavigateToWorkbench from '../../hooks/useNavigateToWorkbench';
+import { mockUseNavigateToWorkbenchData } from './data';
+
+export const mockUseNavigateToWorkbench = (
+ data?: Partial
+) => {
+ const spy = jest.spyOn(useNavigateToWorkbench, 'default');
+ spy.mockImplementation(() => ({
+ ...mockUseNavigateToWorkbenchData,
+ ...data
+ }));
+ return spy;
+};
diff --git a/packages/base/src/testUtils/mockHooks/mockUseSessionUser.ts b/packages/base/src/testUtils/mockHooks/mockUseSessionUser.ts
new file mode 100644
index 000000000..1e02d1d6c
--- /dev/null
+++ b/packages/base/src/testUtils/mockHooks/mockUseSessionUser.ts
@@ -0,0 +1,13 @@
+import * as useSessionUser from '../../hooks/useSessionUser';
+import { mockUseSessionUserData } from './data';
+
+export const mockUseSessionUser = (
+ data?: Partial
+) => {
+ const spy = jest.spyOn(useSessionUser, 'default');
+ spy.mockImplementation(() => ({
+ ...mockUseSessionUserData,
+ ...data
+ }));
+ return spy;
+};