This is an automated email from the ASF dual-hosted git repository.
imbajin pushed a commit to branch hubble-dev
in repository https://gitbox.apache.org/repos/asf/hugegraph-toolchain.git
The following commit(s) were added to refs/heads/hubble-dev by this push:
new c171baed fix(hubble): address review feedback
c171baed is described below
commit c171baed970ee4f23835e9aae7b85d75516c1800
Author: imbajin <[email protected]>
AuthorDate: Sun Jul 12 21:48:32 2026 +0800
fix(hubble): address review feedback
- align favorite-name validation across all editors
- show localized validation guidance for invalid names
- allow navigation rows to grow for disabled reasons
- add Kotlin dependency to the packaged license
- cover all favorite creation and rename flows
---
hugegraph-dist/release-docs/LICENSE | 1 +
.../src/components/FavoriteNameInput/index.js | 46 +++++++++
.../src/components/FavoriteNameInput/index.test.js | 44 ++++++++
.../algorithm/LogsDetail/ExecuteLog/index.js | 13 +--
.../modules/algorithm/LogsDetail/Favorite/index.js | 10 +-
.../analysis/LogsDetail/ExecuteLog/index.js | 13 +--
.../modules/analysis/LogsDetail/Favorite/index.js | 10 +-
.../analysis/QueryBar/ContentCommon/index.js | 9 +-
.../analysis/QueryBar/ContentCommon/index.test.js | 3 +
.../src/modules/favorite-name-validation.test.js | 113 +++++++++++++++++++++
.../src/modules/navigation/Home/index.module.scss | 2 +-
hugegraph-hubble/hubble-fe/src/utils/rules.test.js | 1 +
12 files changed, 230 insertions(+), 35 deletions(-)
diff --git a/hugegraph-dist/release-docs/LICENSE
b/hugegraph-dist/release-docs/LICENSE
index 7543d082..6b0c88c0 100644
--- a/hugegraph-dist/release-docs/LICENSE
+++ b/hugegraph-dist/release-docs/LICENSE
@@ -484,6 +484,7 @@ See licenses/ for text of these licenses.
hugegraph-hubble/ui/favicon.ico
(Apache License, Version 2.0) * kotlin-stdlib
(org.jetbrains.kotlin:kotlin-stdlib:1.6.20 -
https://github.com/JetBrains/kotlin)
(Apache License, Version 2.0) * kotlin-stdlib-common
(org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31 -
https://github.com/JetBrains/kotlin)
+ (Apache License, Version 2.0) * kotlin-stdlib-common
(org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20 -
https://github.com/JetBrains/kotlin)
(Apache License, Version 2.0) * kotlin-stdlib-jdk7
(org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71 -
https://github.com/JetBrains/kotlin)
(Apache License, Version 2.0) * kotlin-stdlib-jdk7
(org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10 -
https://github.com/JetBrains/kotlin)
(Apache License, Version 2.0) * kotlin-stdlib-jdk8
(org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71 -
https://github.com/JetBrains/kotlin)
diff --git
a/hugegraph-hubble/hubble-fe/src/components/FavoriteNameInput/index.js
b/hugegraph-hubble/hubble-fe/src/components/FavoriteNameInput/index.js
new file mode 100644
index 00000000..cb1915d5
--- /dev/null
+++ b/hugegraph-hubble/hubble-fe/src/components/FavoriteNameInput/index.js
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Input} from 'antd';
+import {useTranslation} from 'react-i18next';
+
+import {isValidFavoriteName} from '../../utils/rules';
+
+const FavoriteNameInput = props => {
+ const {t} = useTranslation();
+ const {value, ...inputProps} = props;
+ const invalid = value && !isValidFavoriteName(value);
+
+ return (
+ <>
+ <Input
+ {...inputProps}
+ value={value}
+ showCount
+ maxLength={48}
+ status={invalid ? 'error' : undefined}
+ />
+ {invalid && (
+ <div role='alert' style={{color: '#ff4d4f', fontSize: '12px'}}>
+ {t('common.validation.favorite_name_rule')}
+ </div>
+ )}
+ </>
+ );
+};
+
+export default FavoriteNameInput;
diff --git
a/hugegraph-hubble/hubble-fe/src/components/FavoriteNameInput/index.test.js
b/hugegraph-hubble/hubble-fe/src/components/FavoriteNameInput/index.test.js
new file mode 100644
index 00000000..ee6a6de8
--- /dev/null
+++ b/hugegraph-hubble/hubble-fe/src/components/FavoriteNameInput/index.test.js
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {fireEvent, render, screen} from '@testing-library/react';
+import {useCallback, useState} from 'react';
+
+import FavoriteNameInput from './index';
+
+jest.mock('react-i18next', () => ({
+ initReactI18next: {type: '3rdParty', init: jest.fn()},
+ useTranslation: () => ({t: key => key}),
+}));
+
+test('explains invalid favorite names and clears the error for valid names',
() => {
+ const FavoriteNameEditor = () => {
+ const [name, setName] = useState('');
+ const onChange = useCallback(e => setName(e.target.value), []);
+ return <FavoriteNameInput value={name} onChange={onChange} />;
+ };
+ render(<FavoriteNameEditor />);
+
+ const input = screen.getByRole('textbox');
+ fireEvent.change(input, {target: {value: 'query-name'}});
+ expect(screen.getByRole('alert')).toHaveTextContent(
+ 'common.validation.favorite_name_rule'
+ );
+
+ fireEvent.change(input, {target: {value: 'query_name'}});
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument();
+});
diff --git
a/hugegraph-hubble/hubble-fe/src/modules/algorithm/LogsDetail/ExecuteLog/index.js
b/hugegraph-hubble/hubble-fe/src/modules/algorithm/LogsDetail/ExecuteLog/index.js
index 310309e0..d4cf55c0 100644
---
a/hugegraph-hubble/hubble-fe/src/modules/algorithm/LogsDetail/ExecuteLog/index.js
+++
b/hugegraph-hubble/hubble-fe/src/modules/algorithm/LogsDetail/ExecuteLog/index.js
@@ -22,8 +22,10 @@
import React, {useState, useCallback} from 'react';
import {useTranslation} from 'react-i18next';
-import {Button, Table, Space, Tag, Input, Popconfirm} from 'antd';
+import {Button, Table, Space, Tag, Popconfirm} from 'antd';
import ExecutionContent from '../../../../components/ExecutionContent';
+import FavoriteNameInput from '../../../../components/FavoriteNameInput';
+import {isValidFavoriteName} from '../../../../utils/rules';
import c from './index.module.scss';
const EXECUTE_TYPE_KEY = {
@@ -67,17 +69,14 @@ const ExecuteLog = props => {
} = props;
const [favoriteName, setFavoriteName] = useState();
- const [disabledFavorite, setDisabledFavorite] = useState(true);
const onFavoraiteName = useCallback(
e => {
setFavoriteName(e.target.value);
- e.target.value ? setDisabledFavorite(false) :
setDisabledFavorite(true);
}, []);
const onFavoriteCard = useCallback(() => {
setFavoriteName('');
- setDisabledFavorite(true);
}, []);
const updateAddCollection = useCallback(
@@ -97,11 +96,9 @@ const ExecuteLog = props => {
<div style={{marginBottom: '16px'}}>
{t('analysis.logs.favorite_statement')}
</div>
- <Input
+ <FavoriteNameInput
style={{marginBottom: '18px'}}
placeholder={t('analysis.logs.favorite_name_placeholder')}
- showCount
- maxLength={48}
value={favoriteName}
onChange={onFavoraiteName}
/>
@@ -169,7 +166,7 @@ const ExecuteLog = props => {
placement="left"
title={favoriteContent(rowData)}
onConfirm={createValueHandler(onAddFavorite,
rowData.content)}
- okButtonProps={{disabled: disabledFavorite}}
+ okButtonProps={{disabled:
!isValidFavoriteName(favoriteName)}}
okText={t('analysis.logs.action.favorite')}
cancelText={t('common.action.cancel')}
>
diff --git
a/hugegraph-hubble/hubble-fe/src/modules/algorithm/LogsDetail/Favorite/index.js
b/hugegraph-hubble/hubble-fe/src/modules/algorithm/LogsDetail/Favorite/index.js
index dbbbe8ae..7a90e519 100644
---
a/hugegraph-hubble/hubble-fe/src/modules/algorithm/LogsDetail/Favorite/index.js
+++
b/hugegraph-hubble/hubble-fe/src/modules/algorithm/LogsDetail/Favorite/index.js
@@ -25,6 +25,8 @@ import {useTranslation} from 'react-i18next';
import Highlighter from 'react-highlight-words';
import {Button, Table, Input, Popconfirm, Modal} from 'antd';
import ExecutionContent from '../../../../components/ExecutionContent';
+import FavoriteNameInput from '../../../../components/FavoriteNameInput';
+import {isValidFavoriteName} from '../../../../utils/rules';
import c from './index.module.scss';
const createValueHandler = (handler, value) => () => handler(value);
@@ -48,7 +50,6 @@ const Favorite = props => {
const [favoriteName, setFavoriteName] = useState();
const [searchCache, setSearchCache] = useState('');
const [search, setSearch] = useState('');
- const [isDisabledName, setDisabledName] = useState(false);
const changeCollection = useCallback(
rowData => {
@@ -72,7 +73,6 @@ const Favorite = props => {
const onChangeFavoraiteName = useCallback(
e => {
setFavoriteName(e.target.value);
- e.target.value ? setDisabledName(false) : setDisabledName(true);
}, []);
const onConfirm = id => {
@@ -90,11 +90,9 @@ const Favorite = props => {
<div style={{marginBottom: '16px'}}>
{t('analysis.logs.edit_name')}
</div>
- <Input
+ <FavoriteNameInput
style={{marginBottom: '18px'}}
placeholder={t('analysis.logs.favorite_name_placeholder')}
- showCount
- maxLength={48}
value={favoriteName}
onChange={onChangeFavoraiteName}
/>
@@ -159,7 +157,7 @@ const Favorite = props => {
title={editFavoriteForm}
onConfirm={createValueHandler(onSaveEditFavorite,
rowData)}
okText={t('analysis.logs.action.save')}
- okButtonProps={{disabled: isDisabledName}}
+ okButtonProps={{disabled:
!isValidFavoriteName(favoriteName)}}
cancelText={t('common.action.cancel')}
>
<Button
diff --git
a/hugegraph-hubble/hubble-fe/src/modules/analysis/LogsDetail/ExecuteLog/index.js
b/hugegraph-hubble/hubble-fe/src/modules/analysis/LogsDetail/ExecuteLog/index.js
index 2dcb9a94..b29f02dc 100644
---
a/hugegraph-hubble/hubble-fe/src/modules/analysis/LogsDetail/ExecuteLog/index.js
+++
b/hugegraph-hubble/hubble-fe/src/modules/analysis/LogsDetail/ExecuteLog/index.js
@@ -22,8 +22,10 @@
import {useState, useCallback} from 'react';
import {useTranslation} from 'react-i18next';
-import {Button, Table, Space, Tag, Input, Popconfirm} from 'antd';
+import {Button, Table, Space, Tag, Popconfirm} from 'antd';
import ExecutionContent from '../../../../components/ExecutionContent';
+import FavoriteNameInput from '../../../../components/FavoriteNameInput';
+import {isValidFavoriteName} from '../../../../utils/rules';
import c from './index.module.scss';
const EXECUTE_TYPE_KEY = {
@@ -116,7 +118,6 @@ const ExecuteLog = props => {
} = props;
const [favoriteName, setFavoriteName] = useState();
- const [disabledFavorite, setDisabledFavorite] = useState(true);
const loadStatements = useCallback(
(text, rowData, index) => {
@@ -130,7 +131,6 @@ const ExecuteLog = props => {
const onFavoraiteName = useCallback(
e => {
setFavoriteName(e.target.value);
- e.target.value ? setDisabledFavorite(false) :
setDisabledFavorite(true);
},
[]
);
@@ -138,7 +138,6 @@ const ExecuteLog = props => {
const onFavoriteCard = useCallback(
() => {
setFavoriteName('');
- setDisabledFavorite(true);
},
[]
);
@@ -163,11 +162,9 @@ const ExecuteLog = props => {
<div style={{marginBottom: '16px'}}>
{t('analysis.logs.favorite_statement')}
</div>
- <Input
+ <FavoriteNameInput
style={{marginBottom: '18px'}}
placeholder={t('analysis.logs.favorite_name_placeholder')}
- showCount
- maxLength={48}
value={favoriteName}
onChange={onFavoraiteName}
/>
@@ -254,7 +251,7 @@ const ExecuteLog = props => {
onAddFavorite={onAddFavorite}
onFavoriteCard={onFavoriteCard}
loadStatements={loadStatements}
- disabledFavorite={disabledFavorite}
+ disabledFavorite={!isValidFavoriteName(favoriteName)}
t={t}
/>
);
diff --git
a/hugegraph-hubble/hubble-fe/src/modules/analysis/LogsDetail/Favorite/index.js
b/hugegraph-hubble/hubble-fe/src/modules/analysis/LogsDetail/Favorite/index.js
index ae6d74f4..5da1be01 100644
---
a/hugegraph-hubble/hubble-fe/src/modules/analysis/LogsDetail/Favorite/index.js
+++
b/hugegraph-hubble/hubble-fe/src/modules/analysis/LogsDetail/Favorite/index.js
@@ -24,6 +24,8 @@ import React, {useState, useCallback} from 'react';
import {useTranslation} from 'react-i18next';
import {Button, Table, Input, Popconfirm, Modal} from 'antd';
import ExecutionContent from '../../../../components/ExecutionContent';
+import FavoriteNameInput from '../../../../components/FavoriteNameInput';
+import {isValidFavoriteName} from '../../../../utils/rules';
import Highlighter from 'react-highlight-words';
import c from './index.module.scss';
@@ -98,7 +100,6 @@ const Favorite = props => {
const [favoriteName, setFavoriteName] = useState();
const [searchCache, setSearchCache] = useState('');
const [search, setSearch] = useState('');
- const [isDisabledName, setDisabledName] = useState(false);
const loadStatements = useCallback(
(content, index) => {
@@ -135,7 +136,6 @@ const Favorite = props => {
const onChangeFavoraiteName = useCallback(
e => {
setFavoriteName(e.target.value);
- e.target.value ? setDisabledName(false) : setDisabledName(true);
},
[]
);
@@ -155,11 +155,9 @@ const Favorite = props => {
<div style={{marginBottom: '16px'}}>
{t('analysis.logs.edit_name')}
</div>
- <Input
+ <FavoriteNameInput
style={{marginBottom: '18px'}}
placeholder={t('analysis.logs.favorite_name_placeholder')}
- showCount
- maxLength={48}
value={favoriteName}
onChange={onChangeFavoraiteName}
/>
@@ -225,7 +223,7 @@ const Favorite = props => {
onEditFavorite={onEditFavorite}
onConfirm={onConfirm}
editFavoriteForm={editFavoriteForm}
- isDisabledName={isDisabledName}
+ isDisabledName={!isValidFavoriteName(favoriteName)}
t={t}
/>
);
diff --git
a/hugegraph-hubble/hubble-fe/src/modules/analysis/QueryBar/ContentCommon/index.js
b/hugegraph-hubble/hubble-fe/src/modules/analysis/QueryBar/ContentCommon/index.js
index a06e2d03..7fe4dd4a 100644
---
a/hugegraph-hubble/hubble-fe/src/modules/analysis/QueryBar/ContentCommon/index.js
+++
b/hugegraph-hubble/hubble-fe/src/modules/analysis/QueryBar/ContentCommon/index.js
@@ -22,12 +22,13 @@
import React, {useCallback, useState, useContext} from 'react';
import {useTranslation} from 'react-i18next';
-import {Button, Tooltip, Dropdown, Input, Popover, message, Space} from 'antd';
+import {Button, Tooltip, Dropdown, Popover, message, Space} from 'antd';
import {UpOutlined, DownOutlined, QuestionCircleOutlined} from
'@ant-design/icons';
import {GREMLIN_EXECUTES_MODE} from '../../../../utils/constants';
import GraphAnalysisContext from '../../../Context';
import classnames from 'classnames';
import {isValidFavoriteName} from '../../../../utils/rules';
+import FavoriteNameInput from '../../../../components/FavoriteNameInput';
import c from './index.module.scss';
import * as api from '../../../../api/index';
import KeyboardAction from '../../../../components/KeyboardAction';
@@ -147,14 +148,10 @@ const ContentCommon = props => {
const favoriteContent = (
<>
- <Input
+ <FavoriteNameInput
placeholder={t('analysis.query.favorite_name_placeholder')}
- showCount
- maxLength={48}
value={favoriteName}
onChange={onChangeFavoraiteName}
- status={favoriteName && !isValidFavoriteName(favoriteName)
- ? 'error' : undefined}
/>
<Space style={{marginTop: '24px'}}>
<Button type='primary' onClick={onOkFavorite}
disabled={disabledFavorite}>
diff --git
a/hugegraph-hubble/hubble-fe/src/modules/analysis/QueryBar/ContentCommon/index.test.js
b/hugegraph-hubble/hubble-fe/src/modules/analysis/QueryBar/ContentCommon/index.test.js
index 836da74c..aac91325 100644
---
a/hugegraph-hubble/hubble-fe/src/modules/analysis/QueryBar/ContentCommon/index.test.js
+++
b/hugegraph-hubble/hubble-fe/src/modules/analysis/QueryBar/ContentCommon/index.test.js
@@ -64,6 +64,9 @@ test('keeps favorite submission disabled until the name is
backend-compatible',
fireEvent.change(input, {target: {value: 'query-name'}});
expect(submit).toBeDisabled();
+ expect(screen.getByRole('alert')).toHaveTextContent(
+ 'common.validation.favorite_name_rule'
+ );
fireEvent.click(submit);
expect(api.analysis.addFavoriate).not.toHaveBeenCalled();
diff --git
a/hugegraph-hubble/hubble-fe/src/modules/favorite-name-validation.test.js
b/hugegraph-hubble/hubble-fe/src/modules/favorite-name-validation.test.js
new file mode 100644
index 00000000..fcfe9b36
--- /dev/null
+++ b/hugegraph-hubble/hubble-fe/src/modules/favorite-name-validation.test.js
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {fireEvent, render, screen} from '@testing-library/react';
+
+import AlgorithmExecuteLog from './algorithm/LogsDetail/ExecuteLog';
+import AlgorithmFavorite from './algorithm/LogsDetail/Favorite';
+import AnalysisExecuteLog from './analysis/LogsDetail/ExecuteLog';
+import AnalysisFavorite from './analysis/LogsDetail/Favorite';
+
+jest.mock('react-i18next', () => ({
+ initReactI18next: {type: '3rdParty', init: jest.fn()},
+ useTranslation: () => ({t: key => key}),
+}));
+jest.mock('../components/ExecutionContent', () => () => <div />);
+
+beforeEach(() => {
+ window.matchMedia = jest.fn().mockImplementation(query => ({
+ matches: false,
+ media: query,
+ addListener: jest.fn(),
+ removeListener: jest.fn(),
+ }));
+});
+
+const executeRecord = {
+ id: 1,
+ content: 'g.V()',
+ status: 'SUCCESS',
+ type: 'GREMLIN',
+};
+const favoriteRecord = {
+ id: 1,
+ name: 'valid_name',
+ content: 'g.V()',
+};
+
+const assertInvalidNameDisablesConfirm = async (actionName, confirmName) => {
+ fireEvent.click(screen.getByRole('button', {name: actionName}));
+ const input = await screen.findByPlaceholderText(
+ 'analysis.logs.favorite_name_placeholder'
+ );
+ fireEvent.change(input, {target: {value: 'invalid-name'}});
+ const confirm = screen.getAllByRole('button', {name: confirmName})
+ .find(button => button.disabled);
+ expect(confirm).toBeDefined();
+};
+
+test.each([
+ ['analysis', AnalysisExecuteLog, {
+ executeLogsDataRecords: [executeRecord],
+ executeLogsDataTotal: 1,
+ onLoadContent: jest.fn(),
+ }],
+ ['algorithm', AlgorithmExecuteLog, {
+ executionLogsDataRecords: [executeRecord],
+ executionLogsDataTotal: 1,
+ }],
+])('%s execution log rejects invalid favorite names', async (_, Component,
dataProps) => {
+ render(<Component
+ {...dataProps}
+ pageExecute={1}
+ pageSize={10}
+ onExecutePageChange={jest.fn()}
+ onAddCollection={jest.fn()}
+ />);
+
+ await assertInvalidNameDisablesConfirm(
+ 'analysis.logs.action.favorite',
+ 'analysis.logs.action.favorite'
+ );
+});
+
+test.each([
+ ['analysis', AnalysisFavorite, {
+ onChangeSearchValue: jest.fn(),
+ onLoadContent: jest.fn(),
+ }],
+ ['algorithm', AlgorithmFavorite, {
+ onChangeFavorSearch: jest.fn(),
+ }],
+])('%s favorite editor rejects invalid renamed favorites', async (_,
Component, dataProps) => {
+ render(<Component
+ {...dataProps}
+ favoriteQueriesDataRecords={[favoriteRecord]}
+ favoriteQueriesDataTotal={1}
+ pageFavorite={1}
+ pageSize={10}
+ onFavoritePageChange={jest.fn()}
+ onSortChange={jest.fn()}
+ onEditCollection={jest.fn()}
+ onDel={jest.fn()}
+ />);
+
+ await assertInvalidNameDisablesConfirm(
+ 'analysis.logs.action.edit_name',
+ 'analysis.logs.action.save'
+ );
+});
diff --git
a/hugegraph-hubble/hubble-fe/src/modules/navigation/Home/index.module.scss
b/hugegraph-hubble/hubble-fe/src/modules/navigation/Home/index.module.scss
index e5e17415..06c7e6d5 100644
--- a/hugegraph-hubble/hubble-fe/src/modules/navigation/Home/index.module.scss
+++ b/hugegraph-hubble/hubble-fe/src/modules/navigation/Home/index.module.scss
@@ -28,7 +28,7 @@
}
.container {
- height: 230px;
+ min-height: 230px;
display: flex;
column-gap: 20px;
align-items: center;
diff --git a/hugegraph-hubble/hubble-fe/src/utils/rules.test.js
b/hugegraph-hubble/hubble-fe/src/utils/rules.test.js
index 8128fc93..c99754a0 100644
--- a/hugegraph-hubble/hubble-fe/src/utils/rules.test.js
+++ b/hugegraph-hubble/hubble-fe/src/utils/rules.test.js
@@ -178,6 +178,7 @@ describe('rules i18n defaults', () => {
[rules.isNoramlName(), '只能包含中文、字母、数字、_, 不能超过20个字符'],
[rules.isJDBC(), '请输入正确的jdbc url,
例如:jdbc:mysql://127.0.0.1:3306/db_name'],
[rules.isAccountName(), '账号名不超过16个字符,且不能以下划线开始和结尾'],
+ [rules.isFavoriteName(), '只能包含中文、字母、数字、_, 不能超过48个字符'],
[rules.isUUID(), '非法的数据格式'],
[rules.isInt(), '非法的数据格式'],
];