Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ jest.mock('./Tabs/SnapshotErrataTab', () => ({
SnapshotErrataTab: () => <div>Errata tab body</div>,
}));

jest.mock('./Tabs/SnapshotChangesTab', () => ({
SnapshotChangesTab: () => <div>Changes tab body</div>,
}));

jest.mock('./SnapshotSelector', () => ({
SnapshotSelector: () => <div>Snapshot selector</div>,
}));
Expand Down Expand Up @@ -95,13 +99,46 @@ describe('SnapshotDetailsModal', () => {
});
});

it('syncs changes tab from search params on mount', async () => {
window.history.replaceState({}, '', `/?tab=${SnapshotDetailTab.CHANGES}`);

render(<SnapshotDetailsModal />);

await waitFor(() => {
expect(
screen.getByRole('tabpanel', { name: 'Snapshot changes detail tab' }),
).toBeInTheDocument();
});
});

it('updates search params when switching tabs', async () => {
const user = userEvent.setup();

render(<SnapshotDetailsModal />);

await user.click(screen.getByRole('tab', { name: 'Snapshot errata detail tab' }));
await user.click(screen.getByRole('tab', { name: 'Snapshot changes detail tab' }));

expect(mockSetSearchParams).toHaveBeenCalledWith({ tab: SnapshotDetailTab.CHANGES });
});

it('renders tabs in the expected order', () => {
render(<SnapshotDetailsModal />);

expect(screen.getAllByRole('tab').map((tab) => tab.textContent)).toEqual([
'Packages',
'Changes',
'Advisories',
]);
});

