mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-27 02:51:57 +02:00
Merge pull request #22448 from overleaf/ii-flexible-group-licensing-add-seats-tests
[web] Add seats tests GitOrigin-RevId: 76fb5edc6eba5579fac2d3e05cd1f64fba16046c
This commit is contained in:
@@ -171,12 +171,12 @@ async function previewAddSeatsSubscriptionChange(req, res) {
|
||||
*/
|
||||
async function createAddSeatsSubscriptionChange(req, res) {
|
||||
try {
|
||||
const preview =
|
||||
const create =
|
||||
await SubscriptionGroupHandler.promises.createAddSeatsSubscriptionChange(
|
||||
req
|
||||
)
|
||||
|
||||
res.json(preview)
|
||||
res.json(create)
|
||||
} catch (error) {
|
||||
logger.err(
|
||||
{ error },
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
} from '../../../../../../types/subscription/subscription-change-preview'
|
||||
import { MergeAndOverride } from '../../../../../../types/utils'
|
||||
|
||||
const MAX_NUMBER_OF_USERS = 50
|
||||
export const MAX_NUMBER_OF_USERS = 50
|
||||
|
||||
function AddSeats() {
|
||||
const { t } = useTranslation()
|
||||
@@ -244,6 +244,7 @@ function AddSeats() {
|
||||
className="d-grid gap-4"
|
||||
onSubmit={handleSubmit}
|
||||
ref={formRef}
|
||||
data-testid="add-more-users-group-form"
|
||||
>
|
||||
<div className="d-grid gap-1">
|
||||
<h4 className="fw-bold m-0 card-description-secondary">
|
||||
|
||||
@@ -20,7 +20,10 @@ function CostSummary({ subscriptionChange, totalLicenses }: CostSummaryProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Card className="card-gray card-description-secondary">
|
||||
<Card
|
||||
className="card-gray card-description-secondary"
|
||||
data-testid="cost-summary"
|
||||
>
|
||||
<Card.Body className="d-grid gap-2 p-3">
|
||||
<div>
|
||||
<div className="fw-bold">{t('cost_summary')}</div>
|
||||
@@ -53,35 +56,44 @@ function CostSummary({ subscriptionChange, totalLicenses }: CostSummaryProps) {
|
||||
<>
|
||||
<div>
|
||||
<ListGroup>
|
||||
<ListGroup.Item className="bg-transparent border-0 px-0 gap-3 card-description-secondary">
|
||||
<ListGroup.Item
|
||||
className="bg-transparent border-0 px-0 gap-3 card-description-secondary"
|
||||
data-testid="plan"
|
||||
>
|
||||
<span className="me-auto">
|
||||
{subscriptionChange.nextInvoice.plan.name} x{' '}
|
||||
{subscriptionChange.change.addOn.quantity -
|
||||
subscriptionChange.change.addOn.prevQuantity}{' '}
|
||||
{t('seats')}
|
||||
</span>
|
||||
<span>
|
||||
<span data-testid="price">
|
||||
{formatCurrencyLocalized(
|
||||
subscriptionChange.immediateCharge.subtotal,
|
||||
subscriptionChange.currency
|
||||
)}
|
||||
</span>
|
||||
</ListGroup.Item>
|
||||
<ListGroup.Item className="bg-transparent border-0 px-0 gap-3 card-description-secondary">
|
||||
<ListGroup.Item
|
||||
className="bg-transparent border-0 px-0 gap-3 card-description-secondary"
|
||||
data-testid="tax"
|
||||
>
|
||||
<span className="me-auto">
|
||||
{t('sales_tax')} ·{' '}
|
||||
{subscriptionChange.nextInvoice.tax.rate * 100}%
|
||||
</span>
|
||||
<span>
|
||||
<span data-testid="price">
|
||||
{formatCurrencyLocalized(
|
||||
subscriptionChange.immediateCharge.tax,
|
||||
subscriptionChange.currency
|
||||
)}
|
||||
</span>
|
||||
</ListGroup.Item>
|
||||
<ListGroup.Item className="bg-transparent border-0 px-0 gap-3 card-description-secondary">
|
||||
<ListGroup.Item
|
||||
className="bg-transparent border-0 px-0 gap-3 card-description-secondary"
|
||||
data-testid="total"
|
||||
>
|
||||
<strong className="me-auto">{t('total_due_today')}</strong>
|
||||
<strong>
|
||||
<strong data-testid="price">
|
||||
{formatCurrencyLocalized(
|
||||
subscriptionChange.immediateCharge.total,
|
||||
subscriptionChange.currency
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
import '../../../helpers/bootstrap-5'
|
||||
import AddSeats, {
|
||||
MAX_NUMBER_OF_USERS,
|
||||
} from '@/features/group-management/components/add-seats/add-seats'
|
||||
import { SplitTestProvider } from '@/shared/context/split-test-context'
|
||||
|
||||
describe('<AddSeats />', function () {
|
||||
beforeEach(function () {
|
||||
this.totalLicenses = 5
|
||||
|
||||
cy.window().then(win => {
|
||||
win.metaAttributesCache.set('ol-groupName', 'My Awesome Team')
|
||||
win.metaAttributesCache.set('ol-subscriptionId', '123')
|
||||
win.metaAttributesCache.set(
|
||||
'ol-subscriptionEndsAt',
|
||||
'2025-01-01T12:00:00.000Z'
|
||||
)
|
||||
win.metaAttributesCache.set('ol-totalLicenses', this.totalLicenses)
|
||||
})
|
||||
|
||||
cy.mount(
|
||||
<SplitTestProvider>
|
||||
<AddSeats />
|
||||
</SplitTestProvider>
|
||||
)
|
||||
|
||||
cy.findByRole('button', { name: /add users/i })
|
||||
cy.findByTestId('add-more-users-group-form')
|
||||
})
|
||||
|
||||
it('renders the back button', function () {
|
||||
cy.findByTestId('group-heading').within(() => {
|
||||
cy.findByRole('button', { name: /back to subscription/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/user/subscription'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('shows the group name', function () {
|
||||
cy.findByTestId('group-heading').within(() => {
|
||||
cy.findByRole('heading', { name: 'My Awesome Team' })
|
||||
})
|
||||
})
|
||||
|
||||
it('shows the "Add more users" label', function () {
|
||||
cy.findByText(/add more users/i)
|
||||
})
|
||||
|
||||
it('shows the maximum supported users', function () {
|
||||
cy.findByText(
|
||||
new RegExp(
|
||||
`your current plan supports up to ${this.totalLicenses} users`,
|
||||
'i'
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
it('shows instructions on how to reduce users on a plan', function () {
|
||||
cy.contains(
|
||||
/if you want to reduce the number of users on your plan, please contact customer support/i
|
||||
).within(() => {
|
||||
cy.findByRole('link', { name: /contact customer support/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/contact'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('shows the "Upgrade my plan" link', function () {
|
||||
cy.findByRole('link', { name: /upgrade my plan/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/user/subscription/group/upgrade-subscription'
|
||||
)
|
||||
})
|
||||
|
||||
it('renders the cancel button', function () {
|
||||
cy.findByRole('button', { name: /cancel/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/user/subscription'
|
||||
)
|
||||
})
|
||||
|
||||
describe('cost summary', function () {
|
||||
beforeEach(function () {
|
||||
cy.findByLabelText(/how many users do you want to add/i).as('input')
|
||||
})
|
||||
|
||||
it('shows the title', function () {
|
||||
cy.findByTestId('cost-summary').within(() => {
|
||||
cy.findByText(/cost summary/i)
|
||||
})
|
||||
})
|
||||
|
||||
describe('shows default content when', function () {
|
||||
afterEach(function () {
|
||||
cy.findByTestId('cost-summary').within(() => {
|
||||
cy.findByText(
|
||||
/enter the number of users you’d like to add to see the cost breakdown/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('leaves input empty', function () {
|
||||
cy.get('@input').should('have.value', '')
|
||||
})
|
||||
|
||||
it('fills in a non-numeric value', function () {
|
||||
cy.get('@input').type('ab')
|
||||
cy.findByText(/value must be a number/i)
|
||||
})
|
||||
|
||||
it('fills in a decimal value', function () {
|
||||
cy.get('@input').type('1.5')
|
||||
cy.findByText(/value must be a whole number/i)
|
||||
})
|
||||
|
||||
it('fills in a "0" value', function () {
|
||||
cy.get('@input').type('0')
|
||||
cy.findByText(/value must be at least 1/i)
|
||||
})
|
||||
|
||||
it('fills in a value and clears the input', function () {
|
||||
cy.get('@input').type('a{backspace}')
|
||||
cy.get('@input').should('have.text', '')
|
||||
cy.findByText(/this field is required/i)
|
||||
})
|
||||
})
|
||||
|
||||
describe('entered more than the maximum allowed number of users', function () {
|
||||
beforeEach(function () {
|
||||
this.numberOfUsersExceedingMaxLimit = MAX_NUMBER_OF_USERS + 1
|
||||
|
||||
cy.get('@input').type(this.numberOfUsersExceedingMaxLimit.toString())
|
||||
cy.findByRole('button', { name: /add users/i }).should('not.exist')
|
||||
cy.findByRole('button', { name: /send request/i }).as('sendRequestBtn')
|
||||
})
|
||||
|
||||
it('renders a notification', function () {
|
||||
cy.findByTestId('cost-summary').should('not.exist')
|
||||
cy.findByRole('alert').should(
|
||||
'contain.text',
|
||||
`If you want more than ${MAX_NUMBER_OF_USERS} users on your plan, we need to add them for you. Just click Send request below and we’ll be happy to help.`
|
||||
)
|
||||
})
|
||||
|
||||
describe('request', function () {
|
||||
afterEach(function () {
|
||||
cy.findByRole('button', { name: /go to subscriptions/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/user/subscription'
|
||||
)
|
||||
})
|
||||
|
||||
function makeRequest(statusCode: number, adding: string) {
|
||||
cy.intercept(
|
||||
'POST',
|
||||
'/user/subscription/group/add-users/sales-contact-form',
|
||||
{
|
||||
statusCode,
|
||||
}
|
||||
).as('addUsersRequest')
|
||||
cy.get('@sendRequestBtn').click()
|
||||
cy.get('@addUsersRequest').its('request.body').should('deep.equal', {
|
||||
adding,
|
||||
})
|
||||
cy.findByTestId('add-more-users-group-form').should('not.exist')
|
||||
}
|
||||
|
||||
it('sends a request that succeeds', function () {
|
||||
makeRequest(204, this.numberOfUsersExceedingMaxLimit.toString())
|
||||
cy.findByTestId('title').should(
|
||||
'contain.text',
|
||||
'We’ve got your request'
|
||||
)
|
||||
cy.findByText(/our team will get back to you shortly/i)
|
||||
})
|
||||
|
||||
it('sends a request that fails', function () {
|
||||
makeRequest(400, this.numberOfUsersExceedingMaxLimit.toString())
|
||||
cy.findByTestId('title').should(
|
||||
'contain.text',
|
||||
'Something went wrong'
|
||||
)
|
||||
cy.contains(
|
||||
/it looks like that didn’t work. You can try again or get in touch with our Support team for more help/i
|
||||
).within(() => {
|
||||
cy.findByRole('link', { name: /get in touch/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/contact'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('entered less than the maximum allowed number of users', function () {
|
||||
beforeEach(function () {
|
||||
this.adding = 1
|
||||
|
||||
cy.findByRole('button', { name: /add users/i }).as('addUsersBtn')
|
||||
cy.findByRole('button', { name: /send request/i }).should('not.exist')
|
||||
})
|
||||
|
||||
it('renders the preview data', function () {
|
||||
this.body = {
|
||||
change: {
|
||||
type: 'add-on-update',
|
||||
addOn: {
|
||||
code: 'additional-license',
|
||||
quantity: this.totalLicenses + this.adding,
|
||||
prevQuantity: this.totalLicenses,
|
||||
},
|
||||
},
|
||||
currency: 'USD',
|
||||
immediateCharge: {
|
||||
subtotal: 100,
|
||||
tax: 20,
|
||||
total: 120,
|
||||
},
|
||||
nextInvoice: {
|
||||
date: '2025-12-01T00:00:00.000Z',
|
||||
plan: {
|
||||
name: 'Overleaf Standard Group',
|
||||
amount: 0,
|
||||
},
|
||||
subtotal: 895,
|
||||
tax: {
|
||||
rate: 0.2,
|
||||
},
|
||||
total: 1000,
|
||||
},
|
||||
}
|
||||
|
||||
cy.intercept('POST', '/user/subscription/group/add-users/preview', {
|
||||
statusCode: 200,
|
||||
body: this.body,
|
||||
}).as('addUsersRequest')
|
||||
cy.get('@input').type(this.adding.toString())
|
||||
|
||||
cy.findByTestId('cost-summary').within(() => {
|
||||
cy.contains(
|
||||
new RegExp(
|
||||
`you’re adding ${this.adding} users to your plan giving you a total of ${this.body.change.addOn.quantity} users`,
|
||||
'i'
|
||||
)
|
||||
)
|
||||
|
||||
cy.findByTestId('plan').within(() => {
|
||||
cy.findByText(
|
||||
`${this.body.nextInvoice.plan.name} x ${this.adding} Seats`
|
||||
)
|
||||
cy.findByTestId('price').should(
|
||||
'have.text',
|
||||
`$${this.body.immediateCharge.subtotal}.00`
|
||||
)
|
||||
})
|
||||
|
||||
cy.findByTestId('tax').within(() => {
|
||||
cy.findByText(
|
||||
new RegExp(
|
||||
`sales tax · ${this.body.nextInvoice.tax.rate * 100}%`,
|
||||
'i'
|
||||
)
|
||||
)
|
||||
cy.findByTestId('price').should(
|
||||
'have.text',
|
||||
`$${this.body.immediateCharge.tax}.00`
|
||||
)
|
||||
})
|
||||
|
||||
cy.findByTestId('total').within(() => {
|
||||
cy.findByText(/total due today/i)
|
||||
cy.findByTestId('price').should(
|
||||
'have.text',
|
||||
`$${this.body.immediateCharge.total}.00`
|
||||
)
|
||||
})
|
||||
|
||||
cy.findByText(
|
||||
/we’ll charge you now for the cost of your additional users based on the remaining months of your current subscription/i
|
||||
)
|
||||
cy.findByText(
|
||||
/after that, we’ll bill you \$1,000.00 \+ applicable taxes annually on December 1, unless you cancel/i
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('request', function () {
|
||||
afterEach(function () {
|
||||
cy.findByRole('button', { name: /go to subscriptions/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/user/subscription'
|
||||
)
|
||||
})
|
||||
|
||||
function makeRequest(statusCode: number, adding: string) {
|
||||
cy.intercept('POST', '/user/subscription/group/add-users/create', {
|
||||
statusCode,
|
||||
}).as('addUsersRequest')
|
||||
cy.get('@input').type(adding)
|
||||
cy.get('@addUsersBtn').click()
|
||||
cy.get('@addUsersRequest')
|
||||
.its('request.body')
|
||||
.should('deep.equal', {
|
||||
adding: Number(adding),
|
||||
})
|
||||
cy.findByTestId('add-more-users-group-form').should('not.exist')
|
||||
}
|
||||
|
||||
it('sends a request that succeeds', function () {
|
||||
makeRequest(204, this.adding.toString())
|
||||
cy.findByTestId('title').should(
|
||||
'contain.text',
|
||||
'You’ve added more users'
|
||||
)
|
||||
cy.findByText(/you’ve added more users to your subscription/i)
|
||||
cy.findByRole('link', { name: /invite people/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/manage/groups/123/members'
|
||||
)
|
||||
})
|
||||
|
||||
it('sends a request that fails', function () {
|
||||
makeRequest(400, this.adding.toString())
|
||||
cy.findByTestId('title').should(
|
||||
'contain.text',
|
||||
'Something went wrong'
|
||||
)
|
||||
cy.contains(
|
||||
/it looks like that didn’t work. You can try again or get in touch with our Support team for more help/i
|
||||
).within(() => {
|
||||
cy.findByRole('link', { name: /get in touch/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/contact'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,7 @@
|
||||
import '../../../helpers/bootstrap-5'
|
||||
import RequestStatus from '@/features/group-management/components/request-status'
|
||||
|
||||
describe('request confirmation page', function () {
|
||||
describe('<RequestStatus />', function () {
|
||||
beforeEach(function () {
|
||||
cy.window().then(win => {
|
||||
win.metaAttributesCache.set('ol-groupName', 'My Awesome Team')
|
||||
|
||||
@@ -27,11 +27,35 @@ describe('SubscriptionGroupController', function () {
|
||||
_id: this.subscriptionId,
|
||||
teamName: 'Cool group',
|
||||
groupPlan: true,
|
||||
membersLimit: 5,
|
||||
}
|
||||
|
||||
this.plan = {
|
||||
canUseFlexibleLicensing: true,
|
||||
}
|
||||
|
||||
this.previewSubscriptionChangeData = {
|
||||
change: {},
|
||||
currency: 'USD',
|
||||
}
|
||||
|
||||
this.createSubscriptionChangeData = { adding: 1 }
|
||||
|
||||
this.SubscriptionGroupHandler = {
|
||||
promises: {
|
||||
removeUserFromGroup: sinon.stub().resolves(),
|
||||
getUsersGroupSubscriptionDetails: sinon.stub().resolves({
|
||||
subscription: this.subscription,
|
||||
plan: this.plan,
|
||||
recurlySubscription: {},
|
||||
}),
|
||||
previewAddSeatsSubscriptionChange: sinon
|
||||
.stub()
|
||||
.resolves(this.previewSubscriptionChangeData),
|
||||
createAddSeatsSubscriptionChange: sinon
|
||||
.stub()
|
||||
.resolves(this.createSubscriptionChangeData),
|
||||
ensureFlexibleLicensingEnabled: sinon.stub().resolves(),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -66,14 +90,13 @@ describe('SubscriptionGroupController', function () {
|
||||
|
||||
this.SplitTestHandler = {
|
||||
promises: {
|
||||
getAssignment: sinon.stub().resolves({ variant: 'default' }),
|
||||
getAssignment: sinon.stub().resolves(),
|
||||
},
|
||||
getAssignment: sinon.stub().yields(null, { variant: 'default' }),
|
||||
}
|
||||
|
||||
this.UserGetter = {
|
||||
promises: {
|
||||
getUserEmail: sinon.stub().resolves(this.user),
|
||||
getUserEmail: sinon.stub().resolves(this.user.email),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -105,6 +128,13 @@ describe('SubscriptionGroupController', function () {
|
||||
'../../../../app/src/Features/Subscription/RecurlyClient':
|
||||
this.RecurlyClient,
|
||||
'../../../../app/src/models/Subscription': this.SubscriptionModel,
|
||||
'@overleaf/logger': {
|
||||
err: sinon.stub(),
|
||||
error: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
log: sinon.stub(),
|
||||
debug: sinon.stub(),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -287,4 +317,183 @@ describe('SubscriptionGroupController', function () {
|
||||
this.Controller.removeSelfFromGroup(this.req, res, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('addSeatsToGroupSubscription', function () {
|
||||
it('should render the "add seats" page', function (done) {
|
||||
const res = {
|
||||
render: (page, props) => {
|
||||
this.SubscriptionGroupHandler.promises.getUsersGroupSubscriptionDetails
|
||||
.calledWith(this.req)
|
||||
.should.equal(true)
|
||||
this.SubscriptionGroupHandler.promises.ensureFlexibleLicensingEnabled
|
||||
.calledWith(this.plan)
|
||||
.should.equal(true)
|
||||
page.should.equal('subscriptions/add-seats')
|
||||
props.subscriptionId.should.equal(this.subscriptionId)
|
||||
props.groupName.should.equal(this.subscription.teamName)
|
||||
props.totalLicenses.should.equal(this.subscription.membersLimit)
|
||||
done()
|
||||
},
|
||||
}
|
||||
|
||||
this.Controller.addSeatsToGroupSubscription(this.req, res)
|
||||
})
|
||||
|
||||
it('should redirect to subscription page when getting subscription details fails', function (done) {
|
||||
this.SubscriptionGroupHandler.promises.getUsersGroupSubscriptionDetails =
|
||||
sinon.stub().rejects()
|
||||
|
||||
const res = {
|
||||
redirect: url => {
|
||||
url.should.equal('/user/subscription')
|
||||
done()
|
||||
},
|
||||
}
|
||||
|
||||
this.Controller.addSeatsToGroupSubscription(this.req, res)
|
||||
})
|
||||
|
||||
it('should redirect to subscription page when flexible licensing is not enabled', function (done) {
|
||||
this.SubscriptionGroupHandler.promises.ensureFlexibleLicensingEnabled =
|
||||
sinon.stub().rejects()
|
||||
|
||||
const res = {
|
||||
redirect: url => {
|
||||
url.should.equal('/user/subscription')
|
||||
done()
|
||||
},
|
||||
}
|
||||
|
||||
this.Controller.addSeatsToGroupSubscription(this.req, res)
|
||||
})
|
||||
})
|
||||
|
||||
describe('previewAddSeatsSubscriptionChange', function () {
|
||||
it('should preview "add seats" change', function (done) {
|
||||
const res = {
|
||||
json: data => {
|
||||
this.SubscriptionGroupHandler.promises.previewAddSeatsSubscriptionChange
|
||||
.calledWith(this.req)
|
||||
.should.equal(true)
|
||||
data.should.deep.equal(this.previewSubscriptionChangeData)
|
||||
done()
|
||||
},
|
||||
}
|
||||
|
||||
this.Controller.previewAddSeatsSubscriptionChange(this.req, res)
|
||||
})
|
||||
|
||||
it('should fail previewing "add seats" change', function (done) {
|
||||
this.SubscriptionGroupHandler.promises.previewAddSeatsSubscriptionChange =
|
||||
sinon.stub().rejects()
|
||||
|
||||
const res = {
|
||||
status: statusCode => {
|
||||
statusCode.should.equal(400)
|
||||
|
||||
return {
|
||||
end: () => {
|
||||
done()
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
this.Controller.previewAddSeatsSubscriptionChange(this.req, res)
|
||||
})
|
||||
})
|
||||
|
||||
describe('createAddSeatsSubscriptionChange', function () {
|
||||
it('should apply "add seats" change', function (done) {
|
||||
const res = {
|
||||
json: data => {
|
||||
this.SubscriptionGroupHandler.promises.createAddSeatsSubscriptionChange
|
||||
.calledWith(this.req)
|
||||
.should.equal(true)
|
||||
data.should.deep.equal(this.createSubscriptionChangeData)
|
||||
done()
|
||||
},
|
||||
}
|
||||
|
||||
this.Controller.createAddSeatsSubscriptionChange(this.req, res)
|
||||
})
|
||||
|
||||
it('should fail applying "add seats" change', function (done) {
|
||||
this.SubscriptionGroupHandler.promises.createAddSeatsSubscriptionChange =
|
||||
sinon.stub().rejects()
|
||||
|
||||
const res = {
|
||||
status: statusCode => {
|
||||
statusCode.should.equal(400)
|
||||
|
||||
return {
|
||||
end: () => {
|
||||
done()
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
this.Controller.createAddSeatsSubscriptionChange(this.req, res)
|
||||
})
|
||||
})
|
||||
|
||||
describe('submitForm', function () {
|
||||
it('should build and pass the request body to the sales submit handler', function (done) {
|
||||
const adding = 100
|
||||
this.req.body = { adding }
|
||||
|
||||
const res = {
|
||||
sendStatus: code => {
|
||||
this.Modules.promises.hooks.fire
|
||||
.calledWith('sendSupportRequest', {
|
||||
email: this.user.email,
|
||||
subject: 'Sales Contact Form',
|
||||
message:
|
||||
'\n' +
|
||||
'**Overleaf Sales Contact Form:**\n' +
|
||||
'\n' +
|
||||
'**Subject:** Self-Serve Group User Increase Request\n' +
|
||||
'\n' +
|
||||
`**Estimated Number of Users:** ${adding}\n` +
|
||||
'\n' +
|
||||
`**Message:** This email has been generated on behalf of user with email **${this.user.email}** to request an increase in the total number of users for their subscription.`,
|
||||
inbox: 'sales',
|
||||
})
|
||||
.should.equal(true)
|
||||
sinon.assert.calledOnce(this.Modules.promises.hooks.fire)
|
||||
code.should.equal(204)
|
||||
done()
|
||||
},
|
||||
}
|
||||
this.Controller.submitForm(this.req, res, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('flexibleLicensingSplitTest', function () {
|
||||
it('passes when the variant is "enabled"', function (done) {
|
||||
const res = sinon.stub()
|
||||
const next = () => {
|
||||
this.ErrorController.notFound.notCalled.should.equal(true)
|
||||
done()
|
||||
}
|
||||
this.SplitTestHandler.promises.getAssignment.resolves({
|
||||
variant: 'enabled',
|
||||
})
|
||||
this.Controller.flexibleLicensingSplitTest(this.req, res, next, done)
|
||||
})
|
||||
|
||||
it('returns error page when the variant is "default"', function (done) {
|
||||
const res = sinon.stub()
|
||||
const next = sinon.stub()
|
||||
this.ErrorController.notFound = sinon.stub().callsFake(() => {
|
||||
next.notCalled.should.equal(true)
|
||||
done()
|
||||
})
|
||||
this.SplitTestHandler.promises.getAssignment.resolves({
|
||||
variant: 'default',
|
||||
})
|
||||
this.Controller.flexibleLicensingSplitTest(this.req, res, next, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
const MockRequest = require('../helpers/MockRequest')
|
||||
const modulePath =
|
||||
'../../../../app/src/Features/Subscription/SubscriptionGroupHandler'
|
||||
|
||||
@@ -12,6 +13,12 @@ describe('SubscriptionGroupHandler', function () {
|
||||
this.email = 'jim@example.com'
|
||||
this.user = { _id: this.user_id, email: this.newEmail }
|
||||
this.subscription_id = '31DSd1123D'
|
||||
this.adding = 1
|
||||
this.paymentMethod = { cardType: 'Visa', lastFour: '1111' }
|
||||
this.localPlanInSettings = {
|
||||
membersLimit: 2,
|
||||
membersLimitAddOn: 'additional-license',
|
||||
}
|
||||
|
||||
this.subscription = {
|
||||
admin_id: this.adminUser_id,
|
||||
@@ -19,16 +26,35 @@ describe('SubscriptionGroupHandler', function () {
|
||||
_id: this.subscription_id,
|
||||
}
|
||||
|
||||
this.changeRequest = {
|
||||
timeframe: 'now',
|
||||
}
|
||||
|
||||
this.recurlySubscription = {
|
||||
id: 123,
|
||||
addOns: [
|
||||
{
|
||||
code: 'additional-license',
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
getRequestForAddOnUpdate: sinon.stub().returns(this.changeRequest),
|
||||
}
|
||||
|
||||
this.SubscriptionLocator = {
|
||||
promises: {
|
||||
getUsersSubscription: sinon.stub(),
|
||||
getUsersSubscription: sinon.stub().resolves({ groupPlan: true }),
|
||||
getSubscriptionByMemberIdAndId: sinon.stub(),
|
||||
getSubscription: sinon.stub().resolves(this.subscription),
|
||||
},
|
||||
}
|
||||
|
||||
this.changePreview = {
|
||||
currency: 'USD',
|
||||
}
|
||||
|
||||
this.SubscriptionController = {
|
||||
makeChangePreview: sinon.stub().resolves(),
|
||||
makeChangePreview: sinon.stub().resolves(this.changePreview),
|
||||
}
|
||||
|
||||
this.SubscriptionUpdater = {
|
||||
@@ -44,8 +70,36 @@ describe('SubscriptionGroupHandler', function () {
|
||||
findOne: sinon.stub().returns({ exec: sinon.stub().resolves }),
|
||||
}
|
||||
|
||||
this.SessionManager = {
|
||||
getLoggedInUserId: sinon.stub().returns(this.user._id),
|
||||
}
|
||||
|
||||
this.previewSubscriptionChange = {
|
||||
nextAddOns: [
|
||||
{
|
||||
code: 'additional-license',
|
||||
quantity: this.recurlySubscription.addOns[0].quantity + this.adding,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
this.applySubscriptionChange = {}
|
||||
|
||||
this.RecurlyClient = {
|
||||
promises: {},
|
||||
promises: {
|
||||
getSubscription: sinon.stub().resolves(this.recurlySubscription),
|
||||
getPaymentMethod: sinon.stub().resolves(this.paymentMethod),
|
||||
previewSubscriptionChange: sinon
|
||||
.stub()
|
||||
.resolves(this.previewSubscriptionChange),
|
||||
applySubscriptionChangeRequest: sinon
|
||||
.stub()
|
||||
.resolves(this.applySubscriptionChange),
|
||||
},
|
||||
}
|
||||
|
||||
this.PlansLocator = {
|
||||
findLocalPlanInSettings: sinon.stub(this.localPlanInSettings),
|
||||
}
|
||||
|
||||
this.SubscriptionHandler = {
|
||||
@@ -64,6 +118,8 @@ describe('SubscriptionGroupHandler', function () {
|
||||
Subscription: this.Subscription,
|
||||
},
|
||||
'./RecurlyClient': this.RecurlyClient,
|
||||
'./PlansLocator': this.PlansLocator,
|
||||
'../Authentication/SessionManager': this.SessionManager,
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -183,4 +239,127 @@ describe('SubscriptionGroupHandler', function () {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getUsersGroupSubscriptionDetails', function () {
|
||||
beforeEach(function () {
|
||||
this.req = new MockRequest()
|
||||
this.PlansLocator.findLocalPlanInSettings = sinon.stub().returns({
|
||||
...this.localPlanInSettings,
|
||||
canUseFlexibleLicensing: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('should throw if the subscription is not a group plan', async function () {
|
||||
this.SubscriptionLocator.promises.getUsersSubscription = sinon
|
||||
.stub()
|
||||
.resolves({ groupPlan: false })
|
||||
|
||||
await expect(
|
||||
this.Handler.promises.getUsersGroupSubscriptionDetails(this.req)
|
||||
).to.be.rejectedWith('User subscription is not a group plan')
|
||||
})
|
||||
|
||||
it('should return users group subscription details', async function () {
|
||||
const data = await this.Handler.promises.getUsersGroupSubscriptionDetails(
|
||||
this.req
|
||||
)
|
||||
|
||||
expect(data).to.deep.equal({
|
||||
subscription: { groupPlan: true },
|
||||
plan: {
|
||||
membersLimit: 2,
|
||||
membersLimitAddOn: 'additional-license',
|
||||
canUseFlexibleLicensing: true,
|
||||
},
|
||||
recurlySubscription: this.recurlySubscription,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('add seats subscription change', function () {
|
||||
beforeEach(function () {
|
||||
this.req = new MockRequest()
|
||||
Object.assign(this.req.body, { adding: this.adding })
|
||||
this.PlansLocator.findLocalPlanInSettings = sinon.stub().returns({
|
||||
...this.localPlanInSettings,
|
||||
canUseFlexibleLicensing: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
this.recurlySubscription.getRequestForAddOnUpdate
|
||||
.calledWith(
|
||||
'additional-license',
|
||||
this.recurlySubscription.addOns[0].quantity + this.adding
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
describe('previewAddSeatsSubscriptionChange', function () {
|
||||
it('should return the subscription change preview', async function () {
|
||||
const preview =
|
||||
await this.Handler.promises.previewAddSeatsSubscriptionChange(
|
||||
this.req
|
||||
)
|
||||
|
||||
this.RecurlyClient.promises.getPaymentMethod
|
||||
.calledWith(this.user_id)
|
||||
.should.equal(true)
|
||||
this.RecurlyClient.promises.previewSubscriptionChange
|
||||
.calledWith(this.changeRequest)
|
||||
.should.equal(true)
|
||||
this.SubscriptionController.makeChangePreview
|
||||
.calledWith(
|
||||
{
|
||||
type: 'add-on-update',
|
||||
addOn: {
|
||||
code: 'additional-license',
|
||||
quantity:
|
||||
this.recurlySubscription.addOns[0].quantity + this.adding,
|
||||
prevQuantity: this.adding,
|
||||
},
|
||||
},
|
||||
this.previewSubscriptionChange,
|
||||
this.paymentMethod
|
||||
)
|
||||
.should.equal(true)
|
||||
preview.should.equal(this.changePreview)
|
||||
})
|
||||
})
|
||||
|
||||
describe('createAddSeatsSubscriptionChange', function () {
|
||||
it('should change the subscription', async function () {
|
||||
const result =
|
||||
await this.Handler.promises.createAddSeatsSubscriptionChange(this.req)
|
||||
|
||||
this.RecurlyClient.promises.applySubscriptionChangeRequest
|
||||
.calledWith(this.changeRequest)
|
||||
.should.equal(true)
|
||||
this.SubscriptionHandler.promises.syncSubscription
|
||||
.calledWith({ uuid: this.recurlySubscription.id }, this.user_id)
|
||||
.should.equal(true)
|
||||
expect(result).to.deep.equal({
|
||||
adding: this.req.body.adding,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ensureFlexibleLicensingEnabled', function () {
|
||||
it('should throw if the subscription can not use flexible licensing', async function () {
|
||||
await expect(
|
||||
this.Handler.promises.ensureFlexibleLicensingEnabled({
|
||||
canUseFlexibleLicensing: false,
|
||||
})
|
||||
).to.be.rejectedWith('The group plan does not support flexible licencing')
|
||||
})
|
||||
})
|
||||
|
||||
it('should not throw if the subscription can use flexible licensing', async function () {
|
||||
await expect(
|
||||
this.Handler.promises.ensureFlexibleLicensingEnabled({
|
||||
canUseFlexibleLicensing: true,
|
||||
})
|
||||
).to.not.be.rejected
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user