import { readFileSync } from 'node:fs' import { dirname, resolve } from 'node:path' import ts from 'typescript' import { describe, expect, it } from 'vitest' import { compileStyle, parse } from 'vue/compiler-sfc' import { adminNavigation, getNavigationLeaves } from '@/core/navigation/adminNavigation' // Approved entry/consumer inventory. Do not discover unrelated menus recursively. const cargoConsumers = [ 'ocean-bookings/pages/OceanBookingsView.vue', 'ocean-bookings/pages/OceanBookingDetailView.vue', 'ocean-bookings/components/OceanBookingDocumentsPanel.vue', 'ocean-bookings/components/OceanBookingPrintDialog.vue', 'trucking/pages/TruckAppointmentsView.vue', 'trucking/pages/TruckAppointmentCreateView.vue', 'trucking/pages/TruckAppointmentDetailView.vue', 'trucking/components/TruckAppointmentForm.vue', 'trucking/components/TruckAttachmentDialog.vue', 'container-loading/pages/ContainersView.vue', 'container-loading/pages/ContainerDetailView.vue', 'container-loading/pages/SmartLoadingWorkbenchView.vue', 'container-loading/components/SmartLoadingDialog.vue', 'container-loading/pages/GlobalSmartLoadingView.vue', 'loading-lists/pages/PreloadPlanDetailView.vue', 'container-loading/components/OceanBookingLinkDialog.vue', 'container-loading/components/ContainerOverviewPanel.vue', 'port-schedules/components/ContainerPortSchedulePanel.vue', 'container-loading/components/ContainerCustomsPanel.vue', 'container-loading/components/ContainerLastLegFeesPanel.vue', 'loading-lists/components/ContainerFeesPanel.vue', 'port-schedules/components/CarrierScheduleQueryPanel.vue', ] const overseasConsumers = [ 'overseas-warehouse/pages/LastMileOrdersView.vue', 'overseas-warehouse/pages/LastMileOrderDetailView.vue', 'overseas-warehouse/pages/InboundOrdersView.vue', 'overseas-warehouse/pages/InboundOrderDetailView.vue', 'overseas-warehouse/pages/DispatchesView.vue', 'overseas-warehouse/pages/DispatchDetailView.vue', 'overseas-warehouse/pages/OutboundsView.vue', 'overseas-warehouse/pages/OutboundDetailView.vue', 'overseas-warehouse/pages/DeliveryOrdersView.vue', 'overseas-warehouse/pages/DeliveryOrderDetailView.vue', 'overseas-warehouse/pages/RelabelOrdersView.vue', 'overseas-warehouse/pages/RelabelOrderDetailView.vue', 'overseas-warehouse/pages/PalletsView.vue', 'overseas-warehouse/pages/PalletDetailView.vue', 'overseas-warehouse/pages/LocationsView.vue', 'overseas-warehouse/pages/CargoDiscardsView.vue', 'overseas-warehouse/pages/InventoryView.vue', 'overseas-warehouse/pages/InventoryDetailView.vue', 'overseas-warehouse/pages/InventoryTransactionsView.vue', 'overseas-warehouse/pages/ReturnInventoryManagementView.vue', 'overseas-warehouse/components/WarehouseTransferReviewDialog.vue', 'overseas-warehouse/components/SmartDispatchDialog.vue', 'overseas-warehouse/components/DeliveryDocumentsDialog.vue', ] const overseasBusinessConsumers = [ 'overseas-business/pages/PurchaseOrdersView.vue', 'overseas-business/pages/PurchaseOrderDetailView.vue', 'overseas-business/pages/PurchaseOrderEditView.vue', 'overseas-business/pages/TransportOrdersView.vue', 'overseas-business/pages/TruckOrderDetailView.vue', 'overseas-business/pages/TransportOrderFormView.vue', 'overseas-business/components/ProcurementNodeActions.vue', 'overseas-business/components/ProcurementPhysicalFields.vue', 'overseas-business/components/ProcurementTransportFields.vue', 'overseas-business/components/ProcurementFinanceFields.vue', 'overseas-business/components/ProcurementChargeFields.vue', 'overseas-business/components/ProcurementControlFields.vue', ] const customerServiceConsumers = [ 'overseas-warehouse/pages/ExceptionOrdersView.vue', 'overseas-warehouse/pages/CustomerVisitsView.vue', ] const financeConsumers = [ 'finance/pages/FinanceCustomerAccountsView.vue', 'finance/pages/FinanceCustomerArapView.vue', 'finance/pages/FinanceCustomerStatementsView.vue', 'finance/pages/FinanceFormalSettlementPage.vue', 'finance/pages/FinanceReceivableStatisticsView.vue', ] const generalCatalogConsumers = [ 'suppliers/pages/SuppliersView.vue', 'suppliers/pages/SupplierDetailView.vue', 'suppliers/pages/SupplierFormView.vue', 'suppliers/components/SupplierLicenseDialog.vue', 'overseas-master-data/pages/ExchangeRatesView.vue', 'overseas-master-data/pages/OverseasQuotesView.vue', 'overseas-master-data/pages/VehiclesView.vue', 'overseas-master-data/pages/VehicleDetailView.vue', 'overseas-master-data/components/TransportRouteDialog.vue', 'overseas-master-data/pages/DriversView.vue', 'overseas-master-data/pages/TripsView.vue', 'overseas-master-data/pages/CapacityOffersView.vue', 'overseas-master-data/pages/CapacityOfferDetailView.vue', 'overseas-master-data/components/CapacityInquiryDialog.vue', 'overseas-master-data/components/CapacityInquiryTable.vue', 'overseas-master-data/components/CapacityOfferSettingsDialog.vue', 'overseas-master-data/pages/TripOperationsView.vue', 'overseas-master-data/components/TripOperationsCharts.vue', 'overseas-master-data/pages/TripDetailView.vue', 'overseas-master-data/components/TripPlanDialog.vue', 'overseas-master-data/pages/DriverDetailView.vue', 'overseas-master-data/components/DriverEditorDialog.vue', 'overseas-master-data/components/DriverLicensePreview.vue', 'master-data/express-companies/pages/ExpressCompaniesView.vue', 'master-data/counterparties/pages/PartnersView.vue', 'fee-items/pages/FeeItemsView.vue', 'warehouses/pages/WarehousesView.vue', 'fba-pricing/carrier-rates/pages/CarrierRatesView.vue', 'fba-pricing/carrier-rates/pages/CarrierRateDetailView.vue', 'downloads/pages/DownloadCenterView.vue', ] const notificationConsumers = [ 'notifications/pages/PlatformNotificationsView.vue', 'notifications/pages/MessageManagementView.vue', 'notifications/pages/MessageTemplatesView.vue', 'notifications/components/MessageComposerDialog.vue', 'notifications/components/TemplateEditorDialog.vue', ] const systemConsumers = ['iam/pages/InternalUsersView.vue', 'iam/pages/RolesView.vue', 'iam/pages/PermissionsView.vue'] const selfOperatedPages = ['self-operated/components/AnalyticsView.vue', 'self-operated/components/InventoryTurnoverView.vue'] const selfOperatedConsumers = [...selfOperatedPages, 'self-operated/components/SkuTrend.vue', 'self-operated/pages/OverviewView.vue'] const consumers = [ ...selfOperatedConsumers, 'overseas-business/components/FleetArrangementSummary.vue', ...notificationConsumers, ...systemConsumers, ...generalCatalogConsumers, ...financeConsumers, ...customerServiceConsumers, ...overseasBusinessConsumers, ...overseasConsumers, ...cargoConsumers, 'customers/pages/CustomersView.vue', 'customers/pages/CustomerDetailView.vue', 'customers/pages/OverseasStorageFeesView.vue', 'customers/components/CustomerBillingRulesPanel.vue', 'unified-orders/components/InboundFeeDetailManager.vue', 'unified-orders/components/OrderDeclarationEditorDialog.vue', 'customers/CustomerOverseasStorageFeePanel.vue', 'unified-orders/pages/UnifiedOrdersView.vue', 'inbounds/core/DropshipPalletPrintPanel.vue', 'inbounds/components/InboundTaskList.vue', 'inbounds/measurement/InboundMeasurementDialog.vue', 'inbounds/measurement/InboundMeasurementPanel.vue', 'inbounds/review/MeasurementReviewPanel.vue', 'inbounds/weight/components/WeightOcrPanel.vue', 'loading-lists/pages/PreloadPlanDetailView.vue', 'loading-lists/components/LoadingListsView.vue', 'loading-lists/components/LoadingListDetailDrawer.vue', 'loading-lists/components/PalletLabelPrintDialog.vue', 'warehouse-resources/locations/pages/LocationsView.vue', 'warehouse-resources/pallets/pages/PalletsView.vue', 'domestic-containers/pages/DomesticContainersView.vue', 'domestic-containers/pages/DomesticContainerInventoryDetailView.vue', 'container-loading/components/ContainerSimulationCreate.vue', 'audit/components/CustomerAuditLogsPanel.vue', ] const moduleCss = ['iam/iamDensity.css', 'finance/financeDensity.css', 'overseas-business/overseasBusinessDensity.css', 'customers/customerDensity.css', 'unified-orders/styles/orderDensity.css', 'inbounds/measurement/measurementDensity.css', 'domestic-containers/pages/domesticContainersSummary.css'] const read = (file: string) => readFileSync(resolve(process.cwd(), 'src', file), 'utf8') const domesticPages = [ { title: '订单列表', path: '/orders', file: 'unified-orders/pages/UnifiedOrdersView.vue' }, { title: '订单测量', path: '/inbounds/measurements', file: 'inbounds/components/InboundTaskList.vue', entry: 'inbounds/pages/InboundMeasurementsView.vue', component: 'InboundTaskList', mode: 'measurement' }, { title: '测量数据审核', path: '/inbounds/measurement-reviews', file: 'inbounds/components/InboundTaskList.vue', entry: 'inbounds/pages/MeasurementReviewsView.vue', component: 'InboundTaskList', mode: 'review' }, { title: '预装清单', path: '/loading-plans', file: 'loading-lists/components/LoadingListsView.vue', entry: 'loading-lists/pages/PreloadPlansView.vue', component: 'LoadingListsView', mode: 'preload' }, { title: '库位管理', path: '/master-data/locations', file: 'warehouse-resources/locations/pages/LocationsView.vue' }, { title: '托盘管理', path: '/master-data/pallets', file: 'warehouse-resources/pallets/pages/PalletsView.vue' }, { title: '整柜业务', path: '/domestic-containers', file: 'domestic-containers/pages/DomesticContainersView.vue' }, { title: '船期日历', path: '/sailing-calendar', file: 'sailing-calendar/pages/SailingCalendarView.vue' }, ] const customerPages = [ { title: '客户列表', path: '/customers', file: 'customers/pages/CustomersView.vue' }, { title: '海外仓阶梯费用', path: '/customers/overseas-storage-fees', file: 'customers/pages/OverseasStorageFeesView.vue' }, ] const densityPages = [...new Set([ ...selfOperatedPages, ...notificationConsumers.filter(file => file.includes('/pages/')), ...systemConsumers, ...generalCatalogConsumers.filter(file => file.includes('/pages/')), ...financeConsumers, ...customerServiceConsumers, ...overseasBusinessConsumers, ...overseasConsumers.filter(file => !file.endsWith('PalletDetailView.vue')), ...cargoConsumers.filter(file => file.includes('/pages/') || ['ContainerOverviewPanel.vue', 'ContainerCustomsPanel.vue', 'ContainerLastLegFeesPanel.vue', 'ContainerFeesPanel.vue', 'OceanBookingDocumentsPanel.vue'].some(name => file.endsWith(name))), ...domesticPages.map(page => page.file), 'loading-lists/pages/PreloadPlanDetailView.vue', 'loading-lists/components/LoadingListDetailDrawer.vue', 'domestic-containers/pages/DomesticContainerInventoryDetailView.vue', ...customerPages.map(page => page.file), 'customers/pages/CustomerDetailView.vue', 'audit/components/CustomerAuditLogsPanel.vue', ])] type TemplateNode = NonNullable['descriptor']['template']>['ast']>['children'][number] type ElementNode = Extract const classesOf = (node: ElementNode, preload = true, dense = preload, region = 'DOMESTIC', workbench = false, embedded = false): string[] => node.props.flatMap(prop => { if (prop.type === 6 && prop.name === 'class') return prop.value?.content.split(/\s+/) ?? [] if (prop.type !== 7 || prop.name !== 'bind' || prop.arg?.type !== 4 || prop.arg.content !== 'class' || prop.exp?.type !== 4 || !prop.exp.content.includes('density')) return [] // Keep visual opt-in and embedding independent from the shared component's density/region modes. const value = new Function('mode', 'isPreload', 'dense', 'isDomestic', 'isOverseas', 'workbench', 'embedded', `return (${prop.exp.content})`)(preload ? 'preload' : 'confirmed', preload, dense, region === 'DOMESTIC', region === 'OVERSEAS', workbench, embedded) as Record const flatten = (value: unknown): string[] => typeof value === 'string' ? value.split(/\s+/) : Array.isArray(value) ? value.flatMap(flatten) : value && typeof value === 'object' ? Object.entries(value).filter(([, enabled]) => enabled).flatMap(([names]) => names.split(/\s+/)) : [] return flatten(value) }) const elementsOf = (source: string, tag?: string): ElementNode[] => { const parsed = parse(source) expect(parsed.errors).toEqual([]) const result: ElementNode[] = [] const visit = (nodes: TemplateNode[]) => { for (const node of nodes) { if (node.type !== 1) continue if (!tag || node.tag === tag) result.push(node) visit(node.children) } } visit(parsed.descriptor.template!.ast!.children) return result } const expectWorkbenchDialogs = (source: string) => { const dialogs = elementsOf(source, 'AppDialog') expect(dialogs.length).toBeGreaterThan(0) for (const dialog of dialogs) { expect(classesOf(dialog), dialog.loc.source.split('>')[0]).toEqual(expect.arrayContaining(['admin-density-dialog', 'admin-workbench', 'is-dialog'])) } return dialogs } const confirmationClasses = (source: string): string[][] => { const script = source.includes(' { if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.expression.getText(ast) === 'ElMessageBox') { const options = node.arguments[2] expect(options && ts.isObjectLiteralExpression(options)).toBe(true) const customClass = options && ts.isObjectLiteralExpression(options) ? options.properties.find(prop => ts.isPropertyAssignment(prop) && prop.name.getText(ast) === 'customClass') : undefined expect(customClass && ts.isPropertyAssignment(customClass) && ts.isStringLiteral(customClass.initializer)).toBe(true) if (customClass && ts.isPropertyAssignment(customClass) && ts.isStringLiteral(customClass.initializer)) result.push(customClass.initializer.text.split(/\s+/)) } ts.forEachChild(node, visit) } visit(ast) return result } const styles = [ ...parse(read('shared/components/BatchImportDialog.vue')).descriptor.styles.map(style => ({ file: 'BatchImportDialog.vue', css: style.content })), ...consumers.flatMap(file => parse(read(`modules/${file}`)).descriptor.styles.map(style => ({ file, css: style.content }))), ...moduleCss.map(file => ({ file, css: read(`modules/${file}`) })), ] const shared = read('styles/adminDensity.css') const tokens = read('styles/adminDensityTokens.css') const cssRoot = (css: string) => { const result = compileStyle({ source: css, filename: 'density.css', id: 'density-contract', trim: false }) expect(result.errors).toEqual([]) return result.rawResult!.root } describe('approved Admin density ownership', () => { it('separates shared list filters with the same 5px radius across density themes', () => { const declarations = (source: string, selector: string) => { const values: Record = {} cssRoot(source).walkRules(rule => { if (rule.selector === selector) rule.walkDecls(decl => { values[decl.prop] = decl.value }) }) return values } const foundation = declarations(read('styles/foundation.css'), ':root') expect(foundation['--cc-list-query-radius']).toBe('5px') expect(foundation['--cc-list-query-gap']).toBe('16px') expect(foundation['--cc-list-table-header-bg']).toBe('#edf4ff') expect(foundation['--cc-list-table-header-text']).toBe('#284b78') expect(declarations(shared, ':is(.admin-density, .admin-density-dialog, .admin-density-drawer) .el-table th.el-table__cell')).toEqual({ background: 'var(--cc-list-table-header-bg)', color: 'var(--cc-list-table-header-text)', }) const table = parse(read('shared/components/DataTable.vue')).descriptor.styles.map(style => style.content).join('\n') expect(declarations(table, '.data-table > .data-table__filters')).toMatchObject({ 'margin-bottom': 'var(--cc-list-query-gap, 16px)', 'border-radius': 'var(--cc-list-query-radius, 5px)', border: '1px solid var(--list-card-border, var(--cc-border))', }) const headerSelector = '.data-table > .data-table__toolbar:has(+ .data-table__filters)' expect(declarations(table, headerSelector)['border-bottom']).toBe('0') const fixture = document.createElement('div') fixture.innerHTML = '
' expect(fixture.querySelector('#with-filters')!.matches(headerSelector)).toBe(true) expect(fixture.querySelector('#without-filters')!.matches(headerSelector)).toBe(false) expect(declarations(shared, '.admin-density .query-toolbar')['border-radius']).toBe('var(--cc-list-query-radius, 5px)') expect(declarations(shared, '.admin-density .surface-panel')['border-radius']).toBe('var(--admin-radius, 4px)') const query = parse(read('shared/components/QueryToolbar.vue')).descriptor.styles.map(style => style.content).join('\n') expect(declarations(query, '.query-toolbar')['border-radius']).toBe('var(--cc-list-query-radius, 5px)') }) it('keeps sailing cutoff controls compact and delegates editor borders to the shared opt-in', () => { const view = parse(read('modules/sailing-calendar/pages/SailingCalendarView.vue')).descriptor const rules = new Map>() for (const style of view.styles) cssRoot(style.content).walkRules(rule => { const values: Record = {} rule.walkDecls(decl => { values[decl.prop] = decl.value }) rules.set(rule.selector, values) }) expect(rules.get('.sailing-entry-label')).toEqual({ width: '116px' }) expect(rules.get('.cutoff-presets')).toEqual({ flex: '0 0 auto' }) expect(rules.get('.sailing-date-editor')).toMatchObject({ width: '280px', 'max-width': '100%' }) expect(rules.get('.cutoff-rule-row, .cutoff-time-row')).toMatchObject({ 'flex-wrap': 'wrap' }) expect(view.template!.content).toContain('density-table-editor density-table-editor--outlined cutoff-days-editor') expect(view.template!.content).toContain('density-table-editor--outlined cutoff-time-editor') expect(view.template!.content).toContain('role="status" aria-live="polite"') expect(view.styles.map(style => style.content).join('\n')).not.toContain('--el-input-border-color') }) it('outlines only the three opted-in customer billing editors without overriding native focus or disabled styles', () => { const rules = new Map>() cssRoot(shared).walkRules(rule => { const values: Record = {} rule.walkDecls(decl => { values[decl.prop] = decl.value }) rules.set(rule.selector, values) }) expect(rules.get(':is(.admin-density, .admin-density-dialog) .density-table--editable .density-table-editor--outlined .el-input')).toEqual({ '--el-input-border-color': 'var(--el-color-primary-light-5)', '--el-input-hover-border-color': 'var(--el-color-primary)', '--el-input-bg-color': 'var(--admin-surface)', }) const ast = parse(read('modules/unified-orders/pages/UnifiedOrdersView.vue')).descriptor.template!.ast! const inputIds: string[] = [] const visit = (nodes: typeof ast.children) => { for (const node of nodes) { if (node.type !== 1) continue if (classesOf(node).includes('density-table-editor--outlined')) { const input = node.children.find(child => child.type === 1 && child.tag === 'el-input-number') as ElementNode | undefined expect(input).toBeDefined() const id = input!.props.find(prop => prop.type === 6 && prop.name === 'id') inputIds.push(id?.type === 6 ? id.value?.content || '' : '') } visit(node.children) } } visit(ast.children) expect(inputIds).toEqual(['fee-confirm-weight', 'fee-confirm-volume', 'fee-confirm-unit-price']) }) it('allows container filters and their final action group to wrap by available width', () => { const source = read('modules/container-loading/pages/ContainersView.vue') for (const style of parse(source).descriptor.styles) { cssRoot(style.content).walkRules(rule => { if (!rule.selector.includes('.query-toolbar__tail')) return rule.walkDecls(decl => { if (decl.prop === 'width') expect(decl.value).not.toBe('100%') if (decl.prop === 'flex') expect(decl.value).not.toBe('0 0 100%') }) }) } }) it('keeps declaration order fields on the continuous cell-editor baseline', () => { const source = read('modules/unified-orders/components/OrderDeclarationEditorDialog.vue') const orderFields = source.slice(source.indexOf('') + ''.length) expect(orderFields).toContain('class="order-fields density-form"') expect(orderFields).toContain('density-table density-table--editable') expect(orderFields).not.toMatch(/') expect(orderFields).not.toContain(' { const tokenValues: Record = {} cssRoot(tokens).walkDecls(decl => { tokenValues[decl.prop] = decl.value }) const rules = new Map>() cssRoot(shared).walkRules(rule => { const values: Record = {} rule.walkDecls(decl => { values[decl.prop] = decl.value }) rules.set(rule.selector, values) }) for (const [tone, color] of [['summary', '#f4fbf8'], ['source', '#f7f9ff'], ['work', '#fffbf4']]) { expect(tokenValues[`--admin-dialog-${tone}-bg`]).toBe(color) expect(rules.get(`:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section[data-tone="${tone}"]`)).toMatchObject({ background: `var(--admin-dialog-${tone}-bg)`, 'border-color': `var(--admin-dialog-${tone}-border)`, }) } expect(rules.get(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section')).not.toHaveProperty('height') expect(rules.get(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-note')).toMatchObject({ color: 'var(--cc-muted)', 'font-size': 'var(--admin-font-caption)' }) expect(rules.get(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--editor)) .density-actions--footer')).toMatchObject({ 'justify-content': 'flex-end', 'flex-wrap': 'wrap', gap: 'var(--admin-space-3)' }) for (const [selector, declarations] of rules) { if (selector.includes('density-dialog-section')) expect(Object.keys(declarations).filter(name => name.startsWith('border-left')), selector).toEqual([]) } const editorRule = rules.get(':is(.admin-density, .admin-density-dialog) .density-table--editable :is(.el-input, .el-textarea)') expect(editorRule).toMatchObject({ '--el-input-border-color': 'transparent', '--el-input-hover-border-color': 'var(--admin-table-border)' }) expect(editorRule).not.toHaveProperty('--el-input-focus-border-color') expect(rules.get(':is(.admin-density, .admin-density-dialog) .density-table--editable .el-form-item')).toMatchObject({ 'margin-bottom': '0' }) expect(rules.get(':is(.admin-density, .admin-density-dialog) .density-table--editable .el-form-item.is-error')).toMatchObject({ 'margin-bottom': 'var(--admin-space-5)' }) for (const selector of rules.keys()) if (selector.includes('density-dialog-section')) expect(selector.startsWith(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) ')).toBe(true) const measured = read('modules/inbounds/measurement/measurementDensity.css') expect(measured).not.toMatch(/border-left(?:-color|-width)?\s*:/) for (const color of ['#f4fbf8', '#f7f9ff', '#fffbf4']) expect(measured).not.toContain(color) for (const file of ['unified-orders/pages/UnifiedOrdersView.vue', 'unified-orders/components/OrderDeclarationEditorDialog.vue', 'unified-orders/components/InboundFeeDetailManager.vue', 'inbounds/measurement/InboundMeasurementPanel.vue', 'inbounds/weight/components/WeightOcrPanel.vue', 'domestic-containers/pages/DomesticContainersView.vue', 'sailing-calendar/pages/SailingCalendarView.vue', 'ocean-bookings/components/OceanBookingPrintDialog.vue', 'ocean-bookings/pages/OceanBookingDetailView.vue', 'trucking/components/TruckAttachmentDialog.vue', 'container-loading/pages/ContainersView.vue', 'container-loading/pages/ContainerDetailView.vue', 'container-loading/components/OceanBookingLinkDialog.vue']) { const source = read('modules/' + file) expect(parse(source).errors, file).toEqual([]) const ast = parse(source).descriptor.template!.ast! const visit = (nodes: typeof ast.children, inDialog = false) => { for (const node of nodes) { if (node.type !== 1) continue const inside = inDialog || ['AppDialog', 'el-dialog'].includes(node.tag) if (classesOf(node).some(name => ['density-dialog-section', 'density-table--editable'].includes(name))) expect(inside, file + ': sections and cell editors belong to an explicit dialog').toBe(true) visit(node.children, inside) } } visit(ast.children) } }) it('opts the audit detail drawer into the same palette without styling unselected drawers or clipping snapshots', () => { const rules = new Map>() cssRoot(shared).walkRules(rule => { const declarations: Record = {} rule.walkDecls(decl => { declarations[decl.prop] = decl.value }) rules.set(rule.selector, declarations) }) const snapshot = rules.get(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-code-block')! expect(snapshot).toMatchObject({ 'white-space': 'pre-wrap', 'overflow-wrap': 'anywhere' }) for (const key of ['height', 'min-height', 'max-height', 'overflow', 'overflow-y']) expect(snapshot).not.toHaveProperty(key) for (const selector of rules.keys()) { if (selector.includes('admin-density-drawer') && (selector.includes('density-dialog-section') || selector.includes('density-table'))) expect(selector).toContain('.admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)') if (selector.includes('.el-drawer__header') || selector.includes('.el-drawer__body')) expect(selector).not.toContain('admin-density-drawer--detail') } const audit = parse(read('modules/audit/components/CustomerAuditLogsPanel.vue')).descriptor const ast = audit.template!.ast! const visit = (nodes: typeof ast.children, inside = false) => { for (const node of nodes) { if (node.type !== 1) continue const inDrawer = inside || (node.tag === 'AppDrawer' && classesOf(node).includes('admin-density-drawer--detail') && classesOf(node).includes('admin-density-drawer')) if (classesOf(node).some(value => ['density-dialog-section', 'density-table', 'density-code-block'].includes(value))) expect(inDrawer).toBe(true) visit(node.children, inDrawer) } } visit(audit.template!.ast!.children) for (const style of audit.styles) expect(style.content).not.toMatch(/snapshot[^{}]*\{[^}]*max-height|border-left:/) }) it('keeps review presentation in the shared dialog and resource editors on the shared form scale', () => { const review = parse(read('modules/inbounds/review/MeasurementReviewPanel.vue')).descriptor for (const style of review.styles) { expect(style.content).not.toContain('linear-gradient') expect(style.content).not.toMatch(/box-shadow:\s*inset/) } const host = parse(read('modules/inbounds/components/InboundTaskList.vue')).descriptor.template!.ast! let reviewHostFound = false const visit = (nodes: typeof host.children) => { for (const node of nodes) { if (node.type !== 1) continue if (classesOf(node).includes('inbound-task-dialog')) { expect(node.tag).toBe('AppDialog') expect(classesOf(node)).toContain('admin-density-dialog') expect(node.props.some(prop => prop.type === 6 && prop.name === 'fullscreenable')).toBe(true) expect(node.children.some(child => child.type === 1 && child.props.some(prop => prop.type === 7 && prop.name === 'slot' && prop.arg?.type === 4 && prop.arg.content === 'footer'))).toBe(true) reviewHostFound = true } visit(node.children) } } visit(host.children) expect(reviewHostFound).toBe(true) for (const file of ['locations/pages/LocationsView.vue', 'pallets/pages/PalletsView.vue']) { const source = read('modules/warehouse-resources/' + file) expect(source).toContain('class="editor-form density-form density-form--flush-end"') for (const style of parse(source).descriptor.styles) { expect(style.content).not.toContain('.editor-form :deep(.el-form-item__label)') expect(style.content).not.toContain('.editor-form :deep(.el-form-item)') } } expect(shared).toContain('.admin-density-dialog .density-form--flush-end > .el-form-item:last-child') expect(shared).not.toContain('.admin-density-dialog .density-form > .el-form-item:last-child') }) it('keeps batch approval overflow tooltips single-line without forcing its headers or error tags to truncate', () => { const view = parse(read('modules/unified-orders/pages/UnifiedOrdersView.vue')).descriptor.template!.ast! const tooltipColumns: string[] = [] const visit = (nodes: typeof view.children, inBatch = false) => { for (const node of nodes) { if (node.type !== 1) continue const inside = inBatch || classesOf(node).includes('batch-approval-table') if (inside && node.tag === 'el-table-column') { const label = node.props.find(prop => prop.type === 6 && prop.name === 'label') if (label?.type === 6 && ['客户', '中文品名'].includes(label.value?.content || '')) { expect(node.props.some(prop => prop.type === 6 && prop.name === 'show-overflow-tooltip')).toBe(true) tooltipColumns.push(label.value!.content) } } visit(node.children, inside) } } visit(view.children) expect(tooltipColumns).toEqual(['客户', '中文品名']) const rules = new Map>() cssRoot(read('modules/unified-orders/styles/orderDensity.css')).walkRules(rule => { const declarations: Record = {} rule.walkDecls(decl => { declarations[decl.prop] = decl.value }) rules.set(rule.selector, declarations) }) expect(rules.has('.admin-density-dialog.el-dialog .batch-approval-table .cell')).toBe(false) expect(rules.get('.admin-density-dialog.el-dialog .batch-approval-table td .cell.el-tooltip')).toMatchObject({ 'white-space': 'nowrap', 'text-overflow': 'ellipsis', overflow: 'hidden', 'overflow-wrap': 'normal', }) expect(rules.get('.admin-density-dialog.el-dialog .batch-approval-table .el-tag')?.['white-space']).toBe('normal') }) it('opts transfer approval into the shared centered confirmation appearance', () => { const dialog = read('modules/overseas-warehouse/components/WarehouseTransferReviewDialog.vue') const css = read('styles/adminDensity.css') const [frame] = expectWorkbenchDialogs(dialog) expect(classesOf(frame!)).toContain('density-confirm-dialog') expect(dialog).toContain('align-center') expect(css).toContain('.admin-density-dialog.density-confirm-dialog.el-dialog:not(.is-fullscreen) { margin: auto; }') expect(css).toContain('.admin-density-dialog.density-confirm-dialog.el-dialog > .el-dialog__header { border-bottom: 0; }') expect(css).toContain('.admin-density-dialog.density-confirm-dialog.el-dialog > .el-dialog__footer { border-top: 0;') }) it('keeps the simplified calendar in two columns with bounded previews and scrollable daily details', () => { const view = parse(read('modules/appointment-calendar/AppointmentCalendarView.vue')).descriptor const panel = parse(read('modules/appointment-calendar/AppointmentCalendarPanel.vue')).descriptor expect(view.template!.content).toContain(' { const values: Record = {} cssRoot(panel.styles.map(style => style.content).join('\n')).walkRules(selector, rule => { if (rule.parent?.type !== 'root') return rule.walkDecls(decl => { values[decl.prop] = decl.value }) }) return values } expect(declarations('.appointment-calendar')['grid-template-columns']).toBe('minmax(0, 1fr) 360px') expect(declarations('.calendar-day').height).toBe('146px') expect(declarations('.week-grid .calendar-day').height).toBe('270px') expect(declarations('.detail-events')['overflow-y']).toBe('auto') expect(declarations('.detail-event dd')['overflow-wrap']).toBe('anywhere') expect(panel.template!.content).toContain('.slice(0, 3)') expect(panel.template!.content).toContain('v-if="canCreate"') expect(panel.template!.content).toContain('新增预约') }) it('keeps calendar creation, its two consumers and arrival editing on shared dialog sections', () => { const view = parse(read('modules/appointment-calendar/AppointmentCalendarView.vue')).descriptor const create = parse(read('modules/appointment-calendar/AppointmentCreatePanel.vue')).descriptor const work = parse(read('modules/appointment-calendar/WorkCreateAppointmentForm.vue')).descriptor expect(view.scriptSetup!.content).toContain("import '@/styles/adminDensity.css'") const dialogs = view.template!.content.match(/]+>/g)! expect(dialogs).toHaveLength(3) for (const dialog of dialogs) { expect(dialog).toContain('admin-density-dialog'); expect(dialog).toContain('fullscreenable') } expect(view.template!.content).toContain('density-table density-table--editable') expect(view.template!.content).toContain('label-position="left" label-width="150px"') expect(view.template!.content).toContain(' style.content).join('\n')).not.toContain('arrival-field') expect(create.template!.content).toContain('aria-label="预约保存回执"') expect(create.template!.content).not.toContain(' { for (const file of [ 'unified-orders/pages/UnifiedOrdersView.vue', 'overseas-warehouse/pages/LastMileOrdersView.vue', 'overseas-master-data/pages/VehiclesView.vue', 'suppliers/pages/SuppliersView.vue', 'domestic-containers/pages/DomesticContainersView.vue', 'container-loading/pages/ContainersView.vue', 'appointment-calendar/AppointmentCargoTabs.vue', ]) { const page = parse(read(`modules/${file}`)).descriptor expect(page.template!.content).toMatch(/class="[^"]*density-status-tabs[^"]*" role="tablist"/) expect(page.styles.map((style) => style.content).join('\n')).not.toMatch(/\.(status-tabs?|supplier-tabs|container-scope-tabs)\s*[{:.]/) } const returnTabs = elementsOf(read('modules/overseas-warehouse/pages/ReturnInventoryManagementView.vue'), 'el-tabs') expect(returnTabs.some(node => ['density-element-tabs', 'density-status-tabs'].every(name => classesOf(node).includes(name)))).toBe(true) const selectors: string[] = [] cssRoot(shared).walkRules((rule) => { selectors.push(...rule.selectors) }) expect(selectors).toContain('.admin-density .density-status-tabs > button.is-active::after') expect(selectors).toContain('.admin-density .density-status-tabs > button:focus-visible') const element = '.admin-density .density-element-tabs.density-status-tabs > .el-tabs__header .el-tabs__nav .el-tabs__item' const rules: string[][] = [] cssRoot(shared).walkRules((rule) => { rules.push(rule.selectors) }) for (const suffix of ['', '::after', ':hover', ':focus-visible', '.is-active', '.is-active::after']) { expect(rules.some((rule) => rule.includes('.admin-density .density-status-tabs > button' + suffix) && rule.includes(element + suffix))).toBe(true) } }) it('keeps dashboard visualizations outside detail-field table styling', () => { const dashboard = read('modules/dashboard/pages/DashboardView.vue') const metrics = elementsOf(dashboard, 'div').filter(node => classesOf(node).includes('dashboard-metrics')) expect(metrics).toHaveLength(1) const cards = elementsOf(dashboard, 'article').filter(node => classesOf(node).includes('dashboard-metric')) expect(cards).toHaveLength(1) expect(classesOf(cards[0]!)).toEqual(expect.arrayContaining(['workbench-metric', 'workbench-tone'])) const icon = elementsOf(dashboard, 'span').find(node => classesOf(node).includes('workbench-icon')) expect(icon).toBeDefined() expect(icon!.children.some(node => node.type === 1 && node.tag === 'component')).toBe(true) expect(dashboard).not.toContain('density-fields') }) it('registers the self-operated dashboard with an explicit density root and chart layout', () => { const source = read('modules/self-operated/pages/OverviewView.vue') expect(source).toContain("@/styles/adminDensity.css") expect(source).toContain('') expect(source).toContain('class="dashboard-kpis"') expect(source).toContain('class="dashboard-main-charts"') expect(source).not.toContain('density-fields') expect(getNavigationLeaves(adminNavigation).map(node => node.path)).toContain('/self-operated/overview') }) it('opts application-shell confirmations in even before a business page loads', () => { const entries = ['app/layouts/AppLayout.vue', 'app/components/WorkspaceTabs.vue', 'app/router/index.ts', 'main.ts'] let count = 0 for (const file of entries) { const source = read(file) expect(source, file).toContain("@/styles/adminDensity.css") const script = file.endsWith('.vue') ? parse(source).descriptor.scriptSetup!.content : source const tree = ts.createSourceFile(file, script, ts.ScriptTarget.Latest, true) const visit = (node: ts.Node) => { if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.expression.getText(tree) === 'ElMessageBox') { count += 1 const options = node.arguments[2] expect(options && ts.isObjectLiteralExpression(options), file).toBe(true) const customClass = options && ts.isObjectLiteralExpression(options) ? options.properties.find(prop => ts.isPropertyAssignment(prop) && prop.name.getText(tree) === 'customClass') : undefined expect(customClass && ts.isPropertyAssignment(customClass) && ts.isStringLiteral(customClass.initializer) && customClass.initializer.text, file).toBe('admin-density-confirm') } ts.forEachChild(node, visit) } visit(tree) } expect(count).toBe(5) }) it('checks dialog roots along actual static and dynamic Vue imports from approved consumers', () => { const pending = consumers.map(file => resolve(process.cwd(), 'src/modules', file)) const seen = new Set() while (pending.length) { const file = pending.shift()! if (seen.has(file)) continue seen.add(file) const descriptor = parse(readFileSync(file, 'utf8')).descriptor const enqueue = (reference: string) => { if (!reference.endsWith('.vue')) return if (reference.startsWith('@/')) pending.push(resolve(process.cwd(), 'src', reference.slice(2))) else if (reference.startsWith('.')) pending.push(resolve(dirname(file), reference)) } for (const script of [descriptor.script, descriptor.scriptSetup]) { if (!script) continue const ast = ts.createSourceFile(file, script.content, ts.ScriptTarget.Latest, true) const visitImports = (node: ts.Node) => { if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.expression.getText(ast) === 'ElMessageBox') { expect(node.arguments[2]?.getText(ast), file + ': reachable confirmation').toContain('admin-density-confirm') } if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) enqueue(node.moduleSpecifier.text) if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) { const reference = node.arguments[0] if (reference && ts.isStringLiteral(reference)) enqueue(reference.text) } ts.forEachChild(node, visitImports) } visitImports(ast) } if (['AppDialog.vue', 'AppDrawer.vue', 'BatchImportDialog.vue'].some(name => file.endsWith(name))) continue const visitTemplate = (nodes: TemplateNode[]) => { for (const node of nodes) { if (node.type !== 1) continue if (['AppDialog', 'el-dialog', 'BatchImportDialog'].includes(node.tag)) expect(classesOf(node), `${file}: reachable modal`).toContain('admin-density-dialog') if (['AppDrawer', 'el-drawer'].includes(node.tag)) expect(classesOf(node), `${file}: reachable drawer`).toContain('admin-density-drawer') visitTemplate(node.children) } } if (descriptor.template?.ast) visitTemplate(descriptor.template.ast.children) } }) it('keeps warehouse editors and import results on shared sections and cell editors', () => { const source = read('modules/warehouses/pages/WarehousesView.vue') expect(source).toContain('class="density-form density-dialog-sections"') expect(source).toContain('density-table density-table--editable warehouse-editor-table') const importer = expectWorkbenchDialogs(source).find(node => classesOf(node).includes('warehouse-import-dialog')) expect(importer).toBeDefined() expect(source).toContain('class="density-table warehouse-import-summary"') expect(source).toContain('class="density-actions density-actions--footer"') expect(source).not.toContain(' { const role = read('modules/iam/pages/RolesView.vue') const permission = read('modules/iam/pages/PermissionsView.vue') for (const source of [role, permission]) { expect(source).toContain('fullscreenable :confirm-loading="saving"') expect(source).toContain('density-table--editable') expect(source).toContain('data-tone="work"') expect(source).not.toContain(' style.content).join('') expect(css).not.toContain('el-dialog__body') expect(css).not.toContain('border-left') } expect(role).toContain('data-tone="source"') expect(role).not.toContain('max-height: 380px') expect(role).toContain('align-items: start') expect(permission).not.toContain('class="form-callout"') }) it('keeps internal user forms on shared cells and native fullscreen with original geometry', () => { const source = read('modules/iam/pages/InternalUsersView.vue') expect(source).toContain('class="density-form density-dialog-sections"') expect(source).toContain('class="density-table density-table--editable user-editor-table"') expect(source).toContain('data-tone="work"') expect(source).toContain('fullscreenable destroy-on-close') expect(source).toContain('width="min(650px, calc(100vw - 28px))"') expect(source).not.toContain(' style.content).join('') expect(css).not.toMatch(/font-size|background|border|el-dialog__body|el-dialog__footer/) expect(css).toContain(':not(.is-fullscreen)') }) it('opts fee item editing into drawer sections and existing cell editors without changing other drawers', () => { const source = read('modules/fee-items/pages/FeeItemsView.vue') const ast = parse(source).descriptor.template!.ast! let editorFound = false const visit = (nodes: typeof ast.children, inEditor = false) => { for (const node of nodes) { if (node.type !== 1) continue const inside = inEditor || node.tag === 'AppDrawer' && classesOf(node).includes('admin-density-drawer--editor') if (node.tag === 'AppDrawer') { editorFound = true for (const name of ['admin-density', 'admin-density-drawer', 'admin-density-drawer--editor']) expect(classesOf(node)).toContain(name) } if (classesOf(node).some(name => ['density-dialog-section', 'density-table--editable'].includes(name))) expect(inside).toBe(true) visit(node.children, inside) } } visit(ast.children) expect(editorFound).toBe(true) expect(source).not.toContain(' style.content).join('') expect(css).not.toContain('direction-grid') expect(css).not.toContain('delete-message') }) it('keeps download task detail on shared facts, error and footer primitives', () => { const source = read('modules/downloads/pages/DownloadCenterView.vue') const dialog = source.slice(source.indexOf('')) expectWorkbenchDialogs(source) expect(dialog).toContain('fullscreenable') expect(dialog).toContain('data-tone="summary"') expect(dialog).toContain('class="task-detail-grid density-fields"') expect(dialog).toContain(' style.content).join('') expect(css).not.toMatch(/\.task-(?:detail-summary|failure)\b|\.detail-load-error\b/) }) it('keeps ship price book upload compact with shared table and confirmation styles', () => { const list = read('modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue') const detail = read('modules/fba-pricing/carrier-rates/pages/CarrierRateDetailView.vue') const dialog = list.slice(list.indexOf('')) expectWorkbenchDialogs(list) expect(dialog).toContain('width="min(560px, calc(100vw - 28px))"') expect(dialog).toContain(':confirm-loading="uploading"') expect(dialog).toContain('class="density-table density-table--editable"') expect(dialog).toContain('label-position="left"') expect(dialog).toContain('class="density-dialog-note"') expect(dialog).not.toContain('upload-scope-field') expect(list).not.toContain('.upload-file-name strong') for (const source of [list, detail]) expect(source).toContain("customClass: 'admin-density-confirm'") }) it('uses shared frames, continuous tables and readonly facts for fleet quote dialogs', () => { const editor = read('modules/overseas-master-data/components/FleetQuoteEditor.vue') const importer = read('modules/overseas-master-data/components/FleetQuoteImport.vue') for (const source of [editor, importer]) { const [dialog] = expectWorkbenchDialogs(source) expect(dialog!.props.some(prop => prop.type === 6 && prop.name === 'fullscreenable')).toBe(true) expect(dialog!.props.some(prop => prop.type === 7 && prop.name === 'bind' && prop.arg?.type === 4 && prop.arg.content === 'confirm-loading' && prop.exp?.type === 4 && prop.exp.content === 'saving')).toBe(true) expect(source).toContain('density-table--editable') expect(source).toContain('data-tone="work"') expect(source).toContain('data-tone="source"') expect(source).toContain('class="density-actions density-actions--footer"') expect(source).not.toContain('label-position="top"') const css = parse(source).descriptor.styles.map(style => style.content).join('') expect(css).not.toMatch(/font-size|background|border|el-dialog__body|max-height/) } expect(editor).toContain('v-if="readonly"') expect(editor).toContain('class="density-fields fleet-quote-facts"') const confirmations = confirmationClasses(read('modules/overseas-master-data/pages/FleetQuotesView.vue')) expect(confirmations.length).toBeGreaterThan(0) for (const classes of confirmations) expect(classes).toContain('admin-density-confirm') }) it('keeps pricing dialogs on shared tables and sections without local form paint', () => { const rate = read('modules/overseas-master-data/pages/ExchangeRatesView.vue') const quote = read('modules/overseas-master-data/pages/OverseasQuotesView.vue') for (const source of [rate, quote]) { const dialogs = expectWorkbenchDialogs(source) expect(dialogs.length).toBeGreaterThan(0) for (const dialog of dialogs) { expect(dialog.loc.source).not.toContain('label-position="top"') expect(dialog.loc.source).not.toContain('#header') } expect(source).toContain('density-table--editable') expect(source).not.toContain('line-grid') expect(source).not.toContain('form-grid') } expect(quote.match(/fullscreenable/g)).toHaveLength(2) expect(quote).toContain('data-tone="summary"') expect(quote).toContain('data-tone="source"') expect(quote).toContain('data-tone="work"') const css = parse(quote).descriptor.styles.map(style => style.content).join('') expect(css).not.toMatch(/font-size|background|border|el-dialog__body|max-height/) }) it('keeps the supplier license dialog styled at its own teleported root', () => { const source = read('modules/suppliers/components/SupplierLicenseDialog.vue') expectWorkbenchDialogs(source) expect(source).toContain('width="min(560px, calc(100vw - 24px))"') expect(source).toContain('aria-label="营业执照信息"') const sections = elementsOf(source, 'section') expect(sections).toHaveLength(1) expect(classesOf(sections[0]!)).toEqual(expect.arrayContaining(['density-dialog-section', 'workbench-evidence'])) expect(sections[0]!.props.some(prop => prop.type === 6 && prop.name === 'data-tone' && prop.value?.content === 'source')).toBe(true) expect(source).toContain('class="density-table license-info-table"') expect(source).not.toContain('license-card') expect(source).not.toContain('box-shadow') }) it('uses identical authored tab paint for native domestic and Element overseas tabs', () => { const element = '.admin-density .density-element-tabs > .el-tabs__header .el-tabs__nav .el-tabs__item' const rules: string[][] = [] cssRoot(shared).walkRules(rule => { rules.push(rule.selectors) }) for (const [nativeSuffix, elementSuffix] of [['', ''], ['::after', '::after'], [':hover', ':hover'], ['.active', '.is-active'], ['.active::after', '.is-active::after'], [':focus-visible', ':focus-visible']]) { expect(rules.some(selectors => selectors.includes('.admin-density .density-tabs > button' + nativeSuffix) && selectors.includes(element + elementSuffix))).toBe(true) } const domestic = read('modules/unified-orders/pages/UnifiedOrdersView.vue') const overseasShell = read('modules/overseas-warehouse/components/OverseasDetailShell.vue') for (const className of ['density-detail-header', 'density-detail-heading', 'density-detail-title']) { expect(domestic).toContain(className) expect(overseasShell).toContain(className) } expect(domestic).toContain('density-detail-overview') expect(read('modules/overseas-warehouse/pages/LastMileOrderDetailView.vue')).toContain('density-detail-overview') }) it('covers the approved overseas pages across the latest menu groups, details and both return inventory modes', () => { const paths = getNavigationLeaves(adminNavigation).map(node => node.path) expect(paths).toEqual(expect.arrayContaining(['last-mile-orders', 'inbound-orders', 'locations', 'dispatches', 'outbounds', 'delivery-orders', 'relabel-orders', 'pallets', 'inventory', 'return-inventory', 'return-cartons'].map(path => '/overseas-warehouse/' + path))) const routes = read('modules/overseas-warehouse/routes.ts') for (const mode of ['inventory', 'cartons']) expect(routes).toContain("mode: '" + mode + "'") for (const file of overseasConsumers) { const source = read('modules/' + file) expect(source, file).toContain('@/styles/adminDensity.css') expect(parse(source).errors, file).toEqual([]) if (file.endsWith('DetailView.vue') && !/OutboundDetail|DeliveryOrderDetail/.test(file)) { const root = parse(source).descriptor.template!.ast!.children.find(node => node.type === 1) as ElementNode expect(root.tag).toMatch(/^Overseas(?:DetailShell|ReadOnlyDetail)$/) expect(root.props.some(prop => prop.type === 6 && prop.name === 'dense')).toBe(true) expect(classesOf(root)).toContain('admin-density') } } }) it('keeps contained overseas tab content and external cargo tab bodies separate', () => { expect(shared).toContain('.density-element-tabs:not(.density-contained-tabs) > .el-tabs__content { display: none; }') for (const file of ['LastMileOrderDetailView', 'DeliveryOrderDetailView']) { const source = read('modules/overseas-warehouse/pages/' + file + '.vue') const ast = parse(source).descriptor.template!.ast! let panes = 0 const visit = (nodes: typeof ast.children) => { for (const node of nodes) { if (node.type !== 1) continue if (node.tag === 'el-tabs') expect(classesOf(node)).toContain('density-contained-tabs') if (node.tag === 'el-tab-pane') { panes++ expect(node.children.some(child => child.type === 1 && child.tag !== 'template'), file + ': pane retains body').toBe(true) expect(node.loc.source).toContain('density-tab-icon') } visit(node.children) } } visit(ast.children) expect(panes).toBe(file === 'LastMileOrderDetailView' ? 7 : 4) } }) it('opts shared overseas detail and relationship wrappers in only at approved callers', () => { for (const file of ['modules/overseas-warehouse/components/OverseasDetailShell.vue', 'modules/overseas-warehouse/components/OverseasReadOnlyDetail.vue', 'shared/components/InboundCodeRelationshipDialog.vue']) { const source = read(file) expect(source).toContain('dense?: boolean') const ast = parse(source).descriptor.template!.ast! const root = ast.children.find(node => node.type === 1) as ElementNode for (const workbench of [false, true]) { expect(classesOf(root, false, true, 'DOMESTIC', workbench).some(name => name.startsWith('admin-density')), file).toBe(true) expect(classesOf(root, false, false, 'DOMESTIC', workbench).some(name => name.startsWith('admin-density')), file).toBe(false) if (file.endsWith('OverseasDetailShell.vue')) { for (const dense of [false, true]) expect(classesOf(root, false, dense, 'DOMESTIC', workbench).includes('admin-workbench')).toBe(workbench) } } } expect(read('modules/overseas-warehouse/components/OverseasReadOnlyDetail.vue')).toContain(':dense="dense"') expect(read('modules/overseas-warehouse/components/OverseasReadOnlyDetail.vue')).toContain(':workbench="workbench"') expect(read('modules/overseas-warehouse/pages/InventoryTransactionsView.vue')).toContain(' { const navigation = read('core/navigation/adminNavigation.ts') for (const path of ['/ocean-bookings', '/trucking', '/container-loading/domestic', '/loading-lists', '/loading-plans']) expect(navigation).toContain(`path: '${path}'`) for (const [prefix, workbench] of [['Domestic', 'DOMESTIC'], ['Overseas', 'OVERSEAS']]) { for (const [suffix, component] of [['ContainersView', 'ContainersView'], ['ContainerDetailView', 'ContainerDetailView']]) { expect(read(`modules/container-loading/pages/${prefix}${suffix}.vue`)).toContain(suffix === 'ContainersView' ? ` node.type === 1) as ElementNode expect(classesOf(root, false, true, workbench)).toContain('admin-density') } } const detail = read('modules/container-loading/pages/ContainerDetailView.vue') for (const component of ['CarrierScheduleQueryPanel', 'LoadingListDetailDrawer', 'PalletLabelPrintDialog']) expect(detail).toContain(`<${component} dense`) expect(read('modules/container-loading/pages/ContainersView.vue')).toContain(' { for (const file of ['loading-lists/components/LoadingListDetailDrawer.vue', 'port-schedules/components/CarrierScheduleQueryPanel.vue']) { const ast = parse(read(`modules/${file}`)).descriptor.template!.ast! const root = ast.children.find(node => node.type === 1) as ElementNode for (const embedded of [false, true]) { expect(classesOf(root, false, true, 'DOMESTIC', false, embedded)).toContain('admin-density') expect(classesOf(root, false, false, 'DOMESTIC', false, embedded)).not.toContain('admin-density') if (file.includes('LoadingListDetail')) { expect(classesOf(root, false, true, 'DOMESTIC', false, embedded).includes('workbench-panel')).toBe(!embedded) expect(classesOf(root, false, true, 'DOMESTIC', false, embedded).includes('is-compact')).toBe(!embedded) } } if (file.includes('CarrierSchedule')) { const dialog = root.children.find(node => node.type === 1 && node.tag === 'AppDialog') as ElementNode expect(classesOf(dialog, false, true)).toContain('admin-density-dialog') expect(classesOf(dialog, false, false)).not.toContain('admin-density-dialog') } } }) it('keeps cargo native tables, row actions and detail tabs connected to shared styles', () => { for (const file of cargoConsumers) { const ast = parse(read(`modules/${file}`)).descriptor.template!.ast! const visit = (nodes: typeof ast.children, inCell = false, insideDensity = false, insideEditor = false) => { for (const node of nodes) { if (node.type !== 1) continue const classes = classesOf(node) const enabled = insideDensity || classes.some(name => ['admin-density', 'admin-density-dialog'].includes(name)) const editable = insideEditor || classes.includes('density-table--editable') if (node.tag === 'table') { expect(classes, `${file}: native table`).toContain('density-table') expect(insideDensity, `${file}: native table ancestor`).toBe(true) } if (node.tag === 'el-tabs') expect(classes).toContain('density-element-tabs') if (inCell && !editable && node.tag === 'el-button') { expect(classes, `${file}: native row action`).toContain('density-action') expect(node.children.some(child => child.type === 2 && child.content.trim() || child.type === 5), `${file}: empty action`).toBe(true) } visit(node.children, inCell || node.tag === 'td', enabled, editable) } } visit(ast.children) } }) it('keeps shared list actions and detail content inside a density-enabled page ancestor', () => { for (const file of densityPages) { const template = parse(read(`modules/${file}`)).descriptor.template!.ast! let checked = 0 const visit = (nodes: typeof template.children, insideDensity = false) => { for (const node of nodes) { if (node.type !== 1) continue const classes = classesOf(node) if (classes.includes('density-confirm-dialog')) { expect(classes, `${file}: confirmation density`).toContain('admin-density-dialog') checked += 1 } if (node.tag === 'DataTable' || node.tag === 'LoadingListDetailDrawer' || classes.some(name => ['density-action', 'density-actions', 'density-tabs', 'density-fields', 'density-table', 'density-tab-icon', 'density-back'].includes(name))) { expect(insideDensity, `${file}: ${classes.join(' ')} lacks its density ancestor`).toBe(true) checked += 1 } visit(node.children, insideDensity || classes.some(name => ['admin-density', 'admin-density-dialog'].includes(name))) } } visit(template.children) expect(checked, `${file}: no shared styles checked`).toBeGreaterThan(0) } }) it('covers approved domestic pages across the latest menu groups and loads density CSS on direct entry', () => { const paths = getNavigationLeaves(adminNavigation).map(node => node.path) expect(paths).toEqual(expect.arrayContaining(domesticPages.map(page => page.path))) for (const page of domesticPages) { if (page.entry) expect(read(`modules/${page.entry}`)).toContain(`<${page.component} mode="${page.mode}"`) } for (const file of densityPages) expect(read(`modules/${file}`), `${file}: direct entry CSS`).toContain('@/styles/adminDensity.css') }) it('keeps customer dialogs on shared sections, editors and preview geometry', () => { const customers = read('modules/customers/pages/CustomersView.vue') const detail = read('modules/customers/pages/CustomerDetailView.vue') expect(customers).toContain('density-table density-table--editable customer-editor-table') expect(customers).toContain('density-actions density-actions--footer') expect(customers).not.toContain(' { for (const file of ['customers/CustomerOverseasStorageFeePanel.vue', 'customers/pages/OverseasStorageFeesView.vue', 'customers/components/CustomerBillingRulesPanel.vue']) { const source = read(`modules/${file}`) expect(source, file).toContain('density-table density-table--editable') expect(source, file).toContain('class="density-dialog-section" data-tone="work"') expect(source, file).toContain('fullscreenable') expect(source, file).not.toContain('class="dialog-actions"') } }) it('covers both customer menu entries and their shared detail primitives', () => { const navigation = read('core/navigation/adminNavigation.ts') const customers = navigation.slice(navigation.indexOf("key: 'customers'"), navigation.indexOf("key: 'domestic-warehouse'")) expect([...customers.matchAll(/path: '/g)]).toHaveLength(customerPages.length) for (const page of customerPages) { expect(customers).toContain(`title: '${page.title}'`) expect(customers).toContain(`path: '${page.path}'`) } expect(read('modules/customers/pages/CustomerDetailView.vue')).toContain('class="back-button density-back"') }) it('uses shared outlined actions across domestic tables and explicit classes on teleported dialogs', () => { for (const file of [...new Set([...densityPages, ...consumers])]) { const template = parse(read(`modules/${file}`)).descriptor.template!.ast! const visit = (nodes: typeof template.children, actionSlot = false, parentTag = '') => { for (const node of nodes) { if (node.type !== 1) continue const isActionSlot = actionSlot || (parentTag === 'DataTable' && node.props.some(prop => prop.type === 7 && prop.name === 'slot' && prop.arg?.type === 4 && prop.arg.content === 'actions')) if (densityPages.includes(file) && !selfOperatedPages.includes(file) && isActionSlot && node.tag === 'el-button') expect(classesOf(node), `${file}: row action`).toContain('density-action') if (['AppDialog', 'el-dialog'].includes(node.tag)) expect(classesOf(node), `${file}: teleported dialog`).toContain('admin-density-dialog') if (['AppDrawer', 'el-drawer'].includes(node.tag)) expect(classesOf(node), `${file}: teleported drawer`).toContain('admin-density-drawer') visit(node.children, isActionSlot, node.tag) } } visit(template.children) } }) it('keeps the collection monitor in the Admin theme without legacy list controls', () => { const page = read('modules/self-operated/pages/CollectionView.vue') const monitor = read('modules/self-operated/components/CollectionAccountMonitor.vue') expect(page).toContain('class="admin-density admin-workbench"') expect(page).toContain('@/styles/adminDensity.css') expect(page).toContain('@/styles/adminWorkbench.css') expect(page).toContain(' { expect(getNavigationLeaves(adminNavigation).map(node => node.path)).toEqual(expect.arrayContaining([ '/self-operated/sales', '/self-operated/inventory', '/self-operated/collection', ])) for (const file of selfOperatedPages) { const template = parse(read(`modules/${file}`)).descriptor.template!.ast! let tables = 0 const visit = (nodes: typeof template.children) => { for (const node of nodes) { if (node.type !== 1) continue if (node.tag === 'DataTable') { expect(node.props.some(prop => prop.type === 6 && prop.name === 'appearance' && prop.value?.content === 'integrated'), `${file}: integrated text actions`).toBe(true) tables += 1 } visit(node.children) } } visit(template.children) expect(tables).toBeGreaterThan(0) } }) it('keeps tokens and shared rules opt-in and free of business selectors', () => { expect(shared).toContain("@import './adminDensityTokens.css'") for (const css of [tokens, shared]) cssRoot(css).walkRules(rule => { expect(rule.selector, `unscoped rule: ${rule.selector}`).toContain('.admin-density') expect(rule.selector).not.toMatch(/customer-|order-|inbound-|preload-/) }) }) it('defines every referenced density variable once, with local field width as the documented exception', () => { const definitions = [...tokens.matchAll(/(--admin-[\w-]+)\s*:/g)].map(match => match[1]) expect(new Set(definitions).size).toBe(definitions.length) const declared = new Set([...definitions, '--admin-field-label-width']) for (const { file, css } of [...styles, { file: 'adminDensity.css', css: shared }]) { for (const match of css.matchAll(/var\((--admin-[\w-]+)/g)) expect(declared.has(match[1]!), `${file}: ${match[1]}`).toBe(true) for (const match of css.matchAll(/(--admin-[\w-]+)\s*:/g)) expect(match[1], `${file}: token redefinition`).toBe('--admin-field-label-width') } }) it('connects configurable surface colors and semantic hover states to their consumers', () => { const root = cssRoot(shared) const rules = new Map>() root.walkRules(rule => { const declarations: Record = {} rule.walkDecls(decl => { declarations[decl.prop] = decl.value }) rules.set(rule.selector, declarations) }) const stripeSelector = '.admin-density .el-table__body tr.el-table__row--striped:not(:where(:hover, .hover-row, .current-row)) > td.el-table__cell:not(:where(.hover-cell))' expect(rules.get(stripeSelector)?.background).toBe('var(--admin-row-stripe)') const stripeFixture = document.createElement('div') stripeFixture.innerHTML = '
' const row = stripeFixture.querySelector('tr')! const cell = stripeFixture.querySelector('td')! expect(cell.matches(stripeSelector)).toBe(true) for (const state of ['hover-row', 'current-row']) { row.classList.add(state) expect(cell.matches(stripeSelector)).toBe(false) row.classList.remove(state) } cell.classList.add('hover-cell') expect(cell.matches(stripeSelector)).toBe(false) cell.classList.remove('hover-cell') cell.innerHTML = '
' expect(cell.querySelector('td')!.matches(stripeSelector)).toBe(false) expect(rules.get('.admin-density-dialog .el-descriptions__label.el-descriptions__cell')?.background).toBe('var(--admin-label-bg)') expect(rules.get('.admin-density-dialog .el-form--inline .el-form-item__label')?.['align-items']).toBe('center') expect(rules.get('.admin-density-dialog.el-dialog.admin-density-workspace > .el-dialog__body')?.flex).toBe('1 1 auto') for (const tone of ['warning', 'purple', 'primary', 'success', 'danger']) { expect(rules.get(`:is(.admin-density, .admin-density-dialog) .density-action[data-tone="${tone}"]`)?.['--action-hover-bg']).toBe(`var(--admin-action-${tone}-hover)`) } expect(rules.get(':is(.admin-density, .admin-density-dialog) .el-button.density-action:hover:not(:disabled)')?.background).toBe('var(--action-hover-bg)') }) it('prevents page copies of action paint, tab primitives and desktop density dimensions', () => { for (const { file, css } of styles) cssRoot(css).walkRules(rule => { expect(rule.selector.trim(), file + ': empty selector').not.toBe('') expect(rule.selector, file).not.toMatch(/customer-action-button|order-action-button|\.density-action|\.density-tab-icon/) let desktop = true for (let parent: typeof rule.parent | ReturnType['parent'] = rule.parent; parent; parent = parent.parent) { if (parent.type === 'atrule' && /max-width|print/.test('params' in parent ? String(parent.params) : '')) desktop = false } rule.walkDecls(decl => { expect(decl.prop, file).not.toMatch(/^--el-button-/) if (desktop && decl.prop === 'font-size') expect(decl.value, `${file}: ${rule.selector}`).not.toMatch(/^(10|11|12|13|14|16)px$/) if (desktop && /^(height|min-height)$/.test(decl.prop)) expect(decl.value, `${file}: ${rule.selector}`).not.toMatch(/^(28|32|36|38|64)px$/) }) }) }) it('keeps measurement sections within the clipped panel and gives long references room', () => { const dialog = parse(read('modules/inbounds/measurement/InboundMeasurementDialog.vue')).descriptor const panelCss = parse(read('modules/inbounds/measurement/InboundMeasurementPanel.vue')).descriptor.styles.map(style => style.content).join('\n') const differenceStyles = new Map>() cssRoot(panelCss).walkRules(rule => { const declarations: Record = {} rule.walkDecls(decl => { declarations[decl.prop] = decl.value }) differenceStyles.set(rule.selector, declarations) }) expect(differenceStyles.get('.measurement-comparison__difference')?.color).toBe('var(--el-color-danger-dark-2)') expect(differenceStyles.get('.measurement-deviation-alert')).toMatchObject({ '--el-color-warning': 'var(--el-color-danger-dark-2)', '--el-color-warning-dark-2': 'var(--el-color-danger-dark-2)', }) const layout = read('modules/inbounds/measurement/measurementDensity.css') const summaryMargins: string[] = [] for (const css of [layout, ...dialog.styles.map(style => style.content)]) { cssRoot(css).walkRules(rule => { if (!/\.measurement-(summary-section|declaration|items-section)(?![\w-])/.test(rule.selector)) return rule.walkDecls(decl => { if (decl.prop.startsWith('margin')) { expect(decl.value, `section must not escape the clipped panel: ${rule.selector}`).not.toMatch(/(^|\s)-\d/) if (rule.selector.endsWith('.measurement-summary-section')) summaryMargins.push(decl.value) } }) }) } expect(summaryMargins).toEqual(['0']) const sectionStyles = new Map>() cssRoot(layout).walkRules(rule => { const declarations: Record = {} rule.walkDecls(decl => { declarations[decl.prop] = decl.value }) sectionStyles.set(rule.selector, declarations) }) for (const [section, background] of [ ['summary-section', 'var(--admin-dialog-summary-bg)'], ['declaration', 'var(--admin-dialog-source-bg)'], ['items-section', 'var(--admin-dialog-work-bg)'], ]) { expect(sectionStyles.get(`.inbound-measurement-workspace-dialog.inbound-density .measurement-${section}`)).toMatchObject({ padding: 'var(--admin-space-4, 10px) var(--admin-space-5, 12px)', background, }) } // Shared density must not reset the coloured sections' horizontal gutters. expect(sectionStyles.get('.inbound-density.admin-density-dialog .measurement-section')).toEqual({ 'padding-block': 'var(--admin-space-4, 10px)', }) expect(layout).toContain('.measurement-document-number { display: flex; flex: 1;') expect(layout).toContain('.measurement-platform-fields { display: flex; flex: 1 1 520px;') expect(layout).toContain('.measurement-platform-fields > .measurement-expected-pallets { flex: 0 0 180px; }') for (const style of dialog.styles) { expect(style.content).not.toMatch(/\.measurement-(summary-section|metrics|declaration|items-section)\b/) } }) it('keeps desktop table toolbar dimensions owned by DataTable without cascade overrides', () => { const tableCss = parse(read('shared/components/DataTable.vue')).descriptor.styles.map(style => style.content).join('\n') const dimensions: Record = { padding: [], 'min-height': [] } cssRoot(tableCss).walkRules(rule => { if (!rule.selectors.includes('.data-table__toolbar') && !rule.selectors.includes('.data-table__toolbar.is-compact')) return // Preserve the existing mobile layout; the shared desktop dimensions have one owner. if (rule.parent?.type === 'atrule' && /max-width/.test(rule.parent.params)) return rule.walkDecls(decl => { if (dimensions[decl.prop]) dimensions[decl.prop].push(decl.value) }) }) expect(dimensions).toEqual({ padding: ['var(--admin-space-2, 6px) var(--admin-space-5, 12px)'], 'min-height': ['44px'] }) for (const { file, css } of [{ file: 'adminDensity.css', css: shared }, ...styles]) cssRoot(css).walkRules(rule => { if (!/\.data-table__toolbar(?:\b|\.)/.test(rule.selector)) return rule.walkDecls(decl => { expect(decl.prop, `${file}: toolbar dimensions belong in DataTable`).not.toMatch(/^(padding(?:-.+)?|height|min-height)$/) }) }) }) it('gives simulation creation an opt-in comfortable editor frame', () => { const source = read('modules/container-loading/components/ContainerSimulationCreate.vue') expect(source).toContain("'admin-density-dialog--comfortable': dense") expect(source).toContain('width="min(800px, calc(100vw - 32px))"') expect(shared).toContain('.admin-density-dialog.admin-density-dialog--comfortable.el-dialog { --el-component-size: 40px; border-radius: 12px; }') expect(shared).toContain('> .el-form-item__content > .el-select > .el-select__wrapper { min-height: 40px; }') expect(shared).not.toContain('.admin-density-dialog.admin-density-dialog--comfortable.el-dialog .el-form-item') expect(shared).not.toContain('.admin-density-dialog.admin-density-dialog--comfortable.el-dialog .el-select__wrapper') }) it('keeps conditional shared consumers opt-in instead of enabling other menus', () => { const actionWeights = new Map() cssRoot(read('styles/adminTextActions.css')).walkRules(rule => { rule.walkDecls('font-weight', declaration => { actionWeights.set(rule.selector, declaration.value) }) }) expect(actionWeights.get('.row-actions.admin-text-actions .el-button.el-button')).toBe('400') expect(actionWeights.get('.admin-text-actions.el-popper .el-dropdown-menu__item')).toBe('400') const dispatches = read('modules/overseas-warehouse/pages/DispatchesView.vue') expect(dispatches).toContain('appearance="integrated"') expect(dispatches).toContain('