mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #4035 from overleaf/jel-reconfirmation-dropbox-notification
Notification for Dropbox unlinked due to reconfirmation lapse GitOrigin-RevId: 03d2bed922e1d3dd993f9227b8e7675af42eda4b
This commit is contained in:
parent
1b5d5bfb5b
commit
9b29fa7cbc
13 changed files with 95 additions and 286 deletions
|
@ -1,89 +0,0 @@
|
||||||
const _ = require('lodash')
|
|
||||||
const { ObjectId, waitForDb } = require('../../infrastructure/mongodb')
|
|
||||||
const async = require('async')
|
|
||||||
const logger = require('logger-sharelatex')
|
|
||||||
const FeaturesUpdater = require('../Subscription/FeaturesUpdater')
|
|
||||||
const InstitutionsAPI = require('./InstitutionsAPI')
|
|
||||||
|
|
||||||
const ASYNC_LIMIT = 10
|
|
||||||
|
|
||||||
const processLapsedLogger = {
|
|
||||||
refreshedUsers: [],
|
|
||||||
failedToRefresh: [],
|
|
||||||
printSummary: () => {
|
|
||||||
logger.log(
|
|
||||||
`Reconfirmations lapsed processed. ${processLapsedLogger.refreshedUsers.length} successfull and ${processLapsedLogger.failedToRefresh.length} failed.`,
|
|
||||||
{
|
|
||||||
refreshedUsers: processLapsedLogger.refreshedUsers,
|
|
||||||
failedToRefresh: processLapsedLogger.failedToRefresh,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
function _validateUserIdList(userIds) {
|
|
||||||
if (!Array.isArray(userIds)) throw new Error('users is not an array')
|
|
||||||
|
|
||||||
userIds.forEach(userId => {
|
|
||||||
if (!ObjectId.isValid(userId)) throw new Error('user ID not valid')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function _refreshUser(userId, callback) {
|
|
||||||
FeaturesUpdater.refreshFeatures(userId, 'reconfirmation-lapsed', error => {
|
|
||||||
if (error) {
|
|
||||||
logger.warn(`Failed to refresh features for ${userId}`, error)
|
|
||||||
processLapsedLogger.failedToRefresh.push(userId)
|
|
||||||
} else {
|
|
||||||
processLapsedLogger.refreshedUsers.push(userId)
|
|
||||||
}
|
|
||||||
return callback()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function _loopRefreshUsers(userIds) {
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
async.eachLimit(userIds, ASYNC_LIMIT, _refreshUser, error => {
|
|
||||||
if (error) return reject(error)
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processLapsed() {
|
|
||||||
logger.log('Begin processing lapsed reconfirmations')
|
|
||||||
await waitForDb()
|
|
||||||
|
|
||||||
const result = await InstitutionsAPI.promises.getUsersNeedingReconfirmationsLapsedProcessed()
|
|
||||||
const userIds = _.get(result, ['data', 'users'])
|
|
||||||
|
|
||||||
_validateUserIdList(userIds)
|
|
||||||
logger.log(
|
|
||||||
`Starting to process ${userIds.length} users with lapsed reconfirmations`
|
|
||||||
)
|
|
||||||
await _loopRefreshUsers(userIds)
|
|
||||||
|
|
||||||
processLapsedLogger.printSummary()
|
|
||||||
|
|
||||||
try {
|
|
||||||
logger.log('Updating reconfirmations lapsed processed dates')
|
|
||||||
await InstitutionsAPI.promises.sendUsersWithReconfirmationsLapsedProcessed(
|
|
||||||
processLapsedLogger.refreshedUsers
|
|
||||||
)
|
|
||||||
} catch (error) {
|
|
||||||
logger.log('Error updating features_refreshed_at', error)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.log('Done processing lapsed reconfirmations')
|
|
||||||
|
|
||||||
return {
|
|
||||||
refreshedUsers: processLapsedLogger.refreshedUsers,
|
|
||||||
failedToRefresh: processLapsedLogger.failedToRefresh,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const InstitutionsReconfirmationHandler = {
|
|
||||||
processLapsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = InstitutionsReconfirmationHandler
|
|
|
@ -29,6 +29,26 @@ function dropboxDuplicateProjectNames(userId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dropboxUnlinkedDueToLapsedReconfirmation(userId) {
|
||||||
|
return {
|
||||||
|
key: 'drobox-unlinked-due-to-lapsed-reconfirmation',
|
||||||
|
create(callback) {
|
||||||
|
NotificationsHandler.createNotification(
|
||||||
|
userId,
|
||||||
|
this.key,
|
||||||
|
'notification_dropbox_unlinked_due_to_lapsed_reconfirmation',
|
||||||
|
{},
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
},
|
||||||
|
read(callback) {
|
||||||
|
NotificationsHandler.markAsReadWithKey(userId, this.key, callback)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function featuresUpgradedByAffiliation(affiliation, user) {
|
function featuresUpgradedByAffiliation(affiliation, user) {
|
||||||
return {
|
return {
|
||||||
key: `features-updated-by=${affiliation.institutionId}`,
|
key: `features-updated-by=${affiliation.institutionId}`,
|
||||||
|
@ -206,6 +226,7 @@ function tpdsFileLimit(userId) {
|
||||||
|
|
||||||
const NotificationsBuilder = {
|
const NotificationsBuilder = {
|
||||||
// Note: notification keys should be url-safe
|
// Note: notification keys should be url-safe
|
||||||
|
dropboxUnlinkedDueToLapsedReconfirmation,
|
||||||
dropboxDuplicateProjectNames,
|
dropboxDuplicateProjectNames,
|
||||||
featuresUpgradedByAffiliation,
|
featuresUpgradedByAffiliation,
|
||||||
redundantPersonalSubscription,
|
redundantPersonalSubscription,
|
||||||
|
@ -215,6 +236,9 @@ const NotificationsBuilder = {
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationsBuilder.promises = {
|
NotificationsBuilder.promises = {
|
||||||
|
dropboxUnlinkedDueToLapsedReconfirmation: function (userId) {
|
||||||
|
return promisifyAll(dropboxUnlinkedDueToLapsedReconfirmation(userId))
|
||||||
|
},
|
||||||
redundantPersonalSubscription: function (affiliation, user) {
|
redundantPersonalSubscription: function (affiliation, user) {
|
||||||
return promisifyAll(redundantPersonalSubscription(affiliation, user))
|
return promisifyAll(redundantPersonalSubscription(affiliation, user))
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,13 +33,16 @@ const FeaturesUpdater = {
|
||||||
if (oldFeatures.dropbox === true && features.dropbox === false) {
|
if (oldFeatures.dropbox === true && features.dropbox === false) {
|
||||||
logger.log({ userId }, '[FeaturesUpdater] must unlink dropbox')
|
logger.log({ userId }, '[FeaturesUpdater] must unlink dropbox')
|
||||||
const Modules = require('../../infrastructure/Modules')
|
const Modules = require('../../infrastructure/Modules')
|
||||||
Modules.hooks.fire('removeDropbox', userId, err => {
|
Modules.hooks.fire('removeDropbox', userId, reason, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
return callback(null, newFeatures, featuresChanged)
|
return callback(null, newFeatures, featuresChanged)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return callback(null, newFeatures, featuresChanged)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -142,7 +142,7 @@ function getOrCreateProject(userId, projectName, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDuplicateProjects(userId, projectName, callback) {
|
function handleDuplicateProjects(userId, projectName, callback) {
|
||||||
Modules.hooks.fire('removeDropbox', userId, err => {
|
Modules.hooks.fire('removeDropbox', userId, 'duplicate-projects', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err)
|
return callback(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ mixin reconfirmAffiliationNotification(location)
|
||||||
| !{translate("please_reconfirm_institutional_email", {}, [{name: 'a', attrs: {href: '/user/settings?remove={{userEmail.email}}' }}])}
|
| !{translate("please_reconfirm_institutional_email", {}, [{name: 'a', attrs: {href: '/user/settings?remove={{userEmail.email}}' }}])}
|
||||||
|
|
||||||
|
|
|
|
||||||
a(href="/learn/how-to/Reconfirm_an_institutional_email_address") #{translate("learn_more")}
|
a(href="/learn/how-to/Institutional_Email_Reconfirmation") #{translate("learn_more")}
|
||||||
|
|
||||||
div(ng-if="reconfirm[userEmail.email].reconfirmationSent")
|
div(ng-if="reconfirm[userEmail.email].reconfirmationSent")
|
||||||
| !{translate("please_check_your_inbox_to_confirm", {institutionName: '{{userEmail.affiliation.institution.name}}'}, ['strong'])}
|
| !{translate("please_check_your_inbox_to_confirm", {institutionName: '{{userEmail.affiliation.institution.name}}'}, ['strong'])}
|
||||||
|
|
|
@ -114,6 +114,17 @@ include ../../_mixins/reconfirm_affiliation
|
||||||
span(aria-hidden="true") ×
|
span(aria-hidden="true") ×
|
||||||
span.sr-only #{translate("close")}
|
span.sr-only #{translate("close")}
|
||||||
|
|
||||||
|
.alert.alert-info(
|
||||||
|
ng-switch-when="notification_dropbox_unlinked_due_to_lapsed_reconfirmation"
|
||||||
|
)
|
||||||
|
.notification-body
|
||||||
|
| !{translate("dropbox_unlinked_premium_feature", {}, ['strong'])}
|
||||||
|
|
|
||||||
|
a(href="/learn/how-to/Institutional_Email_Reconfirmation") #{translate("learn_more")}
|
||||||
|
.notification-close
|
||||||
|
button.btn-sm(ng-click="dismiss(notification)").close.pull-right
|
||||||
|
span(aria-hidden="true") ×
|
||||||
|
span.sr-only #{translate("close")}
|
||||||
.alert.alert-info(
|
.alert.alert-info(
|
||||||
ng-switch-default
|
ng-switch-default
|
||||||
)
|
)
|
||||||
|
|
|
@ -1445,5 +1445,6 @@
|
||||||
"no_symbols_found": "No symbols found",
|
"no_symbols_found": "No symbols found",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
"also": "Also",
|
"also": "Also",
|
||||||
"add_email": "Add Email"
|
"add_email": "Add Email",
|
||||||
|
"dropbox_unlinked_premium_feature": "<0>Your Dropbox account has been unlinked</0> because Dropbox Sync is a premium feature that you had through an institutional license. Please confirm you are still at the institution or upgrade your account in order to relink your Dropbox account."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const InstitutionsReconfirmationHandler = require('../app/src/Features/Institutions/InstitutionsReconfirmationHandler')
|
const InstitutionsReconfirmationHandler = require('../modules/overleaf-integration/app/src/Institutions/InstitutionsReconfirmationHandler')
|
||||||
|
|
||||||
InstitutionsReconfirmationHandler.processLapsed()
|
InstitutionsReconfirmationHandler.processLapsed()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
const { expect } = require('chai')
|
|
||||||
const Settings = require('settings-sharelatex')
|
|
||||||
const UserHelper = require('./helpers/UserHelper')
|
|
||||||
const MockV1ApiClass = require('./mocks/MockV1Api')
|
|
||||||
|
|
||||||
const InstitutionsReconfirmationHandler = require('../../../app/src/Features/Institutions/InstitutionsReconfirmationHandler')
|
|
||||||
|
|
||||||
let MockV1Api
|
|
||||||
let userHelper = new UserHelper()
|
|
||||||
|
|
||||||
before(function () {
|
|
||||||
MockV1Api = MockV1ApiClass.instance()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('InstitutionsReconfirmationHandler', function () {
|
|
||||||
const institutionUsers = []
|
|
||||||
let result
|
|
||||||
|
|
||||||
beforeEach(async function () {
|
|
||||||
// create institution
|
|
||||||
const domain = 'institution-1.com'
|
|
||||||
const maxConfirmationMonths = 6
|
|
||||||
MockV1Api.createInstitution({
|
|
||||||
commonsAccount: true,
|
|
||||||
confirmed: true,
|
|
||||||
hostname: domain,
|
|
||||||
maxConfirmationMonths,
|
|
||||||
})
|
|
||||||
|
|
||||||
// create users affiliated with institution
|
|
||||||
async function _createInstitutionUserPastReconfirmation() {
|
|
||||||
userHelper = await UserHelper.createUser()
|
|
||||||
const userId = userHelper.user._id
|
|
||||||
|
|
||||||
// add the affiliation
|
|
||||||
userHelper = await UserHelper.loginUser(
|
|
||||||
userHelper.getDefaultEmailPassword()
|
|
||||||
)
|
|
||||||
const institutionEmail = `${userId}@${domain}`
|
|
||||||
await userHelper.addEmailAndConfirm(userId, institutionEmail)
|
|
||||||
institutionUsers.push(userId)
|
|
||||||
|
|
||||||
// backdate confirmation
|
|
||||||
await userHelper.changeConfirmedToPastReconfirmation(
|
|
||||||
userId,
|
|
||||||
institutionEmail,
|
|
||||||
maxConfirmationMonths
|
|
||||||
)
|
|
||||||
|
|
||||||
// verify user has features before script run
|
|
||||||
const result = await UserHelper.getUser(
|
|
||||||
{ _id: userHelper.user._id },
|
|
||||||
{ features: 1 }
|
|
||||||
)
|
|
||||||
expect(result.user.features).to.deep.equal(Settings.features.professional)
|
|
||||||
|
|
||||||
return userId
|
|
||||||
}
|
|
||||||
await _createInstitutionUserPastReconfirmation()
|
|
||||||
await _createInstitutionUserPastReconfirmation()
|
|
||||||
await _createInstitutionUserPastReconfirmation()
|
|
||||||
|
|
||||||
result = await InstitutionsReconfirmationHandler.processLapsed()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should refresh features', async function () {
|
|
||||||
expect(result.failedToRefresh.length).to.equal(0)
|
|
||||||
expect(result.refreshedUsers.length).to.equal(3)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,108 +0,0 @@
|
||||||
const SandboxedModule = require('sandboxed-module')
|
|
||||||
const path = require('path')
|
|
||||||
const sinon = require('sinon')
|
|
||||||
const { ObjectId } = require('mongodb')
|
|
||||||
const { expect } = require('chai')
|
|
||||||
const modulePath = path.join(
|
|
||||||
__dirname,
|
|
||||||
'../../../../app/src/Features/Institutions/InstitutionsReconfirmationHandler'
|
|
||||||
)
|
|
||||||
|
|
||||||
describe('InstitutionsReconfirmationHandler', function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
this.InstitutionsReconfirmationHandler = SandboxedModule.require(
|
|
||||||
modulePath,
|
|
||||||
{
|
|
||||||
requires: {
|
|
||||||
'../../infrastructure/mongodb': (this.mongodb = {
|
|
||||||
ObjectId,
|
|
||||||
waitForDb: sinon.stub().resolves(),
|
|
||||||
}),
|
|
||||||
'../Subscription/FeaturesUpdater': (this.FeaturesUpdater = {
|
|
||||||
refreshFeatures: sinon.stub(),
|
|
||||||
}),
|
|
||||||
'./InstitutionsAPI': (this.InstitutionsAPI = {
|
|
||||||
promises: {
|
|
||||||
getUsersNeedingReconfirmationsLapsedProcessed: sinon.stub(),
|
|
||||||
sendUsersWithReconfirmationsLapsedProcessed: sinon.stub(),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('userId list', function () {
|
|
||||||
it('should throw an error if IDs not an array', async function () {
|
|
||||||
let error
|
|
||||||
try {
|
|
||||||
await this.InstitutionsReconfirmationHandler.processLapsed()
|
|
||||||
} catch (e) {
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
expect(error).to.exist
|
|
||||||
expect(error.message).to.equal('users is not an array')
|
|
||||||
})
|
|
||||||
it('should throw an error if IDs not valid ObjectIds', async function () {
|
|
||||||
this.InstitutionsAPI.promises.getUsersNeedingReconfirmationsLapsedProcessed.resolves(
|
|
||||||
{
|
|
||||||
data: { users: ['not an objectid'] },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
let error
|
|
||||||
try {
|
|
||||||
await this.InstitutionsReconfirmationHandler.processLapsed()
|
|
||||||
} catch (e) {
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
expect(error).to.exist
|
|
||||||
expect(error.message).to.equal('user ID not valid')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should log users that have refreshFeatures errors', async function () {
|
|
||||||
const anError = new Error('oops')
|
|
||||||
const aUserId = '5efb8b6e9b647b0027e4c0b0'
|
|
||||||
this.FeaturesUpdater.refreshFeatures.yields(anError)
|
|
||||||
this.InstitutionsAPI.promises.getUsersNeedingReconfirmationsLapsedProcessed.resolves(
|
|
||||||
{
|
|
||||||
data: { users: [aUserId] },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
this.InstitutionsAPI.promises.sendUsersWithReconfirmationsLapsedProcessed.resolves()
|
|
||||||
let error, result
|
|
||||||
try {
|
|
||||||
result = await this.InstitutionsReconfirmationHandler.processLapsed()
|
|
||||||
} catch (e) {
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
expect(error).to.not.exist
|
|
||||||
expect(result.failedToRefresh.length).to.equal(1)
|
|
||||||
expect(result.failedToRefresh[0]).to.equal(aUserId)
|
|
||||||
expect(result.refreshedUsers.length).to.equal(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should log but not return errors from sendUsersWithReconfirmationsLapsedProcessed', async function () {
|
|
||||||
const anError = new Error('oops')
|
|
||||||
const aUserId = '5efb8b6e9b647b0027e4c0b0'
|
|
||||||
this.FeaturesUpdater.refreshFeatures.yields()
|
|
||||||
this.InstitutionsAPI.promises.getUsersNeedingReconfirmationsLapsedProcessed.resolves(
|
|
||||||
{
|
|
||||||
data: { users: [aUserId] },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
this.InstitutionsAPI.promises.sendUsersWithReconfirmationsLapsedProcessed.rejects(
|
|
||||||
anError
|
|
||||||
)
|
|
||||||
let error, result
|
|
||||||
try {
|
|
||||||
result = await this.InstitutionsReconfirmationHandler.processLapsed()
|
|
||||||
} catch (e) {
|
|
||||||
error = e
|
|
||||||
}
|
|
||||||
expect(error).to.not.exist
|
|
||||||
expect(result.refreshedUsers.length).to.equal(1)
|
|
||||||
expect(result.refreshedUsers[0]).to.equal(aUserId)
|
|
||||||
expect(result.failedToRefresh.length).to.equal(0)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,4 +1,5 @@
|
||||||
const SandboxedModule = require('sandboxed-module')
|
const SandboxedModule = require('sandboxed-module')
|
||||||
|
const { expect } = require('chai')
|
||||||
const sinon = require('sinon')
|
const sinon = require('sinon')
|
||||||
const modulePath = require('path').join(
|
const modulePath = require('path').join(
|
||||||
__dirname,
|
__dirname,
|
||||||
|
@ -8,7 +9,6 @@ const modulePath = require('path').join(
|
||||||
describe('NotificationsBuilder', function () {
|
describe('NotificationsBuilder', function () {
|
||||||
const userId = '123nd3ijdks'
|
const userId = '123nd3ijdks'
|
||||||
|
|
||||||
describe('ipMatcherAffiliation', function () {
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.handler = { createNotification: sinon.stub().callsArgWith(6) }
|
this.handler = { createNotification: sinon.stub().callsArgWith(6) }
|
||||||
this.settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } }
|
this.settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } }
|
||||||
|
@ -22,6 +22,42 @@ describe('NotificationsBuilder', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('dropboxUnlinkedDueToLapsedReconfirmation', function (done) {
|
||||||
|
it('should create the notification', function (done) {
|
||||||
|
this.controller
|
||||||
|
.dropboxUnlinkedDueToLapsedReconfirmation(userId)
|
||||||
|
.create(error => {
|
||||||
|
expect(error).to.not.exist
|
||||||
|
expect(this.handler.createNotification).to.have.been.calledWith(
|
||||||
|
userId,
|
||||||
|
'drobox-unlinked-due-to-lapsed-reconfirmation',
|
||||||
|
'notification_dropbox_unlinked_due_to_lapsed_reconfirmation',
|
||||||
|
{},
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('NotificationsHandler error', function () {
|
||||||
|
let anError
|
||||||
|
beforeEach(function () {
|
||||||
|
anError = new Error('oops')
|
||||||
|
this.handler.createNotification.yields(anError)
|
||||||
|
})
|
||||||
|
it('should return errors from NotificationsHandler', function (done) {
|
||||||
|
this.controller
|
||||||
|
.dropboxUnlinkedDueToLapsedReconfirmation(userId)
|
||||||
|
.create(error => {
|
||||||
|
expect(error).to.exist
|
||||||
|
expect(error).to.deep.equal(anError)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('ipMatcherAffiliation', function () {
|
||||||
describe('with portal and with SSO', function () {
|
describe('with portal and with SSO', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.body = {
|
this.body = {
|
||||||
|
|
|
@ -170,7 +170,7 @@ describe('FeaturesUpdater', function () {
|
||||||
})
|
})
|
||||||
it('should fire module hook to unlink dropbox', function () {
|
it('should fire module hook to unlink dropbox', function () {
|
||||||
this.Modules.hooks.fire
|
this.Modules.hooks.fire
|
||||||
.calledWith('removeDropbox', this.user._id)
|
.calledWith('removeDropbox', this.user._id, 'test')
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -376,7 +376,8 @@ function expectDropboxUnlinked() {
|
||||||
it('unlinks Dropbox', function () {
|
it('unlinks Dropbox', function () {
|
||||||
expect(this.Modules.hooks.fire).to.have.been.calledWith(
|
expect(this.Modules.hooks.fire).to.have.been.calledWith(
|
||||||
'removeDropbox',
|
'removeDropbox',
|
||||||
this.userId
|
this.userId,
|
||||||
|
'duplicate-projects'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue