Move checkout to subscriptions module (#15022)

* Move checkout to subscriptions module

GitOrigin-RevId: 0ad6587ddd7042aed7f2e18d9d0668e02942eb1e
This commit is contained in:
Thomas
2023-10-03 08:04:31 +00:00
committed by Copybot
parent e5b09df542
commit 843b4f3940
58 changed files with 3 additions and 4594 deletions
@@ -5,7 +5,6 @@ const MockRequest = require('../helpers/MockRequest')
const MockResponse = require('../helpers/MockResponse')
const modulePath =
'../../../../app/src/Features/Subscription/SubscriptionController'
const Errors = require('../../../../app/src/Features/Errors/Errors')
const SubscriptionErrors = require('../../../../app/src/Features/Subscription/Errors')
const mockSubscriptions = {
@@ -85,7 +84,6 @@ describe('SubscriptionController', function () {
.returns({ plans: [], planCodesChangingAtTermEnd: [] }),
}
this.settings = {
restrictedCountries: ['KP'],
coupon_codes: {
upgradeToAnnualPromo: {
student: 'STUDENTCODEHERE',
@@ -305,154 +303,6 @@ describe('SubscriptionController', function () {
})
})
describe('paymentPage', function () {
beforeEach(function () {
this.req.headers = {}
this.SubscriptionHandler.promises.validateNoSubscriptionInRecurly = sinon
.stub()
.resolves(true)
this.GeoIpLookup.promises.getCurrencyCode.resolves({
currencyCode: this.stubbedCurrencyCode,
})
})
describe('with a user without a subscription', function () {
beforeEach(function () {
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(
false
)
this.PlansLocator.findLocalPlanInSettings.returns({})
})
describe('with a valid plan code', function () {
it('should render the new subscription page', function (done) {
this.res.render = (page, opts) => {
page.should.equal('subscriptions/new-react')
done()
}
this.SubscriptionController.paymentPage(this.req, this.res, done)
})
})
})
describe('with a user with subscription', function () {
it('should redirect to the subscription dashboard', function (done) {
this.PlansLocator.findLocalPlanInSettings.returns({})
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(
true
)
this.res.redirect = url => {
url.should.equal('/user/subscription?hasSubscription=true')
done()
}
this.SubscriptionController.paymentPage(this.req, this.res)
})
})
describe('with an invalid plan code', function () {
it('should return 422 error - Unprocessable Entity', function (done) {
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(
false
)
this.PlansLocator.findLocalPlanInSettings.returns(null)
this.HttpErrorHandler.unprocessableEntity = sinon.spy(
(req, res, message) => {
expect(req).to.exist
expect(res).to.exist
expect(message).to.deep.equal('Plan not found')
done()
}
)
this.SubscriptionController.paymentPage(this.req, this.res)
})
})
describe('which currency to use', function () {
beforeEach(function () {
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(
false
)
this.PlansLocator.findLocalPlanInSettings.returns({})
})
it('should use the set currency from the query string', function (done) {
this.req.query.currency = 'EUR'
this.res.render = (page, opts) => {
opts.currency.should.equal('EUR')
opts.currency.should.not.equal(this.stubbedCurrencyCode)
done()
}
this.SubscriptionController.paymentPage(this.req, this.res)
})
it('should upercase the currency code', function (done) {
this.req.query.currency = 'eur'
this.res.render = (page, opts) => {
opts.currency.should.equal('EUR')
done()
}
this.SubscriptionController.paymentPage(this.req, this.res)
})
it('should use the geo ip currency if non is provided', function (done) {
this.req.query.currency = null
this.res.render = (page, opts) => {
opts.currency.should.equal(this.stubbedCurrencyCode)
done()
}
this.SubscriptionController.paymentPage(this.req, this.res)
})
it('should use the geo ip currency if not valid', function (done) {
this.req.query.currency = 'WAT'
this.GeoIpLookup.isValidCurrencyParam.returns(false)
this.res.render = (page, opts) => {
opts.currency.should.equal(this.stubbedCurrencyCode)
done()
}
this.SubscriptionController.paymentPage(this.req, this.res)
})
})
describe('with a recurly subscription already', function () {
it('should redirect to the subscription dashboard', function (done) {
this.PlansLocator.findLocalPlanInSettings.returns({})
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(
false
)
this.SubscriptionHandler.promises.validateNoSubscriptionInRecurly.resolves(
false
)
this.res.redirect = url => {
url.should.equal('/user/subscription?hasSubscription=true')
done()
}
this.SubscriptionController.paymentPage(this.req, this.res)
})
})
describe('with a user from a restricted country', function () {
beforeEach(function () {
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(
false
)
this.PlansLocator.findLocalPlanInSettings.returns({})
this.GeoIpLookup.promises.getCurrencyCode.resolves({
currencyCode: this.stubbedCurrencyCode,
countryCode: 'KP',
})
})
it('should render the restricted country page', function (done) {
this.res.render = (page, opts) => {
page.should.equal('subscriptions/restricted-country')
done()
}
this.SubscriptionController.paymentPage(this.req, this.res, done)
})
})
})
describe('successfulSubscription', function () {
it('without a personal subscription', function (done) {
this.SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel.resolves(
@@ -550,128 +400,6 @@ describe('SubscriptionController', function () {
})
})
describe('createSubscription', function () {
beforeEach(function (done) {
this.res = {
sendStatus() {
done()
},
}
sinon.spy(this.res, 'sendStatus')
this.subscriptionDetails = {
card: '1234',
cvv: '123',
}
this.recurlyTokenIds = {
billing: '1234',
threeDSecureActionResult: '5678',
}
this.req.body.recurly_token_id = this.recurlyTokenIds.billing
this.req.body.recurly_three_d_secure_action_result_token_id =
this.recurlyTokenIds.threeDSecureActionResult
this.req.body.subscriptionDetails = this.subscriptionDetails
this.LimitationsManager.userHasV1OrV2Subscription.yields(null, false)
this.SubscriptionController.createSubscription(this.req, this.res)
})
it('should send the user and subscriptionId to the handler', function (done) {
this.SubscriptionHandler.promises.createSubscription
.calledWithMatch(
this.user,
this.subscriptionDetails,
this.recurlyTokenIds
)
.should.equal(true)
done()
})
it('should redirect to the subscription page', function (done) {
this.res.sendStatus.calledWith(201).should.equal(true)
done()
})
})
describe('createSubscription with errors', function () {
it('should handle users with subscription', function (done) {
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(true)
this.SubscriptionController.createSubscription(this.req, {
sendStatus: status => {
expect(status).to.equal(409)
this.SubscriptionHandler.promises.createSubscription.called.should.equal(
false
)
done()
},
})
})
it('should handle restricted country', function (done) {
this.GeoIpLookup.promises.getCurrencyCode.resolves({
countryCode: 'KP',
})
this.res.callback = () => {
expect(this.res.statusCode).to.equal(422)
expect(this.res.body).to.include(
'sorry_detected_sales_restricted_region'
)
this.SubscriptionHandler.promises.createSubscription.called.should.equal(
false
)
done()
}
this.SubscriptionController.createSubscription(this.req, this.res)
})
it('should handle 3DSecure errors/recurly transaction errors', function (done) {
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(false)
this.SubscriptionHandler.promises.createSubscription.rejects(
new SubscriptionErrors.RecurlyTransactionError({})
)
this.HttpErrorHandler.unprocessableEntity = sinon.spy(
(req, res, message) => {
expect(req).to.exist
expect(res).to.exist
expect(message).to.deep.equal('Unknown transaction error')
done()
}
)
this.SubscriptionController.createSubscription(this.req, this.res)
})
it('should handle validation errors', function (done) {
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(false)
this.SubscriptionHandler.promises.createSubscription.rejects(
new Errors.InvalidError('invalid error test')
)
this.HttpErrorHandler.unprocessableEntity = sinon.spy(
(req, res, message) => {
expect(req).to.exist
expect(res).to.exist
expect(message).to.deep.equal('invalid error test')
done()
}
)
this.SubscriptionController.createSubscription(this.req, this.res)
})
it('should throw errors from createSubscription that are not handled', function (done) {
const genericError = new Error('generic error')
this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(false)
this.SubscriptionHandler.promises.createSubscription.rejects(genericError)
this.SubscriptionController.createSubscription(
this.req,
this.res,
error => {
expect(error).to.be.instanceof(Error)
expect(error.message).to.equal(genericError.message)
done()
}
)
})
})
describe('updateSubscription via post', function () {
beforeEach(function (done) {
this.res = {
@@ -1035,28 +763,4 @@ describe('SubscriptionController', function () {
this.SubscriptionController.processUpgradeToAnnualPlan(this.req, this.res)
})
})
describe('requireConfirmedPrimaryEmailAddress', function () {
describe('when user does not have confirmed email address', function () {
beforeEach(function () {
this.req.user = { _id: 'testing' }
this.UserGetter.promises.getUser.resolves({
email: 'test@example.com',
emails: [{ email: 'test@example.com' }],
})
})
it('should show unconfirmed primary email page', function (done) {
this.res.render = (page, opts) => {
page.should.equal('subscriptions/unconfirmed-primary-email')
opts.email.should.equal('test@example.com')
done()
}
this.SubscriptionController.requireConfirmedPrimaryEmailAddress(
this.req,
this.res
)
})
})
})
})