overleaf/services/web/test/frontend/features/subscription/components/new/checkout.spec.tsx
Alf Eaton e0d327a3e8 Skip/improve flaky pdf tests (#13150)
GitOrigin-RevId: a98b145e86f325d1df79bdbcd1825ee5a3ca6def
2023-05-25 08:04:49 +00:00

455 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import CheckoutPanel from '../../../../../../frontend/js/features/subscription/components/new/checkout/checkout-panel'
import { PaymentProvider } from '../../../../../../frontend/js/features/subscription/context/payment-context'
import { plans } from '../../fixtures/plans'
import {
createFakeRecurly,
defaultSubscription,
ElementsBase,
} from '../../fixtures/recurly-mock'
import { fillForm } from '../../helpers/payment'
import { cloneDeep } from 'lodash'
import { TokenHandler, RecurlyError } from 'recurly__recurly-js'
function CheckoutPanelWithPaymentProvider() {
return (
<PaymentProvider publicKey="0000">
<CheckoutPanel />
</PaymentProvider>
)
}
describe('checkout panel', function () {
const itmCampaign = 'fake_itm_campaign'
const itmContent = 'fake_itm_content'
const itmReferrer = 'fake_itm_referrer'
beforeEach(function () {
const plan = plans.find(({ planCode }) => planCode === 'student-annual')
if (!plan) {
throw new Error('No plan was found while running the test!')
}
cy.window().then(win => {
win.metaAttributesCache = new Map()
win.metaAttributesCache.set('ol-countryCode', '')
win.metaAttributesCache.set('ol-recurlyApiKey', '0000')
win.metaAttributesCache.set('ol-recommendedCurrency', 'USD')
win.metaAttributesCache.set('ol-plan', plan)
win.metaAttributesCache.set('ol-planCode', plan.planCode)
win.metaAttributesCache.set('ol-showCouponField', true)
win.metaAttributesCache.set('ol-itm_campaign', itmCampaign)
win.metaAttributesCache.set('ol-itm_content', itmContent)
win.metaAttributesCache.set('ol-itm_referrer', itmReferrer)
cy.wrap(plan).as('plan')
// init default recurly
win.recurly = createFakeRecurly(defaultSubscription)
cy.interceptEvents()
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.findByTestId('checkout-form').as('form')
})
it('renders heading', function () {
cy.contains(/select a payment method/i)
})
it('renders student disclaimer', function () {
cy.contains(
'The educational discount applies to all students at secondary and postsecondary institutions ' +
'(schools and universities). We may contact you to confirm that youre eligible for the discount.'
)
})
it('renders payment method toggle', function () {
cy.findByTestId('payment-method-toggle').within(() => {
cy.findByLabelText(/card payment/i)
cy.findByLabelText(/paypal/i)
})
})
it('renders address first line input', function () {
cy.get('@form').within(() => {
cy.findByLabelText('Address')
cy.findByLabelText(/this address will be shown on the invoice/i)
})
})
it('renders address second line input', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/address second line/i).should('not.exist')
cy.findByRole('button', { name: /add another address line/i }).click()
cy.findByLabelText(/address second line/i)
})
})
it('renders postal code input', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/postal code/i)
})
})
it('renders country dropdown', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/country/i)
})
})
it('renders company details', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/add company details/i).as('checkbox')
cy.get('@checkbox').should('not.be.checked')
cy.findByLabelText(/company name/i).should('not.exist')
cy.findByLabelText(/vat number/i).should('not.exist')
cy.get('@checkbox').click()
cy.findByLabelText(/company name/i)
cy.findByLabelText(/vat number/i)
})
})
it('renders coupon field', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/coupon code/i)
})
})
it('renders tos agreement notice', function () {
cy.contains(/by subscribing, you agree to our terms of service/i)
})
it('renders recurly error', function () {
cy.window().then(win => {
win.recurly = undefined!
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.contains(
/sorry, there was an error talking to our payment provider. Please try again in a few moments/i
)
cy.contains(
/if you are using any ad or script blocking extensions in your browser, you may need to temporarily disable them/i
)
})
it('calls recurly.token on submit', function () {
cy.window().then(win => {
cy.stub(win.recurly, 'token')
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.get('@form').within(() => fillForm())
cy.findByRole('button', { name: /upgrade now/i }).click()
cy.window().then(win => {
expect(win.recurly.token).to.be.calledOnceWith(
Cypress.sinon.match.instanceOf(ElementsBase),
{
first_name: '1',
last_name: '1',
postal_code: '1',
address1: '1',
address2: '',
state: '',
city: '',
country: 'BG',
coupon: '',
},
Cypress.sinon.match.func
)
})
})
it('renders generic error', function () {
const errorMessage = 'generic error'
cy.window().then(win => {
win.recurly = createFakeRecurly(defaultSubscription, {
token: (_1: unknown, _2: unknown, handler: TokenHandler) => {
const err = new Error(errorMessage) as RecurlyError
setTimeout(() => handler(err, { id: '1', type: 'abc' }), 100)
},
})
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.get('@form').within(() => fillForm())
cy.findByRole('button', { name: /upgrade now/i }).as('button')
cy.get('@button').click()
cy.get('@button').within(() => {
cy.contains(/processing/i)
})
cy.findByRole('alert').should('have.text', errorMessage)
cy.get('@button').within(() => {
cy.findByText(/processing/i).should('not.exist')
})
})
it('renders prefilled coupon input', function () {
const couponCode = 'promo_code'
cy.window().then(win => {
win.metaAttributesCache.set('ol-couponCode', couponCode)
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.findByLabelText(/coupon code/i).should('have.value', couponCode)
})
it('calls coupon method when entering coupon code', function () {
const couponCode = 'promo_code'
cy.window().then(win => {
const couponStub = cy.stub().as('coupon')
couponStub.returnsThis()
win.recurly = createFakeRecurly({
...defaultSubscription,
coupon: couponStub,
})
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.get('@coupon').should('have.been.calledOnce')
cy.findByTestId('checkout-form').within(() => {
cy.findByLabelText(/coupon code/i)
.type(couponCode, { delay: 0 })
.blur()
})
cy.get('@coupon')
.should('have.been.calledTwice')
.and('have.been.calledWith', couponCode)
})
it('enters invalid coupon code', function () {
cy.window().then(win => {
const catchStub = cy.stub().as('catch')
catchStub.onFirstCall().returnsThis()
catchStub
.onSecondCall()
.callsFake(function (this: unknown, cb: (err: RecurlyError) => void) {
const err = {
name: 'api-error',
code: 'not-found',
} as RecurlyError
cb(err)
return this
})
win.recurly = createFakeRecurly({
...defaultSubscription,
catch: catchStub,
})
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.findByTestId('checkout-form').within(() => {
cy.findByLabelText(/coupon code/i)
.type('promo_code', { delay: 0 })
.blur()
})
cy.findByRole('alert').within(() => {
cy.contains(/coupon code is not valid for selected plan/i)
})
})
it('fails coupon verification', function () {
cy.window().then(win => {
const catchStub = cy.stub().as('catch')
// call original method on change event
catchStub.onFirstCall().returnsThis()
catchStub
.onSecondCall()
.callsFake(function (this: unknown, cb: (err: RecurlyError) => void) {
const err = {} as RecurlyError
try {
cb(err)
} catch (e) {}
return this
})
win.recurly = createFakeRecurly({
...defaultSubscription,
catch: catchStub,
})
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.get('@catch').should('have.been.calledOnce')
cy.findByTestId('checkout-form').within(() => {
cy.findByLabelText(/coupon code/i)
.type('promo_code', { delay: 0 })
.blur()
})
cy.get('@catch').should('have.been.calledTwice')
cy.findByRole('alert').within(() => {
cy.contains(/an error occurred when verifying the coupon code/i)
})
})
/* The test is disabled due to https://github.com/overleaf/internal/issues/12004
it.skip('creates a new subscription', function () {
cy.stub(locationModule, 'assign').as('assign')
cy.intercept('POST', 'user/subscription/create', {
statusCode: 201,
}).as('create')
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.findByTestId('checkout-form').within(() => fillForm())
cy.findByRole('button', { name: /upgrade now/i }).click()
// verify itm params are also passed
cy.get('@create')
.its('request.body.subscriptionDetails')
.should('contain', {
ITMCampaign: itmCampaign,
ITMContent: itmContent,
ITMReferrer: itmReferrer,
})
cy.get('@assign')
.should('have.been.calledOnce')
.and('have.been.calledWith', '/user/subscription/thank-you')
})
*/
it('fails to create a new subscription', function () {
cy.intercept('POST', 'user/subscription/create', {
statusCode: 404,
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.findByTestId('checkout-form').within(() => fillForm())
cy.findByRole('button', { name: /upgrade now/i }).click()
cy.findByRole('alert').within(() => {
cy.contains(/something went wrong processing the request/i)
})
})
describe('3DS challenge', function () {
it('shows three d secure challenge', function () {
cy.intercept('POST', 'user/subscription/create', {
statusCode: 404,
body: {
threeDSecureActionTokenId: '123',
},
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.findByTestId('checkout-form').within(() => fillForm())
cy.findByRole('button', { name: /upgrade now/i }).click()
cy.findByRole('alert').within(() => {
cy.contains(
/your card must be authenticated with 3D Secure before continuing/i
)
})
cy.contains('3D challenge content')
})
})
describe('card payments', function () {
beforeEach(function () {
cy.findByLabelText(/card payment/i).click()
})
it('renders card element', function () {
cy.get('@form').within(() => {
cy.findByText(/card details/i, { selector: 'label' })
cy.findByTestId('test-card-element')
})
})
it('verifies the card element does not disappear when switching between payment methods', function () {
cy.get('@form').within(() => {
cy.findByText(/card details/i, { selector: 'label' })
cy.findByTestId('test-card-element')
cy.findByLabelText(/paypal/i).click()
cy.findByLabelText(/card payment/i).click()
cy.findByText(/card details/i, { selector: 'label' })
cy.findByTestId('test-card-element')
})
})
it('renders first name input', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/first name/i)
})
})
it('renders last name input', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/last name/i)
})
})
describe('submit button', function () {
it('renders trial button', function () {
cy.get('@form').within(() => {
cy.findByRole('button', { name: /upgrade now, pay after \d+ days/i })
})
})
it('renders non-trial button', function () {
cy.window().then(win => {
const clone = cloneDeep(defaultSubscription)
clone.items.plan!.trial = undefined
win.recurly = createFakeRecurly(clone)
})
cy.mount(<CheckoutPanelWithPaymentProvider />)
cy.findByTestId('checkout-form').within(() => {
cy.findByRole('button', { name: 'Upgrade Now' })
})
})
it('handles the disabled state of submit button', function () {
cy.get('@form').within(() => {
cy.findByRole('button', { name: /upgrade now/i }).should(
'be.disabled'
)
fillForm()
cy.findByRole('button', { name: /upgrade now/i }).should(
'not.be.disabled'
)
})
})
})
})
describe('paypal payments', function () {
beforeEach(function () {
cy.findByLabelText(/paypal/i).click()
})
it('should not render card element', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/card details/i).should('not.exist')
})
})
it('should not render first name input', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/first name/i).should('not.exist')
})
})
it('should not render last name input', function () {
cy.get('@form').within(() => {
cy.findByLabelText(/last name/i).should('not.exist')
})
})
it('renders proceeding to PayPal notice', function () {
cy.get('@form').within(() => {
cy.contains(
/proceeding to PayPal will take you to the PayPal site to pay for your subscription/i
)
})
})
it('handles the disabled state of submit button', function () {
cy.get('@form').within(() => {
cy.findByRole('button', { name: /proceed to paypal/i }).should(
'be.disabled'
)
cy.findByLabelText(/country/i).select('Bulgaria')
cy.findByRole('button', { name: /proceed to paypal/i }).should(
'not.be.disabled'
)
})
})
})
})