Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
post.test.js 19.22 KiB
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
process.env.SUPPRESS_NO_CONFIG_WARNING = true

const Chance = require('chance')
const cloneDeep = require('lodash/cloneDeep')
const fixturesService = require('pubsweet-component-fixture-service')
const requests = require('../requests')

const { Model, fixtures } = fixturesService
const chance = new Chance()
jest.mock('@pubsweet/component-send-email', () => ({
  send: jest.fn(),
}))
jest.mock('pubsweet-component-mts-package')

const reqBody = {
  recommendation: 'publish',
  comments: [
    {
      content: chance.paragraph(),
      public: chance.bool(),
      files: [
        {
          id: chance.guid(),
          name: 'file.pdf',
          size: chance.natural(),
        },
      ],
    },
  ],
  recommendationType: 'editorRecommendation',
}

const path = '../routes/fragmentsRecommendations/post'
const route = {
  path: '/api/collections/:collectionId/fragments/:fragmentId/recommendations',
}
describe('Post fragments recommendations route handler', () => {
  let testFixtures = {}
  let body = {}
  let models
  beforeEach(() => {
    testFixtures = cloneDeep(fixtures)
    body = cloneDeep(reqBody)
    models = Model.build(testFixtures)
  })
  it('should return an error when params are missing', async () => {
    const { reviewer } = testFixtures.users
    delete body.recommendationType
    const res = await requests.sendRequest({
      body,
      userId: reviewer.id,
      route,
      models,
      path,
    })

    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())
    expect(data.error).toEqual('Recommendation type is required.')
  })
  it('should return success when creating a recommendation as a reviewer', async () => {
    const { reviewer } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments

    const res = await requests.sendRequest({
      body,
      userId: reviewer.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())
    expect(data.userId).toEqual(reviewer.id)
  })

  it('should return success when creating a recommendation as a HE when there is a single version with at least one review.', async () => {
    const { noRecommendationHE } = testFixtures.users
    const { noEditorRecomedationCollection } = testFixtures.collections
    const { noEditorRecomedationFragment } = testFixtures.fragments

    const res = await requests.sendRequest({
      body,
      userId: noRecommendationHE.id,
      models,
      route,
      path,
      params: {
        collectionId: noEditorRecomedationCollection.id,
        fragmentId: noEditorRecomedationFragment.id,
      },
    })
    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())
    expect(data.userId).toEqual(noRecommendationHE.id)
  })

  it('should return an error when creating a recommendation with publish as a HE when there is a single version and there are no reviews.', async () => {
    const { handlingEditor } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments

    fragment.recommendations = []

    const res = await requests.sendRequest({
      body,
      userId: handlingEditor.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())
    expect(data.error).toEqual(
      'Cannot publish without at least one reviewer report.',
    )
  })

  it('should return success when creating a recommendation as a HE after minor revision and we have at least one review on collection.', async () => {
    const { handlingEditor } = testFixtures.users
    const { collection } = testFixtures.collections
    const {
      minorRevisionWithReview,
      noInvitesFragment1,
    } = testFixtures.fragments

    collection.fragments = [minorRevisionWithReview.id, noInvitesFragment1.id]
    const res = await requests.sendRequest({
      body,
      userId: handlingEditor.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: noInvitesFragment1.id,
      },
    })

    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())
    expect(data.userId).toEqual(handlingEditor.id)
  })

  it('should return error when creating a recommendation as a HE after minor revision and there are no reviews.', async () => {
    const { handlingEditor } = testFixtures.users
    const { collection } = testFixtures.collections
    const {
      minorRevisionWithoutReview,
      noInvitesFragment1,
    } = testFixtures.fragments

    collection.fragments = [
      minorRevisionWithoutReview.id,
      noInvitesFragment1.id,
    ]
    const res = await requests.sendRequest({
      body,
      userId: handlingEditor.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: noInvitesFragment1.id,
      },
    })

    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())
    expect(data.error).toEqual(
      'Cannot publish without at least one reviewer report.',
    )
  })

  it('should return success when creating a recommendation as a HE after major revision and there are least one review on fragment.', async () => {
    const { handlingEditor } = testFixtures.users
    const { collection } = testFixtures.collections
    const {
      majorRevisionWithReview,
      reviewCompletedFragment,
    } = testFixtures.fragments

    reviewCompletedFragment.collectionId = collection.id
    collection.fragments = [
      majorRevisionWithReview.id,
      reviewCompletedFragment.id,
    ]
    const res = await requests.sendRequest({
      body,
      userId: handlingEditor.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: reviewCompletedFragment.id,
      },
    })

    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())
    expect(data.userId).toEqual(handlingEditor.id)
  })

  it('should return error when creating a recommendation as a HE after major revision there are no reviews on fragment.', async () => {
    const { handlingEditor } = testFixtures.users
    const { collection } = testFixtures.collections
    const {
      majorRevisionWithReview,
      noInvitesFragment1,
    } = testFixtures.fragments

    collection.fragments = [majorRevisionWithReview.id, noInvitesFragment1.id]
    const res = await requests.sendRequest({
      body,
      userId: handlingEditor.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: noInvitesFragment1.id,
      },
    })

    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())
    expect(data.error).toEqual(
      'Cannot publish without at least one reviewer report.',
    )
  })

  it('should return an error when the fragmentId does not match the collectionId', async () => {
    const { reviewer } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    collection.fragments.length = 0
    const res = await requests.sendRequest({
      body,
      userId: reviewer.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())
    expect(data.error).toEqual('Collection and fragment do not match.')
  })
  it('should return an error when the collection does not exist', async () => {
    const { reviewer } = testFixtures.users
    const { fragment } = testFixtures.fragments
    const res = await requests.sendRequest({
      body,
      userId: reviewer.id,
      models,
      route,
      path,
      params: {
        collectionId: 'invalid-id',
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(404)
    const data = JSON.parse(res._getData())
    expect(data.error).toEqual('Item not found')
  })
  it('should return an error when the request user is not a reviewer', async () => {
    const { author } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments

    const res = await requests.sendRequest({
      body,
      userId: author.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(403)
    const data = JSON.parse(res._getData())
    expect(data.error).toEqual('Unauthorized.')
  })
  it('should return success when a HE recommends to reject', async () => {
    const { noRecommendationHE } = testFixtures.users
    const { noEditorRecomedationCollection } = testFixtures.collections
    const { noEditorRecomedationFragment } = testFixtures.fragments
    body.recommendation = 'reject'
    body.recommendationType = 'editorRecommendation'

    const res = await requests.sendRequest({
      body,
      userId: noRecommendationHE.id,
      models,
      route,
      path,
      params: {
        collectionId: noEditorRecomedationCollection.id,
        fragmentId: noEditorRecomedationFragment.id,
      },
    })

    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())

    expect(data.userId).toEqual(noRecommendationHE.id)
    expect(data.recommendation).toBe('reject')
  })
  it('should return an error when the user is inactive', async () => {
    const { inactiveUser } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    const res = await requests.sendRequest({
      body,
      userId: inactiveUser.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(403)
    const data = JSON.parse(res._getData())
    expect(data.error).toEqual('Unauthorized.')
  })
  it('should return success when the EiC recommends to reject without peer review', async () => {
    const { editorInChief } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    body.recommendation = 'reject'
    body.recommendationType = 'editorRecommendation'

    delete fragment.recommendations
    delete fragment.revision
    delete fragment.invitations
    delete collection.invitations
    delete collection.handlingEditor

    const res = await requests.sendRequest({
      body,
      userId: editorInChief.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())

    expect(data.userId).toEqual(editorInChief.id)
    expect(data.recommendation).toBe('reject')
  })
  it('should return success when the EiC recommends to publish without EQA', async () => {
    const { editorInChief } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    body.recommendation = 'publish'
    body.recommendationType = 'editorRecommendation'
    delete collection.technicalChecks

    const res = await requests.sendRequest({
      body,
      userId: editorInChief.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())

    expect(collection.status).toBe('inQA')
    expect(collection.technicalChecks).toHaveProperty('eqa')
    expect(collection.technicalChecks.eqa).toBeFalsy()
    expect(data.userId).toEqual(editorInChief.id)
    expect(data.recommendation).toBe('publish')
  })
  it('should return success when the EiC recommends to publish with EQA accepted', async () => {
    const { editorInChief } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    body.recommendation = 'publish'
    body.recommendationType = 'editorRecommendation'

    collection.technicalChecks.eqa = true
    const res = await requests.sendRequest({
      body,
      userId: editorInChief.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())

    expect(collection.status).toBe('accepted')
    expect(data.userId).toEqual(editorInChief.id)
    expect(data.recommendation).toBe('publish')
  })
  it('should return success when the EiC returns the manuscript to HE with comments after EQA returned to EiC', async () => {
    const { editorInChief } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    body.recommendation = 'return-to-handling-editor'
    body.recommendationType = 'editorRecommendation'
    body.comments = 'This needs more work'

    delete fragment.recommendations
    delete fragment.revision
    delete fragment.invitations
    collection.technicalChecks.eqa = false

    const res = await requests.sendRequest({
      body,
      userId: editorInChief.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())

    expect(collection.status).toBe('reviewCompleted')
    expect(collection.technicalChecks).not.toHaveProperty('token')
    expect(collection.technicalChecks).not.toHaveProperty('eqa')

    expect(data.userId).toEqual(editorInChief.id)
    expect(data.recommendation).toBe('return-to-handling-editor')
  })
  it('should return an error when a HE recommends to publish without a reviewer report', async () => {
    const { handlingEditor } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    body.recommendation = 'publish'
    body.recommendationType = 'editorRecommendation'
    fragment.recommendations = []

    const res = await requests.sendRequest({
      body,
      userId: handlingEditor.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())

    expect(data.error).toEqual(
      'Cannot publish without at least one reviewer report.',
    )
  })
  it('should return an error when a HE makes a recommendation on an older version of a manuscript', async () => {
    const { handlingEditor } = testFixtures.users
    const { twoVersionsCollection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    body.recommendation = 'publish'
    body.recommendationType = 'editorRecommendation'

    const res = await requests.sendRequest({
      body,
      userId: handlingEditor.id,
      models,
      route,
      path,
      params: {
        collectionId: twoVersionsCollection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())

    expect(data.error).toEqual(
      'Cannot make a recommendation on an older version.',
    )
  })
  it('should return an error when a reviewer writes a review on an older version of a manuscript', async () => {
    const { reviewer } = testFixtures.users
    const { twoVersionsCollection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    body.recommendationType = 'review'

    const res = await requests.sendRequest({
      body,
      userId: reviewer.id,
      models,
      route,
      path,
      params: {
        collectionId: twoVersionsCollection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())

    expect(data.error).toEqual('Cannot write a review on an older version.')
  })
  it('should return an error when a reviewer writes another review on the current version of a manuscript', async () => {
    const { newReviewer } = testFixtures.users
    const { noEditorRecomedationCollection } = testFixtures.collections
    const { noEditorRecomedationFragment } = testFixtures.fragments
    body.recommendationType = 'review'

    const res = await requests.sendRequest({
      body,
      userId: newReviewer.id,
      models,
      route,
      path,
      params: {
        collectionId: noEditorRecomedationCollection.id,
        fragmentId: noEditorRecomedationFragment.id,
      },
    })
    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())

    expect(data.error).toEqual('Cannot write another review on this version.')
  })

  it('should return success when creating another recommendation as a HE on the same version when EiC returned manuscript to He ', async () => {
    const { noRecommendationHE } = testFixtures.users
    const { noEditorRecomedationCollection } = testFixtures.collections
    const { noEditorRecomedationFragment } = testFixtures.fragments

    const res = await requests.sendRequest({
      body,
      userId: noRecommendationHE.id,
      models,
      route,
      path,
      params: {
        collectionId: noEditorRecomedationCollection.id,
        fragmentId: noEditorRecomedationFragment.id,
      },
    })
    expect(res.statusCode).toBe(200)
    const data = JSON.parse(res._getData())
    expect(data.userId).toEqual(noRecommendationHE.id)
  })

  it('should return an error when creating another recommendation as a HE on the same version after EiC made decision to publish', async () => {
    const { handlingEditor } = testFixtures.users
    const { collection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    body.recommendation = 'publish'
    body.recommendationType = 'editorRecommendation'

    const res = await requests.sendRequest({
      body,
      userId: handlingEditor.id,
      models,
      route,
      path,
      params: {
        collectionId: collection.id,
        fragmentId: fragment.id,
      },
    })
    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())
    expect(data.error).toEqual(
      'Cannot make another recommendation on this version.',
    )
  })

  it('should return an error when an EiC makes a decision on an older version of a manuscript', async () => {
    const { editorInChief } = testFixtures.users
    const { twoVersionsCollection } = testFixtures.collections
    const { fragment } = testFixtures.fragments
    body.recommendation = 'publish'
    body.recommendationType = 'editorRecommendation'

    const res = await requests.sendRequest({
      body,
      userId: editorInChief.id,
      models,
      route,
      path,
      params: {
        collectionId: twoVersionsCollection.id,
        fragmentId: fragment.id,
      },
    })

    expect(res.statusCode).toBe(400)
    const data = JSON.parse(res._getData())

    expect(data.error).toEqual(
      'Cannot make a recommendation on an older version.',
    )
  })
})