Merge pull request #25151 from overleaf/dk-use-user-features
UserFeaturesContext with cross-tab syncing via BroadcastChannel GitOrigin-RevId: 4262719f5018f5717211851ce28b3255af65461a
This commit is contained in:
committed by
Copybot
parent
2f3166aa54
commit
82e5b2c5d7
@@ -1121,44 +1121,6 @@ describe('ProjectController', function () {
|
||||
this.ProjectController.loadEditor(this.req, this.res)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when fetching the users featureSet', function () {
|
||||
beforeEach(function () {
|
||||
this.Modules.promises.hooks.fire = sinon.stub().resolves()
|
||||
this.user.features = {}
|
||||
})
|
||||
|
||||
it('should take into account features overrides from modules', function (done) {
|
||||
// this case occurs when the user has bought the ai bundle on WF, which should include our error assistant
|
||||
const bundleFeatures = { aiErrorAssistant: true }
|
||||
this.user.features = { aiErrorAssistant: false }
|
||||
this.Modules.promises.hooks.fire = sinon
|
||||
.stub()
|
||||
.resolves([bundleFeatures])
|
||||
this.res.render = (pageName, opts) => {
|
||||
expect(opts.user.features).to.deep.equal(bundleFeatures)
|
||||
this.Modules.promises.hooks.fire.should.have.been.calledWith(
|
||||
'getModuleProvidedFeatures',
|
||||
this.user._id
|
||||
)
|
||||
done()
|
||||
}
|
||||
this.ProjectController.loadEditor(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should handle modules not returning any features', function (done) {
|
||||
this.Modules.promises.hooks.fire = sinon.stub().resolves([])
|
||||
this.res.render = (pageName, opts) => {
|
||||
expect(opts.user.features).to.deep.equal({})
|
||||
this.Modules.promises.hooks.fire.should.have.been.calledWith(
|
||||
'getModuleProvidedFeatures',
|
||||
this.user._id
|
||||
)
|
||||
done()
|
||||
}
|
||||
this.ProjectController.loadEditor(this.req, this.res)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('userProjectsJson', function () {
|
||||
|
||||
@@ -158,6 +158,7 @@ describe('SubscriptionController', function () {
|
||||
'./FeaturesUpdater': (this.FeaturesUpdater = {
|
||||
promises: {
|
||||
hasFeaturesViaWritefull: sinon.stub().resolves(false),
|
||||
refreshFeatures: sinon.stub().resolves({ features: {} }),
|
||||
},
|
||||
}),
|
||||
'./GroupPlansData': (this.GroupPlansData = {}),
|
||||
@@ -186,6 +187,11 @@ describe('SubscriptionController', function () {
|
||||
'../../util/currency': (this.currency = {
|
||||
formatCurrency: sinon.stub(),
|
||||
}),
|
||||
'../../models/User': {
|
||||
User: {
|
||||
findById: sinon.stub().resolves(this.user),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -221,7 +227,10 @@ describe('SubscriptionController', function () {
|
||||
title: 'thank_you',
|
||||
personalSubscription: 'foo',
|
||||
postCheckoutRedirect: undefined,
|
||||
user: this.user,
|
||||
user: {
|
||||
_id: this.user._id,
|
||||
features: this.user.features,
|
||||
},
|
||||
})
|
||||
done()
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ describe('UserGetter', function () {
|
||||
beforeEach(function () {
|
||||
const confirmedAt = new Date()
|
||||
this.fakeUser = {
|
||||
_id: '12390i',
|
||||
_id: new ObjectId(),
|
||||
email: 'email2@foo.bar',
|
||||
emails: [
|
||||
{
|
||||
@@ -45,6 +45,10 @@ describe('UserGetter', function () {
|
||||
}
|
||||
this.getUserAffiliations = sinon.stub().resolves([])
|
||||
|
||||
this.Modules = {
|
||||
promises: { hooks: { fire: sinon.stub().resolves() } },
|
||||
}
|
||||
|
||||
this.UserGetter = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'../Helpers/Mongo': { normalizeQuery, normalizeMultiQuery },
|
||||
@@ -63,6 +67,7 @@ describe('UserGetter', function () {
|
||||
'../../models/User': {
|
||||
User: (this.User = {}),
|
||||
},
|
||||
'../../infrastructure/Modules': this.Modules,
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -1259,4 +1264,56 @@ describe('UserGetter', function () {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getUserFeatures', function () {
|
||||
beforeEach(function () {
|
||||
this.Modules.promises.hooks.fire = sinon.stub().resolves()
|
||||
this.fakeUser.features = {}
|
||||
})
|
||||
|
||||
it('should return user features', function (done) {
|
||||
this.fakeUser.features = { feature1: true, feature2: false }
|
||||
this.UserGetter.getUserFeatures(new ObjectId(), (error, features) => {
|
||||
expect(error).to.not.exist
|
||||
expect(features).to.deep.equal(this.fakeUser.features)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should return user features when using promises', async function () {
|
||||
this.fakeUser.features = { feature1: true, feature2: false }
|
||||
const features = await this.UserGetter.promises.getUserFeatures(
|
||||
this.fakeUser._id
|
||||
)
|
||||
expect(features).to.deep.equal(this.fakeUser.features)
|
||||
})
|
||||
|
||||
it('should take into account features overrides from modules', async function () {
|
||||
// this case occurs when the user has bought the ai bundle on WF, which should include our error assistant
|
||||
const bundleFeatures = { aiErrorAssistant: true }
|
||||
this.fakeUser.features = { aiErrorAssistant: false }
|
||||
this.Modules.promises.hooks.fire = sinon.stub().resolves([bundleFeatures])
|
||||
const features = await this.UserGetter.promises.getUserFeatures(
|
||||
this.fakeUser._id
|
||||
)
|
||||
expect(features).to.deep.equal(bundleFeatures)
|
||||
this.Modules.promises.hooks.fire.should.have.been.calledWith(
|
||||
'getModuleProvidedFeatures',
|
||||
this.fakeUser._id
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle modules not returning any features', async function () {
|
||||
this.Modules.promises.hooks.fire = sinon.stub().resolves([])
|
||||
this.fakeUser.features = { test: true }
|
||||
const features = await this.UserGetter.promises.getUserFeatures(
|
||||
this.fakeUser._id
|
||||
)
|
||||
expect(features).to.deep.equal({ test: true })
|
||||
this.Modules.promises.hooks.fire.should.have.been.calledWith(
|
||||
'getModuleProvidedFeatures',
|
||||
this.fakeUser._id
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,7 +10,11 @@ describe('UserInfoController', function () {
|
||||
beforeEach(function () {
|
||||
this.UserDeleter = { deleteUser: sinon.stub().callsArgWith(1) }
|
||||
this.UserUpdater = { updatePersonalInfo: sinon.stub() }
|
||||
this.UserGetter = {}
|
||||
this.UserGetter = {
|
||||
promises: {
|
||||
getUserFeatures: sinon.stub(),
|
||||
},
|
||||
}
|
||||
|
||||
this.UserInfoController = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
@@ -148,4 +152,74 @@ describe('UserInfoController', function () {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getUserFeatures', function () {
|
||||
describe('when the user is logged in', function () {
|
||||
beforeEach(async function () {
|
||||
this.user_id = new ObjectId().toString()
|
||||
this.features = {
|
||||
collaborators: 10,
|
||||
trackChanges: true,
|
||||
references: true,
|
||||
}
|
||||
this.SessionManager.getLoggedInUserId.returns(this.user_id)
|
||||
this.UserGetter.promises.getUserFeatures.resolves(this.features)
|
||||
await this.UserInfoController.getUserFeatures(
|
||||
this.req,
|
||||
this.res,
|
||||
this.next
|
||||
)
|
||||
})
|
||||
|
||||
it('should fetch the user features', function () {
|
||||
expect(this.UserGetter.promises.getUserFeatures.callCount).to.equal(1)
|
||||
expect(
|
||||
this.UserGetter.promises.getUserFeatures.calledWith(this.user_id)
|
||||
).to.equal(true)
|
||||
})
|
||||
|
||||
it('should return the features as JSON', function () {
|
||||
expect(this.res.json.callCount).to.equal(1)
|
||||
expect(this.res.json.calledWith(this.features)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the user is not logged in', function () {
|
||||
beforeEach(async function () {
|
||||
this.SessionManager.getLoggedInUserId.returns(null)
|
||||
await this.UserInfoController.getUserFeatures(
|
||||
this.req,
|
||||
this.res,
|
||||
this.next
|
||||
)
|
||||
})
|
||||
|
||||
it('should call next with an error', function () {
|
||||
expect(this.next.callCount).to.equal(1)
|
||||
expect(this.next.firstCall.args[0]).to.be.an.instanceof(Error)
|
||||
expect(this.next.firstCall.args[0].message).to.equal(
|
||||
'User is not logged in'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when fetching features fails', function () {
|
||||
beforeEach(async function () {
|
||||
this.user_id = new ObjectId().toString()
|
||||
this.error = new Error('something went wrong')
|
||||
this.SessionManager.getLoggedInUserId.returns(this.user_id)
|
||||
this.UserGetter.promises.getUserFeatures.rejects(this.error)
|
||||
await this.UserInfoController.getUserFeatures(
|
||||
this.req,
|
||||
this.res,
|
||||
this.next
|
||||
)
|
||||
})
|
||||
|
||||
it('should call next with the error', function () {
|
||||
expect(this.next.callCount).to.equal(1)
|
||||
expect(this.next.firstCall.args[0]).to.equal(this.error)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user