import { readFileSync } from 'node:fs' import { resolve } from 'node:path' import ElementPlus, { ElMessage } from 'element-plus' import { createPinia, setActivePinia } from 'pinia' import { flushPromises, mount } from '@vue/test-utils' import { createMemoryHistory, createRouter } from 'vue-router' import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' import { useAccessPreviewStore } from '@/core/access/accessPreview' import { useAuthStore } from '@/core/auth/auth' import { createPlatformExportTask } from '@/modules/downloads' import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam' import { showExportTaskCreatedDialog } from '@/shared/components/exportTaskCreatedDialog' import { listFinanceAccountLedger, listFinanceAccounts } from '../api/finance' import FinanceCustomerAccountsView from './FinanceCustomerAccountsView.vue' vi.mock('../api/finance', () => ({ listFinanceAccounts: vi.fn(), listFinanceAccountLedger: vi.fn(), getFinanceCustomerTerms: vi.fn(), updateFinanceCustomerTerms: vi.fn(), })) vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn() })) vi.mock('@/modules/downloads', () => ({ createPlatformExportTask: vi.fn() })) vi.mock('@/shared/components/exportTaskCreatedDialog', () => ({ showExportTaskCreatedDialog: vi.fn() })) class ResizeObserverStub implements ResizeObserver { observe() {} unobserve() {} disconnect() {} } function memoryStorage(): 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) } } async function mountAccountsView(permissions: string[], initialPath = '/finance/customer-accounts') { const pinia = createPinia(); setActivePinia(pinia) const auth = useAuthStore(); auth.token = 'token'; auth.permissions = permissions useAccessPreviewStore().initializeAfterLogin() const router = createRouter({ history: createMemoryHistory(), routes: [ { path: '/finance/customer-accounts', name: 'finance-customer-accounts', component: FinanceCustomerAccountsView }, { path: '/finance/customer-accounts/:customerId', name: 'finance-customer-account-detail', component: FinanceCustomerAccountsView }, { path: '/downloads', name: 'download-center', component: { template: '
下载中心
' } }, ] }) await router.push(initialPath); await router.isReady() const wrapper = mount(FinanceCustomerAccountsView, { global: { plugins: [pinia, ElementPlus, router] } }) await flushPromises() return Object.assign(wrapper, { router }) } describe('FinanceCustomerAccountsView delivery', () => { const page = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceCustomerAccountsView.vue'), 'utf8') const routes = readFileSync(resolve(process.cwd(), 'src/modules/finance/routes.ts'), 'utf8') it('keeps the account detail as a full page with traceable ledger controls', () => { expect(page).toContain('v-else-if="!detailOpen"') expect(page).toContain('返回客户账户') expect(page).toContain('') expect(page).toContain('') expect(page).not.toContain('class="detail-toolbar"') expect(page).not.toContain(' { for (const marker of ['class="term-section"', 'class="term-editor"', '账期天数', '#footer', '保存账期']) expect(page).toContain(marker) expect(page).toContain('grid-template-columns: repeat(3, minmax(0, 1fr))') for (const metric of ['总成本(CNY)', '已付成本(CNY)', '总成本', '已付成本', '待付金额']) expect(page).toContain(metric) expect(page).toContain('grid-template-columns: 120px minmax(0, 220px)') expect(page).toContain('justify-content: start') expect(page).toContain('class="surface-panel finance-account-detail__surface"') expect(page).toContain('min-height: 64px') expect(page).toContain('padding: 0 0 12px') expect(page).toContain('class="detail-table-scroll"') expect(page).toContain('class="ledger-identifier"') expect(page).toContain('class="ledger-document"') expect(page).toContain('text-overflow: ellipsis') expect(page).toContain('white-space: nowrap') }) it('keeps account detail and term editing readable across the 1920 workspace', () => { expect(page).toContain('width: min(100%, 1920px)') expect(page).toContain('@media (min-width: 1920px)') expect(page).toContain('.finance-account-detail__layout :deep(.detail-page-layout__content)') expect(page).toContain('padding: 0 14px 10px') expect(page).not.toContain('max-width: 520px') }) it('renders every account row operation as a small plain primary button with an icon', () => { expect(page).toContain("{ key: 'actions', label: '操作', width: 160, fixed: 'left', slotName: 'actions' }") expect(page).toContain('查看') expect(page).toContain('账期') expect(page).not.toMatch(/