diff --git a/services/web/test/acceptance/src/FeatureUpdaterTests.js b/services/web/test/acceptance/src/FeatureUpdaterTests.js index 5bc1ee8847..33bdc5cc88 100644 --- a/services/web/test/acceptance/src/FeatureUpdaterTests.js +++ b/services/web/test/acceptance/src/FeatureUpdaterTests.js @@ -1,21 +1,5 @@ -/* eslint-disable - node/handle-callback-err, - max-len, - no-return-assign, - 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 - */ const { expect } = require('chai') -const async = require('async') -const UserClient = require('./helpers/User') -const request = require('./helpers/request') +const UserHelper = require('./helpers/UserHelper') const settings = require('settings-sharelatex') const { ObjectId } = require('mongodb') const { Subscription } = require('../../../app/src/models/Subscription') @@ -33,67 +17,60 @@ before(function() { }) const syncUserAndGetFeatures = function(user, callback) { - if (callback == null) { - callback = function(error, features) {} - } - return FeaturesUpdater.refreshFeatures(user._id, error => { - if (error != null) { + FeaturesUpdater.refreshFeatures(user._id, error => { + if (error) { return callback(error) } - return User.findById(user._id, (error, user) => { - if (error != null) { + User.findById(user._id, (error, user) => { + if (error) { return callback(error) } const { features } = user.toObject() delete features.$init // mongoose internals - return callback(null, features) + callback(null, features) }) }) } describe('FeatureUpdater.refreshFeatures', function() { - beforeEach(function(done) { - this.user = new UserClient() - return this.user.ensureUserExists(error => { - if (error != null) { - throw error - } - return done() - }) + let userHelper, user + beforeEach(async function() { + userHelper = await UserHelper.createUser() + user = userHelper.user }) describe('when user has no subscriptions', function() { it('should set their features to the basic set', function(done) { - return syncUserAndGetFeatures(this.user, (error, features) => { - if (error != null) { + syncUserAndGetFeatures(user, (error, features) => { + if (error) { throw error } expect(features).to.deep.equal(settings.defaultFeatures) - return done() + done() }) }) }) describe('when the user has an individual subscription', function() { beforeEach(function() { - return Subscription.create({ - admin_id: this.user._id, - manager_ids: [this.user._id], + Subscription.create({ + admin_id: user._id, + manager_ids: [user._id], planCode: 'collaborator', customAccount: true }) }) // returns a promise it('should set their features to the upgraded set', function(done) { - return syncUserAndGetFeatures(this.user, (error, features) => { - if (error != null) { + syncUserAndGetFeatures(user, (error, features) => { + if (error) { throw error } const plan = settings.plans.find( plan => plan.planCode === 'collaborator' ) expect(features).to.deep.equal(plan.features) - return done() + done() }) }) }) @@ -101,10 +78,10 @@ describe('FeatureUpdater.refreshFeatures', function() { describe('when the user is in a group subscription', function() { beforeEach(function() { const groupAdminId = ObjectId() - return Subscription.create({ + Subscription.create({ admin_id: groupAdminId, manager_ids: [groupAdminId], - member_ids: [this.user._id], + member_ids: [user._id], groupAccount: true, planCode: 'collaborator', customAccount: true @@ -112,15 +89,15 @@ describe('FeatureUpdater.refreshFeatures', function() { }) // returns a promise it('should set their features to the upgraded set', function(done) { - return syncUserAndGetFeatures(this.user, (error, features) => { - if (error != null) { + syncUserAndGetFeatures(user, (error, features) => { + if (error) { throw error } const plan = settings.plans.find( plan => plan.planCode === 'collaborator' ) expect(features).to.deep.equal(plan.features) - return done() + done() }) }) }) @@ -129,7 +106,7 @@ describe('FeatureUpdater.refreshFeatures', function() { beforeEach(function() { return User.updateOne( { - _id: this.user._id + _id: user._id }, { refered_user_count: 10 @@ -138,8 +115,8 @@ describe('FeatureUpdater.refreshFeatures', function() { }) // returns a promise it('should set their features to the bonus set', function(done) { - return syncUserAndGetFeatures(this.user, (error, features) => { - if (error != null) { + syncUserAndGetFeatures(user, (error, features) => { + if (error) { throw error } expect(features).to.deep.equal( @@ -149,49 +126,59 @@ describe('FeatureUpdater.refreshFeatures', function() { settings.bonus_features[9] ) ) - return done() + done() }) }) }) describe('when the user has affiliations', function() { - beforeEach(function() { + let email2, institutionId, hostname + beforeEach(async function() { + institutionId = MockV1Api.createInstitution({ commonsAccount: true }) + hostname = 'institution.edu' + MockV1Api.addInstitutionDomain(institutionId, hostname, { + confirmed: true + }) + email2 = `${user._id}@${hostname}` + userHelper = await UserHelper.loginUser( + userHelper.getDefaultEmailPassword() + ) + await userHelper.addEmail(email2) this.institutionPlan = settings.plans.find( plan => plan.planCode === settings.institutionPlanCode ) - this.email = this.user.emails[0].email - return (this.affiliationData = { - email: this.email, - licence: 'pro_plus', - institution: { confirmed: true } - }) }) it('should not set their features if email is not confirmed', function(done) { - MockV1Api.setAffiliations(this.user._id, [this.affiliationData]) - return syncUserAndGetFeatures(this.user, (error, features) => { + syncUserAndGetFeatures(user, (error, features) => { expect(features).to.deep.equal(settings.defaultFeatures) - return done() + done() }) }) - it('should set their features if email is confirmed', function(done) { - MockV1Api.setAffiliations(this.user._id, [this.affiliationData]) - return this.user.confirmEmail(this.email, error => { - return syncUserAndGetFeatures(this.user, (error, features) => { + describe('when email is confirmed', function() { + beforeEach(async function() { + await userHelper.confirmEmail(user._id, email2) + }) + + it('should set their features', function(done) { + syncUserAndGetFeatures(user, (error, features) => { expect(features).to.deep.equal(this.institutionPlan.features) - return done() + done() }) }) - }) - it('should not set their features if institution is not confirmed', function(done) { - this.affiliationData.institution.confirmed = false - MockV1Api.setAffiliations(this.user._id, [this.affiliationData]) - return this.user.confirmEmail(this.email, error => { - return syncUserAndGetFeatures(this.user, (error, features) => { - expect(features).to.deep.equal(settings.defaultFeatures) - return done() + describe('when domain is not confirmed as part of institution', function() { + beforeEach(function() { + MockV1Api.updateInstitutionDomain(institutionId, hostname, { + confirmed: false + }) + }) + it('should not set their features', function(done) { + syncUserAndGetFeatures(user, (error, features) => { + expect(features).to.deep.equal(settings.defaultFeatures) + done() + }) }) }) }) @@ -201,7 +188,7 @@ describe('FeatureUpdater.refreshFeatures', function() { beforeEach(function() { return User.updateOne( { - _id: this.user._id + _id: user._id }, { refered_user_count: 10, @@ -211,8 +198,8 @@ describe('FeatureUpdater.refreshFeatures', function() { }) // returns a promise it('should set their features to the bonus set and downgrade the extras', function(done) { - return syncUserAndGetFeatures(this.user, (error, features) => { - if (error != null) { + syncUserAndGetFeatures(user, (error, features) => { + if (error) { throw error } expect(features).to.deep.equal( @@ -222,7 +209,7 @@ describe('FeatureUpdater.refreshFeatures', function() { settings.bonus_features[9] ) ) - return done() + done() }) }) }) @@ -232,7 +219,7 @@ describe('FeatureUpdater.refreshFeatures', function() { MockV1Api.setUser(42, { plan_name: 'free' }) return User.updateOne( { - _id: this.user._id + _id: user._id }, { overleaf: { @@ -243,13 +230,13 @@ describe('FeatureUpdater.refreshFeatures', function() { }) // returns a promise it('should set their features to the v1 plan', function(done) { - return syncUserAndGetFeatures(this.user, (error, features) => { - if (error != null) { + syncUserAndGetFeatures(user, (error, features) => { + if (error) { throw error } const plan = settings.plans.find(plan => plan.planCode === 'v1_free') expect(features).to.deep.equal(plan.features) - return done() + done() }) }) }) @@ -259,7 +246,7 @@ describe('FeatureUpdater.refreshFeatures', function() { MockV1Api.setUser(42, { plan_name: 'free' }) return User.updateOne( { - _id: this.user._id + _id: user._id }, { overleaf: { @@ -271,8 +258,8 @@ describe('FeatureUpdater.refreshFeatures', function() { }) // returns a promise it('should set their features to the best of the v1 plan and bonus features', function(done) { - return syncUserAndGetFeatures(this.user, (error, features) => { - if (error != null) { + syncUserAndGetFeatures(user, (error, features) => { + if (error) { throw error } const v1plan = settings.plans.find(plan => plan.planCode === 'v1_free') @@ -282,7 +269,7 @@ describe('FeatureUpdater.refreshFeatures', function() { settings.bonus_features[9] ) expect(features).to.deep.equal(expectedFeatures) - return done() + done() }) }) }) @@ -293,20 +280,20 @@ describe('FeatureUpdater.refreshFeatures', function() { Subscription.create( { - admin_id: this.user._id, - manager_ids: [this.user._id], + admin_id: user._id, + manager_ids: [user._id], planCode: 'professional', customAccount: true }, error => { - if (error != null) { + if (error) { throw error } - return Subscription.create( + Subscription.create( { admin_id: groupAdminId, manager_ids: [groupAdminId], - member_ids: [this.user._id], + member_ids: [user._id], groupAccount: true, planCode: 'collaborator', customAccount: true @@ -318,24 +305,24 @@ describe('FeatureUpdater.refreshFeatures', function() { }) it('should set their features to the best set', function(done) { - return syncUserAndGetFeatures(this.user, (error, features) => { - if (error != null) { + syncUserAndGetFeatures(user, (error, features) => { + if (error) { throw error } const plan = settings.plans.find( plan => plan.planCode === 'professional' ) expect(features).to.deep.equal(plan.features) - return done() + done() }) }) }) describe('when the notifyV1Flag is passed', function() { beforeEach(function() { - return User.updateOne( + User.updateOne( { - _id: this.user._id + _id: user._id }, { overleaf: { @@ -352,7 +339,7 @@ describe('FeatureUpdater.refreshFeatures', function() { futureDate.setDate(futureDate.getDate() + 1) return User.updateOne( { - _id: this.user._id + _id: user._id }, { featuresOverrides: [ @@ -379,8 +366,8 @@ describe('FeatureUpdater.refreshFeatures', function() { }) // returns a promise it('should set their features to the overridden set', function(done) { - return syncUserAndGetFeatures(this.user, (error, features) => { - if (error != null) { + syncUserAndGetFeatures(user, (error, features) => { + if (error) { throw error } let expectedFeatures = Object.assign(settings.defaultFeatures, { @@ -388,7 +375,7 @@ describe('FeatureUpdater.refreshFeatures', function() { trackChanges: true }) expect(features).to.deep.equal(expectedFeatures) - return done() + done() }) }) }) diff --git a/services/web/test/acceptance/src/SubscriptionDashboardTests.js b/services/web/test/acceptance/src/SubscriptionDashboardTests.js index 58c308a9f6..b191cbd6c4 100644 --- a/services/web/test/acceptance/src/SubscriptionDashboardTests.js +++ b/services/web/test/acceptance/src/SubscriptionDashboardTests.js @@ -1,18 +1,6 @@ -/* eslint-disable - node/handle-callback-err, - max-len, -*/ -// 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 - */ const { expect } = require('chai') const async = require('async') -const User = require('./helpers/User') +const UserHelper = require('./helpers/UserHelper') const { Subscription } = require('../../../app/src/models/Subscription') const { Institution } = require('../../../app/src/models/Institution') const SubscriptionViewModelBuilder = require('../../../app/src/Features/Subscription/SubscriptionViewModelBuilder') @@ -20,6 +8,18 @@ const RecurlySubscription = require('./helpers/RecurlySubscription') const MockRecurlyApiClass = require('./mocks/MockRecurlyApi') const MockV1ApiClass = require('./mocks/MockV1Api') +async function buildUsersSubscriptionViewModelPromise(userId) { + return new Promise((resolve, reject) => { + SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( + userId, + (error, data) => { + if (error) reject(error) + resolve(data) + } + ) + }) +} + let MockV1Api, MockRecurlyApi before(function() { @@ -29,9 +29,10 @@ before(function() { describe('Subscriptions', function() { describe('dashboard', function() { - beforeEach(function(done) { - this.user = new User() - return this.user.ensureUserExists(done) + let userHelper + beforeEach(async function() { + userHelper = await UserHelper.createUser() + this.user = userHelper.user }) it('should not list personal plan', function() { @@ -45,24 +46,24 @@ describe('Subscriptions', function() { describe('when the user has no subscription', function() { beforeEach(function(done) { - return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( + SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( this.user, (error, data) => { this.data = data - if (error != null) { + if (error) { return done(error) } - return done() + done() } ) }) it('should return no personalSubscription', function() { - return expect(this.data.personalSubscription).to.equal(null) + expect(this.data.personalSubscription).to.equal(null) }) it('should return no memberGroupSubscriptions', function() { - return expect(this.data.memberGroupSubscriptions).to.deep.equal([]) + expect(this.data.memberGroupSubscriptions).to.deep.equal([]) }) }) @@ -89,17 +90,17 @@ describe('Subscriptions', function() { 'test-coupon-3': { name: 'TestCoupon3' } } this.recurlySubscription.ensureExists(error => { - if (error != null) { + if (error) { return done(error) } - return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( + SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( this.user, (error, data) => { this.data = data - if (error != null) { + if (error) { return done(error) } - return done() + done() } ) }) @@ -122,7 +123,7 @@ describe('Subscriptions', function() { expect(subscription).to.exist expect(subscription.planCode).to.equal('collaborator') expect(subscription.recurly).to.exist - return expect(subscription.recurly).to.deep.equal({ + expect(subscription.recurly).to.deep.equal({ activeCoupons: [], billingDetailsLink: 'https://test.recurly.com/account/billing_info/edit?ht=mock-login-token', @@ -137,7 +138,7 @@ describe('Subscriptions', function() { trial_ends_at: new Date(2018, 6, 7), trialEndsAtFormatted: '7th July 2018', account: { - account_code: this.user._id, + account_code: this.user._id.toString(), email: 'mock@email.com', hosted_login_token: 'mock-login-token' }, @@ -147,7 +148,7 @@ describe('Subscriptions', function() { }) it('should return no memberGroupSubscriptions', function() { - return expect(this.data.memberGroupSubscriptions).to.deep.equal([]) + expect(this.data.memberGroupSubscriptions).to.deep.equal([]) }) it('should include redeemed coupons', function(done) { @@ -158,7 +159,7 @@ describe('Subscriptions', function() { ] // rebuild the view model with the redemptions - return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( + SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( this.user, (error, data) => { expect(error).to.not.exist @@ -176,7 +177,7 @@ describe('Subscriptions', function() { description: '' } ]) - return done() + done() } ) }) @@ -197,17 +198,17 @@ describe('Subscriptions', function() { planCode: 'collaborator' }, error => { - if (error != null) { + if (error) { return done(error) } - return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( + SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( this.user, (error, data) => { this.data = data - if (error != null) { + if (error) { return done(error) } - return done() + done() } ) } @@ -227,61 +228,36 @@ describe('Subscriptions', function() { const subscription = this.data.personalSubscription expect(subscription).to.exist expect(subscription.planCode).to.equal('collaborator') - return expect(subscription.recurly).to.not.exist + expect(subscription.recurly).to.not.exist }) it('should return no memberGroupSubscriptions', function() { - return expect(this.data.memberGroupSubscriptions).to.deep.equal([]) + expect(this.data.memberGroupSubscriptions).to.deep.equal([]) }) }) describe('when the user is a member of a group subscription', function() { - beforeEach(function(done) { - this.owner1 = new User() - this.owner2 = new User() - async.series( - [ - cb => this.owner1.ensureUserExists(cb), - cb => this.owner2.ensureUserExists(cb), - cb => - Subscription.create( - { - admin_id: this.owner1._id, - manager_ids: [this.owner1._id], - planCode: 'collaborator', - groupPlan: true, - member_ids: [this.user._id] - }, - cb - ), - cb => - Subscription.create( - { - admin_id: this.owner2._id, - manager_ids: [this.owner2._id], - planCode: 'collaborator', - groupPlan: true, - member_ids: [this.user._id] - }, - cb - ) - ], - error => { - if (error != null) { - return done(error) - } - return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( - this.user, - (error, data) => { - this.data = data - if (error != null) { - return done(error) - } - return done() - } - ) - } - ) + beforeEach(async function() { + const userHelperOwner1 = await UserHelper.createUser() + const userHelperOwner2 = await UserHelper.createUser() + this.owner1 = userHelperOwner1.user + this.owner2 = userHelperOwner2.user + + await Subscription.create({ + admin_id: this.owner1._id, + manager_ids: [this.owner1._id], + planCode: 'collaborator', + groupPlan: true, + member_ids: [this.user._id] + }) + await Subscription.create({ + admin_id: this.owner2._id, + manager_ids: [this.owner2._id], + planCode: 'collaborator', + groupPlan: true, + member_ids: [this.user._id] + }) + this.data = await buildUsersSubscriptionViewModelPromise(this.user._id) }) after(function(done) { @@ -290,10 +266,10 @@ describe('Subscriptions', function() { admin_id: this.owner1._id }, error => { - if (error != null) { + if (error) { return done(error) } - return Subscription.deleteOne( + Subscription.deleteOne( { admin_id: this.owner2._id }, @@ -304,7 +280,7 @@ describe('Subscriptions', function() { }) it('should return no personalSubscription', function() { - return expect(this.data.personalSubscription).to.equal(null) + expect(this.data.personalSubscription).to.equal(null) }) it('should return the two memberGroupSubscriptions', function() { @@ -312,48 +288,27 @@ describe('Subscriptions', function() { expect( // Mongoose populates the admin_id with the user this.data.memberGroupSubscriptions[0].admin_id._id.toString() - ).to.equal(this.owner1._id) - return expect( + ).to.equal(this.owner1._id.toString()) + expect( this.data.memberGroupSubscriptions[1].admin_id._id.toString() - ).to.equal(this.owner2._id) + ).to.equal(this.owner2._id.toString()) }) }) describe('when the user is a manager of a group subscription', function() { - beforeEach(function(done) { - this.owner1 = new User() - this.owner2 = new User() - async.series( - [ - cb => this.owner1.ensureUserExists(cb), - cb => this.owner2.ensureUserExists(cb), - cb => - Subscription.create( - { - admin_id: this.owner1._id, - manager_ids: [this.owner1._id, this.user._id], - planCode: 'collaborator', - groupPlan: true - }, - cb - ) - ], - error => { - if (error != null) { - return done(error) - } - return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( - this.user, - (error, data) => { - this.data = data - if (error != null) { - return done(error) - } - return done() - } - ) - } - ) + beforeEach(async function() { + const userHelperOwner1 = await UserHelper.createUser() + const userHelperOwner2 = await UserHelper.createUser() + this.owner1 = userHelperOwner1.user + this.owner2 = userHelperOwner2.user + + await Subscription.create({ + admin_id: this.owner1._id, + manager_ids: [this.owner1._id, this.user._id], + planCode: 'collaborator', + groupPlan: true + }) + this.data = await buildUsersSubscriptionViewModelPromise(this.user._id) }) after(function(done) { @@ -366,7 +321,7 @@ describe('Subscriptions', function() { }) it('should return no personalSubscription', function() { - return expect(this.data.personalSubscription).to.equal(null) + expect(this.data.personalSubscription).to.equal(null) }) it('should return the managedGroupSubscriptions', function() { @@ -375,8 +330,8 @@ describe('Subscriptions', function() { expect( // Mongoose populates the admin_id with the user subscription.admin_id._id.toString() - ).to.equal(this.owner1._id) - return expect(subscription.groupPlan).to.equal(true) + ).to.equal(this.owner1._id.toString()) + expect(subscription.groupPlan).to.equal(true) }) }) @@ -386,7 +341,7 @@ describe('Subscriptions', function() { async.series( [ cb => { - return Institution.create( + Institution.create( { v1Id: this.v1Id, managerIds: [this.user._id] @@ -396,17 +351,17 @@ describe('Subscriptions', function() { } ], error => { - if (error != null) { + if (error) { return done(error) } - return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( + SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( this.user, (error, data) => { this.data = data - if (error != null) { + if (error) { return done(error) } - return done() + done() } ) } @@ -426,119 +381,67 @@ describe('Subscriptions', function() { expect(this.data.managedInstitutions.length).to.equal(1) const institution = this.data.managedInstitutions[0] expect(institution.v1Id).to.equal(this.v1Id) - return expect(institution.name).to.equal(`Institution ${this.v1Id}`) + expect(institution.name).to.equal(`Institution ${this.v1Id}`) }) }) describe('when the user is a member of an affiliation', function() { - beforeEach(function(done) { + beforeEach(async function() { const v1Id = MockV1Api.nextV1Id() MockV1Api.setUser(v1Id, { subscription: {}, subscription_status: {} }) - MockV1Api.setAffiliations(this.user._id, [ - { - email: 'confirmed-affiliation-email@stanford.example.edu', - licence: 'pro_plus', - department: 'Math', - role: 'Prof', - inferred: false, - institution: { - name: 'Stanford', - confirmed: true - } - }, - { - email: 'unconfirmed-affiliation-email@harvard.example.edu', - licence: 'pro_plus', - institution: { - name: 'Harvard', - confirmed: true - } - }, - { - email: 'confirmed-affiliation-email@mit.example.edu', - licence: 'pro_plus', - institution: { name: 'MIT', confirmed: false } - } - ]) - return async.series( - [ - cb => { - return this.user.setV1Id(v1Id, cb) - }, - cb => { - return this.user.addEmail( - 'unconfirmed-affiliation-email@harvard.example.edu', - cb - ) - }, - cb => { - return this.user.addEmail( - 'confirmed-affiliation-email@stanford.example.edu', - cb - ) - }, - cb => { - return this.user.confirmEmail( - 'confirmed-affiliation-email@stanford.example.edu', - cb - ) - }, - cb => { - return this.user.addEmail( - 'confirmed-affiliation-email@mit.example.edu', - cb - ) - }, - cb => { - return this.user.confirmEmail( - 'confirmed-affiliation-email@mit.example.edu', - cb - ) - } - ], - error => { - if (error != null) { - return done(error) - } - return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( - this.user, - (error, data) => { - this.data = data - if (error != null) { - return done(error) - } - return done() - } - ) - } + await UserHelper.updateUser(this.user._id, { + $set: { overleaf: { id: v1Id } } + }) + + const harvardDomain = 'harvard.example.edu' + const mitDomain = 'mit.example.edu' + const stanfordDomain = 'stanford.example.edu' + const harvardId = MockV1Api.createInstitution({ + name: 'Harvard', + hostname: harvardDomain + }) + const mitId = MockV1Api.createInstitution({ + name: 'MIT', + hostname: mitDomain + }) + const stanfordId = MockV1Api.createInstitution({ + name: 'Stanford', + hostname: stanfordDomain + }) + MockV1Api.updateInstitutionDomain(harvardId, harvardDomain, { + confirmed: true + }) + MockV1Api.updateInstitutionDomain(mitId, mitDomain, { + confirmed: false + }) + MockV1Api.updateInstitutionDomain(stanfordId, stanfordDomain, { + confirmed: true + }) + this.harvardEmail = `unconfirmed-affiliation-email@${harvardDomain}` + this.stanfordEmail = `confirmed-affiliation-email@${stanfordDomain}` + const mitEmail = `confirmed-affiliation-email@${mitDomain}` + userHelper = await UserHelper.loginUser( + userHelper.getDefaultEmailPassword() ) + await userHelper.addEmail(this.harvardEmail) + await userHelper.addEmailAndConfirm(this.user._id, this.stanfordEmail) + await userHelper.addEmailAndConfirm(this.user._id, mitEmail) + this.data = await buildUsersSubscriptionViewModelPromise(this.user._id) }) it('should return only the affilations with confirmed institutions, and confirmed emails', function() { - return expect(this.data.confirmedMemberAffiliations).to.deep.equal([ - { - licence: 'pro_plus', - department: 'Math', - role: 'Prof', - inferred: false, - inReconfirmNotificationPeriod: false, - institution: { - name: 'Stanford', - confirmed: true - }, - lastDayToReconfirm: undefined, - pastReconfirmDate: false, - portal: undefined - } - ]) + expect(this.data.confirmedMemberAffiliations.length).to.equal(1) + expect( + this.data.confirmedMemberAffiliations[0].institution.name + ).to.equal('Stanford') }) }) describe('when the user has a v1 subscription', function() { - beforeEach(function(done) { + beforeEach(async function() { let v1Id MockV1Api.setUser((v1Id = MockV1Api.nextV1Id()), { subscription: (this.subscription = { @@ -556,33 +459,26 @@ describe('Subscriptions', function() { team: null }) }) - return this.user.setV1Id(v1Id, error => { - if (error != null) { - return done(error) - } - return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel( - this.user, - (error, data) => { - this.data = data - if (error != null) { - return done(error) - } - return done() + await UserHelper.updateUser(this.user._id, { + $set: { + overleaf: { + id: v1Id } - ) + } }) + this.data = await buildUsersSubscriptionViewModelPromise(this.user._id) }) it('should return no personalSubscription', function() { - return expect(this.data.personalSubscription).to.equal(null) + expect(this.data.personalSubscription).to.equal(null) }) it('should return no memberGroupSubscriptions', function() { - return expect(this.data.memberGroupSubscriptions).to.deep.equal([]) + expect(this.data.memberGroupSubscriptions).to.deep.equal([]) }) it('should return a v1SubscriptionStatus', function() { - return expect(this.data.v1SubscriptionStatus).to.deep.equal( + expect(this.data.v1SubscriptionStatus).to.deep.equal( this.subscription_status ) }) @@ -590,39 +486,32 @@ describe('Subscriptions', function() { }) describe('canceling', function() { - beforeEach(function(done) { - let v1Id - this.user = new User() - MockV1Api.setUser((v1Id = MockV1Api.nextV1Id()), (this.v1_user = {})) - return async.series( - [cb => this.user.login(cb), cb => this.user.setV1Id(v1Id, cb)], - error => { - return this.user.request( - { - method: 'POST', - url: '/user/subscription/v1/cancel' - }, - (error, response) => { - this.response = response - if (error != null) { - return done(error) - } - return done() - } - ) + let userHelper, v1Id + beforeEach(async function() { + v1Id = MockV1Api.nextV1Id() + console.log('v1Id=', v1Id) + userHelper = await UserHelper.createUser({ overleaf: { id: v1Id } }) + this.user = userHelper.user + MockV1Api.setUser(v1Id, (this.v1_user = {})) + + userHelper = await UserHelper.loginUser( + userHelper.getDefaultEmailPassword() + ) + this.response = await userHelper.request.post( + '/user/subscription/v1/cancel', + { + simple: false } ) }) it('should tell v1 to cancel the subscription', function() { - return expect(this.v1_user.canceled).to.equal(true) + expect(this.v1_user.canceled).to.equal(true) }) it('should redirect to the subscription dashboard', function() { expect(this.response.statusCode).to.equal(302) - return expect(this.response.headers.location).to.equal( - '/user/subscription' - ) + expect(this.response.headers.location).to.equal('/user/subscription') }) }) }) diff --git a/services/web/test/acceptance/src/helpers/UserHelper.js b/services/web/test/acceptance/src/helpers/UserHelper.js index 9c464bc097..fec20b797f 100644 --- a/services/web/test/acceptance/src/helpers/UserHelper.js +++ b/services/web/test/acceptance/src/helpers/UserHelper.js @@ -340,6 +340,7 @@ class UserHelper { .find({ use: 'email_confirmation', 'data.user_id': userId.toString(), + 'data.email': email, usedAt: { $exists: false } }) .next() diff --git a/services/web/test/acceptance/src/mocks/MockV1Api.js b/services/web/test/acceptance/src/mocks/MockV1Api.js index e5b87e3ab0..4dc56e2295 100644 --- a/services/web/test/acceptance/src/mocks/MockV1Api.js +++ b/services/web/test/acceptance/src/mocks/MockV1Api.js @@ -66,10 +66,12 @@ class MockV1Api extends AbstractMockApi { return id } - addInstitutionDomain(id, domain) { + addInstitutionDomain(institutionId, domain, options = {}) { if (this.allInstitutionDomains.has(domain)) return - if (!this.institutionDomains[id]) this.institutionDomains[id] = new Set() - this.institutionDomains[id].add(domain) + if (!this.institutionDomains[institutionId]) { + this.institutionDomains[institutionId] = {} + } + this.institutionDomains[institutionId][domain] = options this.allInstitutionDomains.add(domain) } @@ -77,9 +79,27 @@ class MockV1Api extends AbstractMockApi { Object.assign(this.institutions[id], options) } + updateInstitutionDomain(id, domain, options = {}) { + if (!this.institutionDomains[id] || !this.institutionDomains[id][domain]) + return + this.institutionDomains[id][domain] = Object.assign( + {}, + this.institutionDomains[id][domain], + options + ) + } + addAffiliation(userId, email) { - let institution + let institution = {} if (!email) return + if (!this.affiliations[userId]) this.affiliations[userId] = [] + + if ( + this.affiliations[userId].find(affiliationData => { + return affiliationData.email === email + }) + ) + return const domain = email.split('@').pop() @@ -88,23 +108,20 @@ class MockV1Api extends AbstractMockApi { } if (this.allInstitutionDomains.has(domain)) { - for (const [id, domainSet] of Object.entries(this.institutionDomains)) { - if (domainSet.has(domain)) { - institution = this.institutions[id] + for (const [institutionId, domainData] of Object.entries( + this.institutionDomains + )) { + if (domainData[domain]) { + institution.id = institutionId } } } - if (institution) { - if (!this.affiliations[userId]) this.affiliations[userId] = [] + if (institution.id) { this.affiliations[userId].push({ email, institution }) } } - setAffiliations(userId, affiliations) { - this.affiliations[userId] = affiliations - } - setDocExported(token, info) { this.doc_exported[token] = info } @@ -174,7 +191,34 @@ class MockV1Api extends AbstractMockApi { }) this.app.get('/api/v2/users/:userId/affiliations', (req, res) => { - res.json(this.affiliations[req.params.userId] || []) + if (!this.affiliations[req.params.userId]) return res.json([]) + const affiliations = this.affiliations[req.params.userId].map( + affiliation => { + const institutionId = affiliation.institution.id + const domain = affiliation.email.split('@').pop() + const domainData = + this.institutionDomains[institutionId][domain] || {} + const institutionData = this.institutions[institutionId] || {} + + affiliation.institution = { + id: institutionId, + name: institutionData.name, + commonsAccount: institutionData.commonsAccount, + isUniversity: !institutionData.institution, + ssoBeta: institutionData.sso_beta || false, + ssoEnabled: institutionData.sso_enabled || false, + maxConfirmationMonths: institutionData.maxConfirmationMonths || null + } + + affiliation.institution.confirmed = !!domainData.confirmed + + if (institutionData.commonsAccount) { + affiliation.licence = 'pro_plus' + } + return affiliation + } + ) + res.json(affiliations) }) this.app.post('/api/v2/users/:userId/affiliations', (req, res) => { diff --git a/services/web/test/unit/src/Subscription/FeaturesUpdaterTests.js b/services/web/test/unit/src/Subscription/FeaturesUpdaterTests.js index 2336eda827..cd1f8d134e 100644 --- a/services/web/test/unit/src/Subscription/FeaturesUpdaterTests.js +++ b/services/web/test/unit/src/Subscription/FeaturesUpdaterTests.js @@ -1,28 +1,14 @@ -/* eslint-disable - max-len, - no-return-assign, - 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 - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const SandboxedModule = require('sandboxed-module') -const should = require('chai').should() const { expect } = require('chai') const sinon = require('sinon') const modulePath = '../../../../app/src/Features/Subscription/FeaturesUpdater' -const { assert } = require('chai') const { ObjectId } = require('mongodb') describe('FeaturesUpdater', function() { beforeEach(function() { this.user_id = ObjectId().toString() - return (this.FeaturesUpdater = SandboxedModule.require(modulePath, { + this.FeaturesUpdater = SandboxedModule.require(modulePath, { globals: { console: console }, @@ -43,7 +29,7 @@ describe('FeaturesUpdater', function() { hooks: { fire: sinon.stub() } }) } - })) + }) }) describe('refreshFeatures', function() { @@ -72,51 +58,51 @@ describe('FeaturesUpdater', function() { .stub() .returns({ merged: 'features' }) this.UserGetter.getUser = sinon.stub().yields(null, this.user) - return (this.callback = sinon.stub()) + this.callback = sinon.stub() }) describe('normally', function() { beforeEach(function() { - return this.FeaturesUpdater.refreshFeatures(this.user_id, this.callback) + this.FeaturesUpdater.refreshFeatures(this.user_id, this.callback) }) it('should get the individual features', function() { - return this.FeaturesUpdater._getIndividualFeatures + this.FeaturesUpdater._getIndividualFeatures .calledWith(this.user_id) .should.equal(true) }) it('should get the group features', function() { - return this.FeaturesUpdater._getGroupFeatureSets + this.FeaturesUpdater._getGroupFeatureSets .calledWith(this.user_id) .should.equal(true) }) it('should get the institution features', function() { - return this.InstitutionsFeatures.getInstitutionsFeatures + this.InstitutionsFeatures.getInstitutionsFeatures .calledWith(this.user_id) .should.equal(true) }) it('should get the v1 features', function() { - return this.FeaturesUpdater._getV1Features + this.FeaturesUpdater._getV1Features .calledWith(this.user_id) .should.equal(true) }) it('should get the bonus features', function() { - return this.ReferalFeatures.getBonusFeatures + this.ReferalFeatures.getBonusFeatures .calledWith(this.user_id) .should.equal(true) }) it('should merge from the default features', function() { - return this.FeaturesUpdater._mergeFeatures + this.FeaturesUpdater._mergeFeatures .calledWith(this.Settings.defaultFeatures) .should.equal(true) }) it('should merge the individual features', function() { - return this.FeaturesUpdater._mergeFeatures + this.FeaturesUpdater._mergeFeatures .calledWith(sinon.match.any, { individual: 'features' }) .should.equal(true) }) @@ -125,31 +111,31 @@ describe('FeaturesUpdater', function() { this.FeaturesUpdater._mergeFeatures .calledWith(sinon.match.any, { group: 'features' }) .should.equal(true) - return this.FeaturesUpdater._mergeFeatures + this.FeaturesUpdater._mergeFeatures .calledWith(sinon.match.any, { group: 'features2' }) .should.equal(true) }) it('should merge the institutions features', function() { - return this.FeaturesUpdater._mergeFeatures + this.FeaturesUpdater._mergeFeatures .calledWith(sinon.match.any, { institutions: 'features' }) .should.equal(true) }) it('should merge the v1 features', function() { - return this.FeaturesUpdater._mergeFeatures + this.FeaturesUpdater._mergeFeatures .calledWith(sinon.match.any, { v1: 'features' }) .should.equal(true) }) it('should merge the bonus features', function() { - return this.FeaturesUpdater._mergeFeatures + this.FeaturesUpdater._mergeFeatures .calledWith(sinon.match.any, { bonus: 'features' }) .should.equal(true) }) it('should update the user with the merged features', function() { - return this.UserFeaturesUpdater.updateFeatures + this.UserFeaturesUpdater.updateFeatures .calledWith(this.user_id, { merged: 'features' }) .should.equal(true) }) @@ -164,7 +150,7 @@ describe('FeaturesUpdater', function() { this.FeaturesUpdater._mergeFeatures = sinon .stub() .returns({ dropbox: false }) - return this.FeaturesUpdater.refreshFeatures(this.user_id, this.callback) + this.FeaturesUpdater.refreshFeatures(this.user_id, this.callback) }) it('should fire module hook to unlink dropbox', function() { this.Modules.hooks.fire @@ -212,7 +198,7 @@ describe('FeaturesUpdater', function() { ).to.deep.equal({ compileGroup: 'priority' }) - return expect( + expect( this.FeaturesUpdater._mergeFeatures( { compileGroup: 'standard' @@ -251,7 +237,7 @@ describe('FeaturesUpdater', function() { ).to.deep.equal({ collaborators: -1 }) - return expect( + expect( this.FeaturesUpdater._mergeFeatures( { collaborators: 4 @@ -278,7 +264,7 @@ describe('FeaturesUpdater', function() { ).to.deep.equal({ compileTimeout: 20 }) - return expect( + expect( this.FeaturesUpdater._mergeFeatures( { compileTimeout: 10 @@ -329,7 +315,7 @@ describe('FeaturesUpdater', function() { ).to.deep.equal({ github: true }) - return expect( + expect( this.FeaturesUpdater._mergeFeatures( { github: false @@ -357,45 +343,45 @@ describe('FeaturesUpdater', function() { this.UserGetter.getUser = sinon.stub().callsArgWith(2, null, this.user) this.FeaturesUpdater.refreshFeatures = sinon.stub().yields(null) - return (this.call = cb => { - return this.FeaturesUpdater.doSyncFromV1(this.v1UserId, cb) - }) + this.call = cb => { + this.FeaturesUpdater.doSyncFromV1(this.v1UserId, cb) + } }) describe('when all goes well', function() { it('should call getUser', function(done) { - return this.call(() => { + this.call(() => { expect(this.UserGetter.getUser.callCount).to.equal(1) expect( this.UserGetter.getUser.calledWith({ 'overleaf.id': this.v1UserId }) ).to.equal(true) - return done() + done() }) }) it('should call refreshFeatures', function(done) { - return this.call(() => { + this.call(() => { expect(this.FeaturesUpdater.refreshFeatures.callCount).to.equal(1) expect( this.FeaturesUpdater.refreshFeatures.calledWith(this.user_id) ).to.equal(true) - return done() + done() }) }) it('should not produce an error', function(done) { - return this.call(err => { + this.call(err => { expect(err).to.not.exist - return done() + done() }) }) }) describe('when getUser produces an error', function() { beforeEach(function() { - return (this.UserGetter.getUser = sinon + this.UserGetter.getUser = sinon .stub() - .callsArgWith(2, new Error('woops'))) + .callsArgWith(2, new Error('woops')) }) it('should not call refreshFeatures', function() { @@ -403,31 +389,29 @@ describe('FeaturesUpdater', function() { }) it('should produce an error', function(done) { - return this.call(err => { + this.call(err => { expect(err).to.exist - return done() + done() }) }) }) describe('when getUser does not find a user', function() { beforeEach(function() { - return (this.UserGetter.getUser = sinon - .stub() - .callsArgWith(2, null, null)) + this.UserGetter.getUser = sinon.stub().callsArgWith(2, null, null) }) it('should not call refreshFeatures', function(done) { - return this.call(() => { + this.call(() => { expect(this.FeaturesUpdater.refreshFeatures.callCount).to.equal(0) - return done() + done() }) }) it('should not produce an error', function(done) { - return this.call(err => { + this.call(err => { expect(err).to.not.exist - return done() + done() }) }) })