import { findOne } from 'domutils';
import TBlockImpl from '../tree/TBlockCtor';
import TDocumentCtor from '../tree/TDocumentImpl';
import { TRenderEngine, TRenderEngineOptions } from '../TRenderEngine';
import HTMLContentModel from '../model/HTMLContentModel';
import HTMLElementModel from '../model/HTMLElementModel';
import TEmptyCtor from '../tree/TEmptyCtor';
import { rfc002Source } from '../flow/__tests__/shared';
const href = 'https://domain.com';
const htmlDocument = `
Voici un Titre
Hello world!
`;
const defaultTTreeBuilder = new TRenderEngine();
describe('TRenderEngine > customizeHTMLModels option', () => {
it('should allow to change a model type to block', () => {
const specialTTreeBuilder = new TRenderEngine({
customizeHTMLModels(models) {
const newModels = {
...models,
em: models.em.extend({ contentModel: HTMLContentModel.block })
};
return newModels;
}
});
const ttree = specialTTreeBuilder.buildTTree(
'This should be a block!'
);
expect(ttree).toMatchSnapshot();
});
it('should allow to make an untranslatable element translatable', () => {
const specialTTreeBuilder = new TRenderEngine({
customizeHTMLModels(models) {
const newModels = {
...models,
button: models.button.extend({
contentModel: HTMLContentModel.block
})
};
return newModels;
}
});
const ttree = specialTTreeBuilder.buildTTree('');
expect(ttree).toMatchSnapshot();
});
it('should allow to register custom block tags', () => {
const specialTTreeBuilder = new TRenderEngine({
customizeHTMLModels(models) {
const newModels = {
...models,
customtag: HTMLElementModel.fromCustomModel({
contentModel: HTMLContentModel.block,
tagName: 'customtag'
})
};
return newModels;
}
});
const ttree = specialTTreeBuilder.buildTTree(
'This should be a block!'
);
expect(ttree).toMatchSnapshot();
});
describe('should allow to register custom textual tags', () => {
const specialTTreeBuilder = new TRenderEngine({
customizeHTMLModels(models) {
const newModels = {
...models,
customtag: HTMLElementModel.fromCustomModel({
contentModel: HTMLContentModel.textual,
tagName: 'customtag'
})
};
return newModels;
}
});
it('should translate to TText', () => {
const ttree = specialTTreeBuilder.buildTTree(
'This should be a text!'
);
expect(ttree).toMatchSnapshot();
});
it('should preserve tag when isOpaque is set to true.', () => {
const ttree = specialTTreeBuilder.buildTTree('');
expect(ttree).toMatchSnapshot();
});
});
describe('should allow to register custom mixed tags', () => {
const specialTTreeBuilder = new TRenderEngine({
customizeHTMLModels(models) {
const newModels = {
...models,
customtag: HTMLElementModel.fromCustomModel({
contentModel: HTMLContentModel.mixed,
tagName: 'customtag'
})
};
return newModels;
}
});
it('should handle mixed tags surrounding blocks like blocks', () => {
const ttree = specialTTreeBuilder.buildTTree(
''
);
expect(ttree).toMatchSnapshot();
});
it('should translate mixed tags inside phrasing with text children to TText', () => {
const ttree = specialTTreeBuilder.buildTTree(
'hi!'
);
expect(ttree).toMatchSnapshot();
});
});
});
describe('TRenderEngine > buildTTree method', () => {
it('should handle special case when baseStyle.fontSize is not a number', () => {
const specialTTreeBuilder = new TRenderEngine({
dangerouslyDisableHoisting: false,
stylesConfig: {
baseStyle: {
fontSize: '1rem'
}
}
});
const ttree = specialTTreeBuilder.buildTTree('A');
expect(ttree).toMatchSnapshot();
});
it('should not provide a root fontSize when enableUserAgentStyles is set to false', () => {
const specialTTreeBuilder = new TRenderEngine({
stylesConfig: {
enableUserAgentStyles: false
}
});
const ttree = specialTTreeBuilder.buildTTree('A');
expect(ttree).toMatchSnapshot();
});
it('should still parse inline CSS styles when enableUserAgentStyles is set to false', () => {
const specialTTreeBuilder = new TRenderEngine({
stylesConfig: {
enableUserAgentStyles: false,
enableCSSInlineProcessing: true
}
});
const tdoc = specialTTreeBuilder.buildTTree(
''
);
expect(tdoc).toMatchSnapshot();
});
it('should handle the case where the root element is a body element', () => {
const tdoc = defaultTTreeBuilder.buildTTree('');
expect(tdoc).toMatchSnapshot();
});
it('given a HTML document, should return an instance of TDocument which has one TBlock(body) child', () => {
const tdoc = defaultTTreeBuilder.buildTTree(htmlDocument);
expect(tdoc).toBeInstanceOf(TDocumentCtor);
expect(tdoc.children).toHaveLength(2);
expect(tdoc.children[0]).toBeInstanceOf(TEmptyCtor);
expect(tdoc.children[1]).toBeInstanceOf(TBlockImpl);
expect(tdoc).toMatchSnapshot();
});
describe('regarding context parsing', () => {
it('should register html lang attrib', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
''
);
expect(tdoc.context).toMatchObject({ lang: 'fr' });
});
it('should register html dir attrib', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
''
);
expect(tdoc.context).toMatchObject({ dir: 'rtl' });
});
it('should register html dir attrib when head is defined', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
''
);
expect(tdoc.context).toMatchObject({ dir: 'rtl' });
});
it('should register charset', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
''
);
expect(tdoc.context).toMatchObject({ charset: 'latin1' });
});
it('should register and trim title', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
' Voici un Titre '
);
expect(tdoc.context).toMatchObject({ title: 'Voici un Titre' });
});
it('should ignore empty meta tags', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
''
);
expect(tdoc.context).toMatchObject({});
});
it('should register base with attributes', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
``
);
expect(tdoc.context).toMatchObject({
baseHref: href,
baseTarget: '_blank'
});
});
it('should fallback to defaults when base attributes are missing', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
''
);
expect(tdoc.context).toMatchObject({
baseHref: 'about:blank',
baseTarget: '_self'
});
});
it('should register other meta tags attribtues in the meta array', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
''
);
expect(tdoc.context).toMatchObject({
meta: [
{
name: 'keywords',
value: 'birds'
}
]
});
});
it('should register link tags attributes in the link array', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
''
);
expect(tdoc.context).toMatchObject({
links: [
{
rel: 'author license',
href: '/about'
}
]
});
});
it('it should ignore irrelevant tags', () => {
const tdoc = defaultTTreeBuilder.buildTTree(
'This tag should be ignored'
);
expect(tdoc.context).toMatchObject({});
});
});
it('should handle html snippets', () => {
const snippet = '';
const tdoc = defaultTTreeBuilder.buildTTree(snippet);
expect(tdoc).toBeInstanceOf(TDocumentCtor);
expect(tdoc).toMatchSnapshot();
});
it('should handle html snippets with multiple root nodes', () => {
const snippet = '';
const tdoc = defaultTTreeBuilder.buildTTree(snippet);
expect(tdoc).toBeInstanceOf(TDocumentCtor);
expect(tdoc).toMatchSnapshot();
});
describe('should have its children inherit from UA styles when enableUserAgentStyles is enabled', () => {
const config: TRenderEngineOptions = {
stylesConfig: {
enableUserAgentStyles: true
}
};
const customTTreeBuilder = new TRenderEngine(config);
it('should work with em tags', () => {
const tdoc = customTTreeBuilder.buildTTree(
'This should be italic'
);
expect(tdoc).toMatchSnapshot();
});
});
describe('should retain own baseStyles', () => {
const config = {
stylesConfig: {
baseStyle: {
marginTop: 10
}
}
};
const customTTreeBuilder = new TRenderEngine(config);
it('when provided a html snippet', () => {
const tdoc = customTTreeBuilder.buildTTree('');
expect(tdoc).toMatchSnapshot();
expect(tdoc.getNativeStyles().marginTop).toBe(10);
});
});
describe('should have its children inherit from baseStyles', () => {
const config = {
stylesConfig: {
baseStyle: {
fontSize: 12
}
}
};
const customTTreeBuilder = new TRenderEngine(config);
it('when provided a full html page markup', () => {
const tdoc = customTTreeBuilder.buildTTree(
'This text should inherit baseStyles
'
);
expect(tdoc).toMatchSnapshot();
});
it('when provided a html snippet', () => {
const tdoc = customTTreeBuilder.buildTTree(
'This text should inherit baseStyles
'
);
expect(tdoc).toMatchSnapshot();
});
});
it('should support domVisitors', () => {
const customTTreeBuilder = new TRenderEngine({
domVisitors: {
onText(text) {
text.data = 'hey';
}
}
});
const tdoc = customTTreeBuilder.buildTTree(
'This text should inherit baseStyles'
);
expect(tdoc).toMatchSnapshot();
});
it('should support ignoredDomTags', () => {
const customTTreeBuilder = new TRenderEngine({
ignoredDomTags: ['em']
});
const tdoc = customTTreeBuilder.buildTTree('');
expect(tdoc).toMatchSnapshot();
});
it('should support ignoreDomNode with text nodes', () => {
const customTTreeBuilder = new TRenderEngine({
ignoreDomNode: (node) => node.type === 'text'
});
const tdoc = customTTreeBuilder.buildTTree('Text!');
expect(tdoc).toMatchSnapshot();
});
it('should support disabling hoisting', () => {
const customTTreeBuilder = new TRenderEngine({
dangerouslyDisableHoisting: true
});
expect(customTTreeBuilder.buildTTree(rfc002Source)).toMatchSnapshot();
});
it('should support disabling whitespace collapsing', () => {
const customTTreeBuilder = new TRenderEngine({
dangerouslyDisableWhitespaceCollapsing: true
});
expect(customTTreeBuilder.buildTTree(rfc002Source)).toMatchSnapshot();
});
it('should support ignoredTags', () => {
const customTTreeBuilder = new TRenderEngine({
ignoredDomTags: ['div']
});
const tdoc = customTTreeBuilder.buildTTree(
'Text'
);
expect(tdoc).toMatchSnapshot();
});
it('should support selectDomRoot returning a child', () => {
const customTTreeBuilder = new TRenderEngine({
selectDomRoot(node) {
const article = findOne(
(elem) => elem.tagName === 'article',
node.children as any,
true
);
return article || node;
}
});
const tdoc = customTTreeBuilder.buildTTree(
''
);
expect(tdoc).toMatchSnapshot();
});
it('should support selectDomRoot returning the passed node', () => {
const customTTreeBuilder = new TRenderEngine({
selectDomRoot(node) {
return node;
}
});
const tdoc = customTTreeBuilder.buildTTree(
''
);
expect(tdoc).toMatchSnapshot();
});
it('should support selectDomRoot returning a falsy value', () => {
const customTTreeBuilder = new TRenderEngine({
selectDomRoot() {
return false;
}
});
const tdoc = customTTreeBuilder.buildTTree(
''
);
expect(tdoc).toMatchSnapshot();
});
it('should support setMarkersForTNode', () => {
const customTTreeBuilder = new TRenderEngine({
setMarkersForTNode(targetMarkers) {
//@ts-expect-error
targetMarkers.toto = 'hello';
}
});
const tdoc = customTTreeBuilder.buildTTree(
''
);
//@ts-expect-error
expect(tdoc.markers.toto).toBe('hello');
});
it('should implement getHTMLElementsModels', () => {
const ttreeBuilder = new TRenderEngine();
expect(ttreeBuilder.getHTMLElementsModels()).toBeDefined();
});
});