Files
stablyai--orca/config/scripts/check-styled-scrollbars.test.mjs
2026-07-13 13:05:33 +08:00

200 lines
6.8 KiB
JavaScript

import { describe, expect, it } from 'vitest'
import { plainClassName, reportUnstyledScrollbars } from './check-styled-scrollbars.mjs'
describe('check-styled-scrollbars', () => {
it('reports renderer vertical scroll containers without an Orca scrollbar style', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
'export function Example() { return <div className="max-h-64 overflow-y-auto" /> }'
)
expect(reports).toHaveLength(1)
})
it('accepts obvious styled vertical scroll containers', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
'export function Example() { return <div className="max-h-64 overflow-auto scrollbar-sleek" /> }'
)
expect(reports).toHaveLength(0)
})
it('does not accept nonexistent scrollbar classes as Orca scrollbar styles', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
'export function Example() { return <div className="max-h-64 overflow-auto scrollbar-none" /> }'
)
expect(reports).toHaveLength(1)
})
it('fails closed when a separate class composer argument supplies the scrollbar style', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example() { return <div className={cn('max-h-64 overflow-y-auto', 'scrollbar-sleek')} /> }"
)
expect(reports).toHaveLength(1)
})
it('accepts static class composer arguments when the same literal is styled', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example() { return <div className={cn('max-h-64 overflow-y-auto scrollbar-sleek')} /> }"
)
expect(reports).toHaveLength(0)
})
it('fails closed when a scrollbar class is only conditionally present', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example({ enabled }) { return <div className={cn('overflow-y-auto', enabled && 'scrollbar-sleek')} /> }"
)
expect(reports).toHaveLength(1)
})
it('accepts conditional branches when overflow and scrollbar live in the same class literal', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example({ enabled }) { return <div className={cn(enabled && 'overflow-y-auto scrollbar-sleek')} /> }"
)
expect(reports).toHaveLength(0)
})
it('reports vertical scroll inside arbitrary wrappers when the literal is unstyled', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example() { return <div className={identity('overflow-y-auto')} /> }"
)
expect(reports).toHaveLength(1)
})
it('does not require a vertical scrollbar style for horizontal-only overflow', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
'export function Example() { return <pre className="max-w-full overflow-x-auto" /> }'
)
expect(reports).toHaveLength(0)
})
it('does not let responsive scrollbar variants satisfy unconditional overflow', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
'export function Example() { return <div className="overflow-y-auto md:scrollbar-sleek" /> }'
)
expect(reports).toHaveLength(1)
})
it('accepts matching responsive overflow and scrollbar variants', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
'export function Example() { return <div className="md:overflow-y-auto md:scrollbar-sleek" /> }'
)
expect(reports).toHaveLength(0)
})
it('accepts unconditional scrollbar styles for responsive overflow', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
'export function Example() { return <div className="md:overflow-y-auto scrollbar-sleek" /> }'
)
expect(reports).toHaveLength(0)
})
it('reports inline vertical overflow without an Orca scrollbar class', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example() { return <div style={{ overflowY: 'auto' }} /> }"
)
expect(reports).toHaveLength(1)
})
it('accepts inline vertical overflow with a stable Orca scrollbar class', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
'export function Example() { return <div className="scrollbar-editor" style={{ overflow: \'auto\' }} /> }'
)
expect(reports).toHaveLength(0)
})
it('reports inline vertical overflow when the scrollbar class is conditional or short-circuited', () => {
for (const classNameExpression of [
"enabled && 'scrollbar-sleek'",
"enabled ? 'scrollbar-sleek' : undefined",
"enabled || 'scrollbar-sleek'",
"enabled ?? 'scrollbar-sleek'"
]) {
const reports = reportUnstyledScrollbars(
'Example.tsx',
`export function Example({ enabled }) { return <div className={${classNameExpression}} style={{ overflowY: 'auto' }} /> }`
)
expect(reports, classNameExpression).toHaveLength(1)
}
})
it('reports logical inline style spreads without an Orca scrollbar class', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example({ open }) { return <div style={{ ...(open && { overflowY: 'auto' }) }} /> }"
)
expect(reports).toHaveLength(1)
})
it('reports JSX spread className props with unstyled vertical overflow', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example() { return <div {...{ className: 'overflow-y-auto' }} /> }"
)
expect(reports).toHaveLength(1)
})
it('accepts JSX spread className props when the same literal is styled', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example() { return <div {...{ className: 'overflow-y-auto scrollbar-sleek' }} /> }"
)
expect(reports).toHaveLength(0)
})
it('uses later spread className props over earlier explicit className props', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
'export function Example() { return <div className="scrollbar-sleek" {...{ className: \'overflow-y-auto\' }} /> }'
)
expect(reports).toHaveLength(1)
})
it('supports variant helper className config', () => {
const reports = reportUnstyledScrollbars(
'Example.tsx',
"export function Example() { return <div className={buttonVariants({ className: 'overflow-y-auto scrollbar-sleek' })} /> }"
)
expect(reports).toHaveLength(0)
})
it('normalizes Tailwind variants and important prefixes before matching', () => {
expect(plainClassName('md:overflow-y-auto')).toBe('overflow-y-auto')
expect(plainClassName('[&:hover]:overflow-y-auto')).toBe('overflow-y-auto')
expect(plainClassName('md:!scrollbar-editor')).toBe('scrollbar-editor')
expect(plainClassName('!scrollbar-editor')).toBe('scrollbar-editor')
})
})