TechnicianMiniApp.test.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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: { mode: 'included' as const, amount: '0', per_km: '0' },
  38. service_radius_km: null,
  39. out_of_range_policy: '',
  40. source_type: 'template' as const,
  41. };
  42. const bootstrap = {
  43. technician: {
  44. user_id: '700',
  45. display_name: '测试技师',
  46. username: 'technician700',
  47. eligibility: { ready: true, issues: [] },
  48. service_profile: null,
  49. },
  50. templates: [
  51. {
  52. template_id: 'builtin_quick_at_store',
  53. version: 1,
  54. name: '快速到店服务',
  55. builtin: true,
  56. package: packageTemplate,
  57. },
  58. ],
  59. limits: { max_packages: 5 },
  60. };
  61. beforeEach(() => {
  62. vi.mocked(technicianApiRequest).mockImplementation(async (path) => {
  63. if (path === '/bootstrap') return bootstrap;
  64. if (path === '/profile') {
  65. return {
  66. ...bootstrap.technician,
  67. service_profile: {
  68. headline: '标准到店服务',
  69. bio: '一次标准到店服务',
  70. tags: ['到店'],
  71. contact_hours: '请通过 Telegram 私聊确认服务时间',
  72. service_modes: ['at_store'],
  73. accepting_requests: true,
  74. public_area_text: '测试城区及附近',
  75. venue: { name: '工作地点', address_hint: '测试路 100 号' },
  76. onsite_policy: { description: '' },
  77. packages: [packageTemplate],
  78. is_complete: true,
  79. },
  80. };
  81. }
  82. throw new Error(`Unexpected request: ${path}`);
  83. });
  84. });
  85. test('模板创建只要求技师确认常用中文字段并可直接发布', async () => {
  86. render(<TechnicianMiniApp />);
  87. expect(await screen.findByText('你好,测试技师')).toBeInTheDocument();
  88. fireEvent.click(screen.getByRole('button', { name: /快速到店服务/ }));
  89. expect(await screen.findByText('发布新套餐')).toBeInTheDocument();
  90. expect(screen.getByRole('radio', { name: '固定价' })).toBeChecked();
  91. expect(screen.getByText('每次')).toBeInTheDocument();
  92. expect(screen.queryByText('per_service')).not.toBeInTheDocument();
  93. fireEvent.change(screen.getByLabelText('对外展示的服务区域'), {
  94. target: { value: '测试城区及附近' },
  95. });
  96. fireEvent.change(screen.getByLabelText('到店地址'), {
  97. target: { value: '测试路 100 号' },
  98. });
  99. fireEvent.click(screen.getByRole('button', { name: /确认并发布套餐/ }));
  100. await waitFor(() => {
  101. expect(technicianApiRequest).toHaveBeenCalledWith(
  102. '/profile',
  103. expect.objectContaining({ method: 'PUT' }),
  104. );
  105. });
  106. expect(technicianJsonOptions).toHaveBeenCalledWith(
  107. 'PUT',
  108. expect.objectContaining({
  109. service_profile: expect.objectContaining({
  110. public_area_text: '测试城区及附近',
  111. }),
  112. }),
  113. );
  114. });