Przeglądaj źródła

Merge pull request #1863 from overleaf/msm-refactor-project-upload-manager-promises

Refactored ProjectUploadManager to support Promises

GitOrigin-RevId: 1d747f5ca07164299060bb27c6f14fa9e2968fdf
Miguel Serrano 7 lat temu
rodzic
commit
ab07761eef

+ 109 - 98
services/web/app/src/Features/Project/ProjectCreationHandler.js

@@ -13,7 +13,6 @@
  * DS207: Consider shorter variations of null checks
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
-let ProjectCreationHandler
 const logger = require('logger-sharelatex')
 const async = require('async')
 const metrics = require('metrics-sharelatex')
@@ -27,10 +26,11 @@ const HistoryManager = require('../History/HistoryManager')
 const { User } = require('../../models/User')
 const fs = require('fs')
 const Path = require('path')
+const { promisify } = require('util')
 const _ = require('underscore')
 const AnalyticsManger = require('../Analytics/AnalyticsManager')
 
-module.exports = ProjectCreationHandler = {
+const ProjectCreationHandler = {
   createBlankProject(owner_id, projectName, attributes, callback) {
     if (callback == null) {
       callback = function(error, project) {}
@@ -140,119 +140,122 @@ module.exports = ProjectCreationHandler = {
     if (callback == null) {
       callback = function(error, project) {}
     }
-    return this.createBlankProject(owner_id, projectName, function(
-      error,
-      project
-    ) {
-      if (error != null) {
-        return callback(error)
+    return ProjectCreationHandler.createBlankProject(
+      owner_id,
+      projectName,
+      function(error, project) {
+        if (error != null) {
+          return callback(error)
+        }
+        return ProjectCreationHandler._createRootDoc(
+          project,
+          owner_id,
+          docLines,
+          callback
+        )
       }
-      return ProjectCreationHandler._createRootDoc(
-        project,
-        owner_id,
-        docLines,
-        callback
-      )
-    })
+    )
   },
 
   createBasicProject(owner_id, projectName, callback) {
     if (callback == null) {
       callback = function(error, project) {}
     }
-    const self = this
-    return this.createBlankProject(owner_id, projectName, function(
-      error,
-      project
-    ) {
-      if (error != null) {
-        return callback(error)
-      }
-      return self._buildTemplate(
-        'mainbasic.tex',
-        owner_id,
-        projectName,
-        function(error, docLines) {
-          if (error != null) {
-            return callback(error)
-          }
-          return ProjectCreationHandler._createRootDoc(
-            project,
-            owner_id,
-            docLines,
-            callback
-          )
+    return ProjectCreationHandler.createBlankProject(
+      owner_id,
+      projectName,
+      function(error, project) {
+        if (error != null) {
+          return callback(error)
         }
-      )
-    })
+        return ProjectCreationHandler._buildTemplate(
+          'mainbasic.tex',
+          owner_id,
+          projectName,
+          function(error, docLines) {
+            if (error != null) {
+              return callback(error)
+            }
+            return ProjectCreationHandler._createRootDoc(
+              project,
+              owner_id,
+              docLines,
+              callback
+            )
+          }
+        )
+      }
+    )
   },
 
   createExampleProject(owner_id, projectName, callback) {
     if (callback == null) {
       callback = function(error, project) {}
     }
-    const self = this
-    return this.createBlankProject(owner_id, projectName, function(
-      error,
-      project
-    ) {
-      if (error != null) {
-        return callback(error)
-      }
-      return async.series(
-        [
-          callback =>
-            self._buildTemplate('main.tex', owner_id, projectName, function(
-              error,
-              docLines
-            ) {
-              if (error != null) {
-                return callback(error)
-              }
-              return ProjectCreationHandler._createRootDoc(
-                project,
+    return ProjectCreationHandler.createBlankProject(
+      owner_id,
+      projectName,
+      function(error, project) {
+        if (error != null) {
+          return callback(error)
+        }
+        return async.series(
+          [
+            callback =>
+              ProjectCreationHandler._buildTemplate(
+                'main.tex',
+                owner_id,
+                projectName,
+                function(error, docLines) {
+                  if (error != null) {
+                    return callback(error)
+                  }
+                  return ProjectCreationHandler._createRootDoc(
+                    project,
+                    owner_id,
+                    docLines,
+                    callback
+                  )
+                }
+              ),
+            callback =>
+              ProjectCreationHandler._buildTemplate(
+                'references.bib',
+                owner_id,
+                projectName,
+                function(error, docLines) {
+                  if (error != null) {
+                    return callback(error)
+                  }
+                  return ProjectEntityUpdateHandler.addDoc(
+                    project._id,
+                    project.rootFolder[0]._id,
+                    'references.bib',
+                    docLines,
+                    owner_id,
+                    (error, doc) => callback(error)
+                  )
+                }
+              ),
+            function(callback) {
+              const universePath = Path.resolve(
+                __dirname + '/../../../templates/project_files/universe.jpg'
+              )
+              return ProjectEntityUpdateHandler.addFile(
+                project._id,
+                project.rootFolder[0]._id,
+                'universe.jpg',
+                universePath,
+                null,
                 owner_id,
-                docLines,
                 callback
               )
-            }),
-          callback =>
-            self._buildTemplate(
-              'references.bib',
-              owner_id,
-              projectName,
-              function(error, docLines) {
-                if (error != null) {
-                  return callback(error)
-                }
-                return ProjectEntityUpdateHandler.addDoc(
-                  project._id,
-                  project.rootFolder[0]._id,
-                  'references.bib',
-                  docLines,
-                  owner_id,
-                  (error, doc) => callback(error)
-                )
-              }
-            ),
-          function(callback) {
-            const universePath = Path.resolve(
-              __dirname + '/../../../templates/project_files/universe.jpg'
-            )
-            return ProjectEntityUpdateHandler.addFile(
-              project._id,
-              project.rootFolder[0]._id,
-              'universe.jpg',
-              universePath,
-              null,
-              owner_id,
-              callback
-            )
-          }
-        ],
-        error => callback(error, project)
-      )
-    })
+            }
+          ],
+          error => callback(error, project)
+        )
+      }
+    )
   },
 
   _createRootDoc(project, owner_id, docLines, callback) {
@@ -340,3 +343,11 @@ function __guard__(value, transform) {
     ? transform(value)
     : undefined
 }
+
+const promises = {
+  createBlankProject: promisify(ProjectCreationHandler.createBlankProject)
+}
+
+ProjectCreationHandler.promises = promises
+
+module.exports = ProjectCreationHandler

+ 13 - 5
services/web/app/src/Features/Project/ProjectDetailsHandler.js

@@ -13,7 +13,6 @@
  * DS207: Consider shorter variations of null checks
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
-let ProjectDetailsHandler
 const ProjectGetter = require('./ProjectGetter')
 const UserGetter = require('../User/UserGetter')
 const { Project } = require('../../models/Project')
@@ -28,8 +27,9 @@ const ProjectEntityHandler = require('./ProjectEntityHandler')
 const ProjectHelper = require('./ProjectHelper')
 const CollaboratorsHandler = require('../Collaborators/CollaboratorsHandler')
 const settings = require('settings-sharelatex')
+const { promisify } = require('util')
 
-module.exports = ProjectDetailsHandler = {
+const ProjectDetailsHandler = {
   getDetails(project_id, callback) {
     return ProjectGetter.getProject(
       project_id,
@@ -221,7 +221,7 @@ module.exports = ProjectDetailsHandler = {
       return callback(
         new Errors.InvalidNameError('Project name cannot be blank')
       )
-    } else if (name.length > this.MAX_PROJECT_NAME_LENGTH) {
+    } else if (name.length > ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH) {
       return callback(new Errors.InvalidNameError('Project name is too long'))
     } else if (name.indexOf('/') > -1) {
       return callback(
@@ -303,8 +303,8 @@ module.exports = ProjectDetailsHandler = {
       // backslashes in project name will prevent syncing to dropbox
       name = name.replace(/\\/g, '')
     }
-    if (name.length > this.MAX_PROJECT_NAME_LENGTH) {
-      name = name.substr(0, this.MAX_PROJECT_NAME_LENGTH)
+    if (name.length > ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH) {
+      name = name.substr(0, ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH)
     }
     return name
   },
@@ -427,3 +427,11 @@ function __guard__(value, transform) {
     ? transform(value)
     : undefined
 }
+
+const promises = {
+  generateUniqueName: promisify(ProjectDetailsHandler.generateUniqueName)
+}
+
+ProjectDetailsHandler.promises = promises
+
+module.exports = ProjectDetailsHandler

+ 26 - 0
services/web/app/src/Features/Project/ProjectRootDocManager.js

@@ -20,6 +20,7 @@ const ProjectGetter = require('./ProjectGetter')
 const DocumentHelper = require('../Documents/DocumentHelper')
 const Path = require('path')
 const fs = require('fs')
+const { promisify } = require('util')
 const async = require('async')
 const globby = require('globby')
 const _ = require('underscore')
@@ -307,3 +308,28 @@ module.exports = ProjectRootDocManager = {
     return a.path.localeCompare(b.path)
   }
 }
+
+const promises = {
+  setRootDocAutomatically: promisify(
+    ProjectRootDocManager.setRootDocAutomatically
+  ),
+
+  findRootDocFileFromDirectory: directoryPath =>
+    new Promise((resolve, reject) => {
+      ProjectRootDocManager.findRootDocFileFromDirectory(
+        directoryPath,
+        (error, path, content) => {
+          if (error) {
+            reject(error)
+          } else {
+            resolve({ path, content })
+          }
+        }
+      )
+    }),
+  setRootDocFromName: promisify(ProjectRootDocManager.setRootDocFromName)
+}
+
+ProjectRootDocManager.promises = promises
+
+module.exports = ProjectRootDocManager

+ 10 - 2
services/web/app/src/Features/Uploads/ArchiveManager.js

@@ -12,10 +12,10 @@
  * DS207: Consider shorter variations of null checks
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
-let ArchiveManager
 const logger = require('logger-sharelatex')
 const metrics = require('metrics-sharelatex')
 const fs = require('fs')
+const { promisify } = require('util')
 const Path = require('path')
 const fse = require('fs-extra')
 const yauzl = require('yauzl')
@@ -25,7 +25,7 @@ const _ = require('underscore')
 
 const ONE_MEG = 1024 * 1024
 
-module.exports = ArchiveManager = {
+const ArchiveManager = {
   _isZipTooLarge(source, callback) {
     if (callback == null) {
       callback = function(err, isTooLarge) {}
@@ -242,3 +242,11 @@ module.exports = ArchiveManager = {
     })
   }
 }
+
+const promises = {
+  extractZipArchive: promisify(ArchiveManager.extractZipArchive)
+}
+
+ArchiveManager.promises = promises
+
+module.exports = ArchiveManager

+ 101 - 102
services/web/app/src/Features/Uploads/ProjectUploadManager.js

@@ -12,10 +12,9 @@
  * DS207: Consider shorter variations of null checks
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
-let ProjectUploadHandler
 const path = require('path')
 const rimraf = require('rimraf')
-const async = require('async')
+const { promisify, callbackify } = require('util')
 const ArchiveManager = require('./ArchiveManager')
 const FileSystemImportManager = require('./FileSystemImportManager')
 const ProjectCreationHandler = require('../Project/ProjectCreationHandler')
@@ -23,70 +22,18 @@ const ProjectRootDocManager = require('../Project/ProjectRootDocManager')
 const ProjectDetailsHandler = require('../Project/ProjectDetailsHandler')
 const DocumentHelper = require('../Documents/DocumentHelper')
 
-module.exports = ProjectUploadHandler = {
-  createProjectFromZipArchive(owner_id, defaultName, zipPath, callback) {
-    if (callback == null) {
-      callback = function(error, project) {}
-    }
-    const destination = this._getDestinationDirectory(zipPath)
-    let docPath = null
-    let project = null
-
-    return async.waterfall(
-      [
-        cb => ArchiveManager.extractZipArchive(zipPath, destination, cb),
-        cb =>
-          ProjectRootDocManager.findRootDocFileFromDirectory(
-            destination,
-            (error, _docPath, docContents) => cb(error, _docPath, docContents)
-          ),
-        function(_docPath, docContents, cb) {
-          docPath = _docPath
-          const proposedName = ProjectDetailsHandler.fixProjectName(
-            DocumentHelper.getTitleFromTexContent(docContents || '') ||
-              defaultName
-          )
-          return ProjectDetailsHandler.generateUniqueName(
-            owner_id,
-            proposedName,
-            (error, name) => cb(error, name)
-          )
-        },
-        (name, cb) =>
-          ProjectCreationHandler.createBlankProject(
-            owner_id,
-            name,
-            (error, _project) => cb(error, _project)
-          ),
-        (_project, cb) => {
-          project = _project
-          return this._insertZipContentsIntoFolder(
-            owner_id,
-            project._id,
-            project.rootFolder[0]._id,
-            destination,
-            cb
-          )
-        },
-        function(cb) {
-          if (docPath != null) {
-            return ProjectRootDocManager.setRootDocFromName(
-              project._id,
-              docPath,
-              error => cb(error)
-            )
-          } else {
-            return cb(null)
-          }
-        },
-        cb => cb(null, project)
-      ],
+const ProjectUploadManager = {
+  createProjectFromZipArchive(ownerId, defaultName, zipPath, callback) {
+    callbackify(ProjectUploadManager.promises.createProjectFromZipArchive)(
+      ownerId,
+      defaultName,
+      zipPath,
       callback
     )
   },
 
   createProjectFromZipArchiveWithName(
-    owner_id,
+    ownerId,
     proposedName,
     zipPath,
     attributes,
@@ -99,45 +46,10 @@ module.exports = ProjectUploadHandler = {
       callback = attributes
       attributes = {}
     }
-    return ProjectDetailsHandler.generateUniqueName(
-      owner_id,
-      ProjectDetailsHandler.fixProjectName(proposedName),
-      (error, name) => {
-        if (error != null) {
-          return callback(error)
-        }
-        return ProjectCreationHandler.createBlankProject(
-          owner_id,
-          name,
-          attributes,
-          (error, project) => {
-            if (error != null) {
-              return callback(error)
-            }
-            return this.insertZipArchiveIntoFolder(
-              owner_id,
-              project._id,
-              project.rootFolder[0]._id,
-              zipPath,
-              function(error) {
-                if (error != null) {
-                  return callback(error)
-                }
-                return ProjectRootDocManager.setRootDocAutomatically(
-                  project._id,
-                  function(error) {
-                    if (error != null) {
-                      return callback(error)
-                    }
-                    return callback(error, project)
-                  }
-                )
-              }
-            )
-          }
-        )
-      }
-    )
+
+    callbackify(
+      ProjectUploadManager.promises.createProjectFromZipArchiveWithName
+    )(ownerId, proposedName, zipPath, attributes, callback)
   },
 
   insertZipArchiveIntoFolder(
@@ -150,13 +62,13 @@ module.exports = ProjectUploadHandler = {
     if (callback == null) {
       callback = function(error) {}
     }
-    const destination = this._getDestinationDirectory(zipPath)
+    const destination = ProjectUploadManager._getDestinationDirectory(zipPath)
     return ArchiveManager.extractZipArchive(zipPath, destination, error => {
       if (error != null) {
         return callback(error)
       }
 
-      return this._insertZipContentsIntoFolder(
+      return ProjectUploadManager._insertZipContentsIntoFolder(
         owner_id,
         project_id,
         folder_id,
@@ -206,3 +118,90 @@ module.exports = ProjectUploadHandler = {
     )
   }
 }
+
+const promises = {
+  async createProjectFromZipArchive(ownerId, defaultName, zipPath) {
+    const destination = ProjectUploadManager._getDestinationDirectory(zipPath)
+    await ArchiveManager.promises.extractZipArchive(zipPath, destination)
+
+    const {
+      path,
+      content
+    } = await ProjectRootDocManager.promises.findRootDocFileFromDirectory(
+      destination
+    )
+
+    const projectName =
+      DocumentHelper.getTitleFromTexContent(content || '') || defaultName
+    const proposedName = ProjectDetailsHandler.fixProjectName(projectName)
+    const uniqueName = await ProjectDetailsHandler.promises.generateUniqueName(
+      ownerId,
+      proposedName
+    )
+
+    const project = await ProjectCreationHandler.promises.createBlankProject(
+      ownerId,
+      uniqueName
+    )
+    await ProjectUploadManager.promises._insertZipContentsIntoFolder(
+      ownerId,
+      project._id,
+      project.rootFolder[0]._id,
+      destination
+    )
+
+    if (path) {
+      await ProjectRootDocManager.promises.setRootDocFromName(project._id, path)
+    }
+
+    return project
+  },
+
+  async createProjectFromZipArchiveWithName(
+    ownerId,
+    proposedName,
+    zipPath,
+    attributes
+  ) {
+    attributes = attributes || {}
+
+    const fixedProjectName = ProjectDetailsHandler.fixProjectName(proposedName)
+    const projectName = await ProjectDetailsHandler.promises.generateUniqueName(
+      ownerId,
+      fixedProjectName
+    )
+
+    const project = await ProjectCreationHandler.promises.createBlankProject(
+      ownerId,
+      projectName,
+      attributes
+    )
+    await ProjectUploadManager.promises.insertZipArchiveIntoFolder(
+      ownerId,
+      project._id,
+      project.rootFolder[0]._id,
+      zipPath
+    )
+
+    await ProjectRootDocManager.promises.setRootDocAutomatically(project._id)
+
+    return project
+  },
+
+  _insertZipContentsIntoFolder: promisify(
+    ProjectUploadManager._insertZipContentsIntoFolder
+  ),
+
+  insertZipArchiveIntoFolder(ownerId, projectId, folderId, zipPath) {
+    return promisify(ProjectUploadManager.insertZipArchiveIntoFolder)(
+      ownerId,
+      projectId,
+      folderId,
+      zipPath
+    )
+  }
+}
+
+ProjectUploadManager.promises = promises
+
+module.exports = ProjectUploadManager

+ 1 - 1
services/web/test/unit/src/Project/ProjectRootDocManagerTests.js

@@ -536,7 +536,7 @@ describe('ProjectRootDocManager', function() {
         )
       })
 
-      it('should find the project with only the rootDoc_id fiel', function() {
+      it('should find the project with only the rootDoc_id field', function() {
         return this.ProjectGetter.getProject
           .calledWith(this.project_id, { rootDoc_id: 1 })
           .should.equal(true)

+ 59 - 49
services/web/test/unit/src/Uploads/ProjectUploadManagerTests.js

@@ -7,7 +7,6 @@
 // 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 sinon = require('sinon')
@@ -17,11 +16,13 @@ const modulePath =
   '../../../../app/src/Features/Uploads/ProjectUploadManager.js'
 const SandboxedModule = require('sandboxed-module')
 
+const promiseStub = val => new Promise(resolve => resolve(val))
+
 describe('ProjectUploadManager', function() {
   beforeEach(function() {
     this.project_id = 'project-id-123'
     this.folder_id = 'folder-id-123'
-    this.owner_id = 'onwer-id-123'
+    this.owner_id = 'owner-id-123'
     this.callback = sinon.stub()
     this.source = '/path/to/zip/file-name.zip'
     this.destination = '/path/to/zile/file-extracted'
@@ -36,16 +37,25 @@ describe('ProjectUploadManager', function() {
     this.ProjectUploadManager = SandboxedModule.require(modulePath, {
       requires: {
         './FileSystemImportManager': (this.FileSystemImportManager = {}),
-        './ArchiveManager': (this.ArchiveManager = {}),
-        '../Project/ProjectCreationHandler': (this.ProjectCreationHandler = {}),
-        '../Project/ProjectRootDocManager': (this.ProjectRootDocManager = {}),
-        '../Project/ProjectDetailsHandler': (this.ProjectDetailsHandler = {}),
+        './ArchiveManager': (this.ArchiveManager = { promises: {} }),
+        '../Project/ProjectCreationHandler': (this.ProjectCreationHandler = {
+          promises: {}
+        }),
+        '../Project/ProjectRootDocManager': (this.ProjectRootDocManager = {
+          promises: {}
+        }),
+        '../Project/ProjectDetailsHandler': (this.ProjectDetailsHandler = {
+          promises: {}
+        }),
         '../Documents/DocumentHelper': (this.DocumentHelper = {}),
         rimraf: (this.rimraf = sinon.stub().callsArg(1))
       }
     })
 
     this.ArchiveManager.extractZipArchive = sinon.stub().callsArg(2)
+    this.ArchiveManager.promises.extractZipArchive = sinon
+      .stub()
+      .returns(promiseStub())
     this.ArchiveManager.findTopLevelDirectory = sinon
       .stub()
       .callsArgWith(
@@ -53,17 +63,19 @@ describe('ProjectUploadManager', function() {
         null,
         (this.topLevelDestination = '/path/to/zip/file-extracted/nested')
       )
-    this.ProjectCreationHandler.createBlankProject = sinon
+    this.ProjectCreationHandler.promises.createBlankProject = sinon
       .stub()
-      .callsArgWith(2, null, this.project)
-    this.ProjectRootDocManager.setRootDocAutomatically = sinon
+      .returns(promiseStub(this.project))
+    this.ProjectRootDocManager.promises.setRootDocAutomatically = sinon
       .stub()
-      .callsArg(1)
+      .returns(promiseStub())
     this.FileSystemImportManager.addFolderContents = sinon.stub().callsArg(5)
-    this.ProjectRootDocManager.findRootDocFileFromDirectory = sinon
+    this.ProjectRootDocManager.promises.findRootDocFileFromDirectory = sinon
+      .stub()
+      .returns(promiseStub({ path: 'main.tex', content: this.othername }))
+    this.ProjectRootDocManager.promises.setRootDocFromName = sinon
       .stub()
-      .callsArgWith(1, null, 'main.tex', this.othername)
-    this.ProjectRootDocManager.setRootDocFromName = sinon.stub().callsArg(2)
+      .returns(promiseStub())
     this.DocumentHelper.getTitleFromTexContent = sinon
       .stub()
       .returns(this.othername)
@@ -78,9 +90,9 @@ describe('ProjectUploadManager', function() {
         this.ProjectUploadManager._getDestinationDirectory = sinon
           .stub()
           .returns(this.destination)
-        this.ProjectDetailsHandler.generateUniqueName = sinon
+        this.ProjectDetailsHandler.promises.generateUniqueName = sinon
           .stub()
-          .callsArgWith(2, null, this.othername)
+          .returns(promiseStub(this.othername))
         return this.ProjectUploadManager.createProjectFromZipArchive(
           this.owner_id,
           this.name,
@@ -93,25 +105,25 @@ describe('ProjectUploadManager', function() {
       })
 
       it('should set up the directory to extract the archive to', function() {
-        return this.ProjectUploadManager._getDestinationDirectory
+        this.ProjectUploadManager._getDestinationDirectory
           .calledWith(this.source)
           .should.equal(true)
       })
 
       it('should extract the archive', function() {
-        return this.ArchiveManager.extractZipArchive
+        this.ArchiveManager.promises.extractZipArchive
           .calledWith(this.source, this.destination)
           .should.equal(true)
       })
 
       it('should find the top level directory', function() {
-        return this.ArchiveManager.findTopLevelDirectory
+        this.ArchiveManager.findTopLevelDirectory
           .calledWith(this.destination)
           .should.equal(true)
       })
 
       it('should insert the extracted archive into the folder', function() {
-        return this.FileSystemImportManager.addFolderContents
+        this.FileSystemImportManager.addFolderContents
           .calledWith(
             this.owner_id,
             this.project_id,
@@ -123,31 +135,29 @@ describe('ProjectUploadManager', function() {
       })
 
       it('should create a project owned by the owner_id', function() {
-        return this.ProjectCreationHandler.createBlankProject
+        this.ProjectCreationHandler.promises.createBlankProject
           .calledWith(this.owner_id)
           .should.equal(true)
       })
 
       it('should create a project with the correct name', function() {
-        return this.ProjectCreationHandler.createBlankProject
+        this.ProjectCreationHandler.promises.createBlankProject
           .calledWith(sinon.match.any, this.othername)
           .should.equal(true)
       })
 
       it('should read the title from the tex contents', function() {
-        return this.DocumentHelper.getTitleFromTexContent.called.should.equal(
-          true
-        )
+        this.DocumentHelper.getTitleFromTexContent.called.should.equal(true)
       })
 
       it('should set the root document', function() {
-        return this.ProjectRootDocManager.setRootDocFromName
+        this.ProjectRootDocManager.promises.setRootDocFromName
           .calledWith(this.project_id, 'main.tex')
           .should.equal(true)
       })
 
       it('should call the callback', function() {
-        return this.callback
+        this.callback
           .calledWith(sinon.match.falsy, this.project)
           .should.equal(true)
       })
@@ -161,15 +171,16 @@ describe('ProjectUploadManager', function() {
 
     describe("when the root document can't be determined", function() {
       beforeEach(function(done) {
-        this.ProjectRootDocManager.findRootDocFileFromDirectory = sinon
+        this.ProjectRootDocManager.promises.findRootDocFileFromDirectory = sinon
           .stub()
-          .callsArg(1)
+          .returns(promiseStub())
         this.ProjectUploadManager._getDestinationDirectory = sinon
           .stub()
           .returns(this.destination)
-        this.ProjectDetailsHandler.generateUniqueName = sinon
+        this.ProjectDetailsHandler.promises.generateUniqueName = sinon
           .stub()
-          .callsArgWith(2, null, this.name)
+          .returns(promiseStub(this.name))
+
         return this.ProjectUploadManager.createProjectFromZipArchive(
           this.owner_id,
           this.name,
@@ -182,7 +193,7 @@ describe('ProjectUploadManager', function() {
       })
 
       it('should not try to set the root doc', function() {
-        return this.ProjectRootDocManager.setRootDocFromName.called.should.equal(
+        this.ProjectRootDocManager.promises.setRootDocFromName.called.should.equal(
           false
         )
       })
@@ -191,16 +202,15 @@ describe('ProjectUploadManager', function() {
 
   describe('createProjectFromZipArchiveWithName', function() {
     beforeEach(function(done) {
-      this.ProjectDetailsHandler.generateUniqueName = sinon
+      this.ProjectDetailsHandler.promises.generateUniqueName = sinon
         .stub()
-        .callsArgWith(2, null, this.name)
-      // createBlankProject allows taking optional attributes and will callback the last arg
-      this.ProjectCreationHandler.createBlankProject = sinon
+        .returns(promiseStub(this.name))
+      this.ProjectCreationHandler.promises.createBlankProject = sinon
         .stub()
-        .callsArgWith(3, null, this.project)
-      this.ProjectUploadManager.insertZipArchiveIntoFolder = sinon
+        .returns(promiseStub(this.project))
+      this.ProjectUploadManager.promises.insertZipArchiveIntoFolder = sinon
         .stub()
-        .callsArg(4)
+        .returns(promiseStub())
       return this.ProjectUploadManager.createProjectFromZipArchiveWithName(
         this.owner_id,
         this.name,
@@ -213,19 +223,19 @@ describe('ProjectUploadManager', function() {
     })
 
     it('should create a project owned by the owner_id', function() {
-      return this.ProjectCreationHandler.createBlankProject
+      this.ProjectCreationHandler.promises.createBlankProject
         .calledWith(this.owner_id)
         .should.equal(true)
     })
 
     it('should create a project with the correct name', function() {
-      return this.ProjectCreationHandler.createBlankProject
+      this.ProjectCreationHandler.promises.createBlankProject
         .calledWith(sinon.match.any, this.name)
         .should.equal(true)
     })
 
     it('should insert the zip file contents into the root folder', function() {
-      return this.ProjectUploadManager.insertZipArchiveIntoFolder
+      this.ProjectUploadManager.promises.insertZipArchiveIntoFolder
         .calledWith(
           this.owner_id,
           this.project_id,
@@ -236,7 +246,7 @@ describe('ProjectUploadManager', function() {
     })
 
     it('should automatically set the root doc', function() {
-      return this.ProjectRootDocManager.setRootDocAutomatically
+      this.ProjectRootDocManager.promises.setRootDocAutomatically
         .calledWith(this.project_id)
         .should.equal(true)
     })
@@ -266,25 +276,25 @@ describe('ProjectUploadManager', function() {
     })
 
     it('should set up the directory to extract the archive to', function() {
-      return this.ProjectUploadManager._getDestinationDirectory
+      this.ProjectUploadManager._getDestinationDirectory
         .calledWith(this.source)
         .should.equal(true)
     })
 
     it('should extract the archive', function() {
-      return this.ArchiveManager.extractZipArchive
+      this.ArchiveManager.extractZipArchive
         .calledWith(this.source, this.destination)
         .should.equal(true)
     })
 
     it('should find the top level directory', function() {
-      return this.ArchiveManager.findTopLevelDirectory
+      this.ArchiveManager.findTopLevelDirectory
         .calledWith(this.destination)
         .should.equal(true)
     })
 
     it('should insert the extracted archive into the folder', function() {
-      return this.FileSystemImportManager.addFolderContents
+      this.FileSystemImportManager.addFolderContents
         .calledWith(
           this.owner_id,
           this.project_id,
@@ -296,11 +306,11 @@ describe('ProjectUploadManager', function() {
     })
 
     it('should return the callback', function() {
-      return this.callback.called.should.equal(true)
+      this.callback.called.should.equal(true)
     })
 
     it('should remove the desintation directory afterwards', function() {
-      return this.rimraf.calledWith(this.destination).should.equal(true)
+      this.rimraf.calledWith(this.destination).should.equal(true)
     })
   })
 
@@ -311,6 +321,6 @@ describe('ProjectUploadManager', function() {
       this.ProjectUploadManager._getDestinationDirectory(
         '/path/to/zip/file.zip'
       ).should.equal(`/path/to/zip/file-${date}`)
-      return Date.now.restore()
+      Date.now.restore()
     }))
 })