Skip to content

Commit

Permalink
Switch from jest to vitest in new app templates (#3985)
Browse files Browse the repository at this point in the history
* Switch from jest to vitest in new app templates

* Finish vitest setup

* Handle vitest.config.js vs vitest.config.ts

* Add proper vitest config to js templates

* Add changeset

* Update READMEs in new app templates

* Fix tests after vitest upgrade

* Update spyOn references in tests
  • Loading branch information
beerose authored Nov 28, 2022
1 parent 650a157 commit 8c247e2
Show file tree
Hide file tree
Showing 37 changed files with 1,542 additions and 359 deletions.
6 changes: 6 additions & 0 deletions .changeset/curvy-days-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@blitzjs/rpc": patch
"@blitzjs/generator": patch
---

Switch from jest to vitest in new app templates
4 changes: 2 additions & 2 deletions apps/toolkit-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ TODO

This is a [Blitz.js](https://github.com/blitz-js/blitz) app.

# ****name****
# \***\*name\*\***

## Getting Started

Expand Down Expand Up @@ -38,7 +38,7 @@ Runs your tests using Jest.
yarn test
```

Blitz comes with a test setup using [Jest](https://jestjs.io/) and [react-testing-library](https://testing-library.com/).
Blitz comes with a test setup using [Vitest](https://vitest.dev/) and [react-testing-library](https://testing-library.com/).

## Commands

Expand Down
7 changes: 0 additions & 7 deletions apps/toolkit-app/jest.config.js

This file was deleted.

13 changes: 8 additions & 5 deletions apps/toolkit-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"lint": "next lint",
"prisma:start": "prisma generate && prisma migrate deploy",
"prisma:studio": "prisma studio",
"test:local": "jest"
"test:local": "prisma generate && vitest run --passWithNoTests",
"test:watch": "vitest"
},
"prisma": {
"schema": "db/schema.prisma"
Expand Down Expand Up @@ -41,25 +42,27 @@
},
"devDependencies": {
"@next/bundle-analyzer": "12.0.8",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "13.4.0",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "29.2.2",
"@types/node": "18.11.9",
"@types/preview-email": "2.0.1",
"@types/react": "18.0.25",
"@typescript-eslint/eslint-plugin": "5.42.1",
"@vitejs/plugin-react": "2.2.0",
"eslint": "8.27.0",
"eslint-config-next": "12.3.1",
"eslint-config-prettier": "8.5.0",
"husky": "8.0.2",
"jest": "29.3.0",
"jest-environment-jsdom": "29.3.0",
"jsdom": "20.0.3",
"lint-staged": "13.0.3",
"prettier": "^2.7.1",
"prettier-plugin-prisma": "4.4.0",
"pretty-quick": "3.1.3",
"preview-email": "3.0.7",
"typescript": "^4.8.4"
"typescript": "^4.8.4",
"vite-tsconfig-paths": "3.6.0",
"vitest": "0.25.3"
},
"private": true
}
64 changes: 64 additions & 0 deletions apps/toolkit-app/src/auth/mutations/forgotPassword.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { vi, describe, it, beforeEach } from "vitest"
import db from "db"
import { hash256 } from "@blitzjs/auth"
import forgotPassword from "./forgotPassword"
import previewEmail from "preview-email"
import { Ctx } from "@blitzjs/next"

beforeEach(async () => {
await db.$reset()
})

const generatedToken = "plain-token"
vi.mock("@blitzjs/auth", async () => {
const auth = await vi.importActual<Record<string, unknown>>("@blitzjs/auth")!
return {
...auth,
generateToken: () => generatedToken,
}
})

vi.mock("preview-email", () => ({ default: vi.fn() }))

describe("forgotPassword mutation", () => {
it("does not throw error if user doesn't exist", async () => {
await expect(forgotPassword({ email: "[email protected]" }, {} as Ctx)).resolves.not.toThrow()
})

it("works correctly", async () => {
// Create test user
const user = await db.user.create({
data: {
email: "[email protected]",
tokens: {
// Create old token to ensure it's deleted
create: {
type: "RESET_PASSWORD",
hashedToken: "token",
expiresAt: new Date(),
sentTo: "[email protected]",
},
},
},
include: { tokens: true },
})

// Invoke the mutation
await forgotPassword({ email: user.email }, {} as Ctx)

const tokens = await db.token.findMany({ where: { userId: user.id } })
const token = tokens[0]
if (!user.tokens[0]) throw new Error("Missing user token")
if (!token) throw new Error("Missing token")

// delete's existing tokens
expect(tokens.length).toBe(1)

expect(token.id).not.toBe(user.tokens[0].id)
expect(token.type).toBe("RESET_PASSWORD")
expect(token.sentTo).toBe(user.email)
expect(token.hashedToken).toBe(hash256(generatedToken))
expect(token.expiresAt > new Date()).toBe(true)
expect(previewEmail).toBeCalled()
})
})
83 changes: 83 additions & 0 deletions apps/toolkit-app/src/auth/mutations/resetPassword.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { vi, describe, it, beforeEach, expect } from "vitest"
import resetPassword from "./resetPassword"
import db from "db"
import { SecurePassword, hash256 } from "@blitzjs/auth"

beforeEach(async () => {
await db.$reset()
})

const mockCtx: any = {
session: {
$create: vi.fn(),
},
}

describe("resetPassword mutation", () => {
it("works correctly", async () => {
expect(true).toBe(true)

// Create test user
const goodToken = "randomPasswordResetToken"
const expiredToken = "expiredRandomPasswordResetToken"
const future = new Date()
future.setHours(future.getHours() + 4)
const past = new Date()
past.setHours(past.getHours() - 4)

const user = await db.user.create({
data: {
email: "[email protected]",
tokens: {
// Create old token to ensure it's deleted
create: [
{
type: "RESET_PASSWORD",
hashedToken: hash256(expiredToken),
expiresAt: past,
sentTo: "[email protected]",
},
{
type: "RESET_PASSWORD",
hashedToken: hash256(goodToken),
expiresAt: future,
sentTo: "[email protected]",
},
],
},
},
include: { tokens: true },
})

const newPassword = "newPassword"

// Non-existent token
await expect(
resetPassword({ token: "no-token", password: "", passwordConfirmation: "" }, mockCtx)
).rejects.toThrowError()

// Expired token
await expect(
resetPassword(
{ token: expiredToken, password: newPassword, passwordConfirmation: newPassword },
mockCtx
)
).rejects.toThrowError()

// Good token
await resetPassword(
{ token: goodToken, password: newPassword, passwordConfirmation: newPassword },
mockCtx
)

// Delete's the token
const numberOfTokens = await db.token.count({ where: { userId: user.id } })
expect(numberOfTokens).toBe(0)

// Updates user's password
const updatedUser = await db.user.findFirst({ where: { id: user.id } })
expect(await SecurePassword.verify(updatedUser!.hashedPassword, newPassword)).toBe(
SecurePassword.VALID
)
})
})
1 change: 0 additions & 1 deletion apps/toolkit-app/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import logout from "src/auth/mutations/logout"
import logo from "public/logo.png"
import { useMutation } from "@blitzjs/rpc"
import { Routes, BlitzPage } from "@blitzjs/next"
import { getSession, useSession } from "@blitzjs/auth"

/*
* This file is just for a pleasant getting started page for your new app.
Expand Down
35 changes: 20 additions & 15 deletions apps/toolkit-app/test/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import { useCurrentUser } from "src/users/hooks/useCurrentUser"
/**
* @vitest-environment jsdom
*/

import { expect, vi, test } from "vitest"
import { render } from "test/utils"

import Home from "../src/pages/index"

jest.mock("src/users/hooks/useCurrentUser")
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
vi.mock("public/logo.png", () => ({
default: { src: "/logo.png" },
}))

describe("renders blitz documentation link", () => {
it("test", () => {
// This is an example of how to ensure a specific item is in the document
// But it's disabled by default (by test.skip) so the test doesn't fail
// when you remove the the default content from the page
test.skip("renders blitz documentation link", () => {
// This is an example of how to ensure a specific item is in the document
// But it's disabled by default (by test.skip) so the test doesn't fail
// when you remove the the default content from the page

// This is an example on how to mock api hooks when testing
mockUseCurrentUser.mockReturnValue({
// This is an example on how to mock api hooks when testing
vi.mock("src/users/hooks/useCurrentUser", () => ({
useCurrentUser: () => ({
id: 1,
name: "User",
email: "[email protected]",
role: "user",
})
}),
}))

const { getByText } = render(<Home />)
const linkElement = getByText(/Documentation/i)
expect(linkElement).toBeInTheDocument()
})
const { getByText } = render(<Home />)
const linkElement = getByText(/Documentation/i)
expect(linkElement).toBeInTheDocument()
})
5 changes: 2 additions & 3 deletions apps/toolkit-app/test/setup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// This is the jest 'setupFilesAfterEnv' setup file
// It's a good place to set globals, add global before/after hooks, etc
import "@testing-library/jest-dom"

export {} // so TS doesn't complain
export {}
19 changes: 10 additions & 9 deletions apps/toolkit-app/test/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { vi } from "vitest"
import { render as defaultRender } from "@testing-library/react"
import { renderHook as defaultRenderHook } from "@testing-library/react-hooks"
import { NextRouter } from "next/router"
Expand Down Expand Up @@ -82,16 +83,16 @@ export const mockRouter: NextRouter = {
isReady: true,
isLocaleDomain: false,
isPreview: false,
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
back: jest.fn(),
prefetch: jest.fn(),
beforePopState: jest.fn(),
push: vi.fn(),
replace: vi.fn(),
reload: vi.fn(),
back: vi.fn(),
prefetch: vi.fn(),
beforePopState: vi.fn(),
events: {
on: jest.fn(),
off: jest.fn(),
emit: jest.fn(),
on: vi.fn(),
off: vi.fn(),
emit: vi.fn(),
},
isFallback: false,
}
Expand Down
17 changes: 17 additions & 0 deletions apps/toolkit-app/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { defineConfig } from "vitest/config"

import react from "@vitejs/plugin-react"
import tsconfigPaths from "vite-tsconfig-paths"

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), tsconfigPaths()],
test: {
dir: "./",
globals: true,
setupFiles: "./test/setup.ts",
coverage: {
reporter: ["text", "json", "html"],
},
},
})
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"prettier-plugin-prisma": "4.4.0",
"pretty-quick": "3.1.3",
"turbo": "1.4.2",
"vitest": "0.8.2",
"vitest": "0.25.3",
"wait-on": "6.0.1"
},
"npmClient": "pnpm",
Expand Down
8 changes: 4 additions & 4 deletions packages/blitz-auth/src/client/auth-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @vitest-environment jsdom
*/

import {vi, expect, describe, it, beforeAll, afterAll, spyOn, SpyInstance} from "vitest"
import {vi, expect, describe, it, beforeAll, afterAll, SpyInstance} from "vitest"
import {parsePublicDataToken, getPublicDataStore, useSession} from "./index"
import {COOKIE_PUBLIC_DATA_TOKEN} from "../shared"
import {toBase64} from "b64-lite"
Expand Down Expand Up @@ -54,7 +54,7 @@ describe("parsePublicDataToken", () => {

describe("publicDataStore", () => {
it("calls readCookie token on init", () => {
const spy = spyOn(stdlib, "readCookie")
const spy = vi.spyOn(stdlib, "readCookie")
getPublicDataStore()
expect(spy).toHaveBeenCalledWith(COOKIE_PUBLIC_DATA_TOKEN())
spy.mockRestore()
Expand All @@ -64,7 +64,7 @@ describe("publicDataStore", () => {
let localStorageSpy: SpyInstance

beforeAll(() => {
localStorageSpy = spyOn(Storage.prototype, "setItem")
localStorageSpy = vi.spyOn(Storage.prototype, "setItem")
})

it("sets local storage", () => {
Expand All @@ -84,7 +84,7 @@ describe("publicDataStore", () => {

describe("clear", () => {
it("clears the cookie", () => {
const spy = spyOn(stdlib, "deleteCookie")
const spy = vi.spyOn(stdlib, "deleteCookie")
getPublicDataStore().clear()
expect(spy).toHaveBeenCalledWith(COOKIE_PUBLIC_DATA_TOKEN())
})
Expand Down
4 changes: 2 additions & 2 deletions packages/blitz-next/src/error-boundary-hook.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @vitest-environment jsdom
*/

import {afterEach, beforeEach, spyOn, test, expect, MockedFunction, vi} from "vitest"
import {afterEach, beforeEach, test, expect, MockedFunction, vi} from "vitest"
import {render, screen, cleanup, waitFor} from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import * as React from "react"
Expand All @@ -11,7 +11,7 @@ import {ErrorBoundary, useErrorHandler} from "./error-boundary"

beforeEach(() => {
global.IS_REACT_ACT_ENVIRONMENT = true
spyOn(console, "error").mockImplementation(() => {})
vi.spyOn(console, "error").mockImplementation(() => {})
})
afterEach(() => {
vi.resetAllMocks()
Expand Down
Loading

0 comments on commit 8c247e2

Please sign in to comment.