diff --git a/.changeset/real-hairs-provide.md b/.changeset/real-hairs-provide.md new file mode 100644 index 000000000..d7a17794a --- /dev/null +++ b/.changeset/real-hairs-provide.md @@ -0,0 +1,6 @@ +--- +"@suspensive/react-await": patch +"@suspensive/react": patch +--- + +refactor: add @suspensive/test-utils as dev dependency to remove unnecessary repetitive code diff --git a/configs/test-utils/.eslintrc.cjs b/configs/test-utils/.eslintrc.cjs new file mode 100644 index 000000000..7d0206f78 --- /dev/null +++ b/configs/test-utils/.eslintrc.cjs @@ -0,0 +1,6 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + root: true, + extends: ['@suspensive/eslint-config/react-ts'], + ignorePatterns: ['*.js*', 'dist', 'coverage'], +} diff --git a/configs/test-utils/package.json b/configs/test-utils/package.json new file mode 100644 index 000000000..27914cd1c --- /dev/null +++ b/configs/test-utils/package.json @@ -0,0 +1,28 @@ +{ + "name": "@suspensive/test-utils", + "version": "0.0.0", + "private": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/manudeli" + }, + "license": "MIT", + "author": { + "name": "Jonghyeon Ko", + "email": "manudeli.ko@gmail.com" + }, + "sideEffects": false, + "type": "module", + "main": "src/index.ts", + "scripts": { + "lint": "eslint \"**/*.ts*\"", + "type:check": "tsc --noEmit" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "react": "^18.2.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17 || ^18" + } +} diff --git a/packages/react-await/src/utils/toTest.tsx b/configs/test-utils/src/Suspend.tsx similarity index 63% rename from packages/react-await/src/utils/toTest.tsx rename to configs/test-utils/src/Suspend.tsx index a3cb70c02..c1b0a2042 100644 --- a/packages/react-await/src/utils/toTest.tsx +++ b/configs/test-utils/src/Suspend.tsx @@ -16,10 +16,3 @@ export const Suspend = ({ during, toShow }: SuspendProps) => { Suspend.reset = () => { suspendIsNeed.current = true } - -export const delay = (ms: number) => new Promise((resolve) => setTimeout(() => resolve('done'), ms)) - -export const TEXT = 'TEXT' as const -export const ERROR_MESSAGE = 'ERROR_MESSAGE' as const -export const FALLBACK = 'FALLBACK' as const -export const MS_100 = 100 as const diff --git a/configs/test-utils/src/ThrowError.tsx b/configs/test-utils/src/ThrowError.tsx new file mode 100644 index 000000000..5e919c2b5 --- /dev/null +++ b/configs/test-utils/src/ThrowError.tsx @@ -0,0 +1,33 @@ +import type { PropsWithChildren } from 'react' +import { useEffect, useState } from 'react' + +const useSetTimeout = (fn: (...args: []) => void, delay: number) => + useEffect(() => { + const timeout = setTimeout(fn, delay) + return () => clearTimeout(timeout) + }, [fn, delay]) + +const throwErrorIsNeed = { current: false } +type ThrowErrorProps = PropsWithChildren<{ message: string; after?: number }> +export const ThrowError = ({ message, after = 0, children }: ThrowErrorProps) => { + const [isNeedError, setIsNeedError] = useState(after === 0 ? true : throwErrorIsNeed.current) + if (isNeedError) { + throw new Error(message) + } + useSetTimeout(() => setIsNeedError(true), after) + return <>{children} +} + +type ThrowNullProps = PropsWithChildren<{ after: number }> +export const ThrowNull = ({ after, children }: ThrowNullProps) => { + const [isNeedError, setIsNeedError] = useState(throwErrorIsNeed.current) + if (isNeedError) { + throw null + } + useSetTimeout(() => setIsNeedError(true), after) + return <>{children} +} + +ThrowError.reset = () => { + throwErrorIsNeed.current = false +} diff --git a/configs/test-utils/src/delay.ts b/configs/test-utils/src/delay.ts new file mode 100644 index 000000000..de834c18e --- /dev/null +++ b/configs/test-utils/src/delay.ts @@ -0,0 +1 @@ +export const delay = (ms: number) => new Promise((resolve) => setTimeout(() => resolve('done'), ms)) diff --git a/configs/test-utils/src/index.ts b/configs/test-utils/src/index.ts new file mode 100644 index 000000000..d4b8e7837 --- /dev/null +++ b/configs/test-utils/src/index.ts @@ -0,0 +1,7 @@ +export { Suspend } from './Suspend' +export { ThrowError, ThrowNull } from './ThrowError' +export { delay } from './delay' +export const TEXT = 'TEXT' as const +export const ERROR_MESSAGE = 'ERROR_MESSAGE' as const +export const FALLBACK = 'FALLBACK' as const +export const MS_100 = 100 as const diff --git a/configs/test-utils/tsconfig.json b/configs/test-utils/tsconfig.json new file mode 100644 index 000000000..4f99717c1 --- /dev/null +++ b/configs/test-utils/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@suspensive/tsconfig/react-library.json", + "include": ["."], + "compilerOptions": { + "types": ["@testing-library/jest-dom"] + } +} diff --git a/packages/react-await/package.json b/packages/react-await/package.json index 7874c72c3..68688d654 100644 --- a/packages/react-await/package.json +++ b/packages/react-await/package.json @@ -60,6 +60,7 @@ }, "devDependencies": { "@suspensive/react": "workspace:*", + "@suspensive/test-utils": "workspace:*", "@suspensive/tsup": "workspace:*", "@suspensive/vitest": "workspace:*", "@types/node": "^18.16.2", diff --git a/packages/react-await/src/Await.spec.tsx b/packages/react-await/src/Await.spec.tsx index bf990a4de..5837b89ea 100644 --- a/packages/react-await/src/Await.spec.tsx +++ b/packages/react-await/src/Await.spec.tsx @@ -1,7 +1,7 @@ import { ErrorBoundary, Suspense } from '@suspensive/react' +import { ERROR_MESSAGE, FALLBACK, MS_100, TEXT, delay } from '@suspensive/test-utils' import { act, render, screen, waitFor } from '@testing-library/react' import { vi } from 'vitest' -import { ERROR_MESSAGE, FALLBACK, MS_100, TEXT, delay } from './utils/toTest' import { Await, awaitClient, useAwait } from '.' const key = (id: number) => ['key', id] as const diff --git a/packages/react/package.json b/packages/react/package.json index 72063c211..a3f1b981b 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -56,6 +56,7 @@ "type:check": "tsc --noEmit" }, "devDependencies": { + "@suspensive/test-utils": "workspace:*", "@suspensive/tsup": "workspace:*", "@suspensive/vitest": "workspace:*", "@types/node": "^18.16.2", diff --git a/packages/react/src/AsyncBoundary.spec.tsx b/packages/react/src/AsyncBoundary.spec.tsx index 1aafdecfc..5cf5e017b 100644 --- a/packages/react/src/AsyncBoundary.spec.tsx +++ b/packages/react/src/AsyncBoundary.spec.tsx @@ -1,9 +1,9 @@ +import { ERROR_MESSAGE, FALLBACK, MS_100, Suspend, TEXT, ThrowError } from '@suspensive/test-utils' import { act, render, waitFor } from '@testing-library/react' import type { ComponentProps } from 'react' import { createElement } from 'react' import { createRoot } from 'react-dom/client' import { vi } from 'vitest' -import { ERROR_MESSAGE, FALLBACK, MS_100, Suspend, TEXT, ThrowError } from './utils/toTest' import { AsyncBoundary, withAsyncBoundary } from '.' let container = document.createElement('div') diff --git a/packages/react/src/Delay.spec.tsx b/packages/react/src/Delay.spec.tsx index eb91b20ee..c24da63c5 100644 --- a/packages/react/src/Delay.spec.tsx +++ b/packages/react/src/Delay.spec.tsx @@ -1,6 +1,6 @@ +import { MS_100, TEXT } from '@suspensive/test-utils' import { act, render, screen, waitFor } from '@testing-library/react' import { vi } from 'vitest' -import { MS_100, TEXT } from './utils/toTest' import { Delay, withDelay } from '.' describe('', () => { diff --git a/packages/react/src/ErrorBoundary.spec.tsx b/packages/react/src/ErrorBoundary.spec.tsx index 657ee46c8..aa0c50aff 100644 --- a/packages/react/src/ErrorBoundary.spec.tsx +++ b/packages/react/src/ErrorBoundary.spec.tsx @@ -1,3 +1,4 @@ +import { ERROR_MESSAGE, FALLBACK, MS_100, TEXT, ThrowError, ThrowNull } from '@suspensive/test-utils' import { act, render } from '@testing-library/react' import type { ComponentProps, ComponentRef } from 'react' import { createElement, createRef } from 'react' @@ -5,7 +6,6 @@ import { createRoot } from 'react-dom/client' import { vi } from 'vitest' import { useSetTimeout } from './hooks' import { assert } from './utils' -import { ERROR_MESSAGE, FALLBACK, MS_100, TEXT, ThrowError, ThrowNull } from './utils/toTest' import { ErrorBoundary, useErrorBoundary, useErrorBoundaryFallbackProps, withErrorBoundary } from '.' let container = document.createElement('div') diff --git a/packages/react/src/ErrorBoundaryGroup.spec.tsx b/packages/react/src/ErrorBoundaryGroup.spec.tsx index 18e0e63b6..3bf0d7221 100644 --- a/packages/react/src/ErrorBoundaryGroup.spec.tsx +++ b/packages/react/src/ErrorBoundaryGroup.spec.tsx @@ -1,8 +1,8 @@ +import { ERROR_MESSAGE, MS_100, TEXT, ThrowError } from '@suspensive/test-utils' import { act, render, screen } from '@testing-library/react' import { createElement } from 'react' import { vi } from 'vitest' import { assert } from './utils' -import { ERROR_MESSAGE, MS_100, TEXT, ThrowError } from './utils/toTest' import { ErrorBoundary, ErrorBoundaryGroup, useErrorBoundaryGroup, withErrorBoundaryGroup } from '.' const innerErrorBoundaryCount = 3 diff --git a/packages/react/src/Suspense.spec.tsx b/packages/react/src/Suspense.spec.tsx index 18ae20920..129141ed2 100644 --- a/packages/react/src/Suspense.spec.tsx +++ b/packages/react/src/Suspense.spec.tsx @@ -1,7 +1,7 @@ +import { FALLBACK, MS_100, Suspend, TEXT } from '@suspensive/test-utils' import { act, render, screen, waitFor } from '@testing-library/react' import { createElement } from 'react' import { vi } from 'vitest' -import { FALLBACK, MS_100, Suspend, TEXT } from './utils/toTest' import { Suspense, withSuspense } from '.' describe('', () => { diff --git a/packages/react/src/SuspensiveProvider.spec.tsx b/packages/react/src/SuspensiveProvider.spec.tsx index a48a4d9f4..683e70cd4 100644 --- a/packages/react/src/SuspensiveProvider.spec.tsx +++ b/packages/react/src/SuspensiveProvider.spec.tsx @@ -1,6 +1,6 @@ +import { FALLBACK, MS_100, Suspend, TEXT } from '@suspensive/test-utils' import { act, render, screen, waitFor } from '@testing-library/react' import { vi } from 'vitest' -import { FALLBACK, MS_100, Suspend, TEXT } from './utils/toTest' import { Delay, Suspense, Suspensive, SuspensiveProvider } from '.' const FALLBACK_GLOBAL = 'FALLBACK_GLOBAL' diff --git a/packages/react/src/hooks/useSetTimeout.spec.ts b/packages/react/src/hooks/useSetTimeout.spec.ts index 9f5467a82..f18f8d453 100644 --- a/packages/react/src/hooks/useSetTimeout.spec.ts +++ b/packages/react/src/hooks/useSetTimeout.spec.ts @@ -1,6 +1,6 @@ +import { MS_100 } from '@suspensive/test-utils' import { act, renderHook } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' -import { MS_100 } from '../utils/toTest' import { useSetTimeout } from '.' vi.useFakeTimers() diff --git a/packages/react/src/utils/toTest.tsx b/packages/react/src/utils/toTest.tsx deleted file mode 100644 index 91be62fb3..000000000 --- a/packages/react/src/utils/toTest.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import type { PropsWithChildren, ReactNode } from 'react' -import { useState } from 'react' -import { useSetTimeout } from '../hooks' - -const suspendIsNeed = { current: true } -type SuspendProps = { during: number; toShow?: ReactNode } -export const Suspend = ({ during, toShow }: SuspendProps) => { - if (suspendIsNeed.current) { - throw new Promise((resolve) => - setTimeout(() => { - suspendIsNeed.current = false - resolve('resolved') - }, during) - ) - } - return <>{toShow} -} -Suspend.reset = () => { - suspendIsNeed.current = true -} - -const throwErrorIsNeed = { current: false } -type ThrowErrorProps = PropsWithChildren<{ message: string; after?: number }> -export const ThrowError = ({ message, after = 0, children }: ThrowErrorProps) => { - const [isNeedError, setIsNeedError] = useState(after === 0 ? true : throwErrorIsNeed.current) - if (isNeedError) { - throw new Error(message) - } - useSetTimeout(() => setIsNeedError(true), after) - return <>{children} -} - -type ThrowNullProps = PropsWithChildren<{ after: number }> -export const ThrowNull = ({ after, children }: ThrowNullProps) => { - const [isNeedError, setIsNeedError] = useState(throwErrorIsNeed.current) - if (isNeedError) { - throw null - } - useSetTimeout(() => setIsNeedError(true), after) - return <>{children} -} - -ThrowError.reset = () => { - throwErrorIsNeed.current = false -} - -export const delay = (ms: number) => new Promise((resolve) => setTimeout(() => resolve('done'), ms)) - -export const TEXT = 'TEXT' as const -export const ERROR_MESSAGE = 'ERROR_MESSAGE' as const -export const FALLBACK = 'FALLBACK' as const -export const MS_100 = 100 as const diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a8c236a8..c10f5f666 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -81,7 +81,7 @@ importers: version: 7.1.0(ts-node@10.9.1)(typescript@5.1.6) turbo: specifier: latest - version: 1.10.16 + version: 1.10.15 typescript: specifier: ^5.1.6 version: 5.1.6 @@ -164,6 +164,15 @@ importers: specifier: ^4.2.1 version: 4.2.1(eslint-config-prettier@8.3.0)(eslint@8.42.0)(prettier@2.8.8) + configs/test-utils: + devDependencies: + '@types/react': + specifier: ^18.2.0 + version: 18.2.0 + react: + specifier: ^18.2.0 + version: 18.2.0 + configs/tsconfig: {} configs/tsup: {} @@ -221,6 +230,9 @@ importers: packages/react: devDependencies: + '@suspensive/test-utils': + specifier: workspace:* + version: link:../../configs/test-utils '@suspensive/tsup': specifier: workspace:* version: link:../../configs/tsup @@ -252,6 +264,9 @@ importers: '@suspensive/react': specifier: workspace:* version: link:../react + '@suspensive/test-utils': + specifier: workspace:* + version: link:../../configs/test-utils '@suspensive/tsup': specifier: workspace:* version: link:../../configs/tsup @@ -9589,64 +9604,64 @@ packages: yargs: 17.7.2 dev: true - /turbo-darwin-64@1.10.16: - resolution: {integrity: sha512-+Jk91FNcp9e9NCLYlvDDlp2HwEDp14F9N42IoW3dmHI5ZkGSXzalbhVcrx3DOox3QfiNUHxzWg4d7CnVNCuuMg==} + /turbo-darwin-64@1.10.15: + resolution: {integrity: sha512-Sik5uogjkRTe1XVP9TC2GryEMOJCaKE2pM/O9uLn4koQDnWKGcLQv+mDU+H+9DXvKLnJnKCD18OVRkwK5tdpoA==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-darwin-arm64@1.10.16: - resolution: {integrity: sha512-jqGpFZipIivkRp/i+jnL8npX0VssE6IAVNKtu573LXtssZdV/S+fRGYA16tI46xJGxSAivrZ/IcgZrV6Jk80bw==} + /turbo-darwin-arm64@1.10.15: + resolution: {integrity: sha512-xwqyFDYUcl2xwXyGPmHkmgnNm4Cy0oNzMpMOBGRr5x64SErS7QQLR4VHb0ubiR+VAb8M+ECPklU6vD1Gm+wekg==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-linux-64@1.10.16: - resolution: {integrity: sha512-PpqEZHwLoizQ6sTUvmImcRmACyRk9EWLXGlqceogPZsJ1jTRK3sfcF9fC2W56zkSIzuLEP07k5kl+ZxJd8JMcg==} + /turbo-linux-64@1.10.15: + resolution: {integrity: sha512-dM07SiO3RMAJ09Z+uB2LNUSkPp3I1IMF8goH5eLj+d8Kkwoxd/+qbUZOj9RvInyxU/IhlnO9w3PGd3Hp14m/nA==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-arm64@1.10.16: - resolution: {integrity: sha512-TMjFYz8to1QE0fKVXCIvG/4giyfnmqcQIwjdNfJvKjBxn22PpbjeuFuQ5kNXshUTRaTJihFbuuCcb5OYFNx4uw==} + /turbo-linux-arm64@1.10.15: + resolution: {integrity: sha512-MkzKLkKYKyrz4lwfjNXH8aTny5+Hmiu4SFBZbx+5C0vOlyp6fV5jZANDBvLXWiDDL4DSEAuCEK/2cmN6FVH1ow==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-windows-64@1.10.16: - resolution: {integrity: sha512-+jsf68krs0N66FfC4/zZvioUap/Tq3sPFumnMV+EBo8jFdqs4yehd6+MxIwYTjSQLIcpH8KoNMB0gQYhJRLZzw==} + /turbo-windows-64@1.10.15: + resolution: {integrity: sha512-3TdVU+WEH9ThvQGwV3ieX/XHebtYNHv9HARHauPwmVj3kakoALkpGxLclkHFBLdLKkqDvmHmXtcsfs6cXXRHJg==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /turbo-windows-arm64@1.10.16: - resolution: {integrity: sha512-sKm3hcMM1bl0B3PLG4ifidicOGfoJmOEacM5JtgBkYM48ncMHjkHfFY7HrJHZHUnXM4l05RQTpLFoOl/uIo2HQ==} + /turbo-windows-arm64@1.10.15: + resolution: {integrity: sha512-l+7UOBCbfadvPMYsX08hyLD+UIoAkg6ojfH+E8aud3gcA1padpjCJTh9gMpm3QdMbKwZteT5uUM+wyi6Rbbyww==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /turbo@1.10.16: - resolution: {integrity: sha512-2CEaK4FIuSZiP83iFa9GqMTQhroW2QryckVqUydmg4tx78baftTOS0O+oDAhvo9r9Nit4xUEtC1RAHoqs6ZEtg==} + /turbo@1.10.15: + resolution: {integrity: sha512-mKKkqsuDAQy1wCCIjCdG+jOCwUflhckDMSRoeBPcIL/CnCl7c5yRDFe7SyaXloUUkt4tUR0rvNIhVCcT7YeQpg==} hasBin: true optionalDependencies: - turbo-darwin-64: 1.10.16 - turbo-darwin-arm64: 1.10.16 - turbo-linux-64: 1.10.16 - turbo-linux-arm64: 1.10.16 - turbo-windows-64: 1.10.16 - turbo-windows-arm64: 1.10.16 + turbo-darwin-64: 1.10.15 + turbo-darwin-arm64: 1.10.15 + turbo-linux-64: 1.10.15 + turbo-linux-arm64: 1.10.15 + turbo-windows-64: 1.10.15 + turbo-windows-arm64: 1.10.15 dev: true /typanion@3.12.1: diff --git a/turbo.json b/turbo.json index 186e5466b..6b041de86 100644 --- a/turbo.json +++ b/turbo.json @@ -5,9 +5,9 @@ "build:watch": { "outputs": ["dist/**"] }, "npm:publish": { "dependsOn": ["^build"] }, "lint": { "cache": false }, - "lint:attw": { "dependsOn": ["^build"], "cache": false }, + "lint:attw": { "dependsOn": ["^build"], "cache": false }, "lint:pub": { "cache": false }, - "test": { "outputs": ["coverage/**"], "cache": false }, + "test": { "dependsOn": ["^build"], "outputs": ["coverage/**"], "cache": false }, "test:tsd": {}, "test:watch": { "cache": false }, "prepack": { "dependsOn": ["^prepack"], "cache": false },