wip - claude --resume 742969c2-ab50-4a79-b7bd-3971e9fd314d
This commit is contained in:
@@ -82,5 +82,4 @@ jobs:
|
|||||||
- name: Check & compile i18n
|
- name: Check & compile i18n
|
||||||
run: pnpm intl:build
|
run: pnpm intl:build
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: pnpm test-ci
|
||||||
NODE_ENV=test pnpm test --forceExit
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, {type ReactNode} from 'react'
|
import React, {type ReactNode} from 'react'
|
||||||
import {FlatList, Modal, ScrollView, TextInput, View} from 'react-native'
|
import {FlatList, Modal, ScrollView, TextInput, View} from 'react-native'
|
||||||
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
const BottomSheetModalContext = React.createContext(null)
|
const BottomSheetModalContext = React.createContext(null)
|
||||||
BottomSheetModalContext.displayName = 'BottomSheetModalContext'
|
BottomSheetModalContext.displayName = 'BottomSheetModalContext'
|
||||||
@@ -33,12 +34,12 @@ const BottomSheetScrollView = (props: any) => <ScrollView {...props} />
|
|||||||
const BottomSheetFlatList = (props: any) => <FlatList {...props} />
|
const BottomSheetFlatList = (props: any) => <FlatList {...props} />
|
||||||
const BottomSheetTextInput = (props: any) => <TextInput {...props} />
|
const BottomSheetTextInput = (props: any) => <TextInput {...props} />
|
||||||
|
|
||||||
const useBottomSheet = jest.fn()
|
const useBottomSheet = vi.fn()
|
||||||
const useBottomSheetModal = jest.fn()
|
const useBottomSheetModal = vi.fn()
|
||||||
const useBottomSheetSpringConfigs = jest.fn()
|
const useBottomSheetSpringConfigs = vi.fn()
|
||||||
const useBottomSheetTimingConfigs = jest.fn()
|
const useBottomSheetTimingConfigs = vi.fn()
|
||||||
const useBottomSheetInternal = jest.fn()
|
const useBottomSheetInternal = vi.fn()
|
||||||
const useBottomSheetDynamicSnapPoints = jest.fn()
|
const useBottomSheetDynamicSnapPoints = vi.fn()
|
||||||
|
|
||||||
export {useBottomSheet}
|
export {useBottomSheet}
|
||||||
export {useBottomSheetModal}
|
export {useBottomSheetModal}
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
export const getLocales = jest.fn().mockResolvedValue([])
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
|
export const getLocales = vi.fn().mockResolvedValue([])
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
export const CID = jest.fn().mockImplementation(() => {
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
|
export const CID = vi.fn().mockImplementation(() => {
|
||||||
return {}
|
return {}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
export const from = jest.fn().mockImplementation(() => {
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
|
export const from = vi.fn().mockImplementation(() => {
|
||||||
return {
|
return {
|
||||||
digest: jest.fn().mockImplementation(() => {
|
digest: vi.fn().mockImplementation(() => {
|
||||||
return Promise.resolve('')
|
return Promise.resolve('')
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
jest.mock('sentry-expo', () => ({
|
import {vi} from 'vitest'
|
||||||
init: () => jest.fn(),
|
|
||||||
|
vi.mock('sentry-expo', () => ({
|
||||||
|
init: () => vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {bundleAsync} from '../../../src/lib/async/bundle'
|
import {bundleAsync} from '../../../src/lib/async/bundle'
|
||||||
|
|
||||||
describe('bundle', () => {
|
describe('bundle', () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
import {describe, expect, it} from 'vitest'
|
||||||
import tldts from 'tldts'
|
import tldts from 'tldts'
|
||||||
|
|
||||||
import {isEmailMaybeInvalid} from '#/lib/strings/email'
|
import {isEmailMaybeInvalid} from '#/lib/strings/email'
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {isNetworkError} from '../../src/lib/strings/errors'
|
import {isNetworkError} from '../../src/lib/strings/errors'
|
||||||
|
|
||||||
describe('isNetworkError', () => {
|
describe('isNetworkError', () => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {createDownloadResumable, deleteAsync} from 'expo-file-system/legacy'
|
import {createDownloadResumable, deleteAsync} from 'expo-file-system/legacy'
|
||||||
import {manipulateAsync, SaveFormat} from 'expo-image-manipulator'
|
import {manipulateAsync, SaveFormat} from 'expo-image-manipulator'
|
||||||
|
import {afterEach, beforeEach, describe, expect, it, type Mock, vi} from 'vitest'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
downloadAndResize,
|
downloadAndResize,
|
||||||
@@ -16,10 +17,10 @@ const mockResizedImage = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('downloadAndResize', () => {
|
describe('downloadAndResize', () => {
|
||||||
const errorSpy = jest.spyOn(global.console, 'error')
|
const errorSpy = vi.spyOn(global.console, 'error')
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const mockedCreateResizedImage = manipulateAsync as jest.Mock
|
const mockedCreateResizedImage = manipulateAsync as Mock
|
||||||
mockedCreateResizedImage.mockResolvedValue({
|
mockedCreateResizedImage.mockResolvedValue({
|
||||||
uri: 'file://resized-image.jpg',
|
uri: 'file://resized-image.jpg',
|
||||||
...mockResizedImage,
|
...mockResizedImage,
|
||||||
@@ -27,13 +28,13 @@ describe('downloadAndResize', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return resized image for valid URI and options', async () => {
|
it('should return resized image for valid URI and options', async () => {
|
||||||
const mockedFetch = createDownloadResumable as jest.Mock
|
const mockedFetch = createDownloadResumable as Mock
|
||||||
mockedFetch.mockReturnValue({
|
mockedFetch.mockReturnValue({
|
||||||
cancelAsync: jest.fn(),
|
cancelAsync: vi.fn(),
|
||||||
downloadAsync: jest
|
downloadAsync: jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockResolvedValue({uri: 'file://resized-image.jpg'}),
|
.mockResolvedValue({uri: 'file://resized-image.jpg'}),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {getLikelyType, LikelyType} from '../../src/lib/link-meta/link-meta'
|
import {getLikelyType, LikelyType} from '../../src/lib/link-meta/link-meta'
|
||||||
|
|
||||||
describe('getLikelyType', () => {
|
describe('getLikelyType', () => {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {clamp} from '../../src/lib/numbers'
|
import {clamp} from '../../src/lib/numbers'
|
||||||
|
|
||||||
describe('clamp', () => {
|
describe('clamp', () => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {RichText} from '@atproto/api'
|
import {RichText} from '@atproto/api'
|
||||||
import {i18n} from '@lingui/core'
|
import {i18n} from '@lingui/core'
|
||||||
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player'
|
import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player'
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {type IsValidHandle, validateServiceHandle} from '#/lib/strings/handles'
|
import {type IsValidHandle, validateServiceHandle} from '#/lib/strings/handles'
|
||||||
|
|
||||||
describe('handle validation', () => {
|
describe('handle validation', () => {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getMentionAt,
|
getMentionAt,
|
||||||
insertMentionAt,
|
insertMentionAt,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isPossiblyAUrl,
|
isPossiblyAUrl,
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,5 @@
|
|||||||
module.exports = function (api) {
|
module.exports = function (api) {
|
||||||
api.cache(true)
|
api.cache(true)
|
||||||
const isTestEnv = process.env.NODE_ENV === 'test'
|
|
||||||
return {
|
return {
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
@@ -9,8 +8,7 @@ module.exports = function (api) {
|
|||||||
lazyImports: true,
|
lazyImports: true,
|
||||||
native: {
|
native: {
|
||||||
// Disable ESM -> CJS compilation because Metro takes care of it.
|
// Disable ESM -> CJS compilation because Metro takes care of it.
|
||||||
// However, we need it in Jest tests since those run without Metro.
|
disableImportExportTransform: true,
|
||||||
disableImportExportTransform: !isTestEnv,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
const {RuleTester} = require('eslint')
|
import {RuleTester} from 'eslint'
|
||||||
const tseslint = require('typescript-eslint')
|
import tseslint from 'typescript-eslint'
|
||||||
const avoidUnwrappedText = require('../avoid-unwrapped-text')
|
import {describe, it} from 'vitest'
|
||||||
|
|
||||||
|
import avoidUnwrappedText from '../avoid-unwrapped-text.js'
|
||||||
|
|
||||||
|
RuleTester.describe = describe
|
||||||
|
RuleTester.it = it
|
||||||
|
RuleTester.itOnly = it.only
|
||||||
|
|
||||||
const ruleTester = new RuleTester({
|
const ruleTester = new RuleTester({
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
const {RuleTester} = require('eslint')
|
import {RuleTester} from 'eslint'
|
||||||
const tseslint = require('typescript-eslint')
|
import tseslint from 'typescript-eslint'
|
||||||
const linguiMsgRule = require('../lingui-msg-rule')
|
import {describe, it} from 'vitest'
|
||||||
|
|
||||||
|
import linguiMsgRule from '../lingui-msg-rule.js'
|
||||||
|
|
||||||
|
RuleTester.describe = describe
|
||||||
|
RuleTester.it = it
|
||||||
|
RuleTester.itOnly = it.only
|
||||||
|
|
||||||
const ruleTester = new RuleTester({
|
const ruleTester = new RuleTester({
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
/* global jest */
|
|
||||||
import 'react-native-gesture-handler/jestSetup'
|
|
||||||
|
|
||||||
import {configure} from '@testing-library/react-native'
|
|
||||||
|
|
||||||
configure({asyncUtilTimeout: 20000})
|
|
||||||
|
|
||||||
jest.mock('@react-native-async-storage/async-storage', () =>
|
|
||||||
require('@react-native-async-storage/async-storage/jest/async-storage-mock'),
|
|
||||||
)
|
|
||||||
jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter', () => {
|
|
||||||
// eslint-disable-next-line import-x/no-nodejs-modules
|
|
||||||
const {EventEmitter} = require('events')
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
default: EventEmitter,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
jest.mock('@fortawesome/react-native-fontawesome', () => ({
|
|
||||||
FontAwesomeIcon: '',
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('react-native-safe-area-context', () => {
|
|
||||||
const inset = {top: 0, right: 0, bottom: 0, left: 0}
|
|
||||||
return {
|
|
||||||
SafeAreaProvider: jest.fn().mockImplementation(({children}) => children),
|
|
||||||
SafeAreaConsumer: jest
|
|
||||||
.fn()
|
|
||||||
.mockImplementation(({children}) => children(inset)),
|
|
||||||
useSafeAreaInsets: jest.fn().mockImplementation(() => inset),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
jest.mock('expo-file-system/legacy', () => ({
|
|
||||||
getInfoAsync: jest.fn().mockResolvedValue({exists: true, size: 100}),
|
|
||||||
deleteAsync: jest.fn(),
|
|
||||||
moveAsync: jest.fn().mockResolvedValue(undefined),
|
|
||||||
createDownloadResumable: jest.fn(),
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('expo-image-manipulator', () => ({
|
|
||||||
manipulateAsync: jest.fn().mockResolvedValue({
|
|
||||||
uri: 'file://resized-image',
|
|
||||||
}),
|
|
||||||
SaveFormat: {
|
|
||||||
JPEG: 'jpeg',
|
|
||||||
WEBP: 'webp',
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('expo-camera', () => ({
|
|
||||||
Camera: {
|
|
||||||
useCameraPermissions: jest.fn(() => [true]),
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('expo-media-library', () => ({
|
|
||||||
__esModule: true, // this property makes it work
|
|
||||||
default: jest.fn(),
|
|
||||||
usePermissions: jest.fn(() => [true]),
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('@bsky.app/expo-guess-language', () => ({
|
|
||||||
guessLanguageSync: jest
|
|
||||||
.fn()
|
|
||||||
.mockReturnValue([{language: 'en', confidence: 1}]),
|
|
||||||
guessLanguageAsync: jest
|
|
||||||
.fn()
|
|
||||||
.mockResolvedValue([{language: 'en', confidence: 1}]),
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('sentry-expo', () => ({
|
|
||||||
init: () => jest.fn(),
|
|
||||||
Native: {
|
|
||||||
ReactNativeTracing: jest.fn().mockImplementation(() => ({
|
|
||||||
start: jest.fn(),
|
|
||||||
stop: jest.fn(),
|
|
||||||
})),
|
|
||||||
ReactNavigationInstrumentation: jest.fn(),
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('crypto', () => ({}))
|
|
||||||
|
|
||||||
jest.mock('expo-application', () => ({
|
|
||||||
nativeApplicationVersion: '1.0.0',
|
|
||||||
nativeBuildVersion: '1',
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('expo-modules-core', () => ({
|
|
||||||
requireNativeModule: jest.fn().mockImplementation(moduleName => {
|
|
||||||
if (moduleName === 'ExpoPlatformInfo') {
|
|
||||||
return {
|
|
||||||
getIsReducedMotionEnabled: () => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (moduleName === 'BottomSheet') {
|
|
||||||
return {
|
|
||||||
dismissAll: () => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
requireNativeViewManager: jest.fn().mockImplementation(_ => {
|
|
||||||
return () => null
|
|
||||||
}),
|
|
||||||
createPermissionHook: () => () => [true],
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('expo-localization', () => ({
|
|
||||||
getLocales: () => [],
|
|
||||||
}))
|
|
||||||
+8
-48
@@ -44,10 +44,10 @@
|
|||||||
"build-embed": "cd bskyembed && yarn build && yarn build-snippet && cd .. && node ./scripts/post-embed-build.js",
|
"build-embed": "cd bskyembed && yarn build && yarn build-snippet && cd .. && node ./scripts/post-embed-build.js",
|
||||||
"start": "expo start --dev-client",
|
"start": "expo start --dev-client",
|
||||||
"start:prod": "expo start --dev-client --no-dev --minify",
|
"start:prod": "expo start --dev-client --no-dev --minify",
|
||||||
"test": "NODE_ENV=test jest --forceExit --testTimeout=20000 --bail",
|
"test": "NODE_ENV=test vitest run --bail=1",
|
||||||
"test-watch": "NODE_ENV=test jest --watchAll",
|
"test-watch": "NODE_ENV=test vitest",
|
||||||
"test-ci": "NODE_ENV=test jest --ci --forceExit --reporters=default --reporters=jest-junit",
|
"test-ci": "NODE_ENV=test vitest run --reporter=default --reporter=junit --outputFile.junit=./junit.xml",
|
||||||
"test-coverage": "NODE_ENV=test jest --coverage",
|
"test-coverage": "NODE_ENV=test vitest run --coverage",
|
||||||
"lint": "eslint --cache --quiet src",
|
"lint": "eslint --cache --quiet src",
|
||||||
"lint-native": "swiftlint ./modules && ktlint ./modules",
|
"lint-native": "swiftlint ./modules && ktlint ./modules",
|
||||||
"lint-native:fix": "swiftlint --fix ./modules && ktlint --format ./modules",
|
"lint-native:fix": "swiftlint --fix ./modules && ktlint --format ./modules",
|
||||||
@@ -226,7 +226,7 @@
|
|||||||
"react-native-uitextview": "^1.4.0",
|
"react-native-uitextview": "^1.4.0",
|
||||||
"react-native-uuid": "^2.0.3",
|
"react-native-uuid": "^2.0.3",
|
||||||
"react-native-view-shot": "^4.0.3",
|
"react-native-view-shot": "^4.0.3",
|
||||||
"react-native-web": "^0.21.0",
|
"react-native-web": "^0.21.2",
|
||||||
"react-native-web-webview": "^1.0.2",
|
"react-native-web-webview": "^1.0.2",
|
||||||
"react-native-webview": "^13.15.0",
|
"react-native-webview": "^13.15.0",
|
||||||
"react-remove-scroll-bar": "^2.3.8",
|
"react-remove-scroll-bar": "^2.3.8",
|
||||||
@@ -250,12 +250,11 @@
|
|||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"@lingui/babel-plugin-lingui-macro": "^5.9.2",
|
"@lingui/babel-plugin-lingui-macro": "^5.9.2",
|
||||||
"@lingui/cli": "^5.9.2",
|
"@lingui/cli": "^5.9.2",
|
||||||
|
"@lingui/vite-plugin": "^6.0.1",
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
|
||||||
"@react-native/babel-preset": "0.81.5",
|
"@react-native/babel-preset": "0.81.5",
|
||||||
"@react-native/typescript-config": "^0.81.5",
|
"@react-native/typescript-config": "^0.81.5",
|
||||||
"@sentry/webpack-plugin": "^3.2.2",
|
"@sentry/webpack-plugin": "^3.2.2",
|
||||||
"@testing-library/react-native": "^13.2.0",
|
|
||||||
"@types/jest": "29.5.14",
|
|
||||||
"@types/lodash.chunk": "^4.2.7",
|
"@types/lodash.chunk": "^4.2.7",
|
||||||
"@types/lodash.debounce": "^4.0.7",
|
"@types/lodash.debounce": "^4.0.7",
|
||||||
"@types/lodash.shuffle": "^4.2.7",
|
"@types/lodash.shuffle": "^4.2.7",
|
||||||
@@ -263,7 +262,7 @@
|
|||||||
"@types/react": "^19.1.17",
|
"@types/react": "^19.1.17",
|
||||||
"@types/react-dom": "^19.1.11",
|
"@types/react-dom": "^19.1.11",
|
||||||
"@typescript/native-preview": "^7.0.0-dev.20260428.1",
|
"@typescript/native-preview": "^7.0.0-dev.20260428.1",
|
||||||
"babel-jest": "^29.7.0",
|
"@vitest/coverage-v8": "^4.0.0",
|
||||||
"babel-plugin-module-resolver": "^5.0.2",
|
"babel-plugin-module-resolver": "^5.0.2",
|
||||||
"babel-plugin-react-compiler": "19.1.0-rc.3",
|
"babel-plugin-react-compiler": "19.1.0-rc.3",
|
||||||
"babel-preset-expo": "~54.0.10",
|
"babel-preset-expo": "~54.0.10",
|
||||||
@@ -282,9 +281,6 @@
|
|||||||
"globals": "^17.0.0",
|
"globals": "^17.0.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"is-ci": "^3.0.1",
|
"is-ci": "^3.0.1",
|
||||||
"jest": "^29.7.0",
|
|
||||||
"jest-expo": "~54.0.17",
|
|
||||||
"jest-junit": "^16.0.0",
|
|
||||||
"lint-staged": "^13.2.3",
|
"lint-staged": "^13.2.3",
|
||||||
"prettier": "^3.8.3",
|
"prettier": "^3.8.3",
|
||||||
"react-native-dotenv": "^3.4.11",
|
"react-native-dotenv": "^3.4.11",
|
||||||
@@ -293,45 +289,9 @@
|
|||||||
"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",
|
||||||
|
"vitest": "^4.0.0",
|
||||||
"webpack-bundle-analyzer": "^4.10.1"
|
"webpack-bundle-analyzer": "^4.10.1"
|
||||||
},
|
},
|
||||||
"jest": {
|
|
||||||
"preset": "jest-expo/ios",
|
|
||||||
"setupFilesAfterEnv": [
|
|
||||||
"./jest/jestSetup.js"
|
|
||||||
],
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"ts",
|
|
||||||
"tsx",
|
|
||||||
"js",
|
|
||||||
"jsx",
|
|
||||||
"json",
|
|
||||||
"node"
|
|
||||||
],
|
|
||||||
"transform": {
|
|
||||||
"\\.[jt]sx?$": "babel-jest"
|
|
||||||
},
|
|
||||||
"transformIgnorePatterns": [
|
|
||||||
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|@discord|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|nanoid|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|normalize-url|react-native-svg|@sentry/.*|sentry-expo|bcp-47-match)"
|
|
||||||
],
|
|
||||||
"modulePathIgnorePatterns": [
|
|
||||||
"__tests__/.*/__mocks__",
|
|
||||||
"__e2e__/.*",
|
|
||||||
"bskylink/.*"
|
|
||||||
],
|
|
||||||
"coveragePathIgnorePatterns": [
|
|
||||||
"<rootDir>/node_modules/",
|
|
||||||
"<rootDir>/src/platform",
|
|
||||||
"<rootDir>/src/third-party",
|
|
||||||
"<rootDir>/src/view/com/util",
|
|
||||||
"<rootDir>/src/state/lib",
|
|
||||||
"<rootDir>/__tests__/test-utils.js"
|
|
||||||
],
|
|
||||||
"reporters": [
|
|
||||||
"default",
|
|
||||||
"jest-junit"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
|
|||||||
Generated
+834
-1277
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
|||||||
import {jest} from '@jest/globals'
|
import {beforeEach, describe, expect, it, 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,39 +1,41 @@
|
|||||||
jest.mock('#/storage', () => ({
|
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
||||||
|
|
||||||
|
vi.mock('#/storage', () => ({
|
||||||
device: {
|
device: {
|
||||||
get: jest.fn(),
|
get: vi.fn(),
|
||||||
set: jest.fn(),
|
set: vi.fn(),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
jest.mock('#/analytics/identifiers/util', () => ({
|
vi.mock('#/analytics/identifiers/util', () => ({
|
||||||
isSessionIdExpired: jest.fn(),
|
isSessionIdExpired: vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
jest.mock('#/lib/appState', () => ({
|
vi.mock('#/lib/appState', () => ({
|
||||||
onAppStateChange: jest.fn(() => ({remove: jest.fn()})),
|
onAppStateChange: vi.fn(() => ({remove: vi.fn()})),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetModules()
|
vi.resetModules()
|
||||||
jest.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
function getMocks() {
|
async function getMocks() {
|
||||||
const {device} = require('#/storage')
|
const {device} = await import('#/storage')
|
||||||
const {isSessionIdExpired} = require('#/analytics/identifiers/util')
|
const {isSessionIdExpired} = await import('#/analytics/identifiers/util')
|
||||||
return {
|
return {
|
||||||
device: jest.mocked(device),
|
device: vi.mocked(device),
|
||||||
isSessionIdExpired: jest.mocked(isSessionIdExpired),
|
isSessionIdExpired: vi.mocked(isSessionIdExpired),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('session initialization', () => {
|
describe('session initialization', () => {
|
||||||
it('creates new session and sets timestamp when none exists', () => {
|
it('creates new session and sets timestamp when none exists', async () => {
|
||||||
const {device, isSessionIdExpired} = getMocks()
|
const {device, isSessionIdExpired} = await getMocks()
|
||||||
device.get.mockReturnValue(undefined)
|
device.get.mockReturnValue(undefined)
|
||||||
isSessionIdExpired.mockReturnValue(false)
|
isSessionIdExpired.mockReturnValue(false)
|
||||||
|
|
||||||
const {getInitialSessionId} = require('./session')
|
const {getInitialSessionId} = await import('./session')
|
||||||
const id = getInitialSessionId()
|
const id = getInitialSessionId()
|
||||||
|
|
||||||
expect(id).toBeDefined()
|
expect(id).toBeDefined()
|
||||||
@@ -45,8 +47,8 @@ describe('session initialization', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reuses existing session when not expired', () => {
|
it('reuses existing session when not expired', async () => {
|
||||||
const {device, isSessionIdExpired} = getMocks()
|
const {device, isSessionIdExpired} = await getMocks()
|
||||||
const existingId = 'existing-session-id'
|
const existingId = 'existing-session-id'
|
||||||
device.get.mockImplementation((key: string[]) => {
|
device.get.mockImplementation((key: string[]) => {
|
||||||
if (key[0] === 'nativeSessionId') return existingId
|
if (key[0] === 'nativeSessionId') return existingId
|
||||||
@@ -55,13 +57,13 @@ describe('session initialization', () => {
|
|||||||
})
|
})
|
||||||
isSessionIdExpired.mockReturnValue(false)
|
isSessionIdExpired.mockReturnValue(false)
|
||||||
|
|
||||||
const {getInitialSessionId} = require('./session')
|
const {getInitialSessionId} = await import('./session')
|
||||||
|
|
||||||
expect(getInitialSessionId()).toBe(existingId)
|
expect(getInitialSessionId()).toBe(existingId)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('creates new session when existing is expired', () => {
|
it('creates new session when existing is expired', async () => {
|
||||||
const {device, isSessionIdExpired} = getMocks()
|
const {device, isSessionIdExpired} = await getMocks()
|
||||||
const existingId = 'existing-session-id'
|
const existingId = 'existing-session-id'
|
||||||
device.get.mockImplementation((key: string[]) => {
|
device.get.mockImplementation((key: string[]) => {
|
||||||
if (key[0] === 'nativeSessionId') return existingId
|
if (key[0] === 'nativeSessionId') return existingId
|
||||||
@@ -70,7 +72,7 @@ describe('session initialization', () => {
|
|||||||
})
|
})
|
||||||
isSessionIdExpired.mockReturnValue(true)
|
isSessionIdExpired.mockReturnValue(true)
|
||||||
|
|
||||||
const {getInitialSessionId} = require('./session')
|
const {getInitialSessionId} = await import('./session')
|
||||||
const id = getInitialSessionId()
|
const id = getInitialSessionId()
|
||||||
|
|
||||||
expect(id).not.toBe(existingId)
|
expect(id).not.toBe(existingId)
|
||||||
|
|||||||
@@ -1,26 +1,28 @@
|
|||||||
|
import {afterEach, beforeEach, describe, expect, it, type Mock, vi} from 'vitest'
|
||||||
|
|
||||||
import {MetricsClient} from './client'
|
import {MetricsClient} from './client'
|
||||||
|
|
||||||
let appStateCallback: (state: string) => void
|
let appStateCallback: (state: string) => void
|
||||||
|
|
||||||
jest.mock('#/lib/appState', () => ({
|
vi.mock('#/lib/appState', () => ({
|
||||||
onAppStateChange: jest.fn(cb => {
|
onAppStateChange: vi.fn(cb => {
|
||||||
appStateCallback = cb
|
appStateCallback = cb
|
||||||
return {remove: jest.fn()}
|
return {remove: vi.fn()}
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
jest.mock('#/logger', () => ({
|
vi.mock('#/logger', () => ({
|
||||||
Logger: {
|
Logger: {
|
||||||
create: () => ({
|
create: () => ({
|
||||||
info: jest.fn(),
|
info: vi.fn(),
|
||||||
debug: jest.fn(),
|
debug: vi.fn(),
|
||||||
error: jest.fn(),
|
error: vi.fn(),
|
||||||
}),
|
}),
|
||||||
Context: {Metric: 'metric'},
|
Context: {Metric: 'metric'},
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
jest.mock('#/env', () => ({
|
vi.mock('#/env', () => ({
|
||||||
METRICS_API_HOST: 'https://test.metrics.api',
|
METRICS_API_HOST: 'https://test.metrics.api',
|
||||||
IS_WEB: false,
|
IS_WEB: false,
|
||||||
}))
|
}))
|
||||||
@@ -31,13 +33,13 @@ type TestEvents = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('MetricsClient', () => {
|
describe('MetricsClient', () => {
|
||||||
let fetchMock: jest.Mock
|
let fetchMock: Mock
|
||||||
let fetchRequests: {body: any}[]
|
let fetchRequests: {body: any}[]
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.useFakeTimers({advanceTimers: true})
|
vi.useFakeTimers({shouldAdvanceTime: true})
|
||||||
fetchRequests = []
|
fetchRequests = []
|
||||||
fetchMock = jest.fn().mockImplementation(async (_url, options) => {
|
fetchMock = vi.fn().mockImplementation(async (_url, options) => {
|
||||||
const body = JSON.parse(options.body)
|
const body = JSON.parse(options.body)
|
||||||
fetchRequests.push({body})
|
fetchRequests.push({body})
|
||||||
return {ok: true, status: 200}
|
return {ok: true, status: 200}
|
||||||
@@ -46,8 +48,8 @@ describe('MetricsClient', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.useRealTimers()
|
vi.useRealTimers()
|
||||||
jest.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('flushes events on interval', async () => {
|
it('flushes events on interval', async () => {
|
||||||
@@ -58,7 +60,7 @@ describe('MetricsClient', () => {
|
|||||||
expect(fetchRequests).toHaveLength(0)
|
expect(fetchRequests).toHaveLength(0)
|
||||||
|
|
||||||
// Advance past the 10 second interval
|
// Advance past the 10 second interval
|
||||||
await jest.advanceTimersByTimeAsync(10_000)
|
await vi.advanceTimersByTimeAsync(10_000)
|
||||||
|
|
||||||
expect(fetchRequests).toHaveLength(1)
|
expect(fetchRequests).toHaveLength(1)
|
||||||
expect(fetchRequests[0].body.events).toHaveLength(2)
|
expect(fetchRequests[0].body.events).toHaveLength(2)
|
||||||
@@ -81,7 +83,7 @@ describe('MetricsClient', () => {
|
|||||||
client.track('click', {button: 'btn-trigger'})
|
client.track('click', {button: 'btn-trigger'})
|
||||||
|
|
||||||
// Allow microtasks to run
|
// Allow microtasks to run
|
||||||
await jest.advanceTimersByTimeAsync(0)
|
await vi.advanceTimersByTimeAsync(0)
|
||||||
|
|
||||||
expect(fetchRequests).toHaveLength(1)
|
expect(fetchRequests).toHaveLength(1)
|
||||||
expect(fetchRequests[0].body.events).toHaveLength(6)
|
expect(fetchRequests[0].body.events).toHaveLength(6)
|
||||||
@@ -112,14 +114,14 @@ describe('MetricsClient', () => {
|
|||||||
client.track('click', {button: 'submit'})
|
client.track('click', {button: 'submit'})
|
||||||
|
|
||||||
// Trigger flush via interval
|
// Trigger flush via interval
|
||||||
await jest.advanceTimersByTimeAsync(10_000)
|
await vi.advanceTimersByTimeAsync(10_000)
|
||||||
|
|
||||||
expect(requestCount).toBe(1)
|
expect(requestCount).toBe(1)
|
||||||
expect(fetchRequests).toHaveLength(0)
|
expect(fetchRequests).toHaveLength(0)
|
||||||
|
|
||||||
// Simulate app coming to foreground to trigger retry
|
// Simulate app coming to foreground to trigger retry
|
||||||
appStateCallback('active')
|
appStateCallback('active')
|
||||||
await jest.advanceTimersByTimeAsync(0)
|
await vi.advanceTimersByTimeAsync(0)
|
||||||
|
|
||||||
expect(requestCount).toBe(2)
|
expect(requestCount).toBe(2)
|
||||||
expect(fetchRequests).toHaveLength(1)
|
expect(fetchRequests).toHaveLength(1)
|
||||||
@@ -144,19 +146,19 @@ describe('MetricsClient', () => {
|
|||||||
client.track('click', {button: 'submit'})
|
client.track('click', {button: 'submit'})
|
||||||
|
|
||||||
// First flush fails
|
// First flush fails
|
||||||
await jest.advanceTimersByTimeAsync(10_000)
|
await vi.advanceTimersByTimeAsync(10_000)
|
||||||
|
|
||||||
expect(requestCount).toBe(1)
|
expect(requestCount).toBe(1)
|
||||||
|
|
||||||
// Retry also fails
|
// Retry also fails
|
||||||
appStateCallback('active')
|
appStateCallback('active')
|
||||||
await jest.advanceTimersByTimeAsync(0)
|
await vi.advanceTimersByTimeAsync(0)
|
||||||
|
|
||||||
expect(requestCount).toBe(2)
|
expect(requestCount).toBe(2)
|
||||||
|
|
||||||
// Another foreground event should not retry again (events are dropped)
|
// Another foreground event should not retry again (events are dropped)
|
||||||
appStateCallback('active')
|
appStateCallback('active')
|
||||||
await jest.advanceTimersByTimeAsync(0)
|
await vi.advanceTimersByTimeAsync(0)
|
||||||
|
|
||||||
expect(requestCount).toBe(2) // No additional requests
|
expect(requestCount).toBe(2) // No additional requests
|
||||||
})
|
})
|
||||||
@@ -169,7 +171,7 @@ describe('MetricsClient', () => {
|
|||||||
|
|
||||||
// Simulate app going to background
|
// Simulate app going to background
|
||||||
appStateCallback('background')
|
appStateCallback('background')
|
||||||
await jest.advanceTimersByTimeAsync(0)
|
await vi.advanceTimersByTimeAsync(0)
|
||||||
|
|
||||||
expect(fetchRequests).toHaveLength(1)
|
expect(fetchRequests).toHaveLength(1)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {describe, test} from '@jest/globals'
|
import {describe, expect, test, 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,4 +1,4 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {parseLinkingUrl} from '../parseLinkingUrl'
|
import {parseLinkingUrl} from '../parseLinkingUrl'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {beforeEach, describe, expect, it, jest} from '@jest/globals'
|
import {beforeEach, describe, expect, it, vi} from 'vitest'
|
||||||
|
|
||||||
jest.mock('@bsky.app/react-native-mmkv', () => ({
|
vi.mock('@bsky.app/react-native-mmkv', () => ({
|
||||||
MMKV: class MMKVMock {
|
MMKV: class MMKVMock {
|
||||||
_store = new Map<string, string>()
|
_store = new Map<string, string>()
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
import {describe, expect, it} from 'vitest'
|
||||||
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,4 +1,4 @@
|
|||||||
import {expect, test} from '@jest/globals'
|
import {expect, test} from 'vitest'
|
||||||
|
|
||||||
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
||||||
import {AppLanguage} from '#/locale/languages'
|
import {AppLanguage} from '#/locale/languages'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {expect, test} from '@jest/globals'
|
import {expect, test} from 'vitest'
|
||||||
|
|
||||||
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,4 +1,4 @@
|
|||||||
import {beforeAll, describe, expect, jest, test} from '@jest/globals'
|
import {beforeAll, describe, expect, test, vi} from 'vitest'
|
||||||
import * as Sentry from '@sentry/react-native'
|
import * as Sentry from '@sentry/react-native'
|
||||||
import {nanoid} from 'nanoid/non-secure'
|
import {nanoid} from 'nanoid/non-secure'
|
||||||
|
|
||||||
@@ -6,14 +6,14 @@ import {Logger} from '#/logger'
|
|||||||
import {sentryTransport} from '#/logger/transports/sentry'
|
import {sentryTransport} from '#/logger/transports/sentry'
|
||||||
import {LogLevel} from '#/logger/types'
|
import {LogLevel} from '#/logger/types'
|
||||||
|
|
||||||
jest.mock('@sentry/react-native', () => ({
|
vi.mock('@sentry/react-native', () => ({
|
||||||
addBreadcrumb: jest.fn(),
|
addBreadcrumb: vi.fn(),
|
||||||
captureException: jest.fn(),
|
captureException: vi.fn(),
|
||||||
captureMessage: jest.fn(),
|
captureMessage: vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
jest.useFakeTimers()
|
vi.useFakeTimers()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('general functionality', () => {
|
describe('general functionality', () => {
|
||||||
@@ -41,7 +41,7 @@ describe('general functionality', () => {
|
|||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const logger = new Logger({})
|
const logger = new Logger({})
|
||||||
|
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
|
|
||||||
logger.addTransport(mockTransport)
|
logger.addTransport(mockTransport)
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ describe('general functionality', () => {
|
|||||||
metadata: {bar: true},
|
metadata: {bar: true},
|
||||||
})
|
})
|
||||||
|
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
|
|
||||||
logger.addTransport(mockTransport)
|
logger.addTransport(mockTransport)
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ describe('general functionality', () => {
|
|||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const logger = new Logger({})
|
const logger = new Logger({})
|
||||||
|
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
|
|
||||||
const remove = logger.addTransport(mockTransport)
|
const remove = logger.addTransport(mockTransport)
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ describe('general functionality', () => {
|
|||||||
level: 'log',
|
level: 'log',
|
||||||
timestamp: sentryTimestamp,
|
timestamp: sentryTimestamp,
|
||||||
})
|
})
|
||||||
jest.runAllTimers()
|
vi.runAllTimers()
|
||||||
expect(Sentry.captureMessage).toHaveBeenCalledWith(message, {
|
expect(Sentry.captureMessage).toHaveBeenCalledWith(message, {
|
||||||
level: 'log',
|
level: 'log',
|
||||||
tags: {category: 'logger'},
|
tags: {category: 'logger'},
|
||||||
@@ -203,7 +203,7 @@ describe('general functionality', () => {
|
|||||||
level: 'warning',
|
level: 'warning',
|
||||||
timestamp: sentryTimestamp,
|
timestamp: sentryTimestamp,
|
||||||
})
|
})
|
||||||
jest.runAllTimers()
|
vi.runAllTimers()
|
||||||
expect(Sentry.captureMessage).toHaveBeenCalledWith(message, {
|
expect(Sentry.captureMessage).toHaveBeenCalledWith(message, {
|
||||||
level: 'warning',
|
level: 'warning',
|
||||||
tags: {category: 'logger'},
|
tags: {category: 'logger'},
|
||||||
@@ -262,7 +262,7 @@ describe('general functionality', () => {
|
|||||||
test('add/remove transport', () => {
|
test('add/remove transport', () => {
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const logger = new Logger({})
|
const logger = new Logger({})
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
|
|
||||||
const remove = logger.addTransport(mockTransport)
|
const remove = logger.addTransport(mockTransport)
|
||||||
|
|
||||||
@@ -286,7 +286,7 @@ describe('general functionality', () => {
|
|||||||
|
|
||||||
describe('create', () => {
|
describe('create', () => {
|
||||||
test('create', () => {
|
test('create', () => {
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const logger = Logger.create(Logger.Context.Default)
|
const logger = Logger.create(Logger.Context.Default)
|
||||||
@@ -306,7 +306,7 @@ describe('create', () => {
|
|||||||
|
|
||||||
describe('debug contexts', () => {
|
describe('debug contexts', () => {
|
||||||
test('specific', () => {
|
test('specific', () => {
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const logger = new Logger({
|
const logger = new Logger({
|
||||||
@@ -328,7 +328,7 @@ describe('debug contexts', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('namespaced', () => {
|
test('namespaced', () => {
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const logger = new Logger({
|
const logger = new Logger({
|
||||||
@@ -351,7 +351,7 @@ describe('debug contexts', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('ignores inactive', () => {
|
test('ignores inactive', () => {
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const logger = new Logger({
|
const logger = new Logger({
|
||||||
@@ -380,7 +380,7 @@ describe('supports levels', () => {
|
|||||||
level: LogLevel.Debug,
|
level: LogLevel.Debug,
|
||||||
})
|
})
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
|
|
||||||
logger.addTransport(mockTransport)
|
logger.addTransport(mockTransport)
|
||||||
|
|
||||||
@@ -428,7 +428,7 @@ describe('supports levels', () => {
|
|||||||
level: LogLevel.Info,
|
level: LogLevel.Info,
|
||||||
})
|
})
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
|
|
||||||
logger.addTransport(mockTransport)
|
logger.addTransport(mockTransport)
|
||||||
|
|
||||||
@@ -451,7 +451,7 @@ describe('supports levels', () => {
|
|||||||
level: LogLevel.Warn,
|
level: LogLevel.Warn,
|
||||||
})
|
})
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
|
|
||||||
logger.addTransport(mockTransport)
|
logger.addTransport(mockTransport)
|
||||||
|
|
||||||
@@ -477,7 +477,7 @@ describe('supports levels', () => {
|
|||||||
level: LogLevel.Error,
|
level: LogLevel.Error,
|
||||||
})
|
})
|
||||||
const message = nanoid()
|
const message = nanoid()
|
||||||
const mockTransport = jest.fn()
|
const mockTransport = vi.fn()
|
||||||
|
|
||||||
logger.addTransport(mockTransport)
|
logger.addTransport(mockTransport)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
import {describe, expect, it} from 'vitest'
|
||||||
|
|
||||||
import {parseSearchQuery} from '#/screens/Search/utils'
|
import {parseSearchQuery} from '#/screens/Search/utils'
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {describe, it} from '@jest/globals'
|
import {describe, it} from 'vitest'
|
||||||
|
|
||||||
describe(`#/state/messages/convo`, () => {
|
describe(`#/state/messages/convo`, () => {
|
||||||
describe(`init`, () => {
|
describe(`init`, () => {
|
||||||
|
|||||||
@@ -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 {}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import {BskyAgent} from '@atproto/api'
|
import {BskyAgent} from '@atproto/api'
|
||||||
import {describe, expect, it, jest} from '@jest/globals'
|
import {describe, expect, it, vi} from 'vitest'
|
||||||
|
|
||||||
import {agentToSessionAccountOrThrow} from '../agent'
|
import {agentToSessionAccountOrThrow} from '../agent'
|
||||||
import {type Action, getInitialState, reducer, type State} from '../reducer'
|
import {type Action, getInitialState, reducer, type State} from '../reducer'
|
||||||
|
|
||||||
jest.mock('jwt-decode', () => ({
|
vi.mock('jwt-decode', () => ({
|
||||||
jwtDecode(_token: string) {
|
jwtDecode(_token: string) {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
jest.mock('../../birthdate')
|
vi.mock('../../birthdate')
|
||||||
jest.mock('../../../ageAssurance/data')
|
vi.mock('../../../ageAssurance/data')
|
||||||
jest.mock('../../../ageAssurance/state', () => ({
|
vi.mock('../../../ageAssurance/state', () => ({
|
||||||
getAndComputeAgeAssuranceState: () => ({}),
|
getAndComputeAgeAssuranceState: () => ({}),
|
||||||
}))
|
}))
|
||||||
jest.mock('#/lib/notifications/notifications', () => ({
|
vi.mock('#/lib/notifications/notifications', () => ({
|
||||||
unregisterPushToken(_agents: BskyAgent[]) {
|
unregisterPushToken(_agents: BskyAgent[]) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {beforeEach, expect, jest, test} from '@jest/globals'
|
import {beforeEach, expect, test, vi} from 'vitest'
|
||||||
|
|
||||||
import {Storage} from '#/storage'
|
import {Storage} from '#/storage'
|
||||||
|
|
||||||
jest.mock('@bsky.app/react-native-mmkv', () => ({
|
vi.mock('@bsky.app/react-native-mmkv', () => ({
|
||||||
MMKV: class MMKVMock {
|
MMKV: class MMKVMock {
|
||||||
_store = new Map()
|
_store = new Map()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"lib": ["dom", "esnext"],
|
"lib": ["dom", "esnext"],
|
||||||
"types": ["node", "jest"],
|
"types": ["node"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"#/*": ["./src/*"],
|
"#/*": ["./src/*"],
|
||||||
"crypto": ["./src/platform/crypto.ts"],
|
"crypto": ["./src/platform/crypto.ts"],
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import {fileURLToPath} from 'node:url'
|
||||||
|
|
||||||
|
import {lingui} from '@lingui/vite-plugin'
|
||||||
|
import {defineConfig} from 'vitest/config'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [lingui()],
|
||||||
|
define: {
|
||||||
|
__DEV__: 'true',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
tsconfigPaths: true,
|
||||||
|
alias: {
|
||||||
|
crypto: fileURLToPath(new URL('./src/platform/crypto.ts', import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
globals: false,
|
||||||
|
environment: 'node',
|
||||||
|
setupFiles: ['./vitest/setup.ts'],
|
||||||
|
include: [
|
||||||
|
'__tests__/**/*.test.{ts,tsx,js,jsx}',
|
||||||
|
'src/**/*.test.{ts,tsx}',
|
||||||
|
'src/**/__tests__/**/*.{ts,tsx}',
|
||||||
|
'eslint/**/*.test.{js,ts}',
|
||||||
|
],
|
||||||
|
exclude: [
|
||||||
|
'**/node_modules/**',
|
||||||
|
'**/__mocks__/**',
|
||||||
|
'__e2e__/**',
|
||||||
|
'bskylink/**',
|
||||||
|
'bskyembed/**',
|
||||||
|
],
|
||||||
|
testTimeout: 20000,
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
exclude: [
|
||||||
|
'node_modules/**',
|
||||||
|
'src/platform/**',
|
||||||
|
'src/third-party/**',
|
||||||
|
'src/view/com/util/**',
|
||||||
|
'src/state/lib/**',
|
||||||
|
'__tests__/test-utils.js',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
deps: {
|
||||||
|
inline: [
|
||||||
|
/^react-native($|\/)/,
|
||||||
|
/^@react-native($|\/)/,
|
||||||
|
/^expo($|-|\/)/,
|
||||||
|
/^@expo($|\/)/,
|
||||||
|
/^@react-navigation($|\/)/,
|
||||||
|
/^react-native-svg($|\/)/,
|
||||||
|
/^nanoid($|\/)/,
|
||||||
|
/^@sentry($|\/)/,
|
||||||
|
/^sentry-expo($|\/)/,
|
||||||
|
/^bcp-47-match($|\/)/,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
import {EventEmitter} from 'node:events'
|
||||||
|
|
||||||
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
|
// react-native ships Flow syntax in its index.js entry point, which Vitest's
|
||||||
|
// rolldown parser cannot handle. None of the tests exercise actual RN runtime
|
||||||
|
// behavior, so stub the whole module to satisfy transitive imports.
|
||||||
|
vi.mock('react-native', () => ({
|
||||||
|
Platform: {OS: 'ios', select: (obj: any) => obj.ios ?? obj.default},
|
||||||
|
StyleSheet: {create: (s: any) => s, flatten: (s: any) => s, hairlineWidth: 1},
|
||||||
|
View: 'View',
|
||||||
|
Text: 'Text',
|
||||||
|
ScrollView: 'ScrollView',
|
||||||
|
FlatList: 'FlatList',
|
||||||
|
TextInput: 'TextInput',
|
||||||
|
Modal: 'Modal',
|
||||||
|
Image: 'Image',
|
||||||
|
Pressable: 'Pressable',
|
||||||
|
TouchableOpacity: 'TouchableOpacity',
|
||||||
|
TouchableWithoutFeedback: 'TouchableWithoutFeedback',
|
||||||
|
NativeModules: {},
|
||||||
|
NativeEventEmitter: EventEmitter,
|
||||||
|
AppState: {addEventListener: () => ({remove: () => {}}), currentState: 'active'},
|
||||||
|
Linking: {openURL: vi.fn(), canOpenURL: vi.fn().mockResolvedValue(true)},
|
||||||
|
Dimensions: {get: () => ({width: 375, height: 812}), addEventListener: () => ({remove: () => {}})},
|
||||||
|
PixelRatio: {get: () => 2, getFontScale: () => 1},
|
||||||
|
Keyboard: {addListener: () => ({remove: () => {}}), dismiss: () => {}},
|
||||||
|
InteractionManager: {runAfterInteractions: (fn: () => void) => fn()},
|
||||||
|
Animated: {View: 'AnimatedView', Value: class {}, timing: () => ({start: () => {}})},
|
||||||
|
UIManager: {},
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@react-native-async-storage/async-storage', () =>
|
||||||
|
require('@react-native-async-storage/async-storage/jest/async-storage-mock'),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.mock('react-native/Libraries/EventEmitter/NativeEventEmitter', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: EventEmitter,
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@fortawesome/react-native-fontawesome', () => ({
|
||||||
|
FontAwesomeIcon: '',
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('react-native-safe-area-context', () => {
|
||||||
|
const inset = {top: 0, right: 0, bottom: 0, left: 0}
|
||||||
|
return {
|
||||||
|
SafeAreaProvider: vi.fn().mockImplementation(({children}) => children),
|
||||||
|
SafeAreaConsumer: vi
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(({children}) => children(inset)),
|
||||||
|
useSafeAreaInsets: vi.fn().mockImplementation(() => inset),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
vi.mock('expo-file-system/legacy', () => ({
|
||||||
|
getInfoAsync: vi.fn().mockResolvedValue({exists: true, size: 100}),
|
||||||
|
deleteAsync: vi.fn(),
|
||||||
|
moveAsync: vi.fn().mockResolvedValue(undefined),
|
||||||
|
createDownloadResumable: vi.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('expo-image-manipulator', () => ({
|
||||||
|
manipulateAsync: vi.fn().mockResolvedValue({
|
||||||
|
uri: 'file://resized-image',
|
||||||
|
}),
|
||||||
|
SaveFormat: {
|
||||||
|
JPEG: 'jpeg',
|
||||||
|
WEBP: 'webp',
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('expo-camera', () => ({
|
||||||
|
Camera: {
|
||||||
|
useCameraPermissions: vi.fn(() => [true]),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('expo-media-library', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: vi.fn(),
|
||||||
|
usePermissions: vi.fn(() => [true]),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@bsky.app/expo-guess-language', () => ({
|
||||||
|
guessLanguageSync: vi
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue([{language: 'en', confidence: 1}]),
|
||||||
|
guessLanguageAsync: vi
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValue([{language: 'en', confidence: 1}]),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('sentry-expo', () => ({
|
||||||
|
init: () => vi.fn(),
|
||||||
|
Native: {
|
||||||
|
ReactNativeTracing: vi.fn().mockImplementation(() => ({
|
||||||
|
start: vi.fn(),
|
||||||
|
stop: vi.fn(),
|
||||||
|
})),
|
||||||
|
ReactNavigationInstrumentation: vi.fn(),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('crypto', () => ({}))
|
||||||
|
|
||||||
|
vi.mock('expo-application', () => ({
|
||||||
|
nativeApplicationVersion: '1.0.0',
|
||||||
|
nativeBuildVersion: '1',
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('expo-modules-core', () => ({
|
||||||
|
requireNativeModule: vi.fn().mockImplementation(moduleName => {
|
||||||
|
if (moduleName === 'ExpoPlatformInfo') {
|
||||||
|
return {
|
||||||
|
getIsReducedMotionEnabled: () => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (moduleName === 'BottomSheet') {
|
||||||
|
return {
|
||||||
|
dismissAll: () => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
requireNativeViewManager: vi.fn().mockImplementation(_ => {
|
||||||
|
return () => null
|
||||||
|
}),
|
||||||
|
createPermissionHook: () => () => [true],
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('expo-localization', () => ({
|
||||||
|
getLocales: () => [],
|
||||||
|
}))
|
||||||
Reference in New Issue
Block a user