mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
[web] Promisify ProjectController (#18477)
* Create `promiseAuto` util to replace `async.auto` * Promisify `BrandVariationsHandler.getBrandVariationById` * Promisify `updateProjectSettings` * Promisify `updateProjectAdminSettings` * Promisify `newProject` * Promisify `deleteProject` * Promisify `loadEditor` * Fix brandVariation loading in promise auto * Promisify `_refreshFeatures` * Promisify `_injectProjectUsers` * Fix `no-inner-declarations` * Promisify `cloneProject` * Promisify `userProjectsJson` * Promisify `projectEntitiesJson` * Promisify `restoreProject` * Promisify `renameProject` * Additional warning fix * Update unit tests * Fixup `updateProjectSettings`: call jobs inside the Promise.all * Use `expressify(...)` instead of manually call `next(err)` https://github.com/overleaf/internal/pull/18477#discussion_r1613611987 https://github.com/overleaf/internal/pull/18477#discussion_r1613621146 https://github.com/overleaf/internal/pull/18477#discussion_r1613634000 ... * Replace Promise.all by sequencial awaits https://github.com/overleaf/internal/pull/18477#discussion_r1613852746 https://github.com/overleaf/internal/pull/18477#discussion_r1613611987 * Remove manual throws of 500. Let the generic error handler catch them. https://github.com/overleaf/internal/pull/18477#discussion_r1613623446 https://github.com/overleaf/internal/pull/18477#discussion_r1613628955 * Promisify `untrashProject` https://github.com/overleaf/internal/pull/18477#discussion_r1613627783 * Promisify `expireDeletedProjectsAfterDuration` * Promisify `archiveProject` * Promisify `unarchiveProject` * Promisify `trashProject` * Promisify `expireDeletedProject` * Use async `setTimeout` from `timers/promise` https://github.com/overleaf/internal/pull/18477#discussion_r1613843085 * Remove unused `_injectProjectUsers` https://github.com/overleaf/internal/pull/18477#discussion_r1613855766 * Add missing exec in queries (?) Not sure if that makes a real difference but it's more consistent with the rest of the code * Catch floating promises https://github.com/overleaf/internal/pull/18477#discussion_r1613868876 * Replace custom `promiseAuto` by `p-props` from NPM https://github.com/overleaf/internal/pull/18477#discussion_r1613393294 * Downgrade `p-props` to v4. Later versions require ESM * Simplify code around `splitTestAssignments` GitOrigin-RevId: 84d37f7aa9227b5b9acf9eeb5db1b78afc01b6ee
This commit is contained in:
parent
01e1286a8b
commit
36f0a3e01a
7 changed files with 712 additions and 817 deletions
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -44498,6 +44498,7 @@
|
||||||
"openai": "^4.36.0",
|
"openai": "^4.36.0",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"p-limit": "^2.3.0",
|
"p-limit": "^2.3.0",
|
||||||
|
"p-props": "4.0.0",
|
||||||
"parse-data-url": "^2.0.0",
|
"parse-data-url": "^2.0.0",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
|
@ -53119,6 +53120,7 @@
|
||||||
"openai": "^4.36.0",
|
"openai": "^4.36.0",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"p-limit": "^2.3.0",
|
"p-limit": "^2.3.0",
|
||||||
|
"p-props": "4.0.0",
|
||||||
"parse-data-url": "^2.0.0",
|
"parse-data-url": "^2.0.0",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
|
|
|
@ -70,6 +70,7 @@ async function getPublicAccessLevel(projectId) {
|
||||||
*
|
*
|
||||||
* @param userId - The id of the user that wants to access the project.
|
* @param userId - The id of the user that wants to access the project.
|
||||||
* @param projectId - The id of the project to be accessed.
|
* @param projectId - The id of the project to be accessed.
|
||||||
|
* @param {string} token
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
* @param {boolean} opts.ignoreSiteAdmin - Do not consider whether the user is
|
* @param {boolean} opts.ignoreSiteAdmin - Do not consider whether the user is
|
||||||
* a site admin.
|
* a site admin.
|
||||||
|
|
|
@ -4,9 +4,13 @@ const settings = require('@overleaf/settings')
|
||||||
const logger = require('@overleaf/logger')
|
const logger = require('@overleaf/logger')
|
||||||
const V1Api = require('../V1/V1Api')
|
const V1Api = require('../V1/V1Api')
|
||||||
const sanitizeHtml = require('sanitize-html')
|
const sanitizeHtml = require('sanitize-html')
|
||||||
|
const { promisify } = require('@overleaf/promise-utils')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getBrandVariationById,
|
getBrandVariationById,
|
||||||
|
promises: {
|
||||||
|
getBrandVariationById: promisify(getBrandVariationById),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBrandVariationById(brandVariationId, callback) {
|
function getBrandVariationById(brandVariationId, callback) {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -142,6 +142,7 @@
|
||||||
"openai": "^4.36.0",
|
"openai": "^4.36.0",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"p-limit": "^2.3.0",
|
"p-limit": "^2.3.0",
|
||||||
|
"p-props": "4.0.0",
|
||||||
"parse-data-url": "^2.0.0",
|
"parse-data-url": "^2.0.0",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
|
|
1
services/web/test/unit/bootstrap.js
vendored
1
services/web/test/unit/bootstrap.js
vendored
|
@ -50,6 +50,7 @@ SandboxedModule.configure({
|
||||||
ignoreMissing: true,
|
ignoreMissing: true,
|
||||||
requires: getSandboxedModuleRequires(),
|
requires: getSandboxedModuleRequires(),
|
||||||
globals: {
|
globals: {
|
||||||
|
AbortController,
|
||||||
AbortSignal,
|
AbortSignal,
|
||||||
Buffer,
|
Buffer,
|
||||||
Promise,
|
Promise,
|
||||||
|
|
|
@ -38,48 +38,73 @@ describe('ProjectController', function () {
|
||||||
}
|
}
|
||||||
this.token = 'some-token'
|
this.token = 'some-token'
|
||||||
this.ProjectDeleter = {
|
this.ProjectDeleter = {
|
||||||
deleteProject: sinon.stub().callsArg(2),
|
promises: {
|
||||||
restoreProject: sinon.stub().callsArg(1),
|
deleteProject: sinon.stub().resolves(),
|
||||||
|
restoreProject: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
findArchivedProjects: sinon.stub(),
|
findArchivedProjects: sinon.stub(),
|
||||||
}
|
}
|
||||||
this.ProjectDuplicator = {
|
this.ProjectDuplicator = {
|
||||||
duplicate: sinon.stub().callsArgWith(4, null, { _id: this.project_id }),
|
promises: {
|
||||||
|
duplicate: sinon.stub().resolves({ _id: this.project_id }),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.ProjectCreationHandler = {
|
this.ProjectCreationHandler = {
|
||||||
createExampleProject: sinon
|
promises: {
|
||||||
.stub()
|
createExampleProject: sinon.stub().resolves({ _id: this.project_id }),
|
||||||
.callsArgWith(2, null, { _id: this.project_id }),
|
createBasicProject: sinon.stub().resolves({ _id: this.project_id }),
|
||||||
createBasicProject: sinon
|
},
|
||||||
.stub()
|
}
|
||||||
.callsArgWith(2, null, { _id: this.project_id }),
|
this.SubscriptionLocator = {
|
||||||
|
promises: {
|
||||||
|
getUsersSubscription: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.SubscriptionLocator = { getUsersSubscription: sinon.stub() }
|
|
||||||
this.LimitationsManager = {
|
this.LimitationsManager = {
|
||||||
hasPaidSubscription: sinon.stub(),
|
hasPaidSubscription: sinon.stub(),
|
||||||
userIsMemberOfGroupSubscription: sinon
|
promises: {
|
||||||
.stub()
|
userIsMemberOfGroupSubscription: sinon.stub().resolves(false),
|
||||||
.callsArgWith(1, null, false),
|
},
|
||||||
}
|
}
|
||||||
this.TagsHandler = {
|
this.TagsHandler = {
|
||||||
getTagsForProject: sinon.stub().callsArgWith(2, null, [
|
promises: {
|
||||||
|
getTagsForProject: sinon.stub().resolves([
|
||||||
{
|
{
|
||||||
name: 'test',
|
name: 'test',
|
||||||
project_ids: [this.project_id],
|
project_ids: [this.project_id],
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
},
|
||||||
addProjectToTags: sinon.stub(),
|
addProjectToTags: sinon.stub(),
|
||||||
}
|
}
|
||||||
this.UserModel = { findById: sinon.stub(), updateOne: sinon.stub() }
|
this.UserModel = {
|
||||||
|
findById: sinon.stub().returns({ exec: sinon.stub().resolves() }),
|
||||||
|
updateOne: sinon.stub().returns({ exec: sinon.stub().resolves() }),
|
||||||
|
}
|
||||||
this.AuthorizationManager = {
|
this.AuthorizationManager = {
|
||||||
|
promises: {
|
||||||
getPrivilegeLevelForProject: sinon.stub(),
|
getPrivilegeLevelForProject: sinon.stub(),
|
||||||
|
},
|
||||||
isRestrictedUser: sinon.stub().returns(false),
|
isRestrictedUser: sinon.stub().returns(false),
|
||||||
}
|
}
|
||||||
this.EditorController = { renameProject: sinon.stub() }
|
this.EditorController = {
|
||||||
this.InactiveProjectManager = { reactivateProjectIfRequired: sinon.stub() }
|
promises: {
|
||||||
this.ProjectUpdateHandler = { markAsOpened: sinon.stub() }
|
renameProject: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
this.InactiveProjectManager = {
|
||||||
|
promises: { reactivateProjectIfRequired: sinon.stub() },
|
||||||
|
}
|
||||||
|
this.ProjectUpdateHandler = {
|
||||||
|
promises: {
|
||||||
|
markAsOpened: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
|
}
|
||||||
this.ProjectGetter = {
|
this.ProjectGetter = {
|
||||||
findAllUsersProjects: sinon.stub(),
|
promises: {
|
||||||
getProject: sinon.stub(),
|
findAllUsersProjects: sinon.stub().resolves(),
|
||||||
|
getProject: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.ProjectHelper = {
|
this.ProjectHelper = {
|
||||||
isArchived: sinon.stub(),
|
isArchived: sinon.stub(),
|
||||||
|
@ -88,7 +113,6 @@ describe('ProjectController', function () {
|
||||||
getAllowedImagesForUser: sinon.stub().returns([]),
|
getAllowedImagesForUser: sinon.stub().returns([]),
|
||||||
}
|
}
|
||||||
this.SessionManager = {
|
this.SessionManager = {
|
||||||
getLoggedInUser: sinon.stub().callsArgWith(1, null, this.user),
|
|
||||||
getLoggedInUserId: sinon.stub().returns(this.user._id),
|
getLoggedInUserId: sinon.stub().returns(this.user._id),
|
||||||
getSessionUser: sinon.stub().returns(this.user),
|
getSessionUser: sinon.stub().returns(this.user),
|
||||||
isUserLoggedIn: sinon.stub().returns(true),
|
isUserLoggedIn: sinon.stub().returns(true),
|
||||||
|
@ -100,30 +124,36 @@ describe('ProjectController', function () {
|
||||||
getRequestToken: sinon.stub().returns(this.token),
|
getRequestToken: sinon.stub().returns(this.token),
|
||||||
}
|
}
|
||||||
this.CollaboratorsGetter = {
|
this.CollaboratorsGetter = {
|
||||||
userIsTokenMember: sinon.stub().callsArgWith(2, null, false),
|
promises: {
|
||||||
isUserInvitedMemberOfProject: sinon.stub().callsArgWith(2, null, true),
|
userIsTokenMember: sinon.stub().resolves(false),
|
||||||
|
isUserInvitedMemberOfProject: sinon.stub().resolves(true),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.ProjectEntityHandler = {}
|
this.ProjectEntityHandler = {}
|
||||||
this.UserGetter = {
|
this.UserGetter = {
|
||||||
getUserFullEmails: sinon.stub().yields(null, []),
|
getUserFullEmails: sinon.stub().yields(null, []),
|
||||||
getUser: sinon
|
getUser: sinon.stub().resolves({ lastLoginIp: '192.170.18.2' }),
|
||||||
.stub()
|
|
||||||
.callsArgWith(2, null, { lastLoginIp: '192.170.18.2' }),
|
|
||||||
}
|
}
|
||||||
this.Features = {
|
this.Features = {
|
||||||
hasFeature: sinon.stub(),
|
hasFeature: sinon.stub(),
|
||||||
}
|
}
|
||||||
this.FeaturesUpdater = {
|
this.FeaturesUpdater = {
|
||||||
featuresEpochIsCurrent: sinon.stub().returns(true),
|
featuresEpochIsCurrent: sinon.stub().returns(true),
|
||||||
refreshFeatures: sinon.stub().yields(null, this.user),
|
promises: {
|
||||||
|
refreshFeatures: sinon.stub().resolves(this.user),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.BrandVariationsHandler = {
|
this.BrandVariationsHandler = {
|
||||||
|
promises: {
|
||||||
getBrandVariationById: sinon
|
getBrandVariationById: sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(1, null, this.brandVariationDetails),
|
.resolves(this.brandVariationDetails),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.TpdsProjectFlusher = {
|
this.TpdsProjectFlusher = {
|
||||||
flushProjectToTpdsIfNeeded: sinon.stub().yields(),
|
promises: {
|
||||||
|
flushProjectToTpdsIfNeeded: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.Metrics = {
|
this.Metrics = {
|
||||||
Timer: class {
|
Timer: class {
|
||||||
|
@ -138,10 +168,14 @@ describe('ProjectController', function () {
|
||||||
getAssignment: sinon.stub().yields(null, { variant: 'default' }),
|
getAssignment: sinon.stub().yields(null, { variant: 'default' }),
|
||||||
}
|
}
|
||||||
this.SplitTestSessionHandler = {
|
this.SplitTestSessionHandler = {
|
||||||
sessionMaintenance: sinon.stub().yields(),
|
promises: {
|
||||||
|
sessionMaintenance: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.InstitutionsFeatures = {
|
this.InstitutionsFeatures = {
|
||||||
hasLicence: sinon.stub().callsArgWith(1, null, false),
|
promises: {
|
||||||
|
hasLicence: sinon.stub().resolves(false),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.SubscriptionViewModelBuilder = {
|
this.SubscriptionViewModelBuilder = {
|
||||||
getBestSubscription: sinon.stub().yields(null, { type: 'free' }),
|
getBestSubscription: sinon.stub().yields(null, { type: 'free' }),
|
||||||
|
@ -150,7 +184,9 @@ describe('ProjectController', function () {
|
||||||
getSurvey: sinon.stub().yields(null, {}),
|
getSurvey: sinon.stub().yields(null, {}),
|
||||||
}
|
}
|
||||||
this.ProjectAuditLogHandler = {
|
this.ProjectAuditLogHandler = {
|
||||||
addEntry: sinon.stub().yields(),
|
promises: {
|
||||||
|
addEntry: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.TutorialHandler = {
|
this.TutorialHandler = {
|
||||||
getInactiveTutorials: sinon.stub().returns([]),
|
getInactiveTutorials: sinon.stub().returns([]),
|
||||||
|
@ -195,7 +231,9 @@ describe('ProjectController', function () {
|
||||||
'../Subscription/SubscriptionViewModelBuilder':
|
'../Subscription/SubscriptionViewModelBuilder':
|
||||||
this.SubscriptionViewModelBuilder,
|
this.SubscriptionViewModelBuilder,
|
||||||
'../Spelling/SpellingHandler': {
|
'../Spelling/SpellingHandler': {
|
||||||
getUserDictionary: sinon.stub().yields(null, []),
|
promises: {
|
||||||
|
getUserDictionary: sinon.stub().resolves([]),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'../Institutions/InstitutionsFeatures': this.InstitutionsFeatures,
|
'../Institutions/InstitutionsFeatures': this.InstitutionsFeatures,
|
||||||
'../Survey/SurveyHandler': this.SurveyHandler,
|
'../Survey/SurveyHandler': this.SurveyHandler,
|
||||||
|
@ -235,10 +273,10 @@ describe('ProjectController', function () {
|
||||||
|
|
||||||
describe('updateProjectSettings', function () {
|
describe('updateProjectSettings', function () {
|
||||||
it('should update the name', function (done) {
|
it('should update the name', function (done) {
|
||||||
this.EditorController.renameProject = sinon.stub().callsArg(2)
|
this.EditorController.promises.renameProject = sinon.stub().resolves()
|
||||||
this.req.body = { name: (this.name = 'New name') }
|
this.req.body = { name: (this.name = 'New name') }
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
this.EditorController.renameProject
|
this.EditorController.promises.renameProject
|
||||||
.calledWith(this.project_id, this.name)
|
.calledWith(this.project_id, this.name)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
code.should.equal(204)
|
code.should.equal(204)
|
||||||
|
@ -248,10 +286,10 @@ describe('ProjectController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update the compiler', function (done) {
|
it('should update the compiler', function (done) {
|
||||||
this.EditorController.setCompiler = sinon.stub().callsArg(2)
|
this.EditorController.promises.setCompiler = sinon.stub().resolves()
|
||||||
this.req.body = { compiler: (this.compiler = 'pdflatex') }
|
this.req.body = { compiler: (this.compiler = 'pdflatex') }
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
this.EditorController.setCompiler
|
this.EditorController.promises.setCompiler
|
||||||
.calledWith(this.project_id, this.compiler)
|
.calledWith(this.project_id, this.compiler)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
code.should.equal(204)
|
code.should.equal(204)
|
||||||
|
@ -261,10 +299,10 @@ describe('ProjectController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update the imageName', function (done) {
|
it('should update the imageName', function (done) {
|
||||||
this.EditorController.setImageName = sinon.stub().callsArg(2)
|
this.EditorController.promises.setImageName = sinon.stub().resolves()
|
||||||
this.req.body = { imageName: (this.imageName = 'texlive-1234.5') }
|
this.req.body = { imageName: (this.imageName = 'texlive-1234.5') }
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
this.EditorController.setImageName
|
this.EditorController.promises.setImageName
|
||||||
.calledWith(this.project_id, this.imageName)
|
.calledWith(this.project_id, this.imageName)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
code.should.equal(204)
|
code.should.equal(204)
|
||||||
|
@ -274,10 +312,12 @@ describe('ProjectController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update the spell check language', function (done) {
|
it('should update the spell check language', function (done) {
|
||||||
this.EditorController.setSpellCheckLanguage = sinon.stub().callsArg(2)
|
this.EditorController.promises.setSpellCheckLanguage = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves()
|
||||||
this.req.body = { spellCheckLanguage: (this.languageCode = 'fr') }
|
this.req.body = { spellCheckLanguage: (this.languageCode = 'fr') }
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
this.EditorController.setSpellCheckLanguage
|
this.EditorController.promises.setSpellCheckLanguage
|
||||||
.calledWith(this.project_id, this.languageCode)
|
.calledWith(this.project_id, this.languageCode)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
code.should.equal(204)
|
code.should.equal(204)
|
||||||
|
@ -287,10 +327,10 @@ describe('ProjectController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update the root doc', function (done) {
|
it('should update the root doc', function (done) {
|
||||||
this.EditorController.setRootDoc = sinon.stub().callsArg(2)
|
this.EditorController.promises.setRootDoc = sinon.stub().resolves()
|
||||||
this.req.body = { rootDocId: (this.rootDocId = 'root-doc-id') }
|
this.req.body = { rootDocId: (this.rootDocId = 'root-doc-id') }
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
this.EditorController.setRootDoc
|
this.EditorController.promises.setRootDoc
|
||||||
.calledWith(this.project_id, this.rootDocId)
|
.calledWith(this.project_id, this.rootDocId)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
code.should.equal(204)
|
code.should.equal(204)
|
||||||
|
@ -302,12 +342,14 @@ describe('ProjectController', function () {
|
||||||
|
|
||||||
describe('updateProjectAdminSettings', function () {
|
describe('updateProjectAdminSettings', function () {
|
||||||
it('should update the public access level', function (done) {
|
it('should update the public access level', function (done) {
|
||||||
this.EditorController.setPublicAccessLevel = sinon.stub().callsArg(2)
|
this.EditorController.promises.setPublicAccessLevel = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves()
|
||||||
this.req.body = {
|
this.req.body = {
|
||||||
publicAccessLevel: 'readOnly',
|
publicAccessLevel: 'readOnly',
|
||||||
}
|
}
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
this.EditorController.setPublicAccessLevel
|
this.EditorController.promises.setPublicAccessLevel
|
||||||
.calledWith(this.project_id, 'readOnly')
|
.calledWith(this.project_id, 'readOnly')
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
code.should.equal(204)
|
code.should.equal(204)
|
||||||
|
@ -317,12 +359,14 @@ describe('ProjectController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should record the change in the project audit log', function (done) {
|
it('should record the change in the project audit log', function (done) {
|
||||||
this.EditorController.setPublicAccessLevel = sinon.stub().callsArg(2)
|
this.EditorController.promises.setPublicAccessLevel = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves()
|
||||||
this.req.body = {
|
this.req.body = {
|
||||||
publicAccessLevel: 'readOnly',
|
publicAccessLevel: 'readOnly',
|
||||||
}
|
}
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
this.ProjectAuditLogHandler.addEntry
|
this.ProjectAuditLogHandler.promises.addEntry
|
||||||
.calledWith(
|
.calledWith(
|
||||||
this.project_id,
|
this.project_id,
|
||||||
'toggle-access-level',
|
'toggle-access-level',
|
||||||
|
@ -343,7 +387,7 @@ describe('ProjectController', function () {
|
||||||
describe('deleteProject', function () {
|
describe('deleteProject', function () {
|
||||||
it('should call the project deleter', function (done) {
|
it('should call the project deleter', function (done) {
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
this.ProjectDeleter.deleteProject
|
this.ProjectDeleter.promises.deleteProject
|
||||||
.calledWith(this.project_id, {
|
.calledWith(this.project_id, {
|
||||||
deleterUser: this.user,
|
deleterUser: this.user,
|
||||||
ipAddress: this.req.ip,
|
ipAddress: this.req.ip,
|
||||||
|
@ -359,7 +403,7 @@ describe('ProjectController', function () {
|
||||||
describe('restoreProject', function () {
|
describe('restoreProject', function () {
|
||||||
it('should tell the project deleter', function (done) {
|
it('should tell the project deleter', function (done) {
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
this.ProjectDeleter.restoreProject
|
this.ProjectDeleter.promises.restoreProject
|
||||||
.calledWith(this.project_id)
|
.calledWith(this.project_id)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
code.should.equal(200)
|
code.should.equal(200)
|
||||||
|
@ -372,7 +416,7 @@ describe('ProjectController', function () {
|
||||||
describe('cloneProject', function () {
|
describe('cloneProject', function () {
|
||||||
it('should call the project duplicator', function (done) {
|
it('should call the project duplicator', function (done) {
|
||||||
this.res.json = json => {
|
this.res.json = json => {
|
||||||
this.ProjectDuplicator.duplicate
|
this.ProjectDuplicator.promises.duplicate
|
||||||
.calledWith(this.user, this.project_id, this.projectName)
|
.calledWith(this.user, this.project_id, this.projectName)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
json.project_id.should.equal(this.project_id)
|
json.project_id.should.equal(this.project_id)
|
||||||
|
@ -386,10 +430,10 @@ describe('ProjectController', function () {
|
||||||
it('should call the projectCreationHandler with createExampleProject', function (done) {
|
it('should call the projectCreationHandler with createExampleProject', function (done) {
|
||||||
this.req.body.template = 'example'
|
this.req.body.template = 'example'
|
||||||
this.res.json = json => {
|
this.res.json = json => {
|
||||||
this.ProjectCreationHandler.createExampleProject
|
this.ProjectCreationHandler.promises.createExampleProject
|
||||||
.calledWith(this.user._id, this.projectName)
|
.calledWith(this.user._id, this.projectName)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
this.ProjectCreationHandler.createBasicProject.called.should.equal(
|
this.ProjectCreationHandler.promises.createBasicProject.called.should.equal(
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
done()
|
done()
|
||||||
|
@ -400,10 +444,10 @@ describe('ProjectController', function () {
|
||||||
it('should call the projectCreationHandler with createBasicProject', function (done) {
|
it('should call the projectCreationHandler with createBasicProject', function (done) {
|
||||||
this.req.body.template = 'basic'
|
this.req.body.template = 'basic'
|
||||||
this.res.json = json => {
|
this.res.json = json => {
|
||||||
this.ProjectCreationHandler.createExampleProject.called.should.equal(
|
this.ProjectCreationHandler.promises.createExampleProject.called.should.equal(
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
this.ProjectCreationHandler.createBasicProject
|
this.ProjectCreationHandler.promises.createBasicProject
|
||||||
.calledWith(this.user._id, this.projectName)
|
.calledWith(this.user._id, this.projectName)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
done()
|
done()
|
||||||
|
@ -419,10 +463,10 @@ describe('ProjectController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call the editor controller', function (done) {
|
it('should call the editor controller', function (done) {
|
||||||
this.EditorController.renameProject.callsArgWith(2)
|
this.EditorController.promises.renameProject.resolves()
|
||||||
this.res.sendStatus = code => {
|
this.res.sendStatus = code => {
|
||||||
code.should.equal(200)
|
code.should.equal(200)
|
||||||
this.EditorController.renameProject
|
this.EditorController.promises.renameProject
|
||||||
.calledWith(this.project_id, this.newProjectName)
|
.calledWith(this.project_id, this.newProjectName)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
done()
|
done()
|
||||||
|
@ -432,8 +476,7 @@ describe('ProjectController', function () {
|
||||||
|
|
||||||
it('should send an error to next() if there is a problem', function (done) {
|
it('should send an error to next() if there is a problem', function (done) {
|
||||||
let error
|
let error
|
||||||
this.EditorController.renameProject.callsArgWith(
|
this.EditorController.promises.renameProject.rejects(
|
||||||
2,
|
|
||||||
(error = new Error('problem'))
|
(error = new Error('problem'))
|
||||||
)
|
)
|
||||||
const next = e => {
|
const next = e => {
|
||||||
|
@ -470,17 +513,17 @@ describe('ProjectController', function () {
|
||||||
zotero: { encrypted: 'bbbb' },
|
zotero: { encrypted: 'bbbb' },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
this.ProjectGetter.getProject.callsArgWith(2, null, this.project)
|
this.ProjectGetter.promises.getProject.resolves(this.project)
|
||||||
this.UserModel.findById.callsArgWith(2, null, this.user)
|
this.UserModel.findById.returns({
|
||||||
this.SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {})
|
exec: sinon.stub().resolves(this.user),
|
||||||
this.AuthorizationManager.getPrivilegeLevelForProject.callsArgWith(
|
})
|
||||||
3,
|
this.SubscriptionLocator.promises.getUsersSubscription.resolves({})
|
||||||
null,
|
this.AuthorizationManager.promises.getPrivilegeLevelForProject.resolves(
|
||||||
'owner'
|
'owner'
|
||||||
)
|
)
|
||||||
this.ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
|
this.ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
|
||||||
this.InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1)
|
this.InactiveProjectManager.promises.reactivateProjectIfRequired.resolves()
|
||||||
this.ProjectUpdateHandler.markAsOpened.callsArgWith(1)
|
this.ProjectUpdateHandler.promises.markAsOpened.resolves()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should render the project/editor page', function (done) {
|
it('should render the project/editor page', function (done) {
|
||||||
|
@ -526,7 +569,7 @@ describe('ProjectController', function () {
|
||||||
opts.isRestrictedTokenMember.should.equal(false)
|
opts.isRestrictedTokenMember.should.equal(false)
|
||||||
return done()
|
return done()
|
||||||
}
|
}
|
||||||
return this.ProjectController.loadEditor(this.req, this.res)
|
this.ProjectController.loadEditor(this.req, this.res)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set isRestrictedTokenMember when appropriate', function (done) {
|
it('should set isRestrictedTokenMember when appropriate', function (done) {
|
||||||
|
@ -536,12 +579,12 @@ describe('ProjectController', function () {
|
||||||
opts.isRestrictedTokenMember.should.equal(true)
|
opts.isRestrictedTokenMember.should.equal(true)
|
||||||
return done()
|
return done()
|
||||||
}
|
}
|
||||||
return this.ProjectController.loadEditor(this.req, this.res)
|
this.ProjectController.loadEditor(this.req, this.res)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should invoke the session maintenance for logged in user', function (done) {
|
it('should invoke the session maintenance for logged in user', function (done) {
|
||||||
this.res.render = () => {
|
this.res.render = () => {
|
||||||
this.SplitTestSessionHandler.sessionMaintenance.should.have.been.calledWith(
|
this.SplitTestSessionHandler.promises.sessionMaintenance.should.have.been.calledWith(
|
||||||
this.req,
|
this.req,
|
||||||
this.user
|
this.user
|
||||||
)
|
)
|
||||||
|
@ -553,7 +596,7 @@ describe('ProjectController', function () {
|
||||||
it('should invoke the session maintenance for anonymous user', function (done) {
|
it('should invoke the session maintenance for anonymous user', function (done) {
|
||||||
this.SessionManager.getLoggedInUserId.returns(null)
|
this.SessionManager.getLoggedInUserId.returns(null)
|
||||||
this.res.render = () => {
|
this.res.render = () => {
|
||||||
this.SplitTestSessionHandler.sessionMaintenance.should.have.been.calledWith(
|
this.SplitTestSessionHandler.promises.sessionMaintenance.should.have.been.calledWith(
|
||||||
this.req
|
this.req
|
||||||
)
|
)
|
||||||
done()
|
done()
|
||||||
|
@ -571,11 +614,16 @@ describe('ProjectController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not render the page if the project can not be accessed', function (done) {
|
it('should not render the page if the project can not be accessed', function (done) {
|
||||||
this.AuthorizationManager.getPrivilegeLevelForProject = sinon
|
this.AuthorizationManager.promises.getPrivilegeLevelForProject = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(3, null, null)
|
.resolves(null)
|
||||||
this.res.sendStatus = (resCode, opts) => {
|
this.res.sendStatus = (resCode, opts) => {
|
||||||
resCode.should.equal(401)
|
resCode.should.equal(401)
|
||||||
|
this.AuthorizationManager.promises.getPrivilegeLevelForProject.should.have.been.calledWith(
|
||||||
|
this.user._id,
|
||||||
|
this.project_id,
|
||||||
|
'some-token'
|
||||||
|
)
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
this.ProjectController.loadEditor(this.req, this.res)
|
this.ProjectController.loadEditor(this.req, this.res)
|
||||||
|
@ -583,7 +631,7 @@ describe('ProjectController', function () {
|
||||||
|
|
||||||
it('should reactivateProjectIfRequired', function (done) {
|
it('should reactivateProjectIfRequired', function (done) {
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
this.InactiveProjectManager.reactivateProjectIfRequired
|
this.InactiveProjectManager.promises.reactivateProjectIfRequired
|
||||||
.calledWith(this.project_id)
|
.calledWith(this.project_id)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
done()
|
done()
|
||||||
|
@ -605,7 +653,7 @@ describe('ProjectController', function () {
|
||||||
|
|
||||||
it('should mark project as opened', function (done) {
|
it('should mark project as opened', function (done) {
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
this.ProjectUpdateHandler.markAsOpened
|
this.ProjectUpdateHandler.promises.markAsOpened
|
||||||
.calledWith(this.project_id)
|
.calledWith(this.project_id)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
done()
|
done()
|
||||||
|
@ -614,9 +662,9 @@ describe('ProjectController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call the brand variations handler for branded projects', function (done) {
|
it('should call the brand variations handler for branded projects', function (done) {
|
||||||
this.ProjectGetter.getProject.callsArgWith(2, null, this.brandedProject)
|
this.ProjectGetter.promises.getProject.resolves(this.brandedProject)
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
this.BrandVariationsHandler.getBrandVariationById
|
this.BrandVariationsHandler.promises.getBrandVariationById
|
||||||
.calledWith()
|
.calledWith()
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
done()
|
done()
|
||||||
|
@ -626,7 +674,7 @@ describe('ProjectController', function () {
|
||||||
|
|
||||||
it('should not call the brand variations handler for unbranded projects', function (done) {
|
it('should not call the brand variations handler for unbranded projects', function (done) {
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
this.BrandVariationsHandler.getBrandVariationById.called.should.equal(
|
this.BrandVariationsHandler.promises.getBrandVariationById.called.should.equal(
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
done()
|
done()
|
||||||
|
@ -635,7 +683,7 @@ describe('ProjectController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should expose the brand variation details as locals for branded projects', function (done) {
|
it('should expose the brand variation details as locals for branded projects', function (done) {
|
||||||
this.ProjectGetter.getProject.callsArgWith(2, null, this.brandedProject)
|
this.ProjectGetter.promises.getProject.resolves(this.brandedProject)
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
opts.brandVariation.should.deep.equal(this.brandVariationDetails)
|
opts.brandVariation.should.deep.equal(this.brandVariationDetails)
|
||||||
done()
|
done()
|
||||||
|
@ -645,7 +693,7 @@ describe('ProjectController', function () {
|
||||||
|
|
||||||
it('flushes the project to TPDS if a flush is pending', function (done) {
|
it('flushes the project to TPDS if a flush is pending', function (done) {
|
||||||
this.res.render = () => {
|
this.res.render = () => {
|
||||||
this.TpdsProjectFlusher.flushProjectToTpdsIfNeeded.should.have.been.calledWith(
|
this.TpdsProjectFlusher.promises.flushProjectToTpdsIfNeeded.should.have.been.calledWith(
|
||||||
this.project_id
|
this.project_id
|
||||||
)
|
)
|
||||||
done()
|
done()
|
||||||
|
@ -656,7 +704,7 @@ describe('ProjectController', function () {
|
||||||
it('should refresh the user features if the epoch is outdated', function (done) {
|
it('should refresh the user features if the epoch is outdated', function (done) {
|
||||||
this.FeaturesUpdater.featuresEpochIsCurrent = sinon.stub().returns(false)
|
this.FeaturesUpdater.featuresEpochIsCurrent = sinon.stub().returns(false)
|
||||||
this.res.render = () => {
|
this.res.render = () => {
|
||||||
this.FeaturesUpdater.refreshFeatures.should.have.been.calledWith(
|
this.FeaturesUpdater.promises.refreshFeatures.should.have.been.calledWith(
|
||||||
this.user._id,
|
this.user._id,
|
||||||
'load-editor'
|
'load-editor'
|
||||||
)
|
)
|
||||||
|
@ -898,9 +946,9 @@ describe('ProjectController', function () {
|
||||||
// default to saas enabled
|
// default to saas enabled
|
||||||
this.Features.hasFeature.withArgs('saas').returns(true)
|
this.Features.hasFeature.withArgs('saas').returns(true)
|
||||||
// default to without a subscription
|
// default to without a subscription
|
||||||
this.SubscriptionLocator.getUsersSubscription = sinon
|
this.SubscriptionLocator.promises.getUsersSubscription = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(1, null, null)
|
.resolves(null)
|
||||||
})
|
})
|
||||||
it('should not show without the saas feature', function (done) {
|
it('should not show without the saas feature', function (done) {
|
||||||
this.Features.hasFeature.withArgs('saas').returns(false)
|
this.Features.hasFeature.withArgs('saas').returns(false)
|
||||||
|
@ -918,9 +966,9 @@ describe('ProjectController', function () {
|
||||||
this.ProjectController.loadEditor(this.req, this.res)
|
this.ProjectController.loadEditor(this.req, this.res)
|
||||||
})
|
})
|
||||||
it('should not show for a user with a personal subscription', function (done) {
|
it('should not show for a user with a personal subscription', function (done) {
|
||||||
this.SubscriptionLocator.getUsersSubscription = sinon
|
this.SubscriptionLocator.promises.getUsersSubscription = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(1, null, {})
|
.resolves({})
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
expect(opts.showUpgradePrompt).to.equal(false)
|
expect(opts.showUpgradePrompt).to.equal(false)
|
||||||
done()
|
done()
|
||||||
|
@ -928,9 +976,9 @@ describe('ProjectController', function () {
|
||||||
this.ProjectController.loadEditor(this.req, this.res)
|
this.ProjectController.loadEditor(this.req, this.res)
|
||||||
})
|
})
|
||||||
it('should not show for a user who is a member of a group subscription', function (done) {
|
it('should not show for a user who is a member of a group subscription', function (done) {
|
||||||
this.LimitationsManager.userIsMemberOfGroupSubscription = sinon
|
this.LimitationsManager.promises.userIsMemberOfGroupSubscription = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(1, null, true)
|
.resolves(true)
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
expect(opts.showUpgradePrompt).to.equal(false)
|
expect(opts.showUpgradePrompt).to.equal(false)
|
||||||
done()
|
done()
|
||||||
|
@ -938,9 +986,9 @@ describe('ProjectController', function () {
|
||||||
this.ProjectController.loadEditor(this.req, this.res)
|
this.ProjectController.loadEditor(this.req, this.res)
|
||||||
})
|
})
|
||||||
it('should not show for a user with an affiliated paid university', function (done) {
|
it('should not show for a user with an affiliated paid university', function (done) {
|
||||||
this.InstitutionsFeatures.hasLicence = sinon
|
this.InstitutionsFeatures.promises.hasLicence = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(1, null, true)
|
.resolves(true)
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
expect(opts.showUpgradePrompt).to.equal(false)
|
expect(opts.showUpgradePrompt).to.equal(false)
|
||||||
done()
|
done()
|
||||||
|
@ -999,9 +1047,9 @@ describe('ProjectController', function () {
|
||||||
.withArgs(projects[3], this.user._id)
|
.withArgs(projects[3], this.user._id)
|
||||||
.returns(false)
|
.returns(false)
|
||||||
|
|
||||||
this.ProjectGetter.findAllUsersProjects = sinon
|
this.ProjectGetter.promises.findAllUsersProjects = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(2, null, [])
|
.resolves([])
|
||||||
this.ProjectController._buildProjectList = sinon.stub().returns(projects)
|
this.ProjectController._buildProjectList = sinon.stub().returns(projects)
|
||||||
this.SessionManager.getLoggedInUserId = sinon
|
this.SessionManager.getLoggedInUserId = sinon
|
||||||
.stub()
|
.stub()
|
||||||
|
@ -1033,9 +1081,9 @@ describe('ProjectController', function () {
|
||||||
{ path: '/main.tex', doc: true },
|
{ path: '/main.tex', doc: true },
|
||||||
]
|
]
|
||||||
this.files = [{ path: '/things/a.txt' }]
|
this.files = [{ path: '/things/a.txt' }]
|
||||||
this.ProjectGetter.getProject = sinon
|
this.ProjectGetter.promises.getProject = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.callsArgWith(1, null, this.project)
|
.resolves(this.project)
|
||||||
this.ProjectEntityHandler.getAllEntitiesFromProject = sinon
|
this.ProjectEntityHandler.getAllEntitiesFromProject = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.returns({ docs: this.docs, files: this.files })
|
.returns({ docs: this.docs, files: this.files })
|
||||||
|
@ -1051,7 +1099,7 @@ describe('ProjectController', function () {
|
||||||
{ path: '/things/b.txt', type: 'doc' },
|
{ path: '/things/b.txt', type: 'doc' },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
expect(this.ProjectGetter.getProject.callCount).to.equal(1)
|
expect(this.ProjectGetter.promises.getProject.callCount).to.equal(1)
|
||||||
expect(
|
expect(
|
||||||
this.ProjectEntityHandler.getAllEntitiesFromProject.callCount
|
this.ProjectEntityHandler.getAllEntitiesFromProject.callCount
|
||||||
).to.equal(1)
|
).to.equal(1)
|
||||||
|
|
Loading…
Reference in a new issue