{/* @ts-expect-error - disable because of non-ts component and type mismatch */}
diff --git a/src/ui/Avatar/Avatar.jsx b/src/ui/Avatar/Avatar.jsx
index cb458ee00e..89ba5dcdc8 100644
--- a/src/ui/Avatar/Avatar.jsx
+++ b/src/ui/Avatar/Avatar.jsx
@@ -1,15 +1,19 @@
-import cs from 'classnames'
import PropTypes from 'prop-types'
import { useImage } from 'services/image'
+import { cn } from 'shared/utils/cn'
import AvatarSVG from './AvatarSVG'
-let baseClasses = 'rounded-full h-6 w-6 flex items-center justify-center'
-let borderedClasses = 'border-ds-grey-secondary border-2'
+const baseClasses = 'rounded-full h-6 w-6 flex items-center justify-center'
+const borderClasses = {
+ none: '',
+ light: 'border-ds-gray-secondary border-2',
+ dark: 'border-ds-gray-octonary border-2',
+}
-function Avatar({ user, bordered, ariaLabel }) {
- const classes = cs(baseClasses, bordered ? borderedClasses : '')
+function Avatar({ user, border = 'none', ariaLabel }) {
+ const classes = cn(baseClasses, borderClasses[border])
const { src, error, isLoading } = useImage({
src: user?.avatarUrl,
@@ -36,7 +40,7 @@ Avatar.propTypes = {
username: PropTypes.string.isRequired,
avatarUrl: PropTypes.string,
}),
- bordered: PropTypes.bool,
+ border: PropTypes.oneOf(['light', 'dark', 'none']),
ariaLabel: PropTypes.string,
}
diff --git a/src/ui/ContextSwitcher/ContextSwitcher.jsx b/src/ui/ContextSwitcher/ContextSwitcher.jsx
index 43cee1ed8e..71c0c4461c 100644
--- a/src/ui/ContextSwitcher/ContextSwitcher.jsx
+++ b/src/ui/ContextSwitcher/ContextSwitcher.jsx
@@ -53,7 +53,7 @@ function ContextItem({ context, defaultOrgUsername, setToggle, owner }) {
mutate({ username: orgUsername })
}}
>
-
+
{orgUsername}
@@ -146,7 +146,7 @@ function ContextSwitcher({
aria-expanded={toggle}
onClick={() => setToggle((toggle) => !toggle)}
>
-
+
{activeContext?.username}
{
name: org.username,
value: (
),
From 4a04934d3d43aa180a08b90fe0f32a697cddae3a Mon Sep 17 00:00:00 2001
From: Rohit Vinnakota
<148245014+rohitvinnakota-codecov@users.noreply.github.com>
Date: Fri, 5 Jul 2024 10:15:39 -0400
Subject: [PATCH 2/7] Update Repo queries to use GQL (#2943)
---
.../EraseRepoContent/EraseRepoContent.jsx | 4 +-
.../EraseRepoContent.spec.jsx | 82 +++++++++---
.../EraseRepoContent/useEraseContent.js | 24 ----
.../RepoUploadToken/RepoUploadToken.jsx | 21 +---
.../RepoUploadToken/RepoUploadToken.spec.jsx | 41 +++---
.../YamlTab/SecretString/SecretString.jsx | 9 +-
.../SecretString/SecretString.spec.jsx | 69 +++++++----
.../SecretString/useGenerateSecretSring.js | 13 +-
src/services/repo/hooks.spec.tsx | 16 ++-
src/services/repo/useEncodeString.js | 26 ----
src/services/repo/useEncodeString.spec.js | 60 ---------
src/services/repo/useEncodeString.spec.tsx | 113 +++++++++++++++++
src/services/repo/useEncodeString.tsx | 58 +++++++++
src/services/repo/useEraseRepoContent.js | 32 -----
src/services/repo/useEraseRepoContent.tsx | 71 +++++++++++
.../useRegenerateRepoUploadToken.js | 30 -----
.../useRegenerateRepoUploadToken.spec.js | 90 --------------
.../useRegenerateRepoUploadToken.spec.tsx | 117 ++++++++++++++++++
.../useRegenerateRepoUploadToken.tsx | 71 +++++++++++
19 files changed, 587 insertions(+), 360 deletions(-)
delete mode 100644 src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/useEraseContent.js
delete mode 100644 src/services/repo/useEncodeString.js
delete mode 100644 src/services/repo/useEncodeString.spec.js
create mode 100644 src/services/repo/useEncodeString.spec.tsx
create mode 100644 src/services/repo/useEncodeString.tsx
delete mode 100644 src/services/repo/useEraseRepoContent.js
create mode 100644 src/services/repo/useEraseRepoContent.tsx
delete mode 100644 src/services/repoUploadToken/useRegenerateRepoUploadToken.js
delete mode 100644 src/services/repoUploadToken/useRegenerateRepoUploadToken.spec.js
create mode 100644 src/services/repoUploadToken/useRegenerateRepoUploadToken.spec.tsx
create mode 100644 src/services/repoUploadToken/useRegenerateRepoUploadToken.tsx
diff --git a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/EraseRepoContent.jsx b/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/EraseRepoContent.jsx
index d062ce5761..7bf23123d5 100644
--- a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/EraseRepoContent.jsx
+++ b/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/EraseRepoContent.jsx
@@ -1,10 +1,10 @@
import PropTypes from 'prop-types'
import { useState } from 'react'
+import { useEraseRepoContent } from 'services/repo'
import Button from 'ui/Button'
import EraseRepoContentModal from './EraseRepoContentModal'
-import useEraseContent from './useEraseContent'
function EraseRepoButton({ isLoading, setShowModal }) {
if (isLoading) {
@@ -33,7 +33,7 @@ EraseRepoButton.propTypes = {
function EraseRepoContent() {
const [showModal, setShowModal] = useState(false)
- const { eraseRepoContent, isLoading } = useEraseContent()
+ const { mutate: eraseRepoContent, isLoading } = useEraseRepoContent()
return (
diff --git a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/EraseRepoContent.spec.jsx b/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/EraseRepoContent.spec.jsx
index bcc0fef3a9..9778b3cf46 100644
--- a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/EraseRepoContent.spec.jsx
+++ b/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/EraseRepoContent.spec.jsx
@@ -2,7 +2,7 @@ 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 { graphql } from 'msw'
import { setupServer } from 'msw/node'
import { MemoryRouter, Route } from 'react-router-dom'
@@ -37,9 +37,34 @@ const wrapper = ({ children }) => (
)
+
+const mockErrorResponse = {
+ eraseRepository: {
+ error: {
+ __typename: 'ValidationError',
+ message: 'error',
+ },
+ },
+}
+
+const mockUnauthorized = {
+ eraseRepository: {
+ error: {
+ __typename: 'UnauthorizedError',
+ message: 'UnauthorizedError error',
+ },
+ },
+}
+
+const mockResponse = {
+ eraseRepository: {
+ data: null,
+ },
+}
+
describe('EraseRepoContent', () => {
function setup(
- { failedMutation = false, isLoading = false } = {
+ { failedMutation = false, isLoading = false, unauthorized = false } = {
failedMutation: false,
isLoading: false,
}
@@ -50,22 +75,20 @@ describe('EraseRepoContent', () => {
useAddNotification.mockReturnValue(addNotification)
server.use(
- rest.patch(
- '/internal/github/codecov/repos/codecov-client/erase/',
- (req, res, ctx) => {
- mutate()
-
- if (isLoading) {
- // https://cathalmacdonnacha.com/mocking-error-empty-and-loading-states-with-msw
- return res(ctx.status(200), ctx.json({}), ctx.delay(100))
- }
-
- if (failedMutation) {
- return res(ctx.status(500))
- }
- return res(ctx.status(200))
+ graphql.mutation('EraseRepository', (req, res, ctx) => {
+ mutate()
+ if (isLoading) {
+ // https://cathalmacdonnacha.com/mocking-error-empty-and-loading-states-with-msw
+ return res(ctx.status(200), ctx.data(mockResponse), ctx.delay(100))
}
- )
+ if (failedMutation) {
+ return res(ctx.status(200), ctx.data(mockErrorResponse))
+ }
+ if (unauthorized) {
+ return res(ctx.status(200), ctx.data(mockUnauthorized))
+ }
+ return res(ctx.status(200), ctx.data(mockResponse))
+ })
)
return { user, mutate, addNotification }
@@ -268,4 +291,29 @@ describe('EraseRepoContent', () => {
)
})
})
+
+ describe('when user is unauthorized', () => {
+ it('adds an error notification', async () => {
+ const { user, mutate, addNotification } = setup({ unauthorized: true })
+ render(, { wrapper })
+
+ const eraseButton = await screen.findByRole('button', {
+ name: /Erase Content/,
+ })
+ await user.click(eraseButton)
+
+ const modalEraseButton = await screen.findByRole('button', {
+ name: /Erase Content/,
+ })
+ await user.click(modalEraseButton)
+
+ await waitFor(() => expect(mutate).toHaveBeenCalled())
+ await waitFor(() =>
+ expect(addNotification).toHaveBeenCalledWith({
+ type: 'error',
+ text: "We were unable to erase this repo's content",
+ })
+ )
+ })
+ })
})
diff --git a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/useEraseContent.js b/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/useEraseContent.js
deleted file mode 100644
index 3b6a07a9ff..0000000000
--- a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/DangerZone/EraseRepoContent/useEraseContent.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useEraseRepoContent } from 'services/repo'
-import { useAddNotification } from 'services/toastNotification'
-
-export default function useEraseContent() {
- const addToast = useAddNotification()
- const { mutate, ...rest } = useEraseRepoContent()
-
- async function eraseRepoContent() {
- mutate(null, {
- onError: () =>
- addToast({
- type: 'error',
- text: "We were unable to erase this repo's content",
- }),
- onSuccess: () =>
- addToast({
- type: 'success',
- text: 'Repo coverage content erased successfully',
- }),
- })
- }
-
- return { eraseRepoContent, ...rest }
-}
diff --git a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/Tokens/RepoUploadToken/RepoUploadToken.jsx b/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/Tokens/RepoUploadToken/RepoUploadToken.jsx
index 9db1b231fe..a98241a4a6 100644
--- a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/Tokens/RepoUploadToken/RepoUploadToken.jsx
+++ b/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/Tokens/RepoUploadToken/RepoUploadToken.jsx
@@ -2,7 +2,6 @@ import PropTypes from 'prop-types'
import { useState } from 'react'
import { useRegenerateRepoUploadToken } from 'services/repoUploadToken'
-import { useAddNotification } from 'services/toastNotification'
import A from 'ui/A'
import Button from 'ui/Button'
import TokenWrapper from 'ui/TokenWrapper'
@@ -15,25 +14,13 @@ const TokenFormatEnum = Object.freeze({
})
function useRegenerateToken() {
- const addToast = useAddNotification()
- const { mutate, ...rest } = useRegenerateRepoUploadToken()
-
- async function regenerateToken() {
- mutate(null, {
- onError: () =>
- addToast({
- type: 'error',
- text: 'Something went wrong',
- }),
- })
- }
-
- return { regenerateToken, ...rest }
+ const { mutate, isLoading } = useRegenerateRepoUploadToken()
+ return { mutate, isLoading }
}
function RepoUploadToken({ uploadToken }) {
const [showModal, setShowModal] = useState(false)
- const { regenerateToken, isLoading } = useRegenerateToken()
+ const { mutate, isLoading } = useRegenerateToken()
if (!uploadToken) {
return null
@@ -69,7 +56,7 @@ function RepoUploadToken({ uploadToken }) {
setShowModal(false)}
- regenerateToken={regenerateToken}
+ regenerateToken={mutate}
isLoading={isLoading}
/>
diff --git a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/Tokens/RepoUploadToken/RepoUploadToken.spec.jsx b/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/Tokens/RepoUploadToken/RepoUploadToken.spec.jsx
index 6d1a45c2b4..d46368ff15 100644
--- a/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/Tokens/RepoUploadToken/RepoUploadToken.spec.jsx
+++ b/src/pages/RepoPage/SettingsTab/tabs/GeneralTab/Tokens/RepoUploadToken/RepoUploadToken.spec.jsx
@@ -2,7 +2,7 @@ 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 { graphql } from 'msw'
import { setupServer } from 'msw/node'
import { MemoryRouter, Route } from 'react-router-dom'
@@ -54,22 +54,30 @@ describe('RepoUploadToken', () => {
useAddNotification.mockReturnValue(addNotification)
server.use(
- rest.patch(
- `/internal/github/codecov/repos/codecov-client/regenerate-upload-token/`,
- (req, res, ctx) => {
- mutate(req)
- if (triggerError) {
- return res(ctx.status(500))
- } else {
- return res(
- ctx.status(200),
- ctx.json({
- data: { uploadToken },
- })
- )
- }
+ graphql.mutation('RegenerateRepositoryUploadToken', (req, res, ctx) => {
+ mutate(req.variables)
+ if (triggerError) {
+ return res(
+ ctx.status(200),
+ ctx.data({
+ regenerateRepositoryUploadToken: {
+ error: {
+ __typename: 'ValidationError',
+ },
+ },
+ })
+ )
}
- )
+
+ return res(
+ ctx.status(200),
+ ctx.data({
+ regenerateRepositoryUploadToken: {
+ value: 'test',
+ },
+ })
+ )
+ })
)
return { mutate, addNotification, user }
@@ -226,6 +234,7 @@ describe('RepoUploadToken', () => {
expect(addNotification).toHaveBeenCalledWith({
type: 'error',
text: 'Something went wrong',
+ disappearAfter: 10000,
})
)
})
diff --git a/src/pages/RepoPage/SettingsTab/tabs/YamlTab/SecretString/SecretString.jsx b/src/pages/RepoPage/SettingsTab/tabs/YamlTab/SecretString/SecretString.jsx
index 396154f64d..78ac78ef82 100644
--- a/src/pages/RepoPage/SettingsTab/tabs/YamlTab/SecretString/SecretString.jsx
+++ b/src/pages/RepoPage/SettingsTab/tabs/YamlTab/SecretString/SecretString.jsx
@@ -8,12 +8,15 @@ import GenerateSecretStringModal from './GenerateSecretStringModal'
import useGenerateSecretString from './useGenerateSecretSring'
function SecretString() {
- const { generateSecretString, data, isLoading } = useGenerateSecretString()
+ const {
+ generateSecretString,
+ data: generatedSecretString,
+ isLoading,
+ } = useGenerateSecretString()
const [showGenerateModal, setShowGenerateModal] = useState(false)
const [showCopyModal, setShowCopyModal] = useState(false)
-
- const value = data?.value
+ const value = generatedSecretString?.data?.encodeSecretString?.value
return (
server.listen())
+afterEach(() => server.resetHandlers())
+afterAll(() => server.close())
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
@@ -25,21 +31,39 @@ const wrapper = ({ children }) => (
)
describe('SecretString', () => {
- function setup(value = '') {
+ function setup(value = '', isError = false) {
const user = userEvent.setup()
- const mutate = jest.fn()
const addNotification = jest.fn()
+ const mutation = jest.fn()
useAddNotification.mockReturnValue(addNotification)
- useEncodeString.mockReturnValue({
- isLoading: false,
- mutate,
- data: {
- value,
- },
- })
+ server.use(
+ graphql.mutation('EncodeSecretString', (req, res, ctx) => {
+ mutation(req.variables)
+ if (isError) {
+ return res(
+ ctx.status(200),
+ ctx.data({
+ encodeSecretString: {
+ error: {
+ __typename: 'ValidationError',
+ },
+ },
+ })
+ )
+ }
+ return res(
+ ctx.status(200),
+ ctx.data({
+ encodeSecretString: {
+ value,
+ },
+ })
+ )
+ })
+ )
- return { mutate, addNotification, user }
+ return { mutation, addNotification, user }
}
describe('renders SecretString component', () => {
@@ -91,7 +115,7 @@ describe('SecretString', () => {
describe('when user clicks on Generate button', () => {
it('calls the mutation', async () => {
- const { user, mutate } = setup('test')
+ const { user, mutation } = setup('test')
render(, { wrapper })
const createNewSecret = screen.getByRole('button', {
@@ -105,8 +129,7 @@ describe('SecretString', () => {
const generate = screen.getByRole('button', { name: 'Generate' })
await user.click(generate)
-
- expect(mutate).toHaveBeenCalled()
+ await waitFor(() => expect(mutation).toHaveBeenCalled())
})
it('renders the new token', async () => {
@@ -124,7 +147,7 @@ describe('SecretString', () => {
const generate = screen.getByRole('button', { name: 'Generate' })
await user.click(generate)
- const newSecret = screen.getByText('New secret string')
+ const newSecret = await screen.findByText('New secret string')
expect(newSecret).toBeInTheDocument()
})
@@ -146,7 +169,7 @@ describe('SecretString', () => {
const generate = screen.getByRole('button', { name: 'Generate' })
await user.click(generate)
- const close = screen.getByRole('button', { name: 'Close' })
+ const close = await screen.findByRole('button', { name: 'Close' })
await user.click(close)
createNewSecret = screen.getByRole('button', {
@@ -159,7 +182,7 @@ describe('SecretString', () => {
describe('when mutation is not successful', () => {
it('calls the mutation', async () => {
- const { user, mutate } = setup('test')
+ const { user, mutation } = setup('test', true)
render(, { wrapper })
const createNewSecret = screen.getByRole('button', {
@@ -173,13 +196,11 @@ describe('SecretString', () => {
const generate = screen.getByRole('button', { name: 'Generate' })
await user.click(generate)
- mutate.mock.calls[0][1].onError()
-
- expect(mutate).toHaveBeenCalled()
+ expect(mutation).toHaveBeenCalled()
})
it('adds an error notification', async () => {
- const { user, mutate, addNotification } = setup('test')
+ const { user, addNotification } = setup('test', true)
render(, { wrapper })
const createNewSecret = screen.getByRole('button', {
@@ -193,8 +214,6 @@ describe('SecretString', () => {
const generate = screen.getByRole('button', { name: 'Generate' })
await user.click(generate)
- mutate.mock.calls[0][1].onError()
-
expect(addNotification).toHaveBeenCalledWith({
type: 'error',
text: 'We were unable to generate the secret string',
diff --git a/src/pages/RepoPage/SettingsTab/tabs/YamlTab/SecretString/useGenerateSecretSring.js b/src/pages/RepoPage/SettingsTab/tabs/YamlTab/SecretString/useGenerateSecretSring.js
index 1f745d4878..cc03a0c5c9 100644
--- a/src/pages/RepoPage/SettingsTab/tabs/YamlTab/SecretString/useGenerateSecretSring.js
+++ b/src/pages/RepoPage/SettingsTab/tabs/YamlTab/SecretString/useGenerateSecretSring.js
@@ -1,21 +1,10 @@
import { useEncodeString } from 'services/repo'
-import { useAddNotification } from 'services/toastNotification'
export default function useGenerateSecretString() {
- const addToast = useAddNotification()
const { mutate, ...rest } = useEncodeString()
async function generateSecretString({ value }) {
- mutate(
- { value },
- {
- onError: () =>
- addToast({
- type: 'error',
- text: 'We were unable to generate the secret string',
- }),
- }
- )
+ return mutate(value)
}
return { generateSecretString, ...rest }
diff --git a/src/services/repo/hooks.spec.tsx b/src/services/repo/hooks.spec.tsx
index 8cf8bca6ef..b0db4a0806 100644
--- a/src/services/repo/hooks.spec.tsx
+++ b/src/services/repo/hooks.spec.tsx
@@ -145,12 +145,16 @@ describe('useRepo', () => {
describe('useEraseRepoContent', () => {
function setup() {
server.use(
- rest.patch(
- `internal/github/codecov/repos/test/erase/`,
- (req, res, ctx) => {
- return res(ctx.status(200), ctx.json({}))
- }
- )
+ graphql.mutation('EraseRepository', (req, res, ctx) => {
+ return res(
+ ctx.status(200),
+ ctx.data({
+ eraseRepository: {
+ data: null,
+ },
+ })
+ )
+ })
)
}
diff --git a/src/services/repo/useEncodeString.js b/src/services/repo/useEncodeString.js
deleted file mode 100644
index a768505067..0000000000
--- a/src/services/repo/useEncodeString.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { useMutation } from '@tanstack/react-query'
-import { useParams } from 'react-router-dom'
-
-import Api from 'shared/api'
-import { providerToInternalProvider } from 'shared/utils'
-
-function getEncodeStringPath({ provider, owner, repo }) {
- return `/${provider}/${owner}/repos/${repo}/encode/`
-}
-
-function encodeString({ provider, owner, repo, value }) {
- const refactoredProvider = providerToInternalProvider(provider)
- const path = getEncodeStringPath({
- provider: refactoredProvider,
- owner,
- repo,
- })
- return Api.post({ path, provider: refactoredProvider, body: { value } })
-}
-
-export function useEncodeString() {
- const { provider, owner, repo } = useParams()
- return useMutation({
- mutationFn: ({ value }) => encodeString({ provider, owner, repo, value }),
- })
-}
diff --git a/src/services/repo/useEncodeString.spec.js b/src/services/repo/useEncodeString.spec.js
deleted file mode 100644
index c7112588c8..0000000000
--- a/src/services/repo/useEncodeString.spec.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
-import { renderHook, waitFor } from '@testing-library/react'
-import { rest } from 'msw'
-import { setupServer } from 'msw/node'
-import { MemoryRouter, Route } from 'react-router-dom'
-
-import { useEncodeString } from './useEncodeString'
-
-const server = setupServer()
-
-beforeAll(() => server.listen())
-afterEach(() => server.resetHandlers())
-afterAll(() => server.close())
-
-const queryClient = new QueryClient({
- defaultOptions: { queries: { retry: false } },
-})
-const wrapper = ({ children }) => (
-
-
- {children}
-
-
-)
-describe('useEncodeString', () => {
- function setup() {
- server.use(
- rest.post(
- `internal/github/codecov/repos/gazebo/encode/`,
- (req, res, ctx) => {
- return res(ctx.status(200), ctx.json())
- }
- )
- )
- }
-
- describe('when called', () => {
- beforeEach(() => {
- setup()
- })
-
- describe('when calling the mutation', () => {
- describe('when successful', () => {
- it('returns isSuccess true', async () => {
- const { result } = renderHook(() => useEncodeString(), {
- wrapper,
- })
-
- const data = { value: 'dummy' }
- result.current.mutate(data)
-
- await waitFor(() => result.current.isLoading)
- await waitFor(() => !result.current.isLoading)
-
- await waitFor(() => expect(result.current.isSuccess).toBeTruthy())
- })
- })
- })
- })
-})
diff --git a/src/services/repo/useEncodeString.spec.tsx b/src/services/repo/useEncodeString.spec.tsx
new file mode 100644
index 0000000000..dbb2e3938e
--- /dev/null
+++ b/src/services/repo/useEncodeString.spec.tsx
@@ -0,0 +1,113 @@
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import { renderHook, waitFor } from '@testing-library/react'
+import { graphql } from 'msw'
+import { setupServer } from 'msw/node'
+import { MemoryRouter, Route } from 'react-router-dom'
+
+import { useAddNotification } from 'services/toastNotification'
+
+import { useEncodeString } from './useEncodeString'
+
+const server = setupServer()
+jest.mock('services/toastNotification')
+
+beforeAll(() => server.listen())
+afterEach(() => server.resetHandlers())
+afterAll(() => server.close())
+
+const queryClient = new QueryClient({
+ defaultOptions: { queries: { retry: false } },
+})
+const wrapper: React.FC = ({ children }) => (
+
+
+ {children}
+
+
+)
+
+describe('useEncodeString', () => {
+ function setup({ isErrorResponse = false }) {
+ //@ts-ignore
+ const mockAddToast = jest.fn()
+
+ //@ts-ignore
+ useAddNotification.mockReturnValue(mockAddToast)
+ server.use(
+ graphql.mutation('EncodeSecretString', (req, res, ctx) => {
+ if (isErrorResponse) {
+ return res(
+ ctx.status(200),
+ ctx.data({
+ encodeSecretString: {
+ error: {
+ __typename: 'ValidationError',
+ },
+ },
+ })
+ )
+ }
+ return res(
+ ctx.status(200),
+ ctx.data({
+ encodeSecretString: {
+ value: 'encoded-string',
+ },
+ })
+ )
+ })
+ )
+ return { mockAddToast }
+ }
+
+ describe('when called', () => {
+ beforeEach(() => {
+ setup({})
+ })
+
+ describe('when calling the mutation', () => {
+ describe('when successful', () => {
+ it('returns isSuccess true', async () => {
+ const { result } = renderHook(() => useEncodeString(), {
+ wrapper,
+ })
+
+ result.current.mutate('dummy')
+
+ await waitFor(() => result.current.isLoading)
+ await waitFor(() => !result.current.isLoading)
+
+ await waitFor(() => expect(result.current.isSuccess).toBeTruthy())
+ const data = result.current.data
+ await waitFor(() =>
+ expect(data).toEqual({
+ data: {
+ encodeSecretString: {
+ value: 'encoded-string',
+ },
+ },
+ })
+ )
+ })
+ })
+
+ describe('on error', () => {
+ it('fires a toast', async () => {
+ const { mockAddToast } = setup({ isErrorResponse: true })
+
+ const { result } = renderHook(() => useEncodeString(), {
+ wrapper,
+ })
+
+ result.current.mutate('dummy')
+
+ await waitFor(() => result.current.isLoading)
+ await waitFor(() => !result.current.isLoading)
+
+ await waitFor(() => expect(result.current.isSuccess).toBeTruthy())
+ await waitFor(() => expect(mockAddToast).toHaveBeenCalled())
+ })
+ })
+ })
+ })
+})
diff --git a/src/services/repo/useEncodeString.tsx b/src/services/repo/useEncodeString.tsx
new file mode 100644
index 0000000000..3961314e10
--- /dev/null
+++ b/src/services/repo/useEncodeString.tsx
@@ -0,0 +1,58 @@
+import { useMutation } from '@tanstack/react-query'
+import { useParams } from 'react-router-dom'
+
+import { useAddNotification } from 'services/toastNotification'
+import Api from 'shared/api'
+
+const query = `
+ mutation EncodeSecretString($repoName: String!, $value: String!) {
+ encodeSecretString(input: { repoName: $repoName, value: $value }) {
+ value
+ error {
+ ... on ValidationError {
+ __typename
+ message
+ }
+ ... on UnauthenticatedError {
+ __typename
+ message
+ }
+ }
+ }
+ }
+`
+
+interface URLParams {
+ provider: string
+ owner: string
+ repo: string
+}
+
+export const useEncodeString = () => {
+ const { provider, owner, repo } = useParams()
+ const addToast = useAddNotification()
+ return useMutation({
+ mutationFn: (value: string) => {
+ return Api.graphqlMutation({
+ provider,
+ query,
+ variables: {
+ owner,
+ repoName: repo,
+ value,
+ },
+ mutationPath: 'EncodeSecretString',
+ })
+ },
+ onSuccess: ({ data }) => {
+ const error = data?.encodeSecretString?.error
+ if (error) {
+ addToast({
+ type: 'error',
+ text: `We were unable to generate the secret string`,
+ })
+ }
+ },
+ retry: false,
+ })
+}
diff --git a/src/services/repo/useEraseRepoContent.js b/src/services/repo/useEraseRepoContent.js
deleted file mode 100644
index 37ec53ff75..0000000000
--- a/src/services/repo/useEraseRepoContent.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useMutation, useQueryClient } from '@tanstack/react-query'
-import { useParams } from 'react-router-dom'
-
-import Api from 'shared/api'
-import { providerToInternalProvider } from 'shared/utils'
-
-function getPathEraseRepo({ provider, owner, repo }) {
- return `/${provider}/${owner}/repos/${repo}/erase/`
-}
-
-export function useEraseRepoContent() {
- const { provider, owner, repo } = useParams()
- const queryClient = useQueryClient()
- const refactoredProvider = providerToInternalProvider(provider)
- return useMutation({
- mutationFn: () => {
- const path = getPathEraseRepo({
- provider: refactoredProvider,
- owner,
- repo,
- })
-
- return Api.patch({
- provider: refactoredProvider,
- path,
- })
- },
- onSuccess: () => {
- queryClient.invalidateQueries(['GetRepo'])
- },
- })
-}
diff --git a/src/services/repo/useEraseRepoContent.tsx b/src/services/repo/useEraseRepoContent.tsx
new file mode 100644
index 0000000000..b932cea55f
--- /dev/null
+++ b/src/services/repo/useEraseRepoContent.tsx
@@ -0,0 +1,71 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import { useParams } from 'react-router-dom'
+
+import { useAddNotification } from 'services/toastNotification'
+import Api from 'shared/api'
+
+const query = `
+ mutation EraseRepository($repoName: String!) {
+ eraseRepository(input: { repoName: $repoName }) {
+ error {
+ ... on UnauthorizedError {
+ message
+ }
+ ... on ValidationError {
+ message
+ }
+ ... on UnauthenticatedError {
+ message
+ }
+ }
+ }
+ }
+`
+
+interface URLParams {
+ provider: string
+ owner: string
+ repo: string
+}
+
+export const useEraseRepoContent = () => {
+ const { provider, owner, repo } = useParams()
+ const queryClient = useQueryClient()
+ const addToast = useAddNotification()
+ return useMutation({
+ mutationFn: () => {
+ return Api.graphqlMutation({
+ provider,
+ query,
+ variables: {
+ owner,
+ repoName: repo,
+ },
+ mutationPath: 'eraseRepository',
+ })
+ },
+ onSuccess: ({ data }) => {
+ const error = data?.eraseRepository?.error
+ if (error) {
+ addToast({
+ type: 'error',
+ text: "We were unable to erase this repo's content",
+ })
+ } else {
+ addToast({
+ type: 'success',
+ text: 'Repo coverage content erased successfully',
+ })
+ }
+ queryClient.invalidateQueries(['GetRepo'])
+ queryClient.invalidateQueries(['GetRepoSettings'])
+ },
+ onError: () => {
+ addToast({
+ type: 'error',
+ text: "We were unable to erase this repo's content",
+ })
+ },
+ retry: false,
+ })
+}
diff --git a/src/services/repoUploadToken/useRegenerateRepoUploadToken.js b/src/services/repoUploadToken/useRegenerateRepoUploadToken.js
deleted file mode 100644
index 79b834f484..0000000000
--- a/src/services/repoUploadToken/useRegenerateRepoUploadToken.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { useMutation, useQueryClient } from '@tanstack/react-query'
-import { useParams } from 'react-router-dom/cjs/react-router-dom.min'
-
-import Api from 'shared/api'
-import { providerToInternalProvider } from 'shared/utils'
-
-function getRegenerateRepoTokenPath({ provider, owner, repo }) {
- return `/${provider}/${owner}/repos/${repo}/regenerate-upload-token/`
-}
-
-function regenerateRepoUploadToken({ provider, owner, repo }) {
- const refactoredProvider = providerToInternalProvider(provider)
- const path = getRegenerateRepoTokenPath({
- provider: refactoredProvider,
- owner,
- repo,
- })
- return Api.patch({ path, provider: refactoredProvider })
-}
-
-export function useRegenerateRepoUploadToken() {
- const { provider, owner, repo } = useParams()
- const queryClient = useQueryClient()
- return useMutation({
- mutationFn: () => regenerateRepoUploadToken({ provider, owner, repo }),
- onSuccess: () => {
- queryClient.invalidateQueries(['GetRepo'])
- },
- })
-}
diff --git a/src/services/repoUploadToken/useRegenerateRepoUploadToken.spec.js b/src/services/repoUploadToken/useRegenerateRepoUploadToken.spec.js
deleted file mode 100644
index c77dd5f1f4..0000000000
--- a/src/services/repoUploadToken/useRegenerateRepoUploadToken.spec.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
-import { renderHook, waitFor } from '@testing-library/react'
-import { rest } from 'msw'
-import { setupServer } from 'msw/node'
-import { MemoryRouter, Route } from 'react-router-dom'
-
-import { useRegenerateRepoUploadToken } from './useRegenerateRepoUploadToken'
-
-const repoDetails = {
- can_edit: true,
- can_view: true,
- latest_commit: {
- report: {
- files: [
- {
- name: 'src/App.js',
- totals: {
- files: 0,
- lines: 13,
- hits: 13,
- misses: 0,
- partials: 0,
- coverage: 100.0,
- branches: 0,
- methods: 10,
- sessions: 0,
- complexity: 0.0,
- complexity_total: 0.0,
- complexity_ratio: 0,
- diff: null,
- },
- },
- ],
- uploadToken: 'random',
- },
- },
-}
-
-const server = setupServer()
-
-beforeAll(() => server.listen())
-afterEach(() => server.resetHandlers())
-afterAll(() => server.close())
-
-const queryClient = new QueryClient({
- defaultOptions: { queries: { retry: false } },
-})
-const wrapper = ({ children }) => (
-
-
- {children}
-
-
-)
-
-describe('useRegenerateRepoUploadToken', () => {
- function setup() {
- server.use(
- rest.patch(
- `internal/github/codecov/repos/gazebo/regenerate-upload-token/`,
- (req, res, ctx) => {
- return res(ctx.status(200), ctx.json(repoDetails))
- }
- )
- )
- }
-
- describe('when called', () => {
- beforeEach(() => {
- setup()
- })
-
- describe('when calling the mutation', () => {
- describe('when successful', () => {
- it('returns isSuccess true', async () => {
- const { result } = renderHook(() => useRegenerateRepoUploadToken(), {
- wrapper,
- })
-
- result.current.mutate()
-
- await waitFor(() => result.current.isLoading)
- await waitFor(() => !result.current.isLoading)
-
- await waitFor(() => expect(result.current.isSuccess).toBeTruthy())
- })
- })
- })
- })
-})
diff --git a/src/services/repoUploadToken/useRegenerateRepoUploadToken.spec.tsx b/src/services/repoUploadToken/useRegenerateRepoUploadToken.spec.tsx
new file mode 100644
index 0000000000..a6c59abac2
--- /dev/null
+++ b/src/services/repoUploadToken/useRegenerateRepoUploadToken.spec.tsx
@@ -0,0 +1,117 @@
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import { renderHook, waitFor } from '@testing-library/react'
+import { graphql } from 'msw'
+import { setupServer } from 'msw/node'
+import { MemoryRouter, Route } from 'react-router-dom'
+
+import { useAddNotification } from 'services/toastNotification'
+
+import { useRegenerateRepoUploadToken } from './useRegenerateRepoUploadToken'
+
+jest.mock('services/toastNotification')
+
+const server = setupServer()
+
+beforeAll(() => server.listen())
+afterEach(() => server.resetHandlers())
+afterAll(() => server.close())
+
+const queryClient = new QueryClient({
+ defaultOptions: { queries: { retry: false } },
+})
+
+const wrapper: React.FC = ({ children }) => (
+
+
+ {children}
+
+
+)
+
+describe('useRegenerateRepoUploadToken', () => {
+ function setup({ isErrorResponse = false }) {
+ //@ts-ignore
+ const mockAddToast = jest.fn()
+
+ //@ts-ignore
+ useAddNotification.mockReturnValue(mockAddToast)
+
+ server.use(
+ graphql.mutation('RegenerateRepositoryUploadToken', (req, res, ctx) => {
+ if (isErrorResponse) {
+ return res(
+ ctx.status(200),
+ ctx.data({
+ regenerateRepositoryUploadToken: {
+ error: {
+ __typename: 'ValidationError',
+ },
+ },
+ })
+ )
+ }
+ return res(
+ ctx.status(200),
+ ctx.data({
+ regenerateRepositoryUploadToken: {
+ token: 'repo-token',
+ },
+ })
+ )
+ })
+ )
+
+ return { mockAddToast }
+ }
+
+ describe('when called', () => {
+ beforeEach(() => {
+ setup({})
+ })
+
+ describe('when calling the mutation', () => {
+ describe('when successful', () => {
+ it('returns isSuccess true', async () => {
+ const { result } = renderHook(() => useRegenerateRepoUploadToken(), {
+ wrapper,
+ })
+
+ result.current.mutate()
+
+ await waitFor(() => result.current.isLoading)
+ await waitFor(() => !result.current.isLoading)
+
+ await waitFor(() => expect(result.current.isSuccess).toBeTruthy())
+ const data = result.current.data
+ await waitFor(() =>
+ expect(data).toEqual({
+ data: {
+ regenerateRepositoryUploadToken: {
+ token: 'repo-token',
+ },
+ },
+ })
+ )
+ })
+ })
+
+ describe('on error', () => {
+ it('fires a toast', async () => {
+ const { mockAddToast } = setup({ isErrorResponse: true })
+
+ const { result } = renderHook(() => useRegenerateRepoUploadToken(), {
+ wrapper,
+ })
+
+ result.current.mutate()
+
+ await waitFor(() => result.current.isLoading)
+ await waitFor(() => !result.current.isLoading)
+
+ await waitFor(() => expect(result.current.isSuccess).toBeTruthy())
+ await waitFor(() => expect(mockAddToast).toHaveBeenCalled())
+ })
+ })
+ })
+ })
+})
diff --git a/src/services/repoUploadToken/useRegenerateRepoUploadToken.tsx b/src/services/repoUploadToken/useRegenerateRepoUploadToken.tsx
new file mode 100644
index 0000000000..fdaca22c95
--- /dev/null
+++ b/src/services/repoUploadToken/useRegenerateRepoUploadToken.tsx
@@ -0,0 +1,71 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import { useParams } from 'react-router-dom'
+
+import { useAddNotification } from 'services/toastNotification'
+import Api from 'shared/api'
+
+const TOAST_DURATION = 10000
+
+const query = `
+ mutation RegenerateRepositoryUploadToken(
+ $owner: String!
+ $repoName: String!
+) {
+ regenerateRepositoryUploadToken(input: { owner: $owner, repoName: $repoName }) {
+ error {
+ ... on ValidationError {
+ __typename
+ message
+ }
+ }
+ token
+ }
+ }
+`
+
+interface URLParams {
+ provider: string
+ owner: string
+ repo: string
+}
+
+export const useRegenerateRepoUploadToken = () => {
+ const { provider, owner, repo } = useParams()
+ const addToast = useAddNotification()
+ const queryClient = useQueryClient()
+ return useMutation({
+ mutationFn: () => {
+ return Api.graphqlMutation({
+ provider,
+ query,
+ variables: {
+ owner,
+ repoName: repo,
+ },
+ mutationPath: 'regenerateRepositoryUploadToken',
+ })
+ },
+ onSuccess: ({ data }) => {
+ queryClient.invalidateQueries(['GetRepo'])
+ queryClient.invalidateQueries(['GetRepoSettings'])
+ const error = data?.regenerateRepositoryUploadToken?.error
+ if (error) {
+ if (error?.__typename === 'ValidationError') {
+ addToast({
+ type: 'error',
+ text: 'Something went wrong',
+ disappearAfter: TOAST_DURATION,
+ })
+ }
+ } else {
+ addToast({
+ type: 'success',
+ text: 'Repo upload token regenerated successfully',
+ disappearAfter: TOAST_DURATION,
+ })
+ }
+ return data?.regenerateRepositoryUploadToken?.token
+ },
+ retry: false,
+ })
+}
From d982f76e3586ed51f03718ae520c176a981c06b9 Mon Sep 17 00:00:00 2001
From: nicholas-codecov
Date: Mon, 8 Jul 2024 09:39:54 -0300
Subject: [PATCH 3/7] chore: Update WS (#2989)
---
package-lock.json | 48 +++++++++++++++++++++++------------------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index a68b99e2c6..c12bd3eb8d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6942,9 +6942,9 @@
}
},
"node_modules/@storybook/core-server/node_modules/ws": {
- "version": "8.16.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
- "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"dev": true,
"engines": {
"node": ">=10.0.0"
@@ -26048,9 +26048,9 @@
}
},
"node_modules/puppeteer-core/node_modules/ws": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
- "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
+ "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
"dev": true,
"dependencies": {
"async-limiter": "~1.0.0"
@@ -33252,9 +33252,9 @@
}
},
"node_modules/webpack-dev-server/node_modules/ws": {
- "version": "8.14.2",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
- "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"dev": true,
"engines": {
"node": ">=10.0.0"
@@ -34108,9 +34108,9 @@
}
},
"node_modules/ws": {
- "version": "7.5.9",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
- "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"dev": true,
"engines": {
"node": ">=8.3.0"
@@ -38814,9 +38814,9 @@
}
},
"ws": {
- "version": "8.16.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
- "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"dev": true,
"requires": {}
},
@@ -52764,9 +52764,9 @@
}
},
"ws": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
- "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
+ "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
"dev": true,
"requires": {
"async-limiter": "~1.0.0"
@@ -58290,9 +58290,9 @@
}
},
"ws": {
- "version": "8.14.2",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
- "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"dev": true,
"requires": {}
}
@@ -58949,9 +58949,9 @@
}
},
"ws": {
- "version": "7.5.9",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
- "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"dev": true,
"requires": {}
},
From 2664937fed953ff57124e9a67a34c9aa7efbea84 Mon Sep 17 00:00:00 2001
From: Spencer Murray <159931558+spalmurray-codecov@users.noreply.github.com>
Date: Mon, 8 Jul 2024 09:30:01 -0400
Subject: [PATCH 4/7] feat: Implement help dropdown for new header (#2986)
---
scripts/icons.js | 7 +
src/globals.css | 44 ++++-
src/layouts/Header/Header.tsx | 5 +-
.../HelpDropdown/HelpDropdown.spec.tsx | 159 ++++++++++++++++++
.../components/HelpDropdown/HelpDropdown.tsx | 140 +++++++++++++++
.../Header/components/HelpDropdown/index.ts | 1 +
src/ui/Icon/svg/developer/index.js | 2 +-
src/ui/Icon/svg/outline/index.js | 4 +-
src/ui/Icon/svg/solid/index.js | 6 +-
9 files changed, 357 insertions(+), 11 deletions(-)
create mode 100644 src/layouts/Header/components/HelpDropdown/HelpDropdown.spec.tsx
create mode 100644 src/layouts/Header/components/HelpDropdown/HelpDropdown.tsx
create mode 100644 src/layouts/Header/components/HelpDropdown/index.ts
diff --git a/scripts/icons.js b/scripts/icons.js
index 36f6e4f761..68b237bb1f 100644
--- a/scripts/icons.js
+++ b/scripts/icons.js
@@ -37,6 +37,13 @@ const enabledIcons = [
'cog',
'light-bulb',
'no-symbol',
+ 'x-circle',
+ 'database',
+ 'trash',
+ 'book-open',
+ 'check-circle',
+ 'question-mark-circle',
+ 'plus-circle',
]
console.log('Generating Icons import')
diff --git a/src/globals.css b/src/globals.css
index 6fff9d6559..d21df2c286 100644
--- a/src/globals.css
+++ b/src/globals.css
@@ -80,11 +80,49 @@
--color-bitbucket: 0, 82, 204;
/* --color-okta: 0, 41, 122; */
--color-okta: 25, 25, 25;
+
+ /* Sentry User Feedback widget styles */
+ --widget-accent-background: rgb(var(--color-ds-blue-darker));
+ --widget-background-hover: rgb(var(--color-ds-blue-quinary));
+ --widget-font-family: 'Poppins', sans-serif;
}
}
#sentry-feedback {
- --accent-background: rgb(var(--color-ds-blue-darker));
- --accent-background-hover: rgb(var(--color-ds-blue-quinary));
- --font-family: 'Poppins', sans-serif;
+ --accent-background: var(--widget-accent-background);
+ --accent-background-hover: var(--widget-background-hover);
+ --font-family: var(--widget-font-family);
+}
+
+@layer components {
+ .widget {
+ --inset: 32px 44px auto auto;
+ }
+
+ .sm-widget {
+ --inset: 32px calc(50% - 276px) auto auto;
+ }
+
+ .md-widget {
+ --inset: 32px calc(50% - 340px) auto auto;
+ }
+
+ .lg-widget {
+ --inset: 32px calc(50% - 468px) auto auto;
+ }
+
+ .xl-widget {
+ --inset: 32px calc(50% - 596px) auto auto;
+ }
+
+ .twoxl-widget {
+ --inset: 32px calc(50% - 724px) auto auto;
+ }
+}
+
+#help-dropdown-widget {
+ --accent-background: var(--widget-accent-background);
+ --accent-background-hover: var(--widget-background-hover);
+ --font-family: var(--widget-font-family);
+ @apply widget sm:sm-widget md:md-widget lg:lg-widget xl:xl-widget 2xl:twoxl-widget;
}
diff --git a/src/layouts/Header/Header.tsx b/src/layouts/Header/Header.tsx
index 4bef8f6a20..5356652ec6 100644
--- a/src/layouts/Header/Header.tsx
+++ b/src/layouts/Header/Header.tsx
@@ -1,5 +1,6 @@
import { useUser } from 'services/user'
+import HelpDropdown from './components/HelpDropdown'
import UserDropdown from './components/UserDropdown'
function Header() {
@@ -12,9 +13,9 @@ function Header() {
return (
Navigation
-
+
Self hosted stuff
-
Help dropdown
+
diff --git a/src/layouts/Header/components/HelpDropdown/HelpDropdown.spec.tsx b/src/layouts/Header/components/HelpDropdown/HelpDropdown.spec.tsx
new file mode 100644
index 0000000000..54c564a4a9
--- /dev/null
+++ b/src/layouts/Header/components/HelpDropdown/HelpDropdown.spec.tsx
@@ -0,0 +1,159 @@
+import Sentry from '@sentry/react'
+import { render, screen } from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+import { MemoryRouter, Route, Switch } from 'react-router-dom'
+
+import HelpDropdown from './HelpDropdown'
+
+const wrapper: React.FC
= ({ children }) => (
+
+
+
+ {children}
+
+
+
+)
+
+describe('HelpDropdown', () => {
+ function setup() {
+ return {
+ user: userEvent.setup(),
+ }
+ }
+
+ it('renders dropdown button', async () => {
+ setup()
+ render(, { wrapper })
+
+ const dropdown = await screen.findByTestId('help-dropdown')
+ expect(dropdown).toBeInTheDocument()
+ })
+
+ describe('when not clicked', () => {
+ it('does not render dropdown', async () => {
+ setup()
+ render(, { wrapper })
+
+ const dropdown = await screen.findByRole('combobox')
+ expect(dropdown).toBeInTheDocument()
+
+ const docs = screen.queryByText('Developer docs')
+ expect(docs).not.toBeInTheDocument()
+ })
+ })
+
+ describe('when clicked', () => {
+ it('renders dropdown', async () => {
+ const { user } = setup()
+ render(, { wrapper })
+
+ const dropdown = await screen.findByRole('combobox')
+ expect(dropdown).toBeInTheDocument()
+
+ await user.click(dropdown)
+
+ const docs = await screen.findByText('Developer docs')
+ expect(docs).toBeInTheDocument()
+
+ const support = await screen.findByText('Support center')
+ expect(support).toBeInTheDocument()
+
+ const feedback = await screen.findByText('Share feedback')
+ expect(feedback).toBeInTheDocument()
+
+ const discussions = await screen.findByText('Join GitHub discussions')
+ expect(discussions).toBeInTheDocument()
+ })
+ })
+
+ describe('when Share feedback item is selected', () => {
+ it('opens the sentry user feedback modal', async () => {
+ console.error = () => {}
+ const { user } = setup()
+ const open = jest.fn()
+ const appendToDom = jest.fn()
+ const removeFromDom = jest.fn()
+ const createForm = jest.fn().mockReturnValue({
+ open,
+ appendToDom,
+ removeFromDom,
+ })
+
+ const mockedFeedbackIntegration = jest
+ .spyOn(Sentry, 'feedbackIntegration')
+ .mockImplementation(() => ({
+ createForm,
+ name: 'asdf',
+ attachTo: jest.fn(),
+ createWidget: jest.fn(),
+ remove: jest.fn(),
+ }))
+
+ render(, { wrapper })
+
+ const dropdown = await screen.findByRole('combobox')
+ expect(dropdown).toBeInTheDocument()
+
+ await user.click(dropdown)
+
+ const feedback = await screen.findByText('Share feedback')
+ expect(feedback).toBeInTheDocument()
+
+ await user.click(feedback)
+
+ expect(mockedFeedbackIntegration).toHaveBeenCalled()
+ expect(createForm).toHaveBeenCalled()
+ expect(appendToDom).toHaveBeenCalled()
+ expect(open).toHaveBeenCalled()
+ })
+ })
+
+ describe('if Sentry form has been loaded', () => {
+ describe('and component unmounts', () => {
+ it('removes the form from the DOM', async () => {
+ console.error = () => {}
+ const { user } = setup()
+ const open = jest.fn()
+ const appendToDom = jest.fn()
+ const removeFromDom = jest.fn()
+ const createForm = jest.fn().mockReturnValue({
+ open,
+ appendToDom,
+ removeFromDom,
+ })
+
+ const mockedFeedbackIntegration = jest
+ .spyOn(Sentry, 'feedbackIntegration')
+ .mockImplementation(() => ({
+ createForm,
+ name: 'asdf',
+ attachTo: jest.fn(),
+ createWidget: jest.fn(),
+ remove: jest.fn(),
+ }))
+
+ const { unmount } = render(, { wrapper })
+
+ const dropdown = await screen.findByRole('combobox')
+ expect(dropdown).toBeInTheDocument()
+
+ await user.click(dropdown)
+
+ const feedback = await screen.findByText('Share feedback')
+ expect(feedback).toBeInTheDocument()
+
+ await user.click(feedback)
+
+ expect(mockedFeedbackIntegration).toHaveBeenCalled()
+ expect(createForm).toHaveBeenCalled()
+ expect(appendToDom).toHaveBeenCalled()
+ expect(open).toHaveBeenCalled()
+
+ unmount()
+
+ expect(removeFromDom).toHaveBeenCalled()
+ })
+ })
+ })
+})
diff --git a/src/layouts/Header/components/HelpDropdown/HelpDropdown.tsx b/src/layouts/Header/components/HelpDropdown/HelpDropdown.tsx
new file mode 100644
index 0000000000..5348cdc331
--- /dev/null
+++ b/src/layouts/Header/components/HelpDropdown/HelpDropdown.tsx
@@ -0,0 +1,140 @@
+// Copying UserDropdown implementation for now until we get a proper
+// component made up.
+
+import { feedbackIntegration } from '@sentry/react'
+import { useSelect } from 'downshift'
+import { useEffect, useMemo, useState } from 'react'
+
+import { cn } from 'shared/utils/cn'
+import Button from 'ui/Button'
+import Icon from 'ui/Icon'
+
+type toProps = {
+ pageName: string
+ options?: object
+}
+
+type ItemProps = {
+ to?: toProps
+ hook?: string
+ onClick?: () => void
+}
+
+type Item = {
+ props: ItemProps
+ children: string
+}
+
+function HelpDropdown() {
+ const sentryFeedback = useMemo(
+ () =>
+ feedbackIntegration({
+ colorScheme: 'light',
+ showBranding: false,
+ formTitle: 'Give Feedback',
+ buttonLabel: 'Give Feedback',
+ submitButtonLabel: 'Send Feedback',
+ nameLabel: 'Username',
+ isEmailRequired: true,
+ autoInject: false,
+ id: 'help-dropdown-widget',
+ }),
+ []
+ )
+
+ // Remove the Sentry form from the DOM on unmount.
+ const [removeSentryForm, setRemoveSentryForm] = useState<() => void>(
+ () => () => {}
+ )
+ useEffect(() => removeSentryForm, [removeSentryForm])
+
+ const items: Item[] = [
+ {
+ props: { to: { pageName: 'docs' } },
+ children: 'Developer docs',
+ },
+ {
+ props: { to: { pageName: 'support' } },
+ children: 'Support center',
+ },
+ {
+ props: {
+ onClick: async () => {
+ const form = await sentryFeedback.createForm()
+ form.appendToDom()
+ form.open()
+ setRemoveSentryForm(() => form.removeFromDom)
+ },
+ hook: 'open-modal',
+ },
+ children: 'Share feedback',
+ },
+ {
+ props: { to: { pageName: 'feedback' } },
+ children: 'Join GitHub discussions',
+ },
+ ]
+
+ const {
+ isOpen,
+ getToggleButtonProps,
+ getItemProps,
+ getLabelProps,
+ getMenuProps,
+ } = useSelect({
+ items,
+ })
+
+ return (
+
+
+
+
+ {isOpen &&
+ items.map((item, index) => (
+ -
+ {/* @ts-expect-error props might be overloaded with stuff */}
+
+
+ ))}
+
+
+ )
+}
+
+export default HelpDropdown
diff --git a/src/layouts/Header/components/HelpDropdown/index.ts b/src/layouts/Header/components/HelpDropdown/index.ts
new file mode 100644
index 0000000000..beab3e3172
--- /dev/null
+++ b/src/layouts/Header/components/HelpDropdown/index.ts
@@ -0,0 +1 @@
+export { default } from './HelpDropdown'
diff --git a/src/ui/Icon/svg/developer/index.js b/src/ui/Icon/svg/developer/index.js
index 1cbc99144e..ca6c5730f5 100644
--- a/src/ui/Icon/svg/developer/index.js
+++ b/src/ui/Icon/svg/developer/index.js
@@ -9,4 +9,4 @@ export { ReactComponent as merge } from './merge.svg'
export { ReactComponent as pullRequestClosed } from './pull-request-closed.svg'
export { ReactComponent as pullRequestOpen } from './pull-request-open.svg'
// export { ReactComponent as pullRequest } from './pull-request.svg'
-export { ReactComponent as statusRunning } from './status-running.svg'
+// export { ReactComponent as statusRunning } from './status-running.svg'
diff --git a/src/ui/Icon/svg/outline/index.js b/src/ui/Icon/svg/outline/index.js
index fd2408a4b9..708e943d36 100644
--- a/src/ui/Icon/svg/outline/index.js
+++ b/src/ui/Icon/svg/outline/index.js
@@ -69,7 +69,7 @@ export { ReactComponent as cog } from './cog.svg'
// export { ReactComponent as currencyRupee } from './currency-rupee.svg'
// export { ReactComponent as currencyYen } from './currency-yen.svg'
// export { ReactComponent as cursorClick } from './cursor-click.svg'
-// export { ReactComponent as database } from './database.svg'
+export { ReactComponent as database } from './database.svg'
// export { ReactComponent as desktopComputer } from './desktop-computer.svg'
// export { ReactComponent as deviceMobile } from './device-mobile.svg'
// export { ReactComponent as deviceTablet } from './device-tablet.svg'
@@ -162,7 +162,7 @@ export { ReactComponent as plusCircle } from './plus-circle.svg'
export { ReactComponent as printer } from './printer.svg'
// export { ReactComponent as puzzle } from './puzzle.svg'
// export { ReactComponent as qrcode } from './qrcode.svg'
-// export { ReactComponent as questionMarkCircle } from './question-mark-circle.svg'
+export { ReactComponent as questionMarkCircle } from './question-mark-circle.svg'
// export { ReactComponent as receiptRefund } from './receipt-refund.svg'
// export { ReactComponent as receiptTax } from './receipt-tax.svg'
export { ReactComponent as refresh } from './refresh.svg'
diff --git a/src/ui/Icon/svg/solid/index.js b/src/ui/Icon/svg/solid/index.js
index 11a5726de4..4712964a9f 100644
--- a/src/ui/Icon/svg/solid/index.js
+++ b/src/ui/Icon/svg/solid/index.js
@@ -36,7 +36,7 @@ export { ReactComponent as bookOpen } from './book-open.svg'
// export { ReactComponent as chatAlt2 } from './chat-alt-2.svg'
// export { ReactComponent as chatAlt } from './chat-alt.svg'
// export { ReactComponent as chat } from './chat.svg'
-// export { ReactComponent as checkCircle } from './check-circle.svg'
+export { ReactComponent as checkCircle } from './check-circle.svg'
export { ReactComponent as check } from './check.svg'
// export { ReactComponent as chevronDoubleDown } from './chevron-double-down.svg'
// export { ReactComponent as chevronDoubleLeft } from './chevron-double-left.svg'
@@ -153,7 +153,7 @@ export { ReactComponent as noSymbol } from './no-symbol.svg'
// export { ReactComponent as phone } from './phone.svg'
// export { ReactComponent as photograph } from './photograph.svg'
// export { ReactComponent as play } from './play.svg'
-// export { ReactComponent as plusCircle } from './plus-circle.svg'
+export { ReactComponent as plusCircle } from './plus-circle.svg'
// export { ReactComponent as plusSm } from './plus-sm.svg'
// export { ReactComponent as plus } from './plus.svg'
// export { ReactComponent as presentationChartBar } from './presentation-chart-bar.svg'
@@ -161,7 +161,7 @@ export { ReactComponent as noSymbol } from './no-symbol.svg'
export { ReactComponent as printer } from './printer.svg'
// export { ReactComponent as puzzle } from './puzzle.svg'
// export { ReactComponent as qrcode } from './qrcode.svg'
-// export { ReactComponent as questionMarkCircle } from './question-mark-circle.svg'
+export { ReactComponent as questionMarkCircle } from './question-mark-circle.svg'
// export { ReactComponent as receiptRefund } from './receipt-refund.svg'
// export { ReactComponent as receiptTax } from './receipt-tax.svg'
export { ReactComponent as refresh } from './refresh.svg'
From bfe41cbec61ebea67ce72d9b9d4b0ca5dda49260 Mon Sep 17 00:00:00 2001
From: Spencer Murray <159931558+spalmurray-codecov@users.noreply.github.com>
Date: Mon, 8 Jul 2024 13:28:33 -0400
Subject: [PATCH 5/7] fix: Remove session expiry tracker (#2988)
---
src/config.js | 5 +-
src/index.tsx | 18 ++++
src/layouts/BaseLayout/BaseLayout.jsx | 2 -
.../EnterpriseLoginLayout.spec.tsx | 8 +-
.../EnterpriseLoginLayout.tsx | 9 +-
.../UserDropdown/UserDropdown.spec.tsx | 45 +++-----
.../components/UserDropdown/UserDropdown.tsx | 8 +-
src/layouts/LoginLayout/LoginLayout.spec.tsx | 12 ---
src/layouts/LoginLayout/LoginLayout.tsx | 9 +-
src/layouts/OldHeader/Dropdown.spec.tsx | 45 +++-----
src/layouts/OldHeader/Dropdown.tsx | 8 +-
src/shared/utils/metrics.ts | 3 +
.../SessionExpiryTracker.spec.tsx | 101 ------------------
.../SessionExpiryTracker.tsx | 96 -----------------
src/ui/SessionExpiryTracker/index.ts | 1 -
15 files changed, 57 insertions(+), 313 deletions(-)
delete mode 100644 src/ui/SessionExpiryTracker/SessionExpiryTracker.spec.tsx
delete mode 100644 src/ui/SessionExpiryTracker/SessionExpiryTracker.tsx
delete mode 100644 src/ui/SessionExpiryTracker/index.ts
diff --git a/src/config.js b/src/config.js
index 8e2616ff87..6fe3f63526 100644
--- a/src/config.js
+++ b/src/config.js
@@ -10,11 +10,10 @@ const defaultConfig = {
GH_APP: 'codecov',
}
-export const LOCAL_STORAGE_SESSION_EXPIRED_KEY = 'expired-session'
+// To be removed after we're satisfied session_expiry cookie cleanup is complete
+export const LOCAL_STORAGE_SESION_EXPIRED_KEY = 'expired_session'
export const LOCAL_STORAGE_SESSION_TRACKING_KEY = 'tracking-session-expiry'
-
export const COOKIE_SESSION_EXPIRY = 'session_expiry'
-export const COOKIE_SESSION_ID = 'sessionid'
export function removeReactAppPrefix(obj) {
// in .env file, the variable must start with REACT_APP_
diff --git a/src/index.tsx b/src/index.tsx
index 170404b29b..427a2ff03f 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,14 +1,22 @@
import * as Sentry from '@sentry/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { createBrowserHistory } from 'history'
+import Cookies from 'js-cookie'
import React from 'react'
import { createRoot } from 'react-dom/client'
import ReactModal from 'react-modal'
import { Router } from 'react-router-dom'
import { CompatRouter } from 'react-router-dom-v5-compat'
+import config, {
+ COOKIE_SESSION_EXPIRY,
+ LOCAL_STORAGE_SESION_EXPIRED_KEY,
+ LOCAL_STORAGE_SESSION_TRACKING_KEY,
+} from 'config'
+
import ErrorBoundary from 'layouts/shared/ErrorBoundary'
import { withFeatureFlagProvider } from 'shared/featureFlags'
+import { metrics } from 'shared/utils/metrics'
import App from './App'
import './globals.css'
@@ -43,6 +51,16 @@ const queryClient = new QueryClient({
},
})
+if (Cookies.get(COOKIE_SESSION_EXPIRY)) {
+ localStorage.removeItem(LOCAL_STORAGE_SESION_EXPIRED_KEY)
+ localStorage.removeItem(LOCAL_STORAGE_SESSION_TRACKING_KEY)
+ Cookies.remove(COOKIE_SESSION_EXPIRY, {
+ path: '/',
+ domain: `.${(config.BASE_URL as string).split('/')[2]}`,
+ }) // Remove http(s)://
+ metrics.increment('old_session_expiry.cleanup')
+}
+
const domNode = document.getElementById('root')
if (!domNode) {
diff --git a/src/layouts/BaseLayout/BaseLayout.jsx b/src/layouts/BaseLayout/BaseLayout.jsx
index 1af8a7f0e2..93da2ff43a 100644
--- a/src/layouts/BaseLayout/BaseLayout.jsx
+++ b/src/layouts/BaseLayout/BaseLayout.jsx
@@ -13,7 +13,6 @@ import { useFlags } from 'shared/featureFlags'
import GlobalBanners from 'shared/GlobalBanners'
import GlobalTopBanners from 'shared/GlobalTopBanners'
import LoadingLogo from 'ui/LoadingLogo'
-import SessionExpiryTracker from 'ui/SessionExpiryTracker'
import { useUserAccessGate } from './hooks/useUserAccessGate'
@@ -74,7 +73,6 @@ function BaseLayout({ children }) {
return (
<>
-
{isFullExperience ? (
<>
{newHeader ? : }
diff --git a/src/layouts/EnterpriseLoginLayout/EnterpriseLoginLayout.spec.tsx b/src/layouts/EnterpriseLoginLayout/EnterpriseLoginLayout.spec.tsx
index 27c52e4a14..b15589309e 100644
--- a/src/layouts/EnterpriseLoginLayout/EnterpriseLoginLayout.spec.tsx
+++ b/src/layouts/EnterpriseLoginLayout/EnterpriseLoginLayout.spec.tsx
@@ -2,15 +2,12 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen } from '@testing-library/react'
import { MemoryRouter, Route, useLocation } from 'react-router-dom'
-import { LOCAL_STORAGE_SESSION_EXPIRED_KEY } from 'config'
-
import EnterpriseLoginLayout from './EnterpriseLoginLayout'
jest.mock('./Header', () => () => 'Header')
jest.mock('layouts/Footer', () => () => 'Footer')
jest.mock('shared/GlobalBanners', () => () => 'GlobalBanners')
jest.mock('layouts/ToastNotifications', () => () => 'ToastNotifications')
-jest.mock('ui/SessionExpiryTracker', () => () => 'SessionExpiryTracker')
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
@@ -42,7 +39,7 @@ describe('EnterpriseLoginLayout', () => {
console.error = () => {}
})
beforeEach(() => {
- mockedUseLocation.mockReturnValue({ search: [] })
+ mockedUseLocation.mockReturnValue({ search: [''] })
})
afterAll(() => {
@@ -92,8 +89,7 @@ describe('EnterpriseLoginLayout', () => {
})
it('renders the session expired banner if session is expired', () => {
- localStorage.setItem(LOCAL_STORAGE_SESSION_EXPIRED_KEY, 'true')
-
+ mockedUseLocation.mockReturnValue({ search: ['expired'] })
render(<>children>, { wrapper })
const session = screen.getByText(/Your session has expired/)
diff --git a/src/layouts/EnterpriseLoginLayout/EnterpriseLoginLayout.tsx b/src/layouts/EnterpriseLoginLayout/EnterpriseLoginLayout.tsx
index a36ab95e65..50a82300da 100644
--- a/src/layouts/EnterpriseLoginLayout/EnterpriseLoginLayout.tsx
+++ b/src/layouts/EnterpriseLoginLayout/EnterpriseLoginLayout.tsx
@@ -1,8 +1,6 @@
import { Suspense } from 'react'
import { useLocation } from 'react-router-dom'
-import { LOCAL_STORAGE_SESSION_EXPIRED_KEY } from 'config'
-
import Footer from 'layouts/Footer'
import ErrorBoundary from 'layouts/shared/ErrorBoundary'
import NetworkErrorBoundary from 'layouts/shared/NetworkErrorBoundary'
@@ -22,15 +20,10 @@ const FullPageLoader = () => (
function EnterpriseLoginLayout({ children }: { children: React.ReactNode }) {
const location = useLocation()
- const showExpiryBanner = localStorage.getItem(
- LOCAL_STORAGE_SESSION_EXPIRED_KEY
- )
return (
<>
- {(location.search.includes('expired') || showExpiryBanner) && (
-
- )}
+ {location.search.includes('expired') && }
}>
diff --git a/src/layouts/Header/components/UserDropdown/UserDropdown.spec.tsx b/src/layouts/Header/components/UserDropdown/UserDropdown.spec.tsx
index 2d35df3ffd..987fd28dad 100644
--- a/src/layouts/Header/components/UserDropdown/UserDropdown.spec.tsx
+++ b/src/layouts/Header/components/UserDropdown/UserDropdown.spec.tsx
@@ -1,15 +1,11 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import Cookies from 'js-cookie'
import { graphql, rest } from 'msw'
import { setupServer } from 'msw/node'
-import { MemoryRouter, Route, Switch } from 'react-router-dom'
+import { MemoryRouter, Route, Switch, useLocation } from 'react-router-dom'
-import config, {
- COOKIE_SESSION_EXPIRY,
- LOCAL_STORAGE_SESSION_TRACKING_KEY,
-} from 'config'
+import config from 'config'
import { useImage } from 'services/image'
@@ -67,6 +63,7 @@ const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
})
const server = setupServer()
+let testLocation: ReturnType
const wrapper: (initialEntries?: string) => React.FC =
(initialEntries = '/gh') =>
@@ -77,6 +74,13 @@ const wrapper: (initialEntries?: string) => React.FC =
{children}
+ {
+ testLocation = location
+ return null
+ }}
+ />
@@ -104,10 +108,6 @@ describe('UserDropdown', () => {
})
config.IS_SELF_HOSTED = selfHosted
config.API_URL = ''
- const mockRemoveItem = jest.spyOn(
- window.localStorage.__proto__,
- 'removeItem'
- )
server.use(
rest.post('/logout', (req, res, ctx) => res(ctx.status(205))),
@@ -118,7 +118,6 @@ describe('UserDropdown', () => {
return {
user: userEvent.setup(),
- mockRemoveItem,
}
}
@@ -172,10 +171,9 @@ describe('UserDropdown', () => {
})
it('handles sign out', async () => {
- const { user, mockRemoveItem } = setup()
+ const { user } = setup()
jest.spyOn(console, 'error').mockImplementation()
- const removeSpy = jest.spyOn(Cookies, 'remove').mockReturnValue()
render(, {
wrapper: wrapper(),
})
@@ -187,14 +185,7 @@ describe('UserDropdown', () => {
expect(button).toBeVisible()
await user.click(button)
- await waitFor(() =>
- expect(mockRemoveItem).toHaveBeenCalledWith(
- LOCAL_STORAGE_SESSION_TRACKING_KEY
- )
- )
- await waitFor(() =>
- expect(removeSpy).toHaveBeenCalledWith(COOKIE_SESSION_EXPIRY)
- )
+ await waitFor(() => expect(testLocation.pathname).toBe('/login'))
})
it('shows manage app access link', async () => {
@@ -253,10 +244,9 @@ describe('UserDropdown', () => {
})
it('handles sign out', async () => {
- const { user, mockRemoveItem } = setup()
+ const { user } = setup()
jest.spyOn(console, 'error').mockImplementation()
- const removeSpy = jest.spyOn(Cookies, 'remove').mockReturnValue()
render(, {
wrapper: wrapper(),
})
@@ -268,14 +258,7 @@ describe('UserDropdown', () => {
expect(button).toBeVisible()
await user.click(button)
- await waitFor(() =>
- expect(mockRemoveItem).toHaveBeenCalledWith(
- LOCAL_STORAGE_SESSION_TRACKING_KEY
- )
- )
- await waitFor(() =>
- expect(removeSpy).toHaveBeenCalledWith(COOKIE_SESSION_EXPIRY)
- )
+ await waitFor(() => expect(testLocation.pathname).toBe('/login'))
})
it('does not show manage app access link', async () => {
diff --git a/src/layouts/Header/components/UserDropdown/UserDropdown.tsx b/src/layouts/Header/components/UserDropdown/UserDropdown.tsx
index 743c5d0a76..aeba5f8e2a 100644
--- a/src/layouts/Header/components/UserDropdown/UserDropdown.tsx
+++ b/src/layouts/Header/components/UserDropdown/UserDropdown.tsx
@@ -1,11 +1,7 @@
import { useSelect } from 'downshift'
-import Cookies from 'js-cookie'
import { useHistory, useParams } from 'react-router-dom'
-import config, {
- COOKIE_SESSION_EXPIRY,
- LOCAL_STORAGE_SESSION_TRACKING_KEY,
-} from 'config'
+import config from 'config'
import { useUser } from 'services/user'
import { cn } from 'shared/utils/cn'
@@ -55,8 +51,6 @@ function UserDropdown() {
method: 'POST',
credentials: 'include',
})
- localStorage.removeItem(LOCAL_STORAGE_SESSION_TRACKING_KEY)
- Cookies.remove(COOKIE_SESSION_EXPIRY)
history.replace('/login')
}
diff --git a/src/layouts/LoginLayout/LoginLayout.spec.tsx b/src/layouts/LoginLayout/LoginLayout.spec.tsx
index 4c824e1561..19bb5128e4 100644
--- a/src/layouts/LoginLayout/LoginLayout.spec.tsx
+++ b/src/layouts/LoginLayout/LoginLayout.spec.tsx
@@ -99,18 +99,6 @@ describe('LoginLayout', () => {
})
describe('when session is expired', () => {
- it('renders the expiry banner when local storage prop set', async () => {
- setup()
-
- jest
- .spyOn(window.localStorage.__proto__, 'getItem')
- .mockReturnValue('true')
-
- render(child content, { wrapper: wrapper() })
- await waitFor(() => {
- expect(screen.getByText(/Your session has expired/)).toBeInTheDocument()
- })
- })
it('renders the expiry banner when query param set', async () => {
setup()
diff --git a/src/layouts/LoginLayout/LoginLayout.tsx b/src/layouts/LoginLayout/LoginLayout.tsx
index 06c5112b86..fea731cd3f 100644
--- a/src/layouts/LoginLayout/LoginLayout.tsx
+++ b/src/layouts/LoginLayout/LoginLayout.tsx
@@ -1,8 +1,6 @@
import { Suspense } from 'react'
import { useLocation } from 'react-router-dom'
-import { LOCAL_STORAGE_SESSION_EXPIRED_KEY } from 'config'
-
import Footer from 'layouts/Footer'
import Header from 'layouts/Header'
import OldHeader from 'layouts/OldHeader'
@@ -23,14 +21,9 @@ const LoginLayout: React.FC = ({ children }) => {
newHeader: false,
})
- const showExpiryBanner = localStorage.getItem(
- LOCAL_STORAGE_SESSION_EXPIRED_KEY
- )
return (
<>
- {(location.search.includes('expired') || showExpiryBanner) && (
-
- )}
+ {location.search.includes('expired') && }
{newHeader ? : }
}>
diff --git a/src/layouts/OldHeader/Dropdown.spec.tsx b/src/layouts/OldHeader/Dropdown.spec.tsx
index 6ff5873059..ee14b29415 100644
--- a/src/layouts/OldHeader/Dropdown.spec.tsx
+++ b/src/layouts/OldHeader/Dropdown.spec.tsx
@@ -1,15 +1,11 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import Cookies from 'js-cookie'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
-import { MemoryRouter, Route, Switch } from 'react-router-dom'
+import { MemoryRouter, Route, Switch, useLocation } from 'react-router-dom'
-import config, {
- COOKIE_SESSION_EXPIRY,
- LOCAL_STORAGE_SESSION_TRACKING_KEY,
-} from 'config'
+import config from 'config'
import { useImage } from 'services/image'
@@ -31,6 +27,7 @@ const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
})
const server = setupServer()
+let testLocation: ReturnType
const wrapper: (initialEntries?: string) => React.FC =
(initialEntries = '/gh') =>
@@ -41,6 +38,13 @@ const wrapper: (initialEntries?: string) => React.FC =
{children}
+ {
+ testLocation = location
+ return null
+ }}
+ />
@@ -68,16 +72,11 @@ describe('Dropdown', () => {
})
config.IS_SELF_HOSTED = selfHosted
config.API_URL = ''
- const mockRemoveItem = jest.spyOn(
- window.localStorage.__proto__,
- 'removeItem'
- )
server.use(rest.post('/logout', (req, res, ctx) => res(ctx.status(205))))
return {
user: userEvent.setup(),
- mockRemoveItem,
}
}
@@ -131,10 +130,9 @@ describe('Dropdown', () => {
})
it('handles sign out', async () => {
- const { user, mockRemoveItem } = setup()
+ const { user } = setup()
jest.spyOn(console, 'error').mockImplementation()
- const removeSpy = jest.spyOn(Cookies, 'remove').mockReturnValue()
render(, {
wrapper: wrapper(),
})
@@ -146,14 +144,7 @@ describe('Dropdown', () => {
expect(button).toBeVisible()
await user.click(button)
- await waitFor(() =>
- expect(mockRemoveItem).toHaveBeenCalledWith(
- LOCAL_STORAGE_SESSION_TRACKING_KEY
- )
- )
- await waitFor(() =>
- expect(removeSpy).toHaveBeenCalledWith(COOKIE_SESSION_EXPIRY)
- )
+ await waitFor(() => expect(testLocation.pathname).toBe('/login'))
})
it('shows manage app access link', async () => {
@@ -212,10 +203,9 @@ describe('Dropdown', () => {
})
it('handles sign out', async () => {
- const { user, mockRemoveItem } = setup()
+ const { user } = setup()
jest.spyOn(console, 'error').mockImplementation()
- const removeSpy = jest.spyOn(Cookies, 'remove').mockReturnValue()
render(, {
wrapper: wrapper(),
})
@@ -227,14 +217,7 @@ describe('Dropdown', () => {
expect(button).toBeVisible()
await user.click(button)
- await waitFor(() =>
- expect(mockRemoveItem).toHaveBeenCalledWith(
- LOCAL_STORAGE_SESSION_TRACKING_KEY
- )
- )
- await waitFor(() =>
- expect(removeSpy).toHaveBeenCalledWith(COOKIE_SESSION_EXPIRY)
- )
+ await waitFor(() => expect(testLocation.pathname).toBe('/login'))
})
it('does not show manage app access link', async () => {
diff --git a/src/layouts/OldHeader/Dropdown.tsx b/src/layouts/OldHeader/Dropdown.tsx
index d613f515e1..f06926046c 100644
--- a/src/layouts/OldHeader/Dropdown.tsx
+++ b/src/layouts/OldHeader/Dropdown.tsx
@@ -1,12 +1,8 @@
import cs from 'classnames'
import { useSelect } from 'downshift'
-import Cookies from 'js-cookie'
import { useHistory, useParams } from 'react-router-dom'
-import config, {
- COOKIE_SESSION_EXPIRY,
- LOCAL_STORAGE_SESSION_TRACKING_KEY,
-} from 'config'
+import config from 'config'
import { providerToName } from 'shared/utils/provider'
import Avatar from 'ui/Avatar'
@@ -56,8 +52,6 @@ function Dropdown({ currentUser }: { currentUser: CurrentUser }) {
method: 'POST',
credentials: 'include',
})
- localStorage.removeItem(LOCAL_STORAGE_SESSION_TRACKING_KEY)
- Cookies.remove(COOKIE_SESSION_EXPIRY)
history.replace('/login')
}
diff --git a/src/shared/utils/metrics.ts b/src/shared/utils/metrics.ts
index f90f0cb22c..8a754be85c 100644
--- a/src/shared/utils/metrics.ts
+++ b/src/shared/utils/metrics.ts
@@ -93,6 +93,9 @@ type IncrementKeys = {
checkout_from_page: string
}
}
+ old_session_expiry: {
+ cleanup: string
+ }
}
type SetKeys = {
diff --git a/src/ui/SessionExpiryTracker/SessionExpiryTracker.spec.tsx b/src/ui/SessionExpiryTracker/SessionExpiryTracker.spec.tsx
deleted file mode 100644
index e717619257..0000000000
--- a/src/ui/SessionExpiryTracker/SessionExpiryTracker.spec.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import { act, render, screen, waitFor } from '@testing-library/react'
-import Cookies from 'js-cookie'
-import { MemoryRouter, Route } from 'react-router-dom'
-
-import SessionExpiredBanner from 'pages/LoginPage/SessionExpiredBanner'
-
-import SessionExpiryTracker from './SessionExpiryTracker'
-
-const wrapper: React.FC = ({ children }) => (
-
- {children}
- } />
-
-)
-
-describe('SessionExpiryTracker', () => {
- function setup() {
- const mockSetItem = jest.spyOn(window.localStorage.__proto__, 'setItem')
- const mockRemoveItem = jest.spyOn(
- window.localStorage.__proto__,
- 'removeItem'
- )
- return { mockSetItem, mockRemoveItem }
- }
- beforeEach(() => {
- jest.useFakeTimers()
- jest.setSystemTime(new Date())
- global.fetch = jest.fn()
- })
-
- afterEach(() => {
- jest.useRealTimers()
- jest.restoreAllMocks()
- })
-
- it('shows expired modal only 2 minutes before expiry or later', async () => {
- const expiryTime = new Date()
- expiryTime.setMinutes(expiryTime.getMinutes() + 15)
- Cookies.get = jest.fn().mockImplementation(() => expiryTime.toString())
- const removeCookieSpy = jest.spyOn(Cookies, 'remove')
- const { mockSetItem, mockRemoveItem } = setup()
- render(, { wrapper })
-
- expect(
- screen.queryByText('Your session has expired')
- ).not.toBeInTheDocument()
- expect(mockRemoveItem).toHaveBeenCalled()
-
- act(() => {
- jest.advanceTimersByTime(60 * 14 * 1000) // Advance time by 14 minutes
- })
- await waitFor(() => {
- expect(screen.getByText(/Your session has expired/)).toBeInTheDocument()
- })
- expect(mockSetItem).toHaveBeenCalled()
- expect(mockRemoveItem).toHaveBeenCalled()
- expect(removeCookieSpy).toHaveBeenCalled()
- expect(global.fetch).toHaveBeenCalled()
- })
-
- it('using real timers', async () => {
- jest.useRealTimers()
- const expiryTime = new Date()
- Cookies.get = jest.fn().mockImplementation(() => expiryTime.toString())
-
- render(, { wrapper })
- await waitFor(() => {
- expect(screen.getByText(/Your session has expired/)).toBeInTheDocument()
- })
- })
-
- it('should not display modal when session expiry time is not set', () => {
- Cookies.get = jest.fn().mockImplementation(() => undefined)
- const { mockSetItem, mockRemoveItem } = setup()
-
- render(, { wrapper })
- expect(
- screen.queryByText(/Your session has expired/)
- ).not.toBeInTheDocument()
- expect(mockRemoveItem).toHaveBeenCalled()
- expect(mockSetItem).toHaveBeenCalled()
- })
-
- it('should clear interval and timeout on unmount', () => {
- const expiryTime = new Date()
- expiryTime.setMinutes(expiryTime.getMinutes() + 15)
- Cookies.get = jest.fn().mockImplementation(() => expiryTime.toString())
- const clearIntervalSpy = jest.spyOn(global, 'clearInterval')
- const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout')
-
- const { unmount } = render(, { wrapper })
-
- act(() => {
- jest.advanceTimersByTime(60 * 14 * 1000)
- })
-
- unmount()
- expect(clearIntervalSpy).toHaveBeenCalled()
- expect(clearTimeoutSpy).toHaveBeenCalled()
- })
-})
diff --git a/src/ui/SessionExpiryTracker/SessionExpiryTracker.tsx b/src/ui/SessionExpiryTracker/SessionExpiryTracker.tsx
deleted file mode 100644
index 59c27b24d1..0000000000
--- a/src/ui/SessionExpiryTracker/SessionExpiryTracker.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import Cookies from 'js-cookie'
-import React, { useCallback, useEffect, useState } from 'react'
-import { Redirect } from 'react-router-dom'
-
-import config, {
- COOKIE_SESSION_EXPIRY,
- LOCAL_STORAGE_SESSION_EXPIRED_KEY,
- LOCAL_STORAGE_SESSION_TRACKING_KEY,
-} from 'config'
-
-const ONE_MINUTE_MILLIS = 60 * 1000
-const TWO_MINUTES_MILLIS = 2 * ONE_MINUTE_MILLIS
-const THIRTY_MINUTES_MILLIS = 30 * ONE_MINUTE_MILLIS
-
-const SessionExpiryTracker: React.FC = () => {
- localStorage.setItem(LOCAL_STORAGE_SESSION_TRACKING_KEY, 'true')
- localStorage.removeItem(LOCAL_STORAGE_SESSION_EXPIRED_KEY)
-
- const [redirectToLogout, setRedirectToLogout] = useState(false)
- const sessionExpiryTimeString = Cookies.get(COOKIE_SESSION_EXPIRY)
- const getCheckDelay = (sessionExpiryTime: Date) => {
- const timeLeft = sessionExpiryTime.getTime() - new Date().getTime()
- return timeLeft > THIRTY_MINUTES_MILLIS
- ? timeLeft - THIRTY_MINUTES_MILLIS
- : 0
- }
-
- const checkSession = useCallback(
- (sessionExpiryTime: Date) => {
- const currentTime = new Date()
- const timeLeft = sessionExpiryTime.getTime() - currentTime.getTime()
- if (timeLeft <= TWO_MINUTES_MILLIS && !redirectToLogout) {
- setRedirectToLogout(true)
- }
- },
- [redirectToLogout]
- )
-
- const setupSessionIntervalCheck = useCallback(
- (sessionExpiryTime: Date) => {
- return window.setInterval(() => {
- checkSession(sessionExpiryTime)
- }, ONE_MINUTE_MILLIS)
- },
- [checkSession]
- )
-
- useEffect(() => {
- if (!sessionExpiryTimeString) {
- return
- }
- const sessionExpiryTime = new Date(sessionExpiryTimeString)
-
- let intervalId: number
- const delayBeforeStart = getCheckDelay(sessionExpiryTime)
- const timeoutId = setTimeout(() => {
- intervalId = setupSessionIntervalCheck(sessionExpiryTime)
- checkSession(sessionExpiryTime)
- }, delayBeforeStart)
-
- return () => {
- clearTimeout(timeoutId)
- clearInterval(intervalId)
- }
- }, [sessionExpiryTimeString, checkSession, setupSessionIntervalCheck])
-
- useEffect(() => {
- if (!redirectToLogout) {
- return
- }
-
- const handleLogout = async () => {
- await fetch(`${config.API_URL}/logout`, {
- method: 'POST',
- credentials: 'include',
- })
- Cookies.remove(COOKIE_SESSION_EXPIRY)
- localStorage.setItem(LOCAL_STORAGE_SESSION_EXPIRED_KEY, 'true')
- localStorage.removeItem(LOCAL_STORAGE_SESSION_TRACKING_KEY)
- }
-
- handleLogout()
- }, [redirectToLogout])
-
- if (!sessionExpiryTimeString || !redirectToLogout) {
- return null
- }
-
- return config.IS_SELF_HOSTED ? (
-
- ) : (
-
- )
-}
-
-export default SessionExpiryTracker
diff --git a/src/ui/SessionExpiryTracker/index.ts b/src/ui/SessionExpiryTracker/index.ts
deleted file mode 100644
index 10d470270f..0000000000
--- a/src/ui/SessionExpiryTracker/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './SessionExpiryTracker'
From c8dee689d8fce27ac1816b7273882f2ec81258fb Mon Sep 17 00:00:00 2001
From: nicholas-codecov
Date: Tue, 9 Jul 2024 13:01:38 -0300
Subject: [PATCH 6/7] chore: Make after nullable for BA queries (#2995)
---
src/services/bundleAnalysis/useBundleAssets.tsx | 2 +-
src/services/bundleAnalysis/useBundleTrendData.tsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/services/bundleAnalysis/useBundleAssets.tsx b/src/services/bundleAnalysis/useBundleAssets.tsx
index ebc0c3394c..90a7b01bf4 100644
--- a/src/services/bundleAnalysis/useBundleAssets.tsx
+++ b/src/services/bundleAnalysis/useBundleAssets.tsx
@@ -106,7 +106,7 @@ query BundleAssets(
$bundle: String!
$interval: MeasurementInterval!
$before: DateTime!
- $after: DateTime!
+ $after: DateTime
) {
owner(username: $owner) {
repository(name: $repo) {
diff --git a/src/services/bundleAnalysis/useBundleTrendData.tsx b/src/services/bundleAnalysis/useBundleTrendData.tsx
index f9232e46d1..eae9e798d8 100644
--- a/src/services/bundleAnalysis/useBundleTrendData.tsx
+++ b/src/services/bundleAnalysis/useBundleTrendData.tsx
@@ -82,7 +82,7 @@ query GetBundleTrend(
$bundle: String!
$interval: MeasurementInterval!
$before: DateTime!
- $after: DateTime!
+ $after: DateTime
$filters: BundleAnalysisMeasurementsSetFilters
) {
owner(username: $owner) {
From 9125b95a753d201ec3f66b2fb5f84e6dde9e4ed1 Mon Sep 17 00:00:00 2001
From: nicholas-codecov
Date: Tue, 9 Jul 2024 13:26:44 -0300
Subject: [PATCH 7/7] chore: Remove spotlight (#2994)
---
craco.config.js | 8 +-----
craco.stats.config.js | 8 ------
package-lock.json | 64 -------------------------------------------
package.json | 2 --
src/sentry.ts | 9 ------
5 files changed, 1 insertion(+), 90 deletions(-)
diff --git a/craco.config.js b/craco.config.js
index e872860d5b..edde1b24ab 100644
--- a/craco.config.js
+++ b/craco.config.js
@@ -1,6 +1,5 @@
/* eslint-disable camelcase */
const { sentryWebpackPlugin } = require('@sentry/webpack-plugin')
-const WebpackHookPlugin = require('webpack-hook-plugin')
const WebpackBar = require('webpackbar')
const { resolve } = require('path')
@@ -42,12 +41,7 @@ module.exports = {
plugins: [
...(process.env.SENTRY_AUTH_TOKEN ? [SentryPlugin] : []),
...(process.env.NODE_ENV === 'development'
- ? [
- new WebpackBar({ color: '#FF6600' }),
- new WebpackHookPlugin({
- onBuildStart: ['npx @spotlightjs/spotlight'],
- }),
- ]
+ ? [new WebpackBar({ color: '#FF6600' })]
: []),
],
},
diff --git a/craco.stats.config.js b/craco.stats.config.js
index 475e4639c0..645c55a773 100644
--- a/craco.stats.config.js
+++ b/craco.stats.config.js
@@ -1,6 +1,5 @@
/* eslint-disable camelcase */
const { codecovWebpackPlugin } = require('@codecov/webpack-plugin')
-const WebpackHookPlugin = require('webpack-hook-plugin')
const { resolve } = require('path')
@@ -27,13 +26,6 @@ module.exports = {
sentry: resolve(__dirname, 'src/sentry'),
},
plugins: [
- ...(process.env.NODE_ENV === 'development'
- ? [
- new WebpackHookPlugin({
- onBuildStart: ['npx @spotlightjs/spotlight'],
- }),
- ]
- : []),
...(process.env.CODECOV_ORG_TOKEN && process.env.CODECOV_API_URL
? [
codecovWebpackPlugin({
diff --git a/package-lock.json b/package-lock.json
index c12bd3eb8d..d4e21578e0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -62,7 +62,6 @@
"@codecov/webpack-plugin": "^0.0.1-beta.6",
"@craco/craco": "^7.1.0",
"@sentry/webpack-plugin": "^2.17.0",
- "@spotlightjs/spotlight": "^1.2.7",
"@storybook/addon-a11y": "^7.6.17",
"@storybook/addon-actions": "^7.6.17",
"@storybook/addon-essentials": "^7.6.17",
@@ -113,7 +112,6 @@
"tailwindcss": "^3.4.4",
"typescript": "^4.9.5",
"webpack": "^5.84.1",
- "webpack-hook-plugin": "^1.0.7",
"webpackbar": "^6.0.1"
},
"engines": {
@@ -6026,34 +6024,6 @@
"@sinonjs/commons": "^3.0.0"
}
},
- "node_modules/@spotlightjs/overlay": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@spotlightjs/overlay/-/overlay-1.3.0.tgz",
- "integrity": "sha512-ZBh75oGv12HQ132QtW9qXIkDLHvDZDm7ex4bKzHu61pNfWy3+hIFKUbIftzg0oMdmacFOE3pO0MSjUwmtvzEUA==",
- "dev": true
- },
- "node_modules/@spotlightjs/sidecar": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/@spotlightjs/sidecar/-/sidecar-1.3.4.tgz",
- "integrity": "sha512-uWnmjh3K+2YPpnNu4g6KsXxq/HGmVTL6lHO9C2pYBw6KPPqRHGoZmML6ea8HSJdWmW+jEKmYHS8sjvIQOj2nvA==",
- "dev": true,
- "bin": {
- "spotlight-sidecar": "server.js"
- }
- },
- "node_modules/@spotlightjs/spotlight": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/@spotlightjs/spotlight/-/spotlight-1.2.7.tgz",
- "integrity": "sha512-w2YF0r7y9Jm1B3CsOgXcnZW3IEiiO3YjdvYQ6FSArvOD/WaCEMNXe9wfsHWqgk3+tE0Pw+JDpXUeqlskDojHVw==",
- "dev": true,
- "dependencies": {
- "@spotlightjs/overlay": "1.3.0",
- "@spotlightjs/sidecar": "1.3.4"
- },
- "bin": {
- "spotlight-sidecar": "bin/run.js"
- }
- },
"node_modules/@storybook/addon-a11y": {
"version": "7.6.17",
"resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-7.6.17.tgz",
@@ -33272,12 +33242,6 @@
}
}
},
- "node_modules/webpack-hook-plugin": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/webpack-hook-plugin/-/webpack-hook-plugin-1.0.7.tgz",
- "integrity": "sha512-8GJ+LiR+0rrShCSeeFMaKONX7xMMW3H9Hszmy5IsCereZUJ5AoLhJgn5BhySQChA4Q5lZ7/9KtidDypYHQDDmA==",
- "dev": true
- },
"node_modules/webpack-hot-middleware": {
"version": "2.25.4",
"resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.4.tgz",
@@ -38114,28 +38078,6 @@
"@sinonjs/commons": "^3.0.0"
}
},
- "@spotlightjs/overlay": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@spotlightjs/overlay/-/overlay-1.3.0.tgz",
- "integrity": "sha512-ZBh75oGv12HQ132QtW9qXIkDLHvDZDm7ex4bKzHu61pNfWy3+hIFKUbIftzg0oMdmacFOE3pO0MSjUwmtvzEUA==",
- "dev": true
- },
- "@spotlightjs/sidecar": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/@spotlightjs/sidecar/-/sidecar-1.3.4.tgz",
- "integrity": "sha512-uWnmjh3K+2YPpnNu4g6KsXxq/HGmVTL6lHO9C2pYBw6KPPqRHGoZmML6ea8HSJdWmW+jEKmYHS8sjvIQOj2nvA==",
- "dev": true
- },
- "@spotlightjs/spotlight": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/@spotlightjs/spotlight/-/spotlight-1.2.7.tgz",
- "integrity": "sha512-w2YF0r7y9Jm1B3CsOgXcnZW3IEiiO3YjdvYQ6FSArvOD/WaCEMNXe9wfsHWqgk3+tE0Pw+JDpXUeqlskDojHVw==",
- "dev": true,
- "requires": {
- "@spotlightjs/overlay": "1.3.0",
- "@spotlightjs/sidecar": "1.3.4"
- }
- },
"@storybook/addon-a11y": {
"version": "7.6.17",
"resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-7.6.17.tgz",
@@ -58298,12 +58240,6 @@
}
}
},
- "webpack-hook-plugin": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/webpack-hook-plugin/-/webpack-hook-plugin-1.0.7.tgz",
- "integrity": "sha512-8GJ+LiR+0rrShCSeeFMaKONX7xMMW3H9Hszmy5IsCereZUJ5AoLhJgn5BhySQChA4Q5lZ7/9KtidDypYHQDDmA==",
- "dev": true
- },
"webpack-hot-middleware": {
"version": "2.25.4",
"resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.4.tgz",
diff --git a/package.json b/package.json
index b1710195d6..9c018f179b 100644
--- a/package.json
+++ b/package.json
@@ -95,7 +95,6 @@
"@codecov/webpack-plugin": "^0.0.1-beta.6",
"@craco/craco": "^7.1.0",
"@sentry/webpack-plugin": "^2.17.0",
- "@spotlightjs/spotlight": "^1.2.7",
"@storybook/addon-a11y": "^7.6.17",
"@storybook/addon-actions": "^7.6.17",
"@storybook/addon-essentials": "^7.6.17",
@@ -146,7 +145,6 @@
"tailwindcss": "^3.4.4",
"typescript": "^4.9.5",
"webpack": "^5.84.1",
- "webpack-hook-plugin": "^1.0.7",
"webpackbar": "^6.0.1"
},
"engines": {
diff --git a/src/sentry.ts b/src/sentry.ts
index 600d1a91b3..340b2fe9be 100644
--- a/src/sentry.ts
+++ b/src/sentry.ts
@@ -1,5 +1,4 @@
import * as Sentry from '@sentry/react'
-import * as Spotlight from '@spotlightjs/spotlight'
import { createBrowserHistory } from 'history'
import { Route } from 'react-router-dom'
@@ -79,7 +78,6 @@ export const setupSentry = ({
const integrations = [replay, tracing]
// Only show feedback button in production
- // spotlight takes the place of the feedback widget in dev mode
if (config.NODE_ENV === 'production') {
const feedback = Sentry.feedbackIntegration({
colorScheme: 'light',
@@ -130,11 +128,4 @@ export const setupSentry = ({
},
...deClutterConfig,
})
-
- if (config.NODE_ENV === 'development') {
- Spotlight.init({
- injectImmediately: true,
- integrations: [Spotlight.sentry()],
- })
- }
}