Merge pull request #7225 from overleaf/ta-leave-modal

[DeleteAccount] Create Modal with Form

GitOrigin-RevId: 611f08c7253f59d91c6937b79c80a386b9d21ccd
This commit is contained in:
ilkin-overleaf
2022-04-11 08:03:36 +00:00
committed by Copybot
parent 72f6d89e78
commit d50271c1e9
14 changed files with 688 additions and 8 deletions
@@ -54,7 +54,7 @@ describe('Captcha', function () {
expect(response.statusCode).to.equal(401)
expect(body).to.deep.equal({
message: {
text: 'Your email or password is incorrect. Please try again',
text: 'Your email or password is incorrect. Please try again.',
type: 'error',
},
})
@@ -97,7 +97,9 @@ describe('Registration', function () {
it('should produce the correct responses so far', function () {
expect(results.length).to.equal(9)
expect(results).to.deep.equal(
Array(9).fill('Your email or password is incorrect. Please try again')
Array(9).fill(
'Your email or password is incorrect. Please try again.'
)
)
})
@@ -115,7 +117,7 @@ describe('Registration', function () {
expect(results.length).to.equal(15)
expect(results).to.deep.equal(
Array(10)
.fill('Your email or password is incorrect. Please try again')
.fill('Your email or password is incorrect. Please try again.')
.concat(
Array(5).fill(
'This account has had too many login requests. Please wait 2 minutes before trying to log in again'
@@ -145,7 +147,7 @@ describe('Registration', function () {
it('should not rate limit their request', function () {
expect(messages).to.deep.equal([
'Your email or password is incorrect. Please try again',
'Your email or password is incorrect. Please try again.',
])
})
@@ -194,7 +196,7 @@ describe('Registration', function () {
expect(results.length).to.equal(9)
expect(results).to.deep.equal(
Array(9).fill(
'Your email or password is incorrect. Please try again'
'Your email or password is incorrect. Please try again.'
)
)
})
@@ -0,0 +1,38 @@
import {
fireEvent,
screen,
waitForElementToBeRemoved,
render,
} from '@testing-library/react'
import LeaveSection from '../../../../../frontend/js/features/settings/components/leave-section'
describe('<LeaveSection />', function () {
it('opens modal', async function () {
render(<LeaveSection />)
const button = screen.getByRole('button', {
name: 'Delete your account',
})
fireEvent.click(button)
await screen.findByText('Delete Account')
})
it('closes modal', async function () {
render(<LeaveSection />)
fireEvent.click(
screen.getByRole('button', {
name: 'Delete your account',
})
)
const cancelButton = screen.getByRole('button', {
name: 'Close',
})
fireEvent.click(cancelButton)
await waitForElementToBeRemoved(() => screen.getByText('Delete Account'))
})
})
@@ -0,0 +1,26 @@
import { expect } from 'chai'
import { screen, render } from '@testing-library/react'
import fetchMock from 'fetch-mock'
import LeaveModalContent from '../../../../../../frontend/js/features/settings/components/leave/modal-content'
describe('<LeaveModalContent />', function () {
afterEach(function () {
fetchMock.reset()
})
it('disable delete button if form is not valid', function () {
render(
<LeaveModalContent
handleHide={() => {}}
inFlight={false}
setInFlight={() => {}}
/>
)
const deleteButton = screen.getByRole('button', {
name: 'Delete',
})
expect(deleteButton.hasAttribute('disabled')).to.be.true
})
})
@@ -0,0 +1,196 @@
import { expect } from 'chai'
import sinon from 'sinon'
import { fireEvent, screen, render, waitFor } from '@testing-library/react'
import fetchMock from 'fetch-mock'
import LeaveModalForm from '../../../../../../frontend/js/features/settings/components/leave/modal-form'
describe('<LeaveModalForm />', function () {
beforeEach(function () {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-userDefaultEmail', 'foo@bar.com')
})
afterEach(function () {
window.metaAttributesCache = new Map()
fetchMock.reset()
})
it('validates form', async function () {
const setIsFormValid = sinon.stub()
render(
<LeaveModalForm
setInFlight={() => {}}
isFormValid={false}
setIsFormValid={setIsFormValid}
/>
)
const emailInput = screen.getByLabelText('Email')
fireEvent.change(emailInput, { target: { value: 'foo@bar.com' } })
const passwordInput = screen.getByLabelText('Password')
fireEvent.change(passwordInput, { target: { value: 'foobar' } })
const checkbox = screen.getByLabelText(
'I understand this will delete all projects in my Overleaf account with email address foo@bar.com'
)
fireEvent.click(checkbox)
const setIsFormValidCalls = setIsFormValid.getCalls()
const lastSetIsFormValidCall = setIsFormValidCalls.pop()
expect(lastSetIsFormValidCall.args[0]).to.be.true
for (const setIsFormValidCall of setIsFormValidCalls) {
expect(setIsFormValidCall.args[0]).to.be.false
}
})
describe('submits', async function () {
let setInFlight
let setIsFormValid
let deleteMock
let locationStub
const originalLocation = window.location
beforeEach(function () {
setInFlight = sinon.stub()
setIsFormValid = sinon.stub()
deleteMock = fetchMock.post('/user/delete', 200)
locationStub = sinon.stub()
Object.defineProperty(window, 'location', {
value: {
assign: locationStub,
},
})
})
afterEach(function () {
fetchMock.reset()
Object.defineProperty(window, 'location', {
value: originalLocation,
})
})
it('with valid form', async function () {
render(
<LeaveModalForm
setInFlight={setInFlight}
isFormValid
setIsFormValid={setIsFormValid}
/>
)
fireEvent.submit(screen.getByLabelText('Email'))
sinon.assert.calledOnce(setInFlight)
sinon.assert.calledWithMatch(setInFlight, true)
expect(deleteMock.called()).to.be.true
await waitFor(() => {
sinon.assert.calledTwice(setInFlight)
sinon.assert.calledWithMatch(setInFlight, false)
sinon.assert.calledOnce(locationStub)
sinon.assert.calledWithMatch(locationStub, '/login')
})
})
it('with invalid form', async function () {
render(
<LeaveModalForm
setInFlight={setInFlight}
isFormValid={false}
setIsFormValid={setIsFormValid}
/>
)
fireEvent.submit(screen.getByLabelText('Email'))
expect(deleteMock.called()).to.be.false
sinon.assert.notCalled(setInFlight)
})
})
it('handles credentials error with Saas tip', async function () {
fetchMock.post('/user/delete', 403)
render(
<LeaveModalForm
setInFlight={() => {}}
isFormValid
setIsFormValid={() => {}}
/>
)
fireEvent.submit(screen.getByLabelText('Email'))
await waitFor(() => {
screen.getByText(/Your email or password is incorrect. Please try again/)
})
expect(screen.queryByText(/If you cannot remember your password/)).to.not
.exist
})
it('handles credentials error without Saas tip', async function () {
fetchMock.post('/user/delete', 403)
window.metaAttributesCache.set('ol-isSaas', true)
render(
<LeaveModalForm
setInFlight={() => {}}
isFormValid
setIsFormValid={() => {}}
/>
)
fireEvent.submit(screen.getByLabelText('Email'))
await waitFor(() => {
screen.getByText(/Your email or password is incorrect. Please try again/)
})
screen.getByText(/If you cannot remember your password/)
const link = screen.getByRole('link', { name: 'reset your password' })
expect(link.getAttribute('href')).to.equal('/user/password/reset')
})
it('handles subscription error', async function () {
fetchMock.post('/user/delete', {
status: 422,
body: {
error: 'SubscriptionAdminDeletionError',
},
})
render(
<LeaveModalForm
setInFlight={() => {}}
isFormValid
setIsFormValid={() => {}}
/>
)
fireEvent.submit(screen.getByLabelText('Email'))
await waitFor(() => {
screen.getByText(
'You cannot delete your account while on a subscription. Please cancel your subscription and try again. If you keep seeing this message please contact us.'
)
})
})
it('handles generic error', async function () {
fetchMock.post('/user/delete', 500)
render(
<LeaveModalForm
setInFlight={() => {}}
isFormValid
setIsFormValid={() => {}}
/>
)
fireEvent.submit(screen.getByLabelText('Email'))
await waitFor(() => {
screen.getByText(
'Sorry, something went wrong deleting your account. Please try again in a minute.'
)
})
})
})
@@ -0,0 +1,65 @@
import sinon from 'sinon'
import { fireEvent, screen, render, waitFor } from '@testing-library/react'
import fetchMock from 'fetch-mock'
import LeaveModal from '../../../../../../frontend/js/features/settings/components/leave/modal'
describe('<LeaveModal />', function () {
beforeEach(function () {
window.metaAttributesCache = new Map()
window.metaAttributesCache.set('ol-userDefaultEmail', 'foo@bar.com')
})
afterEach(function () {
window.metaAttributesCache = new Map()
fetchMock.reset()
})
it('closes modal on cancel', async function () {
const handleClose = sinon.stub()
render(<LeaveModal isOpen handleClose={handleClose} />)
const cancelButton = screen.getByRole('button', {
name: 'Cancel',
})
fireEvent.click(cancelButton)
sinon.assert.calledOnce(handleClose)
})
it('does not close modal while in flight', async function () {
fetchMock.post('/user/delete', new Promise(() => {}))
const handleClose = sinon.stub()
render(<LeaveModal isOpen handleClose={handleClose} />)
fillValidForm()
const deleteButton = screen.getByRole('button', {
name: 'Delete',
})
fireEvent.click(deleteButton)
await waitFor(() => {
screen.getByRole('button', {
name: 'Deleting…',
})
})
const cancelButton = screen.getByRole('button', {
name: 'Cancel',
})
fireEvent.click(cancelButton)
sinon.assert.notCalled(handleClose)
})
})
function fillValidForm() {
fireEvent.change(screen.getByLabelText('Email'), {
target: { value: 'foo@bar.com' },
})
fireEvent.change(screen.getByLabelText('Password'), {
target: { value: 'foobar' },
})
fireEvent.click(screen.getByLabelText(/I understand/))
}