first commit

This commit is contained in:
2025-04-24 13:11:28 +08:00
commit ff9c54d5e4
5960 changed files with 834111 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
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'
import getMeta from '@/utils/meta'
describe('<LeaveModalContent />', function () {
beforeEach(function () {
Object.assign(getMeta('ol-ExposedSettings'), { isOverleaf: true })
window.metaAttributesCache.set('ol-hasPassword', true)
})
afterEach(function () {
fetchMock.removeRoutes().clearHistory()
})
it('disable delete button if form is not valid', function () {
render(
<LeaveModalContent
handleHide={() => {}}
inFlight={false}
setInFlight={() => {}}
/>
)
screen.getByLabelText('Email')
const deleteButton = screen.getByRole('button', {
name: 'Delete',
})
expect(deleteButton.hasAttribute('disabled')).to.be.true
})
it('shows no password message', function () {
window.metaAttributesCache.set('ol-isSaas', true)
window.metaAttributesCache.set('ol-hasPassword', false)
render(
<LeaveModalContent
handleHide={() => {}}
inFlight={false}
setInFlight={() => {}}
/>
)
const link = screen.getByRole('link', {
name: 'Please use the password reset form to set a password before deleting your account',
})
expect(link.getAttribute('href')).to.equal('/user/password/reset')
})
})

View File

@@ -0,0 +1,196 @@
import { expect } from 'chai'
import sinon from 'sinon'
import { fireEvent, screen, render, waitFor } from '@testing-library/react'
import fetchMock, { type FetchMock } from 'fetch-mock'
import LeaveModalForm from '../../../../../../frontend/js/features/settings/components/leave/modal-form'
import * as useLocationModule from '../../../../../../frontend/js/shared/hooks/use-location'
import getMeta from '@/utils/meta'
describe('<LeaveModalForm />', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-usersEmail', 'foo@bar.com')
Object.assign(getMeta('ol-ExposedSettings'), { isOverleaf: true })
})
afterEach(function () {
fetchMock.removeRoutes().clearHistory()
})
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: sinon.SinonStub
let setIsFormValid: sinon.SinonStub
let deleteMock: FetchMock
let assignStub: sinon.SinonStub
beforeEach(function () {
setInFlight = sinon.stub()
setIsFormValid = sinon.stub()
deleteMock = fetchMock.post('/user/delete', 200)
assignStub = sinon.stub()
this.locationStub = sinon.stub(useLocationModule, 'useLocation').returns({
assign: assignStub,
replace: sinon.stub(),
reload: sinon.stub(),
setHash: sinon.stub(),
})
Object.assign(getMeta('ol-ExposedSettings'), { isOverleaf: true })
})
afterEach(function () {
fetchMock.removeRoutes().clearHistory()
this.locationStub.restore()
})
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.callHistory.called()).to.be.true
await waitFor(() => {
sinon.assert.calledTwice(setInFlight)
sinon.assert.calledWithMatch(setInFlight, false)
sinon.assert.calledOnce(assignStub)
sinon.assert.calledWith(assignStub, '/')
})
})
it('with invalid form', async function () {
render(
<LeaveModalForm
setInFlight={setInFlight}
isFormValid={false}
setIsFormValid={setIsFormValid}
/>
)
fireEvent.submit(screen.getByLabelText('Email'))
expect(deleteMock.callHistory.called()).to.be.false
sinon.assert.notCalled(setInFlight)
})
})
it('handles credentials error without Saas tip', async function () {
Object.assign(getMeta('ol-ExposedSettings'), { isOverleaf: false })
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 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/)
})
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.'
)
})
})
})

View File

@@ -0,0 +1,66 @@
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'
import getMeta from '@/utils/meta'
describe('<LeaveModal />', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-usersEmail', 'foo@bar.com')
Object.assign(getMeta('ol-ExposedSettings'), { isOverleaf: true })
window.metaAttributesCache.set('ol-hasPassword', true)
})
afterEach(function () {
fetchMock.removeRoutes().clearHistory()
})
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/))
}