import { render } from '@testing-library/react'; import React from 'react'; import type { ComponentType } from 'react'; import StyleWithContainer from '../style'; import { StyleContainerProvider } from '../style-container'; import type * as StyleContainerModule from '../style-container'; jest.mock('../is-server-environment', () => ({ isServerEnvironment: () => false, })); describe('); expect(baseElement.getElementsByTagName('style')).toHaveLength(0); expect(console.error).not.toHaveBeenCalled(); }); }); it('should add style to the head on the client', () => { createIsolatedTest((Style) => { render(); expect(document.head.innerHTML).toInclude(''); expect(console.error).not.toHaveBeenCalled(); }); }); it('should only add one style if it was already added', () => { createIsolatedTest((Style) => { render(); render(); expect(document.head.innerHTML).toIncludeRepeated('', 1); expect(console.error).not.toHaveBeenCalled(); }); }); it('should noop in prod', () => { createIsolatedTest((Style) => { process.env.NODE_ENV = 'production'; render(); expect(console.error).not.toHaveBeenCalled(); }); }); it('should warn in dev when using a dangerous pseudo selector', () => { createIsolatedTest((Style) => { process.env.NODE_ENV = 'development'; render(); expect(console.error).toHaveBeenCalledTimes(1); }); }); it('should warn in dev only once', () => { createIsolatedTest((Style) => { process.env.NODE_ENV = 'development'; render(); render(); expect(console.error).toHaveBeenCalledTimes(1); expect(console.error).toHaveBeenCalledWith( expect.stringMatching('Selectors ":first-child, :nth-child" are dangerous to use') ); }); }); it('should render style tags in buckets', () => { createIsolatedTest((Style) => { render( ); expect(document.head.innerHTML.split('').join('\n')).toMatchInlineSnapshot(` " " `); expect(console.error).not.toHaveBeenCalled(); }); }); it('should render shorthands in buckets', () => { // Our buckets don't actually support mixing pseudo-selectors with shorthand // properties, so the pseudo-selector buckets don't have correct shorthand // property order... createIsolatedTest((Style) => { render( ); expect(document.head.innerHTML.split('').join('\n')).toMatchInlineSnapshot(` " " `); expect(console.error).not.toHaveBeenCalled(); }); }); it('should update styles', () => { createIsolatedTest((Style) => { const { rerender } = render(); rerender(); expect(document.head.innerHTML).toInclude('.second-render { display: block; }'); expect(console.error).not.toHaveBeenCalled(); }); }); describe('StyleContainerProvider', () => { let container: HTMLDivElement; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { document.body.removeChild(container); }); it('should insert styles into the provided container instead of document.head', () => { render( {[`.a { color: red; }`]} ); expect(container.innerHTML).toInclude('.a { color: red; }'); expect(document.head.innerHTML).not.toInclude('.a { color: red; }'); expect(console.error).not.toHaveBeenCalled(); }); it('should maintain bucket ordering within the container', () => { render( {[ `._a1234567:hover{ color: red; }`, `._b1234567:active{ color: blue; }`, `._c1234567{ display: block; }`, `@media (max-width: 800px){ ._d1234567{ color: yellow; } }`, ]} ); expect(container.innerHTML.split('').join('\n')).toMatchInlineSnapshot(` " " `); expect(console.error).not.toHaveBeenCalled(); }); it('should not insert duplicate styles into the container', () => { render( {[`.b { color: blue; }`]} {[`.b { color: blue; }`]} ); expect(container.innerHTML).toIncludeRepeated('.b { color: blue; }', 1); expect(console.error).not.toHaveBeenCalled(); }); it('should track container and document.head caches independently using cacheKey', () => { // Render the same style into both the main document and a container. // Each should receive its own copy since they are independent targets. render({[`.c { color: green; }`]}); render( {[`.c { color: green; }`]} ); expect(document.head.innerHTML).toInclude('.c { color: green; }'); expect(container.innerHTML).toInclude('.c { color: green; }'); expect(console.error).not.toHaveBeenCalled(); }); it('should track two containers independently using different cacheKeys', () => { const container2 = document.createElement('div'); document.body.appendChild(container2); render( {[`.d { color: pink; }`]} ); render( {[`.d { color: pink; }`]} ); expect(container.innerHTML).toInclude('.d { color: pink; }'); expect(container2.innerHTML).toInclude('.d { color: pink; }'); document.body.removeChild(container2); expect(console.error).not.toHaveBeenCalled(); }); it('should forward nonce to style elements in the container', () => { render( {[`.e { color: orange; }`]} ); expect(container.innerHTML).toInclude('nonce="abc123"'); expect(console.error).not.toHaveBeenCalled(); }); it('should warn in dev when used in a server environment', () => { jest.resetModules(); jest.doMock('../is-server-environment', () => ({ isServerEnvironment: () => true, })); // Re-require to pick up the new mock const { StyleContainerProvider: ServerStyleContainerProvider } = jest.requireActual('../style-container'); const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); const { container: renderContainer } = render(
); expect(warnSpy).toHaveBeenCalledWith( expect.stringContaining( '@compiled/react: StyleContainerProvider has no effect in server environments.' ) ); // Children should still be rendered expect(renderContainer.querySelector('div')).not.toBeNull(); warnSpy.mockRestore(); jest.resetModules(); }); }); });