From 2fac626e466d724629946747c4514dac906864b2 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 26 Dec 2025 20:38:17 -0800 Subject: [PATCH] temp tests --- package.json | 3 +- src/index.tsx | 49 ++- src/tests/index.test.tsx | 748 ++++++++++++++++++++++++++++++++++++++ src/tests/test_helpers.ts | 32 ++ 4 files changed, 822 insertions(+), 10 deletions(-) create mode 100644 src/tests/index.test.tsx create mode 100644 src/tests/test_helpers.ts diff --git a/package.json b/package.json index a1aa4c9..78e595e 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "module": "src/index.ts", "type": "module", "scripts": { - "dev": "bun run --hot server.tsx" + "dev": "bun run --hot server.tsx", + "test": "bun test" }, "devDependencies": { "@types/bun": "latest" diff --git a/src/index.tsx b/src/index.tsx index 863d34c..107ec27 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,18 +1,19 @@ import type { JSX } from 'hono/jsx' import { type TagDef, UnitlessProps } from './types' -const styles: Record> = {} +export const styles: Record> = {} export const Styles = () => ') + }) + + test('Styles includes CSS for all registered components', () => { + define('StylesComp2', { layout: { width: 100 } }) + define('StylesComp3', { layout: { height: 200 } }) + + const css = getStylesCSS() + expect(css).toContain('.StylesComp2') + expect(css).toContain('width: 100px') + expect(css).toContain('.StylesComp3') + expect(css).toContain('height: 200px') + }) + + test('Styles includes variant CSS', () => { + define('StylesVariant', { + variants: { + size: { + small: { layout: { padding: 8 } }, + large: { layout: { padding: 24 } } + } + } + }) + + const css = getStylesCSS() + expect(css).toContain('.StylesVariant.size-small') + expect(css).toContain('padding: 8px') + expect(css).toContain('.StylesVariant.size-large') + expect(css).toContain('padding: 24px') + }) + + test('Styles includes part CSS', () => { + define('StylesPart', { + parts: { + Header: { look: { color: 'red' } }, + Body: { look: { color: 'blue' } } + } + }) + + const css = getStylesCSS() + expect(css).toContain('.StylesPart_Header') + expect(css).toContain('color: red') + expect(css).toContain('.StylesPart_Body') + expect(css).toContain('color: blue') + }) +}) + +describe('edge cases', () => { + test('handles empty definition', () => { + const Component = define({}) + const html = renderToString(Component({})) + + expect(html).toBeDefined() + expect(html).toMatch(/class="Def\d+"/) + }) + + test('handles definition with only parts', () => { + const Component = define({ + parts: { + Header: { base: 'header' } + } + }) + + const result = Component({}) + expect(result).toBeDefined() + }) + + test('handles definition with only variants', () => { + const Component = define({ + variants: { + size: { + small: { layout: { padding: 8 } } + } + } + }) + + const html = renderToString(Component({ size: 'small' })) + expect(html).toContain('size-small') + }) + + test('does not duplicate styles when registered multiple times', () => { + define('NoDuplicate', { layout: { width: 100 } }) + define('NoDuplicate', { layout: { width: 200 } }) + + const styles = parseCSS(getStylesCSS()) + // Should keep first value (??= operator) + expect(styles['NoDuplicate']?.['width']).toBe('100px') + }) + + test('handles complex nested structures', () => { + define('ComplexNested', { + layout: { display: 'grid' }, + parts: { + Container: { layout: { padding: 16 } }, + Item: { look: { fontSize: 14 } } + }, + variants: { + theme: { + dark: { + look: { backgroundColor: 'black' }, + parts: { + Container: { look: { backgroundColor: '#222' } }, + Item: { look: { color: 'white' } } + } + } + } + } + }) + + const styles = parseCSS(getStylesCSS()) + expect(styles['ComplexNested']?.['display']).toBe('grid') + expect(styles['ComplexNested_Container']?.['padding']).toBe('16px') + expect(styles['ComplexNested_Item']?.['font-size']).toBe('14px') + expect(styles['ComplexNested.theme-dark']?.['background-color']).toBe('black') + expect(styles['ComplexNested_Container.theme-dark']?.['background-color']).toBe('#222') + expect(styles['ComplexNested_Item.theme-dark']?.['color']).toBe('white') + }) + + test('handles JSX children correctly', () => { + const Component = define('ChildrenTest', {}) + + const html = renderToString(Component({ + children: Nested Element + })) + expect(html).toContain('Nested Element') + }) + + test('handles multiple children', () => { + const Component = define('MultiChildren', { + render: ({ props, parts }) => { + return {props.children} + } + }) + + const html = renderToString(Component({ + children: [ +
First
, +
Second
+ ] + })) + expect(html).toContain('First') + expect(html).toContain('Second') + }) +}) diff --git a/src/tests/test_helpers.ts b/src/tests/test_helpers.ts new file mode 100644 index 0000000..d615394 --- /dev/null +++ b/src/tests/test_helpers.ts @@ -0,0 +1,32 @@ +import { define } from '../index' + +export function renderToString(jsx: any): string { + return jsx.toString() +} + +export function getStylesCSS(): string { + const StylesComponent = define.Styles + const result = StylesComponent() as any + return result.props.dangerouslySetInnerHTML.__html as string +} + +export function parseCSS(css: string): Record> { + const styles: Record> = {} + const classMatches = Array.from(css.matchAll(/\.([^\s{]+)\s*\{([^}]+)\}/g)) + + for (const match of classMatches) { + if (!match[1] || !match[2]) continue + + const className = match[1] + const cssText = match[2] + styles[className] = {} + + const propMatches = Array.from(cssText.matchAll(/\s*([^:]+):\s*([^;]+);/g)) + for (const propMatch of propMatches) { + if (!propMatch[1] || !propMatch[2]) continue + styles[className]![propMatch[1].trim()] = propMatch[2].trim() + } + } + + return styles +}