or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-styling.mdcss-utilities.mdindex.mdreact-native.mdserver-side-rendering.mdtest-utilities.mdtheming.mdtypescript-integration.md
tile.json

test-utilities.mddocs/

Test Utilities

styled-components provides testing utilities for finding and testing styled components in various testing environments. These utilities enable effective testing of styled components without relying on implementation details.

Test Utilities Overview

The test utilities are provided as a separate package and enable finding styled components by their unique identifiers rather than class names or other implementation-specific details.

// From 'styled-components/test-utils'
function enzymeFind(
  wrapper: ReactWrapper, 
  styledComponent: IStyledComponent<'web', any>
): ReactWrapper;

function find(
  element: Element, 
  styledComponent: IStyledComponent<'web', any>
): Element | null;

function findAll(
  element: Element, 
  styledComponent: IStyledComponent<'web', any>
): NodeListOf<Element>;

Enzyme Integration

Basic Enzyme Testing

import { mount } from 'enzyme';
import { enzymeFind } from 'styled-components/test-utils';
import 'jest-styled-components';

const Button = styled.button`
  background-color: #007bff;
  color: white;
  padding: 10px 20px;
`;

const Card = styled.div`
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 16px;
`;

describe('Styled Components', () => {
  it('should find styled components in enzyme wrapper', () => {
    const TestComponent = () => (
      <Card>
        <Button>Click me</Button>
      </Card>
    );
    
    const wrapper = mount(<TestComponent />);
    
    // Find styled components using enzymeFind
    const cardElement = enzymeFind(wrapper, Card);
    const buttonElement = enzymeFind(wrapper, Button);
    
    expect(cardElement).toHaveLength(1);
    expect(buttonElement).toHaveLength(1);
    expect(buttonElement.text()).toBe('Click me');
  });
  
  it('should test styled component props', () => {
    const TestButton = styled.button<{ primary?: boolean }>`
      background-color: ${props => props.primary ? '#007bff' : '#6c757d'};
    `;
    
    const wrapper = mount(<TestButton primary>Test</TestButton>);
    const buttonElement = enzymeFind(wrapper, TestButton);
    
    expect(buttonElement.prop('primary')).toBe(true);
  });
});

Testing with Theme Provider

import { ThemeProvider } from 'styled-components';

const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d'
  }
};

const ThemedButton = styled.button`
  background-color: ${props => props.theme.colors.primary};
`;

describe('Themed Components', () => {
  it('should render with theme', () => {
    const wrapper = mount(
      <ThemeProvider theme={theme}>
        <ThemedButton>Themed Button</ThemedButton>
      </ThemeProvider>
    );
    
    const button = enzymeFind(wrapper, ThemedButton);
    expect(button).toHaveLength(1);
    
    // Test computed styles
    expect(button).toHaveStyleRule('background-color', '#007bff');
  });
});

DOM Testing with find/findAll

React Testing Library Integration

import { render, screen } from '@testing-library/react';
import { find, findAll } from 'styled-components/test-utils';

const NavigationItem = styled.li`
  padding: 8px 16px;
  cursor: pointer;
  
  &:hover {
    background-color: #f8f9fa;
  }
`;

const Navigation = styled.ul`
  list-style: none;
  padding: 0;
  margin: 0;
`;

describe('DOM Testing', () => {
  it('should find styled components in DOM', () => {
    const TestNav = () => (
      <Navigation>
        <NavigationItem>Home</NavigationItem>
        <NavigationItem>About</NavigationItem>
        <NavigationItem>Contact</NavigationItem>
      </Navigation>
    );
    
    const { container } = render(<TestNav />);
    
    // Find single styled component
    const nav = find(container, Navigation);
    expect(nav).toBeInTheDocument();
    
    // Find all instances of styled component
    const navItems = findAll(container, NavigationItem);
    expect(navItems).toHaveLength(3);
    
    // Test content
    expect(navItems[0]).toHaveTextContent('Home');
    expect(navItems[1]).toHaveTextContent('About');
    expect(navItems[2]).toHaveTextContent('Contact');
  });
});

Vanilla DOM Testing

const ListItem = styled.li`
  border-bottom: 1px solid #eee;
  padding: 12px;
`;

