/**
* 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 ) );
} );
} );