/**
* @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
import {
elementToElement, attributeToElement, attributeToAttribute
} from '../../src/conversion/two-way-converters';
import Conversion from '../../src/conversion/conversion';
import UpcastDispatcher from '../../src/conversion/upcastdispatcher';
import { convertText, convertToModelFragment } from '../../src/conversion/upcast-converters';
import EditingController from '../../src/controller/editingcontroller';
import Model from '../../src/model/model';
import ModelRange from '../../src/model/range';
import { stringify as viewStringify, parse as viewParse } from '../../src/dev-utils/view';
import { stringify as modelStringify } from '../../src/dev-utils/model';
describe( 'two-way-converters', () => {
let viewDispatcher, model, schema, conversion, modelRoot, viewRoot;
beforeEach( () => {
model = new Model();
const controller = new EditingController( model );
const modelDoc = model.document;
modelRoot = modelDoc.createRoot();
viewRoot = controller.view.getRoot();
// Set name of view root the same as dom root.
// This is a mock of attaching view root to dom root.
viewRoot._name = 'div';
schema = model.schema;
schema.extend( '$text', {
allowAttributes: [ 'bold' ]
} );
schema.register( 'paragraph', {
inheritAllFrom: '$block'
} );
viewDispatcher = new UpcastDispatcher( model, { schema } );
viewDispatcher.on( 'text', convertText() );
viewDispatcher.on( 'element', convertToModelFragment(), { priority: 'lowest' } );
viewDispatcher.on( 'documentFragment', convertToModelFragment(), { priority: 'lowest' } );
conversion = new Conversion();
conversion.register( 'upcast', [ viewDispatcher ] );
conversion.register( 'downcast', [ controller.downcastDispatcher ] );
} );
describe( 'elementToElement', () => {
it( 'config.view is a string', () => {
elementToElement( conversion, { model: 'paragraph', view: 'p' } );
test( '
Foo
', 'Foo' );
} );
it( 'config.view is an object', () => {
schema.register( 'fancyParagraph', {
inheritAllFrom: 'paragraph'
} );
elementToElement( conversion, {
model: 'fancyParagraph',
view: {
name: 'p',
class: 'fancy'
}
} );
test( 'Foo
', 'Foo' );
} );
it( 'config.view is an object with upcastAlso defined', () => {
elementToElement( conversion, {
model: 'paragraph',
view: 'p',
upcastAlso: [
'div',
{
// Match any name.
name: /./,
style: {
display: 'block'
}
}
]
} );
test( 'Foo
', 'Foo' );
test( 'Foo
', 'Foo', 'Foo
' );
test( 'Foo', 'Foo', 'Foo
' );
} );
it( 'upcastAlso given as a function', () => {
schema.register( 'heading', {
inheritAllFrom: '$block'
} );
elementToElement( conversion, {
model: 'heading',
view: 'h2',
upcastAlso: viewElement => {
const fontSize = viewElement.getStyle( 'font-size' );
if ( !fontSize ) {
return null;
}
const match = fontSize.match( /(\d+)\s*px/ );
if ( !match ) {
return null;
}
const size = Number( match[ 1 ] );
if ( size >= 26 ) {
return { name: true, style: [ 'font-size' ] };
}
return null;
}
} );
elementToElement( conversion, {
model: 'paragraph',
view: 'p'
} );
test( '', '' );
test( '', '', '' );
test( '', '' );
test( '', '', '' );
} );
} );
describe( 'attributeToElement', () => {
beforeEach( () => {
elementToElement( conversion, { model: 'paragraph', view: 'p' } );
} );
it( 'config.view is a string', () => {
attributeToElement( conversion, 'bold', { view: 'strong' } );
test( 'Foo bar
', '<$text bold="true">Foo$text> bar' );
} );
it( 'config.view is an object', () => {
attributeToElement( conversion, 'bold', {
view: {
name: 'span',
class: 'bold'
}
} );
test( 'Foo bar
', '<$text bold="true">Foo$text> bar' );
} );
it( 'config.view is an object with upcastAlso defined', () => {
attributeToElement( conversion, 'bold', {
view: 'strong',
upcastAlso: [
'b',
{
name: 'span',
class: 'bold'
},
{
name: 'span',
style: {
'font-weight': 'bold'
}
},
viewElement => {
const fontWeight = viewElement.getStyle( 'font-weight' );
if ( viewElement.is( 'span' ) && fontWeight && /\d+/.test( fontWeight ) && Number( fontWeight ) > 500 ) {
return {
name: true,
style: [ 'font-weight' ]
};
}
}
]
} );
test(
'Foo
',
'<$text bold="true">Foo$text>'
);
test(
'Foo
',
'<$text bold="true">Foo$text>',
'Foo
'
);
test(
'Foo
',
'<$text bold="true">Foo$text>',
'Foo
'
);
test(
'Foo
',
'<$text bold="true">Foo$text>',
'Foo
'
);
test(
'Foo
',
'Foo',
'Foo
'
);
test(
'Foo
',
'<$text bold="true">Foo$text>',
'Foo
'
);
} );
it( 'config.model is a string', () => {
schema.extend( '$text', {
allowAttributes: [ 'styled' ]
} );
attributeToElement( conversion, 'styled', {
model: 'dark',
view: {
name: 'span',
class: [ 'styled', 'styled-dark' ]
}
} );
test( 'Foo bar
', '<$text styled="dark">Foo$text> bar' );
} );
it( 'config is an array', () => {
schema.extend( '$text', {
allowAttributes: [ 'fontSize' ]
} );
attributeToElement( conversion, 'fontSize', [
{
model: 'big',
view: {
name: 'span',
style: {
'font-size': '1.2em'
}
}
},
{
model: 'small',
view: {
name: 'span',
style: {
'font-size': '0.8em'
}
}
}
] );
test(
'Foo bar
',
'<$text fontSize="big">Foo$text> bar'
);
test(
'Foo bar
',
'<$text fontSize="small">Foo$text> bar'
);
} );
it( 'config is an array with upcastAlso defined', () => {
schema.extend( '$text', {
allowAttributes: [ 'fontSize' ]
} );
attributeToElement( conversion, 'fontSize', [
{
model: 'big',
view: {
name: 'span',
style: {
'font-size': '1.2em'
}
},
upcastAlso: viewElement => {
const fontSize = viewElement.getStyle( 'font-size' );
if ( !fontSize ) {
return null;
}
const match = fontSize.match( /(\d+)\s*px/ );
if ( !match ) {
return null;
}
const size = Number( match[ 1 ] );
if ( viewElement.is( 'span' ) && size > 10 ) {
return { name: true, style: [ 'font-size' ] };
}
return null;
}
},
{
model: 'small',
view: {
name: 'span',
style: {
'font-size': '0.8em'
}
},
upcastAlso: viewElement => {
const fontSize = viewElement.getStyle( 'font-size' );
if ( !fontSize ) {
return null;
}
const match = fontSize.match( /(\d+)\s*px/ );
if ( !match ) {
return null;
}
const size = Number( match[ 1 ] );
if ( viewElement.is( 'span' ) && size < 10 ) {
return { name: true, style: [ 'font-size' ] };
}
return null;
}
}
] );
test(
'Foo bar
',
'<$text fontSize="big">Foo$text> bar'
);
test(
'Foo bar
',
'<$text fontSize="big">Foo$text> bar',
'Foo bar
'
);
test(
'Foo bar
',
'<$text fontSize="small">Foo$text> bar'
);
test(
'Foo bar
',
'<$text fontSize="small">Foo$text> bar',
'Foo bar
'
);
test(
'Foo bar
',
'Foo bar',
'Foo bar
'
);
} );
} );
describe( 'attributeToAttribute', () => {
beforeEach( () => {
elementToElement( conversion, { model: 'image', view: 'img' } );
schema.register( 'image', {
inheritAllFrom: '$block',
} );
} );
it( 'config is not set', () => {
schema.extend( 'image', {
allowAttributes: [ 'src' ]
} );
attributeToAttribute( conversion, 'src' );
test( '
', '' );
} );
it( 'config.view is a string', () => {
schema.extend( 'image', {
allowAttributes: [ 'source' ]
} );
attributeToAttribute( conversion, 'source', { view: 'src' } );
test( '
', '' );
} );
it( 'config.view is an object', () => {
schema.extend( 'image', {
allowAttributes: [ 'aside' ]
} );
attributeToAttribute( conversion, 'aside', {
model: true,
view: {
name: 'img',
key: 'class',
value: 'aside half-size'
}
} );
elementToElement( conversion, { model: 'paragraph', view: 'p' } );
test( '
', '' );
test( '', '', '' );
} );
it( 'config is an array', () => {
schema.extend( 'image', {
allowAttributes: [ 'styled' ]
} );
attributeToAttribute( conversion, 'styled', [
{
model: 'dark',
view: {
key: 'class',
value: 'styled styled-dark'
}
},
{
model: 'light',
view: {
key: 'class',
value: 'styled styled-light'
}
}
] );
test( '
', '' );
test( '
', '' );
} );
it( 'config is an array with upcastAlso defined', () => {
elementToElement( conversion, { model: 'paragraph', view: 'p' } );
schema.extend( 'paragraph', {
allowAttributes: [ 'align' ]
} );
attributeToAttribute( conversion, 'align', [
{
model: 'right',
view: {
key: 'class',
value: 'align-right'
},
upcastAlso: viewElement => {
if ( viewElement.getStyle( 'text-align' ) == 'right' ) {
return {
style: [ 'text-align' ]
};
}
return null;
}
},
{
model: 'center',
view: {
key: 'class',
value: 'align-center'
},
upcastAlso: {
style: {
'text-align': 'center'
}
}
}
] );
test(
'Foo
',
'Foo'
);
test(
'Foo
',
'Foo',
'Foo
'
);
test(
'Foo
',
'Foo'
);
test(
'Foo
',
'Foo',
'Foo
'
);
} );
} );
function test( input, expectedModel, expectedView = null ) {
loadData( input );
expect( modelStringify( model.document.getRoot() ) ).to.equal( expectedModel );
expect( viewStringify( viewRoot, null, { ignoreRoot: true } ) ).to.equal( expectedView || input );
}
function loadData( input ) {
const parsedView = viewParse( input );
const convertedModel = viewDispatcher.convert( parsedView );
model.change( writer => {
writer.remove( ModelRange.createFromParentsAndOffsets( modelRoot, 0, modelRoot, modelRoot.maxOffset ) );
writer.insert( convertedModel, modelRoot, 0 );
} );
}
} );