diff --git a/packages/router/jest.setup.ts b/packages/router/jest.setup.ts deleted file mode 100644 index 47cb7c090c57..000000000000 --- a/packages/router/jest.setup.ts +++ /dev/null @@ -1,3 +0,0 @@ -import '@testing-library/jest-dom' - -globalThis.scrollTo = jest.fn() diff --git a/packages/router/package.json b/packages/router/package.json index dd45715ed7d8..890f31fea959 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -20,9 +20,9 @@ "build:types": "tsc --build --verbose", "build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx\" --ignore dist --exec \"yarn build\"", "prepublishOnly": "NODE_ENV=production yarn build", - "test": "jest", + "test": "vitest run", "test:types": "tstyche", - "test:watch": "yarn test --watch" + "test:watch": "vitest watch" }, "dependencies": { "@babel/runtime-corejs3": "7.24.5", @@ -32,14 +32,14 @@ "devDependencies": { "@babel/cli": "7.24.5", "@babel/core": "^7.22.20", + "@testing-library/jest-dom": "6.4.5", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", - "jest": "29.7.0", - "jest-environment-jsdom": "29.7.0", "react": "19.0.0-beta-04b058868c-20240508", "react-dom": "19.0.0-beta-04b058868c-20240508", "tstyche": "1.1.0", - "typescript": "5.4.5" + "typescript": "5.4.5", + "vitest": "1.6.0" }, "peerDependencies": { "react": "19.0.0-beta-04b058868c-20240508", diff --git a/packages/router/src/PageLoadingContext.tsx b/packages/router/src/PageLoadingContext.tsx index 7101f363a9df..f9001604fc98 100644 --- a/packages/router/src/PageLoadingContext.tsx +++ b/packages/router/src/PageLoadingContext.tsx @@ -1,4 +1,4 @@ -import { useContext, useState } from 'react' +import React, { useContext, useState } from 'react' import { createNamedContext } from './createNamedContext' diff --git a/packages/router/src/__tests__/analyzeRoutes.test.tsx b/packages/router/src/__tests__/analyzeRoutes.test.tsx index 8049be7a308d..d15dc9474c0e 100644 --- a/packages/router/src/__tests__/analyzeRoutes.test.tsx +++ b/packages/router/src/__tests__/analyzeRoutes.test.tsx @@ -1,5 +1,7 @@ import React, { isValidElement } from 'react' +import { describe, test, expect } from 'vitest' + import { analyzeRoutes } from '../analyzeRoutes' import { Route } from '../Route' import { Router } from '../router' diff --git a/packages/router/src/__tests__/history.test.tsx b/packages/router/src/__tests__/history.test.tsx index f749812b3c71..5681e9878e09 100644 --- a/packages/router/src/__tests__/history.test.tsx +++ b/packages/router/src/__tests__/history.test.tsx @@ -1,3 +1,5 @@ +import { describe, it, expect } from 'vitest' + import { navigate } from '../history' describe('navigate', () => { diff --git a/packages/router/src/__tests__/links.test.tsx b/packages/router/src/__tests__/links.test.tsx index dba4eab33205..54f042ecc7f9 100644 --- a/packages/router/src/__tests__/links.test.tsx +++ b/packages/router/src/__tests__/links.test.tsx @@ -1,6 +1,7 @@ import React from 'react' import { render } from '@testing-library/react' +import { describe, it, expect } from 'vitest' import { LocationProvider } from '../location' import { NavLink } from '../navLink' diff --git a/packages/router/src/__tests__/location.test.tsx b/packages/router/src/__tests__/location.test.tsx index 4ea876d610bf..1c5cb21ad6dc 100644 --- a/packages/router/src/__tests__/location.test.tsx +++ b/packages/router/src/__tests__/location.test.tsx @@ -1,5 +1,7 @@ +import React from 'react' + import { render } from '@testing-library/react' -import '@testing-library/jest-dom/jest-globals' +import { describe, it, expect } from 'vitest' import { LocationProvider, useLocation } from '../location' diff --git a/packages/router/src/__tests__/nestedSets.test.tsx b/packages/router/src/__tests__/nestedSets.test.tsx index cdbec24c6765..3cc21d4b682c 100644 --- a/packages/router/src/__tests__/nestedSets.test.tsx +++ b/packages/router/src/__tests__/nestedSets.test.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import type { ReactNode } from 'react' -import '@testing-library/jest-dom/jest-globals' import { act, render } from '@testing-library/react' +import { beforeEach, beforeAll, afterAll, test, expect, vi } from 'vitest' import { navigate, Route, Router } from '../' import { Private, Set } from '../Set' @@ -30,7 +30,7 @@ let err: typeof console.error beforeAll(() => { // Hide thrown exceptions. We're expecting them, and they clutter the output err = console.error - console.error = jest.fn() + console.error = vi.fn() }) afterAll(() => { @@ -118,10 +118,10 @@ test('Sets nested in `` should not error out if no authenticated pr }) test('Nested sets should not cause a re-mount of parent wrap components', async () => { - const layoutOneMount = jest.fn() - const layoutOneUnmount = jest.fn() - const layoutTwoMount = jest.fn() - const layoutTwoUnmount = jest.fn() + const layoutOneMount = vi.fn() + const layoutOneUnmount = vi.fn() + const layoutTwoMount = vi.fn() + const layoutTwoUnmount = vi.fn() const Layout1 = ({ children }: LayoutProps) => { React.useEffect(() => { diff --git a/packages/router/src/__tests__/pageLoadingContext.test.tsx b/packages/router/src/__tests__/pageLoadingContext.test.tsx index eb4088fd20ea..cbc35ae97f0f 100644 --- a/packages/router/src/__tests__/pageLoadingContext.test.tsx +++ b/packages/router/src/__tests__/pageLoadingContext.test.tsx @@ -1,7 +1,7 @@ let mockDelay = 0 -jest.mock('../page', () => { - const actualUtil = jest.requireActual('../util') - const { lazy } = jest.requireActual('react') +vi.mock('../page', async (importOriginal) => { + const actualUtil = await importOriginal() + const { lazy } = await vi.importActual('react') return { ...actualUtil, @@ -10,7 +10,7 @@ jest.mock('../page', () => { prerenderLoader: () => ({ default: specOrPage }), LazyComponent: lazy( () => - new Promise((resolve) => + new Promise((resolve) => setTimeout(() => resolve({ default: specOrPage }), mockDelay), ), ), @@ -20,8 +20,8 @@ jest.mock('../page', () => { import React, { useEffect, useState } from 'react' -import '@testing-library/jest-dom' import { act, configure, render, waitFor } from '@testing-library/react' +import { vi, beforeEach, afterEach, test, expect } from 'vitest' import type { AuthContextInterface } from '@redwoodjs/auth' @@ -36,6 +36,7 @@ import { useParams, } from '..' import { useLocation } from '../location' +import type Page from '../page' import type { Spec } from '../page' import { usePageLoadingContext } from '../PageLoadingContext' diff --git a/packages/router/src/__tests__/redirect.test.tsx b/packages/router/src/__tests__/redirect.test.tsx index 95fdaae2d6ab..1b24b7d808d8 100644 --- a/packages/router/src/__tests__/redirect.test.tsx +++ b/packages/router/src/__tests__/redirect.test.tsx @@ -1,6 +1,7 @@ import React from 'react' import { act, render, waitFor } from '@testing-library/react' +import { test } from 'vitest' import { navigate } from '../history' import { Route } from '../Route' diff --git a/packages/router/src/__tests__/route-announcer.test.tsx b/packages/router/src/__tests__/route-announcer.test.tsx index 504c15399f56..dafdaff35213 100644 --- a/packages/router/src/__tests__/route-announcer.test.tsx +++ b/packages/router/src/__tests__/route-announcer.test.tsx @@ -1,7 +1,7 @@ import React from 'react' import { render, waitFor, act } from '@testing-library/react' -import '@testing-library/jest-dom/jest-globals' +import { beforeEach, test, expect } from 'vitest' import { getAnnouncement } from '../a11yUtils' import { navigate } from '../history' diff --git a/packages/router/src/__tests__/route-focus.test.tsx b/packages/router/src/__tests__/route-focus.test.tsx index 2ccd51d3b4ac..f821f5efcb01 100644 --- a/packages/router/src/__tests__/route-focus.test.tsx +++ b/packages/router/src/__tests__/route-focus.test.tsx @@ -1,5 +1,7 @@ +import React from 'react' + import { render, waitFor } from '@testing-library/react' -import '@testing-library/jest-dom/jest-globals' +import { test, beforeEach, expect } from 'vitest' import { getFocus } from '../a11yUtils' import { namedRoutes as routes } from '../namedRoutes' @@ -49,7 +51,6 @@ const RouteFocusNegativeTabIndexPage = () => ( beforeEach(() => { window.history.pushState({}, '', '/') - // @ts-expect-error - No type gen here for routes like there is in a real app Object.keys(routes).forEach((key) => delete routes[key]) }) diff --git a/packages/router/src/__tests__/route-validators.test.tsx b/packages/router/src/__tests__/route-validators.test.tsx index f8165a19fb60..9078286c83cf 100644 --- a/packages/router/src/__tests__/route-validators.test.tsx +++ b/packages/router/src/__tests__/route-validators.test.tsx @@ -1,3 +1,7 @@ +import React from 'react' + +import { describe, it, expect } from 'vitest' + import { Route } from '../Route' import { isValidRoute } from '../route-validators' diff --git a/packages/router/src/__tests__/routeScrollReset.test.tsx b/packages/router/src/__tests__/routeScrollReset.test.tsx index ac665b919477..98e3f58cc8af 100644 --- a/packages/router/src/__tests__/routeScrollReset.test.tsx +++ b/packages/router/src/__tests__/routeScrollReset.test.tsx @@ -1,7 +1,8 @@ import React from 'react' -import '@testing-library/jest-dom/jest-globals' import { act, cleanup, render, screen } from '@testing-library/react' +import { describe, beforeEach, afterEach, it, expect } from 'vitest' +import type { Mock } from 'vitest' import { navigate } from '../history' import { namedRoutes as routes } from '../namedRoutes' @@ -18,12 +19,8 @@ describe('Router scroll reset', () => { ) - // Redfine the mocks here again (already done in jest.setup) - // Otherwise the mock doesn't clear for some reason - globalThis.scrollTo = jest.fn() - beforeEach(async () => { - ;(globalThis.scrollTo as jest.Mock).mockClear() + ;(globalThis.scrollTo as Mock).mockClear() render() // Make sure we're starting on the home route diff --git a/packages/router/src/__tests__/router.test.tsx b/packages/router/src/__tests__/router.test.tsx index 40027a6f16fb..f86d47771fac 100644 --- a/packages/router/src/__tests__/router.test.tsx +++ b/packages/router/src/__tests__/router.test.tsx @@ -1,7 +1,15 @@ import React, { useEffect, useState } from 'react' -import '@testing-library/jest-dom/jest-globals' import { act, fireEvent, render, waitFor } from '@testing-library/react' +import { + beforeEach, + describe, + test, + beforeAll, + afterAll, + expect, + vi, +} from 'vitest' import type { AuthContextInterface, UseAuth } from '@redwoodjs/auth' @@ -1157,7 +1165,7 @@ describe('Unauthorized redirect error messages', () => { beforeAll(() => { err = console.error - console.error = jest.fn() + console.error = vi.fn() }) afterAll(() => { diff --git a/packages/router/src/__tests__/set.test.tsx b/packages/router/src/__tests__/set.test.tsx index 3c7ae3ab174b..c787a289e7f1 100644 --- a/packages/router/src/__tests__/set.test.tsx +++ b/packages/router/src/__tests__/set.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import type { ReactNode } from 'react' import { act, render, waitFor } from '@testing-library/react' -import '@testing-library/jest-dom/jest-globals' +import { beforeEach, test, describe, vi, expect } from 'vitest' import { navigate } from '../history' import { Route } from '../Route' @@ -174,8 +174,8 @@ describe('Navigating Sets', () => { const Page = () =>

Page

test('Sets should not cause a re-mount of wrap components when navigating within the set', async () => { - const layoutOneMount = jest.fn() - const layoutOneUnmount = jest.fn() + const layoutOneMount = vi.fn() + const layoutOneUnmount = vi.fn() const Layout1 = ({ children }: LayoutProps) => { React.useEffect(() => { @@ -223,8 +223,8 @@ describe('Navigating Sets', () => { }) test('Sets should make wrap components remount when navigating between separate sets with the same wrap component', async () => { - const layoutOneMount = jest.fn() - const layoutOneUnmount = jest.fn() + const layoutOneMount = vi.fn() + const layoutOneUnmount = vi.fn() const Layout1 = ({ children }: LayoutProps) => { React.useEffect(() => { diff --git a/packages/router/src/__tests__/setContextReuse.test.tsx b/packages/router/src/__tests__/setContextReuse.test.tsx index 5d6a698ec2f1..123b01254c80 100644 --- a/packages/router/src/__tests__/setContextReuse.test.tsx +++ b/packages/router/src/__tests__/setContextReuse.test.tsx @@ -1,12 +1,11 @@ import React from 'react' import { act, render, waitFor } from '@testing-library/react' +import { test } from 'vitest' import { Route, Router, navigate } from '../' import { Set } from '../Set' -import '@testing-library/jest-dom/jest-globals' - const HomePage = () => { return

Home Page

} diff --git a/packages/router/src/__tests__/useMatch.test.tsx b/packages/router/src/__tests__/useMatch.test.tsx index 86542353d729..67f162b91359 100644 --- a/packages/router/src/__tests__/useMatch.test.tsx +++ b/packages/router/src/__tests__/useMatch.test.tsx @@ -1,8 +1,7 @@ import React from 'react' -import '@testing-library/jest-dom' - import { render, renderHook as tlrRenderHook } from '@testing-library/react' +import { describe, it, expect, afterEach } from 'vitest' import { Link } from '../link' import { LocationProvider } from '../location' @@ -40,7 +39,7 @@ describe('useMatch', () => { return ( ) @@ -55,7 +54,7 @@ describe('useMatch', () => { , ) - expect(getByText(/Dunder Mifflin/)).toHaveStyle('color: green') + expect(getByText(/Dunder Mifflin/)).toHaveStyle('color: #0F0') }) it('returns a match on the same pathname with search parameters', () => { @@ -70,7 +69,7 @@ describe('useMatch', () => { , ) - expect(getByText(/Dunder Mifflin/)).toHaveStyle('color: green') + expect(getByText(/Dunder Mifflin/)).toHaveStyle('color: #0F0') }) it('does NOT receive active class on different path', () => { @@ -82,7 +81,7 @@ describe('useMatch', () => { , ) - expect(getByText(/Dunder Mifflin/)).toHaveStyle('color: red') + expect(getByText(/Dunder Mifflin/)).toHaveStyle('color: #F00') }) it('does NOT receive active class on the same pathname with different parameters', () => { @@ -97,7 +96,7 @@ describe('useMatch', () => { , ) - expect(getByText(/Dunder Mifflin/)).toHaveStyle('color: red') + expect(getByText(/Dunder Mifflin/)).toHaveStyle('color: #F00') }) describe('routeParams', () => { diff --git a/packages/router/src/__tests__/useRoutePaths.test.tsx b/packages/router/src/__tests__/useRoutePaths.test.tsx index 2da9afe34dfa..2d9ff38117e1 100644 --- a/packages/router/src/__tests__/useRoutePaths.test.tsx +++ b/packages/router/src/__tests__/useRoutePaths.test.tsx @@ -1,7 +1,7 @@ -/** @jest-environment jsdom */ import React from 'react' import { render, act } from '@testing-library/react' +import { test } from 'vitest' import { navigate } from '../history' import { Route } from '../Route' diff --git a/packages/router/src/__tests__/util.test.ts b/packages/router/src/__tests__/util.test.ts index bcab6ea40f4c..dcb09fa581b1 100644 --- a/packages/router/src/__tests__/util.test.ts +++ b/packages/router/src/__tests__/util.test.ts @@ -1,3 +1,5 @@ +import { describe, it, expect } from 'vitest' + import { paramsForRoute, matchPath, diff --git a/packages/router/src/link.tsx b/packages/router/src/link.tsx index 11d13aa6389f..f3a5737c6a1f 100644 --- a/packages/router/src/link.tsx +++ b/packages/router/src/link.tsx @@ -3,7 +3,7 @@ // This needs to be a client component because it uses onClick, and the onClick // event handler can't be serialized when passed as an RSC Flight response -import { forwardRef } from 'react' +import React, { forwardRef } from 'react' import { navigate } from './history' diff --git a/packages/router/src/navLink.tsx b/packages/router/src/navLink.tsx index ecd642f5c65f..15b18db21a28 100644 --- a/packages/router/src/navLink.tsx +++ b/packages/router/src/navLink.tsx @@ -1,6 +1,6 @@ 'use client' -import { forwardRef } from 'react' +import React, { forwardRef } from 'react' import { Link } from './link' import type { LinkProps } from './link' diff --git a/packages/router/vitest.config.mts b/packages/router/vitest.config.mts new file mode 100644 index 000000000000..b5390fdb0a38 --- /dev/null +++ b/packages/router/vitest.config.mts @@ -0,0 +1,11 @@ +import { defineConfig, configDefaults } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: [...configDefaults.exclude, '**/__typetests__'], + environment: 'jsdom', + setupFiles: ['vitest.setup.mts'], + }, +}) + + diff --git a/packages/router/vitest.setup.mts b/packages/router/vitest.setup.mts new file mode 100644 index 000000000000..8aa055054640 --- /dev/null +++ b/packages/router/vitest.setup.mts @@ -0,0 +1,14 @@ +import '@testing-library/jest-dom/vitest' + +import { afterEach, vi } from 'vitest' +import { cleanup } from '@testing-library/react' + +vi.spyOn(globalThis, 'scrollTo').mockImplementation(() => {}) + +afterEach(() => { + // If vitest globals are enabled testing-library will clean up after each + // test automatically, but we don't enable globals, so we have to manually + // clean up here + // https://testing-library.com/docs/react-testing-library/api/#cleanup + cleanup() +}) diff --git a/yarn.lock b/yarn.lock index 1824e2ecd062..3f847f7a96ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8537,15 +8537,15 @@ __metadata: "@babel/core": "npm:^7.22.20" "@babel/runtime-corejs3": "npm:7.24.5" "@redwoodjs/auth": "workspace:*" + "@testing-library/jest-dom": "npm:6.4.5" "@types/react": "npm:^18.2.55" "@types/react-dom": "npm:^18.2.19" core-js: "npm:3.37.1" - jest: "npm:29.7.0" - jest-environment-jsdom: "npm:29.7.0" react: "npm:19.0.0-beta-04b058868c-20240508" react-dom: "npm:19.0.0-beta-04b058868c-20240508" tstyche: "npm:1.1.0" typescript: "npm:5.4.5" + vitest: "npm:1.6.0" peerDependencies: react: 19.0.0-beta-04b058868c-20240508 react-dom: 19.0.0-beta-04b058868c-20240508