chore(ui): cache version response for any hour to avoid frequent calls (#21323)

* chore(ui): cache version response for any hour to avoid frequent calls

* fix tests
This commit is contained in:
Chirag Madlani 2025-05-21 17:12:37 +05:30 committed by GitHub
parent 2e251fccc3
commit 02235cdd9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 95 additions and 122 deletions

View File

@ -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(<div data-testid="cmd">CmdKIcon</div>);
});
jest.mock('../AppBar/SearchOptions', () => {
return jest.fn().mockReturnValue(<div data-testid="cmd">SearchOptions</div>);
});
jest.mock('../AppBar/Suggestions', () => {
return jest.fn().mockReturnValue(<div data-testid="cmd">Suggestions</div>);
});
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(<NavBar />);
render(<NavBarComponent />);
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(<NavBar />);
render(<NavBarComponent />);
expect(getVersion).toHaveBeenCalled();
});
it('should call getHelpDropdownItems function', async () => {
render(<NavBar />);
render(<NavBarComponent />);
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(<NavBarComponent />);
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(<NavBarComponent />);
await Promise.resolve();
await act(async () => {
expect(getVersion).toHaveBeenCalled();
});
expect(mockSetItem).toHaveBeenCalledWith(
'versionFetchTime',
'3000000',
expect.objectContaining({ expires: expect.any(Date) })
);
});
});

View File

@ -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,

View File

@ -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 (
<div className="d-flex items-center" data-testid="cmdicon-container">
{NavigatorHelper.isMacOs() ? (
<CmdButton
data-testid="cmd-button"
style={{
backgroundColor: 'white',
}}
/>
) : (
<CtrlButton
data-testid="ctrl-button"
style={{
backgroundColor: 'white',
}}
/>
)}
<KButton
data-testid="k-button"
style={{
marginLeft: '4px',
backgroundColor: 'white',
}}
/>
</div>
);
};
export default CmdKIcon;

View File

@ -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(<CmdKIcon />);
});
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(<CmdKIcon />);
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(<CmdKIcon />);
expect(getByTestId('ctrl-button')).toBeInTheDocument();
expect(queryByTestId('cmd-button')).toBeNull();
});
});

View File

@ -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;