/**
* tailwind-styled-v4 — Testing Utilities
*
* Integrasi dengan Jest / Vitest untuk test komponen tw().
*
* @example
* // vitest.config.ts
* import { tailwindStyledSetup } from '@tailwind-styled/testing'
* export default defineConfig({ test: { setupFiles: ['@tailwind-styled/testing/setup'] } })
*
* // Button.test.ts
* import { render } from '@testing-library/react'
* import { expectClasses, getVariantClass } from '@tailwind-styled/testing'
*
* test('Button renders primary variant', () => {
* const { container } = render()
* expectClasses(container.firstChild, ['bg-blue-500', 'text-white'])
* })
*/
/**
* Custom matcher: toHaveClass
* Dipakai langsung atau via expect.extend(tailwindMatchers)
*
* @example
* expect(element).toHaveClass('bg-blue-500')
*/
declare function toHaveClass(className: string): (value: {
classList?: {
contains: (name: string) => boolean;
};
}) => {
pass: boolean;
message: () => string;
};
/**
* Custom matcher: toHaveClasses
* Cek beberapa class sekaligus
*
* @example
* expect(element).toHaveClasses(['px-4', 'py-2', 'rounded'])
*/
declare function toHaveClasses(classNames: string[]): (value: Element | null | undefined) => {
pass: boolean;
message: () => string;
};
/**
* Custom matcher: toNotHaveClass
*
* @example
* expect(element).toNotHaveClass('hidden')
*/
declare function toNotHaveClass(className: string): (value: Element | null | undefined) => {
pass: boolean;
message: () => string;
};
/** Semua matchers — pakai dengan expect.extend(tailwindMatchers) */
declare const tailwindMatchers: {
toHaveClass: typeof toHaveClass;
toHaveClasses: typeof toHaveClasses;
toNotHaveClass: typeof toNotHaveClass;
};
/**
* Assert element punya semua class yang diharapkan.
* Lebih ergonomis dari `expect(el).toHaveClasses([...])` untuk banyak class.
*
* @example
* const { container } = render()
* expectClasses(container.firstChild, ['bg-blue-500', 'text-white', 'h-12'])
*/
declare function expectClasses(element: Element | null | undefined, classes: string[]): void;
/**
* Assert element tidak punya class tertentu.
*
* @example
* expectNoClasses(container.firstChild, ['opacity-50', 'cursor-not-allowed'])
*/
declare function expectNoClasses(element: Element | null | undefined, classes: string[]): void;
/**
* Extract class list dari element sebagai sorted array.
* Berguna untuk snapshot testing.
*
* @example
* expect(getClassList(element)).toMatchSnapshot()
*/
declare function getClassList(element: Element | null | undefined): string[];
/**
* Buat snapshot dari semua kombinasi variant.
* Berguna untuk regression testing pada styled components.
*
* @example
* const buttonVariants = snapshotVariants(
* (props) => {
* const { container } = render()
* return container.firstChild?.className ?? ''
* },
* [
* { intent: 'primary', size: 'sm' },
* { intent: 'primary', size: 'lg' },
* { intent: 'danger', size: 'sm' },
* ]
* )
* expect(buttonVariants).toMatchSnapshot()
*/
declare function snapshotVariants(render: (variant: T) => string, variants: T[]): {
variant: T;
output: string;
}[];
/**
* Generate semua kombinasi dari variant matrix.
*
* @example
* const combinations = expandVariantMatrix({
* intent: ['primary', 'danger'],
* size: ['sm', 'md', 'lg'],
* disabled: [true, false],
* })
* // → 2 × 3 × 2 = 12 kombinasi
*/
declare function expandVariantMatrix(matrix: Record>): Array>;
/**
* Test semua kombinasi variant sekaligus — no missing coverage.
*
* @example
* testAllVariants(
* Button,
* { intent: ['primary','danger'], size: ['sm','lg'] },
* (el, variant) => {
* expect(el).not.toBeNull()
* if (variant.intent === 'primary') expectClasses(el, ['bg-blue-500'])
* }
* )
*/
declare function testAllVariants(matrix: Record>, testFn: (variant: Record) => void): void;
/**
* Bandingkan dua class string secara semantik (urutan tidak penting).
*
* @example
* expectClassesEqual('px-4 py-2 bg-blue-500', 'bg-blue-500 px-4 py-2') // pass
*/
declare function expectClassesEqual(actual: string, expected: string): void;
interface EngineMetricsSnapshot {
totalFiles?: number;
uniqueClasses?: number;
buildTimeMs?: number;
cssBytes?: number;
cacheHits?: number;
cacheMisses?: number;
incrementalRuns?: number;
fullRescans?: number;
}
/**
* Assert engine metrics snapshot meets minimum thresholds.
*
* @example
* const result = await engine.build()
* expectEngineMetrics(metrics.snapshot(), {
* totalFiles: 10,
* buildTimeMs: 5000, // max
* })
*/
declare function expectEngineMetrics(metrics: EngineMetricsSnapshot, expectations: {
minFiles?: number;
maxBuildTimeMs?: number;
minUniqueClasses?: number;
cacheHitRateMin?: number;
}): void;
/**
* Custom Jest/Vitest matcher: toHaveEngineMetrics
*
* @example
* expect(metrics).toHaveEngineMetrics({ minFiles: 1 })
*/
declare function toHaveEngineMetrics(expectations: Parameters[1]): (metrics: EngineMetricsSnapshot) => {
pass: boolean;
message: () => string;
};
/** All matchers including engine metrics */
declare const tailwindMatchersWithMetrics: {
toHaveClass: typeof toHaveClass;
toHaveClasses: typeof toHaveClasses;
toNotHaveClass: typeof toNotHaveClass;
toHaveEngineMetrics: typeof toHaveEngineMetrics;
};
export { type EngineMetricsSnapshot, expandVariantMatrix, expectClasses, expectClassesEqual, expectEngineMetrics, expectNoClasses, getClassList, snapshotVariants, tailwindMatchers, tailwindMatchersWithMetrics, testAllVariants, toHaveClass, toHaveClasses, toHaveEngineMetrics, toNotHaveClass };