Merge pull request #2609 from overleaf/ta-cmg-recurly-email-update

Add UI to Update Recurly Email

GitOrigin-RevId: 920a741fd9b4312f031bdd40e3d6bec48f1bd579
This commit is contained in:
Timothée Alby
2020-02-21 04:21:12 +00:00
committed by Copybot
parent 506543d6a0
commit f5e2983a6b
12 changed files with 213 additions and 6 deletions
@@ -0,0 +1,43 @@
const { expect } = require('chai')
const async = require('async')
const User = require('./helpers/User')
const RecurlySubscription = require('./helpers/RecurlySubscription')
require('./helpers/MockV1Api')
describe('Subscriptions', function() {
describe('update', function() {
beforeEach(function(done) {
this.recurlyUser = new User()
async.series(
[
cb => this.recurlyUser.ensureUserExists(cb),
cb => {
this.recurlySubscription = new RecurlySubscription({
adminId: this.recurlyUser._id,
account: {
email: 'stale-recurly@email.com'
}
})
this.recurlySubscription.ensureExists(cb)
},
cb => this.recurlyUser.login(cb)
],
done
)
})
it('updates the email address for the account', function(done) {
let url = '/user/subscription/account/email'
this.recurlyUser.request.post({ url }, (error, { statusCode }) => {
if (error) {
return done(error)
}
expect(statusCode).to.equal(200)
// the actual email update is not tested as the mocked Recurly API
// doesn't handle it
done()
})
})
})
})
@@ -62,7 +62,8 @@ describe('Subscriptions', function() {
describe('when the user has a subscription with recurly', function() {
beforeEach(function(done) {
MockRecurlyApi.accounts['mock-account-id'] = this.accounts = {
hosted_login_token: 'mock-login-token'
hosted_login_token: 'mock-login-token',
email: 'mock@email.com'
}
MockRecurlyApi.subscriptions[
'mock-subscription-id'
@@ -138,7 +139,12 @@ describe('Subscriptions', function() {
tax: 100,
taxRate: 0.2,
trial_ends_at: new Date(2018, 6, 7),
trialEndsAtFormatted: '7th July 2018'
trialEndsAtFormatted: '7th July 2018',
account: {
account_code: 'mock-account-id',
email: 'mock@email.com',
hosted_login_token: 'mock-login-token'
}
})
})
@@ -176,6 +182,12 @@ describe('Subscriptions', function() {
}
)
})
it('should return Recurly account email', function() {
expect(this.data.personalSubscription.recurly.account.email).to.equal(
'mock@email.com'
)
})
})
describe('when the user has a subscription without recurly', function() {
@@ -14,6 +14,7 @@ let MockRecurlyApi
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const SubscriptionController = require('../../../../app/src/Features/Subscription/SubscriptionController')
app.use(bodyParser.json())
@@ -69,11 +70,30 @@ module.exports = MockRecurlyApi = {
<account>
<account_code>${req.params.id}</account_code>
<hosted_login_token>${account.hosted_login_token}</hosted_login_token>
<email>${account.email}</email>
</account>\
`)
}
})
app.put(
'/accounts/:id',
SubscriptionController.recurlyNotificationParser, // required to parse XML requests
(req, res, next) => {
const account = this.accounts[req.params.id]
if (account == null) {
return res.status(404).end()
} else {
return res.send(`\
<account>
<account_code>${req.params.id}</account_code>
<email>${account.email}</email>
</account>\
`)
}
}
)
app.get('/coupons/:code', (req, res, next) => {
const coupon = this.coupons[req.params.code]
if (coupon == null) {
@@ -10,6 +10,9 @@ class RecurlySubscription {
this.uuid = ObjectId().toString()
this.accountId = this.subscription.admin_id.toString()
this.state = options.state || 'active'
this.account = {
email: options.account && options.account.email
}
}
ensureExists(callback) {
@@ -22,7 +25,10 @@ class RecurlySubscription {
account_id: this.accountId,
state: this.state
})
MockRecurlyApi.addAccount({ id: this.accountId })
MockRecurlyApi.addAccount({
id: this.accountId,
email: this.account.email
})
callback()
})
}
@@ -257,6 +257,49 @@ describe('RecurlyWrapper', function() {
})
})
describe('updateAccountEmailAddress', function() {
beforeEach(function(done) {
this.recurlyAccountId = 'account-id-123'
this.newEmail = 'example@overleaf.com'
this.apiRequest = sinon
.stub(this.RecurlyWrapper, 'apiRequest')
.callsFake((options, callback) => {
this.requestOptions = options
callback(null, {}, fixtures['accounts/104'])
})
this.RecurlyWrapper.updateAccountEmailAddress(
this.recurlyAccountId,
this.newEmail,
(error, recurlyAccount) => {
this.recurlyAccount = recurlyAccount
done()
}
)
})
afterEach(function() {
return this.RecurlyWrapper.apiRequest.restore()
})
it('sends correct XML', function() {
this.apiRequest.called.should.equal(true)
const { body } = this.apiRequest.lastCall.args[0]
expect(body).to.equal(`\
<account>
<email>example@overleaf.com</email>
</account>\
`)
this.requestOptions.url.should.equal(`accounts/${this.recurlyAccountId}`)
this.requestOptions.method.should.equal('PUT')
})
it('should return the updated account', function() {
should.exist(this.recurlyAccount)
this.recurlyAccount.account_code.should.equal('104')
})
})
describe('updateSubscription', function() {
beforeEach(function(done) {
this.recurlySubscriptionId = 'subscription-id-123'
@@ -115,7 +115,9 @@ describe('SubscriptionController', function() {
},
'settings-sharelatex': this.settings,
'../User/UserGetter': this.UserGetter,
'./RecurlyWrapper': (this.RecurlyWrapper = {}),
'./RecurlyWrapper': (this.RecurlyWrapper = {
updateAccountEmailAddress: sinon.stub().yields()
}),
'./FeaturesUpdater': (this.FeaturesUpdater = {}),
'./GroupPlansData': (this.GroupPlansData = {}),
'./V1SubscriptionManager': (this.V1SubscriptionManager = {}),
@@ -477,6 +479,28 @@ describe('SubscriptionController', function() {
})
})
describe('updateAccountEmailAddress via put', function() {
beforeEach(function(done) {
this.res = {
sendStatus() {
return done()
}
}
sinon.spy(this.res, 'sendStatus')
this.SubscriptionController.updateAccountEmailAddress(this.req, this.res)
})
it('should send the user and subscriptionId to RecurlyWrapper', function() {
this.RecurlyWrapper.updateAccountEmailAddress
.calledWith(this.user._id, this.user.email)
.should.equal(true)
})
it('shouldrespond with 200', function() {
this.res.sendStatus.calledWith(200).should.equal(true)
})
})
describe('reactivateSubscription', function() {
beforeEach(function(done) {
this.res = {