/* eslint-disable no-console */ import '@testing-library/jest-dom' // jest custom assertions import '@vanilla-extract/css/disableRuntimeStyles' // https://vanilla-extract.style/documentation/test-environments/#disabling-runtime-styles import 'jest-styled-components' // adds style diffs to snapshot tests import 'polyfills' // add polyfills import { setupi18n } from 'uniswap/src/i18n/i18n-setup-interface' import 'utilities/src/logger/mocks' import type { createPopper } from '@popperjs/core' import { useWeb3React } from '@web3-react/core' import failOnConsole from 'jest-fail-on-console' import { disableNetConnect, restore as restoreNetConnect } from 'nock' import React from 'react' import { Readable } from 'stream' import { toBeVisible } from 'test-utils/matchers' import { mocked } from 'test-utils/mocked' import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { mockLocalizationContext } from 'uniswap/src/test/mocks/locale' import { TextDecoder, TextEncoder } from 'util' setupi18n() // Sets origin to the production origin, because some tests depend on this. // This prevents each test file from needing to set this manually. globalThis.origin = 'https://app.uniswap.org' // Polyfill browser APIs (jest is a node.js environment): { window.open = jest.fn() window.getComputedStyle = jest.fn() if (typeof globalThis.TextEncoder === 'undefined') { globalThis.ReadableStream = Readable as unknown as typeof globalThis.ReadableStream globalThis.TextEncoder = TextEncoder globalThis.TextDecoder = TextDecoder as typeof globalThis.TextDecoder } globalThis.matchMedia = globalThis.matchMedia || ((query) => { const reducedMotion = query.match(/prefers-reduced-motion: ([a-zA-Z0-9-]+)/) return { // Needed for reanimated to disable reduced motion warning in tests matches: reducedMotion ? reducedMotion[1] === 'no-preference' : false, addListener: jest.fn(), addEventListener: jest.fn(), removeEventListener: jest.fn(), } }) globalThis.performance.measure = jest.fn() globalThis.performance.mark = jest.fn() globalThis.React = React } jest.mock('react-native-svg', () => require('@tamagui/react-native-svg')) jest.mock('@popperjs/core', () => { const core = jest.requireActual('@popperjs/core') return { ...core, createPopper: (...args: Parameters) => { const [referenceElement, popperElement, options = {}] = args // Prevent popper from making state updates asynchronously. // This is necessary to avoid warnings during tests, as popper will asynchronously update state outside of test setup. options?.modifiers?.push({ name: 'synchronousUpdate', enabled: true, phase: 'beforeMain', effect: (state) => { state.instance.update = () => { state.instance.forceUpdate() return Promise.resolve(state.instance.state) } }, }) return core.createPopper(referenceElement, popperElement, options) }, } }) jest.mock('uniswap/src/features/language/LocalizationContext', () => mockLocalizationContext({})) jest.mock('@web3-react/core', () => { const web3React = jest.requireActual('@web3-react/core') const { Empty } = jest.requireActual('@web3-react/empty') return { ...web3React, initializeConnector: () => web3React.initializeConnector( (actions: Parameters[0]) => new Empty(actions), ), useWeb3React: jest.fn(), } }) jest.mock('state/routing/slice', () => { const routingSlice = jest.requireActual('state/routing/slice') return { ...routingSlice, // Prevents unit tests from logging errors from failed getQuote queries useGetQuoteQuery: () => ({ isError: false, data: undefined, error: undefined, currentData: undefined, }), } }) jest.mock('state/routing/quickRouteSlice', () => { const quickRouteSlice = jest.requireActual('state/routing/quickRouteSlice') return { ...quickRouteSlice, // Prevents unit tests from logging errors from failed getQuote queries useGetQuickRouteQuery: () => ({ isError: false, data: undefined, error: undefined, currentData: undefined, }), } }) /** * Fail tests if anything is logged to the console. This keeps the console clean and ensures test output stays readable. * If something should log to the console, it should be stubbed and asserted: * @example * beforeEach(() => jest.spyOn(console, 'error').mockReturnsValue()) * it('should log an error', () => { * example() * expect(console.error).toHaveBeenCalledWith(expect.any(Error)) * }) */ failOnConsole({ shouldFailOnAssert: true, shouldFailOnDebug: true, shouldFailOnError: true, shouldFailOnInfo: true, shouldFailOnLog: true, shouldFailOnWarn: true, allowMessage: (message, type) => { if (type === 'error') { // TODO(TAM-47): remove this allowed warning once Tamagui is upgraded >= 1.100 if (message.startsWith('[moti]: Invalid transform value.')) { return true } } return false }, }) jest.mock('uniswap/src/features/gating/hooks') const originalConsoleDebug = console.debug // Mocks are configured to reset between tests (by CRA), so they must be set in a beforeEach. beforeEach(() => { // Mock window.getComputedStyle, because it is otherwise too computationally expensive to unit test. // Not mocking this results in multi-second tests when using popper.js. mocked(window.getComputedStyle).mockImplementation(() => new CSSStyleDeclaration()) // Mock useWeb3React to return a chainId of 1 by default. mocked(useWeb3React).mockReturnValue({ chainId: 1 } as ReturnType) // Disable network connections by default. disableNetConnect() // Mock feature flags mocked(useFeatureFlag).mockReturnValue(false) // Prevent amplitude debugs from triggering failOnConsole console.debug = jest.fn((...args) => { if (typeof args[0] === 'string' && args[0].includes('[amplitude(Identify)')) { return } originalConsoleDebug(...args) }) }) afterEach(() => { // Without this, nock causes a memory leak and the tests will fail on CI. // https://github.com/nock/nock/issues/1817 restoreNetConnect() }) expect.extend({ toBeVisible, })