@furystack/shades
Version:
A lightweight UI framework for FuryStack with JSX support
230 lines (186 loc) • 8.08 kB
text/typescript
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { Shade } from './shade.js'
import { StyleManager } from './style-manager.js'
describe('StyleManager', () => {
beforeEach(() => {
StyleManager.clear()
})
afterEach(() => {
StyleManager.clear()
})
describe('registerComponentStyles', () => {
it('should register styles for a component', () => {
const result = StyleManager.registerComponentStyles('test-component', {
color: 'red',
padding: '10px',
})
expect(result).toBe(true)
expect(StyleManager.isRegistered('test-component')).toBe(true)
})
it('should inject CSS into a style element', () => {
StyleManager.registerComponentStyles('test-component', {
color: 'red',
})
const styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement).not.toBeNull()
expect(styleElement?.textContent).toContain('test-component')
expect(styleElement?.textContent).toContain('color: red')
})
it('should not register the same component twice', () => {
const result1 = StyleManager.registerComponentStyles('test-component', {
color: 'red',
})
const result2 = StyleManager.registerComponentStyles('test-component', {
color: 'blue',
})
expect(result1).toBe(true)
expect(result2).toBe(false)
// Should only have the first style
const styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement?.textContent).toContain('color: red')
expect(styleElement?.textContent).not.toContain('color: blue')
})
it('should handle pseudo-selectors', () => {
StyleManager.registerComponentStyles('test-button', {
backgroundColor: 'blue',
'&:hover': { backgroundColor: 'darkblue' },
})
const styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement?.textContent).toContain('test-button:hover')
expect(styleElement?.textContent).toContain('background-color: darkblue')
})
it('should return false for empty CSS object', () => {
const result = StyleManager.registerComponentStyles('empty-component', {})
expect(result).toBe(false)
expect(StyleManager.isRegistered('empty-component')).toBe(false)
})
it('should add comments with component name', () => {
StyleManager.registerComponentStyles('my-component', {
color: 'red',
})
const styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement?.textContent).toContain('/* my-component */')
})
it('should generate attribute selector for customized built-in elements', () => {
StyleManager.registerComponentStyles(
'my-link',
{
color: 'blue',
textDecoration: 'none',
},
'a',
)
const styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement?.textContent).toContain('a[is="my-link"]')
expect(styleElement?.textContent).toContain('color: blue')
expect(styleElement?.textContent).toContain('text-decoration: none')
})
it('should handle pseudo-selectors for customized built-in elements', () => {
StyleManager.registerComponentStyles(
'my-button',
{
backgroundColor: 'gray',
'&:hover': { backgroundColor: 'darkgray' },
'&:active': { transform: 'scale(0.98)' },
},
'button',
)
const styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement?.textContent).toContain('button[is="my-button"]')
expect(styleElement?.textContent).toContain('button[is="my-button"]:hover')
expect(styleElement?.textContent).toContain('button[is="my-button"]:active')
})
})
describe('isRegistered', () => {
it('should return true for registered components', () => {
StyleManager.registerComponentStyles('test-component', { color: 'red' })
expect(StyleManager.isRegistered('test-component')).toBe(true)
})
it('should return false for unregistered components', () => {
expect(StyleManager.isRegistered('unknown-component')).toBe(false)
})
})
describe('getRegisteredComponents', () => {
it('should return all registered component names', () => {
StyleManager.registerComponentStyles('component-a', { color: 'red' })
StyleManager.registerComponentStyles('component-b', { color: 'blue' })
const registered = StyleManager.getRegisteredComponents()
expect(registered.has('component-a')).toBe(true)
expect(registered.has('component-b')).toBe(true)
expect(registered.size).toBe(2)
})
it('should return empty set when no components registered', () => {
const registered = StyleManager.getRegisteredComponents()
expect(registered.size).toBe(0)
})
})
describe('clear', () => {
it('should clear all registered components', () => {
StyleManager.registerComponentStyles('test-component', { color: 'red' })
expect(StyleManager.isRegistered('test-component')).toBe(true)
StyleManager.clear()
expect(StyleManager.isRegistered('test-component')).toBe(false)
expect(StyleManager.getRegisteredComponents().size).toBe(0)
})
it('should remove style element on clear', () => {
StyleManager.registerComponentStyles('test-component', { color: 'red' })
let styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement).not.toBeNull()
StyleManager.clear()
styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement).toBeNull()
})
})
describe('reusing style element', () => {
it('should use the same style element for multiple components', () => {
StyleManager.registerComponentStyles('component-a', { color: 'red' })
StyleManager.registerComponentStyles('component-b', { color: 'blue' })
const styleElements = document.querySelectorAll('[data-shades-styles]')
expect(styleElements.length).toBe(1)
const styleElement = styleElements[0]
expect(styleElement?.textContent).toContain('component-a')
expect(styleElement?.textContent).toContain('component-b')
})
})
describe('Shade integration', () => {
it('should register CSS styles when Shade component is created with css property', () => {
Shade({
customElementName: 'shade-css-test-component',
css: {
color: 'red',
padding: '10px',
'&:hover': { color: 'blue' },
},
render: () => null,
})
expect(StyleManager.isRegistered('shade-css-test-component')).toBe(true)
const styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement?.textContent).toContain('shade-css-test-component')
expect(styleElement?.textContent).toContain('color: red')
expect(styleElement?.textContent).toContain('shade-css-test-component:hover')
})
it('should register CSS with attribute selector for customized built-in elements', () => {
Shade({
customElementName: 'shade-css-test-button',
elementBase: HTMLButtonElement,
elementBaseName: 'button',
css: {
backgroundColor: 'blue',
'&:hover': { backgroundColor: 'darkblue' },
},
render: () => null,
})
expect(StyleManager.isRegistered('shade-css-test-button')).toBe(true)
const styleElement = document.querySelector('[data-shades-styles]')
expect(styleElement?.textContent).toContain('button[is="shade-css-test-button"]')
expect(styleElement?.textContent).toContain('button[is="shade-css-test-button"]:hover')
})
it('should not register styles when Shade component has no css property', () => {
Shade({
customElementName: 'shade-no-css-component',
render: () => null,
})
expect(StyleManager.isRegistered('shade-no-css-component')).toBe(false)
})
})
})