diff --git a/package.json b/package.json index dae1bfcdcc4a217bfb61017e65570233f3ceb044..0f8097f13261ee5e34948a62ec1751c1aa783fa9 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,13 @@ "private": true, "license": "MIT", "devDependencies": { - "babel-eslint": "^8.0.1", + "babel-eslint": "^8.0.3", "babel-preset-es2015": "^6.24.1", "eslint": "^4.13.1", - "eslint-config-stylelint": "^8.0.0", - "eslint-plugin-jest": "^21.4.2", - "stylelint-config-pubsweet": "^0.0.3", "eslint-config-pubsweet": "^0.0.6", + "eslint-config-stylelint": "^8.0.0", "eslint-plugin-import": "^2.8.0", + "eslint-plugin-jest": "^21.4.2", "eslint-plugin-jsx-a11y": "^6.0.2", "eslint-plugin-node": "^5.2.1", "eslint-plugin-prettier": "^2.3.1", @@ -22,6 +21,7 @@ "lint-staged": "^6.0.0", "prettier": "^1.8.2", "stylelint": "^8.2.0", + "stylelint-config-pubsweet": "^0.0.3", "stylelint-config-standard": "^18.0.0" }, "scripts": { diff --git a/packages/cli/cli/add.js b/packages/cli/cli/add.js index 596bb57edec1e87ca3549372473a5ecedb0ab395..e4f56c91924dff678c7102f4fe8878144d2870da 100644 --- a/packages/cli/cli/add.js +++ b/packages/cli/cli/add.js @@ -4,9 +4,7 @@ const program = require('commander') const _ = require('lodash') const readCommand = async argsOverride => { - program - .arguments('<components>') - .description(`Add component(s) to an app. + program.arguments('<components>').description(`Add component(s) to an app. <components> - a space-separated list of one or more components.`) diff --git a/packages/cli/cli/adduser.js b/packages/cli/cli/adduser.js index 3f410b22843a25d62b91650fb5935ed6c80c83c9..8fcd5807e6ceb2e761ec46e0658ea2b0907ed9b5 100644 --- a/packages/cli/cli/adduser.js +++ b/packages/cli/cli/adduser.js @@ -6,8 +6,9 @@ const _ = require('lodash') const config = require('config') const readCommand = async argsOverride => { - program - .description('Add a user to a database of a PubSweet app. Run from your project root') + program.description( + 'Add a user to a database of a PubSweet app. Run from your project root', + ) _.forEach(properties, (value, key) => { if (_.get('type', value) === 'boolean') { @@ -25,7 +26,7 @@ module.exports = async argsOverride => { const configOpts = config.has('dbManager') ? config.get('dbManager') : {} const promptOverride = _.merge(configOpts, commandOpts) - const finalOpts = await runPrompt({properties, override: promptOverride}) + const finalOpts = await runPrompt({ properties, override: promptOverride }) return addUser(finalOpts) } diff --git a/packages/cli/cli/build.js b/packages/cli/cli/build.js index 2d4d68f2d45cadd35cb33e202b178bf3f20a3a82..7e87fa7fb5c0ac52580c339a0ba37163a538d98f 100644 --- a/packages/cli/cli/build.js +++ b/packages/cli/cli/build.js @@ -2,8 +2,7 @@ const program = require('commander') const { buildProd } = require('../src/startup/') const readCommand = async argsOverride => { - program - .description('Build assets with webpack') + program.description('Build assets with webpack') return program.parse(argsOverride || process.argv) } diff --git a/packages/cli/cli/new.js b/packages/cli/cli/new.js index 23b4d038e26ca65b26e2a9b043496729b672cf39..1588215805a5dbe5bf22d7c2807327344a2c559c 100644 --- a/packages/cli/cli/new.js +++ b/packages/cli/cli/new.js @@ -9,7 +9,9 @@ const { STARTER_REPO_URL } = require('../src/constants') const readCommand = async argsOverride => { program .arguments('[name]') - .description('Generate a new app in the current working directory with name [name].') + .description( + 'Generate a new app in the current working directory with name [name].', + ) .option('--clobber', 'Overwrite any existing files') program.parse(argsOverride || process.argv) @@ -24,7 +26,7 @@ const readCommand = async argsOverride => { return { appName, clobber: program.clobber } } -const overWrite = (appPath) => { +const overWrite = appPath => { if (!fs.statSync(appPath).isDirectory()) { throw new Error(appPath, 'exists as a file. Will not overwrite.') } @@ -38,7 +40,9 @@ module.exports = async argsOverride => { const appPath = path.join(process.cwd(), appName) - if (clobber) { overWrite(appPath) } + if (clobber) { + overWrite(appPath) + } spawnSync('git', ['clone', STARTER_REPO_URL, appName], { stdio: 'inherit' }) @@ -48,7 +52,7 @@ module.exports = async argsOverride => { // const localYarn = path.join(__dirname, '..', 'node_modules', '.bin', 'yarn') spawnSync('yarn', ['install'], { cwd: appPath, - stdio: 'inherit' + stdio: 'inherit', }) logger.info('Finished generating initial app') diff --git a/packages/cli/cli/remove.js b/packages/cli/cli/remove.js index ed74051ad678df315109aa2b786a45146213d305..e55cb4c74e074486ecb7143306ebd66c9d0524ed 100644 --- a/packages/cli/cli/remove.js +++ b/packages/cli/cli/remove.js @@ -4,9 +4,7 @@ const program = require('commander') const _ = require('lodash') const readCommand = async argsOverride => { - program - .arguments('<components>') - .description(`Remove component(s) in an app. + program.arguments('<components>').description(`Remove component(s) in an app. <components> - a space-separated list of one or more components.`) @@ -18,7 +16,9 @@ module.exports = async argsOverride => { const components = commandOpts.args if (_.isEmpty(components)) { - const eg = colors.bold(`pubsweet remove ${colors.italic('login signup blog')}`) + const eg = colors.bold( + `pubsweet remove ${colors.italic('login signup blog')}`, + ) throw new Error(`You must specify one or more components, e.g. ${eg}`) } diff --git a/packages/cli/cli/setupdb.js b/packages/cli/cli/setupdb.js index e8cf99bec5fdf022bdf8b0ae99cc0eff6a43ffa9..00e8535487a98afb09886fd2af32876569941b70 100644 --- a/packages/cli/cli/setupdb.js +++ b/packages/cli/cli/setupdb.js @@ -6,8 +6,9 @@ const _ = require('lodash') const runPrompt = require('../src/run-prompt') const readCommand = async argsOverride => { - program - .description('Setup a database for a PubSweet app. Run from your project root') + program.description( + 'Setup a database for a PubSweet app. Run from your project root', + ) _.forEach(properties, (value, key) => { if (value.type === 'boolean') { @@ -26,7 +27,7 @@ module.exports = async argsOverride => { const configOpts = config.has('dbManager') ? config.get('dbManager') : {} const promptOverride = _.merge(configOpts, commandOpts) - const finalOpts = await runPrompt({properties, override: promptOverride}) + const finalOpts = await runPrompt({ properties, override: promptOverride }) return setupDb(finalOpts) } diff --git a/packages/cli/cli/start.js b/packages/cli/cli/start.js index c0b40bf1e981346ba674c2f4cc1c32995610ba2c..b2763843ef0bf735edaaaa154ecc3f57cc7b0419 100644 --- a/packages/cli/cli/start.js +++ b/packages/cli/cli/start.js @@ -10,7 +10,9 @@ const { ordinalize } = require('inflection') const readCommand = async argsOverride => { program .option('--reduxlog-off', 'Switch off Redux logger') - .description('Build assets and start the app with forever (not recommended for production).') + .description( + 'Build assets and start the app with forever (not recommended for production).', + ) return program.parse(argsOverride || process.argv) } @@ -21,7 +23,9 @@ module.exports = async argsOverride => { logger.info('Starting PubSweet app') if (!await dbExists()) { - throw new Error('Create database with "pubsweet setupdb" before starting app') + throw new Error( + 'Create database with "pubsweet setupdb" before starting app', + ) } const executable = path.join(__dirname, '..', 'src', 'startup', 'start.js') @@ -33,15 +37,15 @@ module.exports = async argsOverride => { watchDirectory: path.resolve('config'), // watchIgnorePatterns: ["./client-config.js"] // perhaps max: 10, - env: _.clone(process.env) + env: _.clone(process.env), } const configOpts = config.has('forever') ? config.get('forever') : {} const overrideOpts = { env: { - REDUXLOG_OFF: commandOpts.reduxlogOff - } + REDUXLOG_OFF: commandOpts.reduxlogOff, + }, } const finalOpts = _.merge(defaultOpts, configOpts, overrideOpts) @@ -50,7 +54,9 @@ module.exports = async argsOverride => { child.on('start', () => { logger.info(`App started.`) - logger.info('The app will be kept running, even if errors occur, until you stop it.') + logger.info( + 'The app will be kept running, even if errors occur, until you stop it.', + ) logger.info('To stop the app use ctrl-C') }) diff --git a/packages/cli/config/test.js b/packages/cli/config/test.js index c7dd50650b1d5582a41a4de80bfe0ef28373dda8..4ba52ba2c8df6758685c8f65f490306b5c44eb76 100644 --- a/packages/cli/config/test.js +++ b/packages/cli/config/test.js @@ -1 +1 @@ -module.exports = { } +module.exports = {} diff --git a/packages/cli/src/constants.js b/packages/cli/src/constants.js index 310e997f1dc9dd1d16d4111271da5ffa39f51d50..e4e410e18e1b46cd5b110ba5f161dfe9e6fd17cd 100644 --- a/packages/cli/src/constants.js +++ b/packages/cli/src/constants.js @@ -1,3 +1,4 @@ module.exports = { - STARTER_REPO_URL: 'https://gitlab.coko.foundation/pubsweet/pubsweet-starter.git' + STARTER_REPO_URL: + 'https://gitlab.coko.foundation/pubsweet/pubsweet-starter.git', } diff --git a/packages/cli/src/package-management/add.js b/packages/cli/src/package-management/add.js index cb7a3830ee79260521d91db344dc9a159799d15b..41b2976c4aba5af14964c2bb808b992f25ba292a 100644 --- a/packages/cli/src/package-management/add.js +++ b/packages/cli/src/package-management/add.js @@ -15,9 +15,9 @@ const updateConfig = addedComponents => { const configFile = path.join(process.cwd(), 'config', 'components.json') logger.info(`Adding new components to ${configFile}`) fs.ensureFileSync(configFile) - const oldComponents = fs.readJsonSync(configFile, {throws: false}) + const oldComponents = fs.readJsonSync(configFile, { throws: false }) const newComponents = _.union(oldComponents, addedComponents) - fs.writeJsonSync(configFile, newComponents, {spaces: '\t'}) + fs.writeJsonSync(configFile, newComponents, { spaces: '\t' }) logger.info('Finished updating components.json config') } diff --git a/packages/cli/src/package-management/helpers/index.js b/packages/cli/src/package-management/helpers/index.js index 19d500d9a4290cd225b4df21f0c58c50ea5d1ab9..31401a0564fc9627c18e5458c35838b65a0c4c7c 100644 --- a/packages/cli/src/package-management/helpers/index.js +++ b/packages/cli/src/package-management/helpers/index.js @@ -3,9 +3,8 @@ const path = require('path') // this regex matches all URL patterns and shortcuts accepted by npm // https://regex101.com/r/LWuC1E/1 -const isRepo = string => { - return /^((git\+?[^:]*:\/\/)|(github|gitlab|bitbucket|gist))/.test(string) -} +const isRepo = string => + /^((git\+?[^:]*:\/\/)|(github|gitlab|bitbucket|gist))/.test(string) const resolveName = name => { if (fs.pathExistsSync(name)) return `file:${name}` @@ -13,12 +12,11 @@ const resolveName = name => { return /^pubsweet-component/.test(name) ? name : `pubsweet-component-${name}` } -const getDepsFromPackageJson = () => { - return fs.readJsonSync(path.join(process.cwd(), 'package.json')).dependencies -} +const getDepsFromPackageJson = () => + fs.readJsonSync(path.join(process.cwd(), 'package.json')).dependencies module.exports = { isRepo, resolveName, - getDepsFromPackageJson + getDepsFromPackageJson, } diff --git a/packages/cli/src/package-management/index.js b/packages/cli/src/package-management/index.js index c1418b35b22831575af43e7a5dcfd5264858bc4d..7c6b15fe8d6a2bd9676ed08222365023f0fca4dc 100644 --- a/packages/cli/src/package-management/index.js +++ b/packages/cli/src/package-management/index.js @@ -1,4 +1,4 @@ module.exports = { add: require('./add'), - remove: require('./remove') + remove: require('./remove'), } diff --git a/packages/cli/src/package-management/remove.js b/packages/cli/src/package-management/remove.js index 80bf7d4c2839a4c1f29d39ff72dc9f54accfc7ec..363b38a3f29d2635abd7f9000d3f693ff68cdc9d 100644 --- a/packages/cli/src/package-management/remove.js +++ b/packages/cli/src/package-management/remove.js @@ -15,9 +15,9 @@ const updateConfig = removedComponents => { const configFile = path.join(process.cwd(), 'config', 'components.json') logger.info(`Removing components from ${configFile}`) fs.ensureFileSync(configFile) - const oldComponents = fs.readJsonSync(configFile, {throws: false}) + const oldComponents = fs.readJsonSync(configFile, { throws: false }) const newComponents = _.difference(oldComponents, removedComponents) - fs.writeJsonSync(configFile, newComponents, {spaces: '\t'}) + fs.writeJsonSync(configFile, newComponents, { spaces: '\t' }) logger.info('Finished updating components.json config') } diff --git a/packages/cli/src/run-prompt.js b/packages/cli/src/run-prompt.js index 84416f294f873660baf0f9a8ed10678fa2e3e860..52533d6a0a9e74b4373958e10643f98e00fd1f05 100644 --- a/packages/cli/src/run-prompt.js +++ b/packages/cli/src/run-prompt.js @@ -12,14 +12,14 @@ const logInput = result => { }) } -module.exports = async function runPrompt ({properties, override}) { +module.exports = async function runPrompt({ properties, override }) { const prompt = require('prompt') const promptGet = Promise.promisify(prompt.get) prompt.override = override prompt.message = colors.cyan('question:') prompt.delimiter = colors.green('><') prompt.start() - const result = await promptGet({properties}) + const result = await promptGet({ properties }) _.each(result, (val, key) => { result[key] = val === '' ? undefined : val diff --git a/packages/cli/src/schemas/db.js b/packages/cli/src/schemas/db.js index 69c82587747a958cf48c89f2c36939e3396f6fc0..97f2cbae26c8a1c13064f1cbcf961f291c7e3e00 100644 --- a/packages/cli/src/schemas/db.js +++ b/packages/cli/src/schemas/db.js @@ -1,18 +1,18 @@ module.exports = { username: { - description: 'Admin username' + description: 'Admin username', }, email: { - description: 'Admin email address' + description: 'Admin email address', }, password: { description: 'Admin password', hidden: true, - replace: '*' + replace: '*', }, clobber: { type: 'boolean', description: 'Overwrite any pre-existing database', - default: false - } + default: false, + }, } diff --git a/packages/cli/src/schemas/index.js b/packages/cli/src/schemas/index.js index 828c58a764ef548b31a32ddfcb97a829b52e04ff..fa1ba4193f4f32c6705f7b77c3e8f2ee95e328b0 100644 --- a/packages/cli/src/schemas/index.js +++ b/packages/cli/src/schemas/index.js @@ -1,4 +1,4 @@ module.exports = { db: require('./db'), - user: require('./user') + user: require('./user'), } diff --git a/packages/cli/src/schemas/user.js b/packages/cli/src/schemas/user.js index 33f05b1ea36b379524f9d10f75e5a7149f9e2124..f82efa2433e3d3b40d563b6b4c5b30be67dae998 100644 --- a/packages/cli/src/schemas/user.js +++ b/packages/cli/src/schemas/user.js @@ -1,18 +1,18 @@ module.exports = { username: { - description: 'Username' + description: 'Username', }, email: { - description: 'Email address' + description: 'Email address', }, password: { description: 'Password', hidden: true, - replace: '*' + replace: '*', }, admin: { description: 'Give user admin privileges', type: 'boolean', - default: true - } + default: true, + }, } diff --git a/packages/cli/src/startup/build.js b/packages/cli/src/startup/build.js index 94323b43fedd633162aff673d355a2605e051634..10df2ecb300b9196a343cb6725873cbdd2381773 100644 --- a/packages/cli/src/startup/build.js +++ b/packages/cli/src/startup/build.js @@ -1,4 +1,5 @@ const requireRelative = require('require-relative') + const webpack = requireRelative('webpack') const Promise = require('bluebird') const path = require('path') @@ -8,15 +9,20 @@ const logger = require('@pubsweet/logger') const onError = require('../error-exit') const config = require('config') -const webpackConfig = require(path.resolve('webpack', `webpack.${config.util.getEnv('NODE_ENV')}.config.js`)) +const webpackConfig = require(path.resolve( + 'webpack', + `webpack.${config.util.getEnv('NODE_ENV')}.config.js`, +)) const compiler = webpack(webpackConfig) const buildDev = async app => { try { - app.use(webpackDevMw(compiler, { - stats: 'normal', - publicPath: '/assets/' - })) + app.use( + webpackDevMw(compiler, { + stats: 'normal', + publicPath: '/assets/', + }), + ) app.use(webpackHotMw(compiler)) } catch (e) { logger.error('Webpack compilation failed') @@ -29,11 +35,14 @@ const buildProd = async () => { const compileForProd = Promise.promisify(compiler.run, { context: compiler }) try { const stats = await compileForProd() - logger.info('Webpack compilation completed:', stats.toString({ - hash: false, - chunks: false, - assets: false - })) + logger.info( + 'Webpack compilation completed:', + stats.toString({ + hash: false, + chunks: false, + assets: false, + }), + ) } catch (e) { logger.error('Webpack compilation failed') onError(e) @@ -43,9 +52,8 @@ const buildProd = async () => { const build = async app => { if (config.util.getEnv('NODE_ENV') === 'development') { return buildDev(app) - } else { - return buildProd() } + return buildProd() } module.exports = { build, buildDev, buildProd } diff --git a/packages/cli/src/startup/index.js b/packages/cli/src/startup/index.js index be927f76fbe35fc91f38f17b5ddbc160886ae429..b4ffc53855571216f9018077bac63c655ec03b9b 100644 --- a/packages/cli/src/startup/index.js +++ b/packages/cli/src/startup/index.js @@ -1,4 +1,4 @@ module.exports = { start: require('./start'), - ...require('./build') + ...require('./build'), } diff --git a/packages/cli/src/startup/start.js b/packages/cli/src/startup/start.js index 9db3d818456ebc961b1db4ca8749892232d92692..aa0e600845fba98f5a98503756abacec33c3e3cd 100644 --- a/packages/cli/src/startup/start.js +++ b/packages/cli/src/startup/start.js @@ -14,7 +14,8 @@ const start = async () => { return server } -if (require.main === module) { // file is being executed +if (require.main === module) { + // file is being executed start() } else { module.exports = start diff --git a/packages/cli/test/cli/add.js b/packages/cli/test/cli/add.js index 555a0693fc9f469555d93540183776f240c2937b..8ca9500c8512f350fa7ad6587de7f999d98ad7fa 100644 --- a/packages/cli/test/cli/add.js +++ b/packages/cli/test/cli/add.js @@ -17,12 +17,16 @@ const { getMockArgv } = require('../helpers/') const runAdd = require('../../cli/add') const spawnSpy = require('child_process').spawnSync -const readPkgSpy = require('../../src/package-management/helpers/').getDepsFromPackageJson +const readPkgSpy = require('../../src/package-management/helpers/') + .getDepsFromPackageJson + const writeSpy = fs.writeJsonSync describe('add', () => { beforeAll(() => { - process.chdir(path.join(__dirname, '..', '..', 'node_modules', '@pubsweet', 'starter')) + process.chdir( + path.join(__dirname, '..', '..', 'node_modules', '@pubsweet', 'starter'), + ) }) beforeEach(() => { @@ -34,8 +38,7 @@ describe('add', () => { }) it('requires a component', async () => { - await expect(runAdd(getMockArgv(''))).rejects - .toBeInstanceOf(Error) + await expect(runAdd(getMockArgv(''))).rejects.toBeInstanceOf(Error) }) it('calls function to write new component into components.json with correct argument', async () => { @@ -44,7 +47,7 @@ describe('add', () => { readPkgSpy .mockImplementationOnce(() => ({})) .mockImplementationOnce(() => ({ [fullName]: 'version' })) - await runAdd(getMockArgv({args: componentName})) + await runAdd(getMockArgv({ args: componentName })) const calls = writeSpy.mock.calls expect(calls).toHaveLength(1) expect(calls[0][1]).toContain(fullName) @@ -52,7 +55,7 @@ describe('add', () => { it('spawns yarn child process with correct arguments', async () => { const componentName = 'test-widget' - await runAdd(getMockArgv({args: componentName})) + await runAdd(getMockArgv({ args: componentName })) const calls = spawnSpy.mock.calls expect(calls).toHaveLength(1) expect(calls[0][1][1]).toBe(`pubsweet-component-${componentName}`) diff --git a/packages/cli/test/cli/adduser.js b/packages/cli/test/cli/adduser.js index 56023a98d9f261fed973cc8ce00620dc92c20cc7..41a9b5767d887d118e9f39b79763fef57d486cb2 100644 --- a/packages/cli/test/cli/adduser.js +++ b/packages/cli/test/cli/adduser.js @@ -9,7 +9,7 @@ const user = { username: 'anotheruser', email: 'bar@example.com', password: '12345', - admin: true + admin: true, } describe('adduser', () => { diff --git a/packages/cli/test/cli/new.js b/packages/cli/test/cli/new.js index c4443cdfc4b1b1eee8c3a010f09af3f049d1edb1..b9c3fe77e36c36201cb22fcaa6ddb912f2c44be5 100644 --- a/packages/cli/test/cli/new.js +++ b/packages/cli/test/cli/new.js @@ -11,6 +11,7 @@ const { getMockArgv } = require('../helpers/') const runNew = require('../../cli/new') const spawnSpy = require('child_process').spawnSync + const removeSpy = fs.removeSync const appName = 'testapp' @@ -18,7 +19,7 @@ const appPath = path.join(process.cwd(), appName) describe('new', () => { it('spawns git and yarn child processes with correct arguments', async () => { - await runNew(getMockArgv({args: appName})) + await runNew(getMockArgv({ args: appName })) const calls = spawnSpy.mock.calls expect(calls).toHaveLength(2) expect(calls[0][1][2]).toBe(appName) @@ -27,7 +28,7 @@ describe('new', () => { it('will not overwrite dir without clobber passed', async () => { fs.ensureDirSync(path.join(appPath, 'block-write')) - await runNew(getMockArgv({args: appName})) + await runNew(getMockArgv({ args: appName })) const calls = removeSpy.mock.calls expect(calls).toHaveLength(0) const notOverwritten = fs.existsSync(appPath) @@ -37,7 +38,7 @@ describe('new', () => { it('will overwrite dir with clobber passed', async () => { fs.ensureDirSync(path.join(appPath, 'block-write')) - await runNew(getMockArgv({args: appName, options: {clobber: true}})) + await runNew(getMockArgv({ args: appName, options: { clobber: true } })) const calls = removeSpy.mock.calls expect(calls[0][0]).toBe(appPath) require.requireActual('fs-extra').removeSync(appPath) diff --git a/packages/cli/test/cli/remove.js b/packages/cli/test/cli/remove.js index 7daa144e697a899d2a13ff59ad9285221cb6a23b..8ba9a3570afd6b69f0b9744eb659e67b399aaac6 100644 --- a/packages/cli/test/cli/remove.js +++ b/packages/cli/test/cli/remove.js @@ -17,12 +17,16 @@ const { getMockArgv } = require('../helpers/') const runRemove = require('../../cli/remove') const spawnSpy = require('child_process').spawnSync -const readPkgSpy = require('../../src/package-management/helpers/').getDepsFromPackageJson +const readPkgSpy = require('../../src/package-management/helpers/') + .getDepsFromPackageJson + const writeSpy = fs.writeJsonSync describe('remove', () => { beforeAll(() => { - process.chdir(path.join(__dirname, '..', '..', 'node_modules', '@pubsweet', 'starter')) + process.chdir( + path.join(__dirname, '..', '..', 'node_modules', '@pubsweet', 'starter'), + ) }) beforeEach(() => { @@ -34,8 +38,7 @@ describe('remove', () => { }) it('requires a component', async () => { - await expect(runRemove(getMockArgv(''))).rejects - .toBeInstanceOf(Error) + await expect(runRemove(getMockArgv(''))).rejects.toBeInstanceOf(Error) }) it('removes component from components.json', async () => { @@ -44,7 +47,7 @@ describe('remove', () => { readPkgSpy .mockImplementationOnce(() => ({ [fullName]: 'version' })) .mockImplementationOnce(() => ({})) - await runRemove(getMockArgv({args: componentName})) + await runRemove(getMockArgv({ args: componentName })) const calls = writeSpy.mock.calls expect(calls).toHaveLength(1) expect(calls[0][1]).not.toContain(fullName) @@ -52,7 +55,7 @@ describe('remove', () => { it('spawns yarn child process with correct arguments', async () => { const componentName = 'test-widget' - await runRemove(getMockArgv({args: componentName})) + await runRemove(getMockArgv({ args: componentName })) const calls = spawnSpy.mock.calls expect(calls).toHaveLength(1) expect(calls[0][1][1]).toBe(`pubsweet-component-${componentName}`) diff --git a/packages/cli/test/cli/setupdb.js b/packages/cli/test/cli/setupdb.js index ce55424b8361f9453df26573673e173849d689c7..0a0ba7ea9a0557eec2d7f0ff51e28bca94109418 100644 --- a/packages/cli/test/cli/setupdb.js +++ b/packages/cli/test/cli/setupdb.js @@ -8,7 +8,7 @@ const setupDbSpy = require('@pubsweet/db-manager').setupDb const dbOpts = { username: 'anotheruser', email: 'bar@example.com', - password: '12345' + password: '12345', } describe('setupdb', () => { diff --git a/packages/cli/test/cli/start.js b/packages/cli/test/cli/start.js index 867bea7ec79306a7b27cd00ca80eda23fa145d10..e7038c17f5c8b82e6ac51bbbd84979c582c93ac6 100644 --- a/packages/cli/test/cli/start.js +++ b/packages/cli/test/cli/start.js @@ -1,27 +1,34 @@ -jest.mock('webpack', () => { +jest.mock('webpack', () => {}) +jest.mock( + require('path').resolve( + 'webpack', + `webpack.${require('config').util.getEnv('NODE_ENV')}.config.js`, + ), + () => {}, + { virtual: true }, +) +jest.mock(require('path').resolve('config', 'components.json'), () => [], { + virtual: true, }) -jest.mock(require('path').resolve('webpack', `webpack.${require('config').util.getEnv('NODE_ENV')}.config.js`), () => {}, {virtual: true}) -jest.mock(require('path').resolve('config', 'components.json'), () => [], {virtual: true}) + jest.mock('forever-monitor', () => ({ - start: jest.fn(() => ({ on: jest.fn() })) + start: jest.fn(() => ({ on: jest.fn() })), })) -jest.mock('require-relative', () => { - return (required) => { - if (required === 'webpack') { - const compiler = { - run: cb => cb(null, {}) - } - return () => compiler - } else { - return (app) => require('bluebird').resolve({ on: jest.fn(), app }) +jest.mock('require-relative', () => required => { + if (required === 'webpack') { + const compiler = { + run: cb => cb(null, {}), } + return () => compiler } + return app => require('bluebird').resolve({ on: jest.fn(), app }) }) const { getMockArgv } = require('../helpers/') const Promise = require('bluebird') const config = require('config') + config['pubsweet-server'] = { dbPath: __dirname, adapter: 'leveldb' } const runStart = require('../../cli/start') const start = require('../../src/startup/start.js') @@ -35,7 +42,10 @@ describe('start', () => { }) it('throws an error if no database found', async () => { - await expect(runStart(getMockArgv(''))).rejects.toHaveProperty('message', `Create database with "pubsweet setupdb" before starting app`) + await expect(runStart(getMockArgv(''))).rejects.toHaveProperty( + 'message', + `Create database with "pubsweet setupdb" before starting app`, + ) }) it('calls startServer with an express app', async () => { diff --git a/packages/cli/test/helpers/index.js b/packages/cli/test/helpers/index.js index 0b134354b9e1cf28f26f9caaa5565831692b4c6c..b46128c2ab69fb1bd44f8cb7ac3872757b5e167f 100644 --- a/packages/cli/test/helpers/index.js +++ b/packages/cli/test/helpers/index.js @@ -1,43 +1,50 @@ const spawnSync = require('child_process').spawnSync const spawn = require('child_process').spawn const path = require('path') -const reduce = require('lodash/fp/reduce').convert({cap: false}) +const reduce = require('lodash/fp/reduce').convert({ cap: false }) const pubsweet = path.resolve('bin/pubsweet.js') -const formatOpts = reduce((acc, value, key) => acc.concat(`--${key}`, value), []) -const joinArgsAndOpts = (args, options) => (args ? args.split(' ') : []).concat(formatOpts(options)) - -const getEnvWithConfig = (config = {}) => Object.assign( - {}, - process.env, - { NODE_CONFIG: JSON.stringify(config) } +const formatOpts = reduce( + (acc, value, key) => acc.concat(`--${key}`, value), + [], ) +const joinArgsAndOpts = (args, options) => + (args ? args.split(' ') : []).concat(formatOpts(options)) + +const getEnvWithConfig = (config = {}) => + Object.assign({}, process.env, { NODE_CONFIG: JSON.stringify(config) }) -const getMockArgv = ({ args, options }) => ['mock/exec/path', 'mock/file/path'] - .concat(joinArgsAndOpts(args, options)) +const getMockArgv = ({ args, options }) => + ['mock/exec/path', 'mock/file/path'].concat(joinArgsAndOpts(args, options)) const runCommandSync = ({ args, options, cwd, nodeConfig, stdio }) => { - const { stdout, stderr } = spawnSync(pubsweet, joinArgsAndOpts(args, options), { - cwd, - stdio, - env: getEnvWithConfig(nodeConfig), - shell: true - }) - return ({ stdout: stdout ? stdout.toString() : null, stderr: stderr ? stderr.toString() : null }) + const { stdout, stderr } = spawnSync( + pubsweet, + joinArgsAndOpts(args, options), + { + cwd, + stdio, + env: getEnvWithConfig(nodeConfig), + shell: true, + }, + ) + return { + stdout: stdout ? stdout.toString() : null, + stderr: stderr ? stderr.toString() : null, + } } -const runCommandAsync = ({ args, options, cwd, nodeConfig, stdio }) => { - return spawn(pubsweet, joinArgsAndOpts(args, options), { +const runCommandAsync = ({ args, options, cwd, nodeConfig, stdio }) => + spawn(pubsweet, joinArgsAndOpts(args, options), { cwd, stdio, env: getEnvWithConfig(nodeConfig), - shell: true + shell: true, }) -} module.exports = { getMockArgv, runCommandSync, - runCommandAsync + runCommandAsync, } diff --git a/packages/cli/test/integration.js b/packages/cli/test/integration.js index b0ca201d854f04c328963b9a01ae8282dca49184..05d32603e5469c4f2a1eee2630a4d1b3e4bdb42b 100644 --- a/packages/cli/test/integration.js +++ b/packages/cli/test/integration.js @@ -15,14 +15,14 @@ const dbPath = path.join(dbDir, dbName) const nodeConfig = { 'pubsweet-server': { dbPath, - adapter: 'leveldb' - } + adapter: 'leveldb', + }, } const dbOptions = { username: 'someuser', email: 'user@test.com', - password: '12345678' + password: '12345678', } /* These tests run "pubsweet" commands as child processes with no mocking */ @@ -40,8 +40,14 @@ describe('CLI: integration test', () => { describe('new', () => { it('will not overwrite non-empty dir', () => { fs.ensureDirSync(path.join(appPath, 'blocking-dir')) - const { stderr } = runCommandSync({ args: `new ${appName}`, cwd: tempDir, stdio: 'pipe' }) - expect(stderr).toContain(`destination path 'testapp' already exists and is not an empty directory`) + const { stderr } = runCommandSync({ + args: `new ${appName}`, + cwd: tempDir, + stdio: 'pipe', + }) + expect(stderr).toContain( + `destination path 'testapp' already exists and is not an empty directory`, + ) fs.emptyDirSync(tempDir) }) @@ -61,15 +67,25 @@ describe('CLI: integration test', () => { }) it('adds component', () => { - const { stdout, stderr } = runCommandSync({ args: `add ${componentName}`, cwd: appPath, stdio: 'pipe' }) + const { stdout, stderr } = runCommandSync({ + args: `add ${componentName}`, + cwd: appPath, + stdio: 'pipe', + }) console.log(stdout, stderr) expect(stdout).toContain('Success: 1 components installed') const configPostAdd = fs.readJsonSync(componentsFile) - expect(configPostAdd).toEqual(oldComponents.concat(`pubsweet-component-${componentName}`)) + expect(configPostAdd).toEqual( + oldComponents.concat(`pubsweet-component-${componentName}`), + ) }) it('removes component', () => { - const { stdout, stderr } = runCommandSync({ args: `remove ${componentName}`, cwd: appPath, stdio: 'pipe' }) + const { stdout, stderr } = runCommandSync({ + args: `remove ${componentName}`, + cwd: appPath, + stdio: 'pipe', + }) console.log(stdout, stderr) expect(stdout).toContain('Success: 1 components removed') const configPostRemove = fs.readJsonSync(componentsFile) @@ -86,7 +102,7 @@ describe('CLI: integration test', () => { options: dbOptions, stdio: 'pipe', cwd: appPath, - nodeConfig + nodeConfig, }) console.log(stdout, stderr) @@ -104,7 +120,7 @@ describe('CLI: integration test', () => { args: 'build', stdio: 'inherit', cwd: appPath, - nodeConfig + nodeConfig, }) expect(fs.existsSync(path.join(buildDir, 'assets', 'app.js'))).toBe(true) @@ -113,7 +129,7 @@ describe('CLI: integration test', () => { }) describe('start', () => { - it('starts an app', (done) => { + it('starts an app', done => { fs.ensureDirSync(dbDir) runCommandSync({ @@ -121,14 +137,14 @@ describe('CLI: integration test', () => { options: dbOptions, stdio: 'inherit', cwd: appPath, - nodeConfig + nodeConfig, }) const app = runCommandAsync({ args: 'start', cwd: appPath, stdio: 'pipe', - nodeConfig + nodeConfig, }) app.stderr.on('data', async data => { diff --git a/packages/client/config/test.js b/packages/client/config/test.js index bf905ac797acea67e836c178eda263d5adf93862..f079e8683729ec17a8b8913d22ec45256390f098 100644 --- a/packages/client/config/test.js +++ b/packages/client/config/test.js @@ -6,6 +6,6 @@ module.exports = { }, }, authsome: { - mode: 'fake-mode' + mode: 'fake-mode', }, } diff --git a/packages/client/src/actions/collections.js b/packages/client/src/actions/collections.js index 2a06e4044450e9c13a98f082aba1c4c4f08c799c..20899c4252fc930f4ef8ce8ac71717f26e69b2db 100644 --- a/packages/client/src/actions/collections.js +++ b/packages/client/src/actions/collections.js @@ -20,14 +20,14 @@ function getCollectionsRequest() { function getCollectionsFailure(error) { return { type: T.GET_COLLECTIONS_FAILURE, - error: error, + error, } } function getCollectionsSuccess(collections) { return { type: T.GET_COLLECTIONS_SUCCESS, - collections: collections, + collections, receivedAt: Date.now(), } } @@ -39,7 +39,7 @@ export function getCollections(options) { let url = collectionUrl() if (options && options.fields) { - url += '?fields=' + encodeURIComponent(options.fields.join(',')) + url += `?fields=${encodeURIComponent(options.fields.join(','))}` } return api @@ -60,7 +60,7 @@ function getCollectionTeamsRequest() { function getCollectionTeamsFailure(error) { return { type: T.GET_COLLECTION_TEAMS_FAILURE, - error: error, + error, } } @@ -76,7 +76,7 @@ export function getCollectionTeams(collection) { return dispatch => { dispatch(getCollectionTeamsRequest()) - let url = collectionUrl(collection, 'teams') + const url = collectionUrl(collection, 'teams') return api .get(url) @@ -90,14 +90,14 @@ export function getCollectionTeams(collection) { function createCollectionRequest(collection) { return { type: T.CREATE_COLLECTION_REQUEST, - collection: collection, + collection, } } function createCollectionSuccess(collection) { return { type: T.CREATE_COLLECTION_SUCCESS, - collection: collection, + collection, } } @@ -105,8 +105,8 @@ function createCollectionFailure(collection, error) { return { type: T.CREATE_COLLECTION_FAILURE, isFetching: false, - collection: collection, - error: error, + collection, + error, } } @@ -128,14 +128,14 @@ export function createCollection(collection) { function getCollectionRequest(collection) { return { type: T.GET_COLLECTION_REQUEST, - collection: collection, + collection, } } function getCollectionSuccess(collection) { return { type: T.GET_COLLECTION_SUCCESS, - collection: collection, + collection, receivedAt: Date.now(), } } @@ -144,8 +144,8 @@ function getCollectionFailure(collection, error) { return { type: T.GET_COLLECTION_FAILURE, isFetching: false, - collection: collection, - error: error, + collection, + error, } } @@ -167,15 +167,15 @@ export function getCollection(collection) { function updateCollectionRequest(collection) { return { type: T.UPDATE_COLLECTION_REQUEST, - collection: collection, + collection, } } function updateCollectionSuccess(collection, update) { return { type: T.UPDATE_COLLECTION_SUCCESS, - collection: collection, - update: update, + collection, + update, receivedAt: Date.now(), } } @@ -184,8 +184,8 @@ function updateCollectionFailure(collection, error) { return { type: T.UPDATE_COLLECTION_FAILURE, isFetching: false, - collection: collection, - error: error, + collection, + error, } } @@ -207,7 +207,7 @@ export function updateCollection(collection) { function deleteCollectionRequest(collection) { return { type: T.DELETE_COLLECTION_REQUEST, - collection: collection, + collection, update: { deleted: true }, } } @@ -215,16 +215,16 @@ function deleteCollectionRequest(collection) { function deleteCollectionSuccess(collection) { return { type: T.DELETE_COLLECTION_SUCCESS, - collection: collection, + collection, } } function deleteCollectionFailure(collection, error) { return { type: T.DELETE_COLLECTION_FAILURE, - collection: collection, + collection, update: { deleted: undefined }, - error: error, + error, } } diff --git a/packages/client/src/actions/fileUpload.js b/packages/client/src/actions/fileUpload.js index ee67814de34d550bb8746867443accd2213defca..73ef12bee1e0f259ec69fd825bcb68f459a449fd 100644 --- a/packages/client/src/actions/fileUpload.js +++ b/packages/client/src/actions/fileUpload.js @@ -12,7 +12,7 @@ function fileUploadSuccess(file) { return { type: T.FILE_UPLOAD_SUCCESS, isFetching: false, - file: file, + file, } } @@ -31,7 +31,7 @@ export function fileUpload(file) { const data = new FormData() data.append('file', file) - let opts = { + const opts = { method: 'POST', headers: { Accept: 'text/plain', // the response is a URL diff --git a/packages/client/src/actions/fragments.js b/packages/client/src/actions/fragments.js index 0f9197be1c6a1590c50336c3fcd233effe358f11..23e558257e87240658c72aee15441e8699fbc91a 100644 --- a/packages/client/src/actions/fragments.js +++ b/packages/client/src/actions/fragments.js @@ -13,15 +13,15 @@ export const fragmentUrl = (collection, fragment) => { function getFragmentsRequest(collection) { return { type: T.GET_FRAGMENTS_REQUEST, - collection: collection, + collection, } } function getFragmentsSuccess(collection, fragments) { return { type: T.GET_FRAGMENTS_SUCCESS, - collection: collection, - fragments: fragments, + collection, + fragments, receivedAt: Date.now(), } } @@ -29,7 +29,7 @@ function getFragmentsSuccess(collection, fragments) { function getFragmentsFailure(error) { return { type: T.GET_FRAGMENTS_FAILURE, - error: error, + error, } } @@ -40,7 +40,7 @@ export function getFragments(collection, options) { let url = fragmentUrl(collection) if (options && options.fields) { - url += '?fields=' + encodeURIComponent(options.fields.join(',')) + url += `?fields=${encodeURIComponent(options.fields.join(','))}` } return api @@ -55,15 +55,15 @@ export function getFragments(collection, options) { function createFragmentRequest(fragment) { return { type: T.CREATE_FRAGMENT_REQUEST, - fragment: fragment, + fragment, } } function createFragmentSuccess(collection, fragment) { return { type: T.CREATE_FRAGMENT_SUCCESS, - collection: collection, - fragment: fragment, + collection, + fragment, } } @@ -71,8 +71,8 @@ function createFragmentFailure(fragment, error) { return { type: T.CREATE_FRAGMENT_FAILURE, isFetching: false, - fragment: fragment, - error: error, + fragment, + error, } } @@ -94,14 +94,14 @@ export function createFragment(collection, fragment) { function getFragmentRequest(fragment) { return { type: T.GET_FRAGMENT_REQUEST, - fragment: fragment, + fragment, } } function getFragmentSuccess(fragment) { return { type: T.GET_FRAGMENT_SUCCESS, - fragment: fragment, + fragment, receivedAt: Date.now(), } } @@ -110,8 +110,8 @@ function getFragmentFailure(fragment, error) { return { type: T.GET_FRAGMENT_FAILURE, isFetching: false, - fragment: fragment, - error: error, + fragment, + error, } } @@ -133,15 +133,15 @@ export function getFragment(collection, fragment) { function updateFragmentRequest(fragment) { return { type: T.UPDATE_FRAGMENT_REQUEST, - fragment: fragment, + fragment, } } function updateFragmentSuccess(fragment, update) { return { type: T.UPDATE_FRAGMENT_SUCCESS, - fragment: fragment, - update: update, + fragment, + update, receivedAt: Date.now(), } } @@ -150,8 +150,8 @@ function updateFragmentFailure(fragment, error) { return { type: T.UPDATE_FRAGMENT_FAILURE, isFetching: false, - fragment: fragment, - error: error, + fragment, + error, } } @@ -173,7 +173,7 @@ export function updateFragment(collection, fragment) { function deleteFragmentRequest(fragment) { return { type: T.DELETE_FRAGMENT_REQUEST, - fragment: fragment, + fragment, update: { deleted: true }, } } @@ -181,17 +181,17 @@ function deleteFragmentRequest(fragment) { function deleteFragmentSuccess(collection, fragment) { return { type: T.DELETE_FRAGMENT_SUCCESS, - collection: collection, - fragment: fragment, + collection, + fragment, } } function deleteFragmentFailure(fragment, error) { return { type: T.DELETE_FRAGMENT_FAILURE, - fragment: fragment, + fragment, update: { deleted: undefined }, - error: error, + error, } } diff --git a/packages/client/src/actions/teams.js b/packages/client/src/actions/teams.js index 741bf172a26fd230d0ed6614d823889fc6dac988..c34f637566d693c6fd3c45cff4eeee09573b0559 100644 --- a/packages/client/src/actions/teams.js +++ b/packages/client/src/actions/teams.js @@ -20,7 +20,7 @@ function getTeamsSuccess(teams) { return { type: T.GET_TEAMS_SUCCESS, isFetching: false, - teams: teams, + teams, } } @@ -48,14 +48,14 @@ export function getTeams() { function createTeamRequest(team) { return { type: T.CREATE_TEAM_REQUEST, - team: team, + team, } } function createTeamSuccess(team) { return { type: T.CREATE_TEAM_SUCCESS, - team: team, + team, } } @@ -63,8 +63,8 @@ function createTeamFailure(team, error) { return { type: T.CREATE_TEAM_FAILURE, isFetching: false, - team: team, - error: error, + team, + error, } } @@ -86,14 +86,14 @@ export function createTeam(team) { function updateTeamRequest(team) { return { type: T.UPDATE_TEAM_REQUEST, - team: team, + team, } } function updateTeamSuccess(team) { return { type: T.UPDATE_TEAM_SUCCESS, - team: team, + team, } } @@ -101,8 +101,8 @@ function updateTeamFailure(team, error) { return { type: T.UPDATE_TEAM_FAILURE, isFetching: false, - team: team, - error: error, + team, + error, } } @@ -123,14 +123,14 @@ export function updateTeam(team) { function deleteTeamRequest(team) { return { type: T.DELETE_TEAM_REQUEST, - team: team, + team, } } function deleteTeamSuccess(team) { return { type: T.DELETE_TEAM_SUCCESS, - team: team, + team, } } @@ -138,8 +138,8 @@ function deleteTeamFailure(team, error) { return { type: T.DELETE_TEAM_FAILURE, isFetching: false, - team: team, - error: error, + team, + error, } } diff --git a/packages/client/src/actions/users.js b/packages/client/src/actions/users.js index 1c76c0429949f5649ffd55ba68e44fab19f8ad32..d32d3be55df27c13fc45e4a0d1631d623fcd91ab 100644 --- a/packages/client/src/actions/users.js +++ b/packages/client/src/actions/users.js @@ -12,7 +12,7 @@ function getUsersSuccess(users) { return { type: T.GET_USERS_SUCCESS, isFetching: false, - users: users, + users, } } @@ -64,7 +64,7 @@ export function getUser(user) { dispatch(getUserRequest(user)) return api - .get('/users/' + user.id) + .get(`/users/${user.id}`) .then( user => dispatch(getUserSuccess(user)), err => dispatch(getUserFailure(err)), @@ -75,7 +75,7 @@ export function getUser(user) { function updateUserRequest(user) { return { type: T.UPDATE_USER_REQUEST, - user: user, + user, isFetching: true, } } @@ -84,7 +84,7 @@ function updateUserSuccess(users) { return { type: T.UPDATE_USER_SUCCESS, isFetching: false, - users: users, + users, } } @@ -100,7 +100,7 @@ export function updateUser(user) { return dispatch => { dispatch(updateUserRequest(user)) - const url = '/users/' + user.id + const url = `/users/${user.id}` return api .update(url, user) diff --git a/packages/client/src/components/reducers.js b/packages/client/src/components/reducers.js index e43c49009eb5d9aa1e3ee8dc42c10483607290a2..82d40325057fa828ff2cc43c6ef2d77d93e0a923 100644 --- a/packages/client/src/components/reducers.js +++ b/packages/client/src/components/reducers.js @@ -1,5 +1,6 @@ // const components = require('./components') const isPlainObject = require('lodash/isPlainObject') + const components = PUBSWEET_COMPONENTS module.exports = components diff --git a/packages/client/src/helpers/api.js b/packages/client/src/helpers/api.js index b3e29c4b2cfd7d2576eeb760737ed9c59a533556..d28db43a9ecd34dd4cfe6d9e9a8d72d7917bdbc5 100644 --- a/packages/client/src/helpers/api.js +++ b/packages/client/src/helpers/api.js @@ -14,12 +14,12 @@ const parse = response => { const request = (url, options = {}) => { options.headers = options.headers || {} - options.headers['Accept'] = options.headers['Accept'] || 'application/json' + options.headers.Accept = options.headers.Accept || 'application/json' const token = getToken() if (token) { - options.headers['Authorization'] = 'Bearer ' + token + options.headers.Authorization = `Bearer ${token}` } if (!url.match(/^https?:/)) { diff --git a/packages/client/src/helpers/withAuthsome.js b/packages/client/src/helpers/withAuthsome.js index e78bac5fa9dcdce2b312f6e4079b7fd3efb8e95c..278775d8253cfdb5e590a13e277a77605b60731d 100644 --- a/packages/client/src/helpers/withAuthsome.js +++ b/packages/client/src/helpers/withAuthsome.js @@ -23,9 +23,7 @@ export default function withAuthsome() { find: id => state.teams.find(team => team.id === id), }, User: { - find: id => { - return state.users.users.find(user => user.id === id) - }, + find: id => state.users.users.find(user => user.id === id), }, }, } diff --git a/packages/client/test/actions/fileUpload.test.js b/packages/client/test/actions/fileUpload.test.js index 33b3eaaba8a0dea5d53250cc8ff603fdf6d06828..1f0673403a6daa760d82026b6cdac628895353db 100644 --- a/packages/client/test/actions/fileUpload.test.js +++ b/packages/client/test/actions/fileUpload.test.js @@ -1,6 +1,7 @@ global.PUBSWEET_COMPONENTS = [] require('isomorphic-form-data') + global.FormData.prototype.oldAppend = global.FormData.prototype.append global.FormData.prototype.append = function(field, value, options) { // File upload testing hack diff --git a/packages/client/test/helpers/describeAction.js b/packages/client/test/helpers/describeAction.js index 4d6222d18f3291de6eb0788ad7b2f0e9650e12c3..80dfed69ad6025d9d3eabcfbe24845de84c3808e 100644 --- a/packages/client/test/helpers/describeAction.js +++ b/packages/client/test/helpers/describeAction.js @@ -1,11 +1,9 @@ const allactions = require('../../src/actions').default const api = require('../../src/helpers/api') -const mockGetState = () => { - return { - currentUser: {}, - } -} +const mockGetState = () => ({ + currentUser: {}, +}) function mockApi(succeed = true) { const implementation = () => @@ -23,9 +21,9 @@ expect.extend({ const actualKeys = Object.keys(object) const pass = expectedKeys.every(key => actualKeys.includes(key)) return { - message: `Expected object ${pass - ? 'not to' - : 'to'} have properties: ${expectedKeys.join(', ')}`, + message: `Expected object ${ + pass ? 'not to' : 'to' + } have properties: ${expectedKeys.join(', ')}`, pass, } }, diff --git a/packages/client/test/helpers/withAuthsome.test.js b/packages/client/test/helpers/withAuthsome.test.js index ade5c259180f166f4fa66a5aff620a1b94607f3f..4f34c0a319673d213e4e92dbbe56f41da3818624 100644 --- a/packages/client/test/helpers/withAuthsome.test.js +++ b/packages/client/test/helpers/withAuthsome.test.js @@ -4,7 +4,8 @@ import configureStore from 'redux-mock-store' import Authsome from 'authsome' import withAuthsome from '../../src/helpers/withAuthsome' -jest.mock('fake-mode', () => false, {virtual: true}) + +jest.mock('fake-mode', () => false, { virtual: true }) describe('withAuthsome higher order component', () => { const createStore = configureStore() diff --git a/packages/client/test/reducers/users.test.js b/packages/client/test/reducers/users.test.js index 33e2c1726caff14ba7617da63cfa53d533b13ed6..5fbdac4567ab09fbef48d0b758ae2383b3f00b77 100644 --- a/packages/client/test/reducers/users.test.js +++ b/packages/client/test/reducers/users.test.js @@ -47,7 +47,7 @@ describe('users reducers', () => { { users: [] }, { type: T.GET_USER_SUCCESS, - user: user, + user, }, ) expect(actual).toEqual({ diff --git a/packages/client/test/store/configureStore.test.js b/packages/client/test/store/configureStore.test.js index ccf70ba2066a29503c48e3123dd59096de266650..c3c06020c52bcf539a4944ed3c7942f4bcec08e6 100644 --- a/packages/client/test/store/configureStore.test.js +++ b/packages/client/test/store/configureStore.test.js @@ -10,12 +10,10 @@ describe('configureStore', () => { const customInitialState = { collections: ['initial'] } const customReducers = { - test: (state, action) => { - return { - ...state, - fired: action.fired, - } - }, + test: (state, action) => ({ + ...state, + fired: action.fired, + }), } const customMiddlewares = [ diff --git a/packages/components/config/test.js b/packages/components/config/test.js index ae2c6d22b7b354573e11a376468f1a8d9518e0e8..e70c92cadf2342d99b4c7ef764601cb34fe4932f 100644 --- a/packages/components/config/test.js +++ b/packages/components/config/test.js @@ -1,5 +1,5 @@ module.exports = { - 'authsome': { - 'mode': 'pubsweet-server/test/helpers/authsome_mode' - } + authsome: { + mode: 'pubsweet-server/test/helpers/authsome_mode', + }, } diff --git a/packages/components/packages/Blog/BlogContainer.js b/packages/components/packages/Blog/BlogContainer.js index db01f73a6c8c0142f96c30626cc28a91e3544c26..6f2c91ab8a10780d399124360a884716414b6453 100644 --- a/packages/components/packages/Blog/BlogContainer.js +++ b/packages/components/packages/Blog/BlogContainer.js @@ -4,24 +4,23 @@ import Actions from 'pubsweet-client/src/actions' import Blog from './Blog' -function mapStateToProps (state) { - let blog = state && state.collections[0] - let posts = blog ? blog.fragments.map(f => state.fragments[f]).filter(f => f) : [] +function mapStateToProps(state) { + const blog = state && state.collections[0] + const posts = blog + ? blog.fragments.map(f => state.fragments[f]).filter(f => f) + : [] return { blog: state && state.collections[0], - posts: posts + posts, // errorMessage: state.errorMessage } } -function mapDispatchToProps (dispatch) { +function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators(Actions, dispatch) + actions: bindActionCreators(Actions, dispatch), } } -export default connect( - mapStateToProps, - mapDispatchToProps -)(Blog) +export default connect(mapStateToProps, mapDispatchToProps)(Blog) diff --git a/packages/components/packages/Blog/BlogContainer.test.js b/packages/components/packages/Blog/BlogContainer.test.js index e5c3918de3d9163251ffe5a2979b7ed424784101..4ec843367f6440ab8bcf0f3709cf19ee4c929111 100644 --- a/packages/components/packages/Blog/BlogContainer.test.js +++ b/packages/components/packages/Blog/BlogContainer.test.js @@ -1,16 +1,16 @@ -import {shallow} from 'enzyme' +import { shallow } from 'enzyme' import React from 'react' import configureStore from 'redux-mock-store' import thunk from 'redux-thunk' jest.mock('config', () => ({ 'pubsweet-client': { - API_ENDPOINT: '' - } + API_ENDPOINT: '', + }, })) global.PUBSWEET_COMPONENTS = [] global.window.localStorage = { - getItem: jest.fn(() => 'tok123') + getItem: jest.fn(() => 'tok123'), } const BlogContainer = require('./BlogContainer').default @@ -18,25 +18,25 @@ const BlogContainer = require('./BlogContainer').default describe('<BlogContainer/>', () => { const createMockStore = configureStore([thunk]) - const makeWrapper = (props = {}) => shallow(<BlogContainer {...props}/>) + const makeWrapper = (props = {}) => shallow(<BlogContainer {...props} />) it('allows empty collections', () => { const store = createMockStore({ - collections: [] + collections: [], }) - makeWrapper({store}) + makeWrapper({ store }) }) it('selects fragments as posts', () => { - let blog = {title: 'hello', fragments: [0, 2]} + const blog = { title: 'hello', fragments: [0, 2] } const store = createMockStore({ collections: [blog], - fragments: ['f1', 'f2', 'f3'] + fragments: ['f1', 'f2', 'f3'], }) - const wrapper = makeWrapper({store}) + const wrapper = makeWrapper({ store }) expect(wrapper.props()).toMatchObject({ blog, - posts: ['f1', 'f3'] + posts: ['f1', 'f3'], }) }) }) diff --git a/packages/components/packages/Blog/index.js b/packages/components/packages/Blog/index.js index 2da4efc93f716514d2bcbd2f0dc3bc9f9264d26d..aa36ba66807398b2cd18869309aa8992ec136661 100644 --- a/packages/components/packages/Blog/index.js +++ b/packages/components/packages/Blog/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./BlogContainer') - ] - } + components: [() => require('./BlogContainer')], + }, } diff --git a/packages/components/packages/Draft.js/DraftContainer.js b/packages/components/packages/Draft.js/DraftContainer.js index 98e379b642a6ab0b22a39b61852d9c3d7bf1833e..fe5bf051f613b26c5e132a56c7476e6a22c63f7b 100644 --- a/packages/components/packages/Draft.js/DraftContainer.js +++ b/packages/components/packages/Draft.js/DraftContainer.js @@ -4,21 +4,18 @@ import Actions from 'pubsweet-client/src/actions' import Draft from './Draft' -function mapStateToProps (state, props) { +function mapStateToProps(state, props) { return { blog: state.collections[0], id: props.match.params.id, - fragment: state.fragments[props.match.params.id] + fragment: state.fragments[props.match.params.id], } } -function mapDispatchToProps (dispatch) { +function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators(Actions, dispatch) + actions: bindActionCreators(Actions, dispatch), } } -export default connect( - mapStateToProps, - mapDispatchToProps -)(Draft) +export default connect(mapStateToProps, mapDispatchToProps)(Draft) diff --git a/packages/components/packages/Draft.js/index.js b/packages/components/packages/Draft.js/index.js index e238de76098c5639b97f655953c60a660e00ad50..bb40a8d30c742f252d1465fec70fbe181f569b19 100644 --- a/packages/components/packages/Draft.js/index.js +++ b/packages/components/packages/Draft.js/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./DraftContainer') - ] - } + components: [() => require('./DraftContainer')], + }, } diff --git a/packages/components/packages/Epub/EpubBackend.js b/packages/components/packages/Epub/EpubBackend.js index 94821286ea65c4950f98dabb868509e35037e826..628823e04acbbdd88b2999a1c1126f070cf8aaa7 100644 --- a/packages/components/packages/Epub/EpubBackend.js +++ b/packages/components/packages/Epub/EpubBackend.js @@ -7,8 +7,8 @@ const processFragment = require('./process') const output = require('./output') const config = require('config') -const EpubBackend = function (app) { - app.use('/api/collections/:id/epub', async function (req, res, next) { +const EpubBackend = function(app) { + app.use('/api/collections/:id/epub', async (req, res, next) => { try { const Collection = app.locals.models.Collection @@ -19,7 +19,7 @@ const EpubBackend = function (app) { const book = { title: collection.title, - identifier: collection.id + identifier: collection.id, } // chapters @@ -29,16 +29,17 @@ const EpubBackend = function (app) { // TODO: change it from array to the name of the selected theme let styles = [req.query.style].filter(name => name) // TODO: to be desided where the per applications themes should live - let stylesRoot = process.cwd() + '/static' + let stylesRoot = `${process.cwd()}/static` if (styles.length === 0 || !fs.existsSync(`${stylesRoot}/${styles[0]}`)) { styles = ['default.css'] stylesRoot = `${__dirname}/themes` } - let fontsRoot = config.epub && config.epub.fontsPath - ? process.cwd() + config.epub.fontsPath - : null + let fontsRoot = + config.epub && config.epub.fontsPath + ? process.cwd() + config.epub.fontsPath + : null if (!fs.existsSync(fontsRoot)) fontsRoot = '' @@ -47,14 +48,14 @@ const EpubBackend = function (app) { .filter(name => name && converters[name]) .map(name => converters[name]) - const parts = fragments.sort(sorter).map( - processFragment({ styles, activeConverters, book }) - ) + const parts = fragments + .sort(sorter) + .map(processFragment({ styles, activeConverters, book })) // TODO: read the path to the uploads folder from config - const resourceRoot = process.cwd() + '/uploads' + const resourceRoot = `${process.cwd()}/uploads` - const epub = new HTMLEPUB(book, {resourceRoot, stylesRoot, fontsRoot}) + const epub = new HTMLEPUB(book, { resourceRoot, stylesRoot, fontsRoot }) await epub.load(parts) diff --git a/packages/components/packages/Epub/EpubBackend.test.js b/packages/components/packages/Epub/EpubBackend.test.js index f126bc876b729ae87cd9781a081e991170e5ed1f..bdd6e43372888194d64fbb147df7f112e036d193 100644 --- a/packages/components/packages/Epub/EpubBackend.test.js +++ b/packages/components/packages/Epub/EpubBackend.test.js @@ -3,13 +3,18 @@ const supertest = require('supertest') const component = require('.') -function makeApp (response) { +function makeApp(response) { const app = express() // mock DB app.locals.models = { Collection: { - find: jest.fn(() => response instanceof Error ? Promise.reject(response) : Promise.resolve(response)) - } + find: jest.fn( + () => + response instanceof Error + ? Promise.reject(response) + : Promise.resolve(response), + ), + }, } // register component component.backend()(app) @@ -22,18 +27,21 @@ describe('/collections/*/epub route', () => { const collection = { id: 'col1', title: 'Test thing', - getFragments: () => [{title: 'One thing', source: 'In depth'}] + getFragments: () => [{ title: 'One thing', source: 'In depth' }], } return makeApp(collection) - .get('/api/collections/234/epub') - .expect(200) - .expect('Content-disposition', 'attachment; filename="collection-234.epub"') + .get('/api/collections/234/epub') + .expect(200) + .expect( + 'Content-disposition', + 'attachment; filename="collection-234.epub"', + ) }) it('errors if DB call fails', () => { const error = new Error('Ops!') return makeApp(error) - .get('/api/collections/234/epub') - .expect(500) + .get('/api/collections/234/epub') + .expect(500) }) }) diff --git a/packages/components/packages/Epub/converters/index.js b/packages/components/packages/Epub/converters/index.js index ef6d323ac9af3d484133d94875efe9935b6b6f8a..bb3f3cbe15ed5f670fedf1c289f89ab2df5e31cb 100644 --- a/packages/components/packages/Epub/converters/index.js +++ b/packages/components/packages/Epub/converters/index.js @@ -1,3 +1,3 @@ module.exports = { - wax: require('./wax') + wax: require('./wax'), } diff --git a/packages/components/packages/Epub/converters/wax.js b/packages/components/packages/Epub/converters/wax.js index 4d9caca736907a175d75f764b1687f4c0704b47c..cc42393fe88ae2b7b08bc456a36feb2f6a7b7a93 100644 --- a/packages/components/packages/Epub/converters/wax.js +++ b/packages/components/packages/Epub/converters/wax.js @@ -1,7 +1,14 @@ -module.exports = ($, fragmentTitle, bookTitle, fragmentDivision, fragmentSubcategory, fragmentNumber) => { +module.exports = ( + $, + fragmentTitle, + bookTitle, + fragmentDivision, + fragmentSubcategory, + fragmentNumber, +) => { const body = $('body') - let outerContainer = $('<div/>').attr('class', fragmentDivision) + const outerContainer = $('<div/>').attr('class', fragmentDivision) let innerContainer if (fragmentDivision === 'front') { @@ -12,22 +19,43 @@ module.exports = ($, fragmentTitle, bookTitle, fragmentDivision, fragmentSubcate innerContainer = $('<section/>').attr('data-type', fragmentSubcategory) } - $('<p/>').attr('class', 'ch-start').html('beginning').appendTo(innerContainer) - $('<div/>').attr('class', 'folio').appendTo(innerContainer) - $('<div/>').attr('class', 'booktitle').html(bookTitle).appendTo(innerContainer) - $('<div/>').attr('class', 'dup').html(fragmentTitle).appendTo(innerContainer) + $('<p/>') + .attr('class', 'ch-start') + .html('beginning') + .appendTo(innerContainer) + $('<div/>') + .attr('class', 'folio') + .appendTo(innerContainer) + $('<div/>') + .attr('class', 'booktitle') + .html(bookTitle) + .appendTo(innerContainer) + $('<div/>') + .attr('class', 'dup') + .html(fragmentTitle) + .appendTo(innerContainer) if (fragmentSubcategory === 'part') { - $('<p/>').attr('class', 'part-number').html(fragmentNumber).appendTo(innerContainer) + $('<p/>') + .attr('class', 'part-number') + .html(fragmentNumber) + .appendTo(innerContainer) } else if (fragmentSubcategory === 'chapter') { - $('<p/>').attr('class', 'chapter-number').html(fragmentNumber).appendTo(innerContainer) + $('<p/>') + .attr('class', 'chapter-number') + .html(fragmentNumber) + .appendTo(innerContainer) } - $('<h1/>').attr('class', 'ct').html(fragmentTitle).appendTo(innerContainer) + $('<h1/>') + .attr('class', 'ct') + .html(fragmentTitle) + .appendTo(innerContainer) const replaceWithBlockquote = className => (i, elem) => { const $elem = $(elem) - const blockquote = $(`<blockquote class="${className}"/>`) - .append($elem.contents()) + const blockquote = $(`<blockquote class="${className}"/>`).append( + $elem.contents(), + ) $elem.replaceWith(blockquote) } @@ -60,8 +88,8 @@ module.exports = ($, fragmentTitle, bookTitle, fragmentDivision, fragmentSubcate // add namespaces $('html').attr({ - 'xmlns': 'http://www.w3.org/1999/xhtml', - 'xmlns:epub': 'http://www.idpf.org/2007/ops' + xmlns: 'http://www.w3.org/1999/xhtml', + 'xmlns:epub': 'http://www.idpf.org/2007/ops', }) // replace custom HTML elements @@ -120,7 +148,7 @@ module.exports = ($, fragmentTitle, bookTitle, fragmentDivision, fragmentSubcate $elem.replaceWith(callout) }) - let bodyContent = body.contents() + const bodyContent = body.contents() innerContainer.append(bodyContent) outerContainer.append(innerContainer) body.replaceWith(outerContainer) diff --git a/packages/components/packages/Epub/index.js b/packages/components/packages/Epub/index.js index 1c5a2cba4ecbd2ec11a7c67989cf07d4c0670698..d5e6a6d1bac343d723a4498093c2870750384d5b 100644 --- a/packages/components/packages/Epub/index.js +++ b/packages/components/packages/Epub/index.js @@ -1,3 +1,3 @@ module.exports = { - backend: () => app => require('./EpubBackend')(app) + backend: () => app => require('./EpubBackend')(app), } diff --git a/packages/components/packages/Epub/output.js b/packages/components/packages/Epub/output.js index e7f0438bc1180a16523218d0f58aeca96c550a16..0ea72de6748c805ead8bfb1f2eee5289461e917d 100644 --- a/packages/components/packages/Epub/output.js +++ b/packages/components/packages/Epub/output.js @@ -12,21 +12,21 @@ const attachment = async (epub, res, id) => { // TODO: this might not work if the attachment header is already sent archive.on('error', err => { - res.status(500).json({error: err.message}) + res.status(500).json({ error: err.message }) }) archive.on('end', () => { logger.info('Wrote %d bytes', archive.pointer()) }) } catch (error) { - res.status(500).json({error: error.message}) + res.status(500).json({ error: error.message }) } } const folder = async (epub, res) => { // TODO: read the path to the uploads folder from config - const folder = 'epub/' + crypto.randomBytes(32).toString('hex') - const path = process.cwd() + '/uploads/' + folder + const folder = `epub/${crypto.randomBytes(32).toString('hex')}` + const path = `${process.cwd()}/uploads/${folder}` if (fs.existsSync(path)) { throw new Error('Output path already exists') @@ -35,23 +35,23 @@ const folder = async (epub, res) => { mkdirp.sync(path) try { - const archive = await epub.stream(unzipper.Extract({path})) + const archive = await epub.stream(unzipper.Extract({ path })) archive.on('error', err => { - res.status(500).json({error: err.message}) + res.status(500).json({ error: err.message }) }) archive.on('end', () => { logger.info('Wrote %d bytes', archive.pointer()) - res.json({path: folder}) + res.json({ path: folder }) }) } catch (error) { - res.status(500).json({error: error.message}) + res.status(500).json({ error: error.message }) } } module.exports = { attachment, - folder + folder, } diff --git a/packages/components/packages/Epub/process.js b/packages/components/packages/Epub/process.js index c9174bb78beed21e147096616c74dfa4e0df3d5f..7a2da7226ecdd4b2d9fa3236395465608272c417 100644 --- a/packages/components/packages/Epub/process.js +++ b/packages/components/packages/Epub/process.js @@ -10,14 +10,25 @@ module.exports = ({ styles, activeConverters, book }) => fragment => { ? fragment.number : -1 - activeConverters.forEach(converter => converter($, fragmentTitle, bookTitle, fragmentDivision, fragmentSubcategory, fragmentNumber)) + activeConverters.forEach(converter => + converter( + $, + fragmentTitle, + bookTitle, + fragmentDivision, + fragmentSubcategory, + fragmentNumber, + ), + ) styles.forEach(uri => { - $('<link rel="stylesheet"/>').attr('href', uri).appendTo('head') + $('<link rel="stylesheet"/>') + .attr('href', uri) + .appendTo('head') }) return { title: fragment.title, - content: $.html() + content: $.html(), } } diff --git a/packages/components/packages/Epub/process.test.js b/packages/components/packages/Epub/process.test.js index 368d36e5e6bf571f4b62cae88aa24b0234f15b78..620053bf4ea86b0b59fb069cd8f41dbbcb89f35d 100644 --- a/packages/components/packages/Epub/process.test.js +++ b/packages/components/packages/Epub/process.test.js @@ -14,18 +14,23 @@ test('converts source to html', () => { <p>Test</p> </div> `, - id: '1' + id: '1', } const book = { title: 'Test Book', - identifier: '65ac5abe353ef4d32f1ce55abfe665185d58d811883b1715032b8ed70a8cc1e1' + identifier: + '65ac5abe353ef4d32f1ce55abfe665185d58d811883b1715032b8ed70a8cc1e1', } const styles = ['test.css'] - const activeConverters = [converters['wax']] + const activeConverters = [converters.wax] - const { title, content } = processFragment({styles, activeConverters, book})(fragment) + const { title, content } = processFragment({ + styles, + activeConverters, + book, + })(fragment) expect(title).toBe('A Test') @@ -33,12 +38,9 @@ test('converts source to html', () => { const doc = $('html') - expect(doc.attr('xmlns')) - .toBe('http://www.w3.org/1999/xhtml') + expect(doc.attr('xmlns')).toBe('http://www.w3.org/1999/xhtml') - expect(doc.attr('xmlns:epub')) - .toBe('http://www.idpf.org/2007/ops') + expect(doc.attr('xmlns:epub')).toBe('http://www.idpf.org/2007/ops') - expect($('link[rel=stylesheet]').attr('href')) - .toBe('test.css') + expect($('link[rel=stylesheet]').attr('href')).toBe('test.css') }) diff --git a/packages/components/packages/Epub/sorter.js b/packages/components/packages/Epub/sorter.js index 7928de012ed45dea4d45a803f7d5640beaf47dd4..9eed178593b00c0927a8bbd321565358baff1459 100644 --- a/packages/components/packages/Epub/sorter.js +++ b/packages/components/packages/Epub/sorter.js @@ -1,7 +1,7 @@ const divisions = { front: -1, body: 0, - back: 1 + back: 1, } module.exports = (a, b) => { diff --git a/packages/components/packages/Epub/sorter.test.js b/packages/components/packages/Epub/sorter.test.js index b9a5abac8c95b35cf7b3e1fddb9173c2df99d1da..e2806c8cd6f6c00b57ae1a7894d376444a53bb0f 100644 --- a/packages/components/packages/Epub/sorter.test.js +++ b/packages/components/packages/Epub/sorter.test.js @@ -5,32 +5,32 @@ test('sorts chapters', () => { const chapters = [ { division: 'front', - index: 0 + index: 0, }, { division: 'front', - index: 1 + index: 1, }, { division: 'body', - index: 0 + index: 0, }, { division: 'body', - index: 1 + index: 1, }, { division: 'body', - index: 2 + index: 2, }, { division: 'back', - index: 0 + index: 0, }, { division: 'back', - index: 1 - } + index: 1, + }, ] const shuffledChapters = shuffle(chapters) diff --git a/packages/components/packages/EpubFrontend/actions.js b/packages/components/packages/EpubFrontend/actions.js index 657b985860b0b5a1d3dab0e658845a92494cb4c4..6639dca8a5f1b2167997f95a4b338f6a0ead2eb0 100644 --- a/packages/components/packages/EpubFrontend/actions.js +++ b/packages/components/packages/EpubFrontend/actions.js @@ -2,34 +2,34 @@ import fetch from 'isomorphic-fetch' import { stringify } from 'querystring' import * as T from './types' -function htmlToEpubRequest () { +function htmlToEpubRequest() { return { - type: T.HTML_TO_EPUB_CONV_REQUEST + type: T.HTML_TO_EPUB_CONV_REQUEST, } } -function htmlToEpubSuccess (extractedEpubPath) { +function htmlToEpubSuccess(extractedEpubPath) { return { type: T.HTML_TO_EPUB_CONV_SUCCESS, - extractedEpubPath + extractedEpubPath, } } -function htmlToEpubFailure (message) { +function htmlToEpubFailure(message) { return { type: T.HTML_TO_EPUB_CONV_FAILURE, - error: message + error: message, } } // TODO: use `api` from pubsweet-client instead of `fetch`, for authentication -export function htmlToEpub (bookId, options) { +export function htmlToEpub(bookId, options) { const params = stringify(options) - return function (dispatch) { + return function(dispatch) { dispatch(htmlToEpubRequest()) - return fetch(`/api/collections/${bookId}/epub?` + params) + return fetch(`/api/collections/${bookId}/epub?${params}`) .then(response => { if (!response.ok) { throw new Error(response.statusText) diff --git a/packages/components/packages/EpubFrontend/index.js b/packages/components/packages/EpubFrontend/index.js index 3c808245dd930521d4b59db5e9f67dff99f4f8fb..d2d6dbe5c70e44d35ba609e664bbaaa21d49a977 100644 --- a/packages/components/packages/EpubFrontend/index.js +++ b/packages/components/packages/EpubFrontend/index.js @@ -1,6 +1,6 @@ module.exports = { frontend: { actions: () => require('./actions'), - reducers: () => require('./reducers') - } + reducers: () => require('./reducers'), + }, } diff --git a/packages/components/packages/EpubFrontend/reducers.js b/packages/components/packages/EpubFrontend/reducers.js index 84f11c603fa90cac11e8f5139ef9c9775972ca1d..a0ea9879f430562c0e9a118f1a17239a9c71aeda 100644 --- a/packages/components/packages/EpubFrontend/reducers.js +++ b/packages/components/packages/EpubFrontend/reducers.js @@ -1,30 +1,30 @@ import { HTML_TO_EPUB_CONV_REQUEST, HTML_TO_EPUB_CONV_SUCCESS, - HTML_TO_EPUB_CONV_FAILURE + HTML_TO_EPUB_CONV_FAILURE, } from './types' -export default function htmlToEpub ( +export default function htmlToEpub( state = { isFetching: false, - extractedEpubPath: '' + extractedEpubPath: '', }, - action + action, ) { switch (action.type) { case HTML_TO_EPUB_CONV_REQUEST: return Object.assign({}, state, { - isFetching: true + isFetching: true, }) case HTML_TO_EPUB_CONV_SUCCESS: return Object.assign({}, state, { isFetching: false, - extractedEpubPath: action.extractedEpubPath + extractedEpubPath: action.extractedEpubPath, }) case HTML_TO_EPUB_CONV_FAILURE: return Object.assign({}, state, { isFetching: false, - error: action.error + error: action.error, }) default: return state diff --git a/packages/components/packages/FormGroup/index.js b/packages/components/packages/FormGroup/index.js index 900ef1ec106af4d9f3a0994ca337e4965dbbc107..40282d99663c419e2ca62e038f520bba77335dba 100644 --- a/packages/components/packages/FormGroup/index.js +++ b/packages/components/packages/FormGroup/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - require('./FormGroup') - ] - } + components: [require('./FormGroup')], + }, } diff --git a/packages/components/packages/HTML/HTMLContainer.js b/packages/components/packages/HTML/HTMLContainer.js index c45d2b389306984943ed86160de1cc725a254781..39c9549f078c78caaeb7bc87279d9af94b5fb405 100644 --- a/packages/components/packages/HTML/HTMLContainer.js +++ b/packages/components/packages/HTML/HTMLContainer.js @@ -4,20 +4,17 @@ import Actions from 'pubsweet-client/src/actions' import HTML from './HTML' -function mapStateToProps (state, props) { +function mapStateToProps(state, props) { return { id: props.match.params.id, - fragment: state.fragments[props.match.params.id] + fragment: state.fragments[props.match.params.id], } } -function mapDispatchToProps (dispatch) { +function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators(Actions, dispatch) + actions: bindActionCreators(Actions, dispatch), } } -export default connect( - mapStateToProps, - mapDispatchToProps -)(HTML) +export default connect(mapStateToProps, mapDispatchToProps)(HTML) diff --git a/packages/components/packages/HTML/index.js b/packages/components/packages/HTML/index.js index a2e60b891b8428280e4bb70f26613952ba8fcb74..d7039840b0853baa4a03a9130881e4a41a7f6f96 100644 --- a/packages/components/packages/HTML/index.js +++ b/packages/components/packages/HTML/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./HTMLContainer') - ] - } + components: [() => require('./HTMLContainer')], + }, } diff --git a/packages/components/packages/InkBackend/InkBackend.js b/packages/components/packages/InkBackend/InkBackend.js index af30d3c8319ab28fe7155a440edb8a7729801a14..388f526d8e326221d19f15a475e852d43d59c7c5 100644 --- a/packages/components/packages/InkBackend/InkBackend.js +++ b/packages/components/packages/InkBackend/InkBackend.js @@ -11,61 +11,66 @@ const temp = require('temp') const inkConfig = config.get('pubsweet-component-ink-backend') // Generate the absolute URL -const inkUrl = path => inkConfig.inkEndpoint + 'api/' + path +const inkUrl = path => `${inkConfig.inkEndpoint}api/${path}` // Sign in -const authorize = () => rp({ - method: 'POST', - uri: inkUrl('auth/sign_in'), - formData: { - email: inkConfig.email, - password: inkConfig.password - }, - headers: { - 'Accept': 'application/vnd.ink.1' - }, - resolveWithFullResponse: true -}).then(res => ({ - 'client': res.headers['client'], - 'access-token': res.headers['access-token'] -})) +const authorize = () => + rp({ + method: 'POST', + uri: inkUrl('auth/sign_in'), + formData: { + email: inkConfig.email, + password: inkConfig.password, + }, + headers: { + Accept: 'application/vnd.ink.1', + }, + resolveWithFullResponse: true, + }).then(res => ({ + client: res.headers.client, + 'access-token': res.headers['access-token'], + })) // Upload file to INK and execute the recipe -const upload = (recipeId, inputFile, auth) => rp({ - method: 'POST', - uri: inkUrl('recipes/' + recipeId + '/execute'), - headers: { - uid: inkConfig.email, - ...auth - }, - formData: { - input_files: [inputFile] - }, - json: true, - timeout: 60 * 60 * 1000 // 3600 seconds -}) +const upload = (recipeId, inputFile, auth) => + rp({ + method: 'POST', + uri: inkUrl(`recipes/${recipeId}/execute`), + headers: { + uid: inkConfig.email, + ...auth, + }, + formData: { + input_files: [inputFile], + }, + json: true, + timeout: 60 * 60 * 1000, // 3600 seconds + }) // Download the output file (keep trying if there's a 404 response, until it's ready) const download = async (chain, auth, outputFileName) => { const manifest = chain.input_file_manifest if (manifest.length === 0) { - throw new Error('The INK server gave a malformed response (no input files in the process chain)') + throw new Error( + 'The INK server gave a malformed response (no input files in the process chain)', + ) } const interval = inkConfig.interval || 1000 // try once per second const maxRetries = inkConfig.maxRetries || 300 // retry for up to 5 minutes - const uri = inkUrl('process_chains/' + chain.id + '/download_output_file') + const uri = inkUrl(`process_chains/${chain.id}/download_output_file`) const qs = { - relative_path: outputFileName || path.basename(manifest[0].path, '.docx') + '.html' + relative_path: + outputFileName || `${path.basename(manifest[0].path, '.docx')}.html`, } const headers = { uid: inkConfig.email, - ...auth + ...auth, } for (let i = 0; i < maxRetries; i++) { @@ -77,7 +82,7 @@ const download = async (chain, auth, outputFileName) => { qs, headers, simple: false, - resolveWithFullResponse: true + resolveWithFullResponse: true, }).catch(error => { logger.error('Error downloading from INK:', error.message) throw error @@ -97,19 +102,20 @@ const download = async (chain, auth, outputFileName) => { throw new Error('Unable to download the output from INK') } -const findRecipeId = (recipeKey = 'Editoria Typescript', auth) => rp({ - method: 'GET', - uri: inkUrl('recipes'), - headers: { - uid: inkConfig.email, - ...auth - }, - json: true -}).then(data => { - const recipe = data.recipes.find(recipe => recipe.name === recipeKey) - - return recipe ? recipe.id : null -}) +const findRecipeId = (recipeKey = 'Editoria Typescript', auth) => + rp({ + method: 'GET', + uri: inkUrl('recipes'), + headers: { + uid: inkConfig.email, + ...auth, + }, + json: true, + }).then(data => { + const recipe = data.recipes.find(recipe => recipe.name === recipeKey) + + return recipe ? recipe.id : null + }) const process = async (inputFile, options) => { const auth = await authorize().catch(err => { @@ -118,7 +124,9 @@ const process = async (inputFile, options) => { }) // either use the recipe id from the configuration or search for it by name - const recipeId = inkConfig.recipes[options.recipe] || await findRecipeId(options.recipe, auth) + const recipeId = + inkConfig.recipes[options.recipe] || + (await findRecipeId(options.recipe, auth)) if (!recipeId) throw new Error('Unknown recipe') const response = await upload(recipeId, inputFile, auth).catch(err => { @@ -129,39 +137,44 @@ const process = async (inputFile, options) => { return download(response.process_chain, auth, options.outputFileName) } -const InkBackend = function (app) { +const InkBackend = function(app) { // TODO: authentication on this route app.use('/api/ink', (req, res, next) => { const fileStream = new Busboy({ headers: req.headers }) - fileStream.on('file', (fieldname, file, filename, encoding, contentType) => { - const stream = temp.createWriteStream() - - stream.on('finish', () => { - const inputFile = { - value: fs.createReadStream(stream.path), - options: { filename, contentType } - } - - process(inputFile, req.query).then(converted => { - res.json({ converted }) - - // clean up temp file - fs.unlink(stream.path, () => { - logger.info('Deleted temporary file', stream.path) - }) - }).catch(err => { - logger.error('ERROR CONVERTING WITH INK:', err.message) - next(err) + fileStream.on( + 'file', + (fieldname, file, filename, encoding, contentType) => { + const stream = temp.createWriteStream() + + stream.on('finish', () => { + const inputFile = { + value: fs.createReadStream(stream.path), + options: { filename, contentType }, + } + + process(inputFile, req.query) + .then(converted => { + res.json({ converted }) + + // clean up temp file + fs.unlink(stream.path, () => { + logger.info('Deleted temporary file', stream.path) + }) + }) + .catch(err => { + logger.error('ERROR CONVERTING WITH INK:', err.message) + next(err) + }) }) - }) - file.pipe(stream) + file.pipe(stream) - file.on('end', () => { - stream.end() - }) - }) + file.on('end', () => { + stream.end() + }) + }, + ) fileStream.on('error', err => { logger.error(err) diff --git a/packages/components/packages/InkBackend/index.js b/packages/components/packages/InkBackend/index.js index 732ef26f8642876f80d947c2840dfca918f42b87..df04cf8d11b9ce4320762f5010d87e30d6e5bb12 100644 --- a/packages/components/packages/InkBackend/index.js +++ b/packages/components/packages/InkBackend/index.js @@ -1,3 +1,3 @@ module.exports = { - backend: () => app => require('./InkBackend')(app) + backend: () => app => require('./InkBackend')(app), } diff --git a/packages/components/packages/InkFrontend/InkFrontendContainer.js b/packages/components/packages/InkFrontend/InkFrontendContainer.js index 86e409c17a23638e0f197594d4c5573d589c79bf..f07ff3e1b11d688cd2b9b09d016c1fea9a595594 100644 --- a/packages/components/packages/InkFrontend/InkFrontendContainer.js +++ b/packages/components/packages/InkFrontend/InkFrontendContainer.js @@ -4,9 +4,9 @@ import InkFrontend from './InkFrontend' export default connect( state => ({ - ink: state.ink + ink: state.ink, }), { - convert - } + convert, + }, )(InkFrontend) diff --git a/packages/components/packages/InkFrontend/actions.js b/packages/components/packages/InkFrontend/actions.js index e5a014aa35c14937bb3a12f4fa7322e950c24e34..2b46428ff4370dd88c29e6d144e295bea96c1c1d 100644 --- a/packages/components/packages/InkFrontend/actions.js +++ b/packages/components/packages/InkFrontend/actions.js @@ -3,17 +3,17 @@ import request from 'pubsweet-client/src/helpers/api' import { INK_FAILURE, INK_REQUEST, INK_SUCCESS } from './types' export const inkRequest = () => ({ - type: INK_REQUEST + type: INK_REQUEST, }) export const inkSuccess = converted => ({ type: INK_SUCCESS, - converted + converted, }) export const inkFailure = error => ({ type: INK_FAILURE, - error + error, }) export const ink = (file, options) => dispatch => { @@ -25,19 +25,21 @@ export const ink = (file, options) => dispatch => { let url = '/ink' if (options) { - url += '?' + qs.stringify(options) + url += `?${qs.stringify(options)}` } return request(url, { method: 'POST', - body - }).then(data => { - dispatch(inkSuccess(data.converted)) + body, + }) + .then(data => { + dispatch(inkSuccess(data.converted)) - return data - }).catch(error => { - dispatch(inkFailure(error.message)) + return data + }) + .catch(error => { + dispatch(inkFailure(error.message)) - throw error - }) + throw error + }) } diff --git a/packages/components/packages/InkFrontend/index.js b/packages/components/packages/InkFrontend/index.js index c21c133f03856ab5c067603f95e03079a9c308d9..fc4fb31389b9804c9c7bb100857ef16cb25b0da9 100644 --- a/packages/components/packages/InkFrontend/index.js +++ b/packages/components/packages/InkFrontend/index.js @@ -1,9 +1,7 @@ module.exports = { frontend: { - components: [ - () => require('./InkFrontendContainer') - ], + components: [() => require('./InkFrontendContainer')], actions: () => require('./actions'), - reducers: () => require('./reducers') - } + reducers: () => require('./reducers'), + }, } diff --git a/packages/components/packages/InkFrontend/reducers.js b/packages/components/packages/InkFrontend/reducers.js index 97b8298525bce7130f9782c7fe2821db5f2496bf..ace0890a75861c68a055e61e2004f894902e8421 100644 --- a/packages/components/packages/InkFrontend/reducers.js +++ b/packages/components/packages/InkFrontend/reducers.js @@ -3,30 +3,30 @@ import { INK_FAILURE, INK_REQUEST, INK_SUCCESS } from './types' const initialState = { isFetching: false, converted: null, - error: null + error: null, } -export default function ink (state = initialState, action) { +export default function ink(state = initialState, action) { switch (action.type) { case INK_REQUEST: return { isFetching: true, converted: null, - error: null + error: null, } case INK_SUCCESS: return { isFetching: false, converted: action.converted, - error: null + error: null, } case INK_FAILURE: return { isFetching: false, converted: null, - error: action.error + error: action.error, } default: diff --git a/packages/components/packages/Login/LoginContainer.js b/packages/components/packages/Login/LoginContainer.js index 98e345fa737f7119dcb3868e332c7a266156e0af..942e2669af7f06140570f6364140419e5ab8a1f0 100644 --- a/packages/components/packages/Login/LoginContainer.js +++ b/packages/components/packages/Login/LoginContainer.js @@ -4,18 +4,16 @@ import { loginUser } from './actions' import Login from './Login' -function mapState (state) { +function mapState(state) { return { - error: state.error + error: state.error, } } -function mapDispatch (dispatch) { +function mapDispatch(dispatch) { return { - actions: bindActionCreators({ loginUser }, dispatch) + actions: bindActionCreators({ loginUser }, dispatch), } } -export default connect( - mapState, mapDispatch -)(Login) +export default connect(mapState, mapDispatch)(Login) diff --git a/packages/components/packages/Login/actions.js b/packages/components/packages/Login/actions.js index 867bab6c99e9d350cefb3a7d6d4b1a9e1cd836c3..becc71412b869f70037d61951c4c23e5a76f72ac 100644 --- a/packages/components/packages/Login/actions.js +++ b/packages/components/packages/Login/actions.js @@ -1,6 +1,10 @@ import * as api from 'pubsweet-client/src/helpers/api' import { - LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE, LOGOUT_SUCCESS, LOGOUT_REQUEST + LOGIN_REQUEST, + LOGIN_SUCCESS, + LOGIN_FAILURE, + LOGOUT_SUCCESS, + LOGOUT_REQUEST, } from 'pubsweet-client/src/actions/types' import { push } from 'react-router-redux' @@ -10,37 +14,37 @@ const localStorage = window.localStorage || undefined // There are three possible states for our login // process and we need actions for each of them -function loginRequest (credentials) { +function loginRequest(credentials) { return { type: LOGIN_REQUEST, isFetching: true, isAuthenticated: false, - credentials + credentials, } } -function loginSuccess (user) { +function loginSuccess(user) { return { type: LOGIN_SUCCESS, isFetching: false, isAuthenticated: true, token: user.token, - user: user + user, } } -function loginFailure (message) { +function loginFailure(message) { return { type: LOGIN_FAILURE, isFetching: false, isAuthenticated: false, - error: message + error: message, } } // Calls the API to get a token and // dispatches actions along the way -export function loginUser (credentials, redirectTo) { +export function loginUser(credentials, redirectTo) { return dispatch => { dispatch(loginRequest(credentials)) return api.create('/users/authenticate', credentials).then( @@ -49,31 +53,31 @@ export function loginUser (credentials, redirectTo) { dispatch(loginSuccess(user)) if (redirectTo) dispatch(push(redirectTo)) }, - err => dispatch(loginFailure(err)) + err => dispatch(loginFailure(err)), ) } } -function logoutRequest () { +function logoutRequest() { return { type: LOGOUT_REQUEST, isFetching: true, - isAuthenticated: true + isAuthenticated: true, } } -function logoutSuccess () { +function logoutSuccess() { return { type: LOGOUT_SUCCESS, isFetching: false, - isAuthenticated: false + isAuthenticated: false, } } // Logs the user out // Since we are using JWTs, we just need to remove the token // from localStorage. -export function logoutUser (redirectTo) { +export function logoutUser(redirectTo) { return dispatch => { dispatch(logoutRequest()) localStorage.removeItem('token') diff --git a/packages/components/packages/Login/actions.test.js b/packages/components/packages/Login/actions.test.js index 5f5acf200cecdd0e4a1124643a2e37807dc7c834..faac7caa8fba4c41bca2c6e42b8d11126654ff61 100644 --- a/packages/components/packages/Login/actions.test.js +++ b/packages/components/packages/Login/actions.test.js @@ -1,20 +1,24 @@ import configureStore from 'redux-mock-store' import thunk from 'redux-thunk' import { - LOGIN_FAILURE, LOGIN_REQUEST, LOGIN_SUCCESS, LOGOUT_REQUEST, LOGOUT_SUCCESS + LOGIN_FAILURE, + LOGIN_REQUEST, + LOGIN_SUCCESS, + LOGOUT_REQUEST, + LOGOUT_SUCCESS, } from 'pubsweet-client/src/actions/types' import api from 'pubsweet-client/src/helpers/api' jest.mock('pubsweet-client/src/helpers/api', () => ({ - create: jest.fn(() => Promise.resolve({})) + create: jest.fn(() => Promise.resolve({})), })) global.window.localStorage = { setItem: jest.fn(), - removeItem: jest.fn() + removeItem: jest.fn(), } -const {loginUser, logoutUser} = require('./actions') +const { loginUser, logoutUser } = require('./actions') describe('Login actions', () => { const createMockStore = configureStore([thunk]) @@ -24,66 +28,77 @@ describe('Login actions', () => { describe('loginUser', () => { it("doesn't get authenticated on failure", async () => { const store = createMockStore({}) - let credentials = {username: 'mr blobby'} - let error = new Error('Nope') - jest.spyOn(api, 'create').mockImplementationOnce(jest.fn(() => Promise.reject(error))) + const credentials = { username: 'mr blobby' } + const error = new Error('Nope') + jest + .spyOn(api, 'create') + .mockImplementationOnce(jest.fn(() => Promise.reject(error))) await store.dispatch(loginUser(credentials)) const [firstAction, secondAction] = store.getActions() expect(firstAction).toMatchObject({ type: LOGIN_REQUEST, - credentials + credentials, }) expect(secondAction).toMatchObject({ type: LOGIN_FAILURE, isAuthenticated: false, - error + error, }) }) it('logs user in on success', async () => { const store = createMockStore({}) - let credentials = {username: 'mr blobby'} - const user = {username: 'mr blobby', token: 't0k3n'} - jest.spyOn(api, 'create').mockImplementationOnce(jest.fn(() => Promise.resolve(user))) + const credentials = { username: 'mr blobby' } + const user = { username: 'mr blobby', token: 't0k3n' } + jest + .spyOn(api, 'create') + .mockImplementationOnce(jest.fn(() => Promise.resolve(user))) await store.dispatch(loginUser(credentials)) const [firstAction, secondAction] = store.getActions() expect(firstAction).toMatchObject({ type: LOGIN_REQUEST, - credentials + credentials, }) expect(secondAction).toMatchObject({ type: LOGIN_SUCCESS, isAuthenticated: true, - user + user, }) }) it('stores token in localStorage', async () => { const store = createMockStore({}) - const user = {username: 'mr blobby', token: 't0k3n'} - jest.spyOn(api, 'create').mockImplementationOnce(jest.fn(() => Promise.resolve(user))) + const user = { username: 'mr blobby', token: 't0k3n' } + jest + .spyOn(api, 'create') + .mockImplementationOnce(jest.fn(() => Promise.resolve(user))) await store.dispatch(loginUser()) - expect(global.window.localStorage.setItem).toHaveBeenCalledWith('token', user.token) + expect(global.window.localStorage.setItem).toHaveBeenCalledWith( + 'token', + user.token, + ) }) it('redirects after login', async () => { const store = createMockStore({}) - jest.spyOn(api, 'create').mockImplementationOnce(jest.fn(() => Promise.resolve({}))) + jest + .spyOn(api, 'create') + .mockImplementationOnce(jest.fn(() => Promise.resolve({}))) await store.dispatch(loginUser({}, 'redirect/here')) const [, , thirdAction] = store.getActions() expect(thirdAction).toMatchObject({ - type: '@@router/CALL_HISTORY_METHOD' + type: '@@router/CALL_HISTORY_METHOD', }) }) }) @@ -96,12 +111,12 @@ describe('Login actions', () => { const [firstAction, secondAction, thirdAction] = store.getActions() expect(firstAction).toMatchObject({ - type: LOGOUT_REQUEST + type: LOGOUT_REQUEST, }) expect(secondAction).toMatchObject({ type: LOGOUT_SUCCESS, - isAuthenticated: false + isAuthenticated: false, }) expect(thirdAction).toBeUndefined() @@ -114,7 +129,7 @@ describe('Login actions', () => { const [, , thirdAction] = store.getActions() expect(thirdAction).toMatchObject({ - type: '@@router/CALL_HISTORY_METHOD' + type: '@@router/CALL_HISTORY_METHOD', }) }) @@ -123,7 +138,9 @@ describe('Login actions', () => { await store.dispatch(logoutUser()) - expect(global.window.localStorage.removeItem).toHaveBeenCalledWith('token') + expect(global.window.localStorage.removeItem).toHaveBeenCalledWith( + 'token', + ) }) }) }) diff --git a/packages/components/packages/Login/index.js b/packages/components/packages/Login/index.js index e23ecbbed364e8f01b9c1bca2f3ea520b9f9b91a..155554e77ad3dfe5c27ec6c2d4b5b623dcb3d135 100644 --- a/packages/components/packages/Login/index.js +++ b/packages/components/packages/Login/index.js @@ -1,9 +1,7 @@ module.exports = { frontend: { - components: [ - () => require('./LoginContainer') - ], + components: [() => require('./LoginContainer')], actions: () => require('./actions'), - reducers: () => require('./reducers') - } + reducers: () => require('./reducers'), + }, } diff --git a/packages/components/packages/Login/reducers.js b/packages/components/packages/Login/reducers.js index 490cf7bb30e068e449d5efcaf334c4fcbff31aec..d1f637a01e310491d842ac045d78ac1a0a879527 100644 --- a/packages/components/packages/Login/reducers.js +++ b/packages/components/packages/Login/reducers.js @@ -1,22 +1,29 @@ import { - LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE, LOGOUT_SUCCESS, LOGOUT_REQUEST + LOGIN_REQUEST, + LOGIN_SUCCESS, + LOGIN_FAILURE, + LOGOUT_SUCCESS, + LOGOUT_REQUEST, } from 'pubsweet-client/src/actions/types' // TODO: This will break when rendered on a server const localStorage = window.localStorage || undefined -export default function userLogin (state = { - isFetching: false, - isAuthenticated: false, - token: localStorage.getItem('token') -}, action) { +export default function userLogin( + state = { + isFetching: false, + isAuthenticated: false, + token: localStorage.getItem('token'), + }, + action, +) { switch (action.type) { case LOGIN_REQUEST: return { ...state, isFetching: true, isAuthenticated: false, - username: action.credentials.username + username: action.credentials.username, } case LOGIN_SUCCESS: return { @@ -24,26 +31,26 @@ export default function userLogin (state = { isFetching: false, isAuthenticated: true, user: action.user, - token: action.token + token: action.token, } case LOGIN_FAILURE: return { ...state, isFetching: false, isAuthenticated: false, - error: action.error + error: action.error, } case LOGOUT_SUCCESS: return { ...state, isFetching: false, - isAuthenticated: false + isAuthenticated: false, } case LOGOUT_REQUEST: return { ...state, isFetching: false, - isAuthenticated: false + isAuthenticated: false, } default: return state diff --git a/packages/components/packages/Login/reducers.test.js b/packages/components/packages/Login/reducers.test.js index e3a95e50922ce61e7dc31f28dfcda79bc1c7ac35..478e01416441628c10f4b45688f9be49df09e181 100644 --- a/packages/components/packages/Login/reducers.test.js +++ b/packages/components/packages/Login/reducers.test.js @@ -1,9 +1,13 @@ import { - LOGIN_FAILURE, LOGIN_REQUEST, LOGIN_SUCCESS, LOGOUT_REQUEST, LOGOUT_SUCCESS + LOGIN_FAILURE, + LOGIN_REQUEST, + LOGIN_SUCCESS, + LOGOUT_REQUEST, + LOGOUT_SUCCESS, } from 'pubsweet-client/src/actions/types' global.window.localStorage = { - getItem: jest.fn(() => undefined) + getItem: jest.fn(() => undefined), } const reducer = require('./reducers').default @@ -14,51 +18,58 @@ describe('Login reducer', () => { expect(newState).toEqual({ isFetching: false, isAuthenticated: false, - token: undefined + token: undefined, }) }) it('stores username on login request', () => { - const action = {type: LOGIN_REQUEST, credentials: {username: 'milo minderbinder'}} + const action = { + type: LOGIN_REQUEST, + credentials: { username: 'milo minderbinder' }, + } const newState = reducer(undefined, action) expect(newState).toMatchObject({ isFetching: true, - username: 'milo minderbinder' + username: 'milo minderbinder', }) }) it('stores user and token on login success', () => { - const action = {type: LOGIN_SUCCESS, user: {username: 'nurse duckett'}, token: 't0k3n'} + const action = { + type: LOGIN_SUCCESS, + user: { username: 'nurse duckett' }, + token: 't0k3n', + } const newState = reducer(undefined, action) expect(newState).toMatchObject({ isAuthenticated: true, user: action.user, - token: action.token + token: action.token, }) }) it('stores error on login failure', () => { - const action = {type: LOGIN_FAILURE, error: new Error('Flies in eyes')} - const newState = reducer({isAuthenticated: true}, action) + const action = { type: LOGIN_FAILURE, error: new Error('Flies in eyes') } + const newState = reducer({ isAuthenticated: true }, action) expect(newState).toMatchObject({ isAuthenticated: false, - error: action.error + error: action.error, }) }) it('logs out on request', () => { - const action = {type: LOGOUT_REQUEST} - const newState = reducer({isAuthenticated: true}, action) + const action = { type: LOGOUT_REQUEST } + const newState = reducer({ isAuthenticated: true }, action) expect(newState).toMatchObject({ - isAuthenticated: false + isAuthenticated: false, }) }) it('logs out on logout success', () => { - const action = {type: LOGOUT_SUCCESS} - const newState = reducer({isAuthenticated: true}, action) + const action = { type: LOGOUT_SUCCESS } + const newState = reducer({ isAuthenticated: true }, action) expect(newState).toMatchObject({ - isAuthenticated: false + isAuthenticated: false, }) }) }) diff --git a/packages/components/packages/Manage/index.js b/packages/components/packages/Manage/index.js index 27f2daaf5d30d7ba9293dc1ea6559a2712637af6..9255cba1b0e440303f117f0a8a4edefddbec4922 100644 --- a/packages/components/packages/Manage/index.js +++ b/packages/components/packages/Manage/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./Manage') - ] - } + components: [() => require('./Manage')], + }, } diff --git a/packages/components/packages/MediumDraft/MediumDraftContainer.js b/packages/components/packages/MediumDraft/MediumDraftContainer.js index 7bc31f2b61988b37cf223b6ecc52ba8935567c02..564703e23dd26c67e678d6020b0d2d0ced30289f 100644 --- a/packages/components/packages/MediumDraft/MediumDraftContainer.js +++ b/packages/components/packages/MediumDraft/MediumDraftContainer.js @@ -4,23 +4,20 @@ import Actions from 'pubsweet-client/src/actions' import MediumDraft from './MediumDraft' -function mapStateToProps (state, props) { - let fragmentId = props.match.params.id +function mapStateToProps(state, props) { + const fragmentId = props.match.params.id return { - fragmentId: fragmentId, + fragmentId, blog: state.collections[0], id: fragmentId, - fragment: state.fragments[fragmentId] + fragment: state.fragments[fragmentId], } } -function mapDispatchToProps (dispatch) { +function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators(Actions, dispatch) + actions: bindActionCreators(Actions, dispatch), } } -export default connect( - mapStateToProps, - mapDispatchToProps -)(MediumDraft) +export default connect(mapStateToProps, mapDispatchToProps)(MediumDraft) diff --git a/packages/components/packages/MediumDraft/index.js b/packages/components/packages/MediumDraft/index.js index fcf7150a64d2b3c06a67096528f26bb67026174b..11a15966f184b2beb2a889ad2bb4545bf096fad8 100644 --- a/packages/components/packages/MediumDraft/index.js +++ b/packages/components/packages/MediumDraft/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./MediumDraftContainer') - ] - } + components: [() => require('./MediumDraftContainer')], + }, } diff --git a/packages/components/packages/Navigation/index.js b/packages/components/packages/Navigation/index.js index 3b9371a9b2ba6f5e380af2c4e11c49f48d73fc68..c09b78a607eab0a4a52aef9e03a4197ccecef26e 100644 --- a/packages/components/packages/Navigation/index.js +++ b/packages/components/packages/Navigation/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./Navigation') - ] - } + components: [() => require('./Navigation')], + }, } diff --git a/packages/components/packages/PasswordResetBackend/PasswordResetBackend.js b/packages/components/packages/PasswordResetBackend/PasswordResetBackend.js index 3e680d1ee371a98a0d2305f1b409a43c2caec7a6..64e39f0bddfa1f06f248c22bd8cb8c34889cf1bf 100644 --- a/packages/components/packages/PasswordResetBackend/PasswordResetBackend.js +++ b/packages/components/packages/PasswordResetBackend/PasswordResetBackend.js @@ -7,27 +7,30 @@ const querystring = require('querystring') const bodyParser = require('body-parser') const transport = require('./transport') + const configTokenLength = config.get('password-reset')['token-length'] || 32 const configUrl = config.get('password-reset.url') const configSender = config.get('password-reset.sender') -const PasswordResetBackend = function (app) { - app.post('/api/password-reset', bodyParser.json(), async function (req, res, next) { +const PasswordResetBackend = function(app) { + app.post('/api/password-reset', bodyParser.json(), async (req, res, next) => { try { const { token, password, username } = req.body if (!username) { - res.status(400).json({error: 'Username must be specified'}) + res.status(400).json({ error: 'Username must be specified' }) return } // load the user by username // TODO: use findOneByField - const user = await app.locals.models.User.findByField('username', username) - .then(results => results ? results[0] : null) + const user = await app.locals.models.User.findByField( + 'username', + username, + ).then(results => (results ? results[0] : null)) if (!user) { - res.status(400).json({error: 'User not found'}) + res.status(400).json({ error: 'User not found' }) return } @@ -35,12 +38,16 @@ const PasswordResetBackend = function (app) { // change the password if (token !== user.passwordResetToken) { - res.status(400).json({error: 'invalid'}) + res.status(400).json({ error: 'invalid' }) return } - if (moment().subtract(24, 'hours').isAfter(user.passwordResetTimestamp)) { - res.status(400).json({error: 'expired'}) + if ( + moment() + .subtract(24, 'hours') + .isAfter(user.passwordResetTimestamp) + ) { + res.status(400).json({ error: 'expired' }) return } @@ -54,15 +61,17 @@ const PasswordResetBackend = function (app) { } else { // send a password reset email - user.passwordResetToken = crypto.randomBytes(configTokenLength).toString('hex') + user.passwordResetToken = crypto + .randomBytes(configTokenLength) + .toString('hex') user.passwordResetTimestamp = Number(moment()) await user.save() - const passwordResetURL = configUrl + '?' + querystring.encode({ - username: username, - token: user.passwordResetToken - }) + const passwordResetURL = `${configUrl}?${querystring.encode({ + username, + token: user.passwordResetToken, + })}` logger.info(`Sending password reset email to ${user.email}`) @@ -71,7 +80,7 @@ const PasswordResetBackend = function (app) { to: user.email, subject: 'Password reset', text: `Reset your password: ${passwordResetURL}`, - html: `<p><a href="${passwordResetURL}">Reset your password</a></p>` + html: `<p><a href="${passwordResetURL}">Reset your password</a></p>`, }) res.sendStatus(200) diff --git a/packages/components/packages/PasswordResetBackend/PasswordResetBackend.test.js b/packages/components/packages/PasswordResetBackend/PasswordResetBackend.test.js index 08c8bb4de07c95e1b551fd9755c8ae1b408a8db2..7d0e51b370f7d44757f774ddca9874ae6747b4cf 100644 --- a/packages/components/packages/PasswordResetBackend/PasswordResetBackend.test.js +++ b/packages/components/packages/PasswordResetBackend/PasswordResetBackend.test.js @@ -5,19 +5,24 @@ const supertest = require('supertest') const config = require('config') // mocks -config['password-reset'] = {url: '/', sender: 'me'} -jest.mock('./transport', () => ({sendMail: jest.fn()})) +config['password-reset'] = { url: '/', sender: 'me' } +jest.mock('./transport', () => ({ sendMail: jest.fn() })) const transport = require('./transport') const component = require('.') -function makeApp (response) { +function makeApp(response) { const app = express() // mock DB app.locals.models = { User: { - findByField: jest.fn(() => response instanceof Error ? Promise.reject(response) : Promise.resolve(response)) - } + findByField: jest.fn( + () => + response instanceof Error + ? Promise.reject(response) + : Promise.resolve(response), + ), + }, } // register component component.backend()(app) @@ -27,23 +32,23 @@ function makeApp (response) { describe('/api/password-reset route', () => { describe('initial validation', () => { - it('errors if no username', () => makeApp() + it('errors if no username', () => + makeApp() .post('/api/password-reset') .send({}) - .expect(400, '{"error":"Username must be specified"}') - ) + .expect(400, '{"error":"Username must be specified"}')) - it('errors if no user', () => makeApp(null) + it('errors if no user', () => + makeApp(null) .post('/api/password-reset') - .send({username: 'hey'}) - .expect(400, '{"error":"User not found"}') - ) + .send({ username: 'hey' }) + .expect(400, '{"error":"User not found"}')) - it('errors if DB call fails', () => makeApp(new Error('Ops!')) + it('errors if DB call fails', () => + makeApp(new Error('Ops!')) .post('/api/password-reset') - .send({username: 'hey'}) - .expect(500) - ) + .send({ username: 'hey' }) + .expect(500)) }) describe('sending email', () => { @@ -51,22 +56,24 @@ describe('/api/password-reset route', () => { const user = { username: 'hey', email: 'hey@here.com', - save: jest.fn() + save: jest.fn(), } return makeApp([user]) - .post('/api/password-reset') - .send({username: user.username}) - .expect(200) - .then(() => { - expect(user.passwordResetToken).toBeDefined() - expect(user.passwordResetTimestamp).toBeDefined() - expect(user.save).toHaveBeenCalled() - expect(transport.sendMail).toHaveBeenCalledWith(expect.objectContaining({ + .post('/api/password-reset') + .send({ username: user.username }) + .expect(200) + .then(() => { + expect(user.passwordResetToken).toBeDefined() + expect(user.passwordResetTimestamp).toBeDefined() + expect(user.save).toHaveBeenCalled() + expect(transport.sendMail).toHaveBeenCalledWith( + expect.objectContaining({ from: 'me', to: user.email, - subject: 'Password reset' - })) - }) + subject: 'Password reset', + }), + ) + }) }) }) @@ -74,24 +81,28 @@ describe('/api/password-reset route', () => { it('errors if reset token does not match', () => { const user = { username: 'hey', - passwordResetToken: '123' + passwordResetToken: '123', } return makeApp([user]) - .post('/api/password-reset') - .send({username: user.username, token: 'wrong', password: 'new pass'}) - .expect(400, '{"error":"invalid"}') + .post('/api/password-reset') + .send({ username: user.username, token: 'wrong', password: 'new pass' }) + .expect(400, '{"error":"invalid"}') }) it('errors if reset timestamp is in the past', () => { const user = { username: 'hey', passwordResetToken: '123', - passwordResetTimestamp: Date.now() - 1000 * 60 * 60 * 24 * 2 + passwordResetTimestamp: Date.now() - 1000 * 60 * 60 * 24 * 2, } return makeApp([user]) - .post('/api/password-reset') - .send({username: user.username, token: user.passwordResetToken, password: 'new pass'}) - .expect(400, '{"error":"expired"}') + .post('/api/password-reset') + .send({ + username: user.username, + token: user.passwordResetToken, + password: 'new pass', + }) + .expect(400, '{"error":"expired"}') }) it('saves user if all valid', () => { @@ -99,13 +110,17 @@ describe('/api/password-reset route', () => { username: 'hey', passwordResetToken: '123', passwordResetTimestamp: Date.now(), - save: jest.fn() + save: jest.fn(), } return makeApp([user]) - .post('/api/password-reset') - .send({username: user.username, token: user.passwordResetToken, password: 'new pass'}) - .expect(200) - .then(() => expect(user.save).toHaveBeenCalled()) + .post('/api/password-reset') + .send({ + username: user.username, + token: user.passwordResetToken, + password: 'new pass', + }) + .expect(200) + .then(() => expect(user.save).toHaveBeenCalled()) }) }) }) diff --git a/packages/components/packages/PasswordResetBackend/index.js b/packages/components/packages/PasswordResetBackend/index.js index f560117fa468c76b9a1879d5dac1e31fdaa2819a..3ba370631d04f60e2c67ce9ce93a2574587461f1 100644 --- a/packages/components/packages/PasswordResetBackend/index.js +++ b/packages/components/packages/PasswordResetBackend/index.js @@ -1,3 +1,3 @@ module.exports = { - backend: () => app => require('./PasswordResetBackend')(app) + backend: () => app => require('./PasswordResetBackend')(app), } diff --git a/packages/components/packages/PasswordResetFrontend/index.js b/packages/components/packages/PasswordResetFrontend/index.js index 73ff5168c37f89cc37aeaf75fdd21b2f6d058bcc..0c07eb3638a614ed96dab162a1c5634ba623b7e9 100644 --- a/packages/components/packages/PasswordResetFrontend/index.js +++ b/packages/components/packages/PasswordResetFrontend/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./PasswordReset') - ] - } + components: [() => require('./PasswordReset')], + }, } diff --git a/packages/components/packages/PostsManager/PostsManagerContainer.js b/packages/components/packages/PostsManager/PostsManagerContainer.js index 5480cd72f5320c0ffac719f48afcbba6a6ee9213..3abbb994384ceda720639187c14aa3849cc679a1 100644 --- a/packages/components/packages/PostsManager/PostsManagerContainer.js +++ b/packages/components/packages/PostsManager/PostsManagerContainer.js @@ -4,27 +4,24 @@ import Actions from 'pubsweet-client/src/actions' import PostsManager from './PostsManager' -function mapState (state) { +function mapState(state) { const blog = state.collections[0] const blogposts = blog - ? blog.fragments.map(f => state.fragments[f]).filter(f => f) - : [] + ? blog.fragments.map(f => state.fragments[f]).filter(f => f) + : [] return { blog, blogposts, error: state.error, - currentUser: state.currentUser + currentUser: state.currentUser, } } -function mapDispatch (dispatch) { +function mapDispatch(dispatch) { return { - actions: bindActionCreators(Actions, dispatch) + actions: bindActionCreators(Actions, dispatch), } } -export default connect( - mapState, - mapDispatch -)(PostsManager) +export default connect(mapState, mapDispatch)(PostsManager) diff --git a/packages/components/packages/PostsManager/TextInput.js b/packages/components/packages/PostsManager/TextInput.js index 25d58e9b6820b786144b160818f133743bbaa051..2cba7997aea1362bd75bc8ed2194f4762628c523 100644 --- a/packages/components/packages/PostsManager/TextInput.js +++ b/packages/components/packages/PostsManager/TextInput.js @@ -1,46 +1,47 @@ import React from 'react' import PropTypes from 'prop-types' + const ENTER_KEY_CODE = 13 export default class TextInput extends React.Component { - constructor (props) { + constructor(props) { super(props) this._save = this._save.bind(this) this._onChange = this._onChange.bind(this) this._onKeyDown = this._onKeyDown.bind(this) this.state = { - value: this.props.value || '' + value: this.props.value || '', } } - render () { + render() { return ( <input + autoFocus className={this.props.className} id={this.props.id} - placeholder={this.props.placeholder} onChange={this._onChange} onKeyDown={this._onKeyDown} + placeholder={this.props.placeholder} value={this.state.value} - autoFocus /> ) } - _save () { + _save() { this.props.onSave(this.state.value) this.setState({ - value: '' + value: '', }) } - _onChange (event) { + _onChange(event) { this.setState({ - value: event.target.value + value: event.target.value, }) } - _onKeyDown (event) { + _onKeyDown(event) { if (event.keyCode === ENTER_KEY_CODE) { this._save() } @@ -52,5 +53,5 @@ TextInput.propTypes = { id: PropTypes.string, placeholder: PropTypes.string, onSave: PropTypes.func.isRequired, - value: PropTypes.string + value: PropTypes.string, } diff --git a/packages/components/packages/PostsManager/index.js b/packages/components/packages/PostsManager/index.js index 72116b98b50a9d0980eca524c57217723d9cb741..db202a99353169405de0a4ac69bc2c684ba481e5 100644 --- a/packages/components/packages/PostsManager/index.js +++ b/packages/components/packages/PostsManager/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./PostsManagerContainer') - ] - } + components: [() => require('./PostsManagerContainer')], + }, } diff --git a/packages/components/packages/Signup/SignupContainer.js b/packages/components/packages/Signup/SignupContainer.js index 8aa128a2ecb1e0df15614b10c1ff998e2ef07ea8..0be26a7de29181b97625ab58119d2ca3a9fd5cb1 100644 --- a/packages/components/packages/Signup/SignupContainer.js +++ b/packages/components/packages/Signup/SignupContainer.js @@ -4,18 +4,16 @@ import { signupUser } from './actions' import Signup from './Signup' -function mapState (state) { +function mapState(state) { return { - error: state.error + error: state.error, } } -function mapDispatch (dispatch) { +function mapDispatch(dispatch) { return { - actions: bindActionCreators({ signupUser }, dispatch) + actions: bindActionCreators({ signupUser }, dispatch), } } -export default connect( - mapState, mapDispatch -)(Signup) +export default connect(mapState, mapDispatch)(Signup) diff --git a/packages/components/packages/Signup/actions.js b/packages/components/packages/Signup/actions.js index 7199a237afdda3be47865fed949fdc6310bf8021..4b271acd6e684a3022aff76a0da6d23544aef834 100644 --- a/packages/components/packages/Signup/actions.js +++ b/packages/components/packages/Signup/actions.js @@ -2,32 +2,32 @@ import * as api from 'pubsweet-client/src/helpers/api' import * as T from './types' import { push } from 'react-router-redux' -function signupRequest () { +function signupRequest() { return { type: T.SIGNUP_REQUEST, - isFetching: true + isFetching: true, } } -function signupSuccess (user) { +function signupSuccess(user) { return { type: T.SIGNUP_SUCCESS, isFetching: false, isAuthenticated: true, - user: user + user, } } -function signupFailure (message) { +function signupFailure(message) { return { type: T.SIGNUP_FAILURE, isFetching: false, isAuthenticated: false, - error: message + error: message, } } -export function signupUser (user) { +export function signupUser(user) { return dispatch => { dispatch(signupRequest()) return api.create('/users', user).then( @@ -35,7 +35,7 @@ export function signupUser (user) { dispatch(signupSuccess(user)) dispatch(push('/login')) }, - err => dispatch(signupFailure(err)) + err => dispatch(signupFailure(err)), ) } } diff --git a/packages/components/packages/Signup/index.js b/packages/components/packages/Signup/index.js index 7841cb929b6c1058899bd48ac228065af4970456..97d462d9b9beb987d214e03505412ba1e7389ef5 100644 --- a/packages/components/packages/Signup/index.js +++ b/packages/components/packages/Signup/index.js @@ -1,9 +1,7 @@ module.exports = { frontend: { - components: [ - () => require('./SignupContainer') - ], + components: [() => require('./SignupContainer')], actions: () => require('./actions'), - reducers: () => require('./reducers') - } + reducers: () => require('./reducers'), + }, } diff --git a/packages/components/packages/Signup/reducers.js b/packages/components/packages/Signup/reducers.js index d3686beab46fada58a1688e8359951705184c1b1..823210ef899ffb7ee7251e03585b1240b1092cc2 100644 --- a/packages/components/packages/Signup/reducers.js +++ b/packages/components/packages/Signup/reducers.js @@ -1,17 +1,18 @@ -import { - SIGNUP_SUCCESS -} from './types' +import { SIGNUP_SUCCESS } from './types' -export default function userSignup (state = { - isFetching: false, - isAuthenticated: false -}, action) { +export default function userSignup( + state = { + isFetching: false, + isAuthenticated: false, + }, + action, +) { switch (action.type) { case SIGNUP_SUCCESS: return Object.assign({}, state, { isFetching: true, isAuthenticated: true, - user: action.user + user: action.user, }) default: return state diff --git a/packages/components/packages/TeamsManager/TeamsManagerContainer.js b/packages/components/packages/TeamsManager/TeamsManagerContainer.js index 9c91f2ed0ff926beb58ea3a8dcb8551085407dc0..cc90ecc5e8d0b5e8964e954484834a2f92163e73 100644 --- a/packages/components/packages/TeamsManager/TeamsManagerContainer.js +++ b/packages/components/packages/TeamsManager/TeamsManagerContainer.js @@ -4,22 +4,19 @@ import Actions from 'pubsweet-client/src/actions' import TeamsManager from './TeamsManager' -function mapStateToProps (state) { +function mapStateToProps(state) { return { collections: state.collections, teams: state.teams, users: state.users.users, - error: state.error + error: state.error, } } -function mapDispatchToProps (dispatch) { +function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators(Actions, dispatch) + actions: bindActionCreators(Actions, dispatch), } } -export default connect( - mapStateToProps, - mapDispatchToProps -)(TeamsManager) +export default connect(mapStateToProps, mapDispatchToProps)(TeamsManager) diff --git a/packages/components/packages/TeamsManager/index.js b/packages/components/packages/TeamsManager/index.js index 4b2889db2135d4e12a6441b42d5baad003346e7a..681eaa51bad0c7a4770b2df12adb3a67c7ada878 100644 --- a/packages/components/packages/TeamsManager/index.js +++ b/packages/components/packages/TeamsManager/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./TeamsManagerContainer') - ] - } + components: [() => require('./TeamsManagerContainer')], + }, } diff --git a/packages/components/packages/UsersManager/UsersManagerContainer.js b/packages/components/packages/UsersManager/UsersManagerContainer.js index 0599ae95cf2acbc1b00911b72db37fd87d0ef41c..a7a148be45b794e16be8f66234462ba854b2e665 100644 --- a/packages/components/packages/UsersManager/UsersManagerContainer.js +++ b/packages/components/packages/UsersManager/UsersManagerContainer.js @@ -4,20 +4,17 @@ import Actions from 'pubsweet-client/src/actions' import UsersManager from './UsersManager' -function mapStateToProps (state) { +function mapStateToProps(state) { return { users: state.users.users, - error: state.error + error: state.error, } } -function mapDispatchToProps (dispatch) { +function mapDispatchToProps(dispatch) { return { - actions: bindActionCreators(Actions, dispatch) + actions: bindActionCreators(Actions, dispatch), } } -export default connect( - mapStateToProps, - mapDispatchToProps -)(UsersManager) +export default connect(mapStateToProps, mapDispatchToProps)(UsersManager) diff --git a/packages/components/packages/UsersManager/index.js b/packages/components/packages/UsersManager/index.js index 8e70fd201baa7fa2bed84e2fbe3737c1862ca808..83825f1bedb75e0e47949b377fe28c0d3ab1173c 100644 --- a/packages/components/packages/UsersManager/index.js +++ b/packages/components/packages/UsersManager/index.js @@ -1,7 +1,5 @@ module.exports = { frontend: { - components: [ - () => require('./UsersManagerContainer') - ] - } + components: [() => require('./UsersManagerContainer')], + }, } diff --git a/packages/components/styleguide.config.js b/packages/components/styleguide.config.js index 00b4ff094e9a8869550676dfe7f01634ba750005..480071b7d9e725f876d85546c1b747f24b79abd4 100644 --- a/packages/components/styleguide.config.js +++ b/packages/components/styleguide.config.js @@ -1,6 +1,6 @@ module.exports = { styleguideComponents: { - Wrapper: require.resolve('./styleguidist/Wrapper.jsx') + Wrapper: require.resolve('./styleguidist/Wrapper.jsx'), }, components: 'packages/**/*.jsx', ignore: [ @@ -9,6 +9,6 @@ module.exports = { '**/packages/PasswordResetBackend/**', '**/packages/MediumDraft/CustomImageSideButton.jsx', '**/node_modules/**', - '**/*.test.{js,jsx}' - ] + '**/*.test.{js,jsx}', + ], } diff --git a/packages/components/webpack.config.js b/packages/components/webpack.config.js index ff5770736c9d8ea2e1619369aa3ae5d52e0518fd..7a7eed4f57450d88af2f949a0e375bdcac43e3a8 100644 --- a/packages/components/webpack.config.js +++ b/packages/components/webpack.config.js @@ -7,16 +7,16 @@ module.exports = { { test: /\.jsx?$/, exclude: /node_modules\/(?!pubsweet-)/, - loader: 'babel-loader' + loader: 'babel-loader', }, { test: /\.s?css$/, exclude: /\.local\.s?css$/, loader: [ - {loader: 'style-loader'}, - {loader: 'css-loader'}, - {loader: 'sass-loader'} - ] + { loader: 'style-loader' }, + { loader: 'css-loader' }, + { loader: 'sass-loader' }, + ], }, { test: /\.local\.s?css$/, @@ -26,11 +26,11 @@ module.exports = { loader: 'css-loader', options: { modules: true, - importLoaders: 1 - } + importLoaders: 1, + }, }, - 'sass-loader' - ] + 'sass-loader', + ], }, { test: /\.woff|\.woff2|\.svg|.eot|\.ttf/, @@ -39,23 +39,23 @@ module.exports = { loader: 'url-loader', options: { prefix: 'font', - limit: 1000 - } - } - ] - } - ] + limit: 1000, + }, + }, + ], + }, + ], }, resolve: { alias: { joi: 'joi-browser', - config: path.join(__dirname, 'styleguidist', 'config.json') + config: path.join(__dirname, 'styleguidist', 'config.json'), }, - extensions: ['.js', '.jsx', '.json', '.scss'] + extensions: ['.js', '.jsx', '.json', '.scss'], }, plugins: [ new webpack.DefinePlugin({ - PUBSWEET_COMPONENTS: JSON.stringify([]) - }) - ] + PUBSWEET_COMPONENTS: JSON.stringify([]), + }), + ], } diff --git a/packages/db-manager/src/add-collection/index.js b/packages/db-manager/src/add-collection/index.js index 737caa74e0b775e34add578ed5205f4f3c274b7d..635dd12147a7066e0d6e9ca9fb74fb58a4e127eb 100644 --- a/packages/db-manager/src/add-collection/index.js +++ b/packages/db-manager/src/add-collection/index.js @@ -12,9 +12,9 @@ module.exports = async collectionData => { await collection.save() logger.info( - `Successfully created collection ${user - ? `and set ${user.id} as owner` - : 'with no owner'}`, + `Successfully created collection ${ + user ? `and set ${user.id} as owner` : 'with no owner' + }`, ) return collection } diff --git a/packages/db-manager/src/helpers/db-path.js b/packages/db-manager/src/helpers/db-path.js index bfdcd76f5c044fbe49b2abe1aa3b5a2c985cf2ab..9df6e49b775a968124a1db5715a2aa4c498e94c9 100644 --- a/packages/db-manager/src/helpers/db-path.js +++ b/packages/db-manager/src/helpers/db-path.js @@ -1,7 +1,8 @@ const config = require('config') + const dbPath = config.get('pubsweet-server.dbPath') const getPath = () => - /^http/.test(dbPath) ? (dbPath + '/').replace(/\/\/$/, '/') : dbPath + /^http/.test(dbPath) ? `${dbPath}/`.replace(/\/\/$/, '/') : dbPath module.exports = getPath diff --git a/packages/db-manager/src/validations.js b/packages/db-manager/src/validations.js index 8be4039c13e96d0d1ebef17304d252debe04e3a3..31f421bd85816d8bab84c7b96260c559913652a8 100644 --- a/packages/db-manager/src/validations.js +++ b/packages/db-manager/src/validations.js @@ -1,5 +1,6 @@ const Joi = require('joi') const config = require('config') + let appValidations try { appValidations = require(config.validations) diff --git a/packages/db-manager/test/add-user/index.test.js b/packages/db-manager/test/add-user/index.test.js index 0afa6f7d7eff8b93e5d4591e48ca26dd37aee1cc..bf190f8c4aed0af9c33ccfb989a4b76ab95f2c3a 100644 --- a/packages/db-manager/test/add-user/index.test.js +++ b/packages/db-manager/test/add-user/index.test.js @@ -24,7 +24,7 @@ describe('add-user', () => { // need to reset modules to get fresh db because models hold a reference jest.resetModules() const config = require('config') - config['dbManager'] = baseConfig['dbManager'] + config.dbManager = baseConfig.dbManager const { setupDb } = require('../../src/') await setupDb() }) diff --git a/packages/logger/src/index.js b/packages/logger/src/index.js index 68cb9a20326de3ec4e7e917a38999f85710ecaa3..5901385380da63cb2043754b034182c3f264e869 100644 --- a/packages/logger/src/index.js +++ b/packages/logger/src/index.js @@ -22,11 +22,11 @@ module.exports = { info: (...args) => logger.info(...args), debug: (...args) => logger.debug(...args), stream: { - write: function (message, encoding) { + write(message, encoding) { logger.info(message) - } + }, }, - configure: (theirLogger) => { + configure: theirLogger => { if (configured) { throw new Error('Logger has already been configured') } @@ -36,5 +36,5 @@ module.exports = { logger = theirLogger configured = true }, - getRawLogger: () => logger + getRawLogger: () => logger, } diff --git a/packages/logger/src/validations.js b/packages/logger/src/validations.js index c3eb98bc0c3fae509647040523f02e13d6dd28f0..8a028dcef369d8464b1dc13792274f0b59831a2a 100644 --- a/packages/logger/src/validations.js +++ b/packages/logger/src/validations.js @@ -1,18 +1,16 @@ -'use strict' - const Joi = require('joi') const schema = Joi.object({ error: Joi.func().required(), warn: Joi.func().required(), debug: Joi.func().required(), - info: Joi.func().required() + info: Joi.func().required(), }).optional() module.exports = { - validateConfig: function validateConfig (config) { + validateConfig: function validateConfig(config) { const result = Joi.validate(config, schema, { allowUnknown: true }) if (result.error) throw result.error return result.value - } + }, } diff --git a/packages/logger/test/index.test.js b/packages/logger/test/index.test.js index bc3a74efe8c13e7151c2a5bdca77cbae5bd69441..5c8b175b5b06ea500a9310ca12f691c47b40738e 100644 --- a/packages/logger/test/index.test.js +++ b/packages/logger/test/index.test.js @@ -1,9 +1,8 @@ -'use strict' - process.env.ALLOW_CONFIG_MUTATIONS = true let config = require('config') -config['pubsweet-server'] = { } + +config['pubsweet-server'] = {} describe('Logging manager', () => { describe('when no logger is specifed', () => { @@ -84,7 +83,7 @@ describe('Logging manager', () => { jest.resetModules() config = require('config') const logger = require('../src/') - const bunyan = require('bunyan').createLogger({name: 'test'}) + const bunyan = require('bunyan').createLogger({ name: 'test' }) jest.spyOn(bunyan, 'debug').mockImplementation() jest.spyOn(bunyan, 'info').mockImplementation() jest.spyOn(bunyan, 'warn').mockImplementation() @@ -115,7 +114,7 @@ describe('Logging manager', () => { it('which returns raw logger', () => { jest.resetModules() const logger = require('../src/') - const bunyan = require('bunyan').createLogger({name: 'test'}) + const bunyan = require('bunyan').createLogger({ name: 'test' }) logger.configure(bunyan) const rawLogger = logger.getRawLogger() expect(rawLogger.fields.name).toBe('test') @@ -126,7 +125,7 @@ describe('Logging manager', () => { it('sets logger to "bunyan" if specified', () => { jest.resetModules() config = require('config') - const bunyan = require('bunyan').createLogger({name: 'test'}) + const bunyan = require('bunyan').createLogger({ name: 'test' }) config['pubsweet-server'] = { logger: bunyan } const logger = require('../src/') const rawLogger = logger.getRawLogger() diff --git a/packages/server/config/custom-environment-variables.js b/packages/server/config/custom-environment-variables.js index 8b6b0277d26d0b33a87559ed306183be6caba21c..ac567c7931ee083e9d2c44b2eb4701c8127b6bde 100644 --- a/packages/server/config/custom-environment-variables.js +++ b/packages/server/config/custom-environment-variables.js @@ -1,6 +1,6 @@ module.exports = { 'pubsweet-server': { silent: 'PUBSWEET_BACKEND_SILENT', - secret: 'PUBSWEET_SECRET' - } + secret: 'PUBSWEET_SECRET', + }, } diff --git a/packages/server/config/test.js b/packages/server/config/test.js index b60d9a3905062f72218b894f105861722e700c55..dfcaa8700512368b0376b9ec490f8e5ae5d62828 100644 --- a/packages/server/config/test.js +++ b/packages/server/config/test.js @@ -5,7 +5,7 @@ module.exports = { 'pubsweet-server': { logger: winston, secret: 'test', - sse: false + sse: false, }, validations: path.join(__dirname, 'validations'), authsome: { @@ -13,12 +13,12 @@ module.exports = { teams: { teamContributors: { name: 'Contributors', - permissions: 'POST' + permissions: 'POST', }, teamCoauthors: { name: 'Coauthors', - permissions: 'PATCH' - } - } - } + permissions: 'PATCH', + }, + }, + }, } diff --git a/packages/server/config/validations.js b/packages/server/config/validations.js index 1167eb689230e51a7f981d4aa8754fec9f630bfb..6fb7227a2bce9b32cf517f8d84a642aa79e627f0 100644 --- a/packages/server/config/validations.js +++ b/packages/server/config/validations.js @@ -9,18 +9,18 @@ module.exports = { title: Joi.string(), presentation: Joi.string(), published: Joi.boolean(), - filtered: Joi.string() + filtered: Joi.string(), }, { fragmentType: Joi.valid('file').required(), - path: Joi.string().required() - } + path: Joi.string().required(), + }, ], collection: { published: Joi.boolean(), nonPublicProperty: Joi.string(), filtered: Joi.string(), created: Joi.date().default(Date.now, 'creation time'), - title: Joi.string() - } -} \ No newline at end of file + title: Joi.string(), + }, +} diff --git a/packages/server/src/authentication.js b/packages/server/src/authentication.js index e13dbc5f186e3c8263c40f148c50a826ffd1ce8c..36844a7c1f94ae7f9a98b0ccb562cc3f8ccfaa82 100644 --- a/packages/server/src/authentication.js +++ b/packages/server/src/authentication.js @@ -8,16 +8,16 @@ const LocalStrategy = require('passport-local').Strategy const User = require('./models/User') const config = require('config') -const createToken = (user) => { +const createToken = user => { logger.debug('Creating token for', user.username) return jwt.sign( { username: user.username, - id: user.id + id: user.id, }, config.get('pubsweet-server.secret'), - { expiresIn: 24 * 3600 } + { expiresIn: 24 * 3600 }, ) } @@ -28,35 +28,39 @@ const verifyToken = (token, done) => { return done(null, decoded.id, { username: decoded.username, id: decoded.id, - token: token + token, }) }) } const verifyPassword = (username, password, done) => { - let errorMessage = 'Wrong username or password.' + const errorMessage = 'Wrong username or password.' logger.debug('User finding:', username) - User.findByUsername(username).then(user => { - logger.debug('User found:', user.username) - return Promise.all([user, user.validPassword(password)]) - }).then(([user, isValid]) => { - if (isValid) { - return done(null, user, { id: user.id }) - } else { + User.findByUsername(username) + .then(user => { + logger.debug('User found:', user.username) + return Promise.all([user, user.validPassword(password)]) + }) + .then(([user, isValid]) => { + if (isValid) { + return done(null, user, { id: user.id }) + } logger.debug('Invalid password for user:', username) return done(null, false, { message: errorMessage }) - } - }).catch((err) => { - logger.debug('User not found', err) - if (err) { return done(null, false, { message: errorMessage }) } - }) + }) + .catch(err => { + logger.debug('User not found', err) + if (err) { + return done(null, false, { message: errorMessage }) + } + }) } module.exports = { token: { create: createToken, - verify: verifyToken + verify: verifyToken, }, strategies: { // no credentials @@ -66,6 +70,6 @@ module.exports = { bearer: new BearerStrategy(verifyToken), // email + password - local: new LocalStrategy(verifyPassword) - } + local: new LocalStrategy(verifyPassword), + }, } diff --git a/packages/server/src/db.js b/packages/server/src/db.js index e07d1fe64edd2448f0ac396bc4d811b582ded7cf..ce2de24d0bd3b79dc7febada9d39575f866972f2 100644 --- a/packages/server/src/db.js +++ b/packages/server/src/db.js @@ -7,7 +7,7 @@ const PouchDB = require('pouchdb-core') .plugin(require('pouchdb-upsert')) .plugin(require('relational-pouch')) -const getAdapterIdentifier = (dbPath) => { +const getAdapterIdentifier = dbPath => { if (config.has('pubsweet-server.adapter')) { return config.get('pubsweet-server.adapter') } @@ -23,7 +23,7 @@ const getAdapterIdentifier = (dbPath) => { return 'leveldb' } -const preparePouchConfig = (adapter) => { +const preparePouchConfig = adapter => { switch (adapter) { case 'memory': PouchDB.plugin(require('pouchdb-adapter-memory')) @@ -44,8 +44,7 @@ const dbPath = _.get('pubsweet-server.dbPath', config) const adapterIdentifier = getAdapterIdentifier(dbPath) const pouchConfig = preparePouchConfig(adapterIdentifier) -module.exports = () => { +module.exports = () => // Pass name as first arg because passing { name } on options - // seems to produce different result (see Pouch issue #1137 ?) - return new PouchDB(pouchConfig.name, { adapter: pouchConfig.adapter }) -} + // seems to produce different result (see Pouch issue #1137 ?) + new PouchDB(pouchConfig.name, { adapter: pouchConfig.adapter }) diff --git a/packages/server/src/errors/AuthorizationError.js b/packages/server/src/errors/AuthorizationError.js index 8a3a35ce7e2180519b4e2c4a4af9aa18df997e7e..43e30eac00b467e41f34d0dde82034d913cece30 100644 --- a/packages/server/src/errors/AuthorizationError.js +++ b/packages/server/src/errors/AuthorizationError.js @@ -1,8 +1,7 @@ -'use strict' const STATUS = require('http-status-codes') class AuthorizationError extends Error { - constructor (message, status) { + constructor(message, status) { super(message) Error.captureStackTrace(this, 'AuthorizationError') this.name = 'AuthorizationError' diff --git a/packages/server/src/errors/ConflictError.js b/packages/server/src/errors/ConflictError.js index 30567759402db0c4024eb768864bbc8499b4a6b4..62ff9b007b086b336836f5047c6b6e78a7a0086b 100644 --- a/packages/server/src/errors/ConflictError.js +++ b/packages/server/src/errors/ConflictError.js @@ -1,9 +1,7 @@ -'use strict' - const STATUS = require('http-status-codes') class ConflictError extends Error { - constructor (message, status) { + constructor(message, status) { super(message) Error.captureStackTrace(this, 'ConflictError') this.name = 'ConflictError' diff --git a/packages/server/src/errors/NotFoundError.js b/packages/server/src/errors/NotFoundError.js index 9e1ee1970212080a5295ab5a7ec60090f49e34d2..2d7c2f9623a566c70abf034059b9a79dd3867cbc 100644 --- a/packages/server/src/errors/NotFoundError.js +++ b/packages/server/src/errors/NotFoundError.js @@ -1,8 +1,7 @@ -'use strict' const STATUS = require('http-status-codes') class NotFoundError extends Error { - constructor (message, status) { + constructor(message, status) { super(message) Error.captureStackTrace(this, 'NotFoundError') this.name = 'NotFoundError' diff --git a/packages/server/src/errors/ValidationError.js b/packages/server/src/errors/ValidationError.js index 5ec42347144636919d0e7e63a6343e2233726ccb..6ca48d399d25e28b1ca4b6ee44637467b0c988ab 100644 --- a/packages/server/src/errors/ValidationError.js +++ b/packages/server/src/errors/ValidationError.js @@ -1,8 +1,7 @@ -'use strict' const STATUS = require('http-status-codes') class ValidationError extends Error { - constructor (message, status) { + constructor(message, status) { super(message) Error.captureStackTrace(this, 'ValidationError') this.name = 'ValidationError' diff --git a/packages/server/src/helpers/authsome.js b/packages/server/src/helpers/authsome.js index 55a4dc7442774cb68456c11d1f179f8919afb91e..756218d50ba7737b88322a20f8357f3c9d808263 100644 --- a/packages/server/src/helpers/authsome.js +++ b/packages/server/src/helpers/authsome.js @@ -1,26 +1,29 @@ - const config = require('config') const Authsome = require('authsome') const models = require('../models') + const mode = require(config.get('authsome.mode')) -const authsome = new Authsome({...config.authsome, mode}, { - // restrict methods passed to mode since these have to be shimmed on client - // any changes here should be reflected in the `withAuthsome` component of `pubsweet-client` - models: { - Collection: { - find: id => models.Collection.find(id) - }, - Fragment: { - find: id => models.Fragment.find(id) - }, - User: { - find: id => models.User.find(id) +const authsome = new Authsome( + { ...config.authsome, mode }, + { + // restrict methods passed to mode since these have to be shimmed on client + // any changes here should be reflected in the `withAuthsome` component of `pubsweet-client` + models: { + Collection: { + find: id => models.Collection.find(id), + }, + Fragment: { + find: id => models.Fragment.find(id), + }, + User: { + find: id => models.User.find(id), + }, + Team: { + find: id => models.Team.find(id), + }, }, - Team: { - find: id => models.Team.find(id) - } - } -}) + }, +) module.exports = authsome diff --git a/packages/server/src/index.js b/packages/server/src/index.js index eb79b9da729a27724f3834c62722b3671c0114ab..b8d4c55869ce55c8bd2c22dea47129ac41449e22 100644 --- a/packages/server/src/index.js +++ b/packages/server/src/index.js @@ -1,5 +1,6 @@ const path = require('path') const config = require('config') + const dotenvPath = path.resolve(`.env.${config.util.getEnv('NODE_ENV')}`) require('dotenv').config({ path: dotenvPath }) @@ -21,12 +22,12 @@ const STATUS = require('http-status-codes') const registerComponents = require('./register-components') const startServer = require('./start-server') -const configureApp = (app) => { +const configureApp = app => { global.versions = {} app.locals.models = models - app.use(morgan('combined', { 'stream': logger.stream })) + app.use(morgan('combined', { stream: logger.stream })) app.use(bodyParser.json({ limit: '50mb' })) app.use(bodyParser.urlencoded({ extended: false })) @@ -51,7 +52,11 @@ const configureApp = (app) => { // SSE update stream if (_.get('pubsweet-server.sse', config)) { - app.get('/updates', passport.authenticate('bearer', { session: false }), sse.connect) + app.get( + '/updates', + passport.authenticate('bearer', { session: false }), + sse.connect, + ) } // Serve the index page for front end @@ -73,9 +78,10 @@ const configureApp = (app) => { return res.status(err.status).json({ message: err.message }) } else if (err.name === 'AuthenticationError') { return res.status(STATUS.UNAUTHORIZED).json({ message: err.message }) - } else { - return res.status(err.status || STATUS.INTERNAL_SERVER_ERROR).json({ message: err.message }) } + return res + .status(err.status || STATUS.INTERNAL_SERVER_ERROR) + .json({ message: err.message }) }) return app diff --git a/packages/server/src/models/Collection.js b/packages/server/src/models/Collection.js index 01df3ddf8e12b666ab85cf308e73079bbb7c54a4..c5e747de14dcf9a9977344fce1a961daa6886c79 100644 --- a/packages/server/src/models/Collection.js +++ b/packages/server/src/models/Collection.js @@ -1,11 +1,10 @@ -'use strict' const Model = require('./Model') const Fragment = require('./Fragment') const Team = require('./Team') const without = require('lodash/without') class Collection extends Model { - constructor (properties) { + constructor(properties) { super(properties) this.type = 'collection' this.fragments = this.fragments || [] @@ -13,49 +12,47 @@ class Collection extends Model { // Gets fragments in a collection, supports filtering by function e.g. // collection.getFragments({filter: fragment => {Authorize.can(req.user, 'read', fragment)}) - getFragments (options) { + getFragments(options) { options = options || {} options.filter = options.filter || (() => Promise.resolve(true)) - const fragments = Promise.all(this.fragments.map((id) => Fragment.find(id))) + const fragments = Promise.all(this.fragments.map(id => Fragment.find(id))) - return fragments.then( - fragments => { - let filters = Promise.all( - fragments.map( - fragment => options.filter(fragment).catch(() => false) - ) + return fragments + .then(fragments => { + const filters = Promise.all( + fragments.map(fragment => + options.filter(fragment).catch(() => false), + ), ) return Promise.all([fragments, filters]) - } - ).then( - ([fragments, filters]) => fragments.filter(fragment => filters.shift()) - ) + }) + .then(([fragments, filters]) => + fragments.filter(fragment => filters.shift()), + ) } - addFragment (fragment) { + addFragment(fragment) { this.fragments = this.fragments.map(fragment => { if (typeof fragment === 'object') { return fragment - } else { - return new Fragment({id: fragment}) } + return new Fragment({ id: fragment }) }) this.fragments.push(fragment) } - removeFragment (fragment) { + removeFragment(fragment) { this.fragments = this.fragments.map(fragment => { if (typeof fragment === 'object') { return fragment.id - } else { - return fragment } + return fragment }) this.fragments = without(this.fragments, fragment.id) } - async delete () { + async delete() { await Team.deleteAssociated(this.type, this.id) return super.delete() } diff --git a/packages/server/src/models/Fragment.js b/packages/server/src/models/Fragment.js index 42f18e55f6a009a09ace8ac3a6f455b09d8420f1..64c91cc921a254a71b4d83d508ccbb1e92c93650 100644 --- a/packages/server/src/models/Fragment.js +++ b/packages/server/src/models/Fragment.js @@ -1,14 +1,13 @@ -'use strict' const Model = require('./Model') const Team = require('./Team') class Fragment extends Model { - constructor (properties) { + constructor(properties) { super(properties) this.type = 'fragment' } - async delete () { + async delete() { await Team.deleteAssociated(this.type, this.id) return super.delete() } diff --git a/packages/server/src/models/Model.js b/packages/server/src/models/Model.js index faed34cc641478b5ace8bec5cfce46a71f2e9da3..bebbf9e989a2a4295ade5903d678b79fb8fdb8c6 100644 --- a/packages/server/src/models/Model.js +++ b/packages/server/src/models/Model.js @@ -1,5 +1,3 @@ -'use strict' - const uuid = require('uuid') const Joi = require('joi') const _ = require('lodash') @@ -10,24 +8,25 @@ const ValidationError = require('../errors/ValidationError') const logger = require('@pubsweet/logger') const config = require('config') + const appValidations = require(config.validations) const validations = require('./validations')(appValidations) schema() class Model { - constructor (properties) { + constructor(properties) { schema() this.id = Model.uuid() Object.assign(this, properties) } - static validations () { + static validations() { return validations[this.type] } - validate () { - let validation = Joi.validate(this, this.constructor.validations()) + validate() { + const validation = Joi.validate(this, this.constructor.validations()) if (validation.error) { logger.error(validation.error) @@ -37,44 +36,48 @@ class Model { return true } - async save () { + async save() { logger.debug('Saving', this.type, this.id) this.validate() - if (!this.rev /*is create*/ && typeof this.isUniq === 'function') { + if (!this.rev /* is create */ && typeof this.isUniq === 'function') { await this.isUniq(this) // throws an exception if not unique } return this._put() } - async _put () { + async _put() { await db.rel.save(this.constructor.type, this) logger.debug('Actually _put', this.type, this.id, this) return this } - async delete () { + async delete() { const object = await this.constructor.find(this.id) await db.rel.del(this.type, object) logger.debug('Deleted', this.type, this.id) return this } - async updateProperties (properties) { + async updateProperties(properties) { // These properties are modified through setters delete properties.owners logger.debug('Updating properties to', properties) - const validation = Joi.validate(properties, { rev: Joi.string().required() }, { allowUnknown: true }) + const validation = Joi.validate( + properties, + { rev: Joi.string().required() }, + { allowUnknown: true }, + ) if (validation.error) throw validation.error Object.assign(this, properties) return this } - setOwners (owners) { + setOwners(owners) { if (Array.isArray(owners)) { owners.forEach(owner => this.validateOwner(owner)) this.owners = owners @@ -83,30 +86,31 @@ class Model { } } - validateOwner (owner) { - if (typeof owner !== 'string') throw new ValidationError('owner should be an id') + validateOwner(owner) { + if (typeof owner !== 'string') + throw new ValidationError('owner should be an id') } - isOwner (userId) { + isOwner(userId) { return Array.isArray(this.owners) && this.owners.includes(userId) } - static uuid () { + static uuid() { return uuid.v4() } // Find all of a certain type e.g. // User.all() - static async all () { + static async all() { const results = await db.rel.find(this.type) - return results[this.type + 's'].map(result => new this(result)) + return results[`${this.type}s`].map(result => new this(result)) } // Find by id e.g. // User.find('394') - static async find (id) { - let plural = this.type + 's' + static async find(id) { + const plural = `${this.type}s` let results try { @@ -119,7 +123,7 @@ class Model { } } - let result = results[plural].find(result => result.id === id) + const result = results[plural].find(result => result.id === id) if (!result) { throw new NotFoundError(`Object not found: ${this.type} with id ${id}`) @@ -132,11 +136,11 @@ class Model { // `value` is a primitive, or a query object // or // `field` is an object of field, value pairs - static async findByField (field, value) { + static async findByField(field, value) { logger.debug('Finding', field, value) let selector = { - type: this.type + type: this.type, } if (value !== undefined) { @@ -149,12 +153,12 @@ class Model { await db.createIndex({ index: { - fields: Object.keys(selector) - } + fields: Object.keys(selector), + }, }) const results = await db.find({ - selector + selector, }) if (!results.docs.length) { @@ -162,15 +166,15 @@ class Model { } return results.docs.map(result => { - let id = db.rel.parseDocID(result._id).id - let foundObject = result.data + const id = db.rel.parseDocID(result._id).id + const foundObject = result.data foundObject.id = id foundObject.rev = result._rev return new this(foundObject) }) } - static async findOneByField (field, value) { + static async findOneByField(field, value) { const results = await this.findByField(field, value) return results.length ? results[0] : null diff --git a/packages/server/src/models/Team.js b/packages/server/src/models/Team.js index 864340fc74b7d5c1029b69b2274ecd9f8603954f..bb24ad23c24ded30590d3a145c8335883ba200d1 100644 --- a/packages/server/src/models/Team.js +++ b/packages/server/src/models/Team.js @@ -1,12 +1,10 @@ -'use strict' - const _ = require('lodash') const Model = require('./Model') const User = require('./User') class Team extends Model { - constructor (properties) { + constructor(properties) { super(properties) this.type = 'team' @@ -16,60 +14,63 @@ class Team extends Model { } } - static async deleteAssociated (type, id) { + static async deleteAssociated(type, id) { const teams = await Team.all() return Promise.all( teams - .filter(team => team.object && - team.object.type === type && - team.object.id === id) - .map(team => team.delete()) + .filter( + team => + team.object && team.object.type === type && team.object.id === id, + ) + .map(team => team.delete()), ) } - async updateProperties (properties) { - let currentMembers = new Set(this.members) - let newMembers = new Set(properties.members) - let removedMembers = new Set([...currentMembers].filter(x => !newMembers.has(x))) + async updateProperties(properties) { + const currentMembers = new Set(this.members) + const newMembers = new Set(properties.members) + const removedMembers = new Set( + [...currentMembers].filter(x => !newMembers.has(x)), + ) await Promise.all( - [...removedMembers].map(userId => { - return User.find(userId).then(user => { + [...removedMembers].map(userId => + User.find(userId).then(user => { user.teams = user.teams.filter(teamId => teamId !== this.id) return user.save() - }) - }) + }), + ), ) return super.updateProperties(properties) } - async save () { + async save() { await Promise.all( - this.members.map(member => { - return User.find(member).then(user => { - if (!(user.teams).includes(this.id)) { + this.members.map(member => + User.find(member).then(user => { + if (!user.teams.includes(this.id)) { user.teams.push(this.id) return user.save() } - }) - }) + }), + ), ) return super.save() } - async delete () { + async delete() { await Promise.all( - this.members.map(member => { - return User.find(member).then(user => { + this.members.map(member => + User.find(member).then(user => { if (user.teams.includes(this.id)) { user.teams = _.without(user.teams, this.id) return user.save() } - }) - }) + }), + ), ) return super.delete() diff --git a/packages/server/src/models/User.js b/packages/server/src/models/User.js index bcec03cc7ce8555e869a0e4ddbb675dd21a620bf..c23c02fc4d01f3ee0721c4513d64f0099dd153f9 100644 --- a/packages/server/src/models/User.js +++ b/packages/server/src/models/User.js @@ -1,4 +1,3 @@ -'use strict' const Model = require('./Model') const ConflictError = require('../errors/ConflictError') const bcrypt = require('bcrypt') @@ -9,7 +8,7 @@ const config = require('config') const BCRYPT_COST = config.util.getEnv('NODE_ENV') === 'test' ? 1 : 12 class User extends Model { - constructor (properties) { + constructor(properties) { super(properties) this.type = 'user' @@ -17,11 +16,11 @@ class User extends Model { this.username = properties.username } - toJSON () { + toJSON() { return omit(this, ['passwordHash']) } - async save () { + async save() { if (this.password) { this.passwordHash = await this.hashPassword(this.password) delete this.password @@ -30,15 +29,15 @@ class User extends Model { return Model.prototype.save.call(this) } - validPassword (password) { + validPassword(password) { return bcrypt.compare(password, this.passwordHash) } - hashPassword (password) { + hashPassword(password) { return bcrypt.hash(password, BCRYPT_COST) } - async isUniq (user) { + async isUniq(user) { let result const swallowNotFound = e => { @@ -58,24 +57,22 @@ class User extends Model { } } - static findByEmail (email) { - return this.findByField('email', email).then(function (users) { - return users[0] - }) + static findByEmail(email) { + return this.findByField('email', email).then(users => users[0]) } - static findByUsername (username) { - return this.findByField('username', username).then(function (users) { - return users[0] - }) + static findByUsername(username) { + return this.findByField('username', username).then(users => users[0]) } // For API display/JSON purposes only - static ownersWithUsername (object) { - return Promise.all(object.owners.map(async ownerId => { - const owner = await this.find(ownerId) - return pick(owner, ['id', 'username']) - })) + static ownersWithUsername(object) { + return Promise.all( + object.owners.map(async ownerId => { + const owner = await this.find(ownerId) + return pick(owner, ['id', 'username']) + }), + ) } } diff --git a/packages/server/src/models/index.js b/packages/server/src/models/index.js index bb6b5bd23293f4dddc0ee29b83812e02cbe02034..295dd2e1489ea8c2d6690c9392a445ab50accccb 100644 --- a/packages/server/src/models/index.js +++ b/packages/server/src/models/index.js @@ -2,5 +2,5 @@ module.exports = { Collection: require('./Collection'), Fragment: require('./Fragment'), User: require('./User'), - Team: require('./Team') + Team: require('./Team'), } diff --git a/packages/server/src/models/schema.js b/packages/server/src/models/schema.js index 0af88f69bc105461a925e77f9781e3d3af3f37aa..e4db5a2399c53d96f436e4d707a40acc57c2edef 100644 --- a/packages/server/src/models/schema.js +++ b/packages/server/src/models/schema.js @@ -1,42 +1,40 @@ -'use strict' - global.db = require('../db')() -module.exports = function () { +module.exports = function() { if (!db.rel) { return db.setSchema([ { singular: 'collection', plural: 'collections', relations: { - fragments: {hasMany: 'fragment'}, - owners: {hasMany: 'user'} - } + fragments: { hasMany: 'fragment' }, + owners: { hasMany: 'user' }, + }, }, { singular: 'fragment', plural: 'fragments', relations: { - collection: {belongsTo: 'collection'}, - owners: {hasMany: 'user'} - } + collection: { belongsTo: 'collection' }, + owners: { hasMany: 'user' }, + }, }, { singular: 'user', plural: 'users', relations: { - collections: {hasMany: 'collection'}, - fragments: {hasMany: 'fragment'}, - teams: {hasMany: 'team'} - } + collections: { hasMany: 'collection' }, + fragments: { hasMany: 'fragment' }, + teams: { hasMany: 'team' }, + }, }, { singular: 'team', plural: 'teams', relations: { - members: {hasMany: 'user'} - } - } + members: { hasMany: 'user' }, + }, + }, ]) } } diff --git a/packages/server/src/models/validations.js b/packages/server/src/models/validations.js index 2fcf36548496310e4f7eff1ac1339efe9797bf11..bf1b47127d5920bc472ce32be077fac1af13aebe 100644 --- a/packages/server/src/models/validations.js +++ b/packages/server/src/models/validations.js @@ -1,5 +1,3 @@ -'use strict' - // This module is used for communicating validation requirements to the // client and server, it sits in the middle. @@ -8,17 +6,21 @@ const Joi = require('joi') // These are fixed/required validations, they are combined with configurable // validations later -let validations = { +const validations = { fragment: { - id: Joi.string().guid().required(), + id: Joi.string() + .guid() + .required(), type: Joi.string().required(), fragmentType: Joi.string().required(), rev: Joi.string(), fragments: Joi.array().items(Joi.string().guid()), - owners: Joi.array().items(Joi.string().guid()) + owners: Joi.array().items(Joi.string().guid()), }, collection: { - id: Joi.string().guid().required(), + id: Joi.string() + .guid() + .required(), type: Joi.string().required(), rev: Joi.string(), owners: Joi.array().items(Joi.string().guid()), @@ -27,15 +29,21 @@ let validations = { // a fragment ID Joi.string(), // or a fragment object - Joi.object({ type: Joi.string().valid('fragment') }).unknown(true) - ) - ) + Joi.object({ type: Joi.string().valid('fragment') }).unknown(true), + ), + ), }, user: { - id: Joi.string().guid().required(), + id: Joi.string() + .guid() + .required(), type: Joi.string(), - username: Joi.string().alphanum().required(), - email: Joi.string().email().required(), + username: Joi.string() + .alphanum() + .required(), + email: Joi.string() + .email() + .required(), passwordHash: Joi.string().required(), admin: Joi.boolean(), rev: Joi.string(), @@ -43,20 +51,22 @@ let validations = { collections: Joi.array().items(Joi.string().guid()), teams: Joi.array().items(Joi.string().guid()), passwordResetToken: Joi.string(), - passwordResetTimestamp: Joi.date().timestamp() + passwordResetTimestamp: Joi.date().timestamp(), }, team: { - id: Joi.string().guid().required(), + id: Joi.string() + .guid() + .required(), type: Joi.string().required(), name: Joi.string().required(), object: Joi.object().required(), teamType: Joi.object().required(), rev: Joi.string(), - members: Joi.array().items(Joi.string().guid()) - } + members: Joi.array().items(Joi.string().guid()), + }, } -let allValidations = function (type, appValidations) { +const allValidations = function(type, appValidations) { let appValidationsForType = {} if (appValidations && appValidations[type]) { @@ -64,18 +74,21 @@ let allValidations = function (type, appValidations) { } if (Array.isArray(appValidationsForType)) { - const alternatives = appValidationsForType.map(alternative => ({...validations[type], ...alternative})) + const alternatives = appValidationsForType.map(alternative => ({ + ...validations[type], + ...alternative, + })) return Joi.alternatives().try(...alternatives) } - return Joi.object().keys({...validations[type], ...appValidationsForType}) + return Joi.object().keys({ ...validations[type], ...appValidationsForType }) } -module.exports = function (appValidations) { +module.exports = function(appValidations) { return { fragment: allValidations('fragment', appValidations), collection: allValidations('collection', appValidations), user: allValidations('user', appValidations), - team: allValidations('team', appValidations) + team: allValidations('team', appValidations), } } diff --git a/packages/server/src/routes/api.js b/packages/server/src/routes/api.js index 7237dd7fdf7591b0485bba2faf57551f0d146a35..0e63d524873fe1cd1667b357694153ff55919816 100644 --- a/packages/server/src/routes/api.js +++ b/packages/server/src/routes/api.js @@ -1,28 +1,33 @@ const express = require('express') const helmet = require('helmet') -const api = express.Router({mergeParams: true}) +const api = express.Router({ mergeParams: true }) api.use(helmet()) // Collections const collections = require('./api_collections') + api.use(collections) // Fragments const fragments = require('./api_fragments') + api.use(fragments) // File upload API const upload = require('./api_upload') + api.use(upload) // Users API const users = require('./api_users') + api.use(users) // Teams const teams = require('./api_teams') + api.use(teams) module.exports = api diff --git a/packages/server/src/routes/api_collections.js b/packages/server/src/routes/api_collections.js index 787c68c8de8d51294924aab02c777d7b4b65fd46..ef50f7bb4068916bfe905cddda7b1f00cc43b9b9 100644 --- a/packages/server/src/routes/api_collections.js +++ b/packages/server/src/routes/api_collections.js @@ -1,5 +1,3 @@ -'use strict' - const STATUS = require('http-status-codes') const Collection = require('../models/Collection') @@ -7,6 +5,7 @@ const Team = require('../models/Team') const User = require('../models/User') const express = require('express') + const api = express.Router() const sse = require('pubsweet-sse') @@ -16,12 +15,15 @@ const { buildChangeData, fieldSelector, getTeams, - applyPermissionFilter + applyPermissionFilter, } = require('./util') const passport = require('passport') + const authBearer = passport.authenticate('bearer', { session: false }) -const authBearerAndPublic = passport.authenticate(['bearer', 'anonymous'], { session: false }) +const authBearerAndPublic = passport.authenticate(['bearer', 'anonymous'], { + session: false, +}) // List collections api.get('/collections', authBearerAndPublic, async (req, res, next) => { @@ -30,15 +32,19 @@ api.get('/collections', authBearerAndPublic, async (req, res, next) => { const filteredCollections = await applyPermissionFilter({ req, target: req.route, - filterable: collections + filterable: collections, }) - const collectionsWithSelectedFields = (await Promise.all(filteredCollections.map(async collection => { - collection.owners = await User.ownersWithUsername(collection) - const properties = await applyPermissionFilter({ req, target: collection }) - return fieldSelector(req)(properties) - }))) - .filter(createFilterFromQuery(req.query)) + const collectionsWithSelectedFields = (await Promise.all( + filteredCollections.map(async collection => { + collection.owners = await User.ownersWithUsername(collection) + const properties = await applyPermissionFilter({ + req, + target: collection, + }) + return fieldSelector(req)(properties) + }), + )).filter(createFilterFromQuery(req.query)) res.status(STATUS.OK).json(collectionsWithSelectedFields) } catch (err) { @@ -52,7 +58,7 @@ api.post('/collections', authBearer, async (req, res, next) => { const properties = await applyPermissionFilter({ req, target: req.route, - filterable: req.body + filterable: req.body, }) const collection = new Collection(properties) @@ -71,17 +77,24 @@ api.post('/collections', authBearer, async (req, res, next) => { }) // Retrieve a collection -api.get('/collections/:collectionId', authBearerAndPublic, async (req, res, next) => { - try { - const collection = await Collection.find(req.params.collectionId) - collection.owners = await User.ownersWithUsername(collection) - const properties = await applyPermissionFilter({ req, target: collection }) - - return res.status(STATUS.OK).json(properties) - } catch (err) { - next(err) - } -}) +api.get( + '/collections/:collectionId', + authBearerAndPublic, + async (req, res, next) => { + try { + const collection = await Collection.find(req.params.collectionId) + collection.owners = await User.ownersWithUsername(collection) + const properties = await applyPermissionFilter({ + req, + target: collection, + }) + + return res.status(STATUS.OK).json(properties) + } catch (err) { + next(err) + } + }, +) // Update a collection api.patch('/collections/:collectionId', authBearer, async (req, res, next) => { @@ -90,7 +103,7 @@ api.patch('/collections/:collectionId', authBearer, async (req, res, next) => { const properties = await applyPermissionFilter({ req, target: collection, - filterable: req.body + filterable: req.body, }) await collection.updateProperties(properties) @@ -99,7 +112,10 @@ api.patch('/collections/:collectionId', authBearer, async (req, res, next) => { const updated = buildChangeData(properties, collection) res.status(STATUS.OK).json(updated) - sse.send({ action: 'collection:patch', data: { collection: objectId(collection), updated } }) + sse.send({ + action: 'collection:patch', + data: { collection: objectId(collection), updated }, + }) } catch (err) { next(err) } @@ -116,36 +132,42 @@ api.delete('/collections/:collectionId', authBearer, async (req, res, next) => { await collection.delete() res.status(STATUS.OK).json(output) - sse.send({ action: 'collection:delete', data: { collection: objectId(collection) } }) + sse.send({ + action: 'collection:delete', + data: { collection: objectId(collection) }, + }) } catch (err) { next(err) } }) // Retrieve teams for a collection -api.get('/collections/:collectionId/teams', authBearerAndPublic, async (req, res, next) => { - const collection = await Collection.find(req.params.collectionId) - await applyPermissionFilter({ req, target: collection }) - - try { - const teams = (await getTeams({ - req, - Team, - id: collection.id, - type: 'collection' - })) - .filter(createFilterFromQuery(req.query)) - - res.status(STATUS.OK).json(teams) - } catch (err) { - next(err) - } -}) - +api.get( + '/collections/:collectionId/teams', + authBearerAndPublic, + async (req, res, next) => { + const collection = await Collection.find(req.params.collectionId) + await applyPermissionFilter({ req, target: collection }) + + try { + const teams = (await getTeams({ + req, + Team, + id: collection.id, + type: 'collection', + })).filter(createFilterFromQuery(req.query)) + + res.status(STATUS.OK).json(teams) + } catch (err) { + next(err) + } + }, +) // Teams // TODO: Nested teams API to be deprecated const teams = require('./api_teams') + api.use('/collections/:collectionId/', teams) module.exports = api diff --git a/packages/server/src/routes/api_fragments.js b/packages/server/src/routes/api_fragments.js index 2a970ebdec4839c6a4fdae81f3e044cfaf41b16c..a8d9a7e4410ac26d86ff9c94cd7730f34955fb9b 100644 --- a/packages/server/src/routes/api_fragments.js +++ b/packages/server/src/routes/api_fragments.js @@ -1,4 +1,3 @@ -'use strict' const User = require('../models/User') const Collection = require('../models/Collection') const Team = require('../models/Team') @@ -9,6 +8,7 @@ const AuthorizationError = require('../errors/AuthorizationError') const STATUS = require('http-status-codes') const express = require('express') + const api = express.Router() const passport = require('passport') const sse = require('pubsweet-sse') @@ -20,174 +20,219 @@ const { authorizationError, getTeams, applyPermissionFilter, - getFragment + getFragment, } = require('./util') + const authBearer = passport.authenticate('bearer', { session: false }) -const authBearerAndPublic = passport.authenticate(['bearer', 'anonymous'], { session: false }) +const authBearerAndPublic = passport.authenticate(['bearer', 'anonymous'], { + session: false, +}) // Create a fragment and update the collection with the fragment -api.post('/collections/:collectionId/fragments', authBearer, async (req, res, next) => { - try { - const collection = await Collection.find(req.params.collectionId) - - const object = { - path: req.route.path, - collection, - fragment: req.body - } - - let filteredProperties = await applyPermissionFilter({ - req, - target: object, - filterable: req.body - }) - - const fragment = new Fragment(filteredProperties) - - fragment.setOwners([req.user]) - await fragment.save() - - collection.addFragment(fragment) - await collection.save() - - fragment.owners = await User.ownersWithUsername(fragment) - - res.status(STATUS.CREATED).json(fragment) - sse.send({ action: 'fragment:create', data: { collection: objectId(collection), fragment } }) - } catch (err) { - next(err) - } -}) +api.post( + '/collections/:collectionId/fragments', + authBearer, + async (req, res, next) => { + try { + const collection = await Collection.find(req.params.collectionId) + + const object = { + path: req.route.path, + collection, + fragment: req.body, + } -// Get all fragments -api.get('/collections/:collectionId/fragments', authBearerAndPublic, async (req, res, next) => { - try { - const collection = await Collection.find(req.params.collectionId) - let fragments = await collection.getFragments() + const filteredProperties = await applyPermissionFilter({ + req, + target: object, + filterable: req.body, + }) - // Filter fragments and their properties - fragments = await Promise.all(fragments.map(async fragment => { - try { - return await applyPermissionFilter({ req, target: fragment }) - } catch (e) { - if (e instanceof AuthorizationError) { - return undefined - } + const fragment = new Fragment(filteredProperties) - throw e - } - })) + fragment.setOwners([req.user]) + await fragment.save() - fragments = fragments.filter(fragment => fragment !== undefined) - .filter(createFilterFromQuery(req.query)) + collection.addFragment(fragment) + await collection.save() - // Decorate owners with usernames - await Promise.all(fragments.map(async fragment => { fragment.owners = await User.ownersWithUsername(fragment) - })) - fragments = fragments.map(fieldSelector(req)) + res.status(STATUS.CREATED).json(fragment) + sse.send({ + action: 'fragment:create', + data: { collection: objectId(collection), fragment }, + }) + } catch (err) { + next(err) + } + }, +) - return res.status(STATUS.OK).json(fragments) - } catch (err) { - next(err) - } -}) +// Get all fragments +api.get( + '/collections/:collectionId/fragments', + authBearerAndPublic, + async (req, res, next) => { + try { + const collection = await Collection.find(req.params.collectionId) + let fragments = await collection.getFragments() + + // Filter fragments and their properties + fragments = await Promise.all( + fragments.map(async fragment => { + try { + return await applyPermissionFilter({ req, target: fragment }) + } catch (e) { + if (e instanceof AuthorizationError) { + return undefined + } + + throw e + } + }), + ) + + fragments = fragments + .filter(fragment => fragment !== undefined) + .filter(createFilterFromQuery(req.query)) + + // Decorate owners with usernames + await Promise.all( + fragments.map(async fragment => { + fragment.owners = await User.ownersWithUsername(fragment) + }), + ) + + fragments = fragments.map(fieldSelector(req)) + + return res.status(STATUS.OK).json(fragments) + } catch (err) { + next(err) + } + }, +) // Retrieve a fragment -api.get('/collections/:collectionId/fragments/:fragmentId', authBearerAndPublic, async (req, res, next) => { - try { - const fragment = await getFragment({ req, Collection, Fragment }) - fragment.owners = await User.ownersWithUsername(fragment) - const properties = await applyPermissionFilter({ req, target: fragment }) +api.get( + '/collections/:collectionId/fragments/:fragmentId', + authBearerAndPublic, + async (req, res, next) => { + try { + const fragment = await getFragment({ req, Collection, Fragment }) + fragment.owners = await User.ownersWithUsername(fragment) + const properties = await applyPermissionFilter({ req, target: fragment }) - return res.status(STATUS.OK).json(properties) - } catch (err) { - res.status(STATUS.NOT_FOUND).json(err.message) - } -}) + return res.status(STATUS.OK).json(properties) + } catch (err) { + res.status(STATUS.NOT_FOUND).json(err.message) + } + }, +) // Update a fragment -api.patch('/collections/:collectionId/fragments/:fragmentId', authBearer, async (req, res, next) => { - try { - const fragment = await getFragment({ req, Collection, Fragment }) - const properties = await applyPermissionFilter({ - req, - target: fragment, - filterable: req.body - }) - - await fragment.updateProperties(properties) - await fragment.save() - fragment.owners = await User.ownersWithUsername(fragment) +api.patch( + '/collections/:collectionId/fragments/:fragmentId', + authBearer, + async (req, res, next) => { + try { + const fragment = await getFragment({ req, Collection, Fragment }) + const properties = await applyPermissionFilter({ + req, + target: fragment, + filterable: req.body, + }) + + await fragment.updateProperties(properties) + await fragment.save() + fragment.owners = await User.ownersWithUsername(fragment) - const update = buildChangeData(properties, fragment) + const update = buildChangeData(properties, fragment) - res.status(STATUS.OK).json(update) - sse.send({ action: 'fragment:patch', data: { fragment: objectId(fragment), update } }) - } catch (err) { - next(err) - } -}) + res.status(STATUS.OK).json(update) + sse.send({ + action: 'fragment:patch', + data: { fragment: objectId(fragment), update }, + }) + } catch (err) { + next(err) + } + }, +) // Delete a fragment -api.delete('/collections/:collectionId/fragments/:fragmentId', authBearer, async (req, res, next) => { - try { - const collection = await Collection.find(req.params.collectionId) - const fragment = await getFragment({ req, Collection, Fragment }) - await applyPermissionFilter({ req, target: fragment }) - - await fragment.delete() - collection.removeFragment(fragment) - await collection.save() - - res.status(STATUS.OK).json(fragment) - sse.send({ action: 'fragment:delete', data: { collection: objectId(collection), fragment } }) - } catch (err) { - next(err) - } -}) +api.delete( + '/collections/:collectionId/fragments/:fragmentId', + authBearer, + async (req, res, next) => { + try { + const collection = await Collection.find(req.params.collectionId) + const fragment = await getFragment({ req, Collection, Fragment }) + await applyPermissionFilter({ req, target: fragment }) + + await fragment.delete() + collection.removeFragment(fragment) + await collection.save() + + res.status(STATUS.OK).json(fragment) + sse.send({ + action: 'fragment:delete', + data: { collection: objectId(collection), fragment }, + }) + } catch (err) { + next(err) + } + }, +) // Retrieve teams for a fragment -api.get('/collections/:collectionId/fragments/:fragmentId/teams', authBearerAndPublic, async (req, res, next) => { - try { - const fragment = await getFragment({ req, Collection, Fragment }) - await applyPermissionFilter({ req, target: fragment }) - - const teams = (await getTeams({ - req, - Team, - id: fragment.id, - type: 'fragment' - })) - .filter(createFilterFromQuery(req.query)) - - - res.status(STATUS.OK).json(teams) - } catch (err) { - next(err) - } -}) +api.get( + '/collections/:collectionId/fragments/:fragmentId/teams', + authBearerAndPublic, + async (req, res, next) => { + try { + const fragment = await getFragment({ req, Collection, Fragment }) + await applyPermissionFilter({ req, target: fragment }) + + const teams = (await getTeams({ + req, + Team, + id: fragment.id, + type: 'fragment', + })).filter(createFilterFromQuery(req.query)) + + res.status(STATUS.OK).json(teams) + } catch (err) { + next(err) + } + }, +) // Get all fragments api.get('/fragments', authBearerAndPublic, async (req, res, next) => { try { - const fragments = (await Fragment.all()) - .filter(createFilterFromQuery(req.query)) + const fragments = (await Fragment.all()).filter( + createFilterFromQuery(req.query), + ) // Filter fragments and their properties const propertyFilter = fieldSelector(req) - const filteredFragments = await Promise.all(fragments.map(async fragment => { - try { - return await applyPermissionFilter({ req, target: propertyFilter(fragment) }) - } catch (e) { - if (e instanceof AuthorizationError) { - return undefined + const filteredFragments = await Promise.all( + fragments.map(async fragment => { + try { + return await applyPermissionFilter({ + req, + target: propertyFilter(fragment), + }) + } catch (e) { + if (e instanceof AuthorizationError) { + return undefined + } + + throw e } - - throw e - } - })) + }), + ) res.status(STATUS.OK).json(filteredFragments) } catch (err) { @@ -199,7 +244,7 @@ api.post('/fragments', authBearer, async (req, res, next) => { try { const permission = await authsome.can(req.user, req.method, { path: req.route.path, - fragment: req.body + fragment: req.body, }) if (!permission) { @@ -226,25 +271,29 @@ api.post('/fragments', authBearer, async (req, res, next) => { }) // Retrieve a fragment -api.get('/fragments/:fragmentId', authBearerAndPublic, async (req, res, next) => { - try { - let fragment = await Fragment.find(req.params.fragmentId) - let permission = await authsome.can(req.user, req.method, fragment) +api.get( + '/fragments/:fragmentId', + authBearerAndPublic, + async (req, res, next) => { + try { + const fragment = await Fragment.find(req.params.fragmentId) + const permission = await authsome.can(req.user, req.method, fragment) + + if (!permission) { + throw authorizationError(req.user, req.method, fragment) + } - if (!permission) { - throw authorizationError(req.user, req.method, fragment) + return Fragment.find(req.params.fragmentId).then(fragment => { + if (permission.filter) { + fragment = permission.filter(fragment) + } + return res.status(STATUS.OK).json(fragment) + }) + } catch (err) { + res.status(STATUS.NOT_FOUND).json(err.message) } - - return Fragment.find(req.params.fragmentId).then(fragment => { - if (permission.filter) { - fragment = permission.filter(fragment) - } - return res.status(STATUS.OK).json(fragment) - }) - } catch (err) { - res.status(STATUS.NOT_FOUND).json(err.message) - } -}) + }, +) // Update a fragment api.patch('/fragments/:fragmentId', authBearer, async (req, res, next) => { @@ -267,7 +316,10 @@ api.patch('/fragments/:fragmentId', authBearer, async (req, res, next) => { const update = buildChangeData(req.body, fragment) res.status(STATUS.OK).json(update) - sse.send({ action: 'fragment:patch', data: { fragment: objectId(fragment), update } }) + sse.send({ + action: 'fragment:patch', + data: { fragment: objectId(fragment), update }, + }) } catch (err) { next(err) } @@ -293,19 +345,23 @@ api.delete('/fragments/:fragmentId', authBearer, async (req, res, next) => { }) // Retrieve teams for a fragment -api.get('/fragments/:fragmentId/teams', authBearerAndPublic, async (req, res, next) => { - try { - let teams = (await getTeams({ - req: req, - Team: Team, - id: req.params.fragmentId, - type: 'fragment' })) - .filter(createFilterFromQuery(req.query)) - - res.status(STATUS.OK).json(teams) - } catch (err) { - next(err) - } -}) +api.get( + '/fragments/:fragmentId/teams', + authBearerAndPublic, + async (req, res, next) => { + try { + const teams = (await getTeams({ + req, + Team, + id: req.params.fragmentId, + type: 'fragment', + })).filter(createFilterFromQuery(req.query)) + + res.status(STATUS.OK).json(teams) + } catch (err) { + next(err) + } + }, +) module.exports = api diff --git a/packages/server/src/routes/api_teams.js b/packages/server/src/routes/api_teams.js index 50354943553538b316fd85105d46511b8d3387ad..1dfe11c65b6c8f1849a5fa788849a281ecb18a63 100644 --- a/packages/server/src/routes/api_teams.js +++ b/packages/server/src/routes/api_teams.js @@ -1,4 +1,3 @@ -'use strict' const STATUS = require('http-status-codes') const express = require('express') const passport = require('passport') @@ -8,22 +7,20 @@ const Team = require('../models/Team') const { createFilterFromQuery, authorizationError } = require('./util') const authBearer = passport.authenticate('bearer', { session: false }) -const api = express.Router({mergeParams: true}) +const api = express.Router({ mergeParams: true }) api.get('/teams', authBearer, async (req, res, next) => { try { - const permission = await authsome.can( - req.user, - req.method, - {path: req.path, params: req.params} - ) + const permission = await authsome.can(req.user, req.method, { + path: req.path, + params: req.params, + }) if (!permission) { throw authorizationError(req.user, req.method, req) } - const teams = (await Team.all()) - .filter(createFilterFromQuery(req.query)) + const teams = (await Team.all()).filter(createFilterFromQuery(req.query)) res.status(STATUS.OK).json(teams) } catch (err) { diff --git a/packages/server/src/routes/api_upload.js b/packages/server/src/routes/api_upload.js index 36b2aca80c3e67ba053b27d92844fec911eb435d..1f651aec917a16202a71ad79a9e5877ad9aaea90 100644 --- a/packages/server/src/routes/api_upload.js +++ b/packages/server/src/routes/api_upload.js @@ -3,28 +3,29 @@ const crypto = require('crypto') const multer = require('multer') const passport = require('passport') const express = require('express') + const api = express.Router() -const authBearer = passport.authenticate('bearer', {session: false}) +const authBearer = passport.authenticate('bearer', { session: false }) const storage = multer.diskStorage({ destination: 'uploads/', - filename: function (req, file, cb) { - crypto.pseudoRandomBytes(16, function (err, raw) { + filename(req, file, cb) { + crypto.pseudoRandomBytes(16, (err, raw) => { if (err) return cb(err) cb(null, raw.toString('hex') + path.extname(file.originalname)) }) - } + }, }) const upload = multer({ - storage: storage, - limits: {fileSize: 10000000, files: 1} + storage, + limits: { fileSize: 10000000, files: 1 }, }) -api.post('/upload', authBearer, upload.single('file'), (req, res, next) => { - return res.send('/' + req.file.path) -}) +api.post('/upload', authBearer, upload.single('file'), (req, res, next) => + res.send(`/${req.file.path}`), +) module.exports = api diff --git a/packages/server/src/routes/api_users.js b/packages/server/src/routes/api_users.js index 0dcddb53f6a797d1237c512434429bb76d1064c9..461cc4792a2b6ea81960168a1755457645720247 100644 --- a/packages/server/src/routes/api_users.js +++ b/packages/server/src/routes/api_users.js @@ -1,5 +1,3 @@ -'use strict' - const STATUS = require('http-status-codes') const passport = require('passport') const express = require('express') @@ -13,28 +11,29 @@ const Team = require('../models/Team') const AuthorizationError = require('../errors/AuthorizationError') const ValidationError = require('../errors/ValidationError') -const authLocal = passport.authenticate('local', { failWithError: true, session: false }) +const authLocal = passport.authenticate('local', { + failWithError: true, + session: false, +}) const authBearer = passport.authenticate('bearer', { session: false }) const api = express.Router() const authentication = require('../authentication') // Issue a token -api.post('/users/authenticate', authLocal, (req, res) => { - return res.status( - STATUS.CREATED - ).json( - Object.assign({ token: authentication.token.create(req.user) }, req.user) - ) -}) +api.post('/users/authenticate', authLocal, (req, res) => + res + .status(STATUS.CREATED) + .json( + Object.assign({ token: authentication.token.create(req.user) }, req.user), + ), +) // Verify a token api.get('/users/authenticate', authBearer, async (req, res, next) => { try { const user = await User.find(req.user) user.token = req.authInfo.token - user.teams = await Promise.all( - user.teams.map((teamId) => Team.find(teamId)) - ) + user.teams = await Promise.all(user.teams.map(teamId => Team.find(teamId))) return res.status(STATUS.OK).json(user) } catch (err) { next(err) @@ -63,10 +62,9 @@ api.get('/users', authBearer, async (req, res, next) => { throw authorizationError(req.user, req.method, req.path) } - const users = (await User.all()) - .filter(createFilterFromQuery(req.query)) + const users = (await User.all()).filter(createFilterFromQuery(req.query)) - return res.status(STATUS.OK).json({users: users}) + return res.status(STATUS.OK).json({ users }) } catch (err) { next(err) } diff --git a/packages/server/src/routes/index.js b/packages/server/src/routes/index.js index a55a2d974ac7046c52ed6c83f1ece1afb8d85684..8ce816defeff5bd469c24dee3ecc1c4d4788472f 100644 --- a/packages/server/src/routes/index.js +++ b/packages/server/src/routes/index.js @@ -1,14 +1,16 @@ const express = require('express') -const router = express.Router({mergeParams: true}) + +const router = express.Router({ mergeParams: true }) const path = require('path') const config = require('config') -const filename = config.util.getEnv('NODE_ENV') === 'production' - ? path.join('.', '_build', 'assets') - : path.join('.', 'app') +const filename = + config.util.getEnv('NODE_ENV') === 'production' + ? path.join('.', '_build', 'assets') + : path.join('.', 'app') -router.get('*', (req, res, next) => { - return res.sendFile('index.html', { root: filename }) -}) +router.get('*', (req, res, next) => + res.sendFile('index.html', { root: filename }), +) module.exports = router diff --git a/packages/server/src/routes/util.js b/packages/server/src/routes/util.js index 72b60f58a718fa53fc6c053bed981498286dd986..eaa84da577ef87bd1eebb1899063d6948759b749 100644 --- a/packages/server/src/routes/util.js +++ b/packages/server/src/routes/util.js @@ -4,58 +4,62 @@ const NotFoundError = require('../errors/NotFoundError') const authsome = require('../helpers/authsome') const Util = {} +;(Util.authorizationError = (username, operation, object) => { + username = username || 'public' + const msg = `User ${username} is not allowed to ${operation} ${object}` + return new AuthorizationError(msg) +}), + // Build an object containing only the id + (Util.objectId = object => ({ id: object.id })), + // Build an object containing only the fields of `output` that were in `input` + // TODO: build a real diff, in case other fields were updated indirectly? + (Util.buildChangeData = (input, output) => { + const data = {} + + Object.keys(input).forEach(key => { + // TODO: compare and only add if changed? + data[key] = output[key] + }) -Util.authorizationError = (username, operation, object) => { - username = username || 'public' - const msg = `User ${username} is not allowed to ${operation} ${object}` - return new AuthorizationError(msg) - }, - -// Build an object containing only the id -Util.objectId = object => ({ id: object.id }), - -// Build an object containing only the fields of `output` that were in `input` -// TODO: build a real diff, in case other fields were updated indirectly? -Util.buildChangeData = (input, output) => { - const data = {} - - Object.keys(input).forEach(key => { - // TODO: compare and only add if changed? - data[key] = output[key] + return data }) - return data -} - Util.createFilterFromQuery = query => { const filterPaths = _.difference(_.keys(query), ['fields']) - return (item) => { - return filterPaths.every(filterPath => { - return _.has(item, filterPath) && _.get(item, filterPath) === query[filterPath] - }) - } + return item => + filterPaths.every( + filterPath => + _.has(item, filterPath) && + _.get(item, filterPath) === query[filterPath], + ) } Util.fieldSelector = req => { const fields = req.query.fields ? req.query.fields.split(/\s*,\s*/) : null - return item => fields ? _.pick(item, fields.concat('id', 'rev')) : item + return item => (fields ? _.pick(item, fields.concat('id', 'rev')) : item) } -Util.getTeams = async (opts) => { +Util.getTeams = async opts => { let teams try { teams = await opts.Team.findByField({ 'object.id': opts.id, - 'object.type': opts.type + 'object.type': opts.type, }) - teams = await Promise.all(teams.map(async team => { - let permission = await authsome.can(opts.req.user, opts.req.method, team) - if (permission) { - return team - } - })) + teams = await Promise.all( + teams.map(async team => { + const permission = await authsome.can( + opts.req.user, + opts.req.method, + team, + ) + if (permission) { + return team + } + }), + ) teams = teams.filter(team => team !== undefined) } catch (err) { @@ -85,7 +89,9 @@ Util.getFragment = async opts => { const collection = await opts.Collection.find(opts.req.params.collectionId) const fragmentId = opts.req.params.fragmentId if (!collection.fragments.includes(fragmentId)) { - throw new NotFoundError(`collection ${collection.id} does not contain fragment ${fragmentId}`) + throw new NotFoundError( + `collection ${collection.id} does not contain fragment ${fragmentId}`, + ) } return opts.Fragment.find(fragmentId) @@ -105,19 +111,19 @@ Util.getFragment = async opts => { * * @returns {Promise} The (possibly filtered) target, if permission is granted */ -Util.applyPermissionFilter = async (opts) => { +Util.applyPermissionFilter = async opts => { const permission = await authsome.can( - opts.req.user, opts.req.method, opts.target) + opts.req.user, + opts.req.method, + opts.target, + ) if (!permission) { - throw Util.authorizationError( - opts.req.user, opts.req.method, opts.target - ) + throw Util.authorizationError(opts.req.user, opts.req.method, opts.target) } const object = opts.filterable || opts.target return permission.filter ? permission.filter(object) : object } - module.exports = Util diff --git a/packages/server/src/setup-base.js b/packages/server/src/setup-base.js index 13e6f6315322529bf3d0ce5a41090b84f7a5beb6..c6bb5537099a427858416a346d7ffee812a2ba3e 100644 --- a/packages/server/src/setup-base.js +++ b/packages/server/src/setup-base.js @@ -1,19 +1,17 @@ -'use strict' - const Collection = require('./models/Collection') const User = require('./models/User') const logger = require('@pubsweet/logger') class Setup { - static async setup (user, collection) { + static async setup(user, collection) { logger.info('Starting setup') let admin = new User({ username: user.username, email: user.email, password: user.password, - admin: true + admin: true, }) admin = await admin.save() @@ -26,7 +24,7 @@ class Setup { return { user: admin, - collection: collection + collection, } } } diff --git a/packages/server/src/setup.js b/packages/server/src/setup.js index 329e400b63484283841a8ecfaad930b8754b1765..bf20d85dbebf09fc058f285cab9618eaf63e23be 100755 --- a/packages/server/src/setup.js +++ b/packages/server/src/setup.js @@ -1,5 +1,4 @@ #!/usr/bin/env node -'use strict' const Setup = require('./setup-base.js') @@ -20,40 +19,37 @@ prompt.get( { properties: { username: { - description: colors.magenta("What is the admin's username?") + description: colors.magenta("What is the admin's username?"), }, email: { - description: colors.yellow("What is the admin's email?") + description: colors.yellow("What is the admin's email?"), }, password: { - description: colors.blue("What is the admin's password?") + description: colors.blue("What is the admin's password?"), }, collectionTitle: { - description: colors.cyan("What is the collection's title?") - } - } + description: colors.cyan("What is the collection's title?"), + }, + }, }, (err, result) => { if (err) return logger.info(err) logger.info('Received the following answers:') - logger.info(' username: ' + result.username) - logger.info(' email: ' + result.email) - logger.info(' password: ' + result.password) - logger.info(' collection: ' + result.collectionTitle) + logger.info(` username: ${result.username}`) + logger.info(` email: ${result.email}`) + logger.info(` password: ${result.password}`) + logger.info(` collection: ${result.collectionTitle}`) // Setup - let admin = { + const admin = { username: result.username, email: result.email, - password: result.password + password: result.password, } - Setup.setup( - admin, - {title: result.collectionTitle} - ).then( - () => { logger.info(colors.rainbow('Your PubSweet is now ready!')) } - ) - } + Setup.setup(admin, { title: result.collectionTitle }).then(() => { + logger.info(colors.rainbow('Your PubSweet is now ready!')) + }) + }, ) diff --git a/packages/server/src/start-server.js b/packages/server/src/start-server.js index 20d4239dc35a7f2503931192012b1f4222a4ba82..24b628432be88a060f2a38c51fb52f1b7cf75bbd 100644 --- a/packages/server/src/start-server.js +++ b/packages/server/src/start-server.js @@ -4,7 +4,10 @@ const Promise = require('bluebird') const logger = require('@pubsweet/logger') const startServer = async app => { - const port = config['pubsweet-server'].port || /*deprecated:-->*/process.env.PORT || 3000 + const port = + config['pubsweet-server'].port || + /* deprecated:--> */ process.env.PORT || + 3000 app.set('port', port) const server = http.createServer(app) logger.info(`Starting HTTP server`) diff --git a/packages/server/test/api_admin_test.js b/packages/server/test/api_admin_test.js index bd61f7c097294a43a35fccb1b999cb5b2ab21f4e..1c2ab1d285e62e1c249b31e3493aac5adda141d6 100644 --- a/packages/server/test/api_admin_test.js +++ b/packages/server/test/api_admin_test.js @@ -14,71 +14,65 @@ describe('admin api', () => { let collection let fragment - beforeEach(() => { + beforeEach(() => // Create collection with admin user and one non-admin user - return cleanDB().then( - createBasicCollection - ).then( - (userAndCol) => { + cleanDB() + .then(createBasicCollection) + .then(userAndCol => { collection = userAndCol.collection - } - ).then( - () => { + }) + .then(() => { // Create another user without any roles otherUser = new User(fixtures.updatedUser) return otherUser.save() - } - ).then( - () => { + }) + .then(() => { // Create fragment and add fragment to collection fragment = new Fragment(fixtures.fragment) fragment.setOwners([otherUser.id]) - return fragment.save().then( - fragment => { - collection.addFragment(fragment) - return collection.save() - } - ) - } - ) - }) + return fragment.save().then(fragment => { + collection.addFragment(fragment) + return collection.save() + }) + }), + ) afterEach(cleanDB) - it('creates a fragment in the protected collection if authenticated', () => { - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.fragments.post({ - fragment: fixtures.fragment, collection, token - }) - ).then( - res => expect(res.body.source).toEqual(fixtures.fragment.source) - ) - }) + it('creates a fragment in the protected collection if authenticated', () => + api.users.authenticate + .post(fixtures.user) + .then(token => + api.fragments.post({ + fragment: fixtures.fragment, + collection, + token, + }), + ) + .then(res => expect(res.body.source).toEqual(fixtures.fragment.source))) - it('reads all fragments', () => { - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.fragments.get({ collection, token }) - ).then( - res => expect(res.body.length).toEqual(1) - ) - }) + it('reads all fragments', () => + api.users.authenticate + .post(fixtures.user) + .then(token => api.fragments.get({ collection, token })) + .then(res => expect(res.body).toHaveLength(1))) it('updates a fragment owned by someone else', () => { - const updatedFragment = Object.assign({}, fragment, fixtures.updatedFragment) + const updatedFragment = Object.assign( + {}, + fragment, + fixtures.updatedFragment, + ) - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.fragments.patch({ - fragmentId: fragment.id, - update: updatedFragment, - collection, - token - }).expect(STATUS.OK) + return api.users.authenticate.post(fixtures.user).then(token => + api.fragments + .patch({ + fragmentId: fragment.id, + update: updatedFragment, + collection, + token, + }) + .expect(STATUS.OK), ) }) }) diff --git a/packages/server/test/api_authenticated_test.js b/packages/server/test/api_authenticated_test.js index 4c169f0a91d99df4a2b1b4c1a9efb56d6dd7ee15..56c5ba1bc3eaf1c41e048c222cb79f624f469e62 100644 --- a/packages/server/test/api_authenticated_test.js +++ b/packages/server/test/api_authenticated_test.js @@ -9,7 +9,7 @@ const fixtures = require('./fixtures/fixtures') const Fragment = require('../src/models/Fragment') const User = require('../src/models/User') -describe('authenticated api', function () { +describe('authenticated api', () => { let otherUser let user let collection @@ -17,64 +17,55 @@ describe('authenticated api', function () { beforeEach(async () => { // Create collection with admin user and one non-admin user await dbCleaner() - ;({user, collection} = await createBasicCollection()) + ;({ user, collection } = await createBasicCollection()) // Create another user without any roles otherUser = new User(fixtures.updatedUser) await otherUser.save() }) it(`fails to create a fragment in a protected - collection if authenticated as user without permissions`, () => { - return api.users.authenticate.post( - fixtures.updatedUser - ).then( - (token) => { - return api.fragments.post({ - fragment: fixtures.fragment, collection, token - }).expect( - STATUS.FORBIDDEN - ) - } - ) - }) + collection if authenticated as user without permissions`, () => + api.users.authenticate.post(fixtures.updatedUser).then(token => + api.fragments + .post({ + fragment: fixtures.fragment, + collection, + token, + }) + .expect(STATUS.FORBIDDEN), + )) describe('a non-admin user with a contributor role', () => { - beforeEach(() => { - return setTeamForCollection( + beforeEach(() => + setTeamForCollection( [otherUser.id], collection, - fixtures.contributorTeam - ) - }) + fixtures.contributorTeam, + ), + ) - afterEach(() => { - return setTeamForCollection( - [], - collection, - fixtures.contributorTeam - ) - }) + afterEach(() => + setTeamForCollection([], collection, fixtures.contributorTeam), + ) - it('creates a fragment in a protected collection', () => { - return api.users.authenticate.post( - fixtures.updatedUser - ).then( - token => { - return api.fragments.post({ - fragment: fixtures.fragment, collection, token - }).expect( - STATUS.CREATED - ) - } - ).then( - res => { + it('creates a fragment in a protected collection', () => + api.users.authenticate + .post(fixtures.updatedUser) + .then(token => + api.fragments + .post({ + fragment: fixtures.fragment, + collection, + token, + }) + .expect(STATUS.CREATED), + ) + .then(res => { expect(res.body.owners).toContainEqual({ id: otherUser.id, - username: otherUser.username + username: otherUser.username, }) - } - ) - }) + })) describe('a fragment owned by the same user', () => { let fragment @@ -94,22 +85,17 @@ describe('authenticated api', function () { collection = await collection.save() }) - it('updates a fragment in a protected collection if an owner', () => { - return api.users.authenticate.post( - fixtures.updatedUser - ).then( - (token) => { - return api.fragments.patch({ + it('updates a fragment in a protected collection if an owner', () => + api.users.authenticate.post(fixtures.updatedUser).then(token => + api.fragments + .patch({ fragmentId: fragment.id, - update: {...fixtures.updatedFragment, rev: fragment.rev}, + update: { ...fixtures.updatedFragment, rev: fragment.rev }, collection, - token - }).expect( - STATUS.OK - ) - } - ) - }) + token, + }) + .expect(STATUS.OK), + )) }) describe('actions on a fragment owned by a different user', () => { @@ -130,87 +116,71 @@ describe('authenticated api', function () { await collection.save() }) - it('cannot read a fragment in a protected collection if it is not published', () => { - return api.users.authenticate.post( - fixtures.updatedUser - ).then( - token => api.fragments.get({ - collection: collection, token: token - }).expect(STATUS.OK) - ).then( - res => expect(res.body).toEqual([]) - ) - }) + it('cannot read a fragment in a protected collection if it is not published', () => + api.users.authenticate + .post(fixtures.updatedUser) + .then(token => + api.fragments + .get({ + collection, + token, + }) + .expect(STATUS.OK), + ) + .then(res => expect(res.body).toEqual([]))) it('cannot update a fragment in a protected collection', async () => { const token = await api.users.authenticate.post(fixtures.updatedUser) - return api.fragments.patch({ - fragmentId: fragment.id, - update: fixtures.updatedFragment, - collection, - token - }).expect( - STATUS.FORBIDDEN - ) + return api.fragments + .patch({ + fragmentId: fragment.id, + update: fixtures.updatedFragment, + collection, + token, + }) + .expect(STATUS.FORBIDDEN) }) }) }) describe('a non-admin user with a reader role', () => { - beforeEach(() => { - return setTeamForCollection( - [otherUser.id], - collection, - fixtures.readerTeam - ) - }) + beforeEach(() => + setTeamForCollection([otherUser.id], collection, fixtures.readerTeam), + ) - afterEach(() => { - return setTeamForCollection( - [], - collection, - fixtures.readerTeam - ) - }) + afterEach(() => setTeamForCollection([], collection, fixtures.readerTeam)) - it('can not create a fragment', () => { - return api.users.authenticate.post( - fixtures.updatedUser - ).then( - token => { - return api.fragments.post({ - fragment: fixtures.fragment, collection, token - }).expect( - STATUS.FORBIDDEN - ) - } - ) - }) + it('can not create a fragment', () => + api.users.authenticate.post(fixtures.updatedUser).then(token => + api.fragments + .post({ + fragment: fixtures.fragment, + collection, + token, + }) + .expect(STATUS.FORBIDDEN), + )) - it('can read a fragment', function () { - return api.users.authenticate.post( - fixtures.updatedUser - ).then( - token => { - return api.fragments.get({ collection, token }) - } - ) - }) + it('can read a fragment', () => + api.users.authenticate + .post(fixtures.updatedUser) + .then(token => api.fragments.get({ collection, token }))) }) - it('fails to create a fragment in the protected collection if not authenticated', function () { - return api.fragments.post({ - fragment: fixtures.fragment, collection - }).expect( - STATUS.UNAUTHORIZED - ) - }) + it('fails to create a fragment in the protected collection if not authenticated', () => + api.fragments + .post({ + fragment: fixtures.fragment, + collection, + }) + .expect(STATUS.UNAUTHORIZED)) - it('fails to create a fragment in the protected collection if authentication wrong', function () { - return api.fragments.post({ - fragment: fixtures.fragment, collection, token: 'wrong' - }).expect( - STATUS.UNAUTHORIZED - ) - }) + it('fails to create a fragment in the protected collection if authentication wrong', () => + api.fragments + .post({ + fragment: fixtures.fragment, + collection, + token: 'wrong', + }) + .expect(STATUS.UNAUTHORIZED)) }) diff --git a/packages/server/test/api_collections_test.js b/packages/server/test/api_collections_test.js index 394bbe405fe77597703b933125c438fca2ad3677..03b58174c9bd838084cd9bcb6e042dacd3dc4ed7 100644 --- a/packages/server/test/api_collections_test.js +++ b/packages/server/test/api_collections_test.js @@ -7,28 +7,27 @@ const fixtures = require('./fixtures/fixtures') const api = require('./helpers/api') -const authenticateAdmin = () => { - return api.users.authenticate.post(fixtures.adminUser) -} +const authenticateAdmin = () => api.users.authenticate.post(fixtures.adminUser) -const authenticateUser = () => { - return api.users.authenticate.post(fixtures.user) -} +const authenticateUser = () => api.users.authenticate.post(fixtures.user) describe('Collections API', () => { - beforeEach(() => { - return cleanDB().then(() => Promise.all([ - new User(fixtures.adminUser).save(), - new User(fixtures.user).save() - ])) - }) + beforeEach(() => + cleanDB().then(() => + Promise.all([ + new User(fixtures.adminUser).save(), + new User(fixtures.user).save(), + ]), + ), + ) describe('admin', () => { it('should display an initially empty list of collections', async () => { const adminToken = await authenticateAdmin() // list all the collections - const collections = await api.collections.list(adminToken) + const collections = await api.collections + .list(adminToken) .expect(STATUS.OK) .then(res => res.body) @@ -38,8 +37,11 @@ describe('Collections API', () => { it('should allow an admin user to create a collection (without filtering properties)', async () => { const adminToken = await authenticateAdmin() - const collection = await api.collections.create( - Object.assign({}, fixtures.collection, {filtered: 'example'}), adminToken) + const collection = await api.collections + .create( + Object.assign({}, fixtures.collection, { filtered: 'example' }), + adminToken, + ) .expect(STATUS.CREATED) .then(res => res.body) expect(collection.type).toEqual(fixtures.collection.type) @@ -51,11 +53,13 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - await api.collections.create(fixtures.collection, adminToken) + await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) // list all the collections - const collections = await api.collections.list(adminToken) + const collections = await api.collections + .list(adminToken) .expect(STATUS.OK) .then(res => res.body) @@ -69,22 +73,26 @@ describe('Collections API', () => { it('should allow an admin user to filter collections with query params', async () => { const adminToken = await authenticateAdmin() - await api.collections.create(fixtures.collection, adminToken) + await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) - await api.collections.create(fixtures.collection2, adminToken) + await api.collections + .create(fixtures.collection2, adminToken) .expect(STATUS.CREATED) - const query1 = {'owners.0.username': 'admin'} + const query1 = { 'owners.0.username': 'admin' } - const collections1 = await api.collections.list(adminToken, query1) + const collections1 = await api.collections + .list(adminToken, query1) .expect(STATUS.OK) .then(res => res.body) expect(collections1).toHaveLength(2) - const query2 = {'title': fixtures.collection.title} + const query2 = { title: fixtures.collection.title } - const collections2 = await api.collections.list(adminToken, query2) + const collections2 = await api.collections + .list(adminToken, query2) .expect(STATUS.OK) .then(res => res.body) @@ -94,14 +102,17 @@ describe('Collections API', () => { it('should return empty array if filtering on non-existent property', async () => { const adminToken = await authenticateAdmin() - await api.collections.create(fixtures.collection, adminToken) + await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) - await api.collections.create(fixtures.collection2, adminToken) + await api.collections + .create(fixtures.collection2, adminToken) .expect(STATUS.CREATED) - const query = {'nonexistent': 'x'} + const query = { nonexistent: 'x' } - const collections = await api.collections.list(adminToken, query) + const collections = await api.collections + .list(adminToken, query) .expect(STATUS.OK) .then(res => res.body) @@ -112,34 +123,45 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // retrieve the collection - const retrievedCollection = await api.collections.retrieve(collection.id, adminToken) + const retrievedCollection = await api.collections + .retrieve(collection.id, adminToken) .expect(STATUS.OK) .then(res => res.body) - expect(retrievedCollection.owners[0]).toHaveProperty('username', fixtures.adminUser.username) + expect(retrievedCollection.owners[0]).toHaveProperty( + 'username', + fixtures.adminUser.username, + ) // list all the collections - const collections = await api.collections.list(adminToken) + const collections = await api.collections + .list(adminToken) .expect(STATUS.OK) .then(res => res.body) - expect(collections[0].owners[0]).toHaveProperty('username', fixtures.adminUser.username) + expect(collections[0].owners[0]).toHaveProperty( + 'username', + fixtures.adminUser.username, + ) }) it('should allow an admin user to retrieve only some fields of collections', async () => { const adminToken = await authenticateAdmin() // create a collection - await api.collections.create(fixtures.collection, adminToken) + await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) // list all the collections, asking for all fields - const collections = await api.collections.list(adminToken) + const collections = await api.collections + .list(adminToken) .expect(STATUS.OK) .then(res => res.body) @@ -152,7 +174,8 @@ describe('Collections API', () => { expect(collection).toHaveProperty('created') // list all the collections, asking for only two fields - const filteredCollections = await api.collections.list(adminToken, { fields: ['type', 'title'] }) + const filteredCollections = await api.collections + .list(adminToken, { fields: ['type', 'title'] }) .expect(STATUS.OK) .then(res => res.body) @@ -169,7 +192,8 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) @@ -177,7 +201,12 @@ describe('Collections API', () => { const title = 'Updated title' const filtered = 'example' - const result = await api.collections.update(collection.id, { title, filtered, rev: collection.rev }, adminToken) + const result = await api.collections + .update( + collection.id, + { title, filtered, rev: collection.rev }, + adminToken, + ) .expect(STATUS.OK) .then(res => res.body) @@ -189,18 +218,26 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) const otherUserUpdate = { title: 'Causes conflict', filtered: 'example' } const Collection = require('../src/models/Collection.js') - const collectionHandle = new Collection(Object.assign({}, collection, otherUserUpdate)) + const collectionHandle = new Collection( + Object.assign({}, collection, otherUserUpdate), + ) collectionHandle.save() - const myUpdate = { title: 'Outdated revision', filtered: 'example', rev: collection.rev } + const myUpdate = { + title: 'Outdated revision', + filtered: 'example', + rev: collection.rev, + } - await api.collections.update(collection.id, myUpdate, adminToken) + await api.collections + .update(collection.id, myUpdate, adminToken) .expect(STATUS.CONFLICT) }) @@ -208,13 +245,15 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) const myUpdate = { title: 'Untagged revision', filtered: 'example' } - await api.collections.update(collection.id, myUpdate, adminToken) + await api.collections + .update(collection.id, myUpdate, adminToken) .expect(STATUS.BAD_REQUEST) }) @@ -222,15 +261,25 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // create some fragments - await api.collections.createFragment(collection.id, fixtures.fragment, adminToken) - await api.collections.createFragment(collection.id, fixtures.updatedFragment, adminToken) + await api.collections.createFragment( + collection.id, + fixtures.fragment, + adminToken, + ) + await api.collections.createFragment( + collection.id, + fixtures.updatedFragment, + adminToken, + ) - const result = await api.collections.retrieve(collection.id, adminToken) + const result = await api.collections + .retrieve(collection.id, adminToken) .expect(STATUS.OK) .then(res => res.body) @@ -241,16 +290,17 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // delete the collection - await api.collections.delete(collection.id, adminToken) - .expect(STATUS.OK) + await api.collections.delete(collection.id, adminToken).expect(STATUS.OK) // try to retrieve the deleted collection - await api.collections.retrieve(collection.id, adminToken) + await api.collections + .retrieve(collection.id, adminToken) .expect(STATUS.NOT_FOUND) }) @@ -258,12 +308,14 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // list fragments in this collection - const fragments = await api.collections.listFragments(collection.id, adminToken) + const fragments = await api.collections + .listFragments(collection.id, adminToken) .expect(STATUS.OK) .then(res => res.body) @@ -274,73 +326,97 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // create a fragment in the collection - const fragment = await api.collections.createFragment(collection.id, fixtures.fragment, adminToken) + const fragment = await api.collections + .createFragment(collection.id, fixtures.fragment, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // retrieve the created fragment - const retrievedFragment = await api.collections.retrieveFragment(collection.id, fragment.id, adminToken) + const retrievedFragment = await api.collections + .retrieveFragment(collection.id, fragment.id, adminToken) .expect(STATUS.OK) .then(res => res.body) - expect(retrievedFragment.owners[0]).toHaveProperty('username', fixtures.adminUser.username) + expect(retrievedFragment.owners[0]).toHaveProperty( + 'username', + fixtures.adminUser.username, + ) // list the created fragments - const fragments = await api.collections.listFragments(collection.id, adminToken) + const fragments = await api.collections + .listFragments(collection.id, adminToken) .expect(STATUS.OK) .then(res => res.body) - expect(fragments[0].owners[0]).toHaveProperty('username', fixtures.adminUser.username) + expect(fragments[0].owners[0]).toHaveProperty( + 'username', + fixtures.adminUser.username, + ) }) it('should allow an admin user to create a fragment in a collection', async () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // create a fragment in the collection - const fragment = await api.collections.createFragment(collection.id, fixtures.fragment, adminToken) + const fragment = await api.collections + .createFragment(collection.id, fixtures.fragment, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // retrieve the created fragment - const retrievedFragment = await api.collections.retrieveFragment(collection.id, fragment.id, adminToken) + const retrievedFragment = await api.collections + .retrieveFragment(collection.id, fragment.id, adminToken) .expect(STATUS.OK) .then(res => res.body) - expect(retrievedFragment.presentation).toEqual(fixtures.fragment.presentation) + expect(retrievedFragment.presentation).toEqual( + fixtures.fragment.presentation, + ) }) it('should allow an admin user to update a fragment in a collection', async () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // create a fragment in the collection - const fragment = await api.collections.createFragment(collection.id, fixtures.fragment, adminToken) + const fragment = await api.collections + .createFragment(collection.id, fixtures.fragment, adminToken) .expect(STATUS.CREATED) .then(res => res.body) const source = '<blog>test</blog>' // update the fragment - await api.collections.updateFragment(collection.id, fragment.id, { source, rev: fragment.rev }, adminToken) + await api.collections + .updateFragment( + collection.id, + fragment.id, + { source, rev: fragment.rev }, + adminToken, + ) .expect(STATUS.OK) .then(res => res.body) // retrieve the updated fragment - const retrievedFragment = await api.collections.retrieveFragment(collection.id, fragment.id, adminToken) + const retrievedFragment = await api.collections + .retrieveFragment(collection.id, fragment.id, adminToken) .expect(STATUS.OK) .then(res => res.body) @@ -351,30 +427,36 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // create a fragment in the collection - const fragment = await api.collections.createFragment(collection.id, fixtures.fragment, adminToken) + const fragment = await api.collections + .createFragment(collection.id, fixtures.fragment, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // create another fragment - await api.collections.createFragment(collection.id, fixtures.fragment, adminToken) + await api.collections + .createFragment(collection.id, fixtures.fragment, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // delete the first fragment - await api.collections.deleteFragment(collection.id, fragment.id, adminToken) + await api.collections + .deleteFragment(collection.id, fragment.id, adminToken) .expect(STATUS.OK) // retrieve the updated fragment - await api.collections.retrieveFragment(collection.id, fragment.id, adminToken) + await api.collections + .retrieveFragment(collection.id, fragment.id, adminToken) .expect(STATUS.NOT_FOUND) // list fragments in this collection - const fragments = await api.collections.listFragments(collection.id, adminToken) + const fragments = await api.collections + .listFragments(collection.id, adminToken) .expect(STATUS.OK) .then(res => res.body) @@ -385,17 +467,20 @@ describe('Collections API', () => { const adminToken = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // create a fragment in the collection - await api.collections.createFragment(collection.id, fixtures.fragment, adminToken) + await api.collections + .createFragment(collection.id, fixtures.fragment, adminToken) .expect(STATUS.CREATED) .then(res => res.body) // list fragments in this collection - const fragments = await api.collections.listFragments(collection.id, adminToken) + const fragments = await api.collections + .listFragments(collection.id, adminToken) .expect(STATUS.OK) .then(res => res.body) @@ -409,7 +494,10 @@ describe('Collections API', () => { expect(fragment).toHaveProperty('source') // list fragments in this collection, requesting only certain fields - const filteredFragments = await api.collections.listFragments(collection.id, adminToken, { fields: ['type', 'presentation'] }) + const filteredFragments = await api.collections + .listFragments(collection.id, adminToken, { + fields: ['type', 'presentation'], + }) .expect(STATUS.OK) .then(res => res.body) @@ -427,22 +515,27 @@ describe('Collections API', () => { const token = await authenticateAdmin() // create a collection - const collection = await api.collections.create(fixtures.collection, token) + const collection = await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) .then(res => res.body) // create a fragment in the collection - const fragment = await api.collections.createFragment(collection.id, fixtures.fragment, token) + const fragment = await api.collections + .createFragment(collection.id, fixtures.fragment, token) .expect(STATUS.CREATED) .then(res => res.body) // create the teams await api.teams.post(fixtures.readerTeam, token) - const teamFixture = Object.assign({}, fixtures.contributorTeam, {object: {type: 'fragment', id: fragment.id}}) + const teamFixture = Object.assign({}, fixtures.contributorTeam, { + object: { type: 'fragment', id: fragment.id }, + }) await api.teams.post(teamFixture, token) // retrieve the fragment team(s) - const teams = await api.collections.listFragmentTeams(collection.id, fragment.id, token) + const teams = await api.collections + .listFragmentTeams(collection.id, fragment.id, token) .expect(STATUS.OK) .then(res => res.body) @@ -450,20 +543,22 @@ describe('Collections API', () => { expect(teams[0]).toMatchObject({ name: 'My contributors', object: { - type: 'fragment' - } + type: 'fragment', + }, }) }) }) describe('anonymous', () => { it('should not allow an anonymous user to create a collection', async () => { - await api.collections.create(fixtures.collection) + await api.collections + .create(fixtures.collection) .expect(STATUS.UNAUTHORIZED) }) it('should not allow an anonymous user to create a fragment', async () => { - await api.collections.createFragment(fixtures.collection, fixtures.fragment) + await api.collections + .createFragment(fixtures.collection, fixtures.fragment) .expect(STATUS.UNAUTHORIZED) }) }) @@ -472,10 +567,12 @@ describe('Collections API', () => { it('should allow a user to list all of the collections', async () => { const token = await authenticateUser() - await api.collections.create(fixtures.collection, token) + await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) - const collections = await api.collections.list(token) + const collections = await api.collections + .list(token) .expect(STATUS.OK) .then(res => res.body) @@ -486,7 +583,8 @@ describe('Collections API', () => { const token = await authenticateUser() // create the collection - await api.collections.create(fixtures.collection, token) + await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) }) @@ -494,30 +592,34 @@ describe('Collections API', () => { const token = await authenticateUser() // create the collection - const collection = await api.collections.create(fixtures.collection, token) + const collection = await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) .then(res => res.body) // retrieve the collection - await api.collections.retrieve(collection.id, token) - .expect(STATUS.OK) + await api.collections.retrieve(collection.id, token).expect(STATUS.OK) }) it('should allow a user to retrieve teams for a collection', async () => { const token = await authenticateUser() // create the collection - const collection = await api.collections.create(fixtures.collection, token) + const collection = await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) .then(res => res.body) // create the teams - const teamFixture = Object.assign({}, fixtures.contributorTeam, {object: {type: 'collection', id: collection.id}}) + const teamFixture = Object.assign({}, fixtures.contributorTeam, { + object: { type: 'collection', id: collection.id }, + }) await api.teams.post(teamFixture, token) await api.teams.post(fixtures.readerTeam, token) // retrieve the collection team(s) - const teams = await api.collections.listTeams(collection.id, token) + const teams = await api.collections + .listTeams(collection.id, token) .expect(STATUS.OK) .then(res => res.body) @@ -525,8 +627,8 @@ describe('Collections API', () => { expect(teams[0]).toMatchObject({ name: 'My contributors', object: { - type: 'collection' - } + type: 'collection', + }, }) }) @@ -534,14 +636,16 @@ describe('Collections API', () => { const token = await authenticateUser() // create the collection - const collection = await api.collections.create(fixtures.collection, token) + const collection = await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) .then(res => res.body) // update the collection const title = 'Updated title' - const result = await api.collections.update(collection.id, { title, rev: collection.rev }, token) + const result = await api.collections + .update(collection.id, { title, rev: collection.rev }, token) .expect(STATUS.OK) .then(res => res.body) @@ -552,29 +656,32 @@ describe('Collections API', () => { const userToken = await authenticateUser() const adminToken = await authenticateAdmin() - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) const title = 'Updated title' - await api.collections.update(collection.id, { title, rev: collection.rev }, userToken) + await api.collections + .update(collection.id, { title, rev: collection.rev }, userToken) .expect(STATUS.FORBIDDEN) }) it('should allow a user to delete any collection they own', async () => { const token = await authenticateUser() - const collection = await api.collections.create(fixtures.collection, token) + const collection = await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) .then(res => res.body) expect(collection.title).toEqual(fixtures.collection.title) - await api.collections.delete(collection.id, token) - .expect(STATUS.OK) + await api.collections.delete(collection.id, token).expect(STATUS.OK) - await api.collections.retrieve(collection.id, token) + await api.collections + .retrieve(collection.id, token) .expect(STATUS.NOT_FOUND) }) @@ -582,23 +689,30 @@ describe('Collections API', () => { const userToken = await authenticateUser() const adminToken = await authenticateAdmin() - const collection = await api.collections.create(fixtures.collection, adminToken) + const collection = await api.collections + .create(fixtures.collection, adminToken) .expect(STATUS.CREATED) .then(res => res.body) - await api.collections.delete(collection.id, userToken) + await api.collections + .delete(collection.id, userToken) .expect(STATUS.FORBIDDEN) }) it('should filter collection properties based on authorization on creation', async () => { const token = await authenticateUser() - let collection = await api.collections.create( - Object.assign({}, fixtures.collection, { filtered: 'example' }), token) + let collection = await api.collections + .create( + Object.assign({}, fixtures.collection, { filtered: 'example' }), + token, + ) .expect(STATUS.CREATED) .then(res => res.body) - collection = await api.collections.retrieve(collection.id, token).expect(STATUS.OK) + collection = await api.collections + .retrieve(collection.id, token) + .expect(STATUS.OK) expect(Object.keys(collection)).not.toContain('filtered') }) @@ -606,12 +720,16 @@ describe('Collections API', () => { it('should return empty array if trying to filter with query params on properties for which user lacks authorization', async () => { const token = await authenticateUser() - await api.collections.create( - Object.assign({}, fixtures.collection, { filtered: 'example' }), token) + await api.collections + .create( + Object.assign({}, fixtures.collection, { filtered: 'example' }), + token, + ) .expect(STATUS.CREATED) .then(res => res.body) - const collections = await api.collections.list(token, { filtered: 'example' }) + const collections = await api.collections + .list(token, { filtered: 'example' }) .expect(STATUS.OK) .then(res => res.body) @@ -621,13 +739,15 @@ describe('Collections API', () => { it('should filter collection properties based on authorization on update', async () => { const token = await authenticateUser() - let collection = await api.collections.create(fixtures.collection, token) + let collection = await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) .then(res => res.body) const filtered = 'example' - collection = await api.collections.update(collection.id, { filtered, rev: collection.rev }, token) + collection = await api.collections + .update(collection.id, { filtered, rev: collection.rev }, token) .expect(STATUS.OK) .then(res => res.body) @@ -640,7 +760,8 @@ describe('Collections API', () => { // NOTE: need to authenticate as admin to be able to retrieve teams const token = await authenticateAdmin() - const collection = await api.collections.create(fixtures.collection, token) + const collection = await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) .then(res => res.body) @@ -648,21 +769,23 @@ describe('Collections API', () => { name: 'bar', teamType: { name: 'Test', - permissions: 'read' + permissions: 'read', }, object: { type: 'collection', - id: collection.id - } + id: collection.id, + }, } - await api.teams.post(teamData, token) + await api.teams + .post(teamData, token) .expect(STATUS.CREATED) .then(res => res.body) // create a different team on a different collection - const otherCollection = await api.collections.create(fixtures.collection, token) + const otherCollection = await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) .then(res => res.body) @@ -670,39 +793,41 @@ describe('Collections API', () => { name: 'foo', teamType: { name: 'Foo', - permissions: 'read' + permissions: 'read', }, object: { type: 'collection', - id: otherCollection.id - } + id: otherCollection.id, + }, } - await api.teams.post(otherTeamData, token) - .expect(STATUS.CREATED) + await api.teams.post(otherTeamData, token).expect(STATUS.CREATED) - const collectionTeams = await api.collections.listTeams(collection.id, token) + const collectionTeams = await api.collections + .listTeams(collection.id, token) .expect(STATUS.OK) .then(res => res.body) expect(collectionTeams).toHaveLength(1) - const teamsBeforeDeletion = await api.teams.list(token) + const teamsBeforeDeletion = await api.teams + .list(token) .expect(STATUS.OK) .then(res => res.body) expect(teamsBeforeDeletion).toHaveLength(2) - await api.collections.delete(collection.id, token) - .expect(STATUS.OK) + await api.collections.delete(collection.id, token).expect(STATUS.OK) - await api.collections.retrieve(collection.id, token) + await api.collections + .retrieve(collection.id, token) .expect(STATUS.NOT_FOUND) // await api.collections.listTeams(collection.id, token) // .expect(STATUS.NOT_FOUND) - const teamsAfterDeletion = await api.teams.list(token) + const teamsAfterDeletion = await api.teams + .list(token) .expect(STATUS.OK) .then(res => res.body) @@ -713,11 +838,13 @@ describe('Collections API', () => { // NOTE: need to authenticate as admin to be able to retrieve teams const token = await authenticateAdmin() - const collection = await api.collections.create(fixtures.collection, token) + const collection = await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) .then(res => res.body) - const fragment = await api.fragments.post({fragment: fixtures.fragment, collection, token}) + const fragment = await api.fragments + .post({ fragment: fixtures.fragment, collection, token }) .expect(STATUS.CREATED) .then(res => res.body) @@ -725,21 +852,23 @@ describe('Collections API', () => { name: 'bar', teamType: { name: 'Test', - permissions: 'read' + permissions: 'read', }, object: { type: 'fragment', - id: fragment.id - } + id: fragment.id, + }, } - await api.teams.post(teamData, token) + await api.teams + .post(teamData, token) .expect(STATUS.CREATED) .then(res => res.body) // create a different team on a different fragment - const otherFragment = await api.fragments.post({fragment: fixtures.fragment, collection, token}) + const otherFragment = await api.fragments + .post({ fragment: fixtures.fragment, collection, token }) .expect(STATUS.CREATED) .then(res => res.body) @@ -747,39 +876,43 @@ describe('Collections API', () => { name: 'foo', teamType: { name: 'Foo', - permissions: 'read' + permissions: 'read', }, object: { type: 'fragment', - id: otherFragment.id - } + id: otherFragment.id, + }, } - await api.teams.post(otherTeamData, token) - .expect(STATUS.CREATED) + await api.teams.post(otherTeamData, token).expect(STATUS.CREATED) - const fragmentTeams = await api.collections.listFragmentTeams(collection.id, fragment.id, token) + const fragmentTeams = await api.collections + .listFragmentTeams(collection.id, fragment.id, token) .expect(STATUS.OK) .then(res => res.body) expect(fragmentTeams).toHaveLength(1) - const teamsBeforeDeletion = await api.teams.list(token) + const teamsBeforeDeletion = await api.teams + .list(token) .expect(STATUS.OK) .then(res => res.body) expect(teamsBeforeDeletion).toHaveLength(2) - await api.collections.deleteFragment(collection.id, fragment.id, token) + await api.collections + .deleteFragment(collection.id, fragment.id, token) .expect(STATUS.OK) - await api.collections.retrieveFragment(collection.id, fragment.id, token) + await api.collections + .retrieveFragment(collection.id, fragment.id, token) .expect(STATUS.NOT_FOUND) // await api.collections.listFragmentTeams(collection.id, fragment.id, token) // .expect(STATUS.NOT_FOUND) - const teamsAfterDeletion = await api.teams.list(token) + const teamsAfterDeletion = await api.teams + .list(token) .expect(STATUS.OK) .then(res => res.body) diff --git a/packages/server/test/api_file_upload_test.js b/packages/server/test/api_file_upload_test.js index 2d056000f95e290efdc9ee51609bd5749ab03fdd..6f4ead2fb58680fdfa4f05d58d1e42d5e5463e5d 100644 --- a/packages/server/test/api_file_upload_test.js +++ b/packages/server/test/api_file_upload_test.js @@ -5,21 +5,19 @@ const fixtures = require('./fixtures/fixtures') const cleanDB = require('./helpers/db_cleaner') const User = require('../src/models/User') -function fileName (name) { +function fileName(name) { return path.join(__dirname, 'fixtures', name) } -function file (name) { +function file(name) { return fs.createReadStream(fileName(name)) } -function fileBuffer (name) { +function fileBuffer(name) { return fs.readFileSync(fileName(name)) } -const authenticateUser = () => { - return api.users.authenticate.post(fixtures.user) -} +const authenticateUser = () => api.users.authenticate.post(fixtures.user) describe('File upload/download', () => { beforeEach(async () => { diff --git a/packages/server/test/api_fragments_test.js b/packages/server/test/api_fragments_test.js index 2d231263e29da2b2d31df8cccdc5eac33f226d2d..d8f8b49ee3c2f2a8b234e8e6fb62e889809ce909 100644 --- a/packages/server/test/api_fragments_test.js +++ b/packages/server/test/api_fragments_test.js @@ -7,24 +7,25 @@ const fixtures = require('./fixtures/fixtures') const api = require('./helpers/api') -const authenticateAdmin = () => { - return api.users.authenticate.post(fixtures.adminUser) -} +const authenticateAdmin = () => api.users.authenticate.post(fixtures.adminUser) describe('Fragments API', () => { - beforeEach(() => { - return cleanDB().then(() => Promise.all([ - new User(fixtures.adminUser).save(), - new User(fixtures.user).save() - ])) - }) + beforeEach(() => + cleanDB().then(() => + Promise.all([ + new User(fixtures.adminUser).save(), + new User(fixtures.user).save(), + ]), + ), + ) describe('admin', () => { it('should have an initially empty list of fragments', async () => { const adminToken = await authenticateAdmin() // list all the collections - const collections = await api.fragments.get({ token: adminToken }) + const collections = await api.fragments + .get({ token: adminToken }) .expect(STATUS.OK) .then(res => res.body) @@ -34,10 +35,12 @@ describe('Fragments API', () => { it('should allow an admin user to create a fragment', async () => { const adminToken = await authenticateAdmin() - const fragment = await api.fragments.post({ - fragment: {...fixtures.fragment, filtered: 'example'}, - token: adminToken - }).expect(STATUS.CREATED) + const fragment = await api.fragments + .post({ + fragment: { ...fixtures.fragment, filtered: 'example' }, + token: adminToken, + }) + .expect(STATUS.CREATED) .then(res => res.body) expect(fragment.type).toEqual(fixtures.fragment.type) @@ -48,21 +51,25 @@ describe('Fragments API', () => { it('should allow an admin user to update a fragment (without filtering properties)', async () => { const adminToken = await authenticateAdmin() - const fragment = await api.fragments.post({ - fragment: fixtures.fragment, - token: adminToken - }).expect(STATUS.CREATED) + const fragment = await api.fragments + .post({ + fragment: fixtures.fragment, + token: adminToken, + }) + .expect(STATUS.CREATED) .then(res => res.body) // update the collection const title = 'Updated title' const filtered = 'example' - const result = await api.fragments.patch({ - fragmentId: fragment.id, - update: { title, filtered, rev: fragment.rev }, - token: adminToken - }).expect(STATUS.OK) + const result = await api.fragments + .patch({ + fragmentId: fragment.id, + update: { title, filtered, rev: fragment.rev }, + token: adminToken, + }) + .expect(STATUS.OK) .then(res => res.body) expect(result.title).toEqual(title) @@ -73,35 +80,46 @@ describe('Fragments API', () => { const adminToken = await authenticateAdmin() // create a collection - const fragment = await api.fragments.post({ - fragment: fixtures.fragment, token: adminToken - }).expect(STATUS.CREATED) + const fragment = await api.fragments + .post({ + fragment: fixtures.fragment, + token: adminToken, + }) + .expect(STATUS.CREATED) .then(res => res.body) // delete the collection - await api.fragments.delete({ - fragmentId: fragment.id, - token: adminToken - }).expect(STATUS.OK) + await api.fragments + .delete({ + fragmentId: fragment.id, + token: adminToken, + }) + .expect(STATUS.OK) // try to retrieve the deleted collection - await api.fragments.get({ - fragmentId: fragment.id, - token: adminToken - }).expect(STATUS.NOT_FOUND) + await api.fragments + .get({ + fragmentId: fragment.id, + token: adminToken, + }) + .expect(STATUS.NOT_FOUND) }) it('should allow an admin user to retrieve only some fields when getting fragments', async () => { const adminToken = await authenticateAdmin() - await api.fragments.post({ - fragment: fixtures.fragment, - token: adminToken - }).expect(STATUS.CREATED) - - const fragments = await api.fragments.get({ - token: adminToken - }).expect(STATUS.OK) + await api.fragments + .post({ + fragment: fixtures.fragment, + token: adminToken, + }) + .expect(STATUS.CREATED) + + const fragments = await api.fragments + .get({ + token: adminToken, + }) + .expect(STATUS.OK) .then(res => res.body) expect(fragments).toHaveLength(1) @@ -114,10 +132,12 @@ describe('Fragments API', () => { expect(fragment).toHaveProperty('source') // list fragments in this collection, requesting only certain fields - const filteredFragments = await api.fragments.get({ - token: adminToken, - fields: ['type', 'presentation'] - }).expect(STATUS.OK) + const filteredFragments = await api.fragments + .get({ + token: adminToken, + fields: ['type', 'presentation'], + }) + .expect(STATUS.OK) .then(res => res.body) expect(filteredFragments).toHaveLength(1) @@ -133,28 +153,35 @@ describe('Fragments API', () => { it('should allow admin to retrieve teams for a fragment', async () => { const token = await authenticateAdmin() - const fragment = await api.fragments.post({ - fragment: fixtures.fragment, - token - }).expect(STATUS.CREATED) + const fragment = await api.fragments + .post({ + fragment: fixtures.fragment, + token, + }) + .expect(STATUS.CREATED) .then(res => res.body) // create the teams - const teamFixture = Object.assign({}, fixtures.contributorTeam, {object: {type: 'fragment', id: fragment.id}}) + const teamFixture = Object.assign({}, fixtures.contributorTeam, { + object: { type: 'fragment', id: fragment.id }, + }) await api.teams.post(teamFixture, token) // retrieve the fragment team(s) - const teams = await api.fragments.teams({ - fragmentId: fragment.id, token - }).expect(STATUS.OK) + const teams = await api.fragments + .teams({ + fragmentId: fragment.id, + token, + }) + .expect(STATUS.OK) .then(res => res.body) expect(teams).toHaveLength(1) expect(teams[0]).toMatchObject({ name: 'My contributors', object: { - type: 'fragment' - } + type: 'fragment', + }, }) }) }) diff --git a/packages/server/test/api_locals_test.js b/packages/server/test/api_locals_test.js index a89320c830465e241cc3088b3292c341b2a44b41..35190a3637fc957fe28c067a688b6633b1c91d8c 100644 --- a/packages/server/test/api_locals_test.js +++ b/packages/server/test/api_locals_test.js @@ -17,7 +17,9 @@ describe('api/app locals', () => { expect(api.locals.models.Fragment.type).toEqual('fragment') expect(api.locals.models.Collection.type).toEqual('collection') - const user = await api.locals.models.User.findByEmail(fixtures.adminUser.email) + const user = await api.locals.models.User.findByEmail( + fixtures.adminUser.email, + ) expect(user.username).toEqual(fixtures.adminUser.username) }) }) diff --git a/packages/server/test/api_sse_disabled_test.js b/packages/server/test/api_sse_disabled_test.js index 1bf41f6d914dbd2f26561d3aabd0c973eb0f2778..85405f995ca7868559e07b499bf0bf7e706baf50 100644 --- a/packages/server/test/api_sse_disabled_test.js +++ b/packages/server/test/api_sse_disabled_test.js @@ -17,7 +17,7 @@ describe('API SSE disabled', () => { await cleanDB() await new User(fixtures.adminUser).save() await new Promise((resolve, reject) => { - server = api.api.listen(port, err => err ? reject(err) : resolve()) + server = api.api.listen(port, err => (err ? reject(err) : resolve())) }) }) @@ -28,13 +28,20 @@ describe('API SSE disabled', () => { it('should not send an event if not configured', async () => { const token = await api.users.authenticate.post(fixtures.adminUser) - es = new EventSource(`http://localhost:${port}/updates?access_token=${encodeURIComponent(token)}`) + es = new EventSource( + `http://localhost:${port}/updates?access_token=${encodeURIComponent( + token, + )}`, + ) const eventPromise = new Promise((resolve, reject) => { es.addEventListener('message', resolve) es.addEventListener('error', reject) }) - await expect(eventPromise).rejects.toEqual({type: 'error', status: STATUS.NOT_FOUND}) + await expect(eventPromise).rejects.toEqual({ + type: 'error', + status: STATUS.NOT_FOUND, + }) }) }) diff --git a/packages/server/test/api_sse_enabled_test.js b/packages/server/test/api_sse_enabled_test.js index 409fc4ddf5771c13eb11493d83db87395bd3adaf..72e2d3ff653bde258f779b8493615c171aa3c76c 100644 --- a/packages/server/test/api_sse_enabled_test.js +++ b/packages/server/test/api_sse_enabled_test.js @@ -22,7 +22,7 @@ describe('API SSE enabled', () => { await cleanDB() await new User(fixtures.adminUser).save() await new Promise((resolve, reject) => { - server = api.api.listen(port, err => err ? reject(err) : resolve()) + server = api.api.listen(port, err => (err ? reject(err) : resolve())) }) }) @@ -33,13 +33,20 @@ describe('API SSE enabled', () => { it('should send an event if configured', async () => { const token = await api.users.authenticate.post(fixtures.adminUser) - es = new EventSource(`http://localhost:${port}/updates?access_token=${encodeURIComponent(token)}`) + es = new EventSource( + `http://localhost:${port}/updates?access_token=${encodeURIComponent( + token, + )}`, + ) // wrap event listener in promise - const eventPromise = new Promise(resolve => es.addEventListener('message', resolve)) + const eventPromise = new Promise(resolve => + es.addEventListener('message', resolve), + ) // perform action - await api.collections.create(fixtures.collection, token) + await api.collections + .create(fixtures.collection, token) .expect(STATUS.CREATED) // await event @@ -48,8 +55,8 @@ describe('API SSE enabled', () => { expect(eventData).toMatchObject({ action: 'collection:create', data: { - collection: fixtures.collection - } + collection: fixtures.collection, + }, }) }) }) diff --git a/packages/server/test/api_teams_test.js b/packages/server/test/api_teams_test.js index 2e33acca6b94b868d848076e12eb5cb01e1dc0b4..fee6e6554d225c121fd458d077603dde4ac9d68e 100644 --- a/packages/server/test/api_teams_test.js +++ b/packages/server/test/api_teams_test.js @@ -7,69 +7,55 @@ const Team = require('../src/models/Team') const cleanDB = require('./helpers/db_cleaner') const fixtures = require('./fixtures/fixtures') + const contributors = fixtures.teams.contributors const teamFixture = fixtures.contributorTeam const api = require('./helpers/api') describe('Teams API - admin', () => { - beforeEach(() => { - return cleanDB().then( - () => { return new User(fixtures.adminUser).save() } - ).then( - () => { return new User(fixtures.user).save() } - ) - }) - - it('should display an initially empty list of teams if user is admin', () => { - return api.users.authenticate.post( - fixtures.adminUser - ).then( - token => api.teams.list(token).expect(STATUS.OK) - ).then( - res => expect(res.body).toEqual([]) - ) - }) - - it('should display the existing teams if user is admin', () => { - return new Team( - teamFixture - ).save().then( - () => api.users.authenticate.post(fixtures.adminUser) - ).then( - token => api.teams.list(token).expect(STATUS.OK) - ).then( - res => { - let team = res.body[0] + beforeEach(() => + cleanDB() + .then(() => new User(fixtures.adminUser).save()) + .then(() => new User(fixtures.user).save()), + ) + + it('should display an initially empty list of teams if user is admin', () => + api.users.authenticate + .post(fixtures.adminUser) + .then(token => api.teams.list(token).expect(STATUS.OK)) + .then(res => expect(res.body).toEqual([]))) + + it('should display the existing teams if user is admin', () => + new Team(teamFixture) + .save() + .then(() => api.users.authenticate.post(fixtures.adminUser)) + .then(token => api.teams.list(token).expect(STATUS.OK)) + .then(res => { + const team = res.body[0] expect(team.teamType.name).toEqual(contributors.name) expect(team.members).toEqual([]) - } - ) - }) - - it('should allow retrieval of a team by id', () => { - return new Team( - teamFixture - ).save().then( - () => api.users.authenticate.post(fixtures.adminUser) - ).then( - token => api.teams.list(token).then(res => ({teamId: res.body[0].id, token})) - ).then( - ({teamId, token}) => api.teams.get(token, teamId).expect(STATUS.OK) - ).then( - res => { + })) + + it('should allow retrieval of a team by id', () => + new Team(teamFixture) + .save() + .then(() => api.users.authenticate.post(fixtures.adminUser)) + .then(token => + api.teams.list(token).then(res => ({ teamId: res.body[0].id, token })), + ) + .then(({ teamId, token }) => + api.teams.get(token, teamId).expect(STATUS.OK), + ) + .then(res => { const team = res.body expect(team.teamType.name).toEqual(contributors.name) expect(team.members).toEqual([]) - }) - }) + })) - it('should not allow listing all teams if user is not an admin', () => { - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.teams.list(token).expect(STATUS.FORBIDDEN) - ) - }) + it('should not allow listing all teams if user is not an admin', () => + api.users.authenticate + .post(fixtures.user) + .then(token => api.teams.list(token).expect(STATUS.FORBIDDEN))) }) describe('Teams API - per collection or fragment', () => { @@ -79,65 +65,56 @@ describe('Teams API - per collection or fragment', () => { let otherUserId let team - beforeEach(() => { - return cleanDB().then( - () => new User(fixtures.user).save() - ).then( - user => { - let collection = new Collection(fixtures.collection) + beforeEach(() => + cleanDB() + .then(() => new User(fixtures.user).save()) + .then(user => { + const collection = new Collection(fixtures.collection) collection.setOwners([user.id]) return collection.save() - } - ).then( - collection => { collectionId = collection.id } - ).then( - () => new User(fixtures.updatedUser).save() - ).then( - otherUser => { otherUserId = otherUser.id } - ).then( - () => { team = cloneDeep(teamFixture) } - ) - }) - - it('can display an initially empty list of teams', () => { - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.teams.list(token, collectionId).expect(STATUS.OK) - ).then( - res => expect(res.body).toEqual([]) - ) - }) + }) + .then(collection => { + collectionId = collection.id + }) + .then(() => new User(fixtures.updatedUser).save()) + .then(otherUser => { + otherUserId = otherUser.id + }) + .then(() => { + team = cloneDeep(teamFixture) + }), + ) + + it('can display an initially empty list of teams', () => + api.users.authenticate + .post(fixtures.user) + .then(token => api.teams.list(token, collectionId).expect(STATUS.OK)) + .then(res => expect(res.body).toEqual([]))) it('can add a team with a team member to a collection and this team member can then create fragments', () => { team.name = 'Test team' team.members = [otherUserId] team.object = { id: collectionId, - type: 'collection' + type: 'collection', } - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.teams.post( - team, token - ).expect( - STATUS.CREATED - ) - ).then( - res => { + return api.users.authenticate + .post(fixtures.user) + .then(token => api.teams.post(team, token).expect(STATUS.CREATED)) + .then(res => { expect(res.body.name).toEqual(team.name) - } - ).then( - () => api.users.authenticate.post(fixtures.updatedUser) - ).then( - token => api.fragments.post({ - fragment: fixtures.fragment, collection: collectionId, token - }).expect( - STATUS.CREATED + }) + .then(() => api.users.authenticate.post(fixtures.updatedUser)) + .then(token => + api.fragments + .post({ + fragment: fixtures.fragment, + collection: collectionId, + token, + }) + .expect(STATUS.CREATED), ) - ) }) it('can remove a team member and that removed team member can no longer create fragments', () => { @@ -145,67 +122,60 @@ describe('Teams API - per collection or fragment', () => { team.members = [otherUserId] team.object = { id: collectionId, - type: 'collection' + type: 'collection', } - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.teams.post( - team, token - ).expect( - STATUS.CREATED - ).then( - res => [res, token] + return api.users.authenticate + .post(fixtures.user) + .then(token => + api.teams + .post(team, token) + .expect(STATUS.CREATED) + .then(res => [res, token]), ) - ).then( - ([res, token]) => { + .then(([res, token]) => { const savedTeam = res.body savedTeam.members = [] - return api.teams.patch( - savedTeam, savedTeam.id, token - ).expect( - STATUS.OK - ) - } - ).then( - () => api.users.authenticate.post(fixtures.updatedUser) - ).then( - token => api.fragments.post({ - fragment: fixtures.fragment, collection: collectionId, token - }).expect( - STATUS.FORBIDDEN + return api.teams + .patch(savedTeam, savedTeam.id, token) + .expect(STATUS.OK) + }) + .then(() => api.users.authenticate.post(fixtures.updatedUser)) + .then(token => + api.fragments + .post({ + fragment: fixtures.fragment, + collection: collectionId, + token, + }) + .expect(STATUS.FORBIDDEN), ) - ) }) }) describe('non-owners', () => { let collectionId - beforeEach(() => { - return cleanDB().then( - () => new User(fixtures.user).save() - ).then( - user => { - let collection = new Collection(fixtures.collection) + beforeEach(() => + cleanDB() + .then(() => new User(fixtures.user).save()) + .then(user => { + const collection = new Collection(fixtures.collection) collection.owners = [] return collection.save() - } - ).then( - collection => { collectionId = collection.id } - ) - }) - - it('should not see teams in a collection', () => { - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.teams.list(token, collectionId).expect(STATUS.OK) - ).then(res => { - expect(res.body).toEqual([]) - }) - }) + }) + .then(collection => { + collectionId = collection.id + }), + ) + + it('should not see teams in a collection', () => + api.users.authenticate + .post(fixtures.user) + .then(token => api.teams.list(token, collectionId).expect(STATUS.OK)) + .then(res => { + expect(res.body).toEqual([]) + })) }) }) }) diff --git a/packages/server/test/api_unauthenticated_test.js b/packages/server/test/api_unauthenticated_test.js index ee8631ff41a623d5cba60aad4438817f79022245..4df55609af6f601caeec60c828e9404a7e28dd45 100644 --- a/packages/server/test/api_unauthenticated_test.js +++ b/packages/server/test/api_unauthenticated_test.js @@ -13,7 +13,7 @@ describe('unauthenticated/public api', () => { beforeEach(cleanDB) - async function setNewFragment (opts) { + async function setNewFragment(opts) { const userAndCollection = await createBasicCollection() collection = userAndCollection.collection fragment = await createFragment(opts, collection) @@ -23,44 +23,56 @@ describe('unauthenticated/public api', () => { describe('published fragment', () => { beforeEach(() => setNewFragment({ published: true })) - it('can see a published fragment in a collection', () => { - return api.fragments.get({ collection }).expect(STATUS.OK).then( - res => expect(res.body[0].id).toEqual(fragment.id) - ) - }) - - it('can only see the published fragment in a collection', () => { - return api.fragments.get({ collection }).expect(STATUS.OK).then( - res => expect(res.body.map(f => f.id)).not.toContain(unpublishedFragment.id) - ) - }) - - it('can only see the filtered list of properties for a fragment', () => { - return api.collections.retrieveFragment(collection.id, fragment.id).expect(STATUS.OK).then( - res => expect(Object.keys(res.body)).toEqual(['id', 'title', 'source', 'presentation', 'owners']) - ) - }) - - it('can only see the filtered list of properties for a collection', () => { - return api.collections.retrieve(collection.id).expect(STATUS.OK).then( - res => expect(Object.keys(res.body)).toEqual(['id', 'title', 'owners']) - ) - }) + it('can see a published fragment in a collection', () => + api.fragments + .get({ collection }) + .expect(STATUS.OK) + .then(res => expect(res.body[0].id).toEqual(fragment.id))) + + it('can only see the published fragment in a collection', () => + api.fragments + .get({ collection }) + .expect(STATUS.OK) + .then(res => + expect(res.body.map(f => f.id)).not.toContain(unpublishedFragment.id), + )) + + it('can only see the filtered list of properties for a fragment', () => + api.collections + .retrieveFragment(collection.id, fragment.id) + .expect(STATUS.OK) + .then(res => + expect(Object.keys(res.body)).toEqual([ + 'id', + 'title', + 'source', + 'presentation', + 'owners', + ]), + )) + + it('can only see the filtered list of properties for a collection', () => + api.collections + .retrieve(collection.id) + .expect(STATUS.OK) + .then(res => + expect(Object.keys(res.body)).toEqual(['id', 'title', 'owners']), + )) }) describe('unpublished fragment', () => { beforeEach(() => setNewFragment({ published: false })) - it('can not list unpublished fragments in a protected collection', () => { - return api.fragments.get({ collection }).expect(STATUS.OK).then( - res => expect(res.body).toEqual([]) - ) - }) + it('can not list unpublished fragments in a protected collection', () => + api.fragments + .get({ collection }) + .expect(STATUS.OK) + .then(res => expect(res.body).toEqual([]))) - it('can not find a fragment in a protected collection', () => { - return api.fragments.get({ collection, fragmentId: fragment.id }) - .expect(STATUS.NOT_FOUND) - }) + it('can not find a fragment in a protected collection', () => + api.fragments + .get({ collection, fragmentId: fragment.id }) + .expect(STATUS.NOT_FOUND)) }) describe('collections filtering by object and properties', () => { @@ -71,24 +83,24 @@ describe('unauthenticated/public api', () => { publicCollection = new Collection({ title: 'Public collection', published: true, - nonPublicProperty: 'example' + nonPublicProperty: 'example', }) await publicCollection.save() privateCollection = new Collection({ - title: 'Private collection' + title: 'Private collection', }) await privateCollection.save() }) - it('can only see the filtered list of collections and only filtered properties in each collection', () => { - return api.collections.list().expect(STATUS.OK).then( - res => { + it('can only see the filtered list of collections and only filtered properties in each collection', () => + api.collections + .list() + .expect(STATUS.OK) + .then(res => { const collections = res.body - expect(collections.length).toEqual(1) + expect(collections).toHaveLength(1) expect(Object.keys(collections[0])).toEqual(['id', 'title', 'owners']) - } - ) - }) + })) }) }) diff --git a/packages/server/test/api_users_test.js b/packages/server/test/api_users_test.js index 435208f8532af6d57540fec92377dcd71e3c7c1d..5a5fbde72174ea683fc2cbd758809f3173d96408 100644 --- a/packages/server/test/api_users_test.js +++ b/packages/server/test/api_users_test.js @@ -28,86 +28,62 @@ describe('users api', () => { otherUser = await user.save() }) - afterEach(() => { - return User.find(otherUser.id) - .then(user => user.delete()) - .catch(() => {}) // we might have already deleted the user - }) + afterEach( + () => + User.find(otherUser.id) + .then(user => user.delete()) + .catch(() => {}), // we might have already deleted the user + ) - it('can get a list of users', () => { - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.users.get(null, token).expect(STATUS.OK) - ).then( - res => { - expect(res.body.users.length).toBe(2) + it('can get a list of users', () => + api.users.authenticate + .post(fixtures.user) + .then(token => api.users.get(null, token).expect(STATUS.OK)) + .then(res => { + expect(res.body.users).toHaveLength(2) expect(res.body.users[0].username).not.toBe(undefined) - } - ) - }) + })) - it('can get another user', () => { - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.users.get(otherUser.id, token).expect(STATUS.OK) - ).then( - res => { + it('can get another user', () => + api.users.authenticate + .post(fixtures.user) + .then(token => api.users.get(otherUser.id, token).expect(STATUS.OK)) + .then(res => { expect(res.body.username).toBe(otherUser.username) - } - ) - }) + })) it('can make another user an admin', () => { - const patchedUser = Object.assign( - otherUser, { admin: true } - ) + const patchedUser = Object.assign(otherUser, { admin: true }) - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.users.patch( - otherUser.id, patchedUser, token - ).expect( - STATUS.OK + return api.users.authenticate + .post(fixtures.user) + .then(token => + api.users.patch(otherUser.id, patchedUser, token).expect(STATUS.OK), ) - ) }) - it('deletes a user', () => { - return api.users.authenticate.post( - fixtures.user - ).then( - token => api.users.del(otherUser.id, token).expect(STATUS.OK) - ) - }) + it('deletes a user', () => + api.users.authenticate + .post(fixtures.user) + .then(token => api.users.del(otherUser.id, token).expect(STATUS.OK))) }) describe('unauthenticated user', () => { - it( - 'can not get a list of users', - () => api.users.get().expect(STATUS.UNAUTHORIZED) - ) + it('can not get a list of users', () => + api.users.get().expect(STATUS.UNAUTHORIZED)) it('cannot sign up as an admin directly', () => { - const fakeAdmin = Object.assign( - {}, fixtures.otherUser, { admin: true } - ) + const fakeAdmin = Object.assign({}, fixtures.otherUser, { admin: true }) return api.users.post(fakeAdmin).expect(STATUS.BAD_REQUEST) }) - it('can sign up', () => { - return api.users.post( - fixtures.otherUser - ).expect( - STATUS.CREATED - ).then( - res => { + it('can sign up', () => + api.users + .post(fixtures.otherUser) + .expect(STATUS.CREATED) + .then(res => { expect(res.body.username).toBe(fixtures.otherUser.username) - } - ) - }) + })) }) describe('new user', () => { @@ -118,37 +94,44 @@ describe('users api', () => { otherUser = await user.save() }) - afterEach(() => { - return User.find(otherUser.id) - .then(user => user.delete()) - .catch(() => {}) // we might have already deleted the user - }) - - it('cant log in with the wrong username', () => { - return api.users.authenticate.post({ - username: 'wrongusername', - password: 'wrongpassword' - }, { - expect: false, - token: false - }).then(res => { - expect(res.statusCode).toEqual(STATUS.UNAUTHORIZED) - } - ) - }) + afterEach( + () => + User.find(otherUser.id) + .then(user => user.delete()) + .catch(() => {}), // we might have already deleted the user + ) - it('cant log in with the wrong password', () => { - return api.users.authenticate.post({ - username: otherUser.username, - password: 'wrongpassword' - }, { - expect: false, - token: false - }).then(res => { - expect(res.statusCode).toEqual(STATUS.UNAUTHORIZED) - } - ) - }) + it('cant log in with the wrong username', () => + api.users.authenticate + .post( + { + username: 'wrongusername', + password: 'wrongpassword', + }, + { + expect: false, + token: false, + }, + ) + .then(res => { + expect(res.statusCode).toEqual(STATUS.UNAUTHORIZED) + })) + + it('cant log in with the wrong password', () => + api.users.authenticate + .post( + { + username: otherUser.username, + password: 'wrongpassword', + }, + { + expect: false, + token: false, + }, + ) + .then(res => { + expect(res.statusCode).toEqual(STATUS.UNAUTHORIZED) + })) it('can verify its token', async () => { const token = await api.users.authenticate.post(fixtures.otherUser) @@ -158,75 +141,56 @@ describe('users api', () => { expect(res.body.token).toBe(token) }) - it('can not get a list of users', () => { - return api.users.authenticate.post( - fixtures.otherUser - ).then( - token => api.users.get(null, token).expect(STATUS.FORBIDDEN) - ) - }) + it('can not get a list of users', () => + api.users.authenticate + .post(fixtures.otherUser) + .then(token => api.users.get(null, token).expect(STATUS.FORBIDDEN))) - it('can not delete other users', () => { - return api.users.authenticate.post( - fixtures.otherUser - ).then( - token => api.users.del(userId, token).expect(STATUS.FORBIDDEN) - ) - }) + it('can not delete other users', () => + api.users.authenticate + .post(fixtures.otherUser) + .then(token => api.users.del(userId, token).expect(STATUS.FORBIDDEN))) - it('can not get other users', () => { - return api.users.authenticate.post( - fixtures.otherUser - ).then( - token => api.users.get(userId, token).expect(STATUS.FORBIDDEN) - ) - }) + it('can not get other users', () => + api.users.authenticate + .post(fixtures.otherUser) + .then(token => api.users.get(userId, token).expect(STATUS.FORBIDDEN))) - it('can get itself', () => { - return api.users.authenticate.post( - fixtures.otherUser - ).then( - token => api.users.get(otherUser.id, token).expect(STATUS.OK) - ).then( - res => { + it('can get itself', () => + api.users.authenticate + .post(fixtures.otherUser) + .then(token => api.users.get(otherUser.id, token).expect(STATUS.OK)) + .then(res => { expect(res.body.id).toBe(otherUser.id) expect(res.body.username).toBe(fixtures.otherUser.username) - } - ) - }) + })) it('can not make itself admin', () => { const newself = Object.assign( - { id: otherUser.id, admin: true }, fixtures.otherUser + { id: otherUser.id, admin: true }, + fixtures.otherUser, ) - return api.users.authenticate.post( - fixtures.otherUser - ).then( - token => api.users.patch( - otherUser.id, newself, token - ).expect( - STATUS.FORBIDDEN + return api.users.authenticate + .post(fixtures.otherUser) + .then(token => + api.users + .patch(otherUser.id, newself, token) + .expect(STATUS.FORBIDDEN), ) - ) }) it('updates itself', () => { const newSelf = Object.assign({}, otherUser, fixtures.updatedUser) - return api.users.authenticate.post( - fixtures.otherUser - ).then( - token => api.users.patch( - otherUser.id, newSelf, token - ).expect( - STATUS.OK + return api.users.authenticate + .post(fixtures.otherUser) + .then(token => + api.users.patch(otherUser.id, newSelf, token).expect(STATUS.OK), ) - ) }) it('authenticates an updated user', async () => { - // authenticate const token = await api.users.authenticate.post(fixtures.otherUser) @@ -241,43 +205,43 @@ describe('users api', () => { it('persists an updated user', () => { const newSelf = Object.assign({}, otherUser, fixtures.updatedUser) - return api.users.authenticate.post( - fixtures.otherUser - ).then( - token => api.users.patch( - otherUser.id, newSelf, token - ).expect( - STATUS.OK - ).then( - () => token + return api.users.authenticate + .post(fixtures.otherUser) + .then(token => + api.users + .patch(otherUser.id, newSelf, token) + .expect(STATUS.OK) + .then(() => token), ) - ).then( - token => api.users.get(otherUser.id, token).expect(STATUS.OK) - ).then( - res => { + .then(token => api.users.get(otherUser.id, token).expect(STATUS.OK)) + .then(res => { expect(res.body.id).toBe(otherUser.id) expect(res.body.username).toBe(fixtures.updatedUser.username) - } - ) + }) }) it('user can delete itself', async () => { // authenticate - const otherUserToken = await api.users.authenticate.post(fixtures.otherUser) + const otherUserToken = await api.users.authenticate.post( + fixtures.otherUser, + ) // change username, email and password const updatedUser = Object.assign({}, otherUser, fixtures.updatedUser) - await api.users.patch(otherUser.id, updatedUser, otherUserToken).expect(STATUS.OK) + await api.users + .patch(otherUser.id, updatedUser, otherUserToken) + .expect(STATUS.OK) // authenticate with updated details - const updatedUserToken = await api.users.authenticate.post(fixtures.updatedUser) + const updatedUserToken = await api.users.authenticate.post( + fixtures.updatedUser, + ) // delete the updated user await api.users.del(otherUser.id, updatedUserToken).expect(STATUS.OK) }) }) - it('cannot create a user if user exists', () => { - return api.users.post(fixtures.user).expect(STATUS.CONFLICT) - }) + it('cannot create a user if user exists', () => + api.users.post(fixtures.user).expect(STATUS.CONFLICT)) }) diff --git a/packages/server/test/fixtures/fixtures.js b/packages/server/test/fixtures/fixtures.js index fc1959b68b19a4c889f3c82ba51a87e488b5cd1e..c442fb513f9d4ac077db49478a9ded6e9402b700 100644 --- a/packages/server/test/fixtures/fixtures.js +++ b/packages/server/test/fixtures/fixtures.js @@ -1,101 +1,101 @@ const collection = { - 'type': 'collection', - 'title': 'Science Blogger posts', - 'published': true + type: 'collection', + title: 'Science Blogger posts', + published: true, } const collection2 = { - 'type': 'collection', - 'title': 'Second collection', - 'published': true + type: 'collection', + title: 'Second collection', + published: true, } const updatedCollection = { - 'title': 'Update Blogger posts' + title: 'Update Blogger posts', } const fragment = { - 'type': 'fragment', - 'fragmentType': 'blogpost', - 'title': 'Just your regular blogpost', - 'source': '<blog></blog>', - 'presentation': '<p></p>' + type: 'fragment', + fragmentType: 'blogpost', + title: 'Just your regular blogpost', + source: '<blog></blog>', + presentation: '<p></p>', } const updatedFragment = { - 'fragmentType': 'blogpost', - 'source': '<blog><title>Updated</title></blog>', - 'presentation': '<p><h1>Updated</h1></p>' + fragmentType: 'blogpost', + source: '<blog><title>Updated</title></blog>', + presentation: '<p><h1>Updated</h1></p>', } const user = { - 'type': 'user', - 'username': 'testuser', - 'email': 'test@example.com', - 'password': 'test' + type: 'user', + username: 'testuser', + email: 'test@example.com', + password: 'test', } const updatedUser = { - 'username': 'changeduser', - 'email': 'changed@email.com', - 'password': 'changed' + username: 'changeduser', + email: 'changed@email.com', + password: 'changed', } const otherUser = { - 'type': 'user', - 'username': 'anotheruser', - 'email': 'another@com.nz', - 'password': 'rubgy' + type: 'user', + username: 'anotheruser', + email: 'another@com.nz', + password: 'rubgy', } const adminUser = { - 'type': 'user', - 'username': 'admin', - 'email': 'admin@admins.example.org', - 'password': 'admin', - 'admin': true + type: 'user', + username: 'admin', + email: 'admin@admins.example.org', + password: 'admin', + admin: true, } const contribTeamType = { name: 'Contributors', - permissions: 'POST' + permissions: 'POST', } const readerTeamType = { name: 'Readers', - permissions: 'GET' + permissions: 'GET', } const teams = { contributors: contribTeamType, - readers: readerTeamType + readers: readerTeamType, } const contributorTeam = { type: 'team', name: 'My contributors', teamType: teams.contributors, - object: fragment + object: fragment, } const readerTeam = { type: 'team', name: 'My readers', teamType: teams.readers, - object: fragment + object: fragment, } module.exports = { - collection: collection, + collection, collection2, - updatedCollection: updatedCollection, - fragment: fragment, - updatedFragment: updatedFragment, - user: user, - updatedUser: updatedUser, - otherUser: otherUser, - adminUser: adminUser, - teams: teams, - contributorTeam: contributorTeam, - readerTeam: readerTeam + updatedCollection, + fragment, + updatedFragment, + user, + updatedUser, + otherUser, + adminUser, + teams, + contributorTeam, + readerTeam, } diff --git a/packages/server/test/helpers/api.js b/packages/server/test/helpers/api.js index ba4647d0da398cb20eefa35bedfd1f91fc3e3ed6..44d6cc449384b68e289a40a9947fc2b6eb1a359d 100644 --- a/packages/server/test/helpers/api.js +++ b/packages/server/test/helpers/api.js @@ -8,7 +8,7 @@ const COLLECTIONS_ROOT = '/api/collections/' const authorizedRequest = (req, token) => { if (token) { - req.set('Authorization', 'Bearer ' + token) + req.set('Authorization', `Bearer ${token}`) } return req @@ -29,13 +29,9 @@ const fragments = { url = '/api/fragments' } - const req = request( - api - ).post( - url - ).send( - fragment - ) + const req = request(api) + .post(url) + .send(fragment) return authorizedRequest(req, token) }, @@ -66,12 +62,12 @@ const fragments = { url = '/api/fragments' } - if (fragmentId) url += '/' + fragmentId + if (fragmentId) url += `/${fragmentId}` if (opts.fields) { - url += '?' + querystring.stringify({ - fields: opts.fields.join(',') - }) + url += `?${querystring.stringify({ + fields: opts.fields.join(','), + })}` } const req = request(api).get(url) @@ -89,112 +85,70 @@ const fragments = { const req = request(api).get(url) return authorizedRequest(req, token) - } + }, } const users = { authenticate: { post: (user, opts = {}) => { - let expect = opts.expect === undefined ? true : opts.expect - let token = opts.token === undefined ? true : opts.token - - let req = request( - api - ).post( - '/api/users/authenticate' - ).send( - { + const expect = opts.expect === undefined ? true : opts.expect + const token = opts.token === undefined ? true : opts.token + + let req = request(api) + .post('/api/users/authenticate') + .send({ username: user.username, - password: user.password - } - ) + password: user.password, + }) if (expect) { - req = req.expect( - STATUS.CREATED - ) + req = req.expect(STATUS.CREATED) } if (token) { - return req.then( - res => res.body.token - ) - } else { - return req + return req.then(res => res.body.token) } + return req }, get: token => { - const req = request( - api - ).get( - '/api/users/authenticate' - ).set( - 'Authorization', 'Bearer ' + token - ) + const req = request(api) + .get('/api/users/authenticate') + .set('Authorization', `Bearer ${token}`) return req - } - }, - post: user => { - return request( - api - ).post( - '/api/users' - ).send( - user - ) + }, }, + post: user => + request(api) + .post('/api/users') + .send(user), // deprecated: use patch instead put: (userId, user, token) => { - const req = request( - api - ).put( - `/api/users/${userId}` - ).send( - user - ) + const req = request(api) + .put(`/api/users/${userId}`) + .send(user) - return token ? req.set( - 'Authorization', 'Bearer ' + token - ) : req + return token ? req.set('Authorization', `Bearer ${token}`) : req }, patch: (userId, user, token) => { - const req = request( - api - ).patch( - `/api/users/${userId}` - ).send( - user - ) + const req = request(api) + .patch(`/api/users/${userId}`) + .send(user) - return token ? req.set( - 'Authorization', 'Bearer ' + token - ) : req + return token ? req.set('Authorization', `Bearer ${token}`) : req }, get: (userId, token) => { const url = `/api/users${userId ? `/${userId}` : ''}` - const req = request( - api - ).get( - url - ) + const req = request(api).get(url) - return token ? req.set( - 'Authorization', 'Bearer ' + token - ) : req + return token ? req.set('Authorization', `Bearer ${token}`) : req }, del: (userId, token) => { - const req = request( - api - ).delete( - `/api/users/${userId}` - ) + const req = request(api).delete(`/api/users/${userId}`) - return token ? req.set( - 'Authorization', 'Bearer ' + token - ) : req - } + return token ? req.set('Authorization', `Bearer ${token}`) : req + }, } const collections = { @@ -205,11 +159,14 @@ const collections = { query.fields = query.fields.join(',') } - const req = request(api).get(url).query(query) + const req = request(api) + .get(url) + .query(query) return authorizedRequest(req, token) }, create: (collection, token) => { - const req = request(api).post(COLLECTIONS_ROOT) + const req = request(api) + .post(COLLECTIONS_ROOT) .send(collection) return authorizedRequest(req, token) }, @@ -218,7 +175,8 @@ const collections = { return authorizedRequest(req, token) }, update: (collectionId, patch, token) => { - const req = request(api).patch(COLLECTIONS_ROOT + collectionId) + const req = request(api) + .patch(COLLECTIONS_ROOT + collectionId) .send(patch) return authorizedRequest(req, token) }, @@ -227,134 +185,111 @@ const collections = { return authorizedRequest(req, token) }, listTeams: (collectionId, token) => { - const req = request(api).get(COLLECTIONS_ROOT + collectionId + '/teams') + const req = request(api).get(`${COLLECTIONS_ROOT + collectionId}/teams`) return authorizedRequest(req, token) }, listFragments: (collectionId, token, options) => { - let url = COLLECTIONS_ROOT + collectionId + '/fragments' + let url = `${COLLECTIONS_ROOT + collectionId}/fragments` if (options && options.fields) { - url += '?' + querystring.stringify({ - fields: options.fields.join(',') - }) + url += `?${querystring.stringify({ + fields: options.fields.join(','), + })}` } const req = request(api).get(url) return authorizedRequest(req, token) }, createFragment: (collectionId, fragment, token) => { - const req = request(api).post(COLLECTIONS_ROOT + collectionId + '/fragments') + const req = request(api) + .post(`${COLLECTIONS_ROOT + collectionId}/fragments`) .send(fragment) return authorizedRequest(req, token) }, retrieveFragment: (collectionId, fragmentId, token) => { - const req = request(api).get(COLLECTIONS_ROOT + collectionId + '/fragments/' + fragmentId) + const req = request(api).get( + `${COLLECTIONS_ROOT + collectionId}/fragments/${fragmentId}`, + ) return authorizedRequest(req, token) }, updateFragment: (collectionId, fragmentId, patch, token) => { - const req = request(api).patch(COLLECTIONS_ROOT + collectionId + '/fragments/' + fragmentId) + const req = request(api) + .patch(`${COLLECTIONS_ROOT + collectionId}/fragments/${fragmentId}`) .send(patch) return authorizedRequest(req, token) }, deleteFragment: (collectionId, fragmentId, token) => { - const req = request(api).delete(COLLECTIONS_ROOT + collectionId + '/fragments/' + fragmentId) + const req = request(api).delete( + `${COLLECTIONS_ROOT + collectionId}/fragments/${fragmentId}`, + ) return authorizedRequest(req, token) }, listFragmentTeams: (collectionId, fragmentId, token) => { - const req = request(api).get(`${COLLECTIONS_ROOT}${collectionId}/fragments/${fragmentId}/teams`) + const req = request(api).get( + `${COLLECTIONS_ROOT}${collectionId}/fragments/${fragmentId}/teams`, + ) return authorizedRequest(req, token) - } + }, } const teams = { get: (token, teamId) => { const url = `/api/teams/${teamId}` - return request( - api - ).get( - url - ).set( - 'Authorization', 'Bearer ' + token - ) + return request(api) + .get(url) + .set('Authorization', `Bearer ${token}`) }, list: (token, collection) => { - const collectionId = () => { - return isString(collection) ? collection : collection.id - } + const collectionId = () => + isString(collection) ? collection : collection.id const collectionpart = collection ? `/collections/${collectionId()}` : '' const url = `/api${collectionpart}/teams` - return request( - api - ).get( - url - ).set( - 'Authorization', 'Bearer ' + token - ) + return request(api) + .get(url) + .set('Authorization', `Bearer ${token}`) }, post: (team, token) => { const url = `/api/teams` - return request( - api - ).post( - url - ).send( - team - ).set( - 'Authorization', 'Bearer ' + token - ) + return request(api) + .post(url) + .send(team) + .set('Authorization', `Bearer ${token}`) }, patch: (team, teamId, token) => { const teamPart = teamId ? `/${teamId}` : '' const url = `/api/teams${teamPart}` - return request( - api - ).patch( - url - ).send( - team - ).set( - 'Authorization', 'Bearer ' + token - ) - } + return request(api) + .patch(url) + .send(team) + .set('Authorization', `Bearer ${token}`) + }, } const upload = { post: (file, token) => { - const req = request( - api - ).post( - '/api/upload' - ).attach( - 'file', file - ) + const req = request(api) + .post('/api/upload') + .attach('file', file) - return token ? req.set( - 'Authorization', 'Bearer ' + token - ) : req + return token ? req.set('Authorization', `Bearer ${token}`) : req }, get: (path, token) => { - const req = request( - api - ).get( - path - ) + const req = request(api).get(path) - return token ? req.set( - 'Authorization', 'Bearer ' + token - ) : req - } + return token ? req.set('Authorization', `Bearer ${token}`) : req + }, } module.exports = { - fragments: fragments, - users: users, - collections: collections, - teams: teams, - upload: upload, - api: api + fragments, + users, + collections, + teams, + upload, + api, } diff --git a/packages/server/test/helpers/authsome_mode.js b/packages/server/test/helpers/authsome_mode.js index 8a3a3294f363a4caf9176aac1a42c2b87ff4790c..6c8308a95cd1912df45cb5f93f934182b96d559c 100644 --- a/packages/server/test/helpers/authsome_mode.js +++ b/packages/server/test/helpers/authsome_mode.js @@ -2,7 +2,7 @@ const get = require('lodash/get') const pickBy = require('lodash/pickBy') const omit = require('lodash/omit') -async function teamPermissions (user, operation, object, context) { +async function teamPermissions(user, operation, object, context) { const collection = get(object, 'collection') if (collection) { @@ -13,31 +13,40 @@ async function teamPermissions (user, operation, object, context) { for (const teamId of user.teams) { const team = await context.models.Team.find(teamId) - if (team.teamType.permissions === 'POST' && - team.object.id === collection.id && - operation === 'POST') { + if ( + team.teamType.permissions === 'POST' && + team.object.id === collection.id && + operation === 'POST' + ) { return true - } else if (team.teamType.permissions === 'PATCH' && - team.object.id === object.id && - operation === 'PATCH') { + } else if ( + team.teamType.permissions === 'PATCH' && + team.object.id === object.id && + operation === 'PATCH' + ) { return true } } } } -function unauthenticatedUser (operation, object) { +function unauthenticatedUser(operation, object) { // Public/unauthenticated users can GET /collections, filtered by 'published' if (operation === 'GET' && object && object.path === '/collections') { return { - filter: (collections) => collections.filter(collection => collection.published) + filter: collections => + collections.filter(collection => collection.published), } } // Public/unauthenticated users can GET /collections/:id/fragments, filtered by 'published' - if (operation === 'GET' && object && object.path === '/collections/:id/fragments') { + if ( + operation === 'GET' && + object && + object.path === '/collections/:id/fragments' + ) { return { - filter: (fragments) => fragments.filter(fragment => fragment.published) + filter: fragments => fragments.filter(fragment => fragment.published), } } @@ -45,9 +54,10 @@ function unauthenticatedUser (operation, object) { if (operation === 'GET' && object && object.type === 'collection') { if (object.published) { return { - filter: (collection) => pickBy(collection, (_, key) => { - return ['id', 'title', 'owners'].includes(key) - }) + filter: collection => + pickBy(collection, (_, key) => + ['id', 'title', 'owners'].includes(key), + ), } } } @@ -55,9 +65,10 @@ function unauthenticatedUser (operation, object) { if (operation === 'GET' && object && object.type === 'fragment') { if (object.published) { return { - filter: (fragment) => pickBy(fragment, (_, key) => { - return ['id', 'title', 'source', 'presentation', 'owners'].includes(key) - }) + filter: fragment => + pickBy(fragment, (_, key) => + ['id', 'title', 'source', 'presentation', 'owners'].includes(key), + ), } } } @@ -65,18 +76,18 @@ function unauthenticatedUser (operation, object) { return false } -async function authenticatedUser (user, operation, object, context) { +async function authenticatedUser(user, operation, object, context) { // Allow the authenticated user to POST a collection (but not with a 'filtered' property) if (operation === 'POST' && object.path === '/collections') { return { - filter: (collection) => omit(collection, 'filtered') + filter: collection => omit(collection, 'filtered'), } } // Allow the authenticated user to GET collections they own if (operation === 'GET' && object === '/collections/') { return { - filter: (collection) => collection.owners.includes(user.id) + filter: collection => collection.owners.includes(user.id), } } @@ -92,8 +103,14 @@ async function authenticatedUser (user, operation, object, context) { } } - if (operation === 'GET' && get(object, 'type') === 'team' && get(object, 'object.type') === 'collection') { - const collection = await context.models.Collection.find(get(object, 'object.id')) + if ( + operation === 'GET' && + get(object, 'type') === 'team' && + get(object, 'object.type') === 'collection' + ) { + const collection = await context.models.Collection.find( + get(object, 'object.id'), + ) if (collection.owners.includes(user.id)) { return true } @@ -104,7 +121,9 @@ async function authenticatedUser (user, operation, object, context) { // if they are one of the owners of this collection if (['POST', 'PATCH'].includes(operation) && get(object, 'type') === 'team') { if (get(object, 'object.type') === 'collection') { - const collection = await context.models.Collection.find(get(object, 'object.id')) + const collection = await context.models.Collection.find( + get(object, 'object.id'), + ) if (collection.owners.includes(user.id)) { return true } @@ -139,7 +158,7 @@ async function authenticatedUser (user, operation, object, context) { // Only allow filtered updating (mirroring filtered creation) for non-admin users) if (operation === 'PATCH') { return { - filter: (collection) => omit(collection, 'filtered') + filter: collection => omit(collection, 'filtered'), } } } @@ -156,7 +175,7 @@ async function authenticatedUser (user, operation, object, context) { return unauthenticatedUser(operation, object) } -const authsomeMode = async function (userId, operation, object, context) { +const authsomeMode = async function(userId, operation, object, context) { if (!userId) { return unauthenticatedUser(operation, object) } diff --git a/packages/server/test/helpers/basic_collection.js b/packages/server/test/helpers/basic_collection.js index ab62a828e5172e60f41de1e565140b7ad15a2ad9..e734b99c1593ca5db3e8d04d6b35d7da0c095db4 100644 --- a/packages/server/test/helpers/basic_collection.js +++ b/packages/server/test/helpers/basic_collection.js @@ -1,9 +1,4 @@ const fixtures = require('../fixtures/fixtures') -module.exports = () => { - return require( - '../../src/setup-base' - ).setup( - fixtures.user, fixtures.collection - ) -} +module.exports = () => + require('../../src/setup-base').setup(fixtures.user, fixtures.collection) diff --git a/packages/server/test/helpers/db_cleaner.js b/packages/server/test/helpers/db_cleaner.js index 9b0747e24b77071075926020be517b9a76b44a2d..874b1bb408c722623644845629880b0393595aa2 100644 --- a/packages/server/test/helpers/db_cleaner.js +++ b/packages/server/test/helpers/db_cleaner.js @@ -1,9 +1,7 @@ -'use strict' - const createDb = require('../../src/db') const logger = require('@pubsweet/logger') -let dbCleaner = async () => { +const dbCleaner = async () => { await global.db.destroy() global.db = createDb() diff --git a/packages/server/test/helpers/fragment.js b/packages/server/test/helpers/fragment.js index 8ef8aa7a8e71e0403b48565d12acaf7b80736e7e..140554198723beb490238760bc8123e8b17324e1 100644 --- a/packages/server/test/helpers/fragment.js +++ b/packages/server/test/helpers/fragment.js @@ -6,12 +6,8 @@ module.exports = (opts, collection) => { const fragment = new Fragment(fixtures.fragment) assign(fragment, opts) - return fragment.save().then( - () => { - collection.addFragment(fragment) - return collection.save().then( - () => fragment - ) - } - ) + return fragment.save().then(() => { + collection.addFragment(fragment) + return collection.save().then(() => fragment) + }) } diff --git a/packages/server/test/helpers/set_team.js b/packages/server/test/helpers/set_team.js index 60a49901ac70cea93f078ccbf143dfcd7ec5a0be..728f5e63d2ce6e7aacbf98c7b577a7daf4cc1cb3 100644 --- a/packages/server/test/helpers/set_team.js +++ b/packages/server/test/helpers/set_team.js @@ -3,23 +3,14 @@ const STATUS = require('http-status-codes') const api = require('./api') const fixtures = require('../fixtures/fixtures') -module.exports = (members, collection, team) => { - return api.users.authenticate.post( - fixtures.user - ).then( - (token) => { - team.name = 'Test team' - team.members = members - team.object = { - id: collection.id, - type: 'collection' - } - - return api.teams.post( - team, token - ).expect( - STATUS.CREATED - ) +module.exports = (members, collection, team) => + api.users.authenticate.post(fixtures.user).then(token => { + team.name = 'Test team' + team.members = members + team.object = { + id: collection.id, + type: 'collection', } - ) -} + + return api.teams.post(team, token).expect(STATUS.CREATED) + }) diff --git a/packages/server/test/mocks/mock_component.js b/packages/server/test/mocks/mock_component.js index 6cda8b1980d428ca77c03366e1a613d65eccf4a0..73803ecbdd56675d122e91a9969014be9cf0caa1 100644 --- a/packages/server/test/mocks/mock_component.js +++ b/packages/server/test/mocks/mock_component.js @@ -1,9 +1,11 @@ const STATUS = require('http-status-codes') -const mockComponent = { backend: () => app => { - app.use('/mock-component', (req, res, next) => { - return res.status(STATUS.OK).json({ok: '!'}) - }) -} } +const mockComponent = { + backend: () => app => { + app.use('/mock-component', (req, res, next) => + res.status(STATUS.OK).json({ ok: '!' }), + ) + }, +} module.exports = mockComponent diff --git a/packages/server/test/model_test.js b/packages/server/test/model_test.js index 0a9f4429b35335ee2ba7dd8a114f47ea67ae8d7b..e1d32b3f960b2183cab7090b2077842845f13e81 100644 --- a/packages/server/test/model_test.js +++ b/packages/server/test/model_test.js @@ -8,7 +8,7 @@ const Collection = require('../src/models/Collection') const dbCleaner = require('./helpers/db_cleaner') const fixtures = require('./fixtures/fixtures') -describe('Model', function () { +describe('Model', () => { let user let otherUser @@ -20,35 +20,41 @@ describe('Model', function () { it('raises an error if trying to find on a destroyed database', () => { expect.hasAssertions() - return global.db.destroy().then( - () => Model.findByField('field', 'value') - ).catch(err => { - expect(err.name).toEqual('Error') - }).then(() => { - global.db = createDb() - }) + return global.db + .destroy() + .then(() => Model.findByField('field', 'value')) + .catch(err => { + expect(err.name).toEqual('Error') + }) + .then(() => { + global.db = createDb() + }) }) it('raises an error if trying to find all on a destroyed database', () => { expect.hasAssertions() - return global.db.destroy().then( - () => User.all() - ).catch(err => { - expect(err.name).toEqual('Error') - }).then(() => { - global.db = createDb() - }) + return global.db + .destroy() + .then(() => User.all()) + .catch(err => { + expect(err.name).toEqual('Error') + }) + .then(() => { + global.db = createDb() + }) }) it('raises an error if trying to save on a destroyed database', () => { expect.hasAssertions() - return global.db.destroy().then( - () => user.save() - ).catch(err => { - expect(err.name).toEqual('Error') - }).then(() => { - global.db = createDb() - }) + return global.db + .destroy() + .then(() => user.save()) + .catch(err => { + expect(err.name).toEqual('Error') + }) + .then(() => { + global.db = createDb() + }) }) it('initially has no owner', () => { @@ -92,7 +98,9 @@ describe('Model', function () { expect.hasAssertions() return user.save().catch(err => { expect(err.name).toEqual('ValidationError') - expect(err.message).toEqual('child "email" fails because ["email" must be a valid email]') + expect(err.message).toEqual( + 'child "email" fails because ["email" must be a valid email]', + ) }) }) @@ -104,13 +112,13 @@ describe('Model', function () { return fragment.save().catch(err => { expect(err.name).toEqual('ValidationError') expect(err.message).toEqual( - 'child "fragmentType" fails because ["fragmentType" must be one of [blogpost]], child "path" fails because ["path" is required]' + 'child "fragmentType" fails because ["fragmentType" must be one of [blogpost]], child "path" fails because ["path" is required]', ) }) }) it('accepts a fragment with alternative fragmentType', () => { - const fragment = new Fragment({fragmentType: 'file', path: '/one/two'}) + const fragment = new Fragment({ fragmentType: 'file', path: '/one/two' }) return fragment.save() }) @@ -125,20 +133,23 @@ describe('Model', function () { }) it('can find by multiple fields', async () => { - const users = await User.findByField({username: 'testuser', email: 'test@example.com'}) + const users = await User.findByField({ + username: 'testuser', + email: 'test@example.com', + }) expect(users).toHaveLength(1) expect(users[0]).toMatchObject({ username: 'testuser', - email: 'test@example.com' + email: 'test@example.com', }) }) it('can find with complex value', async () => { - const users = await User.findByField('username', {$ne: 'testuser'}) + const users = await User.findByField('username', { $ne: 'testuser' }) expect(users).toHaveLength(1) expect(users[0]).toMatchObject({ username: 'changeduser', - email: 'changed@email.com' + email: 'changed@email.com', }) }) }) diff --git a/packages/server/test/register_components_test.js b/packages/server/test/register_components_test.js index 45747a0160a9e196a169e40db6fbe3503f7922a8..a43a5507f92a84ef04303e10416d28a9472d7a65 100644 --- a/packages/server/test/register_components_test.js +++ b/packages/server/test/register_components_test.js @@ -2,8 +2,14 @@ const STATUS = require('http-status-codes') const request = require('supertest') const path = require('path') const config = require('config') -const mockComponentPath = path.join(process.cwd(), 'test', 'mocks', 'mock_component.js') -config['pubsweet'] = { components: [mockComponentPath] } + +const mockComponentPath = path.join( + process.cwd(), + 'test', + 'mocks', + 'mock_component.js', +) +config.pubsweet = { components: [mockComponentPath] } const api = require('./helpers/api') diff --git a/packages/server/test/start_server_test.js b/packages/server/test/start_server_test.js index bfc53ef7b6988853a8e5c10235324402a2865108..a82260b5220711f07d827d4394c6828cf7b13592 100644 --- a/packages/server/test/start_server_test.js +++ b/packages/server/test/start_server_test.js @@ -15,5 +15,4 @@ describe('Function exported by src/index.js', () => { expect(secondAccess).toHaveProperty('originalServer') return server.close() }) - }) diff --git a/packages/server/test/teams_test.js b/packages/server/test/teams_test.js index 4521f8781da437729d34a8726fecd84962aaa34d..a6706eceafbf712cfcc776e9ed6a699592917da3 100644 --- a/packages/server/test/teams_test.js +++ b/packages/server/test/teams_test.js @@ -12,39 +12,42 @@ const collectionFixture = fixtures.collection const teamFixture = fixtures.contributorTeam const fragmentFixture = fixtures.fragment -describe('Teams model', function () { +describe('Teams model', () => { let adminId let userId let collectionId let fragmentId - beforeEach(function () { - return dbCleaner().then(function () { - return new User(adminFixture).save() - }).then(function (admin) { - adminId = admin.id - return new User(userFixture).save() - }).then(function (user) { - userId = user.id - return new Collection(collectionFixture).save() - }).then(function (collection) { - collectionId = collection.id - return new Fragment(fragmentFixture).save() - }).then(function (fragment) { - fragmentId = fragment.id - }) - }) + beforeEach(() => + dbCleaner() + .then(() => new User(adminFixture).save()) + .then(admin => { + adminId = admin.id + return new User(userFixture).save() + }) + .then(user => { + userId = user.id + return new Collection(collectionFixture).save() + }) + .then(collection => { + collectionId = collection.id + return new Fragment(fragmentFixture).save() + }) + .then(fragment => { + fragmentId = fragment.id + }), + ) - it('can save a team without members', function () { + it('can save a team without members', () => { let team = teamFixture team.name = 'Test team' team.object = { id: collectionId, - type: 'collection' + type: 'collection', } team = new Team(team) - return team.save().then(function (savedTeam) { + return team.save().then(savedTeam => { expect(savedTeam.members).toEqual([]) expect(savedTeam.object).toEqual(team.object) expect(savedTeam.teamType).toEqual(team.teamType) @@ -52,113 +55,119 @@ describe('Teams model', function () { }) }) - it('can save a team with members', function () { + it('can save a team with members', () => { let team = teamFixture team.name = 'Test team' team.object = { id: collectionId, - type: 'collection' + type: 'collection', } team.members = [userId] team = new Team(team) let teamId - return team.save().then(function (savedTeam) { - teamId = savedTeam.id - expect(savedTeam.members).toEqual([userId]) - expect(savedTeam.object).toEqual(team.object) - expect(savedTeam.teamType).toEqual(team.teamType) - expect(savedTeam.name).toEqual(team.name) - return User.find(userId) - }).then(function (user) { - expect(user.teams).toEqual([teamId]) - }) + return team + .save() + .then(savedTeam => { + teamId = savedTeam.id + expect(savedTeam.members).toEqual([userId]) + expect(savedTeam.object).toEqual(team.object) + expect(savedTeam.teamType).toEqual(team.teamType) + expect(savedTeam.name).toEqual(team.name) + return User.find(userId) + }) + .then(user => { + expect(user.teams).toEqual([teamId]) + }) }) - it('can save a team with members based around a fragment', function () { + it('can save a team with members based around a fragment', () => { let team = teamFixture team.name = 'Test team' team.object = { id: fragmentId, - type: 'fragment' + type: 'fragment', } team.members = [userId] team = new Team(team) let teamId - return team.save().then(function (savedTeam) { - teamId = savedTeam.id - expect(savedTeam.members).toEqual([userId]) - expect(savedTeam.object).toEqual(team.object) - expect(savedTeam.teamType).toEqual(team.teamType) - expect(savedTeam.name).toEqual(team.name) - return User.find(userId) - }).then(function (user) { - expect(user.teams).toEqual([teamId]) - }) + return team + .save() + .then(savedTeam => { + teamId = savedTeam.id + expect(savedTeam.members).toEqual([userId]) + expect(savedTeam.object).toEqual(team.object) + expect(savedTeam.teamType).toEqual(team.teamType) + expect(savedTeam.name).toEqual(team.name) + return User.find(userId) + }) + .then(user => { + expect(user.teams).toEqual([teamId]) + }) }) - it('can update a team with members', function () { + it('can update a team with members', () => { let team = teamFixture team.name = 'Test team' team.object = { id: collectionId, - type: 'collection' + type: 'collection', } team.members = [userId] team = new Team(team) let teamId - return team.save().then(function (savedTeam) { - return Team.find(savedTeam.id) - }).then(function (team) { - return team.updateProperties({ - members: [userId, adminId], - rev: team.rev + return team + .save() + .then(savedTeam => Team.find(savedTeam.id)) + .then(team => + team.updateProperties({ + members: [userId, adminId], + rev: team.rev, + }), + ) + .then(team => team.save()) + .then(savedTeam => { + teamId = savedTeam.id + expect(savedTeam.members).toEqual([userId, adminId]) + expect(savedTeam.object).toEqual(team.object) + expect(savedTeam.teamType).toEqual(team.teamType) + expect(savedTeam.name).toEqual(team.name) + return User.find(userId) + }) + .then(user => { + expect(user.teams).toEqual([teamId]) + return User.find(adminId) + }) + .then(admin => { + expect(admin.teams).toEqual([teamId]) }) - }).then(function (team) { - return team.save() - }).then(function (savedTeam) { - teamId = savedTeam.id - expect(savedTeam.members).toEqual([userId, adminId]) - expect(savedTeam.object).toEqual(team.object) - expect(savedTeam.teamType).toEqual(team.teamType) - expect(savedTeam.name).toEqual(team.name) - return User.find(userId) - }).then(function (user) { - expect(user.teams).toEqual([teamId]) - return User.find(adminId) - }).then(function (admin) { - expect(admin.teams).toEqual([teamId]) - }) }) - it('can delete a team with members', function () { + it('can delete a team with members', () => { let team = teamFixture team.name = 'Test team' team.object = { id: collectionId, - type: 'collection' + type: 'collection', } team.members = [userId] team = new Team(team) - return team.save().then( - savedTeam => Team.find(savedTeam.id) - ).then( - team => team.delete() - ).then( - deletedTeam => Team.find(deletedTeam.id) - ).catch(err => { - expect(err.name).toEqual('NotFoundError') - if (err.name !== 'NotFoundError') throw err - }).then( - () => User.find(userId) - ).then( - user => expect(user.teams).toEqual([]) - ) + return team + .save() + .then(savedTeam => Team.find(savedTeam.id)) + .then(team => team.delete()) + .then(deletedTeam => Team.find(deletedTeam.id)) + .catch(err => { + expect(err.name).toEqual('NotFoundError') + if (err.name !== 'NotFoundError') throw err + }) + .then(() => User.find(userId)) + .then(user => expect(user.teams).toEqual([])) }) }) diff --git a/packages/server/test/user_test.js b/packages/server/test/user_test.js index 2359c76daff271c27ddba8dcda52bbea5ddc11cf..813da2ab3b33d15c3756bca1d88e6d51beeb3bc5 100644 --- a/packages/server/test/user_test.js +++ b/packages/server/test/user_test.js @@ -2,9 +2,10 @@ const dbCleaner = require('./helpers/db_cleaner') const User = require('../src/models/User') const fixtures = require('./fixtures/fixtures') + const userFixture = fixtures.user -describe('User', function () { +describe('User', () => { beforeEach(dbCleaner) it('validates passwords correctly after saving to db', async () => { @@ -70,9 +71,9 @@ describe('User', function () { it('uses custom JSON serialization in an array', async () => { const users = [ - {username: 'user1', email: 'user-1@example.com', password: 'foo1'}, - {username: 'user2', email: 'user-2@example.com', password: 'foo2'}, - {username: 'user3', email: 'user-3@example.com', password: 'foo3'} + { username: 'user1', email: 'user-1@example.com', password: 'foo1' }, + { username: 'user2', email: 'user-2@example.com', password: 'foo2' }, + { username: 'user3', email: 'user-3@example.com', password: 'foo3' }, ] await Promise.all(users.map(user => new User(user).save())) @@ -92,9 +93,14 @@ describe('User', function () { it('finds a list of users', async () => { const users = [ - { username: 'user1', email: 'user-1@example.com', password: 'foo1', admin: true }, + { + username: 'user1', + email: 'user-1@example.com', + password: 'foo1', + admin: true, + }, { username: 'user2', email: 'user-2@example.com', password: 'foo2' }, - { username: 'user3', email: 'user-3@example.com', password: 'foo3' } + { username: 'user3', email: 'user-3@example.com', password: 'foo3' }, ] await Promise.all(users.map(user => new User(user).save())) @@ -107,9 +113,14 @@ describe('User', function () { it('finds a single user by field', async () => { const users = [ - { username: 'user1', email: 'user-1@example.com', password: 'foo1', admin: true }, + { + username: 'user1', + email: 'user-1@example.com', + password: 'foo1', + admin: true, + }, { username: 'user2', email: 'user-2@example.com', password: 'foo2' }, - { username: 'user3', email: 'user-3@example.com', password: 'foo3' } + { username: 'user3', email: 'user-3@example.com', password: 'foo3' }, ] await Promise.all(users.map(user => new User(user).save())) @@ -118,10 +129,12 @@ describe('User', function () { expect(item).toBeInstanceOf(User) - expect(item).toEqual(expect.objectContaining({ - username: 'user1', - email: 'user-1@example.com', - admin: true - })) + expect(item).toEqual( + expect.objectContaining({ + username: 'user1', + email: 'user-1@example.com', + admin: true, + }), + ) }) }) diff --git a/packages/server/test/validations_test.js b/packages/server/test/validations_test.js index f9189d0206fcc910e47fc7afa81b8d1a8185927b..b8f0815f8a62643ba86b313916e3ce4becba7b59 100644 --- a/packages/server/test/validations_test.js +++ b/packages/server/test/validations_test.js @@ -1,17 +1,23 @@ const Fragment = require('../src/models/Fragment') const config = require('config') + const appValidations = require(config.validations) const validations = require('../src/models/validations')(appValidations) -describe('Validations export', function () { +describe('Validations export', () => { it('has validations for each type', () => { - expect(Object.keys(validations).sort()).toEqual(['collection', 'fragment', 'team', 'user']) + expect(Object.keys(validations).sort()).toEqual([ + 'collection', + 'fragment', + 'team', + 'user', + ]) }) it('allows fragment with required fields', () => { const fragment = new Fragment({ title: 'Testing', - fragmentType: 'blogpost' + fragmentType: 'blogpost', }) expect(fragment.validate()).toBe(true) @@ -19,7 +25,7 @@ describe('Validations export', function () { it('rejects fragment with missing type', () => { const fragment = new Fragment({ - title: 'Testing' + title: 'Testing', }) fragment.type = undefined diff --git a/packages/styleguide/src/components/Wrapper.js b/packages/styleguide/src/components/Wrapper.js index 497248b4afba4bbb31f63f76235e38809b210a16..11c73bf368d41cea6b986b9940854d8b158ae8c2 100644 --- a/packages/styleguide/src/components/Wrapper.js +++ b/packages/styleguide/src/components/Wrapper.js @@ -4,7 +4,7 @@ import { BrowserRouter as Router } from 'react-router-dom' import { reducer as formReducer } from 'redux-form' import { createStore, combineReducers } from 'redux' -import "@pubsweet/theme" +import '@pubsweet/theme' import classes from './Wrapper.local.scss' diff --git a/packages/styleguide/src/webpack-config.js b/packages/styleguide/src/webpack-config.js index 01800d6d9758f9d214bc0242de86c7fd882250b2..102bd2e3eda4506e69b52106a69038283799e317 100644 --- a/packages/styleguide/src/webpack-config.js +++ b/packages/styleguide/src/webpack-config.js @@ -13,7 +13,7 @@ module.exports = dir => { /wax-[^/]+\/src/, /@pubsweet\/[^/]+\/src/, /styleguide\/src/, - /ui\/src/ + /ui\/src/, ] return { diff --git a/packages/theme-plugin/index.js b/packages/theme-plugin/index.js index 1bff77fe4bb5c35cbf9036430e1f8e48e9f34f44..14c31628ff51618769fa8dae0cb0ac811a4b9800 100644 --- a/packages/theme-plugin/index.js +++ b/packages/theme-plugin/index.js @@ -1,36 +1,33 @@ const path = require('path') const moduleName = component => { - const namepart = component - .replace( - /([A-Z])/g, - l => `-${l.toLowerCase()}` - ) + const namepart = component.replace(/([A-Z])/g, l => `-${l.toLowerCase()}`) return `pubsweet-component${namepart}` } -function ThemePlugin (theme) { +function ThemePlugin(theme) { this.theme = theme || 'default' } -const componentName = module => module - .replace('pubsweet-component-', '') - .split('-') - .map(w => `${w.slice(0, 1).toUpperCase()}${w.slice(1)}`) - .join('') +const componentName = module => + module + .replace('pubsweet-component-', '') + .split('-') + .map(w => `${w.slice(0, 1).toUpperCase()}${w.slice(1)}`) + .join('') -ThemePlugin.prototype.apply = function (resolver) { - var theme = this.theme - resolver.plugin('resolve', function (request, callback) { - var fs = resolver.fileSystem +ThemePlugin.prototype.apply = function(resolver) { + const theme = this.theme + resolver.plugin('resolve', function(request, callback) { + const fs = resolver.fileSystem const self = this self.cache = {} const requestJson = JSON.stringify({ path: request.path, - request: request.request + request: request.request, }) const done = () => { @@ -41,7 +38,12 @@ ThemePlugin.prototype.apply = function (resolver) { if (self.cache[requestJson] === true) { return callback() } else if (self.cache[requestJson] !== undefined) { - return self.doResolve('resolve', self.cache[requestJson], 'using path: ' + pathWithTheme, callback) + return self.doResolve( + 'resolve', + self.cache[requestJson], + `using path: ${pathWithTheme}`, + callback, + ) } if (theme === 'default') return done() @@ -50,34 +52,36 @@ ThemePlugin.prototype.apply = function (resolver) { if (/\.local\.scss$/.test(request.request)) { var extension = '.local.scss' - var pathWithoutFiletype = path.dirname(request.request) + '/' + path.basename(request.request, extension) + var pathWithoutFiletype = `${path.dirname( + request.request, + )}/${path.basename(request.request, extension)}` } else if (request.request && path.extname(request.request) === '.scss') { extension = '.scss' - pathWithoutFiletype = path.dirname(request.request) + '/' + path.basename(request.request, extension) + pathWithoutFiletype = `${path.dirname(request.request)}/${path.basename( + request.request, + extension, + )}` } else { self.cache[requestJson] = true return callback() } - var folders = request.path.split('/') - var componentModule = folders.pop() + const folders = request.path.split('/') + const componentModule = folders.pop() - if (!(/pubsweet-component/.test(componentModule))) return done() + if (!/pubsweet-component/.test(componentModule)) return done() - var pathWithTheme = path - .resolve( + var pathWithTheme = + path.resolve( request.path, '..', themeModule, componentName(componentModule), - pathWithoutFiletype + pathWithoutFiletype, ) + extension - var pathWithoutTheme = path - .resolve( - request.path, - pathWithoutFiletype - ) + extension + const pathWithoutTheme = + path.resolve(request.path, pathWithoutFiletype) + extension fs.stat(pathWithoutTheme, (err, stats) => { if (err) return done() @@ -85,19 +89,18 @@ ThemePlugin.prototype.apply = function (resolver) { fs.stat(pathWithTheme, (err, stats) => { if (err) return callback() - var obj = { + const obj = { path: path.dirname(pathWithTheme), request: request.request, query: request.query, - directory: request.directory + directory: request.directory, } self.cache[requestJson] = obj - self.doResolve('resolve', obj, 'using path: ' + pathWithTheme, callback) + self.doResolve('resolve', obj, `using path: ${pathWithTheme}`, callback) }) }) }) } module.exports = ThemePlugin - diff --git a/yarn.lock b/yarn.lock index 1a93c1f93d0b111a03da0d7d065b0d2adfe9bb0c..ef66c3c8768cfaeebcf1c37e36db99170672f8c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -653,7 +653,7 @@ babel-core@^6.0.0, babel-core@^6.26.0: slash "^1.0.0" source-map "^0.5.6" -babel-eslint@^8.0.1, babel-eslint@^8.0.3: +babel-eslint@^8.0.3: version "8.0.3" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.0.3.tgz#f29ecf02336be438195325cd47c468da81ee4e98" dependencies: