channel.integration.test.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. import { describe, it, expect, vi, beforeEach } from 'vitest';
  2. import { render, screen, fireEvent, waitFor } from '@testing-library/react';
  3. import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
  4. import ChannelManagement from '../../src/components/ChannelManagement';
  5. import { channelClientManager } from '../../src/api/channelClient';
  6. // 完整的mock响应对象
  7. const createMockResponse = (status: number, data?: any) => ({
  8. status,
  9. ok: status >= 200 && status < 300,
  10. body: null,
  11. bodyUsed: false,
  12. statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
  13. headers: new Headers(),
  14. url: '',
  15. redirected: false,
  16. type: 'basic' as ResponseType,
  17. json: async () => data || {},
  18. text: async () => '',
  19. blob: async () => new Blob(),
  20. arrayBuffer: async () => new ArrayBuffer(0),
  21. formData: async () => new FormData(),
  22. clone: function() { return this; }
  23. });
  24. // Mock API client
  25. vi.mock('../../src/api/channelClient', () => {
  26. const mockChannelClient = {
  27. getAllChannels: {
  28. $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
  29. data: [
  30. {
  31. id: 1,
  32. channelName: '微信小程序',
  33. channelType: '小程序',
  34. contactPerson: '张三',
  35. contactPhone: '13800138000',
  36. description: '微信小程序渠道',
  37. status: 1,
  38. createTime: '2024-01-01T00:00:00Z',
  39. updateTime: '2024-01-01T00:00:00Z'
  40. },
  41. {
  42. id: 2,
  43. channelName: '支付宝生活号',
  44. channelType: '生活号',
  45. contactPerson: '李四',
  46. contactPhone: '13900139000',
  47. description: '支付宝生活号渠道',
  48. status: 1,
  49. createTime: '2024-01-02T00:00:00Z',
  50. updateTime: '2024-01-02T00:00:00Z'
  51. }
  52. ],
  53. total: 2
  54. }))),
  55. },
  56. createChannel: {
  57. $post: vi.fn(() => Promise.resolve(createMockResponse(200, {
  58. id: 3,
  59. channelName: '抖音小程序',
  60. channelType: '小程序',
  61. contactPerson: '王五',
  62. contactPhone: '13700137000',
  63. description: '抖音小程序渠道',
  64. status: 1
  65. }))),
  66. },
  67. updateChannel: {
  68. $post: vi.fn(() => Promise.resolve(createMockResponse(200, {
  69. id: 1,
  70. channelName: '更新后的微信小程序',
  71. channelType: '小程序',
  72. contactPerson: '张三',
  73. contactPhone: '13800138000',
  74. description: '更新后的微信小程序渠道',
  75. status: 1
  76. }))),
  77. },
  78. deleteChannel: {
  79. $post: vi.fn(() => Promise.resolve(createMockResponse(200, {
  80. success: true
  81. }))),
  82. },
  83. searchChannels: {
  84. $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
  85. data: [
  86. {
  87. id: 1,
  88. channelName: '微信小程序',
  89. channelType: '小程序',
  90. contactPerson: '张三',
  91. contactPhone: '13800138000',
  92. description: '微信小程序渠道',
  93. status: 1,
  94. createTime: '2024-01-01T00:00:00Z',
  95. updateTime: '2024-01-01T00:00:00Z'
  96. }
  97. ],
  98. total: 1
  99. }))),
  100. },
  101. getChannel: {
  102. ':id': {
  103. $get: vi.fn(() => Promise.resolve(createMockResponse(200, {
  104. id: 1,
  105. channelName: '微信小程序',
  106. channelType: '小程序',
  107. contactPerson: '张三',
  108. contactPhone: '13800138000',
  109. description: '微信小程序渠道',
  110. status: 1,
  111. createTime: '2024-01-01T00:00:00Z',
  112. updateTime: '2024-01-01T00:00:00Z'
  113. }))),
  114. }
  115. },
  116. // 注意:渠道模块使用自定义路由,没有batchDeleteChannel路由
  117. };
  118. const mockChannelClientManager = {
  119. get: vi.fn(() => mockChannelClient),
  120. };
  121. return {
  122. channelClientManager: mockChannelClientManager,
  123. channelClient: mockChannelClient,
  124. };
  125. });
  126. // Mock toast
  127. vi.mock('sonner', () => ({
  128. toast: {
  129. success: vi.fn(() => {}),
  130. error: vi.fn(() => {}),
  131. },
  132. }));
  133. describe('ChannelManagement 集成测试', () => {
  134. let queryClient: QueryClient;
  135. beforeEach(() => {
  136. queryClient = new QueryClient({
  137. defaultOptions: {
  138. queries: {
  139. retry: false,
  140. },
  141. },
  142. });
  143. vi.clearAllMocks();
  144. });
  145. const renderComponent = () => {
  146. return render(
  147. <QueryClientProvider client={queryClient}>
  148. <ChannelManagement />
  149. </QueryClientProvider>
  150. );
  151. };
  152. it('应该正确渲染渠道列表', async () => {
  153. renderComponent();
  154. // 等待数据加载
  155. await waitFor(() => {
  156. expect(screen.getByText('微信小程序')).toBeInTheDocument();
  157. });
  158. // 验证表格内容
  159. expect(screen.getByText('支付宝生活号')).toBeInTheDocument();
  160. expect(screen.getByText('张三')).toBeInTheDocument();
  161. expect(screen.getByText('13800138000')).toBeInTheDocument();
  162. expect(screen.getByText('小程序')).toBeInTheDocument();
  163. expect(screen.getByText('生活号')).toBeInTheDocument();
  164. });
  165. it('应该打开创建渠道模态框', async () => {
  166. renderComponent();
  167. // 等待数据加载
  168. await waitFor(() => {
  169. expect(screen.getByText('微信小程序')).toBeInTheDocument();
  170. });
  171. // 点击创建按钮
  172. const createButton = screen.getByTestId('create-channel-button');
  173. fireEvent.click(createButton);
  174. // 验证模态框打开
  175. expect(screen.getByTestId('create-channel-modal-title')).toBeInTheDocument();
  176. });
  177. it('应该成功创建渠道', async () => {
  178. renderComponent();
  179. // 等待数据加载
  180. await waitFor(() => {
  181. expect(screen.getByText('微信小程序')).toBeInTheDocument();
  182. });
  183. // 打开创建模态框
  184. const createButton = screen.getByTestId('create-channel-button');
  185. fireEvent.click(createButton);
  186. // 填写表单
  187. const channelNameInput = screen.getByLabelText('渠道名称 *');
  188. const channelTypeInput = screen.getByLabelText('渠道类型');
  189. const contactPersonInput = screen.getByLabelText('联系人');
  190. const contactPhoneInput = screen.getByLabelText('联系电话');
  191. const descriptionInput = screen.getByLabelText('描述');
  192. fireEvent.change(channelNameInput, { target: { value: '抖音小程序' } });
  193. fireEvent.change(channelTypeInput, { target: { value: '小程序' } });
  194. fireEvent.change(contactPersonInput, { target: { value: '王五' } });
  195. fireEvent.change(contactPhoneInput, { target: { value: '13700137000' } });
  196. fireEvent.change(descriptionInput, { target: { value: '抖音小程序渠道' } });
  197. // 提交表单
  198. const submitButton = screen.getByText('创建');
  199. fireEvent.click(submitButton);
  200. // 验证API调用
  201. await waitFor(() => {
  202. expect(channelClientManager.get().createChannel.$post).toHaveBeenCalledWith({
  203. json: {
  204. channelName: '抖音小程序',
  205. channelType: '小程序',
  206. contactPerson: '王五',
  207. contactPhone: '13700137000',
  208. description: '抖音小程序渠道'
  209. }
  210. });
  211. });
  212. });
  213. it('应该打开编辑渠道模态框', async () => {
  214. renderComponent();
  215. // 等待数据加载
  216. await waitFor(() => {
  217. expect(screen.getByText('微信小程序')).toBeInTheDocument();
  218. });
  219. // 点击编辑按钮
  220. const editButton = screen.getByTestId('edit-channel-1');
  221. fireEvent.click(editButton);
  222. // 验证模态框打开并显示正确的数据
  223. expect(screen.getByTestId('create-channel-modal-title')).toBeInTheDocument();
  224. expect(screen.getByDisplayValue('微信小程序')).toBeInTheDocument();
  225. });
  226. it('应该成功更新渠道', async () => {
  227. renderComponent();
  228. // 等待数据加载
  229. await waitFor(() => {
  230. expect(screen.getByText('微信小程序')).toBeInTheDocument();
  231. });
  232. // 打开编辑模态框
  233. const editButton = screen.getByTestId('edit-channel-1');
  234. fireEvent.click(editButton);
  235. // 修改表单数据
  236. const channelNameInput = screen.getByLabelText('渠道名称 *');
  237. fireEvent.change(channelNameInput, { target: { value: '更新后的微信小程序' } });
  238. // 提交表单
  239. const submitButton = screen.getByText('更新');
  240. fireEvent.click(submitButton);
  241. // 验证API调用
  242. await waitFor(() => {
  243. expect(channelClientManager.get().updateChannel.$post).toHaveBeenCalledWith({
  244. json: {
  245. id: 1,
  246. channelName: '更新后的微信小程序',
  247. channelType: '小程序',
  248. contactPerson: '张三',
  249. contactPhone: '13800138000',
  250. description: '微信小程序渠道'
  251. }
  252. });
  253. });
  254. });
  255. it('应该打开删除确认对话框', async () => {
  256. renderComponent();
  257. // 等待数据加载
  258. await waitFor(() => {
  259. expect(screen.getByText('微信小程序')).toBeInTheDocument();
  260. });
  261. // 点击删除按钮
  262. const deleteButton = screen.getByTestId('delete-channel-1');
  263. fireEvent.click(deleteButton);
  264. // 验证删除确认对话框打开
  265. expect(screen.getByTestId('delete-confirm-dialog-title')).toBeInTheDocument();
  266. });
  267. it('应该成功搜索渠道', async () => {
  268. renderComponent();
  269. // 等待数据加载
  270. await waitFor(() => {
  271. expect(screen.getByText('微信小程序')).toBeInTheDocument();
  272. });
  273. // 输入搜索关键词
  274. const searchInput = screen.getByTestId('search-input');
  275. fireEvent.change(searchInput, { target: { value: '微信' } });
  276. // 点击搜索按钮
  277. const searchButton = screen.getByTestId('search-button');
  278. fireEvent.click(searchButton);
  279. // 验证API调用
  280. await waitFor(() => {
  281. expect(channelClientManager.get().searchChannels.$get).toHaveBeenCalledWith({
  282. query: {
  283. name: '微信',
  284. skip: 0,
  285. take: 10
  286. }
  287. });
  288. });
  289. });
  290. it('应该处理API错误', async () => {
  291. // Mock API错误 - 使用vi.spyOn来mock
  292. const mockErrorResponse = createMockResponse(500, { message: '服务器错误' });
  293. vi.spyOn(channelClientManager.get().getAllChannels, '$get').mockResolvedValue(mockErrorResponse as any);
  294. renderComponent();
  295. // 验证错误处理 - 等待错误状态
  296. await waitFor(() => {
  297. // 检查是否没有显示数据
  298. expect(screen.queryByText('微信小程序')).not.toBeInTheDocument();
  299. expect(screen.queryByText('支付宝生活号')).not.toBeInTheDocument();
  300. });
  301. // 恢复原始mock
  302. vi.mocked(channelClientManager.get().getAllChannels.$get).mockRestore();
  303. });
  304. });