Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ASAP-532] - add resubmit manuscript feature #4449

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
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
26 changes: 13 additions & 13 deletions apps/crn-frontend/src/network/teams/TeamManuscript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {
ManuscriptHeader,
usePushFromHere,
} from '@asap-hub/react-components';
import { network, useRouteParams } from '@asap-hub/routing';
import { network } from '@asap-hub/routing';
import { FormProvider, useForm } from 'react-hook-form';
import { useParams } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import {
useAuthorSuggestions,
Expand All @@ -18,27 +19,23 @@ import {
useManuscriptById,
usePostManuscript,
usePutManuscript,
useResubmitManuscript,
useTeamById,
useUploadManuscriptFile,
} from './state';
import { useEligibilityReason } from './useEligibilityReason';
import { useManuscriptToast } from './useManuscriptToast';

const useParamManuscriptVersion = (teamId: string): string => {
const route = network({})
.teams({})
.team({ teamId })
.workspace({}).editManuscript;
const { manuscriptId } = useRouteParams(route);
return manuscriptId;
};

type TeamManuscriptProps = {
teamId: string;
resubmitManuscript?: boolean;
};
const TeamManuscript: React.FC<TeamManuscriptProps> = ({ teamId }) => {
const TeamManuscript: React.FC<TeamManuscriptProps> = ({
teamId,
resubmitManuscript = false,
}) => {
const setRefreshTeamState = useSetRecoilState(refreshTeamState(teamId));
const manuscriptId = useParamManuscriptVersion(teamId);
const { manuscriptId } = useParams<{ manuscriptId: string }>();
const manuscript = useManuscriptById(manuscriptId);

const team = useTeamById(teamId);
Expand All @@ -48,6 +45,7 @@ const TeamManuscript: React.FC<TeamManuscriptProps> = ({ teamId }) => {
const form = useForm();
const createManuscript = usePostManuscript();
const updateManuscript = usePutManuscript();
const handleResubmitManuscript = useResubmitManuscript();
const handleFileUpload = useUploadManuscriptFile();
const getTeamSuggestions = useTeamSuggestions();
const getLabSuggestions = useLabSuggestions();
Expand Down Expand Up @@ -100,12 +98,13 @@ const TeamManuscript: React.FC<TeamManuscriptProps> = ({ teamId }) => {
return (
<FormProvider {...form}>
<Frame title="Create Manuscript">
<ManuscriptHeader />
<ManuscriptHeader resubmitManuscript={resubmitManuscript} />
<ManuscriptForm
manuscriptId={manuscriptId}
onSuccess={onSuccess}
onCreate={createManuscript}
onUpdate={updateManuscript}
onResubmit={handleResubmitManuscript}
teamId={teamId}
handleFileUpload={handleFileUpload}
eligibilityReasons={eligibilityReasons}
Expand All @@ -130,6 +129,7 @@ const TeamManuscript: React.FC<TeamManuscriptProps> = ({ teamId }) => {
additionalAuthors={convertAuthorsToSelectOptions(
manuscriptAdditionalAuthors,
)}
resubmitManuscript={resubmitManuscript}
{...manuscriptVersion}
/>
</Frame>
Expand Down
9 changes: 9 additions & 0 deletions apps/crn-frontend/src/network/teams/TeamProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ const TeamProfile: FC<TeamProfileProps> = ({ currentTime }) => {
<TeamManuscript teamId={teamId} />
</Frame>
</Route>
<Route
path={
workspace({}).$ + workspace({}).resubmitManuscript.template
}
>
<Frame title="Resubmit Manuscript">
<TeamManuscript teamId={teamId} resubmitManuscript />
</Frame>
</Route>
{canCreateComplianceReport && (
<Route
path={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
Auth0Provider,
WhenReady,
} from '@asap-hub/crn-frontend/src/auth/test-utils';
import { createManuscriptResponse } from '@asap-hub/fixtures';
import { AuthorResponse } from '@asap-hub/model';
import { network } from '@asap-hub/routing';
import {
act,
Expand All @@ -12,12 +14,12 @@ import {
within,
} from '@testing-library/react';
import userEvent, { specialChars } from '@testing-library/user-event';
import { createMemoryHistory } from 'history';
import { createMemoryHistory, MemoryHistory } from 'history';
import { ComponentProps, Suspense } from 'react';
import { Route, Router } from 'react-router-dom';
import { RecoilRoot } from 'recoil';

import { createManuscript } from '../api';
import { createManuscript, getManuscript, resubmitManuscript } from '../api';
import { EligibilityReasonProvider } from '../EligibilityReasonProvider';
import { ManuscriptToastProvider } from '../ManuscriptToastProvider';
import { refreshTeamState } from '../state';
Expand All @@ -37,6 +39,7 @@ jest.mock('../../users/api');
jest.mock('../api', () => ({
createManuscript: jest.fn().mockResolvedValue(manuscriptResponse),
getManuscript: jest.fn().mockResolvedValue(null),
resubmitManuscript: jest.fn().mockResolvedValue(null),
uploadManuscriptFile: jest.fn().mockResolvedValue({
filename: 'manuscript.pdf',
url: 'https://example.com/manuscript.pdf',
Expand All @@ -57,15 +60,15 @@ beforeEach(() => {

const renderPage = async (
user: ComponentProps<typeof Auth0Provider>['user'] = {},
) => {
const path =
network.template +
resubmit: boolean = false,
path: string = network.template +
network({}).teams.template +
network({}).teams({}).team.template +
network({}).teams({}).team({ teamId }).workspace.template +
network({}).teams({}).team({ teamId }).workspace({}).createManuscript
.template;

.template,
routerHistory: MemoryHistory = history,
) => {
const { container } = render(
<RecoilRoot
initializeState={({ set }) => {
Expand All @@ -75,11 +78,14 @@ const renderPage = async (
<Suspense fallback="loading">
<Auth0Provider user={user}>
<WhenReady>
<Router history={history}>
<Router history={routerHistory}>
<Route path={path}>
<ManuscriptToastProvider>
<EligibilityReasonProvider>
<TeamManuscript teamId={teamId} />
<TeamManuscript
teamId={teamId}
resubmitManuscript={resubmit}
/>
</EligibilityReasonProvider>
</ManuscriptToastProvider>
</Route>
Expand All @@ -97,7 +103,7 @@ it('renders manuscript form page', async () => {
const { container } = await renderPage();

expect(container).toHaveTextContent(
'Submit your manuscript to receive a compliance report and find out which areas need to be improved before publishing your article',
'Start a new manuscript to receive an itemized compliance report outlining action items for compliance with the ASAP Open Science Policy',
);
expect(container).toHaveTextContent('What are you sharing');
expect(container).toHaveTextContent('Title of Manuscript');
Expand Down Expand Up @@ -241,3 +247,62 @@ it('can publish a form when the data is valid and navigates to team workspace',
);
});
}, 180_000);

it('can resubmit a manuscript and navigates to team workspace', async () => {
const mockResubmitManuscript = resubmitManuscript as jest.MockedFunction<
typeof resubmitManuscript
>;
const mockGetManuscript = getManuscript as jest.MockedFunction<
typeof getManuscript
>;

const manuscript = createManuscriptResponse();
manuscript.versions[0]!.lifecycle = 'Preprint';
manuscript.versions[0]!.firstAuthors = [
{
label: 'Author 1',
value: 'author-1',
id: 'author-1',
displayName: 'Author 1',
email: '[email protected]',
} as AuthorResponse,
];

mockGetManuscript.mockResolvedValue(manuscript);
mockResubmitManuscript.mockResolvedValue(manuscript);

const resubmitPath = `/network/teams/${teamId}/workspace/resubmit-manuscript/:manuscriptId`;
const resubmitHistory = createMemoryHistory({
initialEntries: [
`/network/teams/${teamId}/workspace/resubmit-manuscript/${manuscript.id}`,
],
});

await renderPage({}, true, resubmitPath, resubmitHistory);

const preprintDoi = '10.4444/test';

const preprintDoiTextbox = screen.getByRole('textbox', {
name: /Preprint DOI/i,
});
userEvent.type(preprintDoiTextbox, preprintDoi);

await act(async () => {
userEvent.click(await screen.findByRole('button', { name: /Submit/ }));
});

userEvent.click(screen.getByRole('button', { name: /Submit Manuscript/ }));

await waitFor(() => {
expect(mockResubmitManuscript).toHaveBeenCalledWith(
manuscript.id,
expect.objectContaining({
versions: [expect.objectContaining({ preprintDoi })],
}),
expect.anything(),
);
expect(resubmitHistory.location.pathname).toBe(
`/network/teams/${teamId}/workspace`,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
import Workspace from '../Workspace';
import { ManuscriptToastProvider } from '../ManuscriptToastProvider';

jest.setTimeout(30000);
jest.setTimeout(60000);
jest.mock('../api', () => ({
patchTeam: jest.fn(),
updateManuscript: jest.fn().mockResolvedValue({}),
Expand Down
43 changes: 43 additions & 0 deletions apps/crn-frontend/src/network/teams/__tests__/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
updateDiscussion,
updateTeamResearchOutput,
uploadManuscriptFile,
resubmitManuscript,
} from '../api';

jest.mock('../../../config');
Expand Down Expand Up @@ -450,6 +451,48 @@ describe('Manuscript', () => {
expect(handleErrorMock).toHaveBeenCalledWith('Validation Error');
});
});

describe('resubmitManuscript', () => {
const payload: ManuscriptPostRequest = {
title: 'The Manuscript',
teamId: '42',
versions: [
{
lifecycle: 'Publication',
type: 'Original Research',
manuscriptFile: {
id: '42',
filename: 'test-file',
url: 'https://example.com/test-file',
},
teams: ['42'],
labs: [],
description: '',
firstAuthors: [],
},
],
};
const manuscriptId = 'manuscript-id-1';

it('makes an authorized POST request to resubmit a manuscript', async () => {
nock(API_BASE_URL, { reqheaders: { authorization: 'Bearer x' } })
.post(`/manuscripts/${manuscriptId}`, payload)
.reply(201, { id: manuscriptId });

await resubmitManuscript(manuscriptId, payload, 'Bearer x');
expect(nock.isDone()).toBe(true);
});

it('errors for an error status', async () => {
nock(API_BASE_URL).post(`/manuscripts/${manuscriptId}`).reply(500, {});

await expect(
resubmitManuscript(manuscriptId, payload, 'Bearer x'),
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Failed to resubmit manuscript with id manuscript-id-1. Expected status 201. Received status 500."`,
);
});
});
});

describe('Compliance Report', () => {
Expand Down
25 changes: 25 additions & 0 deletions apps/crn-frontend/src/network/teams/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,31 @@ export const updateManuscript = async (
return response;
};

export const resubmitManuscript = async (
manuscriptId: string,
manuscript: ManuscriptPostRequest,
authorization: string,
): Promise<ManuscriptResponse> => {
const resp = await fetch(`${API_BASE_URL}/manuscripts/${manuscriptId}`, {
method: 'POST',
headers: {
authorization,
'content-type': 'application/json',
...createSentryHeaders(),
},
body: JSON.stringify(manuscript),
});
const response = await resp.json();
if (!resp.ok) {
throw new BackendError(
`Failed to resubmit manuscript with id ${manuscriptId}. Expected status 201. Received status ${`${resp.status} ${resp.statusText}`.trim()}.`,
response,
resp.status,
);
}
return response;
};

export const getManuscript = async (
id: string,
authorization: string,
Expand Down
11 changes: 11 additions & 0 deletions apps/crn-frontend/src/network/teams/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
updateManuscript,
updateDiscussion,
uploadManuscriptFile,
resubmitManuscript,
} from './api';

const teamIndexState = atomFamily<
Expand Down Expand Up @@ -216,6 +217,16 @@ export const usePostManuscript = () => {
};
};

export const useResubmitManuscript = () => {
const authorization = useRecoilValue(authorizationState);
const setManuscriptItem = useSetManuscriptItem();
return async (id: string, payload: ManuscriptPostRequest) => {
const manuscript = await resubmitManuscript(id, payload, authorization);
setManuscriptItem(manuscript);
return manuscript;
};
};

export const usePutManuscript = () => {
const authorization = useRecoilValue(authorizationState);
const setManuscriptItem = useSetManuscriptItem();
Expand Down
Loading