import { readFileSync } from 'node:fs' import { resolve } from 'node:path' import { describe, expect, it } from 'vitest' import { compileStyle, parse } from 'vue/compiler-sfc' import { adminWorkbenchChartTheme } from './adminWorkbenchChartTheme' const read = (path: string) => readFileSync(resolve(process.cwd(), 'src', path), 'utf8') const tokens = read('styles/adminWorkbenchTokens.css') const styles = read('styles/adminWorkbench.css') const page = read('modules/self-operated/pages/OverviewView.vue') const listPages = [ 'modules/self-operated/components/AnalyticsView.vue', 'modules/self-operated/components/InventoryTurnoverView.vue', ] const consumers = [ ...listPages, 'modules/self-operated/pages/InventoryView.vue', 'modules/self-operated/components/SkuTrend.vue', 'modules/self-operated/components/ChartDetailsDialog.vue', 'modules/self-operated/pages/CollectionView.vue', 'modules/self-operated/components/CollectionAccountMonitor.vue', 'modules/self-operated/components/CollectionProgressGauge.vue', 'modules/self-operated/components/CollectionTaskProgress.vue', ] const root = (source: string) => { const result = compileStyle({ source, filename: 'workbench.css', id: 'workbench-contract' }) expect(result.errors).toEqual([]) return result.rawResult!.root } describe('Admin workbench opt-in styles', () => { it('keeps canvas chart colors aligned with the public CSS palette', () => { const declarations = new Map() root(tokens).walkRules(rule => { if (rule.selector !== '.admin-workbench') return rule.walkDecls(declaration => { declarations.set(declaration.prop, declaration.value) }) }) for (const name of ['primary', 'text', 'muted', 'border', 'surface'] as const) { expect(adminWorkbenchChartTheme.colors[name]).toBe(declarations.get(`--workbench-${name}`)) } expect(adminWorkbenchChartTheme.colors.primarySoft).toBe(declarations.get('--workbench-accent-soft')) }) it('keeps lazy-loaded styles scoped to opted-in pages and dialog roots', () => { for (const source of [tokens, styles]) { root(source).walkRules(rule => { if (rule.parent?.type === 'atrule' && rule.parent.name.endsWith('keyframes')) return for (const selector of rule.selectors) { expect(selector).toMatch(/^\.admin-workbench(?:\s|\.|:|\[|$)/) expect(selector).not.toMatch(/:global|:root/) if (selector.includes('.el-dialog')) expect(selector).toMatch(/^\.admin-workbench\.is-dialog\.el-dialog/) } }) root(source).walkDecls(declaration => { expect(declaration.prop).not.toMatch(/^--admin-/) expect(declaration.important).toBeFalsy() }) } }) it('resolves every workbench variable from the public tokens instead of page defaults', () => { const declared = new Set() root(tokens).walkDecls(declaration => { if (declaration.prop.startsWith('--workbench-')) declared.add(declaration.prop) }) const pageStyles = [page, ...consumers.map(read)].flatMap(source => parse(source).descriptor.styles.map(style => style.content)).join('\n') for (const source of [styles, pageStyles]) { root(source).walkDecls(declaration => { expect(declaration.prop).not.toMatch(/^--workbench-/) }) for (const match of source.matchAll(/var\((--workbench-[\w-]+)/g)) { expect(declared.has(match[1]!)).toBe(true) } } }) it('opts list cards and teleported dialogs in on their actual rendering containers', () => { for (const file of listPages) { const source = read(file) expect(source).toContain("import '@/styles/adminWorkbench.css'") const template = parse(source).descriptor.template!.ast! type TemplateElement = Extract<(typeof template.children)[number], { type: 1 }> const classesOf = (node?: TemplateElement) => node?.props.flatMap(prop => prop.type === 6 && prop.name === 'class' ? prop.value?.content.split(/\s+/) ?? [] : []) ?? [] let tables = 0 const visit = (nodes: typeof template.children, ancestors: TemplateElement[]) => { for (const node of nodes) { if (node.type !== 1) continue if (node.tag === 'AdminPage') expect(classesOf(node)).toEqual(expect.arrayContaining(['admin-density', 'admin-workbench'])) if (node.tag === 'DataTable') { expect(classesOf(ancestors.at(-1))).toContain('workbench-table') expect(classesOf(node)).not.toContain('workbench-table') tables++ } if (node.tag === 'AppDialog') expect(classesOf(node)).toEqual(expect.arrayContaining(['admin-density-dialog', 'admin-workbench', 'is-dialog'])) visit(node.children, [...ancestors, node]) } } visit(template.children, []) expect(tables).toBe(1) } }) it('preserves complete metric values and the existing toolbar layout ownership', () => { root(styles).walkRules(rule => { if (rule.selector.includes('.workbench-metric')) { rule.walkDecls(declaration => { expect(['text-overflow', 'line-clamp', '-webkit-line-clamp']).not.toContain(declaration.prop) if (declaration.prop === 'overflow') expect(declaration.value).not.toBe('hidden') expect(declaration.prop).not.toBe('height') }) } if (rule.selector.includes('.data-table__toolbar')) { rule.walkDecls(declaration => { if (rule.selector.includes('.workbench-table.is-compact-toolbar')) { expect(['min-height', 'padding-top', 'padding-bottom']).toContain(declaration.prop) } else { expect(declaration.prop).not.toMatch(/^(?:padding|height|min-height|display|gap)/) } }) } if (rule.selector.includes('.is-dialog')) { rule.walkDecls(declaration => { expect(declaration.prop).not.toMatch(/^(?:width|height|min-height|max-height|padding|margin|overflow)/) }) } }) }) })