Commit ba64a761 authored by Ana Ellis's avatar Ana Ellis
Browse files

feat: pulled latest test changes from parent branch

parents d60e1e5b eb88c546
......@@ -9,8 +9,8 @@ This is the current set of features and their status on our roadmap.
|Module |Description |In progress |Done |Issue
|:--- |--- |:---: |:---: |:---:
|**Current**|
|Summary Info |Update text for metadata questions |✔ | |#165
|Summary Info |Add instructions for uploading files |✔ | |#168
|Summary Info |Update text for metadata questions | |✔ |#165
|Summary Info |Add instructions for uploading files | |✔ |#168
|System |Apply Coko theme |✔ | |pubsweet/pubsweet#372
|System |Implement roles & permissions |✔ | |#58
|System |Write integration tests |✔ | |
......
......@@ -13,6 +13,8 @@ export default {
value: 'no',
},
],
description:
'The journal requires data be openly available, and our full policy is <a href="https://www.collabra.org/about/editorialpolicies/#open-data-open-analytic-methods-code-and-research-materials-transparency" target="_blank">here</a>. If you have exceptions that need to be considered, please click "No" and explain in your cover letter below. Please click N/A if your submission does not feature data.',
},
{
id: 'previouslySubmitted',
......@@ -27,6 +29,8 @@ export default {
value: 'no',
},
],
description:
'Provide further details in your cover letter below, if necessary.',
},
{
id: 'openPeerReview',
......@@ -41,6 +45,8 @@ export default {
value: 'no',
},
],
description:
'Please read a description of our <a href="https://www.collabra.org/about/editorialpolicies/#open-peer-review" target="_blank">“Open Review”</a> option and select “Yes” if you choose this process.',
},
{
id: 'streamlinedReview',
......@@ -55,10 +61,13 @@ export default {
value: 'no',
},
],
description:
'Please read a description of our <a href="https://www.collabra.org/about/editorialpolicies/#streamlined-review" target="_blank">“Streamlined Review”</a> option and select “Yes” if you choose this process. If “Yes”, please upload your ported decision letter and reviews as “Supplementary Files” below, clearly labeled.',
},
{
id: 'researchNexus',
legend: 'Submitted as part of the research nexus ?',
legend:
'Submitted as part of the <a href="https://www.collabra.org/collections/special/" target="_blank">research nexus</a> ?',
options: [
{
label: 'Yes',
......@@ -69,6 +78,8 @@ export default {
value: 'no',
},
],
description:
'If yes, mention the name of the Research Nexus in your cover letter below.',
},
{
id: 'preregistered',
......@@ -83,6 +94,8 @@ export default {
value: 'no',
},
],
description:
'If any or all elements of your study have been pre-registered, click yes and ensure details are in the Acknowledgements section of your manuscript, following these <a href="https://www.collabra.org/about/editorialpolicies/#preregistration-of-studies-and-analysis-plans" target="_blank">guidelines</a>.',
},
],
}
......@@ -8,3 +8,5 @@ export { default as articleTypes } from './article-types'
export { default as editors } from './editors'
export { default as roles } from './roles'
export { default as reviewStatus } from './review-status'
export { default as notes } from './submit-notes'
export { default as supplementary } from './supplementary'
export default {
fundingAcknowledgement: {
placeholder: 'Enter an acknowledgment…',
title: 'Funding body acknowledgement (required)',
description:
'Please also specifically state if your work was <u>not</u> supported by funding.',
},
specialInstructions: {
placeholder: 'Enter instructions for the editor…',
title: 'Special instructions (confidential, to Editors only)',
description:
'Please write or paste what you would normally write into a cover letter here, focusing on information that has not been covered by this form, or any further information prompted by your answers to the questions above.',
},
}
export default {
description:
'<pre>There are 3 types of item which should be uploaded here:<br />' +
'<ul>' +
'<li>Regular figures which should also be be embedded in the manuscript.(These are helpful for the production<br />process,if your article is accepted, in case there are any issues with the embedded versions.)</li>' +
'<li>Supplemental items that provide a relevant and useful expansion of the article (Examples include appendices,<br/>very large tables, audios, videos, three-dimensional visualizations, interactive graphics, and so on.)</li>' +
'<li>Items that support the peer review process. (Examples are ported reviews and decision letters for use during<br />the Streamlined Review workflow. Also, if you have a long cover letter you would rather upload than paste<br/>into the text box, please also upload it here.)</li>' +
'</ul>' +
'Supplemental materials can be named in almost any way, provided that the files are clearly and consistently named,<br />are uploaded in chronological order, and grouped as they are described above. For example, a typical article<br/>might include:' +
'<br />' +
'<ul>' +
'<li>Figure 1.jpg</li>' +
'<li>Figure 2.jpg</li>' +
'<li>Figure 3.jpg</li>' +
'<li>Supplemental Table 1.docx</li>' +
'<li>Ported Decision Letter and Reviews from Journal X.docx</li>' +
'</ul>' +
'</pre>',
}
......@@ -13,7 +13,7 @@ class XpubCollabraMode {
constructor(userId, operation, object, context) {
this.userId = userId
this.operation = XpubCollabraMode.mapOperation(operation)
this.object = object
this.object = object.current ? object.current : object
this.context = context
}
......@@ -62,7 +62,7 @@ class XpubCollabraMode {
const memberships = await Promise.all(
this.user.teams.map(async teamId => {
const team = await this.context.models.Team.find(teamId)
if (!team) return [false]
return membershipCondition(team)
}),
)
......@@ -254,12 +254,17 @@ class XpubCollabraMode {
if (!this.isAuthenticated()) {
return false
}
const fragment = this.object
let permission = this.isAuthor(fragment)
permission = permission ? true : this.isAssignedReviewerEditor(fragment)
permission = permission ? true : this.isAssignedManagingEditor(fragment)
// Caveat: this means every logged-in user can read every fragment (but needs its UUID)
// Ideally we'd check if the fragment (version) belongs to a collection (project)
// where the user is a member of a team with the appropriate rights. However there is no
// link from a fragment back to a collection at this point. Something to keep in mind!
return true
return permission
}
/**
......@@ -444,6 +449,7 @@ class XpubCollabraMode {
(await this.isAssignedHandlingEditor(this.object)) ||
(await this.isAssignedSeniorEditor(this.object)) ||
(await this.isAssignedReviewerEditor(this.object))
return permission
}
......@@ -524,6 +530,14 @@ class XpubCollabraMode {
return this.checkPageSubmit(params)
}
if (path === '/projects/:project/versions/:version/review') {
return this.checkPageReview(params)
}
if (path === '/projects/:project/versions/:version/decision/:project') {
return this.checkPageDecision(params)
}
return true
}
......@@ -547,6 +561,25 @@ class XpubCollabraMode {
return permission
}
async checkPageReview(params) {
const collection = this.context.models.Collection.find(params.project)
const version = this.context.models.Fragment.find(params.version)
if (this.isAuthor(collection)) return false
let permission = await this.checkTeamMembers(
['isAssignedSeniorEditor', 'isAssignedHandlingEditor'],
collection,
)
// set object to Fragment so can validate on canReadFragment
this.object = version
permission = permission ? true : await this.canReadFragment()
return permission
}
async checkTeamMembers(team, object) {
const permission = await Promise.all(team.map(t => this[t](object)))
return permission.includes(true)
......@@ -574,32 +607,32 @@ module.exports = {
GET: (userId, operation, object, context) => {
const mode = new XpubCollabraMode(userId, operation, object, context)
// GET /api/collections
if (object && object.path === '/collections') {
if (mode.object && mode.object.path === '/collections') {
return mode.canListCollections()
}
// GET /api/users
if (object && object.path === '/users') {
if (mode.object && mode.object.path === '/users') {
return mode.canListUsers()
}
// GET /api/fragments
if (object && object.path === '/fragments') {
if (mode.object && mode.object.path === '/fragments') {
return mode.canListFragments()
}
// GET /api/teams
if (object && object.path === '/teams') {
if (mode.object && mode.object.path === '/teams') {
return mode.canListTeams()
}
// GET /api/collection
if (object && object.type === 'collection') {
if (mode.object && mode.object.type === 'collection') {
return mode.canReadCollection()
}
// GET /api/fragment
if (object && object.type === 'fragment') {
if (mode.object && mode.object.type === 'fragment') {
return mode.canReadFragment()
}
......@@ -609,7 +642,7 @@ module.exports = {
}
// GET /api/user
if (object && object.type === 'user') {
if (mode.object && mode.object.type === 'user') {
return mode.canReadUser()
}
......@@ -619,27 +652,30 @@ module.exports = {
const mode = new XpubCollabraMode(userId, operation, object, context)
// POST /api/collections
if (object && object.path === '/collections') {
if (mode.object && mode.object.path === '/collections') {
return mode.canCreateCollection()
}
// POST /api/users
if (object && object.path === '/users') {
if (mode.object && mode.object.path === '/users') {
return mode.canCreateUser()
}
// POST /api/fragments
if (object && object.path === '/fragments') {
if (mode.object && mode.object.path === '/fragments') {
return mode.canCreateFragment()
}
// POST /api/collections/:collectionId/fragments
if (object && object.path === '/collections/:collectionId/fragments') {
if (
mode.object &&
mode.object.path === '/collections/:collectionId/fragments'
) {
return mode.canCreateFragmentInACollection()
}
// POST /api/teams
if (object && object.path === '/teams') {
if (mode.object && mode.object.path === '/teams') {
return mode.canCreateTeam()
}
......@@ -649,27 +685,27 @@ module.exports = {
const mode = new XpubCollabraMode(userId, operation, object, context)
// PATCH /api/make-invitation
if (object && object.path === '/make-invitation') {
if (mode.object && mode.object.path === '/make-invitation') {
return mode.canMakeInvitation()
}
// PATCH /api/collections/:id
if (object && object.type === 'collection') {
if (mode.object && mode.object.type === 'collection') {
return mode.canUpdateCollection()
}
// PATCH /api/users/:id
if (object && object.type === 'user') {
if (mode.object && mode.object.type === 'user') {
return mode.canUpdateUser()
}
// PATCH /api/fragments/:id
if (object && object.type === 'fragment') {
if (mode.object && mode.object.type === 'fragment') {
return mode.canUpdateFragment()
}
// PATCH /api/teams/:id
if (object && object.type === 'team') {
if (mode.object && mode.object.type === 'team') {
return mode.canUpdateTeam()
}
......
import config from 'config'
import { Selector } from 'testcafe'
import { createSubmission } from './helpers/submission'
import { startServer, setup, teardown, setupManuscript } from './helpers/setup'
import { startServer, setup, teardown } from './helpers/setup'
import { setupWithTwoUnsubmittedManuscripts } from './fixtures/manuscript-setup/setup-two-unsubmitted'
import { login, dashboard, submission, confirmation } from './pageObjects'
import { collection2, fragment2 } from './fixtures/unsubmittedManuscript'
const goodInkConfig = {
inkEndpoint: 'http://inkdemo-api.coko.foundation/',
......@@ -24,25 +24,29 @@ fixture
})
.afterEach(teardown)
test.before(async t => {
const result = await setup()
author = result.userData
await login.doLogin(author.username, author.password)
})('create new submission', async t => {
await t.expect(Selector(dashboard.mySubmissionsTitle).exists).notOk()
test.skip
.before(async t => {
const result = await setup()
author = result.userData
await login.doLogin(author.username, author.password)
})('create new submission', async t => {
await t.expect(Selector(dashboard.mySubmissionsTitle).exists).notOk()
await createSubmission('./fixtures/testSubmission1.docx')
await createSubmission('./fixtures/testSubmission1.docx')
await t.click(dashboard.collabraHome)
await t.click(dashboard.collabraHome)
await t
.expect(Selector(dashboard.submissionStatus(1)).exists)
.ok()
.expect(dashboard.submissionStatus(1).innerText)
.contains('UNSUBMITTED')
.expect(Selector(dashboard.submissionSummaryInfoLink(0)).exists)
.ok()
})
await t
.expect(Selector(dashboard.submissionStatus(1)).exists)
.ok()
.expect(dashboard.submissionStatus(1).innerText)
.contains('UNSUBMITTED')
.expect(Selector(dashboard.submissionSummaryInfoLink(0)).exists)
.ok()
})
.after(async t => {
teardown()
})
test
.before(async t => {
......@@ -61,73 +65,74 @@ test
JSON.parse(JSON.stringify(config.get('pubsweet-component-ink-backend'))),
JSON.parse(JSON.stringify(config.get('pubsweet-component-ink-backend'))),
)
teardown()
})
test.before(async t => {
const result = await setup()
await setupManuscript(collection2, fragment2)
await setupManuscript(collection2, fragment2)
test
.before(async t => {
await setupWithTwoUnsubmittedManuscripts()
await login.doLogin('john', 'johnjohn')
})('Author submits manuscript', async t => {
await t
.wait(1000)
.click(dashboard.submissionSummaryInfoLink(1))
.wait(1000)
.click(submission.submit.withText('SUBMIT YOUR MANUSCRIPT'))
.wait(1000)
.expect(confirmation.returnToSubmission.exists)
author = result.userData
await t.wait(500).click(confirmation.returnToSubmission)
await login.doLogin(author.username, author.password)
})('Author submits manuscript', async t => {
await t
.click(dashboard.submissionSummaryInfoLink(1))
.wait(1000)
.click(submission.submit.withText('SUBMIT YOUR MANUSCRIPT'))
.wait(1000)
.expect(confirmation.returnToSubmission.exists)
await t
.expect(
Selector(submission.submit.withText('SUBMIT YOUR MANUSCRIPT')).exists,
)
.ok()
await t.wait(500).click(confirmation.returnToSubmission)
await t.click(submission.submit.withText('SUBMIT YOUR MANUSCRIPT'))
await t
.expect(
Selector(submission.submit.withText('SUBMIT YOUR MANUSCRIPT')).exists,
)
.ok()
await t.click(submission.submit.withText('SUBMIT YOUR MANUSCRIPT'))
await t
.wait(1000)
.click(submission.submit)
.expect(confirmation.submitManuscript.exists)
.ok()
await t.wait(1000).click(confirmation.submitManuscript)
await t
.expect(Selector(dashboard.myManuscriptsTitle).exists) // fails when admin user == false
.ok()
await t
.expect(Selector(dashboard.manuscript(1)).exists)
.ok()
.expect(Selector(dashboard.manuscriptStatus(1)).exists)
.ok()
.expect(dashboard.manuscriptStatus(1).innerText)
.contains('SUBMITTED')
.expect(Selector(dashboard.submissionStatus(1)).exists)
.ok()
.expect(dashboard.submissionStatus(1).innerText)
.contains('UNSUBMITTED')
await t
.expect(Selector(dashboard.submissionStatus(2)).exists)
.ok()
.expect(dashboard.submissionStatus(2).innerText)
.contains('SUBMITTED')
.wait(5000)
await t
.expect(Selector(dashboard.submissionSummaryInfoLink(0)).exists)
.ok()
.click(dashboard.submissionSummaryInfoLink(0))
.wait(5000)
.expect(
Selector(submission.submit.withText('SUBMIT YOUR MANUSCRIPT')).exists,
)
.notOk()
})
await t
.wait(1000)
.click(submission.submit)
.expect(confirmation.submitManuscript.exists)
.ok()
await t.wait(1000).click(confirmation.submitManuscript)
await t
.expect(Selector(dashboard.myManuscriptsTitle).exists) // fails when admin user == false
.ok()
await t
.expect(Selector(dashboard.manuscript(1)).exists)
.ok()
.expect(Selector(dashboard.manuscriptStatus(1)).exists)
.ok()
.expect(dashboard.manuscriptStatus(1).innerText)
.contains('SUBMITTED')
.expect(Selector(dashboard.submissionStatus(1)).exists)
.ok()
.expect(dashboard.submissionStatus(1).innerText)
.contains('UNSUBMITTED')
await t
.expect(Selector(dashboard.submissionStatus(2)).exists)
.ok()
.expect(dashboard.submissionStatus(2).innerText)
.contains('SUBMITTED')
.wait(5000)
await t
.expect(Selector(dashboard.submissionSummaryInfoLink(2)).exists)
.ok()
.click(dashboard.submissionSummaryInfoLink(2))
.wait(5000)
.expect(
Selector(submission.submit.withText('SUBMIT YOUR MANUSCRIPT')).exists,
)
.notOk()
})
.after(async t => {
teardown()
})
const logger = require('@pubsweet/logger')
const { Collection, Fragment, User } = require('pubsweet-server/src/models')
const { setupDb } = require('@pubsweet/db-manager')
export async function setupWithOneUnsubmittedManuscript() {
await setupDb({
username: 'admin',
password: 'password',
email: 'admin@example.com',
admin: true,
clobber: true,
})
const user = await new User({
username: 'john',
email: 'john@example.com',
password: 'johnjohn',
admin: true,
}).save()
const collection = new Collection({
title: 'My Blog',
owners: [user.id],
})
await collection.save()
const fragment1 = await new Fragment({
type: 'fragment',
files: {
manuscript: {
url: '/uploads/ec0a2df13f11ef9feaf4411b3ffd8c47.docx',
name: 'testSubmission1.docx',
},
supplementary: [],
},
notes: {
specialInstructions: 'special instructions...',
fundingAcknowledgement: '<p>funding acknowledgement...</p>',
},
owners: [user.id],
source:
'<html xmlns="http://www.w3.org/1999/xhtml">\n<head><title>This is a dummy document for testing purposes</title>\n<meta charset="UTF-8"/></head>\n<body>\n<container id="main">\n<h1>This is a dummy document for testing purposes</h1></container>\n\n</body></html>',
version: 1,
metadata: {
title: 'This is a dummy document for testing purposes',
authors: [
{
email: 'email@example.com',
lastName: 'John',
firstName: 'Cena',
affiliation: 'WWE',
},
],
abstract:
'This is a dummy document for testing purposes This is a dummy document for testing purposes This is a dummy document for testing purposes This is a dummy document for testing purposes This is a dummy document for testing purposes',
keywords: ['keywords...'],
articleType: 'original-research',