setup.ts 16 KB

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