Skip to content
Open
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
29 changes: 24 additions & 5 deletions src/pages/tickets/TicketModalPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {
const [visible, setVisible] = useState(false);
const [isDeleted, setisDeleted] = useState(false);
const [accessDenied, setAccessDenied] = useState(false);
const [hidePrevArrow, setHidePrevArrow] = useState(false);
const [hideNextArrow, setHideNextArrow] = useState(false);

const [toasts, setToasts]: any = useState([]);

Expand Down Expand Up @@ -78,7 +80,7 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {

if (bountyId) {
const isNumeric = /^\d+$/.test(bountyId);

if (isNumeric) {
bounty = await main.getBountyById(Number(bountyId), unlockCode);
bountyIndex = await main.getBountyIndexById(Number(bountyId));
Expand Down Expand Up @@ -166,6 +168,11 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {
getBounty();
}, [getBounty, removeNextAndPrev]);

useEffect(() => {
setHidePrevArrow(false);
setHideNextArrow(false);
}, [bountyId, activeWorkspace]);

const isDirectAccess = useCallback(() => !document.referrer, [location.pathname]);

const goBack = () => {
Expand All @@ -188,6 +195,7 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {
try {
const bountyId = await main.getWorkspaceNextBountyByCreated(activeWorkspace, created);
if (bountyId === 0) {
setHidePrevArrow(true);
addToast('There are no more bounties to display!', 'primary');
} else {
history.replace(`/bounty/${bountyId}`);
Expand All @@ -198,7 +206,12 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {
} else {
try {
const bountyId = await main.getNextBountyByCreated(created);
history.replace(`/bounty/${bountyId}`);
if (bountyId === 0) {
setHidePrevArrow(true);
addToast('There are no more bounties to display!', 'primary');
} else {
history.replace(`/bounty/${bountyId}`);
}
} catch (e) {
console.error(e);
}
Expand All @@ -212,6 +225,7 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {
try {
const bountyId = await main.getWorkspacePreviousBountyByCreated(activeWorkspace, created);
if (bountyId === 0) {
setHideNextArrow(true);
addToast('There are no more bounties to display!', 'primary');
} else {
history.replace(`/bounty/${bountyId}`);
Expand All @@ -222,7 +236,12 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {
} else {
try {
const bountyId = await main.getPreviousBountyByCreated(created);
history.replace(`/bounty/${bountyId}`);
if (bountyId === 0) {
setHideNextArrow(true);
addToast('There are no more bounties to display!', 'primary');
} else {
history.replace(`/bounty/${bountyId}`);
}
} catch (e) {
console.error(e);
}
Expand Down Expand Up @@ -324,8 +343,8 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {
right: '-50px',
borderRadius: '50%'
}}
prevArrowNew={removeNextAndPrev ? undefined : prevArrHandler}
nextArrowNew={removeNextAndPrev ? undefined : nextArrHandler}
prevArrowNew={removeNextAndPrev || hidePrevArrow ? undefined : prevArrHandler}
nextArrowNew={removeNextAndPrev || hideNextArrow ? undefined : nextArrHandler}
>
<EuiGlobalToastList toasts={toasts} dismissToast={removeToast} toastLifeTimeMs={5000} />
<FocusedView
Expand Down
102 changes: 100 additions & 2 deletions src/pages/tickets/__tests__/TicketModalPage.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,34 @@ import * as helpers from 'helpers';
import { TicketModalPage } from '../TicketModalPage';
import { withCreateModal } from '../../../components/common/withCreateModal';

declare const fetchMock: any;

// eslint-disable-next-line @typescript-eslint/no-empty-function
jest.mock('remark-gfm', () => () => {});
// eslint-disable-next-line @typescript-eslint/no-empty-function
jest.mock('rehype-raw', () => () => {});

jest.mock('hooks', () => ({
useIsMobile: jest.fn(),
useFeatureFlag: jest.fn().mockReturnValue({ isEnabled: true })
}));

const mockPush = jest.fn();
const mockGoBack = jest.fn();
const mockReplace = jest.fn();
let mockLocationState: any;

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: () => ({
push: mockPush,
goBack: mockGoBack
goBack: mockGoBack,
replace: mockReplace
}),
useLocation: () => ({
pathname: '/bounty/1239',
search: '',
state: {}
state: mockLocationState
}),
useParams: () => ({
uuid: 'ck95pe04nncjnaefo08g',
Expand All @@ -47,6 +57,11 @@ jest.mock('helpers', () => ({

describe('TicketModalPage Component', () => {
beforeEach(() => {
mockLocationState = undefined;
mainStore.setActiveWorkspace('');
fetchMock.doMock();
fetchMock.mockResponse(JSON.stringify([]));

const mockIntersectionObserver = jest.fn();
mockIntersectionObserver.mockReturnValue({
observe: () => null,
Expand All @@ -58,6 +73,9 @@ describe('TicketModalPage Component', () => {

afterEach(() => {
jest.clearAllMocks();
fetchMock.resetMocks();
fetchMock.dontMock();
mainStore.setActiveWorkspace('');
});

it('reder ticket modal', async () => {
Expand Down Expand Up @@ -811,6 +829,86 @@ describe('TicketModalPage Component', () => {
});
});

it('hides the public next arrow and keeps the user off the deleted bounty page at list end', async () => {
jest
.spyOn(mainStore, 'getBountyById')
.mockReturnValue(
Promise.resolve([
{ ...newBounty, body: { ...mockBountiesMutated[1].body, assignee: user } }
])
);
jest.spyOn(mainStore, 'getBountyIndexById').mockReturnValue(Promise.resolve(1239));
jest.spyOn(mainStore, 'getNextBountyByCreated').mockResolvedValueOnce(0);

await act(async () => {
render(
<MemoryRouter initialEntries={['/bounty/1234']}>
<Route path="/bounty/:bountyId" component={TicketModalPage} />
</MemoryRouter>
);
});

await waitFor(() => screen.getByText('chevron_left'));
fireEvent.click(screen.getByText('chevron_left'));

await waitFor(() => expect(screen.queryByText('chevron_left')).not.toBeInTheDocument());
expect(mockReplace).not.toHaveBeenCalledWith('/bounty/0');
expect(screen.getByText('There are no more bounties to display!')).toBeInTheDocument();
});

it('hides the workspace previous arrow and keeps the user off the deleted bounty page at list end', async () => {
mockLocationState = { activeWorkspace: 'workspace-uuid' };
jest
.spyOn(mainStore, 'getBountyById')
.mockReturnValue(
Promise.resolve([
{ ...newBounty, body: { ...mockBountiesMutated[1].body, assignee: user } }
])
);
jest.spyOn(mainStore, 'getBountyIndexById').mockReturnValue(Promise.resolve(1239));
jest.spyOn(mainStore, 'getWorkspacePreviousBountyByCreated').mockResolvedValueOnce(0);

await act(async () => {
render(
<MemoryRouter initialEntries={['/workspace/bounties/workspace-uuid/bounty/1234']}>
<Route path="/workspace/bounties/:uuid/bounty/:bountyId" component={TicketModalPage} />
</MemoryRouter>
);
});

await waitFor(() => screen.getByText('chevron_right'));
fireEvent.click(screen.getByText('chevron_right'));

await waitFor(() => expect(screen.queryByText('chevron_right')).not.toBeInTheDocument());
expect(mockReplace).not.toHaveBeenCalledWith('/bounty/0');
expect(screen.getByText('There are no more bounties to display!')).toBeInTheDocument();
});

it('navigates to the exact public bounty returned by the L/R lookup', async () => {
jest
.spyOn(mainStore, 'getBountyById')
.mockReturnValue(
Promise.resolve([
{ ...newBounty, body: { ...mockBountiesMutated[1].body, assignee: user } }
])
);
jest.spyOn(mainStore, 'getBountyIndexById').mockReturnValue(Promise.resolve(1239));
jest.spyOn(mainStore, 'getPreviousBountyByCreated').mockResolvedValueOnce(5678);

await act(async () => {
render(
<MemoryRouter initialEntries={['/bounty/1234']}>
<Route path="/bounty/:bountyId" component={TicketModalPage} />
</MemoryRouter>
);
});

await waitFor(() => screen.getByText('chevron_right'));
fireEvent.click(screen.getByText('chevron_right'));

await waitFor(() => expect(mockReplace).toHaveBeenCalledWith('/bounty/5678'));
});

it('checks for enabled state of the delete button based on no assignment status', async () => {
uiStore.setMeInfo(user);
jest.spyOn(mainStore, 'getBountyById').mockReturnValue(
Expand Down