diff --git a/services/web/app/src/Features/Metadata/MetaController.js b/services/web/app/src/Features/Metadata/MetaController.js index 2fcc7583c7..1a0140cf18 100644 --- a/services/web/app/src/Features/Metadata/MetaController.js +++ b/services/web/app/src/Features/Metadata/MetaController.js @@ -1,66 +1,61 @@ -/* eslint-disable - max-len, - no-unused-vars, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -let MetaController const OError = require('@overleaf/o-error') const EditorRealTimeController = require('../Editor/EditorRealTimeController') const MetaHandler = require('./MetaHandler') const logger = require('@overleaf/logger') +const { expressify } = require('@overleaf/promise-utils') -module.exports = MetaController = { - getMetadata(req, res, next) { - const { project_id: projectId } = req.params - logger.debug({ projectId }, 'getting all labels for project') - return MetaHandler.getAllMetaForProject( - projectId, - function (err, projectMeta) { - if (err != null) { - OError.tag( - err, - '[MetaController] error getting all labels from project', - { - project_id: projectId, - } - ) - return next(err) - } - return res.json({ projectId, projectMeta }) +async function getMetadata(req, res) { + const { project_id: projectId } = req.params + + logger.debug({ projectId }, 'getting all labels for project') + + let projectMeta + try { + projectMeta = await MetaHandler.promises.getAllMetaForProject(projectId) + } catch (error) { + throw OError.tag( + error, + '[MetaController] error getting all labels from project', + { + project_id: projectId, } ) - }, + } - broadcastMetadataForDoc(req, res, next) { - const { project_id: projectId } = req.params - const { doc_id: docId } = req.params - const { broadcast } = req.body - logger.debug({ projectId, docId, broadcast }, 'getting labels for doc') - return MetaHandler.getMetaForDoc(projectId, docId, function (err, docMeta) { - if (err != null) { - OError.tag(err, '[MetaController] error getting labels from doc', { - project_id: projectId, - doc_id: docId, - }) - return next(err) - } - // default to broadcasting, unless explicitly disabled (for backwards compatibility) - if (broadcast !== false) { - EditorRealTimeController.emitToRoom(projectId, 'broadcastDocMeta', { - docId, - meta: docMeta, - }) - return res.sendStatus(200) - } else { - return res.json({ docId, meta: docMeta }) - } - }) - }, + res.json({ projectId, projectMeta }) +} + +async function broadcastMetadataForDoc(req, res) { + const { project_id: projectId } = req.params + const { doc_id: docId } = req.params + const { broadcast } = req.body + + logger.debug({ projectId, docId, broadcast }, 'getting labels for doc') + + let docMeta + try { + docMeta = await MetaHandler.promises.getMetaForDoc(projectId, docId) + } catch (error) { + throw OError.tag(error, '[MetaController] error getting labels from doc', { + project_id: projectId, + doc_id: docId, + }) + } + + // default to broadcasting, unless explicitly disabled (for backwards compatibility) + if (broadcast === false) { + return res.json({ docId, meta: docMeta }) + } + + EditorRealTimeController.emitToRoom(projectId, 'broadcastDocMeta', { + docId, + meta: docMeta, + }) + + res.sendStatus(200) // 204? +} + +module.exports = { + getMetadata: expressify(getMetadata), + broadcastMetadataForDoc: expressify(broadcastMetadataForDoc), } diff --git a/services/web/app/src/Features/Metadata/MetaHandler.js b/services/web/app/src/Features/Metadata/MetaHandler.js index 1c0c604031..02f2c0d26e 100644 --- a/services/web/app/src/Features/Metadata/MetaHandler.js +++ b/services/web/app/src/Features/Metadata/MetaHandler.js @@ -1,142 +1,127 @@ -/* eslint-disable - n/handle-callback-err, - max-len, - no-cond-assign, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -let MetaHandler const ProjectEntityHandler = require('../Project/ProjectEntityHandler') const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler') const packageMapping = require('./packageMapping') +const { callbackify } = require('@overleaf/promise-utils') -module.exports = MetaHandler = { - labelRegex() { - return /\\label{(.{0,80}?)}/g - }, +/** @typedef {{ + * labels: string[] + * packages: Record>, + * packageNames: string[], + * }} DocMeta + */ - usepackageRegex() { - return /^\\usepackage(?:\[.{0,80}?])?{(.{0,80}?)}/g - }, +/** + * @param {string[]} lines + * @return {Promise} + */ +async function extractMetaFromDoc(lines) { + /** @type {DocMeta} */ + const docMeta = { + labels: [], + packages: {}, + packageNames: [], + } - ReqPackageRegex() { - return /^\\RequirePackage(?:\[.{0,80}?])?{(.{0,80}?)}/g - }, + const labelRe = /\\label{(.{0,80}?)}/g + const packageRe = /^\\usepackage(?:\[.{0,80}?])?{(.{0,80}?)}/g + const reqPackageRe = /^\\RequirePackage(?:\[.{0,80}?])?{(.{0,80}?)}/g - getAllMetaForProject(projectId, callback) { - if (callback == null) { - callback = function () {} + for (const rawLine of lines) { + const line = getNonCommentedContent(rawLine) + + for (const pkg of lineMatches(labelRe, line)) { + docMeta.labels.push(pkg) } - return DocumentUpdaterHandler.flushProjectToMongo( - projectId, - function (err) { - if (err != null) { - return callback(err) - } - return ProjectEntityHandler.getAllDocs(projectId, function (err, docs) { - if (err != null) { - return callback(err) - } - const projectMeta = MetaHandler.extractMetaFromProjectDocs(docs) - return callback(null, projectMeta) - }) - } - ) - }, - getMetaForDoc(projectId, docId, callback) { - if (callback == null) { - callback = function () {} + for (const pkg of lineMatches(packageRe, line, ',')) { + docMeta.packageNames.push(pkg) } - return DocumentUpdaterHandler.flushDocToMongo( - projectId, - docId, - function (err) { - if (err != null) { - return callback(err) - } - return ProjectEntityHandler.getDoc( - projectId, - docId, - function (err, lines) { - if (err != null) { - return callback(err) - } - const docMeta = MetaHandler.extractMetaFromDoc(lines) - return callback(null, docMeta) - } - ) - } - ) - }, - extractMetaFromDoc(lines) { - let pkg - const docMeta = { labels: [], packages: {} } - const packages = [] - const labelRe = MetaHandler.labelRegex() - const packageRe = MetaHandler.usepackageRegex() - const reqPackageRe = MetaHandler.ReqPackageRegex() - for (const rawLine of Array.from(lines)) { - const line = MetaHandler._getNonCommentedContent(rawLine) - let labelMatch - let clean, messy, packageMatch - while ((labelMatch = labelRe.exec(line))) { - let label - if ((label = labelMatch[1])) { - docMeta.labels.push(label) - } - } - while ((packageMatch = packageRe.exec(line))) { - if ((messy = packageMatch[1])) { - for (pkg of Array.from(messy.split(','))) { - if ((clean = pkg.trim())) { - packages.push(clean) - } - } - } - } - while ((packageMatch = reqPackageRe.exec(line))) { - if ((messy = packageMatch[1])) { - for (pkg of Array.from(messy.split(','))) { - if ((clean = pkg.trim())) { - packages.push(clean) - } - } - } - } + for (const pkg of lineMatches(reqPackageRe, line, ',')) { + docMeta.packageNames.push(pkg) } - for (pkg of Array.from(packages)) { - if (packageMapping[pkg] != null) { - docMeta.packages[pkg] = packageMapping[pkg] - } - } - docMeta.packageNames = packages - return docMeta - }, + } - extractMetaFromProjectDocs(projectDocs) { - const projectMeta = {} - for (const _path in projectDocs) { - const doc = projectDocs[_path] - projectMeta[doc._id] = MetaHandler.extractMetaFromDoc(doc.lines) + for (const packageName of docMeta.packageNames) { + if (packageMapping[packageName]) { + docMeta.packages[packageName] = packageMapping[packageName] } - return projectMeta - }, + } - /** - * Trims comment content from line - * @param {string} rawLine - * @returns {string} - */ - _getNonCommentedContent(rawLine) { - return rawLine.replace(/(^|[^\\])%.*/, '$1') - }, + return docMeta +} + +/** + * + * @param {RegExp} matchRe + * @param {string} line + * @param {string} [separator] + * @return {Generator} + */ +function* lineMatches(matchRe, line, separator) { + let match + while ((match = matchRe.exec(line))) { + const matched = match[1].trim() + + if (matched) { + if (separator) { + const items = matched + .split(',') + .map(item => item.trim()) + .filter(Boolean) + + for (const item of items) { + yield item + } + } else { + yield matched + } + } + } +} + +/** + * @param {Record<{ lines: string[] }, any>} projectDocs + * @return {Promise<{}>} + */ +async function extractMetaFromProjectDocs(projectDocs) { + const projectMeta = {} + for (const doc of Object.values(projectDocs)) { + projectMeta[doc._id] = await extractMetaFromDoc(doc.lines) + } + return projectMeta +} + +/** + * Trims comment content from line + * @param {string} rawLine + * @returns {string} + */ +function getNonCommentedContent(rawLine) { + return rawLine.replace(/(^|[^\\])%.*/, '$1') +} + +async function getAllMetaForProject(projectId) { + await DocumentUpdaterHandler.promises.flushProjectToMongo(projectId) + + const docs = await ProjectEntityHandler.promises.getAllDocs(projectId) + + return await extractMetaFromProjectDocs(docs) +} + +async function getMetaForDoc(projectId, docId) { + await DocumentUpdaterHandler.promises.flushDocToMongo(projectId, docId) + + const { lines } = await ProjectEntityHandler.promises.getDoc(projectId, docId) + + return await extractMetaFromDoc(lines) +} + +module.exports = { + promises: { + getAllMetaForProject, + getMetaForDoc, + }, + getAllMetaForProject: callbackify(getAllMetaForProject), + getMetaForDoc: callbackify(getMetaForDoc), } diff --git a/services/web/app/src/Features/Metadata/packageMapping.js b/services/web/app/src/Features/Metadata/packageMapping.js index f0b1a3a1f1..19d843afad 100644 --- a/services/web/app/src/Features/Metadata/packageMapping.js +++ b/services/web/app/src/Features/Metadata/packageMapping.js @@ -1,8 +1,3 @@ -/* eslint-disable - max-len, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. module.exports = { inputenc: [ { diff --git a/services/web/test/unit/src/Metadata/MetaControllerTests.js b/services/web/test/unit/src/Metadata/MetaControllerTests.js index 68ebdd6212..ff5bf12165 100644 --- a/services/web/test/unit/src/Metadata/MetaControllerTests.js +++ b/services/web/test/unit/src/Metadata/MetaControllerTests.js @@ -1,263 +1,164 @@ -/* eslint-disable - max-len, - no-return-assign, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const { expect } = require('chai') const sinon = require('sinon') const modulePath = '../../../../app/src/Features/Metadata/MetaController' const SandboxedModule = require('sandboxed-module') +const MockResponse = require('../helpers/MockResponse') describe('MetaController', function () { beforeEach(function () { - this.projectId = 'somekindofid' this.EditorRealTimeController = { emitToRoom: sinon.stub(), } + this.MetaHandler = { - getAllMetaForProject: sinon.stub(), - getMetaForDoc: sinon.stub(), + promises: { + getAllMetaForProject: sinon.stub(), + getMetaForDoc: sinon.stub(), + }, } - return (this.MetadataController = SandboxedModule.require(modulePath, { + + this.MetadataController = SandboxedModule.require(modulePath, { requires: { '../Editor/EditorRealTimeController': this.EditorRealTimeController, './MetaHandler': this.MetaHandler, }, - })) + }) }) describe('getMetadata', function () { - beforeEach(function () { - this.fakeLabels = { somedoc: ['a_label'] } - this.MetaHandler.getAllMetaForProject = sinon + it('should respond with json', async function () { + const projectMeta = { + 'doc-id': { + labels: ['foo'], + packages: { a: { commands: [] } }, + packageNames: ['a'], + }, + } + + this.MetaHandler.promises.getAllMetaForProject = sinon .stub() - .callsArgWith(1, null, this.fakeLabels) - this.req = { params: { project_id: this.projectId } } - this.res = { json: sinon.stub() } - return (this.next = sinon.stub()) + .resolves(projectMeta) + + const req = { params: { project_id: 'project-id' } } + const res = new MockResponse() + const next = sinon.stub() + + await this.MetadataController.getMetadata(req, res, next) + + this.MetaHandler.promises.getAllMetaForProject.should.have.been.calledWith( + 'project-id' + ) + res.json.should.have.been.calledOnceWith({ + projectId: 'project-id', + projectMeta, + }) + next.should.not.have.been.called }) - it('should call MetaHandler.getAllMetaForProject', function () { - this.MetadataController.getMetadata(this.req, this.res, this.next) - this.MetaHandler.getAllMetaForProject.callCount.should.equal(1) - return this.MetaHandler.getAllMetaForProject - .calledWith(this.projectId) - .should.equal(true) - }) + it('should handle an error', async function () { + this.MetaHandler.promises.getAllMetaForProject = sinon + .stub() + .throws(new Error('woops')) - it('should call not call next with an error', function () { - this.MetadataController.getMetadata(this.req, this.res, this.next) - return this.next.callCount.should.equal(0) - }) + const req = { params: { project_id: 'project-id' } } + const res = new MockResponse() + const next = sinon.stub() - it('should send a json response', function () { - this.MetadataController.getMetadata(this.req, this.res, this.next) - this.res.json.callCount.should.equal(1) - return expect(this.res.json.lastCall.args[0]).to.have.all.keys([ - 'projectId', - 'projectMeta', - ]) - }) + await this.MetadataController.getMetadata(req, res, next) - describe('when MetaHandler.getAllMetaForProject produces an error', function () { - beforeEach(function () { - this.MetaHandler.getAllMetaForProject = sinon - .stub() - .callsArgWith(1, new Error('woops')) - this.req = { params: { project_id: this.projectId } } - this.res = { json: sinon.stub() } - return (this.next = sinon.stub()) - }) - - it('should call MetaHandler.getAllMetaForProject', function () { - this.MetadataController.getMetadata(this.req, this.res, this.next) - this.MetaHandler.getAllMetaForProject.callCount.should.equal(1) - return this.MetaHandler.getAllMetaForProject - .calledWith(this.projectId) - .should.equal(true) - }) - - it('should call next with an error', function () { - this.MetadataController.getMetadata(this.req, this.res, this.next) - this.next.callCount.should.equal(1) - return expect(this.next.lastCall.args[0]).to.be.instanceof(Error) - }) - - it('should not send a json response', function () { - this.MetadataController.getMetadata(this.req, this.res, this.next) - return this.res.json.callCount.should.equal(0) - }) + this.MetaHandler.promises.getAllMetaForProject.should.have.been.calledWith( + 'project-id' + ) + res.json.should.not.have.been.called + next.should.have.been.calledWithMatch(error => error instanceof Error) }) }) describe('broadcastMetadataForDoc', function () { - beforeEach(function () { - this.MetaHandler.getMetaForDoc = sinon - .stub() - .callsArgWith(2, null, this.fakeLabels) + it('should broadcast on broadcast:true ', async function () { + this.MetaHandler.promises.getMetaForDoc = sinon.stub().resolves({ + labels: ['foo'], + packages: { a: { commands: [] } }, + packageNames: ['a'], + }) + this.EditorRealTimeController.emitToRoom = sinon.stub() - this.docId = 'somedoc' - this.res = { sendStatus: sinon.stub(), json: sinon.stub() } - return (this.next = sinon.stub()) + + const req = { + params: { project_id: 'project-id', doc_id: 'doc-id' }, + body: { broadcast: true }, + } + const res = new MockResponse() + const next = sinon.stub() + + await this.MetadataController.broadcastMetadataForDoc(req, res, next) + + this.MetaHandler.promises.getMetaForDoc.should.have.been.calledWith( + 'project-id' + ) + res.json.should.not.have.been.called + res.sendStatus.should.have.been.calledOnceWith(200) + next.should.not.have.been.called + + this.EditorRealTimeController.emitToRoom.should.have.been.calledOnce + const { lastCall } = this.EditorRealTimeController.emitToRoom + expect(lastCall.args[0]).to.equal('project-id') + expect(lastCall.args[1]).to.equal('broadcastDocMeta') + expect(lastCall.args[2]).to.have.all.keys(['docId', 'meta']) }) - describe('with broadcast:true', function () { - beforeEach(function () { - this.req = { - params: { project_id: this.projectId, doc_id: this.docId }, - body: { broadcast: true }, - } - }) + it('should return json on broadcast:false ', async function () { + const docMeta = { + labels: ['foo'], + packages: { a: [] }, + packageNames: ['a'], + } - it('should call MetaHandler.getMetaForDoc', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - this.MetaHandler.getMetaForDoc.callCount.should.equal(1) - return this.MetaHandler.getMetaForDoc - .calledWith(this.projectId) - .should.equal(true) - }) + this.MetaHandler.promises.getMetaForDoc = sinon.stub().resolves(docMeta) - it('should call not call next with an error', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - return this.next.callCount.should.equal(0) - }) + this.EditorRealTimeController.emitToRoom = sinon.stub() - it('should send a success response', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - this.res.sendStatus.callCount.should.equal(1) - return this.res.sendStatus.calledWith(200).should.equal(true) - }) + const req = { + params: { project_id: 'project-id', doc_id: 'doc-id' }, + body: { broadcast: false }, + } + const res = new MockResponse() + const next = sinon.stub() - it('should emit a message to room', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - this.EditorRealTimeController.emitToRoom.callCount.should.equal(1) - const { lastCall } = this.EditorRealTimeController.emitToRoom - expect(lastCall.args[0]).to.equal(this.projectId) - expect(lastCall.args[1]).to.equal('broadcastDocMeta') - return expect(lastCall.args[2]).to.have.all.keys(['docId', 'meta']) + await this.MetadataController.broadcastMetadataForDoc(req, res, next) + + this.MetaHandler.promises.getMetaForDoc.should.have.been.calledWith( + 'project-id' + ) + this.EditorRealTimeController.emitToRoom.should.not.have.been.called + res.json.should.have.been.calledOnceWith({ + docId: 'doc-id', + meta: docMeta, }) + next.should.not.have.been.called }) - describe('with broadcast:false', function () { - beforeEach(function () { - this.req = { - params: { project_id: this.projectId, doc_id: this.docId }, - body: { broadcast: false }, - } - }) + it('should handle an error', async function () { + this.MetaHandler.promises.getMetaForDoc = sinon + .stub() + .throws(new Error('woops')) - it('should call MetaHandler.getMetaForDoc', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - this.MetaHandler.getMetaForDoc.callCount.should.equal(1) - return this.MetaHandler.getMetaForDoc - .calledWith(this.projectId) - .should.equal(true) - }) + this.EditorRealTimeController.emitToRoom = sinon.stub() - it('should call not call next with an error', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - return this.next.callCount.should.equal(0) - }) + const req = { + params: { project_id: 'project-id', doc_id: 'doc-id' }, + body: { broadcast: true }, + } + const res = new MockResponse() + const next = sinon.stub() - it('should send the metadata in the response', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - this.res.json.callCount.should.equal(1) - return this.res.json - .calledWith({ docId: this.docId, meta: this.fakeLabels }) - .should.equal(true) - }) + await this.MetadataController.broadcastMetadataForDoc(req, res, next) - it('should not emit a message to room', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - return this.EditorRealTimeController.emitToRoom.callCount.should.equal( - 0 - ) - }) - }) - - describe('when MetaHandler.getMetaForDoc produces an error', function () { - beforeEach(function () { - this.MetaHandler.getMetaForDoc = sinon - .stub() - .callsArgWith(2, new Error('woops')) - this.EditorRealTimeController.emitToRoom = sinon.stub() - this.docId = 'somedoc' - this.req = { - params: { project_id: this.projectId, doc_id: this.docId }, - body: { broadcast: true }, - } - this.res = { json: sinon.stub() } - return (this.next = sinon.stub()) - }) - - it('should call MetaHandler.getMetaForDoc', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - this.MetaHandler.getMetaForDoc.callCount.should.equal(1) - return this.MetaHandler.getMetaForDoc - .calledWith(this.projectId) - .should.equal(true) - }) - - it('should call next with an error', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - this.next.callCount.should.equal(1) - return expect(this.next.lastCall.args[0]).to.be.instanceof(Error) - }) - - it('should not send a json response', function () { - this.MetadataController.broadcastMetadataForDoc( - this.req, - this.res, - this.next - ) - return this.res.json.callCount.should.equal(0) - }) + this.MetaHandler.promises.getMetaForDoc.should.have.been.calledWith( + 'project-id' + ) + res.json.should.not.have.been.called + next.should.have.been.calledWithMatch(error => error instanceof Error) }) }) }) diff --git a/services/web/test/unit/src/Metadata/MetaHandlerTests.js b/services/web/test/unit/src/Metadata/MetaHandlerTests.js index 91d551baa4..b7d98c954c 100644 --- a/services/web/test/unit/src/Metadata/MetaHandlerTests.js +++ b/services/web/test/unit/src/Metadata/MetaHandlerTests.js @@ -1,15 +1,3 @@ -/* eslint-disable - n/handle-callback-err, - max-len, - no-return-assign, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const { expect } = require('chai') const sinon = require('sinon') const modulePath = '../../../../app/src/Features/Metadata/MetaHandler' @@ -19,13 +7,40 @@ describe('MetaHandler', function () { beforeEach(function () { this.projectId = 'someprojectid' this.docId = 'somedocid' + + this.lines = [ + '\\usepackage{ foo, bar }', + '\\usepackage{baz}', + 'one', + '\\label{aaa}', + 'two', + 'commented label % \\label{bbb}', // bbb should not be in the returned labels + '\\label{ccc}%bar', // ccc should be in the returned labels + '\\label{ddd} % bar', // ddd should be in the returned labels + '\\label{ e,f,g }', // e,f,g should be in the returned labels + ] + + this.docs = { + [this.docId]: { + _id: this.docId, + lines: this.lines, + }, + } + this.ProjectEntityHandler = { - getAllDocs: sinon.stub(), - getDoc: sinon.stub(), + promises: { + getAllDocs: sinon.stub().resolves(this.docs), + getDoc: sinon.stub().resolves(this.docs[this.docId]), + }, } + this.DocumentUpdaterHandler = { - flushDocToMongo: sinon.stub(), + promises: { + flushDocToMongo: sinon.stub().resolves(), + flushProjectToMongo: sinon.stub().resolves(), + }, } + this.packageMapping = { foo: [ { @@ -51,58 +66,47 @@ describe('MetaHandler', function () { ], } - return (this.MetaHandler = SandboxedModule.require(modulePath, { + this.MetaHandler = SandboxedModule.require(modulePath, { requires: { '../Project/ProjectEntityHandler': this.ProjectEntityHandler, '../DocumentUpdater/DocumentUpdaterHandler': this.DocumentUpdaterHandler, './packageMapping': this.packageMapping, }, - })) + }) }) - describe('extractMetaFromDoc', function () { - beforeEach(function () { - return (this.lines = [ - '\\usepackage{foo}', - '\\usepackage{amsmath, booktabs}', - 'one', - 'two', - 'three \\label{aaa}', - 'four five', - '\\label{bbb}', - 'six seven', - ]) - }) + describe('getMetaForDoc', function () { + it('should extract all the labels and packages', async function () { + const result = await this.MetaHandler.promises.getMetaForDoc( + this.projectId, + this.docId + ) - it('should extract all the labels and packages', function () { - const docMeta = this.MetaHandler.extractMetaFromDoc(this.lines) - return expect(docMeta).to.deep.equal({ - labels: ['aaa', 'bbb'], + expect(result).to.deep.equal({ + labels: ['aaa', 'ccc', 'ddd', 'e,f,g'], packages: { - foo: [ - { - caption: '\\bar', - snippet: '\\bar', - meta: 'foo-cmd', - score: 12, - }, - { - caption: '\\bat[]{}', - snippet: '\\bar[$1]{$2}', - meta: 'foo-cmd', - score: 10, - }, - ], + foo: this.packageMapping.foo, + baz: this.packageMapping.baz, }, - packageNames: ['foo', 'amsmath', 'booktabs'], + packageNames: ['foo', 'bar', 'baz'], }) + + this.DocumentUpdaterHandler.promises.flushDocToMongo.should.be.calledWith( + this.projectId, + this.docId + ) + + this.ProjectEntityHandler.promises.getDoc.should.be.calledWith( + this.projectId, + this.docId + ) }) }) - describe('extractMetaFromProjectDocs', function () { - beforeEach(function () { - return (this.docs = { + describe('getAllMetaForProject', function () { + it('should extract all metadata', async function () { + this.ProjectEntityHandler.promises.getAllDocs = sinon.stub().resolves({ doc_one: { _id: 'id_one', lines: ['one', '\\label{aaa} two', 'three'], @@ -134,14 +138,27 @@ describe('MetaHandler', function () { ], }, }) - }) - it('should extract all metadata', function () { - const projectMeta = this.MetaHandler.extractMetaFromProjectDocs(this.docs) - return expect(projectMeta).to.deep.equal({ - id_one: { labels: ['aaa'], packages: {}, packageNames: [] }, - id_two: { labels: [], packages: {}, packageNames: [] }, - id_three: { labels: ['bbb', 'ccc'], packages: {}, packageNames: [] }, + const result = await this.MetaHandler.promises.getAllMetaForProject( + this.projectId + ) + + expect(result).to.deep.equal({ + id_one: { + labels: ['aaa'], + packages: {}, + packageNames: [], + }, + id_two: { + labels: [], + packages: {}, + packageNames: [], + }, + id_three: { + labels: ['bbb', 'ccc'], + packages: {}, + packageNames: [], + }, id_four: { labels: [], packages: { @@ -185,161 +202,14 @@ describe('MetaHandler', function () { packageNames: ['foo', 'baz', 'hello'], }, }) - }) - }) - describe('getMetaForDoc', function () { - beforeEach(function () { - this.fakeLines = [ - '\\usepackage{abc}', - 'one', - '\\label{aaa}', - 'two', - // bbb should not be in the returned labels - 'commented label % \\label{bbb}', - '\\label{ccc}%bar', - '\\label{ddd} % bar', - ] - this.fakeMeta = { - labels: ['aaa', 'ccc', 'ddd'], - packages: { abc: [] }, - packageNames: ['abc'], - } - this.DocumentUpdaterHandler.flushDocToMongo = sinon - .stub() - .callsArgWith(2, null) - this.ProjectEntityHandler.getDoc = sinon - .stub() - .callsArgWith(2, null, this.fakeLines) - this.MetaHandler.extractMetaFromDoc = sinon.stub().returns(this.fakeMeta) - return (this.call = callback => { - return this.MetaHandler.getMetaForDoc( - this.projectId, - this.docId, - callback - ) - }) - }) + this.DocumentUpdaterHandler.promises.flushProjectToMongo.should.be.calledWith( + this.projectId + ) - it('should not produce an error', function (done) { - return this.call((err, docMeta) => { - expect(err).to.equal(null) - return done() - }) - }) - - it('should produce docMeta', function (done) { - return this.call((err, docMeta) => { - expect(docMeta).to.equal(this.fakeMeta) - return done() - }) - }) - - it('should call flushDocToMongo', function (done) { - return this.call((err, docMeta) => { - this.DocumentUpdaterHandler.flushDocToMongo.callCount.should.equal(1) - this.DocumentUpdaterHandler.flushDocToMongo - .calledWith(this.projectId, this.docId) - .should.equal(true) - return done() - }) - }) - - it('should call getDoc', function (done) { - return this.call((err, docMeta) => { - this.ProjectEntityHandler.getDoc.callCount.should.equal(1) - this.ProjectEntityHandler.getDoc - .calledWith(this.projectId, this.docId) - .should.equal(true) - return done() - }) - }) - - it('should call extractMetaFromDoc', function (done) { - return this.call((err, docMeta) => { - this.MetaHandler.extractMetaFromDoc.callCount.should.equal(1) - this.MetaHandler.extractMetaFromDoc - .calledWith(this.fakeLines) - .should.equal(true) - return done() - }) - }) - }) - - describe('getAllMetaForProject', function () { - beforeEach(function () { - this.fakeDocs = { - doc_one: { - lines: ['\\usepackage[some-options,more=foo]{foo}', '\\label{aaa}'], - }, - } - - this.fakeMeta = { - labels: ['aaa'], - packages: { - foo: [ - { - caption: '\\bar', - snippet: '\\bar', - meta: 'foo-cmd', - score: 12, - }, - { - caption: '\\bat[]{}', - snippet: '\\bar[$1]{$2}', - meta: 'foo-cmd', - score: 10, - }, - ], - }, - packageNames: ['foo'], - } - this.DocumentUpdaterHandler.flushProjectToMongo = sinon - .stub() - .callsArgWith(1, null) - this.ProjectEntityHandler.getAllDocs = sinon - .stub() - .callsArgWith(1, null, this.fakeDocs) - this.MetaHandler.extractMetaFromProjectDocs = sinon - .stub() - .returns(this.fakeMeta) - return (this.call = callback => { - return this.MetaHandler.getAllMetaForProject(this.projectId, callback) - }) - }) - - it('should not produce an error', function (done) { - return this.call((err, projectMeta) => { - expect(err).to.equal(null) - return done() - }) - }) - - it('should produce projectMeta', function (done) { - return this.call((err, projectMeta) => { - expect(projectMeta).to.equal(this.fakeMeta) - return done() - }) - }) - - it('should call getAllDocs', function (done) { - return this.call((err, projectMeta) => { - this.ProjectEntityHandler.getAllDocs.callCount.should.equal(1) - this.ProjectEntityHandler.getAllDocs - .calledWith(this.projectId) - .should.equal(true) - return done() - }) - }) - - it('should call extractMetaFromDoc', function (done) { - return this.call((err, docMeta) => { - this.MetaHandler.extractMetaFromProjectDocs.callCount.should.equal(1) - this.MetaHandler.extractMetaFromProjectDocs - .calledWith(this.fakeDocs) - .should.equal(true) - return done() - }) + this.ProjectEntityHandler.promises.getAllDocs.should.be.calledWith( + this.projectId + ) }) }) })