...
 
Commits (25)
{
"env": {
"es6": true,
"browser": true
"browser": true,
"cypress/globals": true
},
"extends": [
"pubsweet"
],
"extends": ["pubsweet"],
"plugins": ["cypress", "chai-friendly"],
"parser": "babel-eslint",
"overrides": [
{
"files": ["*.spec.js"],
"rules": {
"no-unused-expressions": 0,
"chai-friendly/no-unused-expressions": 2
}
}
],
"rules": {
"camelcase": 0,
"consistent-return": 0,
......
......@@ -2211,6 +2211,21 @@
}
}
},
"eslint-plugin-chai-friendly": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.4.1.tgz",
"integrity": "sha512-hkpLN7VVoGGsofZjUhcQ+sufC3FgqMJwD0DvAcRfxY1tVRyQyVsqpaKnToPHJQOrRo0FQ0fSEDwW2gr4rsNdGA==",
"dev": true
},
"eslint-plugin-cypress": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.0.1.tgz",
"integrity": "sha512-iC17zJhUxW4JMGQYyxq2lYl5vCUDqGnC2Qkmt1mMk3kn5XIu6ypbtAbREDTC+cFY6SG7USlsmURjTAJ4LtlM6A==",
"dev": true,
"requires": {
"globals": "^11.0.1"
}
},
"eslint-plugin-import": {
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.13.0.tgz",
......
......@@ -9,6 +9,8 @@
"cz-conventional-changelog": "^2.1.0",
"eslint": "4.19.1",
"eslint-config-pubsweet": "^0.0.6",
"eslint-plugin-chai-friendly": "^0.4.1",
"eslint-plugin-cypress": "^2.0.1",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jest": "^21.4.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
......@@ -36,6 +38,7 @@
"styleguide": "cd packages/styleguide && yarn styleguide",
"test": "lerna run test",
"start": "docker-compose up",
"test:e2e": "cd packages/hindawi-e2e && yarn cypress:open",
"start:services": "cd packages/xpub-faraday && yarn start:services",
"server": "cd packages/xpub-faraday && yarn server"
},
......
......@@ -27,6 +27,7 @@ const Login = ({ handleSubmit, fetchingError }) => (
<Label required>Email</Label>
<ValidatedField
component={TextField}
data-test-id="login-username"
name="username"
validate={[requiredValidator]}
/>
......@@ -38,6 +39,7 @@ const Login = ({ handleSubmit, fetchingError }) => (
<Label required>Password</Label>
<ValidatedField
component={PasswordField}
data-test-id="login-password"
name="password"
validate={[requiredValidator]}
/>
......@@ -50,7 +52,7 @@ const Login = ({ handleSubmit, fetchingError }) => (
</ActionLink>
</Row>
<Button primary type="submit">
<Button data-test-id="login-button" primary type="submit">
LOG IN
</Button>
......
cypress/screenshots
cypress/videos
cypress.json
\ No newline at end of file
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
\ No newline at end of file
{
"title": "Fragment 2",
"manuscriptType": "Research Article",
"abstract": "802.11B",
"authors": [
{
"firstName": "A",
"lastName": "M",
"email": "sabina.deliu+",
"country": "Romania",
"affiliation": "TSD"
}
]
}
{
"username": "admin",
"password": "password"
}
[{
"firstName": "Author",
"lastName": "boss",
"email": "auth@thinslices.com",
"username": "auth@thinslices.com",
"admin": false,
"editorInChief": false,
"handlingEditor": false
},
{
"firstName": "EiC",
"lastName": "Chief",
"email": "eic@thinslices.com",
"username": "eic@thinslices.com",
"admin": false,
"editorInChief": true,
"handlingEditor": false
},
{
"firstName": "Revington",
"lastName": "Dude",
"email": "rev@thinslices.com",
"username": "rev@thinslices.com",
"admin": false,
"editorInChief": false,
"handlingEditor": false
},
{
"firstName": "Revington2",
"lastName": "Dude",
"email": "rev2@thinslices.com",
"username": "rev2@thinslices.com",
"admin": false,
"editorInChief": false,
"handlingEditor": false
},
{
"firstName": "HE",
"lastName": "Hendlington",
"email": "he@thinslices.com",
"username": "he@thinslices.com",
"admin": false,
"editorInChief": false,
"handlingEditor": true
}
]
\ No newline at end of file
{
"firstName": "testAuthor",
"lastName": "testAuthor",
"affiliation": "TST",
"email": "auth@thinslices.com",
"password": "password",
"country": "Australia",
"title": "Professor"
}
\ No newline at end of file
{
"username": "eic@thinslices.com",
"password": "password"
}
\ No newline at end of file
{
"firstName": "testHE",
"lastName": "testHE",
"affiliation": "TST",
"email": "he@thinslices.com",
"password": "qweQWE123!@#",
"country": "Australia",
"title": "Professor"
}
\ No newline at end of file
[{
"firstName": "Reviewer1",
"lastName": "Rev1",
"affiliation": "TST",
"email": "rev1@thinslices.com",
"password": "password",
"country": "Australia"
},
{
"firstName": "Reviewer2",
"lastName": "Rev2",
"affiliation": "TST",
"email": "rev2@thinslices.com",
"password": "password",
"country": "Australia"
},
{
"firstName": "Reviewer",
"lastName": "Rev",
"affiliation": "TST",
"email": "rev@thinslices.com",
"password": "password",
"country": "Australia"
}]
\ No newline at end of file
describe('Clear Manuscripts from DB', () => {
it('Remove manuscripts from DB', () => {
cy.task('deleteAllManuscripts').should('equal', true)
})
})
describe('Clear users from DB', () => {
it('Remove users except admin from DB', () => {
cy.task('deleteAllUsers').should('equal', true)
})
})
describe('Create users into DB', () => {
beforeEach(() => {
cy.fixture('users/allUsers').as('users')
})
it('Create users except admin in DB', function createUsers() {
const { users } = this
cy.task('createUser', users).should('equal', true)
})
})
describe('Create new author', () => {
beforeEach(() => {
cy.fixture('users/author').as('author')
cy.fixture('users/admin').as('admin')
cy.fixture('users/he').as('he')
cy.visit('/')
})
it('Create succesfully a new author account', function createAuthor() {
const { author } = this
cy
.url()
.should('include', 'login?next=/dashboard')
.get('[href="/signup"]')
.click()
.url()
.should('include', '/signup')
.get('[name="firstName"]')
.type(author.firstName)
.get('[name="lastName"]')
.type(author.lastName)
.get('[role="listbox"]')
.first()
.click()
.get('[role="option"]')
.contains(author.title)
.click()
.get('[data-test-id="sign-up-country"]')
.click()
.get('[role="option"]')
.contains(author.country)
.click()
.get('[name="affiliation"]')
.type(author.affiliation)
.get('[type="checkbox"]')
.check({ force: true })
.get('[data-test-id="sign-up-proceed-to-set-email-and-password"]')
.click()
.url()
.should('include', '/signup')
.get('[name="email"]')
.type(
`${Cypress.env('email') +
Math.random()
.toString(22)
.substring(8)}@thinslices.com`,
)
.get('[name="password"]')
.type(author.password)
.get('[name="confirmNewPassword"]')
.type(author.password)
.get('[data-test-id="sign-up-confirm-button"]')
.click()
.wait(2000)
.url()
.should('include', '/dashboard')
.get('[data-test-id="new-manuscript"]')
.should('be.visible')
cy.contains('Your account is not confirmed. Please check your email.')
})
it('create new HE account', function createHE() {
const { he, admin } = this
cy.loginApi(admin.username, admin.password)
cy.wait(5000)
cy
.get('[data-test-id="admin-menu-button"]')
.click()
.get('[data-test-id="admin-dropdown-dashboard"]')
.first()
.click()
.title('contains', 'Admin dashboard')
.get('[role="img"]')
.first()
.click()
.visit('/admin/users')
.url()
.should('include', '/admin/users')
.get('[type="button"]')
.eq(2)
.click()
.get('[data-test-id="row"]')
.get('[name="email"]')
.type(
`${Cypress.env('email') +
Math.random()
.toString(22)
.substring(8)}@thinslices.com`,
)
.get('[role="listbox"]')
.first()
.click()
.get('[role="option"]')
.eq(2)
.click()
.get('[name="firstName"]')
.type(he.firstName)
.get('[name="lastName"]')
.type(he.lastName)
.get('[role="listbox"]')
.eq(1)
.click()
.get('[role="option"]')
.contains(he.title)
.click()
.get('[role="listbox"]')
.eq(2)
.click()
.get('[role="option"]')
.contains(he.country)
.click()
.get('[name="affiliation"]')
.type(he.affiliation)
.get('[type="button"]')
.contains('SAVE USER')
.click()
.wait(5000)
.get('[data-test-id="new-manuscript"]')
.should('be.visible')
})
})
describe('Login', () => {
beforeEach(() => {
cy.fixture('users/admin').as('admin')
cy.fixture('users/eic').as('eic')
cy.fixture('users/he').as('he')
cy.fixture('users/author').as('author')
cy.fixture('users/reviewer').as('reviewer')
cy.fixture('models/fragment').as('fragment')
})
let fragmentId = null
const num = Math.floor(Math.random() * 9000000) + 1000000
it('Successfully submits a manuscript', function submitNewManuscript() {
const { admin, fragment, author } = this
cy.visit(`/`)
cy.get('[name="username"]').type(Cypress.env('email') + author.email)
cy.get('[name="password"]').type(admin.password)
cy.get('[type="submit"]').click()
cy
.get('[data-test-id="new-manuscript"]')
.should('be.visible')
.debug()
.click()
cy
.get('[data-test-id="agree-checkbox"] input')
.should('be.visible')
.check({ force: true })
cy.get('[data-test-id="submission-next"]').click()
cy.location().then(loc => {
fragmentId = loc.pathname
.replace('/submit', '')
.split('/')
.pop()
})
cy
.get('[data-test-id="submission-title"] input')
.clear()
.type(fragment.title)
cy.get('[data-test-id="submission-type"] button').click()
cy.contains(fragment.manuscriptType).click({ force: true })
cy
.get('[data-test-id="submission-abstract"] textarea')
.clear()
.type(fragment.abstract)
fragment.authors.forEach(author => {
cy.get('[data-test-id="submission-add-author"] button').click()
cy.get('[data-test-id="author-card-email"]').type(
`${Cypress.env('email') +
Math.random()
.toString(22)
.substring(8)}@thinslices.com`,
)
cy.get('[data-test-id="author-card-firstname"]').type(author.firstName)
cy.get('[data-test-id="author-card-lastname"]').type(author.lastName)
cy
.get('[data-test-id="author-card-affiliation"]')
.type(author.affiliation)
// cy.get('[data-test-id="author-card-country"] button').click()
// cy.contains(author.country).click()
cy
.get('[data-test-id="item"]')
.find('[role="listbox"]')
.click()
cy
.get('[role="option"]')
.contains(author.country)
.click()
cy.get('[data-test-id="author-card-save"] span').click()
})
cy.wait(5000)
cy.get('[data-test-id="submission-next"]').click()
cy.wait(5000)
cy
.get('input[type="file"]')
.first()
.then(filePicker => {
cy.uploadFile({ fileName: 'file1.pdf', filePicker })
})
cy.wait(2000)
cy
.get('[data-test-id="submission-next"]')
.scrollIntoView()
.click({ force: true })
cy
.get('[data-test-id="modal-confirm"]')
.should('be.visible')
.click()
cy.wait(2000)
cy
.get('[data-test-id="go-to-dashboard"]')
.click()
.wait(2000)
.then(() => {
cy.get(`[data-test-id="fragment-${fragmentId}"]`).should('be.visible')
})
})
it('Manuscript approved by EQA', function approveManuscriptByEQA() {
const { admin } = this
cy.visit(`/`)
cy.get('[name="username"]').type(admin.username)
cy.get('[name="password"]').type(admin.password)
cy.get('[type="submit"]').click()
cy.wait(5000)
cy.get(`[data-test-id="fragment-${fragmentId}"]`).click()
cy
.get('[data-test-id="button-qa-manuscript-technical-checks"]')
.should('be.visible')
.click()
cy.contains('Did manuscript titled Fragment 2 pass EQS checks?')
cy.get('[data-test-id="eqs-yes-button"]').click()
cy
.get('[data-test-id="eqs-manuscript-id"]')
.should('be.visible')
.type(num)
cy.get('[data-test-id="modal-confirm"]').click()
cy.wait(2000)
cy.contains('Manuscript accepted. Thank you for your technical check!')
})
it('Assign HE as EiC', function assignHE() {
const { eic, he } = this
cy.visit('/')
cy.get('[name="username"]').type(Cypress.env('email') + eic.username)
cy.get('[name="password"]').type(eic.password)
cy.get('[type="submit"]').click()
cy
.get(`[data-test-id="fragment-${fragmentId}"]`)
.should('be.visible')
.click()
.wait(2000)
cy
.get('[data-test-id="manuscript-invite-he-button"]')
.should('be.visible')
.click()
cy
.get('[data-test-id="manuscript-assign-he-filter"]')
.type(Cypress.env('email') + he.email, { force: true })
cy.contains('INVITE').click()
cy.get('[data-test-id="modal-confirm"]').click()
cy
.get('[data-test-id="fragment-status"]')
.should('be.visible')
.should('contain', 'HE Invited')
})
it('Invite reviewers as HE', function inviteRev() {
const { reviewer, he } = this
cy.visit('/')
cy.get('[name="username"]').type(Cypress.env('email') + he.email)
cy.get('[name="password"]').type(he.password)
cy.get('[type="submit"]').click()
cy
.get(`[data-test-id="fragment-${fragmentId}"]`)
.should('be.visible')
.click()
cy
.get('[data-test-id="fragment-status"]')
.should('be.visible')
.should('contain', 'Respond to Invite')
cy.contains('Respond to Editorial Invitation')
cy.get('[value="accept"]').click()
cy
.get('[data-test-id="radio-respond-to-invitation"]')
.find('button')
.click()
cy.get('[data-test-id="modal-confirm"]').click()
cy
.get('[data-test-id="fragment-status"]')
.should('be.visible')
.should('contain', 'Invite Reviewers')
cy.reload(true)
cy
.get('[data-test-id="accordion-header"]')
.eq(2)
.click()
reviewer.forEach(reviewer => {
cy
.wait(5000)
.get('[data-test-id="invite-reviewer-email"]')
.should('be.visible')
.type(Cypress.env('email') + reviewer.email)
cy
.get('[data-test-id="invite-reviewer-first-name"]')
.type(reviewer.firstName)
cy
.get('[data-test-id="invite-reviewer-last-name"]')
.type(reviewer.lastName)
cy
.get('[data-test-id="invite-reviewer-affiliation"]')
.type(reviewer.affiliation)
cy.get('[placeholder="Please select"]').click()
cy.wait(2000)
cy.contains(reviewer.country).click()
cy.get('[data-type-id="button-invite-reviewer-invite"]').click()
cy.get('[data-test-id="modal-confirm"]').click()
})
})
it('Give a review for major revision as Reviewer', function submitMajReview() {
const { reviewer } = this
cy.visit('/')
cy.get('[name="username"]').type(Cypress.env('email') + reviewer[0].email)
cy.get('[name="password"]').type(reviewer[0].password)
cy.get('[type="submit"]').click()
cy
.get(`[data-test-id="fragment-${fragmentId}"]`)
.should('be.visible')
.click()
cy
.get('[data-test-id="fragment-status"]')
.should('be.visible')
.should('contain', 'Respond to Invite')
cy.contains('Respond to Invitation to Review')
cy.get('[value="accept"]').click()
cy
.get('[data-test-id="radio-respond-to-invitation"]')
.find('button')
.click()
cy.get('[data-test-id="modal-confirm"]').click()
cy.wait(3000)
cy
.get('[data-test-id="accordion-header"]')
.eq(2)
.should('be.visible')
.click()
cy
.get('[data-test-id="form-report-recommendation"]')
.find('[role="listbox"]')
.click()
cy
.get('[role="option"]')
.contains('Major Revision')
.click()
.wait(3000)
cy
.get('input[type="file"]')
.first()
.then(filePicker => {
cy.uploadFile({ fileName: 'file1.pdf', filePicker })
})
cy.wait(2000)
cy
.get('[data-test-id="form-report-textarea"] textarea')
.should('be.visible')
.wait(2000)
.click({ force: true })
.type(Math.random().toString(22))
cy
.get('[data-test-id="button-submit-report"]')
.should('be.visible')
.click()
})
})
describe('Login', () => {
beforeEach(() => {
cy.fixture('users/admin').as('admin')
cy.fixture('models/fragment').as('fragment')
})
it('Login successfully as admin', function loginAdmin() {
const { admin } = this
cy.visit(`https://qa.review.hindawi.com`)
cy.get('[name="username"]').type(admin.username)
cy.get('[name="password"]').type(admin.password)
cy.get('[type="submit"]').click()
cy.url().should('include', '/dashboard')
})
it('Login failure as admin', () => {
cy.visit(`https://qa.review.hindawi.com`)
cy.get('[name="username"]').type('admin')
cy.get('[name="password"]').type('password123')
cy.get('[type="submit"]').click()
cy.wait(2000)
cy.contains('Unauthorized')
cy.url().should('not.include', '.com/dashboard')
})
it('Test ', function loginReuse() {
const { admin } = this
cy.loginReuse2(admin.email, admin.password)
})
})
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
const uuid = require('uuid')
const getDBConfig = config => ({
user: config.env.dbConfig.user,
host: config.env.dbConfig.host,
database: config.env.dbConfig.database,
password: config.env.dbConfig.password,
port: 5432,
})
const createUserData = (userData, config) => ({
firstName: userData.firstName,
email: config.env.email + userData.email,
username: config.env.email + userData.email,
type: 'user',
orcid: {},
teams: [],
title: 'mrs',
country: 'RO',
isActive: true,
fragments: [],
collections: [],
isConfirmed: true,
affiliation: 'Hindawi Research',
accessTokens: {
unsubscribe: '6c1e12999266f4c7f535878c961a19f0',
},
passwordHash: '$2a$12$KA.cxkhmlyL9DfmBYLZV3.lgD5aQ2/88zXiesdaTcqMLNmGlOhzn.',
notifications: {
email: {
user: true,
},
},
})
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('task', {
async deleteAllManuscripts() {
const { Client } = require('pg')
const qaClient = new Client(getDBConfig(config))
await qaClient.connect()
await qaClient.query(
`DELETE FROM entities WHERE data->>'type' != 'user'`,
)
await qaClient.query(
`UPDATE entities SET data = jsonb_set(data, '{teams}', '[]') WHERE data->>'type' = 'user'`,
)
qaClient.end()
return true
},
async deleteAllUsers() {
const { Client } = require('pg')
const qaClient = new Client(getDBConfig(config))
await qaClient.connect()
await qaClient.query(
`DELETE FROM entities WHERE data->>'admin' <> 'true' OR data->>'admin' is null`,
)
qaClient.end()
return true
},
async createUser(users) {
const { Client } = require('pg')
const qaClient = new Client(getDBConfig(config))
await qaClient.connect()
await Promise.all(
users.map(async user => {
const userId = uuid.v4()
await qaClient.query(
`INSERT INTO entities (id, data) VALUES ('${userId}', '${JSON.stringify(
createUserData(user, config),
)}')`,
)
}),
)
qaClient.end()
return true
},
})
}
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
const fileTypes = {
pdf: 'application/pdf',
doc: 'application/msword',
docx:
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
txt: 'text/plain',
excel: 'application/vnd.ms-excel',
xlm: 'application/vnd.ms-excel',
}
Cypress.Commands.add(
'uploadFile',
({ fileName, fileType = 'pdf', filePicker }) => {
cy
.fixture(fileName, 'base64')
.then(Cypress.Blob.base64StringToBlob)
.then(blob => {
const el = filePicker[0]
const testFile = new File([blob], fileName, {
type: fileTypes[fileType],
})
const dataTransfer = new DataTransfer()
dataTransfer.items.add(testFile)
el.files = dataTransfer.files
})
},
)
Cypress.Commands.add('loginApi', (user, pwd) => {
cy
.request({
method: 'POST',
url: '/api/users/authenticate',
body: {
username: user,
password: pwd,
},
})
.then(response => {
const { token } = response.body
window.localStorage.setItem('token', token)
return token
})
cy.visit('dashboard')
})
Cypress.Commands.add('useFilters', () => {
cy.get('[data-test-id="row"]').contains('Filters')
cy.get('[data-test-id="dashboard-filter-priority"]').click()
cy
.get('[role="option"]')
.contains('Needs Attention')
.click()
cy.get('[data-test-id="fragment-status"]').then($fragment => {
Cypress._.each($fragment => {
expect($fragment).contain('Submit Revision')
})
})
})
Cypress.Commands.add('checkStatus', status => {
cy
.get('[data-test-id="fragment-status"]')
.contains('status')
.should('contain', status)
})
Cypress.Commands.add('searchManuscriptByFragmentId', () => {
cy.get('[data-test-id="/^fragment-w+/"]')
})
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// index.js
const customCommands = require('./commands.js')
module.exports = {
commands: customCommands,
}
// Alternatively you can use CommonJS syntax:
// require('./commands')
{
"name": "hindawi-e2e",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"cypress": "^3.1.0"
},
"scripts": {
"cypress:open": "./node_modules/.bin/cypress open",
"cypress:run": "./node_modules/.bin/cypress run",
"test": "echo \"Error: no test specified\" && exit 0"
},
"dependencies": {
"pg": "^7.7.1"
}
}
This diff is collapsed.