describe('Vanilla DOM Testing', () => {
  it('should work with vanilla DOM methods', () => {
    // Create DOM structure
    document.body.innerHTML = `
      <div id="test-container">
        <!-- Styled components will be rendered here -->
      </div>
    `;
    
    const container = document.getElementById('test-container');
    const root = createRoot(container);
    
    const TestList = () => (
      <ul>
        <ListItem>Item 1</ListItem>
        <ListItem>Item 2</ListItem>
      </ul>
    );
    
    act(() => {
      root.render(<TestList />);
    });
    
    // Use find and findAll utilities
    const firstItem = find(container, ListItem);
    const allItems = findAll(container, ListItem);
    
    expect(firstItem.textContent).toBe('Item 1');
    expect(allItems.length).toBe(2);
  });
});

jest-styled-components Integration

Style Rule Testing

import 'jest-styled-components';

const StyledButton = styled.button<{ 
  variant?: 'primary' | 'secondary'; 
  size?: 'small' | 'large' 
}>`
  padding: ${props => props.size === 'small' ? '4px 8px' : '12px 24px'};
  background-color: ${props => {
    switch (props.variant) {
      case 'primary': return '#007bff';
      case 'secondary': return '#6c757d';
      default: return 'transparent';
    }
  }};
  border: ${props => props.variant ? 'none' : '1px solid #ccc'};
`;

describe('Style Rule Testing', () => {
  it('should have correct default styles', () => {
    const { container } = render(<StyledButton>Default</StyledButton>);
    const button = find(container, StyledButton);
    
    expect(button).toHaveStyleRule('padding', '12px 24px');
    expect(button).toHaveStyleRule('background-color', 'transparent');
    expect(button).toHaveStyleRule('border', '1px solid #ccc');
  });
  
  it('should have correct primary variant styles', () => {
    const { container } = render(
      <StyledButton variant="primary" size="small">Primary</StyledButton>
    );
    const button = find(container, StyledButton);
    
    expect(button).toHaveStyleRule('padding', '4px 8px');
    expect(button).toHaveStyleRule('background-color', '#007bff');
    expect(button).toHaveStyleRule('border', 'none');
  });
  
  it('should have hover styles', () => {
    const HoverButton = styled.button`
      background-color: #007bff;
      
      &:hover {
        background-color: #0056b3;
      }
    `;
    
    const { container } = render(<HoverButton>Hover me</HoverButton>);
    const button = find(container, HoverButton);
    
    expect(button).toHaveStyleRule('background-color', '#007bff');
    expect(button).toHaveStyleRule('background-color', '#0056b3', {
      modifier: ':hover'
    });
  });
});

Media Query Testing

const ResponsiveComponent = styled.div`
  padding: 16px;
  
  @media (max-width: 768px) {
    padding: 8px;
  }
  
  @media (min-width: 1024px) {
    padding: 32px;
  }
`;

describe('Media Query Testing', () => {
  it('should have responsive styles', () => {
    const { container } = render(<ResponsiveComponent>Content</ResponsiveComponent>);
    const component = find(container, ResponsiveComponent);
    
    expect(component).toHaveStyleRule('padding', '16px');
    expect(component).toHaveStyleRule('padding', '8px', {
      media: '(max-width: 768px)'
    });
    expect(component).toHaveStyleRule('padding', '32px', {
      media: '(min-width: 1024px)'
    });
  });
});

Snapshot Testing

Component Snapshots

const Card = styled.div`
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
`;

const CardTitle = styled.h3`
  margin: 0 0 12px 0;
  color: #333;
  font-size: 18px;
`;

const CardContent = styled.p`
  margin: 0;
  color: #666;
  line-height: 1.5;
`;

describe('Snapshot Testing', () => {
  it('should match snapshot', () => {
    const TestCard = () => (
      <Card>
        <CardTitle>Card Title</CardTitle>
        <CardContent>Card content goes here</CardContent>
      </Card>
    );
    
    const { container } = render(<TestCard />);
    expect(container.firstChild).toMatchSnapshot();
  });
  
  it('should match styled component snapshot', () => {
    const tree = renderer
      .create(<Card><CardTitle>Title</CardTitle></Card>)
      .toJSON();
    expect(tree).toMatchSnapshot();
  });
});

