/** * External dependencies */ import { render } from '@testing-library/react'; /** * Internal dependencies */ import { createElement, Fragment, Component } from '../react'; import createInterpolateElement from '../create-interpolate-element'; import type { ExtractTags, InterpolationInput, InterpolationString, } from '../types'; describe( 'createInterpolateElement', () => { it( 'throws an error when there is no conversion map', () => { const testString = 'This is a string'; expect( () => createInterpolateElement( testString, {} ) ).toThrow( TypeError ); } ); it( 'returns same string when there are no tokens in the string', () => { const testString = 'This is a string'; const expectedElement = <>{ testString }; expect( createInterpolateElement( testString, { someValue: } ) ).toEqual( expectedElement ); } ); it( 'throws an error when there is an invalid conversion map', () => { const testString = 'This is a string'; expect( () => createInterpolateElement( testString, // @ts-expect-error - Invalid argument type [ 'someValue', { value: 10 } ] ) ).toThrow( TypeError ); } ); it( 'throws an error when there is an invalid entry in the conversion ' + 'map', () => { const testString = 'This is a string and '; expect( () => createInterpolateElement( testString, { item: , // @ts-expect-error - Invalid type for somethingElse somethingElse: 10, } ) ).toThrow( TypeError ); } ); it( 'returns same string when there is an non matching token in the ' + 'string', () => { const testString = 'This is a string'; const expectedElement = <>{ testString }; expect( createInterpolateElement( testString, { // @ts-expect-error - Unknown tag someValue: , } ) ).toEqual( expectedElement ); } ); it( 'returns same string when there is spaces in the token', () => { const testString = 'This is a string'; const expectedElement = <>{ testString }; expect( createInterpolateElement( testString, { 'spaced token': } ) ).toEqual( expectedElement ); } ); it( 'returns expected react element for non nested components', () => { const testString = 'This is a string with a link.'; const expectedElement = createElement( Fragment, null, 'This is a string with ', createElement( 'a', { href: 'https://github.com', className: 'some_class' }, 'a link' ), '.' ); const component = createInterpolateElement( testString, { // eslint-disable-next-line jsx-a11y/anchor-has-content a: , } ); expect( JSON.stringify( component ) ).toEqual( JSON.stringify( expectedElement ) ); } ); it( 'returns expected react element for nested components', () => { const testString = 'This is a string that is linked.'; const expectedElement = createElement( Fragment, {}, 'This is a ', createElement( 'a', null, 'string that is ', createElement( 'em', null, 'linked' ) ), '.' ); expect( JSON.stringify( createInterpolateElement( testString, { a: createElement( 'a' ), em: , } ) ) ).toEqual( JSON.stringify( expectedElement ) ); } ); it( 'returns expected output for a custom component with children ' + 'replacement', () => { const TestComponent = ( props ) => { return
{ props.children }
; }; const testString = 'This is a string with a Custom Component'; const expectedElement = createElement( Fragment, null, 'This is a string with a ', createElement( TestComponent, null, 'Custom Component' ) ); expect( JSON.stringify( createInterpolateElement( testString, { TestComponent: , } ) ) ).toEqual( JSON.stringify( expectedElement ) ); } ); it( 'returns expected output for self closing custom component', () => { const TestComponent = ( props ) => { return
; }; const testString = 'This is a string with a self closing custom component: '; const expectedElement = createElement( Fragment, null, 'This is a string with a self closing custom component: ', createElement( TestComponent ) ); expect( JSON.stringify( createInterpolateElement( testString, { TestComponent: , } ) ) ).toEqual( JSON.stringify( expectedElement ) ); } ); it( 'throws an error with an invalid element in the conversion map', () => { const test = () => createInterpolateElement( 'This is a string', { invalid: 10, } ); expect( test ).toThrow( TypeError ); } ); it( 'returns expected output for complex replacement', () => { class TestComponent extends Component { render() { return
; } } const testString = 'This is a complex string with ' + 'a nested emphasized string link and value: '; const expectedElement = createElement( Fragment, null, 'This is a complex string with a ', createElement( 'a', null, 'nested ', createElement( 'em', null, 'emphasized string' ), ' link' ), ' and value: ', createElement( TestComponent ) ); expect( JSON.stringify( createInterpolateElement( testString, { TestComponent: , em1: , a1: createElement( 'a' ), } ) ) ).toEqual( JSON.stringify( expectedElement ) ); } ); it( 'renders expected components across renders for keys in use', () => { const TestComponent = ( { switchKey } ) => { const elementConfig = switchKey ? { item: } : { item: }; return (
{ createInterpolateElement( 'This is a string!', elementConfig ) }
); }; const { container, rerender } = render( ); expect( container ).toContainHTML( 'string!' ); expect( container ).not.toContainHTML( '' ); rerender( ); expect( container ).toContainHTML( 'string!' ); expect( container ).not.toContainHTML( '' ); } ); it( 'extracts all tag names from a template literal string', () => { // Type-level test: verify ExtractTags extracts all tags correctly. type Tags = ExtractTags< 'link and bold nested' >; const tags: Tags[] = [ 'a', 'b', 'c' ]; expect( tags ).toHaveLength( 3 ); } ); it( 'extracts tags from a TransformedText input', () => { // Type-level test: verify InterpolationString unwraps TransformedText. type Text = InterpolationString< string & { readonly __transformedText: 'link and emphasis'; } >; type Tags = ExtractTags< Text >; const tags: Tags[] = [ 'a', 'em' ]; expect( tags ).toHaveLength( 2 ); } ); it( 'extracts tags from a sprintf (TransformedText) input', () => { // Type-level test: sprintf returns TransformedText, so // InterpolationString unwraps it for tag inference. type SprintfResult = string & { readonly __transformedText: '%1$s wrote %2$s'; }; const _check: InterpolationInput = '' as SprintfResult; void _check; type Text = InterpolationString< SprintfResult >; type Tags = ExtractTags< Text >; const tags: Tags[] = [ 'Name', 'Link' ]; expect( tags ).toHaveLength( 2 ); } ); it( 'handles parsing emojii correctly', () => { const testString = '👳‍♀️🚨🤷‍♂️⛈️fully here'; const expectedElement = createElement( Fragment, null, '👳‍♀️', createElement( 'strong', null, '🚨🤷‍♂️⛈️fully' ), ' here' ); expect( JSON.stringify( createInterpolateElement( testString, { icon: , } ) ) ).toEqual( JSON.stringify( expectedElement ) ); } ); } );