diff --git a/.eslintrc.json b/.eslintrc.json
index 5875299217..96e3530419 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -10,6 +10,10 @@
"plugin:storybook/recommended",
"plugin:tailwindcss/recommended"
],
+ "plugins": ["@vitest"],
+ "globals": {
+ "vi": true
+ },
"ignorePatterns": [
"!src/**/*.{js,jsx,ts,tsx}",
"src/old_ui/Icon/svg/*.jsx",
diff --git a/package.json b/package.json
index eb8876a5e8..9481658098 100644
--- a/package.json
+++ b/package.json
@@ -151,6 +151,7 @@
"@vitejs/plugin-react": "^4.3.1",
"@vitest/coverage-istanbul": "^2.1.1",
"@vitest/coverage-v8": "^2.1.1",
+ "@vitest/eslint-plugin": "^1.1.4",
"@vitest/ui": "^2.1.1",
"autoprefixer": "^10.4.14",
"eslint": "^8.39.0",
diff --git a/src/layouts/BaseLayout/InstallationHelpBanner/InstallationHelpBanner.test.jsx b/src/layouts/BaseLayout/InstallationHelpBanner/InstallationHelpBanner.test.jsx
index 6a559cbe6d..1f2e466b72 100644
--- a/src/layouts/BaseLayout/InstallationHelpBanner/InstallationHelpBanner.test.jsx
+++ b/src/layouts/BaseLayout/InstallationHelpBanner/InstallationHelpBanner.test.jsx
@@ -4,7 +4,6 @@ import userEvent from '@testing-library/user-event'
import { graphql, HttpResponse } from 'msw2'
import { setupServer } from 'msw2/node'
import { MemoryRouter, Route, Switch } from 'react-router-dom'
-import { vi } from 'vitest'
import InstallationHelpBanner from './InstallationHelpBanner'
diff --git a/src/layouts/Footer/Footer.test.tsx b/src/layouts/Footer/Footer.test.tsx
index 3bfd5ac3fc..705e7e30fa 100644
--- a/src/layouts/Footer/Footer.test.tsx
+++ b/src/layouts/Footer/Footer.test.tsx
@@ -1,6 +1,5 @@
import { render, screen } from '@testing-library/react'
import { MemoryRouter, Route } from 'react-router-dom'
-import { vi } from 'vitest'
import config from 'config'
diff --git a/src/layouts/SidebarLayout/SidebarLayout.spec.jsx b/src/layouts/SidebarLayout/SidebarLayout.test.tsx
similarity index 53%
rename from src/layouts/SidebarLayout/SidebarLayout.spec.jsx
rename to src/layouts/SidebarLayout/SidebarLayout.test.tsx
index 7c11282cc0..49dc9c51d8 100644
--- a/src/layouts/SidebarLayout/SidebarLayout.spec.jsx
+++ b/src/layouts/SidebarLayout/SidebarLayout.test.tsx
@@ -3,74 +3,82 @@ import { MemoryRouter } from 'react-router-dom'
import SidebarLayout from './SidebarLayout'
-jest.mock('../shared/ErrorBoundary', () => ({ children }) => <>{children}>)
-jest.mock('layouts/Footer', () => () => 'Footer')
+vi.mock('../shared/ErrorBoundary', () => ({
+ default: ({ children }: { children: React.ReactNode }) => <>{children}>,
+}))
+vi.mock('layouts/Footer', () => ({ default: () => 'Footer' }))
const robinQuote = 'Holy Tintinnabulation!'
const batmanQuote =
'Why do we fall? So that we can learn to pick ourselves back up.'
describe('SidebarLayout', () => {
- function setup(content, overrideClass) {
- render(
- {robinQuote}}
- >
- {content}
- ,
- {
- wrapper: MemoryRouter,
- }
- )
- }
-
describe('it renders with no children', () => {
- beforeEach(() => {
- setup()
- })
-
it('renders the sidebar', () => {
+ render(
+ {robinQuote}}>,
+ { wrapper: MemoryRouter }
+ )
+
const sidebar = screen.getByText(robinQuote)
expect(sidebar).toBeInTheDocument()
})
})
describe('it renders with children', () => {
- beforeEach(() => {
- setup(
> = ({
+ sidebar,
+ children,
+ className = '',
+}) => {
return (
@@ -22,8 +30,4 @@ function SidebarLayout({ sidebar, children, className = '' }) {
)
}
-SidebarLayout.propTypes = {
- sidebar: PropType.element.isRequired,
-}
-
export default SidebarLayout
diff --git a/src/layouts/SidebarLayout/index.js b/src/layouts/SidebarLayout/index.ts
similarity index 100%
rename from src/layouts/SidebarLayout/index.js
rename to src/layouts/SidebarLayout/index.ts
diff --git a/src/layouts/ToastNotifications/ToastNotifications.spec.jsx b/src/layouts/ToastNotifications/ToastNotifications.test.jsx
similarity index 88%
rename from src/layouts/ToastNotifications/ToastNotifications.spec.jsx
rename to src/layouts/ToastNotifications/ToastNotifications.test.jsx
index 5715c1367b..00271007f6 100644
--- a/src/layouts/ToastNotifications/ToastNotifications.spec.jsx
+++ b/src/layouts/ToastNotifications/ToastNotifications.test.jsx
@@ -1,4 +1,5 @@
import { render, screen } from '@testing-library/react'
+import { vi } from 'vitest'
import {
useNotifications,
@@ -22,10 +23,10 @@ const notifications = [
},
]
-jest.mock('services/toastNotification')
+vi.mock('services/toastNotification')
describe('ToastNotifications', () => {
- const removeNotification = jest.fn()
+ const removeNotification = vi.fn()
function setup() {
useNotifications.mockReturnValue(notifications)
@@ -35,7 +36,7 @@ describe('ToastNotifications', () => {
describe('when rendered', () => {
beforeEach(() => {
- jest.useFakeTimers()
+ vi.useFakeTimers()
removeNotification.mockReset()
setup()
})
@@ -47,7 +48,7 @@ describe('ToastNotifications', () => {
describe('when enough time passes', () => {
beforeEach(() => {
- jest.runOnlyPendingTimers()
+ vi.runOnlyPendingTimers()
})
it('calls removeNotification with the notification that disappear', () => {
diff --git a/src/layouts/ToastNotifications/index.js b/src/layouts/ToastNotifications/index.ts
similarity index 100%
rename from src/layouts/ToastNotifications/index.js
rename to src/layouts/ToastNotifications/index.ts
diff --git a/src/layouts/shared/ErrorBoundary/ErrorBoundary.spec.jsx b/src/layouts/shared/ErrorBoundary/ErrorBoundary.test.jsx
similarity index 95%
rename from src/layouts/shared/ErrorBoundary/ErrorBoundary.spec.jsx
rename to src/layouts/shared/ErrorBoundary/ErrorBoundary.test.jsx
index 9d2eb4970e..4461ee45b3 100644
--- a/src/layouts/shared/ErrorBoundary/ErrorBoundary.spec.jsx
+++ b/src/layouts/shared/ErrorBoundary/ErrorBoundary.test.jsx
@@ -1,6 +1,7 @@
import * as Sentry from '@sentry/browser'
import { render, screen } from '@testing-library/react'
import { MemoryRouter, Route } from 'react-router-dom'
+import { vi } from 'vitest'
import ErrorBoundary from './ErrorBoundary'
@@ -10,15 +11,14 @@ function BadComponent() {
}
// https://docs.sentry.io/platforms/javascript/guides/react/components/errorboundary/#using-multiple-error-boundaries
-const sentryMockScope = jest.fn()
+const sentryMockScope = vi.fn()
describe('Error Boundary', () => {
let mockError
- beforeAll(() => jest.disableAutomock())
- afterAll(() => jest.enableAutomock())
+
beforeEach(() => {
- mockError = jest.fn()
- const spy = jest.spyOn(console, 'error')
+ mockError = vi.fn()
+ const spy = vi.spyOn(console, 'error')
spy.mockImplementation(mockError)
})
diff --git a/src/layouts/shared/NetworkErrorBoundary/NetworkErrorBoundary.spec.jsx b/src/layouts/shared/NetworkErrorBoundary/NetworkErrorBoundary.test.jsx
similarity index 99%
rename from src/layouts/shared/NetworkErrorBoundary/NetworkErrorBoundary.spec.jsx
rename to src/layouts/shared/NetworkErrorBoundary/NetworkErrorBoundary.test.jsx
index 775d45bca8..8f9f64250d 100644
--- a/src/layouts/shared/NetworkErrorBoundary/NetworkErrorBoundary.spec.jsx
+++ b/src/layouts/shared/NetworkErrorBoundary/NetworkErrorBoundary.test.jsx
@@ -4,14 +4,15 @@ import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Component, useState } from 'react'
import { MemoryRouter, useHistory } from 'react-router-dom'
+import { vi } from 'vitest'
import config from 'config'
import NetworkErrorBoundary from './NetworkErrorBoundary'
// silence all verbose console.error
-jest.spyOn(console, 'error').mockImplementation()
-jest.mock('config')
+vi.spyOn(console, 'error').mockImplementation(() => undefined)
+vi.mock('config')
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
@@ -354,7 +355,7 @@ describe('NetworkErrorBoundary', () => {
})
// Mock the global fetch function
- global.fetch = jest.fn(() =>
+ global.fetch = vi.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({}),
diff --git a/src/layouts/shared/NetworkErrorBoundary/index.js b/src/layouts/shared/NetworkErrorBoundary/index.ts
similarity index 100%
rename from src/layouts/shared/NetworkErrorBoundary/index.js
rename to src/layouts/shared/NetworkErrorBoundary/index.ts
diff --git a/src/layouts/shared/NetworkErrorBoundary/networkErrorMetrics.spec.ts b/src/layouts/shared/NetworkErrorBoundary/networkErrorMetrics.test.ts
similarity index 100%
rename from src/layouts/shared/NetworkErrorBoundary/networkErrorMetrics.spec.ts
rename to src/layouts/shared/NetworkErrorBoundary/networkErrorMetrics.test.ts
diff --git a/src/layouts/shared/SilentNetworkError/SilentNetworkError.spec.jsx b/src/layouts/shared/SilentNetworkError/SilentNetworkError.test.jsx
similarity index 95%
rename from src/layouts/shared/SilentNetworkError/SilentNetworkError.spec.jsx
rename to src/layouts/shared/SilentNetworkError/SilentNetworkError.test.jsx
index fba96174a0..bcf9de68bf 100644
--- a/src/layouts/shared/SilentNetworkError/SilentNetworkError.spec.jsx
+++ b/src/layouts/shared/SilentNetworkError/SilentNetworkError.test.jsx
@@ -1,9 +1,10 @@
import { render, screen } from '@testing-library/react'
+import { vi } from 'vitest'
import SilentNetworkError from './SilentNetworkError'
// silence all verbose console.error
-jest.spyOn(console, 'error').mockImplementation()
+vi.spyOn(console, 'error').mockImplementation(() => undefined)
describe('SilentNetworkError', () => {
function setup(ComponentToRender, props = {}) {
diff --git a/src/layouts/shared/SilentNetworkError/index.js b/src/layouts/shared/SilentNetworkError/index.ts
similarity index 100%
rename from src/layouts/shared/SilentNetworkError/index.js
rename to src/layouts/shared/SilentNetworkError/index.ts
diff --git a/src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.spec.jsx b/src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.test.tsx
similarity index 87%
rename from src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.spec.jsx
rename to src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.test.tsx
index a0c79ee35f..f55bd3cd22 100644
--- a/src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.spec.jsx
+++ b/src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.test.tsx
@@ -3,15 +3,13 @@ import { render, screen } from '@testing-library/react'
import SilentNetworkError from './SilentNetworkErrorWrapper'
describe('SilentNetworkErrorWrapper', () => {
- function setup(data) {
+ function setup() {
render(Hi)
}
- beforeEach(() => {
+ it('renders children', async () => {
setup()
- })
- it('renders children', async () => {
const Hello = await screen.findByText(/Hi/)
expect(Hello).toBeInTheDocument()
})
diff --git a/src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.jsx b/src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.tsx
similarity index 77%
rename from src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.jsx
rename to src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.tsx
index cfb2236aa6..89c0f9d94f 100644
--- a/src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.jsx
+++ b/src/layouts/shared/SilentNetworkErrorWrapper/SilentNetworkErrorWrapper.tsx
@@ -3,7 +3,9 @@ import { Suspense } from 'react'
import SilentNetworkError from 'layouts/shared/SilentNetworkError'
// IMPORTANT! Make sure to lazy load the children component
-function SilentNetworkErrorWrapper({ children }) {
+const SilentNetworkErrorWrapper: React.FC = ({
+ children,
+}) => {
return (
{children}
diff --git a/src/layouts/shared/SilentNetworkErrorWrapper/index.js b/src/layouts/shared/SilentNetworkErrorWrapper/index.ts
similarity index 100%
rename from src/layouts/shared/SilentNetworkErrorWrapper/index.js
rename to src/layouts/shared/SilentNetworkErrorWrapper/index.ts
diff --git a/src/old_ui/Message/Message.test.jsx b/src/old_ui/Message/Message.test.jsx
index 0750868590..fe86caa7a0 100644
--- a/src/old_ui/Message/Message.test.jsx
+++ b/src/old_ui/Message/Message.test.jsx
@@ -1,6 +1,5 @@
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import { vi } from 'vitest'
import Message from '.'
diff --git a/src/ui/TotalsNumber/TotalsNumber.spec.tsx b/src/ui/TotalsNumber/TotalsNumber.test.tsx
similarity index 50%
rename from src/ui/TotalsNumber/TotalsNumber.spec.tsx
rename to src/ui/TotalsNumber/TotalsNumber.test.tsx
index f918492e9e..19a67cdfb6 100644
--- a/src/ui/TotalsNumber/TotalsNumber.spec.tsx
+++ b/src/ui/TotalsNumber/TotalsNumber.test.tsx
@@ -1,42 +1,70 @@
-import { render, screen } from '@testing-library/react'
+import { cleanup, render, screen } from '@testing-library/react'
-import TotalsNumber from '.'
+import TotalsNumber from './TotalsNumber'
-describe('TotalsNumber', () => {
- function setup({ value, variant }: { value?: number; variant: string }) {
- render(
-
- )
- }
+afterEach(() => {
+ cleanup()
+})
+describe('TotalsNumber', () => {
describe('when rendered', () => {
it('renders commit change when there is a valid value', () => {
- setup({ value: 23, variant: 'default' })
+ render(
+
+ )
+
const changeValue = screen.getByTestId('number-value')
expect(changeValue).toHaveTextContent('23.00%')
expect(changeValue).toHaveClass("before:content-['+']")
})
it('renders negative number when change is negative', () => {
- setup({ value: -17, variant: 'default' })
+ render(
+
+ )
+
const changeValue = screen.getByTestId('change-value')
expect(changeValue).toHaveTextContent('-17.00%')
})
it('renders - when there is an invalid value', () => {
- setup({ value: undefined, variant: 'default' })
+ render(
+
+ )
+
const changeValue = screen.getByTestId('change-value')
expect(changeValue).toHaveTextContent('-')
})
it('renders 0 when you get 0 change', () => {
- setup({ value: 0, variant: 'default' })
+ render(
+
+ )
+
const changeValue = screen.getByTestId('change-value')
expect(changeValue).toHaveTextContent('0')
})
diff --git a/src/ui/TruncatedMessage/TruncatedMessage.spec.tsx b/src/ui/TruncatedMessage/TruncatedMessage.test.tsx
similarity index 96%
rename from src/ui/TruncatedMessage/TruncatedMessage.spec.tsx
rename to src/ui/TruncatedMessage/TruncatedMessage.test.tsx
index 6aafd865cb..505e04651a 100644
--- a/src/ui/TruncatedMessage/TruncatedMessage.spec.tsx
+++ b/src/ui/TruncatedMessage/TruncatedMessage.test.tsx
@@ -1,10 +1,14 @@
-import { render, screen } from '@testing-library/react'
+import { cleanup, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useTruncation } from './hooks'
import TruncatedMessage from './TruncatedMessage'
-jest.mock('./hooks')
+vi.mock('./hooks')
+
+afterEach(() => {
+ cleanup()
+})
describe('TruncatedMessage', () => {
function setup({ canTruncate = false }) {
diff --git a/src/ui/TruncatedMessage/hooks/useTruncation.spec.ts b/src/ui/TruncatedMessage/hooks/useTruncation.test.ts
similarity index 84%
rename from src/ui/TruncatedMessage/hooks/useTruncation.spec.ts
rename to src/ui/TruncatedMessage/hooks/useTruncation.test.ts
index 926f452f6a..aa1744df20 100644
--- a/src/ui/TruncatedMessage/hooks/useTruncation.spec.ts
+++ b/src/ui/TruncatedMessage/hooks/useTruncation.test.ts
@@ -1,5 +1,4 @@
-import { renderHook } from '@testing-library/react'
-import React from 'react'
+import { cleanup, renderHook } from '@testing-library/react'
import { useTruncation } from './useTruncation'
@@ -24,9 +23,24 @@ class ResizeObserver {
global.window.ResizeObserver = ResizeObserver
-describe('useTruncation', () => {
- let useRefSpy: any
+const mocks = vi.hoisted(() => ({
+ useRef: vi.fn(),
+}))
+
+vi.mock('react', async () => {
+ const original = await vi.importActual('react')
+
+ return {
+ ...original,
+ useRef: mocks.useRef,
+ }
+})
+afterEach(() => {
+ cleanup()
+})
+
+describe('useTruncation', () => {
function setup({
clientHeight = 0,
scrollHeight = 0,
@@ -45,7 +59,8 @@ describe('useTruncation', () => {
scrollWidth,
},
}
- useRefSpy = jest.spyOn(React, 'useRef').mockReturnValue(refReturn)
+
+ mocks.useRef.mockReturnValue(refReturn)
if (enableEntry) {
entry = {
@@ -62,7 +77,7 @@ describe('useTruncation', () => {
}
afterEach(() => {
- jest.restoreAllMocks()
+ vi.restoreAllMocks()
})
describe('scrolls are larger then clients', () => {
@@ -71,7 +86,7 @@ describe('useTruncation', () => {
setup({ scrollHeight: 10, scrollWidth: 10 })
const { result } = renderHook(() => useTruncation())
- expect(useRefSpy).toHaveBeenCalled()
+ expect(mocks.useRef).toHaveBeenCalled()
expect(result.current.canTruncate).toBeTruthy()
})
})
@@ -81,7 +96,7 @@ describe('useTruncation', () => {
setup({ scrollHeight: 10, scrollWidth: 0 })
const { result } = renderHook(() => useTruncation())
- expect(useRefSpy).toHaveBeenCalled()
+ expect(mocks.useRef).toHaveBeenCalled()
expect(result.current.canTruncate).toBeTruthy()
})
})
@@ -91,7 +106,7 @@ describe('useTruncation', () => {
setup({ scrollHeight: 0, scrollWidth: 10 })
const { result } = renderHook(() => useTruncation())
- expect(useRefSpy).toHaveBeenCalled()
+ expect(mocks.useRef).toHaveBeenCalled()
expect(result.current.canTruncate).toBeTruthy()
})
})
@@ -103,7 +118,7 @@ describe('useTruncation', () => {
setup({ clientHeight: 10, clientWidth: 10 })
const { result } = renderHook(() => useTruncation())
- expect(useRefSpy).toHaveBeenCalled()
+ expect(mocks.useRef).toHaveBeenCalled()
expect(result.current.canTruncate).toBeFalsy()
})
})
@@ -113,7 +128,7 @@ describe('useTruncation', () => {
setup({ clientHeight: 10, clientWidth: 0 })
const { result } = renderHook(() => useTruncation())
- expect(useRefSpy).toHaveBeenCalled()
+ expect(mocks.useRef).toHaveBeenCalled()
expect(result.current.canTruncate).toBeFalsy()
})
})
@@ -123,7 +138,7 @@ describe('useTruncation', () => {
setup({ clientHeight: 0, clientWidth: 10 })
const { result } = renderHook(() => useTruncation())
- expect(useRefSpy).toHaveBeenCalled()
+ expect(mocks.useRef).toHaveBeenCalled()
expect(result.current.canTruncate).toBeFalsy()
})
})
diff --git a/src/vitest.setup.ts b/src/vitest.setup.ts
index 04b5458531..43131cdb46 100644
--- a/src/vitest.setup.ts
+++ b/src/vitest.setup.ts
@@ -1,6 +1,5 @@
import * as matchers from '@testing-library/jest-dom/matchers'
import { cleanup } from '@testing-library/react'
-import { vi } from 'vitest'
import '@testing-library/jest-dom/vitest'
// not sure why this lint is being fired here so I'm disabling it
diff --git a/yarn.lock b/yarn.lock
index 06aa13a2e3..4eebaee762 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6871,6 +6871,25 @@ __metadata:
languageName: node
linkType: hard
+"@vitest/eslint-plugin@npm:^1.1.4":
+ version: 1.1.4
+ resolution: "@vitest/eslint-plugin@npm:1.1.4"
+ peerDependencies:
+ "@typescript-eslint/utils": ">= 8.0"
+ eslint: ">= 8.57.0"
+ typescript: ">= 5.0.0"
+ vitest: "*"
+ peerDependenciesMeta:
+ "@typescript-eslint/utils":
+ optional: true
+ typescript:
+ optional: true
+ vitest:
+ optional: true
+ checksum: 10c0/e1de76593acefa063498081978746750fcd4906f9de31a463e7675b98d44b0e600c1b9d54d1873f6c01efcf2230184f414d3a4e9a7933e7105ae60c0ea18591c
+ languageName: node
+ linkType: hard
+
"@vitest/expect@npm:2.1.1":
version: 2.1.1
resolution: "@vitest/expect@npm:2.1.1"
@@ -11736,6 +11755,7 @@ __metadata:
"@vitejs/plugin-react": "npm:^4.3.1"
"@vitest/coverage-istanbul": "npm:^2.1.1"
"@vitest/coverage-v8": "npm:^2.1.1"
+ "@vitest/eslint-plugin": "npm:^1.1.4"
"@vitest/ui": "npm:^2.1.1"
autoprefixer: "npm:^10.4.14"
classnames: "npm:^2.3.1"