TechnicianMiniApp.test.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { fireEvent, render, screen, waitFor } from '@testing-library/react';
  2. import { beforeEach, expect, test, vi } from 'vitest';
  3. import TechnicianMiniApp from '@/pages/TechnicianMiniApp';
  4. import {
  5. technicianApiRequest,
  6. technicianJsonOptions,
  7. } from '@/services/technicianApi';
  8. vi.mock('@/services/technicianApi', () => ({
  9. initializeTelegramMiniApp: vi.fn(),
  10. TechnicianApiError: class TechnicianApiError extends Error {
  11. code = '';
  12. status = 400;
  13. },
  14. technicianApiRequest: vi.fn(),
  15. technicianJsonOptions: vi.fn((method, body) => ({
  16. method,
  17. body: JSON.stringify(body),
  18. })),
  19. }));
  20. const packageTemplate = {
  21. package_id: 'template-package',
  22. name: '标准到店服务',
  23. category: '通用服务',
  24. description: '一次标准到店服务',
  25. tags: ['到店'],
  26. service_modes: ['at_store'] as const,
  27. price_mode: 'fixed' as const,
  28. currency: 'CNY',
  29. price_unit: 'per_service' as const,
  30. min_price: '200.00',
  31. max_price: '200.00',
  32. duration_minutes: 60,
  33. included_items: '基础服务',
  34. excluded_items: '',
  35. preparation: '',
  36. addons: [],
  37. travel_fee: {
  38. mode: 'included' as const,
  39. amount: '0',
  40. per_km: '0',
  41. description: '无额外费用',
  42. },
  43. service_radius_km: null,
  44. out_of_range_policy: '',
  45. source_type: 'template' as const,
  46. };
  47. const bootstrap = {
  48. technician: {
  49. user_id: '700',
  50. display_name: '测试技师',
  51. username: 'technician700',
  52. eligibility: { ready: true, issues: [] },
  53. service_profile: null,
  54. },
  55. templates: [
  56. {
  57. template_id: 'builtin_quick_at_store',
  58. version: 1,
  59. name: '快速到店服务',
  60. builtin: true,
  61. package: packageTemplate,
  62. },
  63. ],
  64. limits: { max_packages: 5 },
  65. };
  66. beforeEach(() => {
  67. vi.mocked(technicianApiRequest).mockImplementation(async (path) => {
  68. if (path === '/bootstrap') return bootstrap;
  69. if (path === '/profile') {
  70. return {
  71. ...bootstrap.technician,
  72. service_profile: {
  73. headline: '标准到店服务',
  74. bio: '一次标准到店服务',
  75. tags: ['到店'],
  76. contact_hours: '请通过 Telegram 私聊确认服务时间',
  77. service_modes: ['at_store'],
  78. accepting_requests: true,
  79. public_area_text: '测试城区及附近',
  80. venue: { name: '工作地点', address_hint: '测试路 100 号' },
  81. onsite_policy: { description: '' },
  82. packages: [packageTemplate],
  83. is_complete: true,
  84. },
  85. };
  86. }
  87. throw new Error(`Unexpected request: ${path}`);
  88. });
  89. });
  90. test('模板创建只要求技师确认五项信息并可直接发布', async () => {
  91. render(<TechnicianMiniApp />);
  92. expect(await screen.findByText('你好,测试技师')).toBeInTheDocument();
  93. fireEvent.click(screen.getByRole('button', { name: /快速到店服务/ }));
  94. expect(await screen.findByText('发布服务')).toBeInTheDocument();
  95. expect(screen.getByLabelText('服务价格')).toHaveValue('CNY 200.00/次');
  96. expect(screen.getByLabelText('服务内容')).toHaveValue('一次标准到店服务');
  97. expect(screen.getByLabelText('额外费用说明')).toHaveValue('无额外费用');
  98. expect(screen.queryByText('per_service')).not.toBeInTheDocument();
  99. fireEvent.change(screen.getByLabelText('大概服务区域'), {
  100. target: { value: '测试城区及附近' },
  101. });
  102. fireEvent.click(screen.getByRole('button', { name: /直接发布/ }));
  103. await waitFor(() => {
  104. expect(technicianApiRequest).toHaveBeenCalledWith(
  105. '/profile',
  106. expect.objectContaining({ method: 'PUT' }),
  107. );
  108. });
  109. expect(technicianJsonOptions).toHaveBeenCalledWith(
  110. 'PUT',
  111. expect.objectContaining({
  112. service_profile: expect.objectContaining({
  113. public_area_text: '测试城区及附近',
  114. }),
  115. }),
  116. );
  117. });