import ElementPlus from 'element-plus' import { createPinia, setActivePinia } from 'pinia' import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils' import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { listPlatformPartners, type PlatformPartnerResource, } from '@/modules/master-data/counterparties/api/platformPartners' import { useAccessPreviewStore } from '@/core/access/accessPreview' import { useAuthStore } from '@/core/auth/auth' import { listPlatformTenants, type PlatformTenantResource } from '../../../iam/api/platformIam' import PlatformTenantSelector from '../../../iam/components/PlatformTenantSelector.vue' import PartnersView from './PartnersView.vue' vi.mock('@/modules/master-data/counterparties/api/platformPartners', () => ({ listPlatformPartners: vi.fn(), createPlatformPartner: vi.fn(), getPlatformPartner: vi.fn(), downloadPlatformPartnerImportTemplate: vi.fn(), importPlatformPartners: vi.fn(), updatePlatformPartner: vi.fn(), changePlatformPartnerStatus: vi.fn(), })) vi.mock('../../../iam/api/platformIam', () => ({ listPlatformTenants: vi.fn() })) class ResizeObserverStub implements ResizeObserver { observe() {} unobserve() {} disconnect() {} } function createMemoryStorage(): Storage { const values = new Map() return { get length() { return values.size }, clear: () => values.clear(), getItem: (key) => values.get(key) ?? null, key: (index) => [...values.keys()][index] ?? null, removeItem: (key) => values.delete(key), setItem: (key, value) => values.set(key, value), } } const partner: PlatformPartnerResource = { id: '19001', tenantId: 10001, dataScopeId: 10001, partnerCode: 'CUS-001', partnerName: '深圳示例贸易有限公司', partnerType: 'CUSTOMER', types: ['CUSTOMER', 'SHIPPER'], contactName: '张三', contactPhone: '13800138000', contactEmail: 'contact@example.com', countryCode: 'CN', province: '广东省', city: '深圳市', district: '宝安区', addressLine: '航城街道示例路 1 号', postalCode: '518000', status: 'ACTIVE', remark: '长期合作单位', updatedAt: '2026-07-30T22:45:00', } const tenant = (id: number | string, tenantName: string): PlatformTenantResource => ({ id, tenantCode: String(id), tenantName, status: 'ACTIVE', legalName: tenantName, legalRepresentative: null, unifiedSocialCreditCode: null, contactName: null, contactPhone: null, contactEmail: null, servicePackage: null, contactWechat: null, salesOwnerName: null, paymentTermDays: null, enterpriseType: null, establishedDate: null, registeredCapital: null, registeredCapitalCurrency: null, registeredAddress: null, internalRemark: null, createdAt: null, approvedAt: null, }) const snowflakeTenantId = '9223372036854775806' async function mountView() { vi.stubGlobal('localStorage', createMemoryStorage()) vi.stubGlobal('sessionStorage', createMemoryStorage()) const pinia = createPinia() setActivePinia(pinia) const auth = useAuthStore() auth.token = 'token' auth.permissions = [ 'platform:counterparty:list', 'platform:counterparty:create', 'platform:counterparty:edit', 'platform:counterparty:status', ] useAccessPreviewStore().initializeAfterLogin() const wrapper = mount(PartnersView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] }, }) await flushPromises() return wrapper } enableAutoUnmount(afterEach) afterAll(() => vi.unstubAllGlobals()) describe('PartnersView real API integration', () => { beforeEach(() => { vi.stubGlobal('ResizeObserver', ResizeObserverStub) document.body.innerHTML = '' vi.mocked(listPlatformTenants).mockReset().mockResolvedValue({ records: [tenant(10001, '默认企业'), tenant(snowflakeTenantId, '雪花企业')], page: 1, size: 200, total: 2, pages: 1, }) vi.mocked(listPlatformPartners).mockReset().mockResolvedValue({ records: [partner], page: 1, size: 20, total: 1, pages: 1, }) }) it('loads the fixed tenant page and renders the real partner fields', async () => { const wrapper = await mountView() expect(listPlatformPartners).toHaveBeenCalledWith(10001, { page: 1, size: 20, keyword: undefined, type: undefined, status: undefined, }) expect(wrapper.text()).toContain('深圳示例贸易有限公司') expect(wrapper.text()).toContain('CUS-001') expect(wrapper.text()).toContain('contact@example.com') expect(wrapper.text()).toContain('广东省 深圳市 宝安区 航城街道示例路 1 号') expect(wrapper.text()).not.toContain('往来单位总数') expect(wrapper.text()).not.toContain('往来单位档案') expect(wrapper.find('.query-toolbar').exists()).toBe(true) expect(wrapper.find('.data-table').exists()).toBe(true) expect(wrapper.find('.table-panel').exists()).toBe(true) }) it('preserves a snowflake tenant id as a string when switching enterprises', async () => { const wrapper = await mountView() wrapper.findComponent(PlatformTenantSelector).vm.$emit('update:modelValue', snowflakeTenantId) await flushPromises() expect(listPlatformPartners).toHaveBeenLastCalledWith(snowflakeTenantId, { page: 1, size: 20, keyword: undefined, type: undefined, status: undefined, }) }) it('updates filters locally and only requests after search', async () => { const wrapper = await mountView() const keywordInput = wrapper.find('input[placeholder="单位名称、编码、联系人或电话"]') await keywordInput.setValue('深圳') await flushPromises() expect(listPlatformPartners).toHaveBeenCalledTimes(1) const searchButton = wrapper.findAll('button').find((button) => button.text().trim() === '查询') expect(searchButton).toBeDefined() await searchButton?.trigger('click') await flushPromises() expect(listPlatformPartners).toHaveBeenCalledTimes(2) expect(listPlatformPartners).toHaveBeenLastCalledWith(10001, { page: 1, size: 20, keyword: '深圳', type: undefined, status: undefined, }) }) it('shows useful placeholders and visible required markers in the create form', async () => { const wrapper = await mountView() const createButton = wrapper.findAll('button').find((button) => button.text().trim() === '新增往来单位') expect(createButton).toBeDefined() await createButton?.trigger('click') await flushPromises() expect(document.querySelector('input[placeholder="请输入工商登记或常用单位名称"]')).not.toBeNull() expect(document.querySelector('input[placeholder="请输入主要联系人姓名"]')).not.toBeNull() expect(document.querySelector('textarea[placeholder="请输入街道、园区、楼栋及门牌信息"]')).not.toBeNull() expect(document.querySelectorAll('.el-form-item.is-required').length).toBe(9) }) it('shows a recoverable API failure without demo data', async () => { vi.mocked(listPlatformPartners).mockRejectedValueOnce(new Error('Gateway unavailable')) const wrapper = await mountView() expect(wrapper.text()).toContain('往来单位列表暂时无法加载') expect(wrapper.text()).toContain('Gateway unavailable') expect(wrapper.text()).toContain('重新加载') expect(wrapper.text()).not.toContain('深圳示例贸易有限公司') }) })