diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx
index e67df02b71b..b0ee2238085 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx
@@ -10,13 +10,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { render, screen } from '@testing-library/react';
+import { act, render, screen } from '@testing-library/react';
import React from 'react';
+import { ONE_HOUR_MS } from '../../constants/constants';
import { HELP_ITEMS_ENUM } from '../../constants/Navbar.constants';
-
import { getVersion } from '../../rest/miscAPI';
import { getHelpDropdownItems } from '../../utils/NavbarUtils';
-import NavBar from './NavBar';
+import NavBarComponent from './NavBar';
+
+// Place these at the very top of your test file, before any imports!
+const mockGetItem = jest.fn();
+const mockSetItem = jest.fn();
+
+jest.mock('cookie-storage', () => ({
+ CookieStorage: class {
+ getItem(...args: any[]) {
+ return mockGetItem(...args);
+ }
+ setItem(...args: any[]) {
+ return mockSetItem(...args);
+ }
+ constructor() {
+ // Do nothing
+ }
+ },
+}));
jest.mock('../../hooks/useApplicationStore', () => ({
useApplicationStore: jest.fn().mockImplementation(() => ({
@@ -99,15 +117,6 @@ jest.mock('react-router-dom', () => ({
useHistory: jest.fn(),
}));
-jest.mock('../common/CmdKIcon/CmdKIcon.component', () => {
- return jest.fn().mockReturnValue(
CmdKIcon
);
-});
-jest.mock('../AppBar/SearchOptions', () => {
- return jest.fn().mockReturnValue(SearchOptions
);
-});
-jest.mock('../AppBar/Suggestions', () => {
- return jest.fn().mockReturnValue(Suggestions
);
-});
jest.mock('antd', () => ({
...jest.requireActual('antd'),
@@ -120,16 +129,6 @@ jest.mock('antd', () => ({
}),
}));
-jest.mock('../../rest/miscAPI', () => ({
- getVersion: jest.fn().mockImplementation(() =>
- Promise.resolve({
- data: {
- version: '0.5.0-SNAPSHOT',
- },
- })
- ),
-}));
-
jest.mock('../../utils/NavbarUtils', () => ({
getHelpDropdownItems: jest.fn().mockReturnValue([
{
@@ -139,9 +138,15 @@ jest.mock('../../utils/NavbarUtils', () => ({
]),
}));
+jest.mock('../../rest/miscAPI', () => ({
+ getVersion: jest.fn().mockResolvedValue({
+ version: '0.5.0-SNAPSHOT',
+ }),
+}));
+
describe('Test NavBar Component', () => {
it('Should render NavBar component', async () => {
- render();
+ render();
expect(await screen.findByTestId('global-search-bar')).toBeInTheDocument();
expect(await screen.findByTestId('user-profile-icon')).toBeInTheDocument();
@@ -160,14 +165,61 @@ describe('Test NavBar Component', () => {
});
it('should call getVersion onMount', () => {
- render();
+ render();
expect(getVersion).toHaveBeenCalled();
});
it('should call getHelpDropdownItems function', async () => {
- render();
+ render();
expect(getHelpDropdownItems).toHaveBeenCalled();
});
});
+
+// --- Additional tests for fetchOMVersion one hour threshold ---
+describe('fetchOMVersion one hour threshold', () => {
+ const OLD_DATE_NOW = Date.now;
+
+ beforeEach(() => {
+ jest.resetModules();
+
+ global.Date.now = jest.fn();
+ });
+
+ afterEach(() => {
+ global.Date.now = OLD_DATE_NOW;
+ });
+
+ it('should NOT call getVersion if less than one hour since last fetch', async () => {
+ const now = 2000000;
+ const lastFetch = now - (ONE_HOUR_MS - 1000); // less than 1 hour ago
+ mockGetItem.mockReturnValue(String(lastFetch));
+ jest.spyOn(global.Date, 'now').mockReturnValue(now);
+
+ render();
+ await screen.findByTestId('global-search-bar');
+
+ expect(getVersion).not.toHaveBeenCalled();
+ });
+
+ it('should call getVersion and setItem if more than one hour since last fetch', async () => {
+ const now = 3000000;
+ const lastFetch = now - (ONE_HOUR_MS + 1000); // more than 1 hour ago
+ mockGetItem.mockReturnValue(String(lastFetch));
+ (global.Date.now as jest.Mock).mockReturnValue(now);
+
+ render();
+ await Promise.resolve();
+
+ await act(async () => {
+ expect(getVersion).toHaveBeenCalled();
+ });
+
+ expect(mockSetItem).toHaveBeenCalledWith(
+ 'versionFetchTime',
+ '3000000',
+ expect.objectContaining({ expires: expect.any(Date) })
+ );
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx
index 40abdaeb4d3..3f1d678d5da 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx
@@ -46,7 +46,9 @@ import { ReactComponent as SidebarExpandedIcon } from '../../assets/svg/ic-sideb
import {
DEFAULT_DOMAIN_VALUE,
NOTIFICATION_READ_TIMER,
+ ONE_HOUR_MS,
SOCKET_EVENTS,
+ VERSION_FETCH_TIME_KEY,
} from '../../constants/constants';
import { GlobalSettingsMenuCategory } from '../../constants/GlobalSettings.constants';
import { HELP_ITEMS_ENUM } from '../../constants/Navbar.constants';
@@ -124,9 +126,22 @@ const NavBar = () => {
} = useCurrentUserPreferences();
const fetchOMVersion = async () => {
+ // If version fetch happens within an hour, skip fetching
+ const lastFetchTime = cookieStorage.getItem(VERSION_FETCH_TIME_KEY);
+ const now = Date.now();
+
+ if (lastFetchTime && now - Number(lastFetchTime) < ONE_HOUR_MS) {
+ // Less than an hour since last fetch, skip fetching
+ return;
+ }
+
try {
const res = await getVersion();
setVersion(res.version);
+ // Set/update the cookie with current time, expires in 1 hour
+ cookieStorage.setItem(VERSION_FETCH_TIME_KEY, String(now), {
+ expires: new Date(now + ONE_HOUR_MS),
+ });
} catch (err) {
showErrorToast(
err as AxiosError,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CmdKIcon/CmdKIcon.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CmdKIcon/CmdKIcon.component.tsx
deleted file mode 100644
index 41304336042..00000000000
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CmdKIcon/CmdKIcon.component.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2022 Collate.
- * Licensed 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 React from 'react';
-import { ReactComponent as CmdButton } from '../../../assets/svg/command-button.svg';
-import { ReactComponent as CtrlButton } from '../../../assets/svg/control-button.svg';
-import { ReactComponent as KButton } from '../../../assets/svg/k-button.svg';
-import { NavigatorHelper } from '../../../utils/NavigatorUtils';
-
-const CmdKIcon = () => {
- return (
-
- {NavigatorHelper.isMacOs() ? (
-
- ) : (
-
- )}
-
-
- );
-};
-
-export default CmdKIcon;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CmdKIcon/CmdKIcon.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CmdKIcon/CmdKIcon.test.tsx
deleted file mode 100644
index 1758096d98c..00000000000
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/CmdKIcon/CmdKIcon.test.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2024 Collate.
- * Licensed 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 { act, render, screen } from '@testing-library/react';
-import React from 'react';
-import { NavigatorHelper } from '../../../utils/NavigatorUtils';
-import CmdKIcon from './CmdKIcon.component';
-
-jest.mock('../../../utils/NavigatorUtils', () => ({
- NavigatorHelper: {
- isMacOs: jest.fn(),
- },
-}));
-
-describe('CmdKIcon', () => {
- it('should render CmdKIcon', async () => {
- await act(async () => {
- render();
- });
-
- expect(screen.getByTestId('cmdicon-container')).toBeInTheDocument();
- });
-
- it('should render CmdButton when isMacOs is true', () => {
- (NavigatorHelper.isMacOs as jest.Mock).mockReturnValue(true);
- const { getByTestId, queryByTestId } = render();
-
- expect(getByTestId('cmd-button')).toBeInTheDocument();
- expect(queryByTestId('ctrl-button')).toBeNull();
- });
-
- it('should render CtrlButton when isMacOs is false', () => {
- (NavigatorHelper.isMacOs as jest.Mock).mockReturnValue(false);
- const { getByTestId, queryByTestId } = render();
-
- expect(getByTestId('ctrl-button')).toBeInTheDocument();
- expect(queryByTestId('cmd-button')).toBeNull();
- });
-});
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts
index 029825f462d..d4dbf50dbd4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts
@@ -424,3 +424,6 @@ export const MAX_VISIBLE_OWNERS_FOR_FEED_TAB = 4;
export const MAX_VISIBLE_OWNERS_FOR_FEED_CARD = 2;
export const BREADCRUMB_SEPARATOR = '/';
+
+export const VERSION_FETCH_TIME_KEY = 'versionFetchTime';
+export const ONE_HOUR_MS = 60 * 60 * 1000;