From c3c789740aa8078c625c06d89c5a728cc3393e5b Mon Sep 17 00:00:00 2001 From: Jhonny Michel Date: Mon, 6 Feb 2023 10:47:28 -0500 Subject: [PATCH] Fixing tests for blitz apps that use vitest (#4072) Co-authored-by: Siddharth Suresh Closes https://github.com/blitz-js/blitz/issues/4065 --- .changeset/plenty-kiwis-greet.md | 6 ++ apps/toolkit-app/test/index.test.tsx | 2 +- integration-tests/middleware/.env | 3 +- integration-tests/qm/.env | 1 + integration-tests/rpc/.env | 1 + .../src/data-client/react-query-utils.test.ts | 65 ++++++++++++++++++- .../src/data-client/react-query-utils.ts | 9 +-- packages/blitz/src/errors.ts | 5 +- packages/blitz/src/utils/enhance-prisma.ts | 3 +- packages/blitz/src/utils/index.ts | 8 +++ .../templates/app/test/index.test.tsx | 17 +++-- 11 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 .changeset/plenty-kiwis-greet.md create mode 100644 integration-tests/qm/.env create mode 100644 integration-tests/rpc/.env diff --git a/.changeset/plenty-kiwis-greet.md b/.changeset/plenty-kiwis-greet.md new file mode 100644 index 0000000000..d0ecd3a259 --- /dev/null +++ b/.changeset/plenty-kiwis-greet.md @@ -0,0 +1,6 @@ +--- +"blitz": patch +"@blitzjs/rpc": patch +--- + +Updates internal functions and tests to support blitz apps that run tests with vitest diff --git a/apps/toolkit-app/test/index.test.tsx b/apps/toolkit-app/test/index.test.tsx index 5c6e60a55b..0703f9810a 100644 --- a/apps/toolkit-app/test/index.test.tsx +++ b/apps/toolkit-app/test/index.test.tsx @@ -27,6 +27,6 @@ test.skip("renders blitz documentation link", () => { })) const { getByText } = render() - const linkElement = getByText(/Documentation/i) + const linkElement = getByText(/Blitz Docs/i) expect(linkElement).toBeInTheDocument() }) diff --git a/integration-tests/middleware/.env b/integration-tests/middleware/.env index dc405d323a..0220cc9822 100644 --- a/integration-tests/middleware/.env +++ b/integration-tests/middleware/.env @@ -1,2 +1,3 @@ SESSION_SECRET_KEY=hsdenhJfpLHrGjgdgg3jdF8g2bYD2PaQ -HEADLESS=true \ No newline at end of file +HEADLESS=true +VITE_BLITZ_TEST_ENVIRONMENT=true \ No newline at end of file diff --git a/integration-tests/qm/.env b/integration-tests/qm/.env new file mode 100644 index 0000000000..d138747da2 --- /dev/null +++ b/integration-tests/qm/.env @@ -0,0 +1 @@ +VITE_BLITZ_TEST_ENVIRONMENT=true \ No newline at end of file diff --git a/integration-tests/rpc/.env b/integration-tests/rpc/.env new file mode 100644 index 0000000000..d138747da2 --- /dev/null +++ b/integration-tests/rpc/.env @@ -0,0 +1 @@ +VITE_BLITZ_TEST_ENVIRONMENT=true \ No newline at end of file diff --git a/packages/blitz-rpc/src/data-client/react-query-utils.test.ts b/packages/blitz-rpc/src/data-client/react-query-utils.test.ts index 238b7f8ee1..de0ae6a480 100644 --- a/packages/blitz-rpc/src/data-client/react-query-utils.test.ts +++ b/packages/blitz-rpc/src/data-client/react-query-utils.test.ts @@ -1,7 +1,7 @@ -import {describe, expect, it} from "vitest" +import {describe, expect, it, vi} from "vitest" import superJson from "superjson" -import {getQueryKey, getQueryKeyFromUrlAndParams} from "./react-query-utils" +import {getQueryKey, getQueryKeyFromUrlAndParams, validateQueryFn} from "./react-query-utils" import {RpcClient} from "./rpc" const API_ENDPOINT = "http://localhost:3000" @@ -74,4 +74,65 @@ describe("react-query-utils", () => { expect(queryKey).toEqual([API_ENDPOINT]) }) }) + + describe("validateQueryFn", () => { + const originalEnv = process.env + + function mockEnv() { + const originalEnv = process.env + + process.env = { + ...originalEnv, + } + + delete process.env.JEST_WORKER_ID + delete process.env.VITEST_WORKER_ID + delete process.env.BLITZ_TEST_ENVIRONMENT + + return process.env + } + + function restoreEnv() { + process.env = originalEnv + } + + const notAQuery = vi.fn() + const realQuery = vi.fn() + //@ts-ignore + realQuery._isRpcClient = true + + vi.mock("blitz", async () => { + const actualBlitz = await import("blitz") + return { + ...actualBlitz, + isClient: true, + } + }) + + describe("when called from test environments", () => { + it("always validate as true, allowing query functions to be mocked in tests") + const jestEnv = mockEnv() + jestEnv.JEST_WORKER_ID = "123" + expect(() => validateQueryFn(notAQuery)).not.toThrowError() + expect(() => validateQueryFn(realQuery)).not.toThrowError() + restoreEnv() + + const vitestEnv = mockEnv() + vitestEnv.VITEST_WORKER_ID = "123" + expect(() => validateQueryFn(notAQuery)).not.toThrowError() + expect(() => validateQueryFn(realQuery)).not.toThrowError() + restoreEnv() + }) + + describe("when called from outside of test environments", () => { + it("throws an error when the passed function is not a query function") + // removes jest and vitest env vars + mockEnv() + + expect(() => validateQueryFn(notAQuery)).toThrowError() + expect(() => validateQueryFn(realQuery)).not.toThrowError() + + restoreEnv() + }) + }) }) diff --git a/packages/blitz-rpc/src/data-client/react-query-utils.ts b/packages/blitz-rpc/src/data-client/react-query-utils.ts index e9e181e3b4..cfe98c6f20 100644 --- a/packages/blitz-rpc/src/data-client/react-query-utils.ts +++ b/packages/blitz-rpc/src/data-client/react-query-utils.ts @@ -1,6 +1,6 @@ import {QueryClient} from "@tanstack/react-query" import {serialize} from "superjson" -import {isClient, isServer, AsyncFunc} from "blitz" +import {isClient, isServer, AsyncFunc, isNotInUserTestEnvironment} from "blitz" import {ResolverType, RpcClient} from "./rpc" export type Resolver = (input: TInput, ctx?: any) => Promise @@ -83,13 +83,6 @@ export const emptyQueryFn: RpcClient = (() => { return fn })() -const isNotInUserTestEnvironment = () => { - if (process.env.JEST_WORKER_ID === undefined) return true - if (process.env.VITEST_WORKER_ID === undefined) return true - if (process.env.BLITZ_TEST_ENVIRONMENT !== undefined) return true - return false -} - export const validateQueryFn = ( queryFn: Resolver | RpcClient, ) => { diff --git a/packages/blitz/src/errors.ts b/packages/blitz/src/errors.ts index 0663f9b3e4..29a5c236e9 100644 --- a/packages/blitz/src/errors.ts +++ b/packages/blitz/src/errors.ts @@ -1,5 +1,6 @@ import _SuperJson from "superjson" import type {UrlObject} from "url" +import {isNotInUserTestEnvironment} from "./utils" declare module globalThis { let _BLITZ_ERROR_CLASS_REGISTERED: boolean @@ -9,7 +10,7 @@ const SuperJson: typeof _SuperJson = "default" in _SuperJson ? (_SuperJson as any).default : _SuperJson const errorProps = ["name", "message", "code", "statusCode", "meta", "url"] -if (process.env.JEST_WORKER_ID === undefined) { +if (isNotInUserTestEnvironment()) { SuperJson.allowErrorProps(...errorProps) } @@ -75,7 +76,7 @@ export class PaginationArgumentError extends Error { } } -if (process.env.JEST_WORKER_ID === undefined && !globalThis._BLITZ_ERROR_CLASS_REGISTERED) { +if (isNotInUserTestEnvironment() && !globalThis._BLITZ_ERROR_CLASS_REGISTERED) { SuperJson.registerClass(AuthenticationError, { identifier: "BlitzAuthenticationError", allowProps: errorProps, diff --git a/packages/blitz/src/utils/enhance-prisma.ts b/packages/blitz/src/utils/enhance-prisma.ts index 8af9461ff5..18b9b06e2f 100644 --- a/packages/blitz/src/utils/enhance-prisma.ts +++ b/packages/blitz/src/utils/enhance-prisma.ts @@ -1,5 +1,6 @@ import {spawn} from "cross-spawn" import which from "npm-which" +import {isNotInUserTestEnvironment} from "../index-browser" export interface Constructor { new (...args: never[]): T @@ -19,7 +20,7 @@ export const enhancePrisma = ( ): EnhancedPrismaClientConstructor => { return new Proxy(client as EnhancedPrismaClientConstructor, { construct(target, args) { - if (typeof window !== "undefined" && process.env.JEST_WORKER_ID === undefined) { + if (typeof window !== "undefined" && isNotInUserTestEnvironment()) { // Return object with $use method if in the browser // Skip in Jest tests because window is defined in Jest tests return {$use: () => {}} diff --git a/packages/blitz/src/utils/index.ts b/packages/blitz/src/utils/index.ts index 8c77bdb287..ecd7a76456 100644 --- a/packages/blitz/src/utils/index.ts +++ b/packages/blitz/src/utils/index.ts @@ -143,3 +143,11 @@ export function interopDefault(mod: any) { export function truncateString(str: string, maxLength: number): string { return str.length > maxLength ? str.substring(0, maxLength - 3) + "..." : str } + +export function isNotInUserTestEnvironment() { + if (process.env.VITE_BLITZ_TEST_ENVIRONMENT) { + return true + } + + return process.env.JEST_WORKER_ID === undefined && process.env.VITEST_WORKER_ID === undefined +} diff --git a/packages/generator/templates/app/test/index.test.tsx b/packages/generator/templates/app/test/index.test.tsx index 3178ebf95e..479c1cf9f5 100644 --- a/packages/generator/templates/app/test/index.test.tsx +++ b/packages/generator/templates/app/test/index.test.tsx @@ -4,19 +4,19 @@ import { expect, vi, test } from "vitest" import { render } from "test/utils" - + import Home from "../src/pages/index" - + vi.mock("public/logo.png", () => ({ default: {src: "/logo.png"} })) - - + + 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 vi.mock("src/users/hooks/useCurrentUser", () => ( { @@ -28,10 +28,9 @@ }) } )) - + const { getByText } = render() - const linkElement = getByText(/Documentation/i) + const linkElement = getByText(/Blitz Docs/i) expect(linkElement).toBeInTheDocument() }) - - \ No newline at end of file +