diff --git a/.eslintrc.js b/.eslintrc.js index 7c718a1374..a39a0051ce 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,11 +1,12 @@ module.exports = { root: true, - plugins: ['jest'], - extends: ['@react-native', 'plugin:jest/recommended'], - + extends: ['@react-native'], overrides: [ { - files: ['**/__tests__/**', '**/*.test.*', '**/*.spec.*'], + // Test files only + plugins: ['jest'], + files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'], + extends: ['plugin:testing-library/react', 'plugin:jest/recommended'], }, { files: ['*.js', '*.jsx', '*.ts', '*.tsx'], diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 0000000000..65d3c4b4a4 --- /dev/null +++ b/.github/workflows/testing.yml @@ -0,0 +1,48 @@ +name: Testing +on: + push: + branches: + - master + pull_request: + branches: + - master + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Test + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Get pnpm Store Directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm Cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Run Tests + run: pnpm run test diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000000..21ae843b7e --- /dev/null +++ b/TESTING.md @@ -0,0 +1,80 @@ +# Testing Guide for LNReader + +This guide explains how to write tests in this React Native project using Jest and React Testing Library. + + +## Existing Mocks + +### Global Mocks + +The project has global mocks configured in Jest. These are automatically applied: + +- `__mocks__/` - Global mocks for native modules (react-native-mmkv, react-navigation, all database queries, etc.) +- `src/hooks/__mocks__/index.ts` - Hook-specific mocks (showToast, getString, parseChapterNumber, etc.) +- `src/hooks/__tests__/mocks.ts` - Extended mocks for persisted hooks + +### Using @test-utils + +There's a custom render wrapper at `__tests-modules__/test-utils.tsx` with: + +- `render` - wraps with GestureHandlerRootView, SafeAreaProvider, PaperProvider, etc. +- `renderNovel` - includes NovelContextProvider +- `AllTheProviders` - the full provider wrapper + +Usage: + +```typescript +import { render, renderNovel } from '@test-utils'; +``` + +## Common Issues + +### 1. ESM Modules Not Transforming + +If you see `Cannot use import statement outside a module`, you need to add mocks for the module: + +```typescript +jest.mock('@hooks/persisted/usePlugins'); +// Add more specific mocks as needed +``` + +### 2. Mock Functions Not Working + +If `mockReturnValue` throws "not a function", create mock functions at module level: + +```typescript +// CORRECT: Module-level mock functions +const mockUseTheme = jest.fn(); +jest.mock('@hooks/persisted', () => ({ + useTheme: () => mockUseTheme(), +})); + +// INCORRECT: Trying to use jest.Mock type casting +// (useTheme as jest.Mock).mockReturnValue(...) // This fails! +``` + +### 3. Test Isolation + +Tests must mock at module level, not in `beforeEach`: + +```typescript +// CORRECT +const mockFn = jest.fn(); +jest.mock('module', () => ({ useHook: () => mockFn() })); + +// INCORRECT - mocks get reset between tests +jest.mock('module'); +beforeEach(() => { + // This doesn't work properly +}); +``` + +## Running Tests + +```bash +pnpm test # Run all tests +pnpm test:watch # Watch mode +pnpm test:coverage # With coverage +pnpm test:rn # React Native only +pnpm test:db # Database only +``` diff --git a/__mocks__/database.js b/__mocks__/database.js new file mode 100644 index 0000000000..f023116ec2 --- /dev/null +++ b/__mocks__/database.js @@ -0,0 +1,66 @@ +jest.mock('@database/queries/NovelQueries', () => ({ + getNovelByPath: jest.fn(), + deleteCachedNovels: jest.fn(), + getCachedNovels: jest.fn(), + insertNovelAndChapters: jest.fn(), +})); + +jest.mock('@database/queries/CategoryQueries', () => ({ + getCategoriesFromDb: jest.fn(), + getCategoriesWithCount: jest.fn(), + createCategory: jest.fn(), + deleteCategoryById: jest.fn(), + updateCategory: jest.fn(), + isCategoryNameDuplicate: jest.fn(), + updateCategoryOrderInDb: jest.fn(), + getAllNovelCategories: jest.fn(), + _restoreCategory: jest.fn(), +})); + +jest.mock('@database/queries/ChapterQueries', () => ({ + bookmarkChapter: jest.fn(), + markChapterRead: jest.fn(), + markChaptersRead: jest.fn(), + markPreviuschaptersRead: jest.fn(), + markPreviousChaptersUnread: jest.fn(), + markChaptersUnread: jest.fn(), + deleteChapter: jest.fn(), + deleteChapters: jest.fn(), + getPageChapters: jest.fn(), + insertChapters: jest.fn(), + getCustomPages: jest.fn(), + getChapterCount: jest.fn(), + getPageChaptersBatched: jest.fn(), + getFirstUnreadChapter: jest.fn(), + updateChapterProgress: jest.fn(), +})); + +jest.mock('@database/queries/HistoryQueries', () => ({ + getHistoryFromDb: jest.fn(), + insertHistory: jest.fn(), + deleteChapterHistory: jest.fn(), + deleteAllHistory: jest.fn(), +})); + +jest.mock('@database/queries/LibraryQueries', () => ({ + getLibraryNovelsFromDb: jest.fn(), + getLibraryWithCategory: jest.fn(), +})); + +jest.mock('@database/queries/RepositoryQueries', () => ({ + getRepositoriesFromDb: jest.fn(), + isRepoUrlDuplicated: jest.fn(), + createRepository: jest.fn(), + deleteRepositoryById: jest.fn(), + updateRepository: jest.fn(), +})); + +jest.mock('@database/queries/StatsQueries', () => ({ + getLibraryStatsFromDb: jest.fn(), + getChaptersTotalCountFromDb: jest.fn(), + getChaptersReadCountFromDb: jest.fn(), + getChaptersUnreadCountFromDb: jest.fn(), + getChaptersDownloadedCountFromDb: jest.fn(), + getNovelGenresFromDb: jest.fn(), + getNovelStatusFromDb: jest.fn(), +})); diff --git a/__mocks__/index.js b/__mocks__/index.js new file mode 100644 index 0000000000..08a7b5dfce --- /dev/null +++ b/__mocks__/index.js @@ -0,0 +1,4 @@ +require('./nativeModules'); +require('./react-native-mmkv'); +require('./database'); +require('./react-navigation'); diff --git a/__mocks__/nativeModules.js b/__mocks__/nativeModules.js new file mode 100644 index 0000000000..fa3c82cdfd --- /dev/null +++ b/__mocks__/nativeModules.js @@ -0,0 +1,67 @@ +// require('react-native-gesture-handler/jestSetup'); +// require('react-native-reanimated').setUpTests(); + +jest.mock('@specs/NativeFile', () => ({ + __esModule: true, + default: { + writeFile: jest.fn(), + readFile: jest.fn(() => ''), + copyFile: jest.fn(), + moveFile: jest.fn(), + exists: jest.fn(() => true), + mkdir: jest.fn(), + unlink: jest.fn(), + readDir: jest.fn(() => []), + downloadFile: jest.fn().mockResolvedValue(), + getConstants: jest.fn(() => ({ + ExternalDirectoryPath: '/mock/external', + ExternalCachesDirectoryPath: '/mock/caches', + })), + }, +})); + +jest.mock('@specs/NativeEpub', () => ({ + __esModule: true, + default: { + parseNovelAndChapters: jest.fn(() => ({ + name: 'Mock Novel', + cover: null, + summary: null, + author: null, + artist: null, + chapters: [], + cssPaths: [], + imagePaths: [], + })), + }, +})); + +jest.mock('@specs/NativeTTSMediaControl', () => ({ + __esModule: true, + default: { + showMediaNotification: jest.fn(), + updatePlaybackState: jest.fn(), + updateProgress: jest.fn(), + dismiss: jest.fn(), + addListener: jest.fn(), + removeListeners: jest.fn(), + }, +})); + +jest.mock('@specs/NativeVolumeButtonListener', () => ({ + __esModule: true, + default: { + addListener: jest.fn(), + removeListeners: jest.fn(), + }, +})); + +jest.mock('@specs/NativeZipArchive', () => ({ + __esModule: true, + default: { + zip: jest.fn().mockResolvedValue(), + unzip: jest.fn().mockResolvedValue(), + remoteUnzip: jest.fn().mockResolvedValue(), + remoteZip: jest.fn().mockResolvedValue(''), + }, +})); diff --git a/__mocks__/react-native-mmkv.js b/__mocks__/react-native-mmkv.js new file mode 100644 index 0000000000..fdb54351a5 --- /dev/null +++ b/__mocks__/react-native-mmkv.js @@ -0,0 +1,9 @@ +// Mock for react-native-mmkv (v3 uses NitroModules under the hood) +module.exports = { + NitroModules: { + createHybridObject: jest.fn(() => { + // Return a mock object that won't be used since MMKV has its own mock + return {}; + }), + }, +}; diff --git a/__mocks__/react-navigation.js b/__mocks__/react-navigation.js new file mode 100644 index 0000000000..b3b856a46b --- /dev/null +++ b/__mocks__/react-navigation.js @@ -0,0 +1,29 @@ +const mockNavigate = jest.fn(); +const mockSetOptions = jest.fn(); + +jest.mock('react-native-worklets', () => + require('react-native-worklets/src/mock'), +); + +// Include this line for mocking react-native-gesture-handler +require('react-native-gesture-handler/jestSetup'); + +// Include this section for mocking react-native-reanimated +const { setUpTests } = require('react-native-reanimated'); + +setUpTests(); + +jest.mock('@react-navigation/native', () => { + return { + useFocusEffect: jest.fn(), + useNavigation: () => ({ + navigate: mockNavigate, + setOptions: mockSetOptions, + }), + useRoute: () => ({ + params: {}, + }), + }; +}); + +module.exports = { mockNavigate, mockSetOptions }; diff --git a/__tests-modules__/test-utils.tsx b/__tests-modules__/test-utils.tsx new file mode 100644 index 0000000000..9e16ced519 --- /dev/null +++ b/__tests-modules__/test-utils.tsx @@ -0,0 +1,48 @@ +import { render } from '@testing-library/react-native'; +import React from 'react'; +import { GestureHandlerRootView } from 'react-native-gesture-handler'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; +import { Provider as PaperProvider } from 'react-native-paper'; +import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; + +import AppErrorBoundary from '@components/AppErrorBoundary/AppErrorBoundary'; +import { NovelContextProvider } from '@screens/novel/NovelContext'; +import { NovelScreenProps, ChapterScreenProps } from '@navigators/types'; + +const AllTheProviders = ({ children }: { children: React.ReactElement }) => { + return ( + + + + + {children} + + + + + ); +}; + +const customRender = (ui: React.ReactElement, options?: object) => + render(ui, { wrapper: AllTheProviders, ...options }); + +const renderNovel = ( + ui: React.ReactElement, + options?: { + route?: NovelScreenProps['route'] | ChapterScreenProps['route']; + }, +) => { + const { route } = options || {}; + return render( + + {ui} + , + { wrapper: AllTheProviders, ...options }, + ); +}; + +export * from '@testing-library/react-native'; + +export { customRender as render, renderNovel, AllTheProviders }; diff --git a/__tests__/jest.setup.ts b/__tests__/jest.setup.ts new file mode 100644 index 0000000000..c9d4e40363 --- /dev/null +++ b/__tests__/jest.setup.ts @@ -0,0 +1,3 @@ +process.env.EXPO_OS = 'android'; + +global.IS_REACT_ACT_ENVIRONMENT = true; diff --git a/babel.config.js b/babel.config.js index bfffe2484d..842ceb57a6 100644 --- a/babel.config.js +++ b/babel.config.js @@ -26,6 +26,7 @@ module.exports = function (api) { '@api': './src/api', '@type': './src/type', '@specs': './specs', + '@test-utils': './__tests-modules__/test-utils', 'react-native-vector-icons/MaterialCommunityIcons': '@react-native-vector-icons/material-design-icons', }, diff --git a/jest.config.js b/jest.config.js index 40461025b7..5268de4553 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,57 +1,85 @@ -module.exports = { - // Use node environment for database tests (no React Native needed) - testEnvironment: 'node', - roots: ['/src'], - testMatch: ['**/__tests__/**/*.test.ts', '**/__tests__/**/*.test.tsx'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - - // Transform TypeScript and JavaScript files using Babel (uses babel.config.js) - transform: { - '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest', - }, +// jest.config.js +const baseModuleNameMapper = { + '^@components$': '/src/components/index', + '^@components/(.*)$': '/src/components/$1', + '^@database/(.*)$': '/src/database/$1', + '^@hooks$': '/src/hooks/index', + '^@hooks/(.*)$': '/src/hooks/$1', + '^@screens/(.*)$': '/src/screens/$1', + '^@strings/(.*)$': '/strings/$1', + '^@theme/(.*)$': '/src/theme/$1', + '^@utils/(.*)$': '/src/utils/$1', + '^@plugins/(.*)$': '/src/plugins/$1', + '^@services/(.*)$': '/src/services/$1', + '^@navigators/(.*)$': '/src/navigators/$1', + '^@native/(.*)$': '/src/native/$1', + '^@api/(.*)$': '/src/api/$1', + '^@type/(.*)$': '/src/type/$1', + '^@specs/(.*)$': '/specs/$1', + '^@test-utils$': '/__tests-modules__/test-utils', + // Mock static assets + '\\.(jpg|jpeg|png|gif|webp|svg)$': '/__mocks__/fileMock.js', +}; - // Transform node_modules packages that use ES modules - // Exclude most packages, but include ones that need transformation - // Note: The pattern uses negative lookahead - packages NOT matching this pattern are ignored - transformIgnorePatterns: [ - 'node_modules/(?!(react-native|@react-native|@react-native-community|@react-navigation|expo|expo-.*|@expo|@op-engineering|drizzle-orm|lodash-es|@babel/runtime)/)', - ], +const baseTransform = { + '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest', +}; - // Module name mapping for path aliases - moduleNameMapper: { - '^@components$': '/src/components/index', - '^@components/(.*)$': '/src/components/$1', - '^@database/(.*)$': '/src/database/$1', - '^@hooks$': '/src/hooks/index', - '^@hooks/(.*)$': '/src/hooks/$1', - '^@screens/(.*)$': '/src/screens/$1', - '^@strings/(.*)$': '/strings/$1', - '^@theme/(.*)$': '/src/theme/$1', - '^@utils/(.*)$': '/src/utils/$1', - '^@plugins/(.*)$': '/src/plugins/$1', - '^@services/(.*)$': '/src/services/$1', - '^@navigators/(.*)$': '/src/navigators/$1', - '^@native/(.*)$': '/src/native/$1', - '^@api/(.*)$': '/src/api/$1', - '^@type/(.*)$': '/src/type/$1', - '^@specs/(.*)$': '/specs/$1', - }, +const baseTransformIgnorePatterns = [ + // 'node_modules/(?!(.pnpm/|@op-engineering|drizzle-orm|lodash-es|@babel/runtime))', + 'node_modules/(?!(?:.pnpm/)?((jest-)?react-native|@react-native(-community)?|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg|color|@op-engineering|drizzle-orm|lodash-es))', +]; - // Setup file runs after Jest environment is set up - setupFilesAfterEnv: ['/src/database/queries/__tests__/setup.ts'], +module.exports = { + moduleDirectories: ['node_modules', '__tests-modules__'], + projects: [ + // --- Project 1: Database / pure logic tests (node environment) --- + { + displayName: 'db', + testEnvironment: 'node', + roots: ['/src/database'], + testMatch: ['**/__tests__/**/*.test.ts'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + transform: baseTransform, + transformIgnorePatterns: baseTransformIgnorePatterns, + moduleNameMapper: baseModuleNameMapper, + setupFilesAfterEnv: ['/src/database/queries/__tests__/setup.ts'], + collectCoverageFrom: [ + 'src/database/queries/**/*.ts', + '!src/database/queries/**/__tests__/**', + ], + }, - // Coverage configuration - collectCoverageFrom: [ - 'src/database/queries/**/*.ts', - '!src/database/queries/**/__tests__/**', + // --- Project 2: React Native component / hook / integration tests --- + { + displayName: 'rn', + preset: 'jest-expo', + roots: ['/src'], + testMatch: [ + '**/__tests__/**/*.test.tsx', + // Also pick up non-db .test.ts (hooks, utils, services) + '**/__tests__/**/*.test.ts', + ], + testPathIgnorePatterns: ['/node_modules/', '/src/database/'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + transform: baseTransform, + transformIgnorePatterns: baseTransformIgnorePatterns, + moduleNameMapper: baseModuleNameMapper, + setupFiles: ['/__mocks__/index.js'], + setupFilesAfterEnv: ['/__tests__/jest.setup.ts'], + collectCoverageFrom: [ + 'src/**/*.{ts,tsx}', + '!src/database/queries/**/__tests__/**', + '!src/**/__tests__/**', + ], + }, ], + + // Global settings coverageDirectory: 'coverage', coverageReporters: ['text', 'lcov', 'html'], - - // Test configuration testTimeout: 10000, clearMocks: true, - resetMocks: true, restoreMocks: true, verbose: true, }; diff --git a/package.json b/package.json index c91ea2b763..c7f636cd54 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,8 @@ "test:watch": "jest --watch", "test:coverage": "jest --coverage", "test:queries": "jest --testPathPattern=queries", + "test:db": "jest --selectProjects db", + "test:rn": "jest --selectProjects rn", "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx", "lint:fix": "pnpm run lint -- --fix", "format": "prettier --write \"./src/**/*.{js,jsx,ts,tsx}\" ./scripts", @@ -127,6 +129,7 @@ "@react-native/eslint-plugin": "^0.83.2", "@react-native/metro-config": "^0.81.6", "@react-native/typescript-config": "^0.81.6", + "@testing-library/react-native": "^13.3.3", "@types/better-sqlite3": "^7.6.13", "@types/color": "^4.2.0", "@types/jest": "^29.5.14", @@ -146,11 +149,14 @@ "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-native": "^5.0.0", + "eslint-plugin-testing-library": "^7.16.0", "husky": "^7.0.4", "jest": "^29.7.0", + "jest-expo": "^54.0.17", "lint-staged": "^12.5.0", "prettier": "2.8.8", "react-native-dotenv": "^3.4.11", + "react-test-renderer": "19.1.4", "typescript": "~5.9.3" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54f94659db..00f9b98f03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -249,6 +249,9 @@ importers: '@react-native/typescript-config': specifier: ^0.81.6 version: 0.81.6 + '@testing-library/react-native': + specifier: ^13.3.3 + version: 13.3.3(jest@29.7.0(@types/node@25.2.3))(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react-test-renderer@19.1.4(react@19.1.4))(react@19.1.4) '@types/better-sqlite3': specifier: ^7.6.13 version: 7.6.13 @@ -306,12 +309,18 @@ importers: eslint-plugin-react-native: specifier: ^5.0.0 version: 5.0.0(eslint@8.57.1) + eslint-plugin-testing-library: + specifier: ^7.16.0 + version: 7.16.0(eslint@8.57.1)(typescript@5.9.3) husky: specifier: ^7.0.4 version: 7.0.4 jest: specifier: ^29.7.0 version: 29.7.0(@types/node@25.2.3) + jest-expo: + specifier: ^54.0.17 + version: 54.0.17(@babel/core@7.29.0)(expo@54.0.33(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react@19.1.4))(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react@19.1.4))(jest@29.7.0(@types/node@25.2.3))(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react@19.1.4) lint-staged: specifier: ^12.5.0 version: 12.5.0 @@ -321,6 +330,9 @@ importers: react-native-dotenv: specifier: ^3.4.11 version: 3.4.11(@babel/runtime@7.28.6) + react-test-renderer: + specifier: 19.1.4 + version: 19.1.4(react@19.1.4) typescript: specifier: ~5.9.3 version: 5.9.3 @@ -1510,6 +1522,10 @@ packages: resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/diff-sequences@30.0.1': + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/environment@29.7.0': resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1526,6 +1542,10 @@ packages: resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/globals@29.7.0': resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1543,6 +1563,10 @@ packages: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@30.0.5': + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/source-map@29.6.3': resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1922,6 +1946,9 @@ packages: '@sinclair/typebox@0.27.10': resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + '@sinclair/typebox@0.34.48': + resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} + '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -1931,6 +1958,22 @@ packages: '@tediousjs/connection-string@0.5.0': resolution: {integrity: sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==} + '@testing-library/react-native@13.3.3': + resolution: {integrity: sha512-k6Mjsd9dbZgvY4Bl7P1NIpePQNi+dfYtlJ5voi9KQlynxSyQkfOgJmYGCYmw/aSgH/rUcFvG8u5gd4npzgRDyg==} + engines: {node: '>=18'} + peerDependencies: + jest: '>=29.0.0' + react: '>=18.2.0' + react-native: '>=0.71' + react-test-renderer: '>=18.2.0' + peerDependenciesMeta: + jest: + optional: true + + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1973,6 +2016,9 @@ packages: '@types/jest@29.5.14': resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + '@types/jsdom@20.0.1': + resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2003,6 +2049,9 @@ packages: '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -2175,6 +2224,10 @@ packages: resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} engines: {node: '>=10.0.0'} + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -2183,16 +2236,27 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -2211,6 +2275,10 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} + ansi-escapes@6.2.1: + resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==} + engines: {node: '>=14.16'} + ansi-fragments@0.2.1: resolution: {integrity: sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==} @@ -2314,6 +2382,9 @@ packages: async-limiter@1.0.1: resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -2531,6 +2602,10 @@ packages: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2539,6 +2614,10 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + char-regex@2.0.2: + resolution: {integrity: sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg==} + engines: {node: '>=12.20'} + cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -2658,6 +2737,10 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + command-exists@1.2.9: resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} @@ -2738,9 +2821,23 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} + cssom@0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + + cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + + cssstyle@2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-urls@3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -2785,6 +2882,9 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} @@ -2846,6 +2946,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2884,6 +2988,11 @@ packages: domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} @@ -3141,6 +3250,11 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + eslint-config-prettier@8.10.2: resolution: {integrity: sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==} hasBin: true @@ -3226,6 +3340,12 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + eslint-plugin-testing-library@7.16.0: + resolution: {integrity: sha512-lHZI6/Olb2oZqxd1+s1nOLCtL2PXKrc1ERz6oDbUKS0xZAMFH3Fy6wJo75z3pXTop3BV6+loPi2MSjIYt3vpAg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -3541,6 +3661,10 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + freeport-async@2.0.0: resolution: {integrity: sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==} engines: {node: '>=8'} @@ -3725,6 +3849,10 @@ packages: resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} engines: {node: ^16.14.0 || >=18.0.0} + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -3738,10 +3866,18 @@ packages: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -3941,6 +4077,9 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -4065,6 +4204,10 @@ packages: resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-diff@30.2.0: + resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-docblock@29.7.0: resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4073,10 +4216,30 @@ packages: resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-environment-jsdom@29.7.0: + resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + jest-environment-node@29.7.0: resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-expo@54.0.17: + resolution: {integrity: sha512-LyIhrsP4xvHEEcR1R024u/LBj3uPpAgB+UljgV+YXWkEHjprnr0KpE4tROsMNYCVTM1pPlAnPuoBmn5gnAN9KA==} + hasBin: true + peerDependencies: + expo: '*' + react-native: '*' + react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 + peerDependenciesMeta: + react-server-dom-webpack: + optional: true + jest-get-type@29.6.3: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4093,6 +4256,10 @@ packages: resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-matcher-utils@30.2.0: + resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-message-util@29.7.0: resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4142,6 +4309,15 @@ packages: resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-watch-select-projects@2.0.0: + resolution: {integrity: sha512-j00nW4dXc2NiCW6znXgFLF9g8PJ0zP25cpQ1xRro/HU2GBfZQFZD0SoXnAlaoKkIY4MlfTMkKGbNXFpvCdjl1w==} + + jest-watch-typeahead@2.2.1: + resolution: {integrity: sha512-jYpYmUnTzysmVnwq49TAxlmtOAwp8QIqvZyoofQFn8fiWhEDZj33ZXzg3JA4nGnzWFm1hbWf3ADpteUokvXgFA==} + engines: {node: ^14.17.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + jest: ^27.0.0 || ^28.0.0 || ^29.0.0 + jest-watcher@29.7.0: resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4190,6 +4366,15 @@ packages: jsc-safe-url@0.2.4: resolution: {integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==} + jsdom@20.0.3: + resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -4574,6 +4759,10 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + minimatch@10.2.1: resolution: {integrity: sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==} engines: {node: 20 || >=22} @@ -4695,6 +4884,9 @@ packages: nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + nwsapi@2.2.23: + resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} + ob1@0.83.3: resolution: {integrity: sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==} engines: {node: '>=20.19.4'} @@ -4959,6 +5151,10 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@30.2.0: + resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + proc-log@4.2.0: resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -4985,6 +5181,9 @@ packages: resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} engines: {node: '>=12.0.0'} + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} @@ -5007,6 +5206,9 @@ packages: resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} engines: {node: '>=6'} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -5225,6 +5427,16 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-test-renderer@19.1.0: + resolution: {integrity: sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw==} + peerDependencies: + react: ^19.1.0 + + react-test-renderer@19.1.4: + resolution: {integrity: sha512-dmcR2lSwouaNJKNRd53eeZHuWjF4YYZIq/q6wLiVz50ENrkU/Pds+pS+4XyJ/nwWmjA1ZhHxAp+vfR8A9PEcYQ==} + peerDependencies: + react: ^19.1.4 + react@19.1.4: resolution: {integrity: sha512-DHINL3PAmPUiK1uszfbKiXqfE03eszdt5BpVSuEAHb5nfmNPwnsy7g39h2t8aXFc/Bv99GH81s+j8dobtD+jOw==} engines: {node: '>=0.10.0'} @@ -5237,6 +5449,10 @@ packages: resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -5284,6 +5500,9 @@ packages: resolution: {integrity: sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==} engines: {node: '>= 4.0.0'} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + reselect@4.1.8: resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==} @@ -5381,6 +5600,10 @@ packages: resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==} engines: {node: '>=11.0.0'} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} @@ -5410,6 +5633,9 @@ packages: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -5482,6 +5708,10 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + slice-ansi@2.1.0: resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==} engines: {node: '>=6'} @@ -5512,6 +5742,10 @@ packages: source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + source-map@0.5.7: resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} engines: {node: '>=0.10.0'} @@ -5530,6 +5764,9 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + stack-generator@2.0.10: + resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -5537,6 +5774,12 @@ packages: stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + stacktrace-gps@3.1.2: + resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + + stacktrace-js@2.0.2: + resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + stacktrace-parser@0.1.11: resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} engines: {node: '>=6'} @@ -5572,6 +5815,10 @@ packages: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} + string-length@5.0.1: + resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} + engines: {node: '>=12.20'} + string-natural-compare@3.0.1: resolution: {integrity: sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==} @@ -5625,6 +5872,10 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -5668,6 +5919,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tar-fs@2.1.4: resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} @@ -5739,6 +5993,14 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + ts-api-utils@1.4.3: resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} engines: {node: '>=16'} @@ -5853,6 +6115,10 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -5866,6 +6132,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + urlencode@2.0.0: resolution: {integrity: sha512-K4+koEq4II9FqKKdLyMwfVFiWvTLJsdsIihXCprumjlOwpviO44E4hAhLYBLb6CEVTZh9hXXMTQHIT+Hwv5BPw==} @@ -5912,6 +6181,10 @@ packages: vlq@1.0.1: resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} + w3c-xmlserializer@4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -5925,13 +6198,30 @@ packages: resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} engines: {node: '>=8'} + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + whatwg-url-without-unicode@8.0.0-3: resolution: {integrity: sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==} engines: {node: '>=10'} + whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -6024,6 +6314,10 @@ packages: resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} engines: {node: '>=10.0.0'} + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + xml2js@0.6.0: resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==} engines: {node: '>=4.0.0'} @@ -6036,6 +6330,9 @@ packages: resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} engines: {node: '>=8.0'} + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -7677,6 +7974,8 @@ snapshots: dependencies: '@jest/types': 29.6.3 + '@jest/diff-sequences@30.0.1': {} + '@jest/environment@29.7.0': dependencies: '@jest/fake-timers': 29.7.0 @@ -7704,6 +8003,8 @@ snapshots: jest-mock: 29.7.0 jest-util: 29.7.0 + '@jest/get-type@30.1.0': {} + '@jest/globals@29.7.0': dependencies: '@jest/environment': 29.7.0 @@ -7746,6 +8047,10 @@ snapshots: dependencies: '@sinclair/typebox': 0.27.10 + '@jest/schemas@30.0.5': + dependencies: + '@sinclair/typebox': 0.34.48 + '@jest/source-map@29.6.3': dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -8396,6 +8701,8 @@ snapshots: '@sinclair/typebox@0.27.10': {} + '@sinclair/typebox@0.34.48': {} + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 @@ -8406,6 +8713,20 @@ snapshots: '@tediousjs/connection-string@0.5.0': {} + '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@25.2.3))(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react-test-renderer@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + jest-matcher-utils: 30.2.0 + picocolors: 1.1.1 + pretty-format: 30.2.0 + react: 19.1.4 + react-native: 0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4) + react-test-renderer: 19.1.4(react@19.1.4) + redent: 3.0.0 + optionalDependencies: + jest: 29.7.0(@types/node@25.2.3) + + '@tootallnate/once@2.0.0': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.29.0 @@ -8462,6 +8783,12 @@ snapshots: expect: 29.7.0 pretty-format: 29.7.0 + '@types/jsdom@20.0.1': + dependencies: + '@types/node': 25.2.3 + '@types/tough-cookie': 4.0.5 + parse5: 7.3.0 + '@types/json-schema@7.0.15': {} '@types/lodash-es@4.17.12': @@ -8499,6 +8826,8 @@ snapshots: '@types/stack-utils@2.0.3': {} + '@types/tough-cookie@4.0.5': {} + '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.35': @@ -8744,6 +9073,8 @@ snapshots: '@xmldom/xmldom@0.8.11': {} + abab@2.0.6: {} + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -8753,12 +9084,27 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + acorn-globals@7.0.1: + dependencies: + acorn: 8.15.0 + acorn-walk: 8.3.5 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 + acorn-walk@8.3.5: + dependencies: + acorn: 8.15.0 + acorn@8.15.0: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3(supports-color@9.4.0) + transitivePeerDependencies: + - supports-color + agent-base@7.1.4: {} aggregate-error@3.1.0: @@ -8779,6 +9125,8 @@ snapshots: dependencies: type-fest: 0.21.3 + ansi-escapes@6.2.1: {} + ansi-fragments@0.2.1: dependencies: colorette: 1.4.0 @@ -8897,6 +9245,8 @@ snapshots: async-limiter@1.0.1: {} + asynckit@0.4.0: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -9206,6 +9556,11 @@ snapshots: escape-string-regexp: 1.0.5 supports-color: 5.5.0 + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -9213,6 +9568,8 @@ snapshots: char-regex@1.0.2: {} + char-regex@2.0.2: {} + cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 @@ -9348,6 +9705,10 @@ snapshots: colorette@2.0.20: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + command-exists@1.2.9: {} commander@11.1.0: {} @@ -9439,8 +9800,22 @@ snapshots: css-what@6.2.2: {} + cssom@0.3.8: {} + + cssom@0.5.0: {} + + cssstyle@2.3.0: + dependencies: + cssom: 0.3.8 + csstype@3.2.3: {} + data-urls@3.0.2: + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -9477,6 +9852,8 @@ snapshots: decamelize@1.2.0: {} + decimal.js@10.6.0: {} + decode-uri-component@0.2.2: {} decompress-response@6.0.0: @@ -9522,6 +9899,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delayed-stream@1.0.0: {} + depd@2.0.0: {} destroy@1.2.0: {} @@ -9552,6 +9931,10 @@ snapshots: domelementtype@2.3.0: {} + domexception@4.0.0: + dependencies: + webidl-conversions: 7.0.0 + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 @@ -9782,6 +10165,14 @@ snapshots: escape-string-regexp@4.0.0: {} + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + eslint-config-prettier@8.10.2(eslint@8.57.1): dependencies: eslint: 8.57.1 @@ -9877,6 +10268,15 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 + eslint-plugin-testing-library@7.16.0(eslint@8.57.1)(typescript@5.9.3): + dependencies: + '@typescript-eslint/scope-manager': 8.56.0 + '@typescript-eslint/utils': 8.56.0(eslint@8.57.1)(typescript@5.9.3) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 @@ -10258,6 +10658,14 @@ snapshots: dependencies: is-callable: 1.2.7 + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + freeport-async@2.0.0: {} fresh@0.5.2: {} @@ -10446,6 +10854,10 @@ snapshots: dependencies: lru-cache: 10.4.3 + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + html-escaper@2.0.2: {} htmlparser2@10.1.0: @@ -10470,6 +10882,14 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.4.3(supports-color@9.4.0) + transitivePeerDependencies: + - supports-color + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -10477,6 +10897,13 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3(supports-color@9.4.0) + transitivePeerDependencies: + - supports-color + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -10654,6 +11081,8 @@ snapshots: is-plain-object@5.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -10849,6 +11278,13 @@ snapshots: jest-get-type: 29.6.3 pretty-format: 29.7.0 + jest-diff@30.2.0: + dependencies: + '@jest/diff-sequences': 30.0.1 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.2.0 + jest-docblock@29.7.0: dependencies: detect-newline: 3.1.0 @@ -10861,6 +11297,21 @@ snapshots: jest-util: 29.7.0 pretty-format: 29.7.0 + jest-environment-jsdom@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/jsdom': 20.0.1 + '@types/node': 25.2.3 + jest-mock: 29.7.0 + jest-util: 29.7.0 + jsdom: 20.0.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + jest-environment-node@29.7.0: dependencies: '@jest/environment': 29.7.0 @@ -10870,6 +11321,33 @@ snapshots: jest-mock: 29.7.0 jest-util: 29.7.0 + jest-expo@54.0.17(@babel/core@7.29.0)(expo@54.0.33(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react@19.1.4))(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react@19.1.4))(jest@29.7.0(@types/node@25.2.3))(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react@19.1.4): + dependencies: + '@expo/config': 12.0.13 + '@expo/json-file': 10.0.8 + '@jest/create-cache-key-function': 29.7.0 + '@jest/globals': 29.7.0 + babel-jest: 29.7.0(@babel/core@7.29.0) + expo: 54.0.33(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react@19.1.4))(react-native@0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4))(react@19.1.4) + jest-environment-jsdom: 29.7.0 + jest-snapshot: 29.7.0 + jest-watch-select-projects: 2.0.0 + jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@25.2.3)) + json5: 2.2.3 + lodash: 4.17.23 + react-native: 0.81.6(@babel/core@7.29.0)(@react-native-community/cli@20.1.1(typescript@5.9.3))(@react-native/metro-config@0.81.6(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.4) + react-test-renderer: 19.1.0(react@19.1.4) + server-only: 0.0.1 + stacktrace-js: 2.0.2 + transitivePeerDependencies: + - '@babel/core' + - bufferutil + - canvas + - jest + - react + - supports-color + - utf-8-validate + jest-get-type@29.6.3: {} jest-haste-map@29.7.0: @@ -10900,6 +11378,13 @@ snapshots: jest-get-type: 29.6.3 pretty-format: 29.7.0 + jest-matcher-utils@30.2.0: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.2.0 + pretty-format: 30.2.0 + jest-message-util@29.7.0: dependencies: '@babel/code-frame': 7.29.0 @@ -11039,6 +11524,23 @@ snapshots: leven: 3.1.0 pretty-format: 29.7.0 + jest-watch-select-projects@2.0.0: + dependencies: + ansi-escapes: 4.3.2 + chalk: 3.0.0 + prompts: 2.4.2 + + jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@25.2.3)): + dependencies: + ansi-escapes: 6.2.1 + chalk: 4.1.2 + jest: 29.7.0(@types/node@25.2.3) + jest-regex-util: 29.6.3 + jest-watcher: 29.7.0 + slash: 5.1.0 + string-length: 5.0.1 + strip-ansi: 7.1.2 + jest-watcher@29.7.0: dependencies: '@jest/test-result': 29.7.0 @@ -11098,6 +11600,39 @@ snapshots: jsc-safe-url@0.2.4: {} + jsdom@20.0.3: + dependencies: + abab: 2.0.6 + acorn: 8.15.0 + acorn-globals: 7.0.1 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.6.0 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.5 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.23 + parse5: 7.3.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.19.0 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -11572,6 +12107,8 @@ snapshots: mimic-response@3.1.0: {} + min-indent@1.0.1: {} + minimatch@10.2.1: dependencies: brace-expansion: 5.0.2 @@ -11678,6 +12215,8 @@ snapshots: nullthrows@1.1.1: {} + nwsapi@2.2.23: {} + ob1@0.83.3: dependencies: flow-enums-runtime: 0.0.6 @@ -11960,6 +12499,12 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + pretty-format@30.2.0: + dependencies: + '@jest/schemas': 30.0.5 + ansi-styles: 5.2.0 + react-is: 18.3.1 + proc-log@4.2.0: {} process@0.11.10: {} @@ -11996,6 +12541,10 @@ snapshots: '@types/node': 25.2.3 long: 5.3.2 + psl@1.15.0: + dependencies: + punycode: 2.3.1 + pump@3.0.3: dependencies: end-of-stream: 1.4.5 @@ -12018,6 +12567,8 @@ snapshots: split-on-first: 1.1.0 strict-uri-encode: 2.0.0 + querystringify@2.2.0: {} + queue-microtask@1.2.3: {} queue@6.0.2: @@ -12284,6 +12835,18 @@ snapshots: react-refresh@0.14.2: {} + react-test-renderer@19.1.0(react@19.1.4): + dependencies: + react: 19.1.4 + react-is: 19.2.4 + scheduler: 0.26.0 + + react-test-renderer@19.1.4(react@19.1.4): + dependencies: + react: 19.1.4 + react-is: 19.2.4 + scheduler: 0.26.0 + react@19.1.4: {} readable-stream@3.6.2: @@ -12300,6 +12863,11 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -12359,6 +12927,8 @@ snapshots: rc: 1.2.8 resolve: 1.7.1 + requires-port@1.0.0: {} + reselect@4.1.8: {} resolve-cwd@3.0.0: @@ -12460,6 +13030,10 @@ snapshots: sax@1.4.4: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.26.0: {} semver@6.3.1: {} @@ -12497,6 +13071,8 @@ snapshots: transitivePeerDependencies: - supports-color + server-only@0.0.1: {} + set-blocking@2.0.0: {} set-function-length@1.2.2: @@ -12585,6 +13161,8 @@ snapshots: slash@3.0.0: {} + slash@5.1.0: {} + slice-ansi@2.1.0: dependencies: ansi-styles: 3.2.1 @@ -12622,6 +13200,8 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 + source-map@0.5.6: {} + source-map@0.5.7: {} source-map@0.6.1: {} @@ -12632,12 +13212,27 @@ snapshots: sprintf-js@1.1.3: {} + stack-generator@2.0.10: + dependencies: + stackframe: 1.3.4 + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 stackframe@1.3.4: {} + stacktrace-gps@3.1.2: + dependencies: + source-map: 0.5.6 + stackframe: 1.3.4 + + stacktrace-js@2.0.2: + dependencies: + error-stack-parser: 2.1.4 + stack-generator: 2.0.10 + stacktrace-gps: 3.1.2 + stacktrace-parser@0.1.11: dependencies: type-fest: 0.7.1 @@ -12664,6 +13259,11 @@ snapshots: char-regex: 1.0.2 strip-ansi: 6.0.1 + string-length@5.0.1: + dependencies: + char-regex: 2.0.2 + strip-ansi: 7.1.2 + string-natural-compare@3.0.1: {} string-width@4.2.3: @@ -12742,6 +13342,10 @@ snapshots: strip-final-newline@2.0.0: {} + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} @@ -12781,6 +13385,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} + tar-fs@2.1.4: dependencies: chownr: 1.1.4 @@ -12885,6 +13491,17 @@ snapshots: toidentifier@1.0.1: {} + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tr46@3.0.0: + dependencies: + punycode: 2.3.1 + ts-api-utils@1.4.3(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -12990,6 +13607,8 @@ snapshots: universalify@0.1.2: {} + universalify@0.2.0: {} + unpipe@1.0.0: {} update-browserslist-db@1.2.3(browserslist@4.28.1): @@ -13002,6 +13621,11 @@ snapshots: dependencies: punycode: 2.3.1 + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + urlencode@2.0.0: dependencies: iconv-lite: 0.6.3 @@ -13042,6 +13666,10 @@ snapshots: vlq@1.0.1: {} + w3c-xmlserializer@4.0.0: + dependencies: + xml-name-validator: 4.0.0 + walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -13054,14 +13682,27 @@ snapshots: webidl-conversions@5.0.0: {} + webidl-conversions@7.0.0: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + whatwg-fetch@3.6.20: {} + whatwg-mimetype@3.0.0: {} + whatwg-url-without-unicode@8.0.0-3: dependencies: buffer: 5.7.1 punycode: 2.3.1 webidl-conversions: 5.0.0 + whatwg-url@11.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -13153,6 +13794,8 @@ snapshots: simple-plist: 1.3.1 uuid: 7.0.3 + xml-name-validator@4.0.0: {} + xml2js@0.6.0: dependencies: sax: 1.4.4 @@ -13162,6 +13805,8 @@ snapshots: xmlbuilder@15.1.1: {} + xmlchars@2.2.0: {} + y18n@4.0.3: {} y18n@5.0.8: {} diff --git a/src/components/Context/__mocks__/LibraryContext.tsx b/src/components/Context/__mocks__/LibraryContext.tsx new file mode 100644 index 0000000000..f420e08b0d --- /dev/null +++ b/src/components/Context/__mocks__/LibraryContext.tsx @@ -0,0 +1,35 @@ +import React from 'react'; + +const defaultLibraryContext = { + library: [], + categories: [], + isLoading: false, + setCategories: jest.fn(), + refreshCategories: jest.fn().mockResolvedValue(undefined), + setLibrary: jest.fn(), + novelInLibrary: jest.fn(() => false), + switchNovelToLibrary: jest.fn().mockResolvedValue(undefined), + refetchLibrary: jest.fn(), + setLibrarySearchText: jest.fn(), + settings: { + sortOrder: undefined, + filter: undefined, + showDownloadBadges: false, + showUnreadBadges: false, + showNumberOfNovels: false, + displayMode: undefined, + novelsPerRow: undefined, + incognitoMode: false, + downloadedOnlyMode: false, + }, +}; + +export const LibraryContextProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + return <>{children}; +}; + +export const useLibraryContext = jest.fn(() => defaultLibraryContext); diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx new file mode 100644 index 0000000000..04ebea6565 --- /dev/null +++ b/src/components/__tests__/Button.test.tsx @@ -0,0 +1,65 @@ +import './mocks'; +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react-native'; +import { Text } from 'react-native'; +import Button from '../Button/Button'; + +const mockUseTheme = jest.fn(); + +jest.mock('@hooks/persisted', () => ({ + useTheme: () => mockUseTheme(), +})); + +describe('Button', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockUseTheme.mockReturnValue({ + primary: '#6200ee', + background: '#ffffff', + surface: '#f5f5f5', + onPrimary: '#ffffff', + onSurface: '#000000', + }); + }); + + it('renders with title prop', () => { + render(, + ); + + expect(screen.getByText('Child Text')).toBeTruthy(); + }); + + it('calls onPress when pressed', () => { + const onPress = jest.fn(); + render(, + ); + + expect(screen.getByText('Title')).toBeTruthy(); + expect(screen.queryByText('Children')).toBeNull(); + }); + + it('uses theme from useTheme hook', () => { + const { toJSON } = render(