VideoManagement.test.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. import React from 'react'
  2. import { render, screen, fireEvent, waitFor } from '@testing-library/react'
  3. import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
  4. import VideoManagement from '../src/pages/VideoManagement/VideoManagement'
  5. // Mock Taro
  6. jest.mock('@tarojs/taro', () => ({
  7. showToast: jest.fn(),
  8. showLoading: jest.fn(),
  9. hideLoading: jest.fn(),
  10. showShareMenu: jest.fn(),
  11. showModal: jest.fn(({ success }) => success?.({ confirm: true })),
  12. openDocument: jest.fn()
  13. }))
  14. // Mock API client
  15. jest.mock('../src/api/enterpriseOrderClient', () => ({
  16. enterpriseOrderClient: {
  17. 'video-statistics': {
  18. $get: jest.fn()
  19. },
  20. 'company-videos': {
  21. $get: jest.fn()
  22. },
  23. 'batch-download': {
  24. $post: jest.fn()
  25. }
  26. }
  27. }))
  28. // Mock layouts
  29. jest.mock('@d8d/yongren-shared-ui/components/YongrenTabBarLayout', () => ({
  30. YongrenTabBarLayout: ({ children, activeKey }: any) => (
  31. <div data-testid="tab-bar-layout" data-active-key={activeKey}>
  32. {children}
  33. </div>
  34. )
  35. }))
  36. jest.mock('@d8d/mini-shared-ui-components/components/navbar', () => ({
  37. Navbar: ({ title }: any) => <div data-testid="navbar">{title}</div>
  38. }))
  39. const { enterpriseOrderClient } = require('../../api/enterpriseOrderClient')
  40. const Taro = require('@tarojs/taro')
  41. const createTestQueryClient = () => new QueryClient({
  42. defaultOptions: {
  43. queries: { retry: false },
  44. mutations: { retry: false }
  45. },
  46. logger: { error: jest.fn(), warn: jest.fn(), log: jest.fn() }
  47. })
  48. const renderWithQueryClient = (component: React.ReactElement) => {
  49. const queryClient = createTestQueryClient()
  50. return render(
  51. <QueryClientProvider client={queryClient}>
  52. {component}
  53. </QueryClientProvider>
  54. )
  55. }
  56. describe('VideoManagement', () => {
  57. beforeEach(() => {
  58. jest.clearAllMocks()
  59. })
  60. describe('视频列表加载', () => {
  61. it('应该显示加载状态', async () => {
  62. enterpriseOrderClient['video-statistics'].$get.mockResolvedValue({
  63. ok: true,
  64. json: async () => ({ total: 10, stats: [] })
  65. })
  66. enterpriseOrderClient['company-videos'].$get.mockImplementation(
  67. () => new Promise(() => {}) // 永不resolve,测试加载状态
  68. )
  69. renderWithQueryClient(<VideoManagement />)
  70. expect(screen.getByText('加载中...')).toBeInTheDocument()
  71. })
  72. it('应该成功加载并显示视频列表', async () => {
  73. const mockStatistics = {
  74. total: 12,
  75. stats: [
  76. { assetType: 'salary_video', count: 4, percentage: 33 },
  77. { assetType: 'tax_video', count: 3, percentage: 25 },
  78. { assetType: 'checkin_video', count: 5, percentage: 42 }
  79. ]
  80. }
  81. const mockVideos = {
  82. data: [
  83. {
  84. id: '1',
  85. talentName: '张明',
  86. assetType: 'salary_video',
  87. status: 'verified',
  88. fileSize: 12345678,
  89. duration: 45,
  90. description: '工资发放确认视频',
  91. createdAt: '2023-11-25T10:00:00Z',
  92. fileUrl: 'https://example.com/video1.mp4'
  93. },
  94. {
  95. id: '2',
  96. talentName: '李小红',
  97. assetType: 'tax_video',
  98. status: 'pending',
  99. fileSize: 10800000,
  100. duration: 38,
  101. description: '个税申报确认视频',
  102. createdAt: '2023-11-24T10:00:00Z',
  103. fileUrl: 'https://example.com/video2.mp4'
  104. }
  105. ]
  106. }
  107. enterpriseOrderClient['video-statistics'].$get.mockResolvedValue({
  108. ok: true,
  109. json: async () => mockStatistics
  110. })
  111. enterpriseOrderClient['company-videos'].$get.mockResolvedValue({
  112. ok: true,
  113. json: async () => mockVideos
  114. })
  115. renderWithQueryClient(<VideoManagement />)
  116. await waitFor(() => {
  117. expect(screen.getByText('视频列表 (2)')).toBeInTheDocument()
  118. expect(screen.getByText('张明 - 工资视频')).toBeInTheDocument()
  119. expect(screen.getByText('李小红 - 个税视频')).toBeInTheDocument()
  120. expect(screen.getByText('已验证')).toBeInTheDocument()
  121. expect(screen.getByText('待审核')).toBeInTheDocument()
  122. })
  123. })
  124. it('应该显示空状态', async () => {
  125. enterpriseOrderClient['video-statistics'].$get.mockResolvedValue({
  126. ok: true,
  127. json: async () => ({ total: 0, stats: [] })
  128. })
  129. enterpriseOrderClient['company-videos'].$get.mockResolvedValue({
  130. ok: true,
  131. json: async () => ({ data: [] })
  132. })
  133. renderWithQueryClient(<VideoManagement />)
  134. await waitFor(() => {
  135. expect(screen.getByText('暂无视频数据')).toBeInTheDocument()
  136. })
  137. })
  138. })
  139. describe('视频分类筛选', () => {
  140. it('应该显示所有分类标签及计数', async () => {
  141. const mockStatistics = {
  142. total: 12,
  143. stats: [
  144. { assetType: 'salary_video', count: 4 },
  145. { assetType: 'tax_video', count: 3 },
  146. { assetType: 'checkin_video', count: 5 }
  147. ]
  148. }
  149. enterpriseOrderClient['video-statistics'].$get.mockResolvedValue({
  150. ok: true,
  151. json: async () => mockStatistics
  152. })
  153. enterpriseOrderClient['company-videos'].$get.mockResolvedValue({
  154. ok: true,
  155. json: async () => ({ data: [] })
  156. })
  157. renderWithQueryClient(<VideoManagement />)
  158. await waitFor(() => {
  159. expect(screen.getByText('全部视频 (12)')).toBeInTheDocument()
  160. expect(screen.getByText('工资视频 (4)')).toBeInTheDocument()
  161. expect(screen.getByText('个税视频 (3)')).toBeInTheDocument()
  162. expect(screen.getByText('打卡视频 (5)')).toBeInTheDocument()
  163. })
  164. })
  165. it('应该能够切换视频分类', async () => {
  166. enterpriseOrderClient['video-statistics'].$get.mockResolvedValue({
  167. ok: true,
  168. json: async () => ({ total: 10, stats: [] })
  169. })
  170. enterpriseOrderClient['company-videos'].$get.mockResolvedValue({
  171. ok: true,
  172. json: async () => ({ data: [] })
  173. })
  174. renderWithQueryClient(<VideoManagement />)
  175. await waitFor(() => {
  176. expect(screen.getByText('工资视频')).toBeInTheDocument()
  177. })
  178. fireEvent.click(screen.getByText('工资视频'))
  179. await waitFor(() => {
  180. expect(enterpriseOrderClient['company-videos'].$get).toHaveBeenCalledWith(
  181. expect.objectContaining({
  182. query: { assetType: 'salary_video' }
  183. })
  184. )
  185. })
  186. })
  187. })
  188. describe('视频操作', () => {
  189. beforeEach(async () => {
  190. const mockVideos = {
  191. data: [
  192. {
  193. id: '1',
  194. talentName: '张明',
  195. assetType: 'salary_video',
  196. status: 'verified',
  197. fileSize: 12345678,
  198. duration: 45,
  199. description: '工资发放确认视频',
  200. createdAt: '2023-11-25T10:00:00Z',
  201. fileUrl: 'https://example.com/video1.mp4'
  202. }
  203. ]
  204. }
  205. enterpriseOrderClient['video-statistics'].$get.mockResolvedValue({
  206. ok: true,
  207. json: async () => ({ total: 1, stats: [] })
  208. })
  209. enterpriseOrderClient['company-videos'].$get.mockResolvedValue({
  210. ok: true,
  211. json: async () => mockVideos
  212. })
  213. })
  214. it('应该能够选择视频进行批量下载', async () => {
  215. renderWithQueryClient(<VideoManagement />)
  216. await waitFor(() => {
  217. expect(screen.getByText('张明 - 工资视频')).toBeInTheDocument()
  218. })
  219. // 点击选择按钮
  220. const selectButtons = screen.getAllByText('选择')
  221. fireEvent.click(selectButtons[0])
  222. await waitFor(() => {
  223. expect(screen.getByText('已选择')).toBeInTheDocument()
  224. expect(screen.getByText('批量下载 (1)')).toBeInTheDocument()
  225. })
  226. })
  227. it('应该能够执行批量下载', async () => {
  228. enterpriseOrderClient['batch-download'].$post.mockResolvedValue({
  229. ok: true,
  230. json: async () => ({ success: true })
  231. })
  232. renderWithQueryClient(<VideoManagement />)
  233. await waitFor(() => {
  234. expect(screen.getByText('张明 - 工资视频')).toBeInTheDocument()
  235. })
  236. // 先选择视频
  237. const selectButtons = screen.getAllByText('选择')
  238. fireEvent.click(selectButtons[0])
  239. // 再点击批量下载
  240. fireEvent.click(screen.getByText(/批量下载/))
  241. await waitFor(() => {
  242. expect(enterpriseOrderClient['batch-download'].$post).toHaveBeenCalled()
  243. expect(Taro.showToast).toHaveBeenCalledWith(
  244. expect.objectContaining({ title: '下载任务已创建' })
  245. )
  246. })
  247. })
  248. it('应该在批量下载前提示选择视频', async () => {
  249. renderWithQueryClient(<VideoManagement />)
  250. await waitFor(() => {
  251. expect(screen.getByText('张明 - 工资视频')).toBeInTheDocument()
  252. })
  253. // 直接点击批量下载,不选择视频
  254. fireEvent.click(screen.getByText(/批量下载/))
  255. expect(Taro.showToast).toHaveBeenCalledWith(
  256. expect.objectContaining({ title: '请先选择要下载的视频' })
  257. )
  258. })
  259. it('应该能够播放视频', async () => {
  260. renderWithQueryClient(<VideoManagement />)
  261. await waitFor(() => {
  262. expect(screen.getByText('张明 - 工资视频')).toBeInTheDocument()
  263. })
  264. const playButton = screen.getByText('播放')
  265. fireEvent.click(playButton)
  266. expect(Taro.showModal).toHaveBeenCalledWith(
  267. expect.objectContaining({
  268. title: '播放视频',
  269. content: '即将播放:张明 - 工资视频'
  270. })
  271. )
  272. })
  273. it('应该能够下载单个视频', async () => {
  274. enterpriseOrderClient['batch-download'].$post.mockResolvedValue({
  275. ok: true,
  276. json: async () => ({ success: true })
  277. })
  278. renderWithQueryClient(<VideoManagement />)
  279. await waitFor(() => {
  280. expect(screen.getByText('张明 - 工资视频')).toBeInTheDocument()
  281. })
  282. const downloadButton = screen.getByText('下载')
  283. fireEvent.click(downloadButton)
  284. await waitFor(() => {
  285. expect(enterpriseOrderClient['batch-download'].$post).toHaveBeenCalled()
  286. expect(Taro.showToast).toHaveBeenCalledWith(
  287. expect.objectContaining({ title: '下载成功' })
  288. )
  289. })
  290. })
  291. it('应该能够分享视频', async () => {
  292. renderWithQueryClient(<VideoManagement />)
  293. await waitFor(() => {
  294. expect(screen.getByText('张明 - 工资视频')).toBeInTheDocument()
  295. })
  296. const shareButton = screen.getByText('分享')
  297. fireEvent.click(shareButton)
  298. expect(Taro.showShareMenu).toHaveBeenCalledWith(
  299. expect.objectContaining({ withShareTicket: true })
  300. )
  301. })
  302. })
  303. describe('组件集成', () => {
  304. it('应该正确集成Navbar组件', async () => {
  305. enterpriseOrderClient['video-statistics'].$get.mockResolvedValue({
  306. ok: true,
  307. json: async () => ({ total: 0, stats: [] })
  308. })
  309. enterpriseOrderClient['company-videos'].$get.mockResolvedValue({
  310. ok: true,
  311. json: async () => ({ data: [] })
  312. })
  313. renderWithQueryClient(<VideoManagement />)
  314. await waitFor(() => {
  315. const navbar = screen.getByTestId('navbar')
  316. expect(navbar).toBeInTheDocument()
  317. expect(navbar).toHaveTextContent('视频管理')
  318. })
  319. })
  320. it('应该正确集成TabBarLayout并激活settings标签', async () => {
  321. enterpriseOrderClient['video-statistics'].$get.mockResolvedValue({
  322. ok: true,
  323. json: async () => ({ total: 0, stats: [] })
  324. })
  325. enterpriseOrderClient['company-videos'].$get.mockResolvedValue({
  326. ok: true,
  327. json: async () => ({ data: [] })
  328. })
  329. renderWithQueryClient(<VideoManagement />)
  330. await waitFor(() => {
  331. const layout = screen.getByTestId('tab-bar-layout')
  332. expect(layout).toHaveAttribute('data-active-key', 'settings')
  333. })
  334. })
  335. })
  336. })