it('clears search params when switching back to packages', async () => {
const user = userEvent.setup();
window.history.replaceState({}, '', `/?tab=${SnapshotDetailTab.CHANGES}`);

render(<SnapshotDetailsModal />);

await user.click(screen.getByRole('tab', { name: 'Snapshot package detail tab' }));

expect(mockSetSearchParams).toHaveBeenCalledWith({ tab: SnapshotDetailTab.ERRATA });
expect(mockSetSearchParams).toHaveBeenCalledWith({});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { createUseStyles } from 'react-jss';
import { SnapshotSelector } from './SnapshotSelector';
import { REPOSITORIES_ROUTE } from 'Routes/constants';
import { SnapshotErrataTab } from './Tabs/SnapshotErrataTab';
import { SnapshotChangesTab } from './Tabs/SnapshotChangesTab';
import { modalTableSurfaceStyles } from 'helpers';

const useStyles = createUseStyles({
Expand All @@ -38,30 +39,36 @@ const useStyles = createUseStyles({

export enum SnapshotDetailTab {
PACKAGES = 'packages',
CHANGES = 'changes',
ERRATA = 'errata',
}

const isSnapshotDetailTab = (value: string | null): value is SnapshotDetailTab =>
Object.values(SnapshotDetailTab).includes(value as SnapshotDetailTab);

export default function SnapshotDetailsModal() {
const { contentOrigin } = useAppContext();
const classes = useStyles();
const { repoUUID, snapshotUUID } = useParams();
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
const rootPath = useRootPath();
const navigate = useNavigate();
const [activeTabKey, setActiveTabKey] = useState<string | number>(0);
const activeTab = urlSearchParams.get('tab');
const [activeTabKey, setActiveTabKey] = useState<SnapshotDetailTab>(SnapshotDetailTab.PACKAGES);

useEffect(() => {
if (urlSearchParams.get('tab') === SnapshotDetailTab.ERRATA) {
setActiveTabKey(1);
}
}, []);
setActiveTabKey(isSnapshotDetailTab(activeTab) ? activeTab : SnapshotDetailTab.PACKAGES);
}, [activeTab]);

const handleTabClick = (
_: React.MouseEvent<HTMLElement, MouseEvent>,
tabIndex: string | number,
) => {
setUrlSearchParams(tabIndex ? { tab: SnapshotDetailTab.ERRATA } : {});
setActiveTabKey(tabIndex);
const selectedTab = isSnapshotDetailTab(String(tabIndex))
? (tabIndex as SnapshotDetailTab)
: SnapshotDetailTab.PACKAGES;
setUrlSearchParams(selectedTab === SnapshotDetailTab.PACKAGES ? {} : { tab: selectedTab });
setActiveTabKey(selectedTab);
};

const onClose = () =>
Expand Down Expand Up @@ -100,15 +107,23 @@ export default function SnapshotDetailsModal() {
aria-label='Snapshot detail tabs'
>
<Tab
eventKey={0}
eventKey={SnapshotDetailTab.PACKAGES}
ouiaId='packages_tab'
title={<TabTitleText>Packages</TabTitleText>}
aria-label='Snapshot package detail tab'
>
<SnapshotPackagesTab />
</Tab>
<Tab
eventKey={1}
eventKey={SnapshotDetailTab.CHANGES}
ouiaId='changes_tab'
title={<TabTitleText>Changes</TabTitleText>}
aria-label='Snapshot changes detail tab'
>
<SnapshotChangesTab />
</Tab>
<Tab
eventKey={SnapshotDetailTab.ERRATA}
ouiaId='advisories_tab'
title={<TabTitleText>Advisories</TabTitleText>}
aria-label='Snapshot errata detail tab'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { SnapshotSelector } from './SnapshotSelector';
import { useGetSnapshotList } from 'services/Content/ContentQueries';
import { defaultMetaItem, defaultSnapshotItem } from 'testingHelpers';
import { formatDateDDMMMYYYY } from 'helpers';
import { SnapshotDetailTab } from './SnapshotDetailsModal';

const mockNavigate = jest.fn();
const mockSearchParams = new URLSearchParams();

jest.mock('Hooks/useRootPath', () => () => 'someUrl');

Expand All @@ -11,26 +16,70 @@ jest.mock('services/Content/ContentQueries', () => ({
}));

jest.mock('react-router-dom', () => ({
useNavigate: jest.fn(),
useNavigate: () => mockNavigate,
useSearchParams: () => [mockSearchParams],
useParams: () => ({
repoUUID: 'repo-uuid',
snapshotUUID: defaultSnapshotItem.uuid,
}),
}));

it('Render SnapshotSelector', () => {
(useGetSnapshotList as jest.Mock).mockImplementation(() => ({
data: {
meta: defaultMetaItem,
data: [defaultSnapshotItem],
},
isLoading: false,
isFetching: false,
}));

const { getByText } = render(<SnapshotSelector />);

// This is testing the date format specifically.
// if we update the date format, this test needs updating as well.
const selectorElement = getByText(formatDateDDMMMYYYY(defaultSnapshotItem.created_at, true));
expect(selectorElement).toBeInTheDocument();
describe('SnapshotSelector', () => {
beforeEach(() => {
mockNavigate.mockClear();
mockSearchParams.delete('tab');
});

it('renders the selected snapshot label', () => {
(useGetSnapshotList as jest.Mock).mockImplementation(() => ({
data: {
meta: defaultMetaItem,
data: [defaultSnapshotItem],
},
isLoading: false,
isFetching: false,
}));

render(<SnapshotSelector />);

// This is testing the date format specifically.
// if we update the date format, this test needs updating as well.
const selectorElement = screen.getByText(
formatDateDDMMMYYYY(defaultSnapshotItem.created_at, true),
);
expect(selectorElement).toBeInTheDocument();
});

it('preserves the active tab when selecting a different snapshot', async () => {
const user = userEvent.setup();
const alternateSnapshot = {
...defaultSnapshotItem,
uuid: '11111111-1111-4111-8111-111111111111',
created_at: '2024-02-08T20:23:32.711372-06:00',
};

mockSearchParams.set('tab', SnapshotDetailTab.CHANGES);

(useGetSnapshotList as jest.Mock).mockImplementation(() => ({
data: {
meta: { ...defaultMetaItem, count: 2 },
data: [defaultSnapshotItem, alternateSnapshot],
},
isLoading: false,
isFetching: false,
}));

render(<SnapshotSelector />);

await user.click(screen.getByRole('button', { name: 'snapshot selector' }));
await user.click(
screen.getByRole('menuitem', {
name: formatDateDDMMMYYYY(alternateSnapshot.created_at, true),
}),
);

expect(mockNavigate).toHaveBeenCalledWith(
`someUrl/repositories/repo-uuid/snapshots/${alternateSnapshot.uuid}?tab=${SnapshotDetailTab.CHANGES}`,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
MenuToggle,
} from '@patternfly/react-core';
import { createUseStyles } from 'react-jss';
import { useNavigate, useParams } from 'react-router-dom';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { useGetSnapshotList } from 'services/Content/ContentQueries';
import { useMemo, useState } from 'react';
import useRootPath from 'Hooks/useRootPath';
Expand All @@ -27,6 +27,7 @@ export function SnapshotSelector() {
const classes = useStyles();
const rootPath = useRootPath();
const navigate = useNavigate();
const [urlSearchParams] = useSearchParams();
const [selectorOpen, setSelectorOpen] = useState(false);
const { repoUUID: uuid = '', snapshotUUID = '' } = useParams();

Expand All @@ -48,7 +49,12 @@ export function SnapshotSelector() {
}, [data?.data]);

const setSelected = (selectedDate: string) => {
navigate(`${rootPath}/${REPOSITORIES_ROUTE}/${uuid}/snapshots/${dateMapper[selectedDate]}`);
const activeTab = urlSearchParams.get('tab');
navigate(
`${rootPath}/${REPOSITORIES_ROUTE}/${uuid}/snapshots/${dateMapper[selectedDate]}${
activeTab ? `?tab=${activeTab}` : ''
}`,
);
};

return (
Expand Down
Loading
Loading