setup.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. import '@testing-library/jest-dom'
  2. // 扩展全局类型以支持 Taro 配置测试
  3. declare global {
  4. // eslint-disable-next-line no-var
  5. var defineAppConfig: (config: any) => any
  6. }
  7. /* eslint-disable react/display-name */
  8. // 设置环境变量
  9. process.env.TARO_ENV = 'h5'
  10. process.env.TARO_PLATFORM = 'web'
  11. process.env.SUPPORT_TARO_POLYFILL = 'disabled'
  12. // 定义 defineAppConfig 全局函数用于测试 Taro 配置文件
  13. global.defineAppConfig = (config: any) => config
  14. // Mock Taro 组件
  15. // eslint-disable-next-line react/display-name
  16. jest.mock('@tarojs/components', () => {
  17. const React = require('react')
  18. const MockView = React.forwardRef((props: any, ref: any) => {
  19. const { children, ...restProps } = props
  20. return React.createElement('div', { ...restProps, ref }, children)
  21. })
  22. MockView.displayName = 'MockView'
  23. const MockScrollView = React.forwardRef((props: any, ref: any) => {
  24. const {
  25. children,
  26. onScroll,
  27. onTouchStart,
  28. onScrollEnd,
  29. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  30. scrollY,
  31. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  32. showScrollbar,
  33. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  34. scrollTop,
  35. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  36. scrollWithAnimation,
  37. ...restProps
  38. } = props
  39. return React.createElement('div', {
  40. ...restProps,
  41. ref,
  42. onScroll: (e: any) => {
  43. if (onScroll) onScroll(e)
  44. },
  45. onTouchStart: (e: any) => {
  46. if (onTouchStart) onTouchStart(e)
  47. },
  48. onTouchEnd: () => {
  49. if (onScrollEnd) onScrollEnd()
  50. },
  51. style: {
  52. overflow: 'auto',
  53. height: '200px',
  54. ...restProps.style
  55. }
  56. }, children)
  57. })
  58. MockScrollView.displayName = 'MockScrollView'
  59. return {
  60. View: MockView,
  61. ScrollView: MockScrollView,
  62. Text: (() => {
  63. const MockText = React.forwardRef((props: any, ref: any) => {
  64. const { children, ...restProps } = props
  65. return React.createElement('span', { ...restProps, ref }, children)
  66. })
  67. MockText.displayName = 'MockText'
  68. return MockText
  69. })(),
  70. Button: (() => {
  71. const MockButton = React.forwardRef((props: any, ref: any) => {
  72. const { children, ...restProps } = props
  73. return React.createElement('button', { ...restProps, ref }, children)
  74. })
  75. MockButton.displayName = 'MockButton'
  76. return MockButton
  77. })(),
  78. Input: (() => {
  79. const MockInput = React.forwardRef((props: any, ref: any) => {
  80. const { ...restProps } = props
  81. return React.createElement('input', { ...restProps, ref })
  82. })
  83. MockInput.displayName = 'MockInput'
  84. return MockInput
  85. })(),
  86. Textarea: (() => {
  87. const MockTextarea = React.forwardRef((props: any, ref: any) => {
  88. const { children, ...restProps } = props
  89. return React.createElement('textarea', { ...restProps, ref }, children)
  90. })
  91. MockTextarea.displayName = 'MockTextarea'
  92. return MockTextarea
  93. })(),
  94. Image: (() => {
  95. const MockImage = React.forwardRef((props: any, ref: any) => {
  96. const { ...restProps } = props
  97. return React.createElement('img', { ...restProps, ref })
  98. })
  99. MockImage.displayName = 'MockImage'
  100. return MockImage
  101. })(),
  102. Form: (() => {
  103. const MockForm = React.forwardRef((props: any, ref: any) => {
  104. const { children, ...restProps } = props
  105. return React.createElement('form', { ...restProps, ref }, children)
  106. })
  107. MockForm.displayName = 'MockForm'
  108. return MockForm
  109. })(),
  110. Label: (() => {
  111. const MockLabel = React.forwardRef((props: any, ref: any) => {
  112. const { children, ...restProps } = props
  113. return React.createElement('label', { ...restProps, ref }, children)
  114. })
  115. MockLabel.displayName = 'MockLabel'
  116. return MockLabel
  117. })(),
  118. Picker: (() => {
  119. const MockPicker = React.forwardRef((props: any, ref: any) => {
  120. const { children, ...restProps } = props
  121. return React.createElement('div', { ...restProps, ref }, children)
  122. })
  123. MockPicker.displayName = 'MockPicker'
  124. return MockPicker
  125. })(),
  126. Switch: (() => {
  127. const MockSwitch = React.forwardRef((props: any, ref: any) => {
  128. const { ...restProps } = props
  129. return React.createElement('input', { type: 'checkbox', ...restProps, ref })
  130. })
  131. MockSwitch.displayName = 'MockSwitch'
  132. return MockSwitch
  133. })(),
  134. Slider: (() => {
  135. const MockSlider = React.forwardRef((props: any, ref: any) => {
  136. const { ...restProps } = props
  137. return React.createElement('input', { type: 'range', ...restProps, ref })
  138. })
  139. MockSlider.displayName = 'MockSlider'
  140. return MockSlider
  141. })(),
  142. Radio: React.forwardRef((props: any, ref: any) => {
  143. const { children, ...restProps } = props
  144. return React.createElement('input', { type: 'radio', ...restProps, ref }, children)
  145. }),
  146. RadioGroup: React.forwardRef((props: any, ref: any) => {
  147. const { children, ...restProps } = props
  148. return React.createElement('div', { ...restProps, ref }, children)
  149. }),
  150. Checkbox: React.forwardRef((props: any, ref: any) => {
  151. const { children, ...restProps } = props
  152. return React.createElement('input', { type: 'checkbox', ...restProps, ref }, children)
  153. }),
  154. CheckboxGroup: React.forwardRef((props: any, ref: any) => {
  155. const { children, ...restProps } = props
  156. return React.createElement('div', { ...restProps, ref }, children)
  157. }),
  158. Progress: React.forwardRef((props: any, ref: any) => {
  159. const { ...restProps } = props
  160. return React.createElement('progress', { ...restProps, ref })
  161. }),
  162. RichText: React.forwardRef((props: any, ref: any) => {
  163. const { children, ...restProps } = props
  164. return React.createElement('div', { ...restProps, ref }, children)
  165. }),
  166. MovableArea: React.forwardRef((props: any, ref: any) => {
  167. const { children, ...restProps } = props
  168. return React.createElement('div', { ...restProps, ref }, children)
  169. }),
  170. MovableView: React.forwardRef((props: any, ref: any) => {
  171. const { children, ...restProps } = props
  172. return React.createElement('div', { ...restProps, ref }, children)
  173. }),
  174. Swiper: React.forwardRef((props: any, ref: any) => {
  175. const { children, ...restProps } = props
  176. return React.createElement('div', { ...restProps, ref }, children)
  177. }),
  178. SwiperItem: React.forwardRef((props: any, ref: any) => {
  179. const { children, ...restProps } = props
  180. return React.createElement('div', { ...restProps, ref }, children)
  181. }),
  182. Navigator: React.forwardRef((props: any, ref: any) => {
  183. const { children, ...restProps } = props
  184. return React.createElement('a', { ...restProps, ref }, children)
  185. }),
  186. Audio: React.forwardRef((props: any, ref: any) => {
  187. const { ...restProps } = props
  188. return React.createElement('audio', { ...restProps, ref })
  189. }),
  190. Video: React.forwardRef((props: any, ref: any) => {
  191. const { children, ...restProps } = props
  192. return React.createElement('video', { ...restProps, ref }, children)
  193. }),
  194. Camera: React.forwardRef((props: any, ref: any) => {
  195. const { children, ...restProps } = props
  196. return React.createElement('div', { ...restProps, ref }, children)
  197. }),
  198. LivePlayer: React.forwardRef((props: any, ref: any) => {
  199. const { children, ...restProps } = props
  200. return React.createElement('div', { ...restProps, ref }, children)
  201. }),
  202. LivePusher: React.forwardRef((props: any, ref: any) => {
  203. const { children, ...restProps } = props
  204. return React.createElement('div', { ...restProps, ref }, children)
  205. }),
  206. Map: React.forwardRef((props: any, ref: any) => {
  207. const { children, ...restProps } = props
  208. return React.createElement('div', { ...restProps, ref }, children)
  209. }),
  210. Canvas: React.forwardRef((props: any, ref: any) => {
  211. const { children, ...restProps } = props
  212. return React.createElement('canvas', { ...restProps, ref }, children)
  213. }),
  214. OpenData: React.forwardRef((props: any, ref: any) => {
  215. const { children, ...restProps } = props
  216. return React.createElement('div', { ...restProps, ref }, children)
  217. }),
  218. WebView: React.forwardRef((props: any, ref: any) => {
  219. const { children, ...restProps } = props
  220. return React.createElement('iframe', { ...restProps, ref }, children)
  221. }),
  222. Ad: React.forwardRef((props: any, ref: any) => {
  223. const { children, ...restProps } = props
  224. return React.createElement('div', { ...restProps, ref }, children)
  225. }),
  226. OfficialAccount: React.forwardRef((props: any, ref: any) => {
  227. const { children, ...restProps } = props
  228. return React.createElement('div', { ...restProps, ref }, children)
  229. }),
  230. CoverView: React.forwardRef((props: any, ref: any) => {
  231. const { children, ...restProps } = props
  232. return React.createElement('div', { ...restProps, ref }, children)
  233. }),
  234. CoverImage: React.forwardRef((props: any, ref: any) => {
  235. const { ...restProps } = props
  236. return React.createElement('img', { ...restProps, ref })
  237. }),
  238. FunctionalPageNavigator: React.forwardRef((props: any, ref: any) => {
  239. const { children, ...restProps } = props
  240. return React.createElement('div', { ...restProps, ref }, children)
  241. }),
  242. AdContent: React.forwardRef((props: any, ref: any) => {
  243. const { children, ...restProps } = props
  244. return React.createElement('div', { ...restProps, ref }, children)
  245. }),
  246. MatchMedia: React.forwardRef((props: any, ref: any) => {
  247. const { children, ...restProps } = props
  248. return React.createElement('div', { ...restProps, ref }, children)
  249. }),
  250. PageContainer: React.forwardRef((props: any, ref: any) => {
  251. const { children, ...restProps } = props
  252. return React.createElement('div', { ...restProps, ref }, children)
  253. }),
  254. ShareElement: React.forwardRef((props: any, ref: any) => {
  255. const { children, ...restProps } = props
  256. return React.createElement('div', { ...restProps, ref }, children)
  257. }),
  258. KeyboardAccessory: React.forwardRef((props: any, ref: any) => {
  259. const { children, ...restProps } = props
  260. return React.createElement('div', { ...restProps, ref }, children)
  261. }),
  262. RootPortal: React.forwardRef((props: any, ref: any) => {
  263. const { children, ...restProps } = props
  264. return React.createElement('div', { ...restProps, ref }, children)
  265. }),
  266. PageMeta: React.forwardRef((props: any, ref: any) => {
  267. const { children, ...restProps } = props
  268. return React.createElement('div', { ...restProps, ref }, children)
  269. }),
  270. NavigationBar: React.forwardRef((props: any, ref: any) => {
  271. const { children, ...restProps } = props
  272. return React.createElement('div', { ...restProps, ref }, children)
  273. }),
  274. Block: React.forwardRef((props: any, ref: any) => {
  275. const { children, ...restProps } = props
  276. return React.createElement('div', { ...restProps, ref }, children)
  277. }),
  278. Import: React.forwardRef((props: any, ref: any) => {
  279. const { children, ...restProps } = props
  280. return React.createElement('div', { ...restProps, ref }, children)
  281. }),
  282. Include: React.forwardRef((props: any, ref: any) => {
  283. const { children, ...restProps } = props
  284. return React.createElement('div', { ...restProps, ref }, children)
  285. }),
  286. Template: React.forwardRef((props: any, ref: any) => {
  287. const { children, ...restProps } = props
  288. return React.createElement('div', { ...restProps, ref }, children)
  289. }),
  290. Slot: React.forwardRef((props: any, ref: any) => {
  291. const { children, ...restProps } = props
  292. return React.createElement('div', { ...restProps, ref }, children)
  293. }),
  294. NativeSlot: React.forwardRef((props: any, ref: any) => {
  295. const { children, ...restProps } = props
  296. return React.createElement('div', { ...restProps, ref }, children)
  297. }),
  298. CustomWrapper: React.forwardRef((props: any, ref: any) => {
  299. const { children, ...restProps } = props
  300. return React.createElement('div', { ...restProps, ref }, children)
  301. }),
  302. Editor: React.forwardRef((props: any, ref: any) => {
  303. const { children, ...restProps } = props
  304. return React.createElement('div', { ...restProps, ref }, children)
  305. }),
  306. VoipRoom: React.forwardRef((props: any, ref: any) => {
  307. const { children, ...restProps } = props
  308. return React.createElement('div', { ...restProps, ref }, children)
  309. }),
  310. AdCustom: React.forwardRef((props: any, ref: any) => {
  311. const { children, ...restProps } = props
  312. return React.createElement('div', { ...restProps, ref }, children)
  313. })
  314. }
  315. })
  316. // 模拟 MutationObserver
  317. // @ts-ignore
  318. global.MutationObserver = class {
  319. disconnect() {}
  320. observe(_element: any, _initObject: any) {}
  321. takeRecords() { return [] }
  322. }
  323. // 模拟 IntersectionObserver
  324. // @ts-ignore
  325. global.IntersectionObserver = class {
  326. constructor(fn: (args: any[]) => void) {
  327. setTimeout(() => {
  328. fn([{ isIntersecting: true }])
  329. }, 1000)
  330. }
  331. observe() {}
  332. unobserve() {}
  333. disconnect() {}
  334. takeRecords() { return [] }
  335. root: null = null
  336. rootMargin: string = ''
  337. thresholds: number[] = []
  338. }
  339. // 模拟 ResizeObserver
  340. // @ts-ignore
  341. global.ResizeObserver = class {
  342. observe() {}
  343. unobserve() {}
  344. disconnect() {}
  345. }
  346. // 模拟 matchMedia
  347. Object.defineProperty(window, 'matchMedia', {
  348. writable: true,
  349. value: jest.fn().mockImplementation(query => ({
  350. matches: false,
  351. media: query,
  352. onchange: null,
  353. addListener: jest.fn(), // deprecated
  354. removeListener: jest.fn(), // deprecated
  355. addEventListener: jest.fn(),
  356. removeEventListener: jest.fn(),
  357. dispatchEvent: jest.fn(),
  358. })),
  359. })
  360. // 模拟 getComputedStyle
  361. Object.defineProperty(window, 'getComputedStyle', {
  362. value: () => ({
  363. getPropertyValue: (prop: string) => {
  364. return {
  365. 'font-size': '16px',
  366. 'font-family': 'Arial',
  367. color: 'rgb(0, 0, 0)',
  368. 'background-color': 'rgb(255, 255, 255)',
  369. width: '100px',
  370. height: '100px',
  371. top: '0px',
  372. left: '0px',
  373. right: '0px',
  374. bottom: '0px',
  375. x: '0px',
  376. y: '0px'
  377. }[prop] || ''
  378. }
  379. })
  380. })
  381. // 模拟 Element.prototype.getBoundingClientRect
  382. Element.prototype.getBoundingClientRect = jest.fn(() => ({
  383. width: 100,
  384. height: 100,
  385. top: 0,
  386. left: 0,
  387. bottom: 100,
  388. right: 100,
  389. x: 0,
  390. y: 0,
  391. toJSON: () => ({
  392. width: 100,
  393. height: 100,
  394. top: 0,
  395. left: 0,
  396. bottom: 100,
  397. right: 100,
  398. x: 0,
  399. y: 0
  400. })
  401. }))
  402. // 静默 console.error 在测试中
  403. const originalConsoleError = console.error
  404. console.error = (...args: any[]) => {
  405. // 检查是否在测试环境中(通过 Jest 环境变量判断)
  406. const isTestEnv = process.env.JEST_WORKER_ID !== undefined ||
  407. typeof jest !== 'undefined'
  408. // 在测试环境中静默错误输出,除非是重要错误
  409. if (isTestEnv && !args[0]?.includes?.('重要错误')) {
  410. return
  411. }
  412. originalConsoleError(...args)
  413. }
  414. // Mock 常用 UI 组件
  415. jest.mock('@/components/ui/dialog', () => {
  416. const React = require('react')
  417. return {
  418. Dialog: ({ open, children }: any) => open ? React.createElement('div', { 'data-testid': 'dialog' }, children) : null,
  419. DialogContent: ({ children, className }: any) => React.createElement('div', { className }, children),
  420. DialogHeader: ({ children, className }: any) => React.createElement('div', { className }, children),
  421. DialogTitle: ({ children, className }: any) => React.createElement('div', { className }, children),
  422. DialogFooter: ({ children, className }: any) => React.createElement('div', { className }, children)
  423. }
  424. })