| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- import React from 'react'
- import { render, screen, fireEvent, waitFor } from '@testing-library/react'
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
- import AddressEditPage from '@/pages/address-edit/index'
- // 导入Taro mock函数
- import { mockUseRouter, mockNavigateBack, mockShowToast } from '~/__mocks__/taroMock'
- // Mock API client
- jest.mock('@/api', () => ({
- deliveryAddressClient: {
- $post: jest.fn(),
- ':id': {
- $get: jest.fn(),
- $put: jest.fn(),
- },
- },
- }))
- // Mock auth hook
- jest.mock('@/utils/auth', () => ({
- useAuth: () => ({
- user: { id: 1 },
- }),
- }))
- // Mock react-hook-form
- let mockFormData = {
- name: '',
- phone: '',
- province: undefined as number | undefined,
- city: undefined as number | undefined,
- district: undefined as number | undefined,
- town: undefined as number | undefined,
- address: '',
- isDefault: false
- }
- const mockHandleSubmit = jest.fn((onSubmit, onError) => (e: any) => {
- e?.preventDefault?.()
- // 模拟验证通过,调用onSubmit
- onSubmit(mockFormData)
- })
- const mockReset = jest.fn((data: any) => {
- if (data) {
- mockFormData = { ...mockFormData, ...data }
- }
- })
- const mockSetValue = jest.fn((name: string, value: any) => {
- mockFormData = { ...mockFormData, [name]: value }
- })
- const mockWatch = jest.fn()
- jest.mock('react-hook-form', () => ({
- useForm: () => ({
- formState: {
- isValid: true,
- errors: {},
- },
- handleSubmit: mockHandleSubmit,
- reset: mockReset,
- setValue: mockSetValue,
- watch: mockWatch,
- getValues: () => mockFormData,
- }),
- }))
- // Mock components
- jest.mock('@/components/ui/navbar', () => ({
- Navbar: ({ title, onClickLeft }: { title: string; onClickLeft: () => void }) => (
- <div data-testid="navbar">
- <span>{title}</span>
- <button onClick={onClickLeft}>返回</button>
- </div>
- ),
- }))
- jest.mock('@/components/ui/button', () => ({
- Button: ({ children, onClick, disabled, className }: any) => (
- <button
- onClick={onClick}
- disabled={disabled}
- className={className}
- data-testid="button"
- >
- {children}
- </button>
- ),
- }))
- jest.mock('@/components/ui/card', () => ({
- Card: ({ children, className }: any) => (
- <div className={className} data-testid="card">
- {children}
- </div>
- ),
- }))
- jest.mock('@/components/ui/form', () => ({
- Form: ({ children, ...props }: any) => (
- <form {...props} data-testid="form">
- {children}
- </form>
- ),
- FormField: ({ name, render }: any) => (
- <div data-testid={`form-field-${name}`}>
- {render({ field: { value: '', onChange: jest.fn() } })}
- </div>
- ),
- FormItem: ({ children }: any) => <div data-testid="form-item">{children}</div>,
- FormLabel: ({ children }: any) => <label data-testid="form-label">{children}</label>,
- FormControl: ({ children }: any) => <div data-testid="form-control">{children}</div>,
- FormMessage: () => <div data-testid="form-message">错误消息</div>,
- }))
- jest.mock('@/components/ui/input', () => ({
- Input: ({ placeholder, ...props }: any) => (
- <input
- placeholder={placeholder}
- {...props}
- data-testid="input"
- />
- ),
- }))
- jest.mock('@/components/ui/switch', () => ({
- Switch: ({ checked, onChange, color }: any) => (
- <input
- type="checkbox"
- checked={checked}
- onChange={onChange}
- data-testid="switch"
- style={{ color }}
- />
- ),
- }))
- jest.mock('@/components/ui/city-selector', () => ({
- CitySelector: ({
- provinceValue,
- cityValue,
- districtValue,
- townValue,
- onProvinceChange,
- onCityChange,
- onDistrictChange,
- onTownChange,
- showLabels
- }: any) => (
- <div data-testid="city-selector">
- <div>省份: {provinceValue}</div>
- <div>城市: {cityValue}</div>
- <div>区县: {districtValue}</div>
- <div>乡镇: {townValue}</div>
- </div>
- ),
- }))
- describe('AddressEditPage', () => {
- let queryClient: QueryClient
- beforeEach(() => {
- queryClient = new QueryClient({
- defaultOptions: {
- queries: { retry: false },
- mutations: { retry: false },
- },
- })
- // Reset all mocks
- jest.clearAllMocks()
- // 重置表单数据
- mockFormData = {
- name: '',
- phone: '',
- province: undefined,
- city: undefined,
- district: undefined,
- town: undefined,
- address: '',
- isDefault: false
- }
- // 默认设置无地址ID(添加模式)
- mockUseRouter.mockReturnValue({
- params: {},
- })
- })
- const renderWithProviders = (component: React.ReactElement) => {
- return render(
- <QueryClientProvider client={queryClient}>
- {component}
- </QueryClientProvider>
- )
- }
- const mockAddress = {
- id: 1,
- name: '张三',
- phone: '13812345678',
- receiverProvince: 440000,
- receiverCity: 440300,
- receiverDistrict: 440305,
- receiverTown: 440305001,
- address: '科技大厦A座',
- isDefault: 1,
- }
- it('渲染添加地址页面标题和布局', async () => {
- renderWithProviders(<AddressEditPage />)
- expect(screen.getByTestId('navbar')).toBeInTheDocument()
- expect(screen.getByText('添加地址')).toBeInTheDocument()
- expect(screen.getByTestId('form')).toBeInTheDocument()
- })
- it('渲染编辑地址页面标题和布局', async () => {
- const { deliveryAddressClient } = await import('@/api')
- ;(deliveryAddressClient[':id'].$get as jest.Mock).mockResolvedValue({
- status: 200,
- json: async () => mockAddress,
- } as any)
- mockUseRouter.mockReturnValue({
- params: { id: '1' },
- })
- renderWithProviders(<AddressEditPage />)
- await waitFor(() => {
- expect(screen.getByText('编辑地址')).toBeInTheDocument()
- expect(deliveryAddressClient[':id'].$get).toHaveBeenCalledWith({
- param: { id: 1 }
- })
- })
- })
- it('填充编辑模式下的表单数据', async () => {
- const { deliveryAddressClient } = await import('@/api')
- ;(deliveryAddressClient[':id'].$get as jest.Mock).mockResolvedValue({
- status: 200,
- json: async () => mockAddress,
- } as any)
- mockUseRouter.mockReturnValue({
- params: { id: '1' },
- })
- renderWithProviders(<AddressEditPage />)
- await waitFor(() => {
- expect(deliveryAddressClient[':id'].$get).toHaveBeenCalledWith({
- param: { id: 1 }
- })
- })
- })
- it('提交新建地址表单', async () => {
- const { deliveryAddressClient } = await import('@/api')
- const mockResponse = { id: 2, ...mockAddress, isDefault: 0 }
- ;(deliveryAddressClient.$post as jest.Mock).mockResolvedValue({
- status: 201,
- json: async () => mockResponse,
- } as any)
- renderWithProviders(<AddressEditPage />)
- // 找到保存按钮并点击
- await waitFor(() => {
- const saveButton = screen.getByText('保存地址')
- expect(saveButton).toBeInTheDocument()
- fireEvent.click(saveButton)
- })
- await waitFor(() => {
- expect(deliveryAddressClient.$post).toHaveBeenCalled()
- expect(mockShowToast).toHaveBeenCalledWith({
- title: '添加成功',
- icon: 'success'
- })
- expect(mockNavigateBack).toHaveBeenCalled()
- })
- })
- it('提交编辑地址表单', async () => {
- const { deliveryAddressClient } = await import('@/api')
- ;(deliveryAddressClient[':id'].$get as jest.Mock).mockResolvedValue({
- status: 200,
- json: async () => mockAddress,
- } as any)
- const updatedAddress = { ...mockAddress, name: '李四' }
- ;(deliveryAddressClient[':id'].$put as jest.Mock).mockResolvedValue({
- status: 200,
- json: async () => updatedAddress,
- } as any)
- mockUseRouter.mockReturnValue({
- params: { id: '1' },
- })
- renderWithProviders(<AddressEditPage />)
- // 等待数据加载
- await waitFor(() => {
- expect(deliveryAddressClient[':id'].$get).toHaveBeenCalled()
- })
- // 确保表单已重置
- await waitFor(() => {
- expect(mockReset).toHaveBeenCalled()
- })
- // 找到保存按钮并点击
- await waitFor(() => {
- const saveButton = screen.getByText('保存地址')
- expect(saveButton).toBeInTheDocument()
- fireEvent.click(saveButton)
- })
- await waitFor(() => {
- expect(deliveryAddressClient[':id'].$put).toHaveBeenCalled()
- // 检查调用参数
- const call = deliveryAddressClient[':id'].$put.mock.calls[0]
- expect(call[0]).toEqual(expect.objectContaining({
- param: { id: 1 }
- }))
- expect(mockShowToast).toHaveBeenCalledWith({
- title: '更新成功',
- icon: 'success'
- })
- expect(mockNavigateBack).toHaveBeenCalled()
- })
- })
- it('显示API错误提示', async () => {
- const { deliveryAddressClient } = await import('@/api')
- ;(deliveryAddressClient.$post as jest.Mock).mockResolvedValue({
- status: 400,
- json: async () => ({ message: '保存失败' }),
- } as any)
- renderWithProviders(<AddressEditPage />)
- // 找到保存按钮并点击
- await waitFor(() => {
- const saveButton = screen.getByText('保存地址')
- fireEvent.click(saveButton)
- })
- await waitFor(() => {
- expect(mockShowToast).toHaveBeenCalledWith({
- title: '保存地址失败',
- icon: 'none'
- })
- })
- })
- it('验证表单字段渲染', async () => {
- renderWithProviders(<AddressEditPage />)
- expect(screen.getByText('收货人姓名')).toBeInTheDocument()
- expect(screen.getByText('手机号码')).toBeInTheDocument()
- expect(screen.getByText('详细地址')).toBeInTheDocument()
- expect(screen.getByText('设为默认地址')).toBeInTheDocument()
- })
- it('点击返回按钮', async () => {
- renderWithProviders(<AddressEditPage />)
- const navbar = screen.getByTestId('navbar')
- const backButton = navbar.querySelector('button')
- if (backButton) {
- fireEvent.click(backButton)
- expect(mockNavigateBack).toHaveBeenCalled()
- }
- })
- it('显示保存中的加载状态', async () => {
- const { deliveryAddressClient } = await import('@/api')
- let resolvePromise: any
- const promise = new Promise(resolve => {
- resolvePromise = resolve
- })
- ;(deliveryAddressClient.$post as jest.Mock).mockReturnValue(promise)
- renderWithProviders(<AddressEditPage />)
- // 点击保存按钮
- await waitFor(() => {
- const saveButton = screen.getByText('保存地址')
- fireEvent.click(saveButton)
- })
- // 应该显示加载状态
- await waitFor(() => {
- expect(screen.getByText('保存中...')).toBeInTheDocument()
- })
- // 解析promise
- resolvePromise({
- status: 201,
- json: async () => ({ id: 1 }),
- })
- await waitFor(() => {
- expect(screen.getByText('保存地址')).toBeInTheDocument()
- })
- })
- })
|