migrate pure-logic tests to vitest (phase 1)
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
import tldts from 'tldts'
|
import tldts from 'tldts'
|
||||||
|
|
||||||
import {isEmailMaybeInvalid} from '#/lib/strings/email'
|
import {isEmailMaybeInvalid} from '#/lib/strings/email'
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getChatInviteCodeFromUrl,
|
getChatInviteCodeFromUrl,
|
||||||
isPossiblyAUrl,
|
isPossiblyAUrl,
|
||||||
|
|||||||
@@ -307,6 +307,7 @@
|
|||||||
"ts-plugin-sort-import-suggestions": "^1.0.4",
|
"ts-plugin-sort-import-suggestions": "^1.0.4",
|
||||||
"typescript": "^6.0.2",
|
"typescript": "^6.0.2",
|
||||||
"typescript-eslint": "^8.58.0",
|
"typescript-eslint": "^8.58.0",
|
||||||
|
"vite-plugin-babel": "^1.7.3",
|
||||||
"vitest": "^4.1.9",
|
"vitest": "^4.1.9",
|
||||||
"webpack-bundle-analyzer": "^4.10.1"
|
"webpack-bundle-analyzer": "^4.10.1"
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+15
-1
@@ -868,6 +868,9 @@ importers:
|
|||||||
typescript-eslint:
|
typescript-eslint:
|
||||||
specifier: ^8.58.0
|
specifier: ^8.58.0
|
||||||
version: 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
|
version: 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
|
||||||
|
vite-plugin-babel:
|
||||||
|
specifier: ^1.7.3
|
||||||
|
version: 1.7.3(@babel/core@7.29.0)(vite@8.1.0(@types/node@24.12.4)(jiti@2.7.0)(terser@5.47.1)(yaml@2.9.0))
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.1.9
|
specifier: ^4.1.9
|
||||||
version: 4.1.9(@types/node@24.12.4)(@vitest/coverage-v8@4.1.9)(jsdom@20.0.3)(vite@8.1.0(@types/node@24.12.4)(jiti@2.7.0)(terser@5.47.1)(yaml@2.9.0))
|
version: 4.1.9(@types/node@24.12.4)(@vitest/coverage-v8@4.1.9)(jsdom@20.0.3)(vite@8.1.0(@types/node@24.12.4)(jiti@2.7.0)(terser@5.47.1)(yaml@2.9.0))
|
||||||
@@ -9406,6 +9409,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
vite-plugin-babel@1.7.3:
|
||||||
|
resolution: {integrity: sha512-PsomVbH74/XyqMzXSwBDqb/1o5sAqE68gPlHLfx4FiMGbvKDhaZVQNv/gj/7oRQ5BvuIlyTKBtNEA9MnirGwRA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0
|
||||||
|
vite: ^2.7.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
|
|
||||||
vite@8.1.0:
|
vite@8.1.0:
|
||||||
resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==}
|
resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
@@ -13398,7 +13407,7 @@ snapshots:
|
|||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
minimatch: 10.2.5
|
minimatch: 10.2.5
|
||||||
semver: 7.8.0
|
semver: 7.8.0
|
||||||
tinyglobby: 0.2.16
|
tinyglobby: 0.2.17
|
||||||
ts-api-utils: 2.5.0(typescript@6.0.3)
|
ts-api-utils: 2.5.0(typescript@6.0.3)
|
||||||
typescript: 6.0.3
|
typescript: 6.0.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -19644,6 +19653,11 @@ snapshots:
|
|||||||
|
|
||||||
vary@1.1.2: {}
|
vary@1.1.2: {}
|
||||||
|
|
||||||
|
vite-plugin-babel@1.7.3(@babel/core@7.29.0)(vite@8.1.0(@types/node@24.12.4)(jiti@2.7.0)(terser@5.47.1)(yaml@2.9.0)):
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.0
|
||||||
|
vite: 8.1.0(@types/node@24.12.4)(jiti@2.7.0)(terser@5.47.1)(yaml@2.9.0)
|
||||||
|
|
||||||
vite@8.1.0(@types/node@24.12.4)(jiti@2.7.0)(terser@5.47.1)(yaml@2.9.0):
|
vite@8.1.0(@types/node@24.12.4)(jiti@2.7.0)(terser@5.47.1)(yaml@2.9.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
lightningcss: 1.32.0
|
lightningcss: 1.32.0
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {jest} from '@jest/globals'
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
import {transparentifyColor} from '../colorGeneration'
|
import {transparentifyColor} from '../colorGeneration'
|
||||||
|
|
||||||
describe('transparentifyColor', () => {
|
describe('transparentifyColor', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('converts hsl() to hsla()', () => {
|
it('converts hsl() to hsla()', () => {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {describe, test} from '@jest/globals'
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
computeCompletedState,
|
computeCompletedState,
|
||||||
syncCompletedState,
|
syncCompletedState,
|
||||||
} from '#/components/PolicyUpdateOverlay/usePolicyUpdateState'
|
} from '#/components/PolicyUpdateOverlay/usePolicyUpdateState'
|
||||||
|
|
||||||
jest.mock('../../../state/queries/nuxs')
|
vi.mock('../../../state/queries/nuxs')
|
||||||
|
|
||||||
describe('computeCompletedState', () => {
|
describe('computeCompletedState', () => {
|
||||||
test(`initial state`, () => {
|
test(`initial state`, () => {
|
||||||
@@ -67,8 +67,8 @@ describe('computeCompletedState', () => {
|
|||||||
describe('syncCompletedState', () => {
|
describe('syncCompletedState', () => {
|
||||||
describe('!nuxIsReady', () => {
|
describe('!nuxIsReady', () => {
|
||||||
test(`!completedForDevice, no-op`, () => {
|
test(`!completedForDevice, no-op`, () => {
|
||||||
const save = jest.fn()
|
const save = vi.fn()
|
||||||
const setCompletedForDevice = jest.fn()
|
const setCompletedForDevice = vi.fn()
|
||||||
syncCompletedState({
|
syncCompletedState({
|
||||||
nuxIsReady: false,
|
nuxIsReady: false,
|
||||||
nuxIsCompleted: false,
|
nuxIsCompleted: false,
|
||||||
@@ -83,8 +83,8 @@ describe('syncCompletedState', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test(`completedForDevice, no-op`, () => {
|
test(`completedForDevice, no-op`, () => {
|
||||||
const save = jest.fn()
|
const save = vi.fn()
|
||||||
const setCompletedForDevice = jest.fn()
|
const setCompletedForDevice = vi.fn()
|
||||||
syncCompletedState({
|
syncCompletedState({
|
||||||
nuxIsReady: false,
|
nuxIsReady: false,
|
||||||
nuxIsCompleted: false,
|
nuxIsCompleted: false,
|
||||||
@@ -103,8 +103,8 @@ describe('syncCompletedState', () => {
|
|||||||
describe(`!nuxIsCompleted`, () => {
|
describe(`!nuxIsCompleted`, () => {
|
||||||
describe(`!nuxIsOptimisticallyCompleted`, () => {
|
describe(`!nuxIsOptimisticallyCompleted`, () => {
|
||||||
test(`!completedForDevice, no-op`, () => {
|
test(`!completedForDevice, no-op`, () => {
|
||||||
const save = jest.fn()
|
const save = vi.fn()
|
||||||
const setCompletedForDevice = jest.fn()
|
const setCompletedForDevice = vi.fn()
|
||||||
syncCompletedState({
|
syncCompletedState({
|
||||||
nuxIsReady: true,
|
nuxIsReady: true,
|
||||||
nuxIsCompleted: false,
|
nuxIsCompleted: false,
|
||||||
@@ -119,8 +119,8 @@ describe('syncCompletedState', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test(`completedForDevice, syncs to server`, () => {
|
test(`completedForDevice, syncs to server`, () => {
|
||||||
const save = jest.fn()
|
const save = vi.fn()
|
||||||
const setCompletedForDevice = jest.fn()
|
const setCompletedForDevice = vi.fn()
|
||||||
syncCompletedState({
|
syncCompletedState({
|
||||||
nuxIsReady: true,
|
nuxIsReady: true,
|
||||||
nuxIsCompleted: false,
|
nuxIsCompleted: false,
|
||||||
@@ -141,8 +141,8 @@ describe('syncCompletedState', () => {
|
|||||||
*/
|
*/
|
||||||
describe(`nuxIsOptimisticallyCompleted`, () => {
|
describe(`nuxIsOptimisticallyCompleted`, () => {
|
||||||
test(`completedForDevice, no-op`, () => {
|
test(`completedForDevice, no-op`, () => {
|
||||||
const save = jest.fn()
|
const save = vi.fn()
|
||||||
const setCompletedForDevice = jest.fn()
|
const setCompletedForDevice = vi.fn()
|
||||||
syncCompletedState({
|
syncCompletedState({
|
||||||
nuxIsReady: true,
|
nuxIsReady: true,
|
||||||
nuxIsCompleted: false,
|
nuxIsCompleted: false,
|
||||||
@@ -160,8 +160,8 @@ describe('syncCompletedState', () => {
|
|||||||
|
|
||||||
describe(`nuxIsCompleted`, () => {
|
describe(`nuxIsCompleted`, () => {
|
||||||
test(`!completedForDevice, syncs to device`, () => {
|
test(`!completedForDevice, syncs to device`, () => {
|
||||||
const save = jest.fn()
|
const save = vi.fn()
|
||||||
const setCompletedForDevice = jest.fn()
|
const setCompletedForDevice = vi.fn()
|
||||||
syncCompletedState({
|
syncCompletedState({
|
||||||
nuxIsReady: true,
|
nuxIsReady: true,
|
||||||
nuxIsCompleted: true,
|
nuxIsCompleted: true,
|
||||||
@@ -176,8 +176,8 @@ describe('syncCompletedState', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test(`completedForDevice, no-op`, () => {
|
test(`completedForDevice, no-op`, () => {
|
||||||
const save = jest.fn()
|
const save = vi.fn()
|
||||||
const setCompletedForDevice = jest.fn()
|
const setCompletedForDevice = vi.fn()
|
||||||
syncCompletedState({
|
syncCompletedState({
|
||||||
nuxIsReady: true,
|
nuxIsReady: true,
|
||||||
nuxIsCompleted: true,
|
nuxIsCompleted: true,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import {getInviteTheme, INVITE_THEME_KEYS, INVITE_THEMES} from './themes'
|
import {getInviteTheme, INVITE_THEME_KEYS, INVITE_THEMES} from './themes'
|
||||||
|
|
||||||
describe('invite themes', () => {
|
describe('invite themes', () => {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import {getInviteDisplayUrl, getInviteShareUrl} from './urls'
|
import {getInviteDisplayUrl, getInviteShareUrl} from './urls'
|
||||||
|
|
||||||
describe('invite URLs', () => {
|
describe('invite URLs', () => {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import {parseLinkingUrl} from '../parseLinkingUrl'
|
import {parseLinkingUrl} from '../parseLinkingUrl'
|
||||||
|
|
||||||
describe('parseLinkingUrl', () => {
|
describe('parseLinkingUrl', () => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
import {addDays, subDays, subHours, subMinutes, subSeconds} from 'date-fns'
|
import {addDays, subDays, subHours, subMinutes, subSeconds} from 'date-fns'
|
||||||
|
|
||||||
import {dateDiff} from '../useTimeAgo'
|
import {dateDiff} from '../useTimeAgo'
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {expect, test} from '@jest/globals'
|
|
||||||
|
|
||||||
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
||||||
import {AppLanguage} from '#/locale/languages'
|
import {AppLanguage} from '#/locale/languages'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {expect, test} from '@jest/globals'
|
|
||||||
|
|
||||||
import {add, type ConsoleTransportEntry, getEntries} from '#/logger/logDump'
|
import {add, type ConsoleTransportEntry, getEntries} from '#/logger/logDump'
|
||||||
import {LogContext, LogLevel} from '#/logger/types'
|
import {LogContext, LogLevel} from '#/logger/types'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
countActiveFilters,
|
countActiveFilters,
|
||||||
definedFilterParams,
|
definedFilterParams,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import {parseSearchQuery} from '#/screens/Search/utils'
|
import {parseSearchQuery} from '#/screens/Search/utils'
|
||||||
|
|
||||||
describe(`parseSearchQuery`, () => {
|
describe(`parseSearchQuery`, () => {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
makeFilter,
|
makeFilter,
|
||||||
parseAdvancedSearch,
|
parseAdvancedSearch,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {describe, it} from '@jest/globals'
|
|
||||||
|
|
||||||
describe(`#/state/messages/convo`, () => {
|
describe(`#/state/messages/convo`, () => {
|
||||||
describe(`init`, () => {
|
describe(`init`, () => {
|
||||||
it.todo(`fails if sender and recipients aren't found`)
|
it.todo(`fails if sender and recipients aren't found`)
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
buildSearchPostsV2Filters,
|
buildSearchPostsV2Filters,
|
||||||
extractSearchPostsParams,
|
extractSearchPostsParams,
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import {jest} from '@jest/globals'
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
export {Nux} from '#/state/queries/nuxs/definitions'
|
export {Nux} from '#/state/queries/nuxs/definitions'
|
||||||
|
|
||||||
export const useNuxs = jest.fn(() => {
|
export const useNuxs = vi.fn(() => {
|
||||||
return {
|
return {
|
||||||
nuxs: undefined,
|
nuxs: undefined,
|
||||||
status: 'loading' as const,
|
status: 'loading' as const,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const useNux = jest.fn((id: string) => {
|
export const useNux = vi.fn((id: string) => {
|
||||||
return {
|
return {
|
||||||
nux: undefined,
|
nux: undefined,
|
||||||
status: 'loading' as const,
|
status: 'loading' as const,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const useSaveNux = jest.fn(() => {
|
export const useSaveNux = vi.fn(() => {
|
||||||
return {}
|
return {}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const useResetNuxs = jest.fn(() => {
|
export const useResetNuxs = vi.fn(() => {
|
||||||
return {}
|
return {}
|
||||||
})
|
})
|
||||||
|
|||||||
+104
-7
@@ -1,19 +1,37 @@
|
|||||||
import {fileURLToPath} from 'node:url'
|
import {fileURLToPath} from 'node:url'
|
||||||
|
|
||||||
import react from '@vitejs/plugin-react'
|
import babel from 'vite-plugin-babel'
|
||||||
import {defineConfig} from 'vitest/config'
|
import {defineConfig} from 'vitest/config'
|
||||||
|
|
||||||
const r = (p: string) => fileURLToPath(new URL(p, import.meta.url))
|
const r = (p: string) => fileURLToPath(new URL(p, import.meta.url))
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
react({
|
/*
|
||||||
babel: {
|
* esbuild handles TS/JSX transpilation; babel runs only for the lingui
|
||||||
|
* macro, which must be expanded in every .ts/.tsx file that uses it (not
|
||||||
|
* just JSX files). We deliberately omit react-compiler, reanimated/plugin,
|
||||||
|
* and react-native-dotenv - those matter for render semantics and
|
||||||
|
* native/build output, none of which our tests exercise.
|
||||||
|
*/
|
||||||
|
babel({
|
||||||
|
include: /\.[jt]sx?$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
babelConfig: {
|
||||||
|
babelrc: false,
|
||||||
|
configFile: false,
|
||||||
/*
|
/*
|
||||||
* Only what tests actually need. We deliberately omit react-compiler,
|
* The plugin runs with enforce: 'pre', so babel receives raw
|
||||||
* reanimated/plugin, and react-native-dotenv - those matter for render
|
* TypeScript before esbuild strips types - it needs the TS preset to
|
||||||
* semantics and native/build output, none of which our tests exercise.
|
* parse it. preset-typescript auto-enables JSX for .tsx filenames.
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
* Default onlyRemoveTypeImports: false so babel elides imports whose
|
||||||
|
* specifiers are all types (e.g. `import {type X} from '...'`).
|
||||||
|
* Otherwise such imports survive as side-effect imports and drag whole
|
||||||
|
* runtime module graphs (and their native deps) into tests.
|
||||||
|
*/
|
||||||
|
presets: ['@babel/preset-typescript'],
|
||||||
plugins: [
|
plugins: [
|
||||||
'@lingui/babel-plugin-lingui-macro',
|
'@lingui/babel-plugin-lingui-macro',
|
||||||
'@babel/plugin-transform-class-static-block',
|
'@babel/plugin-transform-class-static-block',
|
||||||
@@ -22,6 +40,29 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
|
/*
|
||||||
|
* Mirror Metro's platform-specific resolution so Component.native.ts /
|
||||||
|
* Component.ios.ts win over Component.ts (matching the jest-expo/ios
|
||||||
|
* preset, which resolves ios then native). Without this, files like
|
||||||
|
* PlatformInfo/index.ts (which throws NotImplementedError) load instead of
|
||||||
|
* index.native.ts (which uses the stubbed requireNativeModule).
|
||||||
|
*/
|
||||||
|
extensions: [
|
||||||
|
'.ios.ts',
|
||||||
|
'.ios.tsx',
|
||||||
|
'.ios.js',
|
||||||
|
'.ios.jsx',
|
||||||
|
'.native.ts',
|
||||||
|
'.native.tsx',
|
||||||
|
'.native.js',
|
||||||
|
'.native.jsx',
|
||||||
|
'.ts',
|
||||||
|
'.tsx',
|
||||||
|
'.mjs',
|
||||||
|
'.js',
|
||||||
|
'.jsx',
|
||||||
|
'.json',
|
||||||
|
],
|
||||||
alias: [
|
alias: [
|
||||||
/*
|
/*
|
||||||
* Mirror the babel module-resolver aliases (also in tsconfig.json).
|
* Mirror the babel module-resolver aliases (also in tsconfig.json).
|
||||||
@@ -29,6 +70,56 @@ export default defineConfig({
|
|||||||
{find: /^#\/(.*)$/, replacement: r('./src/$1')},
|
{find: /^#\/(.*)$/, replacement: r('./src/$1')},
|
||||||
{find: /^crypto$/, replacement: r('./src/platform/crypto.ts')},
|
{find: /^crypto$/, replacement: r('./src/platform/crypto.ts')},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* react-native's real entry is Flow-typed and boots a native runtime.
|
||||||
|
* Redirect it (and deep Libraries/* subpaths some packages import) to a
|
||||||
|
* small stub. Aliasing at resolution time is more robust than a
|
||||||
|
* setup-file vi.mock, which doesn't reach imports from externalized CJS
|
||||||
|
* deps like @bsky.app/react-native-mmkv.
|
||||||
|
*/
|
||||||
|
{find: /^react-native$/, replacement: r('./vitest/react-native-stub.ts')},
|
||||||
|
{
|
||||||
|
find: /^react-native\/.*$/,
|
||||||
|
replacement: r('./vitest/react-native-stub.ts'),
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Native MMKV module - binds to a native lib and imports react-native.
|
||||||
|
* Stubbed so transitive importers (#/storage) load. Tests that exercise
|
||||||
|
* storage logic still vi.mock this with their own in-memory impl, which
|
||||||
|
* takes precedence over the alias.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
find: /^@bsky\.app\/react-native-mmkv$/,
|
||||||
|
replacement: r('./vitest/mmkv-stub.ts'),
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @sentry/react-native is CJS and requires react-native internally.
|
||||||
|
* Stubbed so modules that pull it in via #/logger load. logger.test.ts
|
||||||
|
* still vi.mocks it directly to assert on calls.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
find: /^@sentry\/react-native$/,
|
||||||
|
replacement: r('./vitest/sentry-stub.ts'),
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* expo-application pulls in the `expo` package's native winter runtime,
|
||||||
|
* which does bare CJS requires that don't resolve under Vitest.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
find: /^expo-application$/,
|
||||||
|
replacement: r('./vitest/expo-application-stub.ts'),
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The bare `expo` package's entry runs the winter runtime side-effect on
|
||||||
|
* import (bare CJS requires that don't resolve under node). Stub it to
|
||||||
|
* the named exports test-reachable code uses.
|
||||||
|
*/
|
||||||
|
{find: /^expo$/, replacement: r('./vitest/expo-stub.ts')},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Former Jest moduleNameMapper entries. Vite's ESM resolution may make
|
* Former Jest moduleNameMapper entries. Vite's ESM resolution may make
|
||||||
* some of these unnecessary, but they're harmless and proven, so keep
|
* some of these unnecessary, but they're harmless and proven, so keep
|
||||||
@@ -72,7 +163,13 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
define: {__DEV__: 'true'},
|
/*
|
||||||
|
* Match jest's effective environment: under jest __DEV__ was true but
|
||||||
|
* JEST_WORKER_ID was set, so dev-only branches guarded by
|
||||||
|
* `__DEV__ && !process.env.JEST_WORKER_ID` took the production path. Setting
|
||||||
|
* __DEV__ to false reproduces that for the equivalent branches in Vitest.
|
||||||
|
*/
|
||||||
|
define: {__DEV__: 'false'},
|
||||||
test: {
|
test: {
|
||||||
globals: true,
|
globals: true,
|
||||||
environment: 'node',
|
environment: 'node',
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Stub for expo-application. The real package transitively imports the `expo`
|
||||||
|
* package, whose native winter runtime does bare CJS requires that don't
|
||||||
|
* resolve under Vitest. #/env only reads nativeBuildVersion.
|
||||||
|
*
|
||||||
|
* Aliased in place of expo-application via resolve.alias in vitest.config.ts.
|
||||||
|
*/
|
||||||
|
export const nativeApplicationVersion = '1.0.0'
|
||||||
|
export const nativeBuildVersion = '1'
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Stub for the bare `expo` package. Importing the real entry runs Expo's
|
||||||
|
* "winter" runtime side-effect, which does bare CJS requires (./TextDecoder
|
||||||
|
* etc) that don't resolve under Vitest's node environment. Node already
|
||||||
|
* provides TextDecoder and friends, so we just need the named exports
|
||||||
|
* test-reachable code pulls from `expo`.
|
||||||
|
*
|
||||||
|
* Aliased in place of `expo` via resolve.alias in vitest.config.ts.
|
||||||
|
*/
|
||||||
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
|
export function requireNativeModule(moduleName: string) {
|
||||||
|
if (moduleName === 'ExpoPlatformInfo') {
|
||||||
|
return {getIsReducedMotionEnabled: () => false}
|
||||||
|
}
|
||||||
|
if (moduleName === 'BottomSheet') {
|
||||||
|
return {dismissAll: () => {}}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function requireOptionalNativeModule() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const requireNativeView = vi.fn(() => () => null)
|
||||||
|
|
||||||
|
export class NativeModule {}
|
||||||
|
export class SharedObject {}
|
||||||
|
export class SharedRef {}
|
||||||
|
|
||||||
|
export const useEvent = vi.fn()
|
||||||
|
export const useEventListener = vi.fn()
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Stub for @bsky.app/react-native-mmkv. The real package binds to a native
|
||||||
|
* module and imports `react-native`, neither of which works under Vitest's
|
||||||
|
* node environment. Tests that exercise storage logic mock this module's MMKV
|
||||||
|
* with their own in-memory implementation; this stub just provides a class
|
||||||
|
* shape so transitive importers (e.g. #/storage) load without booting RN.
|
||||||
|
*
|
||||||
|
* Aliased in place of @bsky.app/react-native-mmkv via resolve.alias in
|
||||||
|
* vitest.config.ts.
|
||||||
|
*/
|
||||||
|
export class MMKV {
|
||||||
|
getString() {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
set() {}
|
||||||
|
delete() {}
|
||||||
|
getAllKeys(): string[] {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
clearAll() {}
|
||||||
|
addOnValueChangedListener() {
|
||||||
|
return {remove() {}}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Stub for `react-native` used in unit tests. react-native's real entry is
|
||||||
|
* Flow-typed and boots a native runtime, neither of which works (or is needed)
|
||||||
|
* under Vitest's node environment. This is aliased in place of `react-native`
|
||||||
|
* via resolve.alias in vitest.config.ts.
|
||||||
|
*
|
||||||
|
* Only the surface our test-reachable modules actually touch is implemented:
|
||||||
|
* - Image.getSize (src/lib/media/manip.ts)
|
||||||
|
* - NativeModules / Platform (@bsky.app/react-native-mmkv)
|
||||||
|
* Extend as new transitive usages appear.
|
||||||
|
*/
|
||||||
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
|
export const Image = {getSize: vi.fn()}
|
||||||
|
|
||||||
|
export const NativeModules: Record<string, unknown> = {}
|
||||||
|
|
||||||
|
export const Platform = {
|
||||||
|
OS: 'ios' as const,
|
||||||
|
select: <T>(o: {ios?: T; android?: T; native?: T; default?: T}): T =>
|
||||||
|
(o.ios ?? o.native ?? o.default) as T,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Dimensions = {
|
||||||
|
get: () => ({width: 390, height: 844, scale: 3, fontScale: 1}),
|
||||||
|
addEventListener: () => ({remove() {}}),
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Stub for @sentry/react-native. The real package is CJS and requires
|
||||||
|
* `react-native` internally, which boots the native runtime under node. Many
|
||||||
|
* test-reachable modules pull it in transitively via #/logger. We only need
|
||||||
|
* the surface sentryTransport touches (src/logger/transports/sentry.ts).
|
||||||
|
*
|
||||||
|
* Aliased in place of @sentry/react-native via resolve.alias in
|
||||||
|
* vitest.config.ts. Tests that assert on Sentry calls (logger.test.ts) still
|
||||||
|
* vi.mock it directly, which takes precedence.
|
||||||
|
*/
|
||||||
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
|
export const addBreadcrumb = vi.fn()
|
||||||
|
export const captureException = vi.fn()
|
||||||
|
export const captureMessage = vi.fn()
|
||||||
|
export const init = vi.fn()
|
||||||
+28
-8
@@ -1,16 +1,36 @@
|
|||||||
import {vi} from 'vitest'
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The only place `react-native` enters the test module graph is a single
|
* Note: `react-native` itself is stubbed via resolve.alias in
|
||||||
* transitive import in src/lib/media/manip.ts (`import {Image} from
|
* vitest.config.ts (-> vitest/react-native-stub.ts), not here.
|
||||||
* 'react-native'`). react-native's entry is Flow-typed, which esbuild cannot
|
|
||||||
* parse, so we stub it. manip.ts only touches RNImage.getSize; Platform is
|
|
||||||
* included for any other incidental use.
|
|
||||||
*/
|
*/
|
||||||
vi.mock('react-native', () => ({
|
|
||||||
Image: {getSize: vi.fn()},
|
/*
|
||||||
|
* expo-modules-core reads its native EventEmitter / NativeModule classes off
|
||||||
|
* globalThis.expo, which only exists in a real Expo runtime. Several utility
|
||||||
|
* modules pull it in transitively (via expo-application, expo-localization,
|
||||||
|
* etc). We only need the handful of helpers tests actually touch.
|
||||||
|
*/
|
||||||
|
vi.mock('expo-modules-core', () => ({
|
||||||
|
requireNativeModule: vi.fn((moduleName: string) => {
|
||||||
|
if (moduleName === 'ExpoPlatformInfo') {
|
||||||
|
return {getIsReducedMotionEnabled: () => false}
|
||||||
|
}
|
||||||
|
if (moduleName === 'BottomSheet') {
|
||||||
|
return {dismissAll: () => {}}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}),
|
||||||
|
requireNativeViewManager: vi.fn(() => () => null),
|
||||||
|
requireOptionalNativeModule: vi.fn(() => null),
|
||||||
|
createPermissionHook: () => () => [true],
|
||||||
|
NativeModule: class {},
|
||||||
|
SharedObject: class {},
|
||||||
|
SharedRef: class {},
|
||||||
|
EventEmitter: class {},
|
||||||
Platform: {
|
Platform: {
|
||||||
OS: 'ios',
|
OS: 'ios',
|
||||||
select: (o: Record<string, unknown>) => o.ios ?? o.default,
|
isDOMAvailable: false,
|
||||||
|
select: (o: Record<string, unknown>) => o.ios,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|||||||
Reference in New Issue
Block a user