Theme Snapshot Testing

const themes = {
  light: {
    colors: { background: '#ffffff', text: '#333333' }
  },
  dark: {
    colors: { background: '#333333', text: '#ffffff' }
  }
};

const ThemedDiv = styled.div`
  background-color: ${props => props.theme.colors.background};
  color: ${props => props.theme.colors.text};
`;

describe('Theme Snapshots', () => {
  it.each(Object.entries(themes))('should match %s theme snapshot', (themeName, theme) => {
    const tree = renderer
      .create(
        <ThemeProvider theme={theme}>
          <ThemedDiv>Themed content</ThemedDiv>
        </ThemeProvider>
      )
      .toJSON();
    expect(tree).toMatchSnapshot(`themed-div-${themeName}`);
  });
});

Performance Testing

Component Mount Testing

const ExpensiveComponent = styled.div`
  /* Complex styles that might impact performance */
  background: linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%);
  box-shadow: 0 3px 5px 2px rgba(255, 105, 135, .3);
  transform: rotate(${props => props.rotation || 0}deg);
  transition: all 0.3s ease;
`;

describe('Performance Testing', () => {
  it('should render quickly', () => {
    const startTime = performance.now();
    
    const { container } = render(
      <div>
        {Array.from({ length: 100 }, (_, i) => (
          <ExpensiveComponent key={i} rotation={i * 3.6}>
            Item {i}
          </ExpensiveComponent>
        ))}
      </div>
    );
    
    const endTime = performance.now();
    const renderTime = endTime - startTime;
    
    expect(renderTime).toBeLessThan(100); // Should render in less than 100ms
    expect(findAll(container, ExpensiveComponent)).toHaveLength(100);
  });
});

Mock and Spy Testing

Testing Style Functions

const mockTheme = {
  colors: { primary: '#007bff' },
  spacing: { md: '16px' }
};

const getButtonStyles = (props) => `
  background-color: ${props.theme.colors.primary};
  padding: ${props.theme.spacing.md};
`;

const TestableButton = styled.button`
  ${getButtonStyles}
`;

describe('Style Function Testing', () => {
  it('should call style function with correct props', () => {
    const stylesSpy = jest.fn(getButtonStyles);
    
    const SpiedButton = styled.button`
      ${stylesSpy}
    `;
    
    render(
      <ThemeProvider theme={mockTheme}>
        <SpiedButton>Test</SpiedButton>
      </ThemeProvider>
    );
    
    expect(stylesSpy).toHaveBeenCalledWith(
      expect.objectContaining({
        theme: mockTheme
      })
    );
  });
});

Test Helpers and Utilities

Custom Test Utilities

// Custom test utilities for styled-components
export function renderWithTheme(component: React.ReactElement, theme = defaultTheme) {
  return render(
    <ThemeProvider theme={theme}>
      {component}
    </ThemeProvider>
  );
}

export function findStyledComponent<T extends IStyledComponent<'web', any>>(
  container: HTMLElement,
  StyledComponent: T
): HTMLElement | null {
  return find(container, StyledComponent);
}

export function findAllStyledComponents<T extends IStyledComponent<'web', any>>(
  container: HTMLElement,
  StyledComponent: T
): NodeListOf<HTMLElement> {
  return findAll(container, StyledComponent);
}

// Usage in tests
describe('Custom Utilities', () => {
  it('should use custom render helper', () => {
    const { container } = renderWithTheme(<ThemedButton>Test</ThemedButton>);
    const button = findStyledComponent(container, ThemedButton);
    
    expect(button).toBeInTheDocument();
    expect(button).toHaveStyleRule('background-color', '#007bff');
  });
});

Test Setup Configuration

// test-setup.js
import 'jest-styled-components';
import { configure } from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';

configure({ adapter: new Adapter() });

// Global test utilities
global.renderWithTheme = (component, theme = defaultTheme) => {
  return render(
    <ThemeProvider theme={theme}>
      {component}
    </ThemeProvider>
  );
};

// Mock console warnings in tests
const originalConsoleWarn = console.warn;
beforeEach(() => {
  console.warn = jest.fn();
});

afterEach(() => {
  console.warn = originalConsoleWarn;
});