@@ -7,8 +7,11 @@ import { default as Schema, SchemaItem } from '../../../src/model/schema';
77import Document from '../../../src/model/document' ;
88import Element from '../../../src/model/element' ;
99import Position from '../../../src/model/position' ;
10+ import Range from '../../../src/model/range' ;
11+ import Selection from '../../../src/model/selection' ;
1012import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror' ;
1113import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils' ;
14+ import { setData , stringify } from '../../../src/dev-utils/model' ;
1215
1316testUtils . createSinonSandbox ( ) ;
1417
@@ -61,7 +64,7 @@ describe( 'Schema', () => {
6164 } ) ;
6265 } ) ;
6366
64- describe ( 'registerItem' , ( ) => {
67+ describe ( 'registerItem() ' , ( ) => {
6568 it ( 'should register in schema item under given name' , ( ) => {
6669 schema . registerItem ( 'new' ) ;
6770
@@ -101,7 +104,7 @@ describe( 'Schema', () => {
101104 } ) ;
102105 } ) ;
103106
104- describe ( 'hasItem' , ( ) => {
107+ describe ( 'hasItem() ' , ( ) => {
105108 it ( 'should return true if given item name has been registered in schema' , ( ) => {
106109 expect ( schema . hasItem ( '$block' ) ) . to . be . true ;
107110 } ) ;
@@ -111,7 +114,7 @@ describe( 'Schema', () => {
111114 } ) ;
112115 } ) ;
113116
114- describe ( '_getItem' , ( ) => {
117+ describe ( '_getItem() ' , ( ) => {
115118 it ( 'should return SchemaItem registered under given name' , ( ) => {
116119 schema . registerItem ( 'new' ) ;
117120
@@ -127,7 +130,7 @@ describe( 'Schema', () => {
127130 } ) ;
128131 } ) ;
129132
130- describe ( 'allow' , ( ) => {
133+ describe ( 'allow() ' , ( ) => {
131134 it ( 'should add passed query to allowed in schema' , ( ) => {
132135 schema . registerItem ( 'p' , '$block' ) ;
133136 schema . registerItem ( 'div' , '$block' ) ;
@@ -140,7 +143,7 @@ describe( 'Schema', () => {
140143 } ) ;
141144 } ) ;
142145
143- describe ( 'disallow' , ( ) => {
146+ describe ( 'disallow() ' , ( ) => {
144147 it ( 'should add passed query to disallowed in schema' , ( ) => {
145148 schema . registerItem ( 'p' , '$block' ) ;
146149 schema . registerItem ( 'div' , '$block' ) ;
@@ -155,7 +158,7 @@ describe( 'Schema', () => {
155158 } ) ;
156159 } ) ;
157160
158- describe ( 'check' , ( ) => {
161+ describe ( 'check() ' , ( ) => {
159162 describe ( 'string or array of strings as inside' , ( ) => {
160163 it ( 'should return false if given element is not registered in schema' , ( ) => {
161164 expect ( schema . check ( { name : 'new' , inside : [ 'div' , 'header' ] } ) ) . to . be . false ;
@@ -409,7 +412,7 @@ describe( 'Schema', () => {
409412 } ) ;
410413 } ) ;
411414
412- describe ( 'itemExtends' , ( ) => {
415+ describe ( 'itemExtends() ' , ( ) => {
413416 it ( 'should return true if given item extends another given item' , ( ) => {
414417 schema . registerItem ( 'div' , '$block' ) ;
415418 schema . registerItem ( 'myDiv' , 'div' ) ;
@@ -438,7 +441,7 @@ describe( 'Schema', () => {
438441 } ) ;
439442 } ) ;
440443
441- describe ( '_normalizeQueryPath' , ( ) => {
444+ describe ( '_normalizeQueryPath() ' , ( ) => {
442445 it ( 'should normalize string with spaces to an array of strings' , ( ) => {
443446 expect ( Schema . _normalizeQueryPath ( '$root div strong' ) ) . to . deep . equal ( [ '$root' , 'div' , 'strong' ] ) ;
444447 } ) ;
@@ -471,4 +474,167 @@ describe( 'Schema', () => {
471474 expect ( Schema . _normalizeQueryPath ( input ) ) . to . deep . equal ( [ '$root' , 'div' , 'p' , 'strong' ] ) ;
472475 } ) ;
473476 } ) ;
477+
478+ describe ( 'checkAttributeInSelection()' , ( ) => {
479+ const attribute = 'bold' ;
480+ let doc , schema ;
481+
482+ beforeEach ( ( ) => {
483+ doc = new Document ( ) ;
484+ doc . createRoot ( ) ;
485+
486+ schema = doc . schema ;
487+
488+ schema . registerItem ( 'p' , '$block' ) ;
489+ schema . registerItem ( 'h1' , '$block' ) ;
490+ schema . registerItem ( 'img' , '$inline' ) ;
491+
492+ // Bold text is allowed only in P.
493+ schema . allow ( { name : '$text' , attributes : 'bold' , inside : 'p' } ) ;
494+ schema . allow ( { name : 'p' , attributes : 'bold' , inside : '$root' } ) ;
495+
496+ // Disallow bold on image.
497+ schema . disallow ( { name : 'img' , attributes : 'bold' , inside : '$root' } ) ;
498+ } ) ;
499+
500+ describe ( 'when selection is collapsed' , ( ) => {
501+ it ( 'should return true if characters with the attribute can be placed at caret position' , ( ) => {
502+ setData ( doc , '<p>f[]oo</p>' ) ;
503+ expect ( schema . checkAttributeInSelection ( doc . selection , attribute ) ) . to . be . true ;
504+ } ) ;
505+
506+ it ( 'should return false if characters with the attribute cannot be placed at caret position' , ( ) => {
507+ setData ( doc , '<h1>[]</h1>' ) ;
508+ expect ( schema . checkAttributeInSelection ( doc . selection , attribute ) ) . to . be . false ;
509+
510+ setData ( doc , '[]' ) ;
511+ expect ( schema . checkAttributeInSelection ( doc . selection , attribute ) ) . to . be . false ;
512+ } ) ;
513+ } ) ;
514+
515+ describe ( 'when selection is not collapsed' , ( ) => {
516+ it ( 'should return true if there is at least one node in selection that can have the attribute' , ( ) => {
517+ // Simple selection on a few characters.
518+ setData ( doc , '<p>[foo]</p>' ) ;
519+ expect ( schema . checkAttributeInSelection ( doc . selection , attribute ) ) . to . be . true ;
520+
521+ // Selection spans over characters but also include nodes that can't have attribute.
522+ setData ( doc , '<p>fo[o<img />b]ar</p>' ) ;
523+ expect ( schema . checkAttributeInSelection ( doc . selection , attribute ) ) . to . be . true ;
524+
525+ // Selection on whole root content. Characters in P can have an attribute so it's valid.
526+ setData ( doc , '[<p>foo<img />bar</p><h1></h1>]' ) ;
527+ expect ( schema . checkAttributeInSelection ( doc . selection , attribute ) ) . to . be . true ;
528+
529+ // Selection on empty P. P can have the attribute.
530+ setData ( doc , '[<p></p>]' ) ;
531+ expect ( schema . checkAttributeInSelection ( doc . selection , attribute ) ) . to . be . true ;
532+ } ) ;
533+
534+ it ( 'should return false if there are no nodes in selection that can have the attribute' , ( ) => {
535+ // Selection on DIV which can't have bold text.
536+ setData ( doc , '[<h1></h1>]' ) ;
537+ expect ( schema . checkAttributeInSelection ( doc . selection , attribute ) ) . to . be . false ;
538+
539+ // Selection on two images which can't be bold.
540+ setData ( doc , '<p>foo[<img /><img />]bar</p>' ) ;
541+ expect ( schema . checkAttributeInSelection ( doc . selection , attribute ) ) . to . be . false ;
542+ } ) ;
543+ } ) ;
544+ } ) ;
545+
546+ describe ( 'getValidRanges()' , ( ) => {
547+ const attribute = 'bold' ;
548+ let doc , root , schema , ranges ;
549+
550+ beforeEach ( ( ) => {
551+ doc = new Document ( ) ;
552+ schema = doc . schema ;
553+ root = doc . createRoot ( ) ;
554+
555+ schema . registerItem ( 'p' , '$block' ) ;
556+ schema . registerItem ( 'h1' , '$block' ) ;
557+ schema . registerItem ( 'img' , '$inline' ) ;
558+
559+ schema . allow ( { name : '$text' , attributes : 'bold' , inside : 'p' } ) ;
560+ schema . allow ( { name : 'p' , attributes : 'bold' , inside : '$root' } ) ;
561+
562+ setData ( doc , '<p>foo<img />bar</p>' ) ;
563+ ranges = [ Range . createOn ( root . getChild ( 0 ) ) ] ;
564+ } ) ;
565+
566+ it ( 'should return unmodified ranges when attribute is allowed on each item (text is not allowed in img)' , ( ) => {
567+ schema . allow ( { name : 'img' , attributes : 'bold' , inside : 'p' } ) ;
568+
569+ expect ( schema . getValidRanges ( ranges , attribute ) ) . to . deep . equal ( ranges ) ;
570+ } ) ;
571+
572+ it ( 'should return unmodified ranges when attribute is allowed on each item (text is allowed in img)' , ( ) => {
573+ schema . allow ( { name : 'img' , attributes : 'bold' , inside : 'p' } ) ;
574+ schema . allow ( { name : '$text' , inside : 'img' } ) ;
575+
576+ expect ( schema . getValidRanges ( ranges , attribute ) ) . to . deep . equal ( ranges ) ;
577+ } ) ;
578+
579+ it ( 'should return two ranges when attribute is not allowed on one item' , ( ) => {
580+ schema . allow ( { name : 'img' , attributes : 'bold' , inside : 'p' } ) ;
581+ schema . allow ( { name : '$text' , inside : 'img' } ) ;
582+
583+ setData ( doc , '[<p>foo<img>xxx</img>bar</p>]' ) ;
584+
585+ const validRanges = schema . getValidRanges ( doc . selection . getRanges ( ) , attribute ) ;
586+ const sel = new Selection ( ) ;
587+ sel . setRanges ( validRanges ) ;
588+
589+ expect ( stringify ( root , sel ) ) . to . equal ( '[<p>foo<img>]xxx[</img>bar</p>]' ) ;
590+ } ) ;
591+
592+ it ( 'should return three ranges when attribute is not allowed on one element but is allowed on its child' , ( ) => {
593+ schema . allow ( { name : '$text' , inside : 'img' } ) ;
594+ schema . allow ( { name : '$text' , attributes : 'bold' , inside : 'img' } ) ;
595+
596+ setData ( doc , '[<p>foo<img>xxx</img>bar</p>]' ) ;
597+
598+ const validRanges = schema . getValidRanges ( doc . selection . getRanges ( ) , attribute ) ;
599+ const sel = new Selection ( ) ;
600+ sel . setRanges ( validRanges ) ;
601+
602+ expect ( stringify ( root , sel ) ) . to . equal ( '[<p>foo]<img>[xxx]</img>[bar</p>]' ) ;
603+ } ) ;
604+
605+ it ( 'should not leak beyond the given ranges' , ( ) => {
606+ setData ( doc , '<p>[foo<img></img>bar]x[bar<img></img>foo]</p>' ) ;
607+
608+ const validRanges = schema . getValidRanges ( doc . selection . getRanges ( ) , attribute ) ;
609+ const sel = new Selection ( ) ;
610+ sel . setRanges ( validRanges ) ;
611+
612+ expect ( stringify ( root , sel ) ) . to . equal ( '<p>[foo]<img></img>[bar]x[bar]<img></img>[foo]</p>' ) ;
613+ } ) ;
614+
615+ it ( 'should correctly handle a range which ends in a disallowed position' , ( ) => {
616+ schema . allow ( { name : '$text' , inside : 'img' } ) ;
617+
618+ setData ( doc , '<p>[foo<img>bar]</img>bom</p>' ) ;
619+
620+ const validRanges = schema . getValidRanges ( doc . selection . getRanges ( ) , attribute ) ;
621+ const sel = new Selection ( ) ;
622+ sel . setRanges ( validRanges ) ;
623+
624+ expect ( stringify ( root , sel ) ) . to . equal ( '<p>[foo]<img>bar</img>bom</p>' ) ;
625+ } ) ;
626+
627+ it ( 'should split range into two ranges and omit disallowed element' , ( ) => {
628+ // Disallow bold on img.
629+ doc . schema . disallow ( { name : 'img' , attributes : 'bold' , inside : 'p' } ) ;
630+
631+ const result = schema . getValidRanges ( ranges , attribute ) ;
632+
633+ expect ( result ) . to . length ( 2 ) ;
634+ expect ( result [ 0 ] . start . path ) . to . members ( [ 0 ] ) ;
635+ expect ( result [ 0 ] . end . path ) . to . members ( [ 0 , 3 ] ) ;
636+ expect ( result [ 1 ] . start . path ) . to . members ( [ 0 , 4 ] ) ;
637+ expect ( result [ 1 ] . end . path ) . to . members ( [ 1 ] ) ;
638+ } ) ;
639+ } ) ;
474640} ) ;
0 commit comments