Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import tseslint from 'typescript-eslint'

export default defineConfig(
{
ignores: ['dist/', 'examples/', 'website/'],
ignores: ['dist/', 'examples/', 'website/', 'coverage/'],
},
eslint.configs.recommended,
importPlugin.flatConfigs.recommended,
Expand All @@ -19,6 +19,11 @@ export default defineConfig(
react.configs.flat['jsx-runtime'],
reactHooks.configs.flat.recommended,
{
languageOptions: {
parserOptions: {
project: true,
},
},
settings: {
react: {
version: 'detect',
Expand Down Expand Up @@ -76,8 +81,21 @@ export default defineConfig(
{
files: ['tests/**/*.{ts,tsx}'],
...testingLibrary.configs['flat/react'],
rules: {
'testing-library/no-unnecessary-act': 'off',
},
},
{
files: ['tests/**/*.{ts,tsx}'],
...jestDom.configs['flat/recommended'],
},
{
files: ['tests/**/*.{ts,tsx}'],
...vitest.configs.recommended,
settings: { vitest: { typecheck: true } },
},
{
files: ['tests/**/*.{ts,tsx}'],
rules: {
'import/extensions': ['error', 'never'],
'vitest/consistent-test-it': [
Expand All @@ -86,4 +104,12 @@ export default defineConfig(
],
},
},
{
files: ['*.config.*'],
languageOptions: {
parserOptions: {
project: null,
},
},
},
)
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@
"rxjs": "^7.8.2",
"shelljs": "^0.10.0",
"shx": "^0.4.0",
"ts-expect": "^1.3.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.48.0",
"vitest": "^4.0.14",
Expand Down
16 changes: 4 additions & 12 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions tests/react/error.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ describe('throws an error while updating in effect cleanup', () => {
)
}

it('[DEV-ONLY] single setCount', async () => {
it('[DEV-ONLY] single setCount', () => {
render(
<>
<ErrorBoundary>
Expand All @@ -464,6 +464,10 @@ describe('throws an error while updating in effect cleanup', () => {
)

fireEvent.click(screen.getByText('close'))

// NOTE: Conditional expect is required because behavior differs by React version
// AND build mode (dev vs prod). Using it.runIf/skipIf causes production build failures.
/* eslint-disable vitest/no-conditional-expect */
if (reactVersion.startsWith('17.')) {
expect(
errorMessages.some((m) => m.includes('err_in_effect_cleanup')),
Expand All @@ -473,9 +477,10 @@ describe('throws an error while updating in effect cleanup', () => {
screen.getByText('Errored: err_in_effect_cleanup'),
).toBeInTheDocument()
}
/* eslint-enable vitest/no-conditional-expect */
})

it('[DEV-ONLY] dobule setCount', () => {
it('[DEV-ONLY] double setCount', () => {
doubleSetCount = true

render(
Expand All @@ -492,6 +497,10 @@ describe('throws an error while updating in effect cleanup', () => {
)

fireEvent.click(screen.getByText('close'))

// NOTE: Conditional expect is required because behavior differs by React version
// AND build mode (dev vs prod). Using it.runIf/skipIf causes production build failures.
/* eslint-disable vitest/no-conditional-expect */
if (reactVersion.startsWith('17.')) {
expect(
errorMessages.some((m) => m.includes('err_in_effect_cleanup')),
Expand All @@ -501,6 +510,7 @@ describe('throws an error while updating in effect cleanup', () => {
screen.getByText('Errored: err_in_effect_cleanup'),
).toBeInTheDocument()
}
/* eslint-enable vitest/no-conditional-expect */
})
})

Expand Down
4 changes: 3 additions & 1 deletion tests/react/provider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ it('only uses initial value from provider for specific atom', () => {
})

it('renders correctly without children', () => {
render(
const { container } = render(
<StrictMode>
<Provider />
</StrictMode>,
)

expect(container).toBeInTheDocument()
})
106 changes: 51 additions & 55 deletions tests/react/types.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expectType } from 'ts-expect'
import type { TypeEqual } from 'ts-expect'
// NOTE: Using variable assignment for type checking instead of expectTypeOf
// because TypeScript 3.8.3 doesn't support generic type arguments on untyped function calls.
import { expect, it } from 'vitest'
import { useAtom, useSetAtom } from 'jotai/react'
import { atom } from 'jotai/vanilla'
Expand All @@ -8,11 +8,15 @@ it('useAtom should return the correct types', () => {
function Component() {
// primitive atom
const primitiveAtom = atom(0)
expectType<[number, (arg: number) => void]>(useAtom(primitiveAtom))
const _primitiveAtomResult: [number, (arg: number) => void] =
useAtom(primitiveAtom)
expect(_primitiveAtomResult).toBeDefined()

// read-only derived atom
const readonlyDerivedAtom = atom((get) => get(primitiveAtom) * 2)
expectType<[number, (arg: number) => void]>(useAtom(readonlyDerivedAtom))
const _readonlyDerivedAtomResult: [number, (arg: number) => void] =
useAtom(readonlyDerivedAtom)
expect(_readonlyDerivedAtomResult).toBeDefined()

// read-write derived atom
const readWriteDerivedAtom = atom(
Expand All @@ -21,13 +25,17 @@ it('useAtom should return the correct types', () => {
set(primitiveAtom, get(primitiveAtom) + value)
},
)
expectType<[number, (arg: number) => void]>(useAtom(readWriteDerivedAtom))
const _readWriteDerivedAtomResult: [number, (arg: number) => void] =
useAtom(readWriteDerivedAtom)
expect(_readWriteDerivedAtomResult).toBeDefined()

// write-only derived atom
const writeonlyDerivedAtom = atom(null, (get, set) => {
set(primitiveAtom, get(primitiveAtom) - 1)
})
expectType<[null, (arg: number) => void]>(useAtom(writeonlyDerivedAtom))
const _writeonlyDerivedAtomResult: [null, (arg: number) => void] =
useAtom(writeonlyDerivedAtom)
expect(_writeonlyDerivedAtomResult).toBeDefined()
}
expect(Component).toBeDefined()
})
Expand All @@ -45,33 +53,28 @@ it('useAtom should handle inference of atoms (#1831 #1387)', () => {
const [username, setUsername] = useField('username')
expect(username).toBeDefined()
expect(setUsername).toBeDefined()
expectType<TypeEqual<string, typeof username>>(true)
expectType<
TypeEqual<
(arg: string | ((prev: string) => string)) => void,
typeof setUsername
>
>(true)
const _username: string = username
const _setUsername: (arg: string | ((prev: string) => string)) => void =
setUsername
expect(_username).toBeDefined()
expect(_setUsername).toBeDefined()

const [age, setAge] = useField('age')
expect(age).toBeDefined()
expect(setAge).toBeDefined()
expectType<TypeEqual<number, typeof age>>(true)
expectType<
TypeEqual<
(arg: number | ((prev: number) => number)) => void,
typeof setAge
>
>(true)
const _age: number = age
const _setAge: (arg: number | ((prev: number) => number)) => void = setAge
expect(_age).toBeDefined()
expect(_setAge).toBeDefined()

const [checked, setChecked] = useField('checked')
expect(checked).toBeDefined()
expect(setChecked).toBeDefined()
expectType<TypeEqual<boolean, typeof checked>>(true)
expectType<
TypeEqual<
(arg: boolean | ((prev: boolean) => boolean)) => void,
typeof setChecked
>
>(true)
const _checked: boolean = checked
const _setChecked: (arg: boolean | ((prev: boolean) => boolean)) => void =
setChecked
expect(_checked).toBeDefined()
expect(_setChecked).toBeDefined()
}
expect(Component).toBeDefined()
})
Expand All @@ -86,9 +89,12 @@ it('useAtom should handle inference of read-only atoms', () => {
return useAtom(fieldAtoms[prop])
}
function Component() {
expectType<[string, never]>(useField('username'))
expectType<[number, never]>(useField('age'))
expectType<[boolean, never]>(useField('checked'))
const _username: [string, never] = useField('username')
const _age: [number, never] = useField('age')
const _checked: [boolean, never] = useField('checked')
expect(_username).toBeDefined()
expect(_age).toBeDefined()
expect(_checked).toBeDefined()
}
expect(Component).toBeDefined()
})
Expand All @@ -105,28 +111,20 @@ it('useSetAtom should handle inference of atoms', () => {
function Component() {
const setUsername = useSetField('username')
expect(setUsername).toBeDefined()
expectType<
TypeEqual<
(arg: string | ((prev: string) => string)) => void,
typeof setUsername
>
>(true)
const _setUsername: (arg: string | ((prev: string) => string)) => void =
setUsername
expect(_setUsername).toBeDefined()

const setAge = useSetField('age')
expect(setAge).toBeDefined()
expectType<
TypeEqual<
(arg: number | ((prev: number) => number)) => void,
typeof setAge
>
>(true)
const _setAge: (arg: number | ((prev: number) => number)) => void = setAge
expect(_setAge).toBeDefined()

const setChecked = useSetField('checked')
expect(setChecked).toBeDefined()
expectType<
TypeEqual<
(arg: boolean | ((prev: boolean) => boolean)) => void,
typeof setChecked
>
>(true)
const _setChecked: (arg: boolean | ((prev: boolean) => boolean)) => void =
setChecked
expect(_setChecked).toBeDefined()
}
expect(Component).toBeDefined()
})
Expand All @@ -137,13 +135,11 @@ it('useAtom should handle primitive atom with one type argeument', () => {
const [count, setCount] = useAtom<number>(countAtom)
expect(count).toBeDefined()
expect(setCount).toBeDefined()
expectType<TypeEqual<typeof count, number>>(true)
expectType<
TypeEqual<
typeof setCount,
(arg: number | ((prev: number) => number)) => void
>
>(true)
const _count: number = count
const _setCount: (arg: number | ((prev: number) => number)) => void =
setCount
expect(_count).toBeDefined()
expect(_setCount).toBeDefined()
}
expect(Component).toBeDefined()
})
2 changes: 2 additions & 0 deletions tests/react/utils/types.test.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// NOTE: Using variable assignment for type checking instead of expectTypeOf
// because TypeScript 3.8.3 doesn't support generic type arguments on untyped function calls.
import { expect, it } from 'vitest'
import { useHydrateAtoms } from 'jotai/react/utils'
import { atom } from 'jotai/vanilla'
Expand Down
Loading
Loading