diff --git a/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.jsx b/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.jsx deleted file mode 100644 index 8befef1a18..0000000000 --- a/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import PropTypes from 'prop-types' - -import Card from 'old_ui/Card' -import A from 'ui/A' - -import ErasePersonalAccountButton from './ErasePersonalAccountButton' - -function EraseSection({ isPersonalSettings }) { - if (isPersonalSettings) { - return ( -
-
-

Remove my account content

-

Erase all my personal content and personal projects.

-
- -
- ) - } - return ( -

- Erase all my organization content and projects.{' '} - Contact support -

- ) -} - -EraseSection.propTypes = { - isPersonalSettings: PropTypes.bool, -} - -function DeletionCard({ isPersonalSettings }) { - return ( -
-

Delete account

- - - -
- ) -} - -DeletionCard.propTypes = { - isPersonalSettings: PropTypes.bool.isRequired, -} - -export default DeletionCard diff --git a/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.spec.jsx b/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.spec.jsx deleted file mode 100644 index 424bcb43a5..0000000000 --- a/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.spec.jsx +++ /dev/null @@ -1,240 +0,0 @@ -import { render, screen, waitFor } from 'custom-testing-library' - -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import userEvent from '@testing-library/user-event' -import { rest } from 'msw' -import { setupServer } from 'msw/node' -import { MemoryRouter, Route } from 'react-router-dom' - -import { useAddNotification } from 'services/toastNotification' - -import DeletionCard from './DeletionCard' - -jest.mock('services/toastNotification') -jest.mock('js-cookie') - -const server = setupServer() - -beforeAll(() => { - server.listen() -}) -afterEach(() => { - queryClient.clear() - server.resetHandlers() -}) -afterAll(() => { - server.close() -}) - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - suspense: false, - retry: false, - }, - }, -}) - -const wrapper = ({ children }) => ( - - - {children} - - -) - -describe('DeletionCard', () => { - function setup( - { failMutation = false } = { - failMutation: false, - } - ) { - const user = userEvent.setup() - const mutate = jest.fn() - const addNotification = jest.fn() - - useAddNotification.mockReturnValue(addNotification) - - server.use( - rest.delete(`/internal/gh/codecov/account-details/`, (req, res, ctx) => { - mutate() - - if (failMutation) { - return res(ctx.status(500)) - } - - return res(ctx.status(200), null) - }) - ) - - return { mutate, addNotification, user } - } - - describe('when rendered for organization', () => { - beforeEach(() => setup()) - - it('renders the copy for organization', () => { - render(, { - wrapper, - }) - - const EraseOrgContent = screen.getByText( - /Erase all my organization content and projects/ - ) - expect(EraseOrgContent).toBeInTheDocument() - }) - - it('has a link to the support page', () => { - render(, { - wrapper, - }) - - const contactSupport = screen.getByRole('link', { - name: /contact support/i, - }) - expect(contactSupport).toBeInTheDocument() - }) - }) - - describe('when rendering for individual', () => { - beforeEach(setup) - - it('renders the copy for individual', () => { - render(, { - wrapper, - }) - - const eraseAllContent = screen.getByText( - /erase all my personal content and personal projects\./i - ) - expect(eraseAllContent).toBeInTheDocument() - }) - - it('has a link to the support page', () => { - render(, { - wrapper, - }) - - const eraseAccount = screen.getByRole('button', { - name: /erase account/i, - }) - expect(eraseAccount).toBeInTheDocument() - }) - }) - - describe('when clicking on the button to erase', () => { - it('opens the modal with warning', async () => { - const { user } = setup() - render(, { - wrapper, - }) - - const eraseAccount = screen.getByRole('button', { - name: /erase account/i, - }) - await user.click(eraseAccount) - - const areYouSure = screen.getByRole('heading', { - name: /are you sure\?/i, - }) - expect(areYouSure).toBeInTheDocument() - }) - - describe('when clicking Cancel', () => { - beforeEach(() => {}) - - it('closes the modal', async () => { - const { user } = setup() - render(, { - wrapper, - }) - - const eraseAccount = screen.getByRole('button', { - name: /erase account/i, - }) - await user.click(eraseAccount) - - const cancel = screen.getByRole('button', { name: /Cancel/ }) - await user.click(cancel) - - const areYouSure = screen.queryByRole('heading', { - name: /are you sure\?/i, - }) - expect(areYouSure).not.toBeInTheDocument() - }) - }) - - describe('when clicking Close icon', () => { - it('closes the modal', async () => { - const { user } = setup() - render(, { - wrapper, - }) - - const eraseAccount = screen.getByRole('button', { - name: /erase account/i, - }) - await user.click(eraseAccount) - - const close = screen.getByRole('button', { name: /Close/ }) - await user.click(close) - - const areYouSure = screen.queryByRole('heading', { - name: /are you sure\?/i, - }) - expect(areYouSure).not.toBeInTheDocument() - }) - }) - - describe('when confirming', () => { - it('calls the mutation', async () => { - const { mutate, user } = setup() - render(, { - wrapper, - }) - - const eraseAccount = await screen.findByRole('button', { - name: /Erase account/, - }) - await user.click(eraseAccount) - - const eraseMyAccount = await screen.findByRole('button', { - name: /Erase my account/, - }) - await user.click(eraseMyAccount) - - await waitFor(() => queryClient.isFetching) - await waitFor(() => expect(queryClient.isFetching()).toBeFalsy()) - await waitFor(() => expect(mutate).toHaveBeenCalled()) - }) - - describe('when the mutation fails', () => { - it('adds an error notification', async () => { - const { user, addNotification } = setup({ - failMutation: true, - }) - render(, { - wrapper, - }) - - const eraseAccount = await screen.findByRole('button', { - name: /erase account/i, - }) - await user.click(eraseAccount) - - const eraseMyAccount = await screen.findByRole('button', { - name: /Erase my account/, - }) - await user.click(eraseMyAccount) - - await waitFor(() => - expect(addNotification).toHaveBeenCalledWith({ - type: 'error', - text: 'Something went wrong', - }) - ) - }) - }) - }) - }) -}) diff --git a/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.spec.tsx b/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.spec.tsx new file mode 100644 index 0000000000..be4662087e --- /dev/null +++ b/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.spec.tsx @@ -0,0 +1,41 @@ +import { render, screen } from '@testing-library/react' +import { MemoryRouter, Route } from 'react-router-dom' + +import DeletionCard from './DeletionCard' + +const wrapper: React.FC = ({ children }) => ( + + {children} + +) + +describe('DeletionCard', () => { + it('renders header', () => { + render(, { wrapper }) + + const header = screen.getByRole('heading', { name: 'Delete account' }) + expect(header).toBeInTheDocument() + }) + + describe('when isPersonalSettings is true', () => { + it('renders account deletion message', () => { + render(, { wrapper }) + + const message = screen.getByText( + /Erase all my personal content and projects./ + ) + expect(message).toBeInTheDocument() + }) + }) + + describe('when isPersonalSettings is false', () => { + it('renders organization deletion message', () => { + render(, { wrapper }) + + const message = screen.getByText( + /Erase all my organization content and projects./ + ) + expect(message).toBeInTheDocument() + }) + }) +}) diff --git a/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.tsx b/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.tsx new file mode 100644 index 0000000000..da9cb9d21f --- /dev/null +++ b/src/pages/AccountSettings/tabs/Admin/DeletionCard/DeletionCard.tsx @@ -0,0 +1,45 @@ +import Card from 'old_ui/Card' +import A from 'ui/A' + +interface EraseSectionProps { + isPersonalSettings: boolean +} + +function EraseSection({ isPersonalSettings }: EraseSectionProps) { + if (isPersonalSettings) { + return ( +

+ Erase all my personal content and projects.{' '} + + Contact support + +

+ ) + } + + return ( +

+ Erase all my organization content and projects.{' '} + + Contact support + +

+ ) +} + +interface DeletionCardProps { + isPersonalSettings: boolean +} + +function DeletionCard({ isPersonalSettings }: DeletionCardProps) { + return ( +
+

Delete account

+ + + +
+ ) +} + +export default DeletionCard diff --git a/src/pages/AccountSettings/tabs/Admin/DeletionCard/ErasePersonalAccountButton.jsx b/src/pages/AccountSettings/tabs/Admin/DeletionCard/ErasePersonalAccountButton.jsx deleted file mode 100644 index e36b24f00f..0000000000 --- a/src/pages/AccountSettings/tabs/Admin/DeletionCard/ErasePersonalAccountButton.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useState } from 'react' -import { useParams } from 'react-router-dom' - -import Modal from 'old_ui/Modal' -import { useEraseAccount } from 'services/account' -import { useAddNotification } from 'services/toastNotification' -import Button from 'ui/Button' - -function ErasePersonalAccountButton() { - const { provider, owner } = useParams() - const { mutate, isLoading } = useEraseAccount({ provider, owner }) - const [isModalOpen, setIsModalOpen] = useState(false) - const addToast = useAddNotification() - - function eraseAccount() { - mutate(null, { - onError: () => - addToast({ - type: 'error', - text: 'Something went wrong', - }), - }) - } - - return ( - <> - - setIsModalOpen(false)} - title="Are you sure?" - > -

Erasing your account will:

-
    -
  • - This will delete all your session data, oauth token, email and other - personal data -
  • -
  • This will delete all your personal repositories
  • -
  • This will NOT touch organization information
  • -
-
- - -
-
- - ) -} - -export default ErasePersonalAccountButton diff --git a/src/pages/AccountSettings/tabs/Admin/DeletionCard/index.js b/src/pages/AccountSettings/tabs/Admin/DeletionCard/index.ts similarity index 100% rename from src/pages/AccountSettings/tabs/Admin/DeletionCard/index.js rename to src/pages/AccountSettings/tabs/Admin/DeletionCard/index.ts diff --git a/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.jsx b/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.jsx deleted file mode 100644 index 0635c35b7a..0000000000 --- a/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import PropTypes from 'prop-types' - -import Card from 'old_ui/Card' - -import ErasePersonalAccountButton from './ErasePersonalAccountButton' - -function DeletionCard({ provider, owner }) { - return ( - -

- Delete account -

- <> -

- Erase all my personal content and personal projects. -

- - -
- ) -} - -DeletionCard.propTypes = { - provider: PropTypes.string.isRequired, - owner: PropTypes.string.isRequired, -} - -export default DeletionCard diff --git a/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.spec.jsx b/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.spec.jsx deleted file mode 100644 index c1bfa0a2c8..0000000000 --- a/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.spec.jsx +++ /dev/null @@ -1,234 +0,0 @@ -import { render, screen, waitFor } from 'custom-testing-library' - -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import userEvent from '@testing-library/user-event' -import { rest } from 'msw' -import { setupServer } from 'msw/node' -import { MemoryRouter } from 'react-router-dom' - -import { useAddNotification } from 'services/toastNotification' - -import DeletionCard from './DeletionCard' - -jest.mock('services/toastNotification') - -const queryClient = new QueryClient({ - defaultOptions: { queries: { retry: false } }, - logger: { - error: () => {}, - }, -}) -const server = setupServer() - -beforeAll(() => { - server.listen() -}) -beforeEach(() => { - queryClient.clear() - server.resetHandlers() -}) -afterAll(() => { - server.close() -}) - -const wrapper = ({ children }) => ( - - {children} - -) - -describe('DeletionCard', () => { - function setup({ returnError = false } = { returnError: false }) { - const user = userEvent.setup() - const addNotification = jest.fn() - - useAddNotification.mockReturnValue(addNotification) - server.use( - rest.delete('/internal/gh/codecov/account-details/', (req, res, ctx) => { - if (returnError) { - return res(ctx.status(500)) - } - return res(ctx.status(200)) - }) - ) - - return { addNotification, user } - } - - describe('when rendering for individual', () => { - beforeEach(() => setup()) - - it('renders the copy for individual', () => { - render( - , - { wrapper } - ) - - expect( - screen.getByText( - /erase all my personal content and personal projects\./i - ) - ).toBeInTheDocument() - }) - - it('has a link to the support page', () => { - render( - , - { wrapper } - ) - - expect( - screen.getByRole('button', { - name: /erase account/i, - }) - ).toBeInTheDocument() - }) - }) - - describe('when clicking on the button to erase', () => { - it('opens the modal with warning', async () => { - const { user } = setup() - render( - , - { wrapper } - ) - - const eraseButton = await screen.findByRole('button', { - name: /erase account/i, - }) - await user.click(eraseButton) - - const confirmationButton = await screen.findByRole('heading', { - name: /are you sure\?/i, - }) - expect(confirmationButton).toBeInTheDocument() - }) - - describe('when clicking Cancel', () => { - it('closes the modal', async () => { - const { user } = setup() - render( - , - { wrapper } - ) - - const eraseButton = await screen.findByRole('button', { - name: /erase account/i, - }) - await user.click(eraseButton) - - const cancelButton = await screen.findByRole('button', { - name: /Cancel/, - }) - await user.click(cancelButton) - - const confirmationButton = screen.queryByRole('heading', { - name: /are you sure\?/i, - }) - expect(confirmationButton).not.toBeInTheDocument() - }) - }) - - describe('when clicking Close icon', () => { - it('closes the modal', async () => { - const { user } = setup() - render( - , - { wrapper } - ) - - const eraseButton = await screen.findByRole('button', { - name: /erase account/i, - }) - await user.click(eraseButton) - - const closeButton = await screen.findByLabelText('Close') - await user.click(closeButton) - - const heading = screen.queryByRole('heading', { - name: /are you sure\?/i, - }) - expect(heading).not.toBeInTheDocument() - }) - }) - - describe('when confirming', () => { - it('calls the mutation', async () => { - const { user } = setup() - render( - , - { wrapper } - ) - - const erase1Button = await screen.findByRole('button', { - name: /erase account/i, - }) - await user.click(erase1Button) - - const eraseButton = await screen.findByRole('button', { - name: /Erase my account/, - }) - await user.click(eraseButton) - - await waitFor(() => queryClient.isFetching) - await waitFor(() => expect(queryClient.isFetching()).toBeFalsy()) - }) - }) - - describe('when the mutation fails', () => { - it('adds an error notification', async () => { - const { user } = setup() - const { addNotification } = setup({ returnError: true }) - render( - , - { wrapper } - ) - - const erase1Button = await screen.findByRole('button', { - name: /erase account/i, - }) - await user.click(erase1Button) - - const eraseButton = await screen.findByRole('button', { - name: /Erase my account/, - }) - await user.click(eraseButton) - - await waitFor(() => - expect(addNotification).toHaveBeenCalledWith({ - type: 'error', - text: 'Something went wrong', - }) - ) - }) - }) - }) -}) diff --git a/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.spec.tsx b/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.spec.tsx new file mode 100644 index 0000000000..d607588661 --- /dev/null +++ b/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.spec.tsx @@ -0,0 +1,28 @@ +import { render, screen } from '@testing-library/react' +import { MemoryRouter, Route } from 'react-router-dom' + +import DeletionCard from './DeletionCard' + +const wrapper: React.FC = ({ children }) => ( + + {children} + +) + +describe('DeletionCard', () => { + it('renders header', () => { + render(, { wrapper }) + + const header = screen.getByRole('heading', { name: 'Delete account' }) + expect(header).toBeInTheDocument() + }) + + it('renders account deletion message', () => { + render(, { wrapper }) + + const message = screen.getByText( + /Erase all my personal content and projects./ + ) + expect(message).toBeInTheDocument() + }) +}) diff --git a/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.tsx b/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.tsx new file mode 100644 index 0000000000..1440fe5ddf --- /dev/null +++ b/src/pages/AccountSettings/tabs/Profile/DeletionCard/DeletionCard.tsx @@ -0,0 +1,24 @@ +import Card from 'old_ui/Card' +import A from 'ui/A' + +function DeletionCard() { + return ( +
+

Delete account

+ +

+ Erase all my personal content and projects.{' '} + + Contact support + +

+
+
+ ) +} + +export default DeletionCard diff --git a/src/pages/AccountSettings/tabs/Profile/DeletionCard/ErasePersonalAccountButton.jsx b/src/pages/AccountSettings/tabs/Profile/DeletionCard/ErasePersonalAccountButton.jsx deleted file mode 100644 index 4ef095ef61..0000000000 --- a/src/pages/AccountSettings/tabs/Profile/DeletionCard/ErasePersonalAccountButton.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import PropTypes from 'prop-types' -import { useState } from 'react' - -import { useEraseAccount } from 'services/account' -import { useAddNotification } from 'services/toastNotification' -import Button from 'ui/Button' -import Modal from 'ui/Modal' - -function ErasePersonalAccountButton({ provider, owner }) { - const { mutate, isLoading } = useEraseAccount({ provider, owner }) - const [isModalOpen, setIsModalOpen] = useState(false) - const addToast = useAddNotification() - - function eraseAccount() { - mutate(null, { - onError: () => { - addToast({ - type: 'error', - text: 'Something went wrong', - }) - }, - }) - } - - return ( - <> - - setIsModalOpen(false)} - title="Are you sure?" - body={ - <> -

Erasing your account will:

-
    -
  • - This will delete all your session data, oauth token, email and - other personal data -
  • -
  • This will delete all your personal repositories
  • -
  • This will NOT touch organization information
  • -
-
- - -
- - } - /> - - ) -} - -ErasePersonalAccountButton.propTypes = { - provider: PropTypes.string.isRequired, - owner: PropTypes.string.isRequired, -} - -export default ErasePersonalAccountButton diff --git a/src/pages/AccountSettings/tabs/Profile/DeletionCard/index.js b/src/pages/AccountSettings/tabs/Profile/DeletionCard/index.ts similarity index 100% rename from src/pages/AccountSettings/tabs/Profile/DeletionCard/index.js rename to src/pages/AccountSettings/tabs/Profile/DeletionCard/index.ts diff --git a/src/pages/AccountSettings/tabs/Profile/Profile.jsx b/src/pages/AccountSettings/tabs/Profile/Profile.jsx index bbff9ded68..8122f9ecd8 100644 --- a/src/pages/AccountSettings/tabs/Profile/Profile.jsx +++ b/src/pages/AccountSettings/tabs/Profile/Profile.jsx @@ -24,11 +24,7 @@ function Profile({ provider, owner }) { {currentUser?.isAdmin && } - + ) }