Merge pull request #22584 from overleaf/ls-test-for-group-subscription-upgrade

Add tests for group subscription upgrade

GitOrigin-RevId: 0718dd420930d56602831f127494102045cb6cfc
This commit is contained in:
Liangjun Song
2024-12-18 11:15:08 +00:00
committed by Copybot
parent 6d88c74ec2
commit d51b8f7fd1
6 changed files with 254 additions and 6 deletions
@@ -28,7 +28,7 @@ function UpgradeSubscriptionPlanDetails() {
<b>{preview.nextInvoice.plan.name}</b>
<Row xs="auto" className="gx-2">
<Col>
<span className="per-user-price">
<span className="per-user-price" data-testid="per-user-price">
<b>
{formatCurrencyLocalized(
licenseUnitPrice,
@@ -38,7 +38,7 @@ function UpgradeSummary({ subscriptionChange }: UpgradeSummaryProps) {
{subscriptionChange.nextInvoice.plan.name} x {totalLicenses}{' '}
{t('users')}
</span>
<span>
<span data-testid="subtotal">
{formatCurrencyLocalized(
subscriptionChange.immediateCharge.subtotal,
subscriptionChange.currency
@@ -47,7 +47,7 @@ function UpgradeSummary({ subscriptionChange }: UpgradeSummaryProps) {
</ListGroup.Item>
<ListGroup.Item className="bg-transparent border-0 px-0 gap-3 card-description-secondary">
<span className="me-auto">{t('sales_tax')}</span>
<span>
<span data-testid="tax">
{formatCurrencyLocalized(
subscriptionChange.immediateCharge.tax,
subscriptionChange.currency
@@ -56,7 +56,7 @@ function UpgradeSummary({ subscriptionChange }: UpgradeSummaryProps) {
</ListGroup.Item>
<ListGroup.Item className="bg-transparent border-0 px-0 gap-3 card-description-secondary">
<strong className="me-auto">{t('total_due_today')}</strong>
<strong>
<strong data-testid="total">
{formatCurrencyLocalized(
subscriptionChange.immediateCharge.total,
subscriptionChange.currency
@@ -10,6 +10,7 @@ import RequestStatus from '../request-status'
import UpgradeSummary, {
SubscriptionChange,
} from './upgrade-subscription-upgrade-summary'
import { debugConsole } from '@/utils/debugging'
function UpgradeSubscription() {
const { t } = useTranslation()
@@ -17,7 +18,9 @@ function UpgradeSubscription() {
const preview = getMeta('ol-subscriptionChangePreview') as SubscriptionChange
const { isError, runAsync, isSuccess, isLoading } = useAsync()
const onSubmit = () => {
runAsync(postJSON('/user/subscription/group/upgrade-subscription'))
runAsync(postJSON('/user/subscription/group/upgrade-subscription')).catch(
debugConsole.error
)
}
if (isSuccess) {
@@ -51,7 +54,7 @@ function UpgradeSubscription() {
<div className="container">
<Row>
<Col xl={{ span: 8, offset: 2 }}>
<div className="group-heading">
<div className="group-heading" data-testid="group-heading">
<IconButton
variant="ghost"
href="/user/subscription"
@@ -0,0 +1,120 @@
import '../../../helpers/bootstrap-5'
import UpgradeSubscription from '@/features/group-management/components/upgrade-subscription/upgrade-subscription'
import { SplitTestProvider } from '@/shared/context/split-test-context'
describe('<UpgradeSubscription />', function () {
beforeEach(function () {
this.totalLicenses = 2
this.preview = {
change: {
type: 'group-plan-upgrade',
prevPlan: { name: 'Overleaf Standard Group' },
},
currency: 'USD',
immediateCharge: { subtotal: 353.99, tax: 70.8, total: 424.79 },
paymentMethod: 'Visa **** 1111',
nextPlan: { annual: true },
nextInvoice: {
date: '2025-11-05T11:35:32.000Z',
plan: { name: 'Overleaf Professional Group', amount: 0 },
addOns: [
{
code: 'additional-license',
name: 'Seat',
quantity: 2,
unitAmount: 399,
amount: 798,
},
],
subtotal: 798,
tax: { rate: 0.2, amount: 159.6 },
total: 957.6,
},
}
cy.window().then(win => {
win.metaAttributesCache.set('ol-groupName', 'My Awesome Team')
win.metaAttributesCache.set('ol-totalLicenses', this.totalLicenses)
win.metaAttributesCache.set('ol-subscriptionChangePreview', this.preview)
})
cy.mount(
<SplitTestProvider>
<UpgradeSubscription />
</SplitTestProvider>
)
})
it('shows the group name', function () {
cy.findByTestId('group-heading').within(() => {
cy.findByRole('heading', { name: 'My Awesome Team' })
})
})
it('shows the "Add more users to my plan" label', function () {
cy.findByText(/add more users to my plan/i).should(
'have.attr',
'href',
'/user/subscription/group/add-users'
)
})
it('shows the "Upgrade" and "Cancel" buttons', function () {
cy.findByRole('button', { name: /upgrade/i })
cy.findByRole('button', { name: /cancel/i }).should(
'have.attr',
'href',
'/user/subscription'
)
})
describe('shows plan details', function () {
it('shows per user price', function () {
cy.findByTestId('per-user-price').within(() => {
cy.findByText('$399')
})
})
it('shows additional features', function () {
cy.findByText(/unlimited collaborators per project/i)
cy.findByText(/sso/i)
cy.findByText(/managed user accounts/i)
})
})
describe('shows upgrade summary', function () {
it('shows subtotal, tax and total price', function () {
cy.findByTestId('subtotal').within(() => {
cy.findByText('$353.99')
})
cy.findByTestId('tax').within(() => {
cy.findByText('$70.80')
})
cy.findByTestId('total').within(() => {
cy.findByText('$424.79')
})
})
it('shows total users', function () {
cy.findByText(/you have 2 users on your subscription./i)
})
})
describe('submit upgrade request', function () {
it('request succeeded', function () {
cy.intercept('POST', '/user/subscription/group/upgrade-subscription', {
statusCode: 200,
}).as('upgradeRequest')
cy.findByRole('button', { name: /upgrade/i }).click()
cy.findByText(/youve upgraded your plan!/i)
})
it('request failed', function () {
cy.intercept('POST', '/user/subscription/group/upgrade-subscription', {
statusCode: 400,
}).as('upgradeRequest')
cy.findByRole('button', { name: /upgrade/i }).click()
cy.findByText(/something went wrong/i)
})
})
})
@@ -56,6 +56,9 @@ describe('SubscriptionGroupController', function () {
.stub()
.resolves(this.createSubscriptionChangeData),
ensureFlexibleLicensingEnabled: sinon.stub().resolves(),
getGroupPlanUpgradePreview: sinon
.stub()
.resolves(this.previewSubscriptionChangeData),
},
}
@@ -496,4 +499,77 @@ describe('SubscriptionGroupController', function () {
this.Controller.flexibleLicensingSplitTest(this.req, res, next, done)
})
})
describe('subscriptionUpgradePage', function () {
it('should render "subscription upgrade" page', function (done) {
const olSubscription = { membersLimit: 1, teamName: 'test team' }
this.SubscriptionModel.Subscription.findOne = () => {
return {
exec: () => olSubscription,
}
}
const res = {
render: (page, data) => {
this.SubscriptionGroupHandler.promises.getGroupPlanUpgradePreview
.calledWith(this.req.session.user._id)
.should.equal(true)
page.should.equal('subscriptions/upgrade-group-subscription-react')
data.totalLicenses.should.equal(olSubscription.membersLimit)
data.groupName.should.equal(olSubscription.teamName)
data.changePreview.should.equal(this.previewSubscriptionChangeData)
done()
},
}
this.Controller.subscriptionUpgradePage(this.req, res)
})
it('should redirect if failed to generate preview', function (done) {
this.SubscriptionGroupHandler.promises.getGroupPlanUpgradePreview = sinon
.stub()
.rejects()
const res = {
redirect: url => {
url.should.equal('/user/subscription')
done()
},
}
this.Controller.subscriptionUpgradePage(this.req, res)
})
})
describe('upgradeSubscription', function () {
it('should send 200 response', function (done) {
this.SubscriptionGroupHandler.promises.upgradeGroupPlan = sinon
.stub()
.resolves()
const res = {
sendStatus: code => {
code.should.equal(200)
done()
},
}
this.Controller.upgradeSubscription(this.req, res)
})
it('should send 500 response', function (done) {
this.SubscriptionGroupHandler.promises.upgradeGroupPlan = sinon
.stub()
.rejects()
const res = {
sendStatus: code => {
code.should.equal(500)
done()
},
}
this.Controller.upgradeSubscription(this.req, res)
})
})
})
@@ -28,6 +28,9 @@ describe('SubscriptionGroupHandler', function () {
this.changeRequest = {
timeframe: 'now',
subscription: {
id: 'test_id',
},
}
this.recurlySubscription = {
@@ -39,6 +42,9 @@ describe('SubscriptionGroupHandler', function () {
},
],
getRequestForAddOnUpdate: sinon.stub().returns(this.changeRequest),
getRequestForFlexibleLicensingGroupPlanUpgrade: sinon
.stub()
.returns(this.changeRequest),
}
this.SubscriptionLocator = {
@@ -81,6 +87,9 @@ describe('SubscriptionGroupHandler', function () {
quantity: this.recurlySubscription.addOns[0].quantity + this.adding,
},
],
subscription: {
planName: 'test plan',
},
}
this.applySubscriptionChange = {}
@@ -362,4 +371,44 @@ describe('SubscriptionGroupHandler', function () {
})
).to.not.be.rejected
})
describe('upgradeGroupPlan', function () {
it('should upgrade the subscription', async function () {
this.SubscriptionLocator.promises.getUsersSubscription = sinon
.stub()
.resolves({ groupPlan: true, planCode: 'group_collaborator' })
await this.Handler.promises.upgradeGroupPlan(this.user_id)
this.RecurlyClient.promises.applySubscriptionChangeRequest
.calledWith(this.changeRequest)
.should.equal(true)
this.SubscriptionHandler.promises.syncSubscription
.calledWith({ uuid: this.changeRequest.subscription.id }, this.user_id)
.should.equal(true)
})
it('should fail the upgrade if not eligible', async function () {
this.SubscriptionLocator.promises.getUsersSubscription = sinon
.stub()
.resolves({ groupPlan: true, planCode: 'group_professional' })
await expect(
this.Handler.promises.upgradeGroupPlan(this.user_id)
).to.be.rejectedWith('Not eligible for group plan upgrade')
})
})
describe('getGroupPlanUpgradePreview', function () {
it('should generate preview for subscription upgrade', async function () {
this.SubscriptionLocator.promises.getUsersSubscription = sinon
.stub()
.resolves({ groupPlan: true, planCode: 'group_collaborator' })
const result = await this.Handler.promises.getGroupPlanUpgradePreview(
this.user_id
)
this.RecurlyClient.promises.previewSubscriptionChange
.calledWith(this.changeRequest)
.should.equal(true)
result.should.equal(this.changePreview)
})
})
})