@@ -384,6 +384,110 @@ describe('Entries Helper', () => {
384384 expect ( restoreJsonRteEntryRefs ) . to . be . a ( 'function' ) ;
385385 } ) ;
386386
387+ it ( 'should remap entry UIDs containing regex special characters in HTML RTE' , ( ) => {
388+ const sourceUid = 'source.entry.1' ;
389+ const ctSchema = [
390+ { uid : 'html_rte_field' , data_type : 'text' , field_metadata : { rich_text_type : true } }
391+ ] ;
392+ const sourceStackEntry = {
393+ html_rte_field : `<p>ref</p><span data-uid="${ sourceUid } "></span>`
394+ } ;
395+ // non-empty: restoreJsonRteEntryRefs skips falsy fields before restoring from source
396+ const entry = { html_rte_field : '<p></p>' } ;
397+
398+ const result = restoreJsonRteEntryRefs ( entry , sourceStackEntry , ctSchema , {
399+ uidMapper : { [ sourceUid ] : 'bltTargetEntry1' } ,
400+ mappedAssetUids : { } ,
401+ mappedAssetUrls : { }
402+ } ) ;
403+
404+ expect ( result . html_rte_field ) . to . not . include ( sourceUid ) ;
405+ expect ( result . html_rte_field ) . to . include ( 'bltTargetEntry1' ) ;
406+ } ) ;
407+
408+ it ( 'should leave an unmapped special character entry UID untouched in HTML RTE' , ( ) => {
409+ const sourceUid = 'source.entry.1' ;
410+ const ctSchema = [
411+ { uid : 'html_rte_field' , data_type : 'text' , field_metadata : { rich_text_type : true } }
412+ ] ;
413+ const sourceStackEntry = {
414+ html_rte_field : `<span data-uid="${ sourceUid } "></span>`
415+ } ;
416+ // non-empty: restoreJsonRteEntryRefs skips falsy fields before restoring from source
417+ const entry = { html_rte_field : '<p></p>' } ;
418+
419+ const result = restoreJsonRteEntryRefs ( entry , sourceStackEntry , ctSchema , {
420+ uidMapper : { 'other.entry' : 'bltOther' } ,
421+ mappedAssetUids : { } ,
422+ mappedAssetUrls : { }
423+ } ) ;
424+
425+ // no mapping applies, so the field must come through byte-for-byte, with no escape characters injected
426+ expect ( result . html_rte_field ) . to . equal ( sourceStackEntry . html_rte_field ) ;
427+ } ) ;
428+
429+ it ( 'should not let a UID that is a prefix of another UID clobber the longer one in HTML RTE' , ( ) => {
430+ const ctSchema = [
431+ { uid : 'html_rte_field' , data_type : 'text' , field_metadata : { rich_text_type : true } }
432+ ] ;
433+ const sourceStackEntry = {
434+ html_rte_field : '<span data-uid="entry.10"></span><span data-uid="entry.1"></span>'
435+ } ;
436+ const entry = { html_rte_field : '<p></p>' } ;
437+
438+ const result = restoreJsonRteEntryRefs ( entry , sourceStackEntry , ctSchema , {
439+ uidMapper : { 'entry.1' : 'bltShort' , 'entry.10' : 'bltLong' } ,
440+ mappedAssetUids : { } ,
441+ mappedAssetUrls : { }
442+ } ) ;
443+
444+ expect ( result . html_rte_field ) . to . equal ( '<span data-uid="bltLong"></span><span data-uid="bltShort"></span>' ) ;
445+ } ) ;
446+
447+ it ( 'should remap regex-special-character entry UIDs in a multiple HTML RTE field' , ( ) => {
448+ const sourceUid = 'source.entry.1' ;
449+ const ctSchema = [
450+ { uid : 'html_rte_field' , data_type : 'text' , field_metadata : { rich_text_type : true } , multiple : true }
451+ ] ;
452+ // array-branch matching is exact-element (Array.indexOf), not substring, so the mapped
453+ // UID must be a whole array element here to be found
454+ const sourceStackEntry = {
455+ html_rte_field : [ sourceUid , 'unrelated value' ]
456+ } ;
457+ const entry = { html_rte_field : [ 'placeholder' ] } ;
458+
459+ const result = restoreJsonRteEntryRefs ( entry , sourceStackEntry , ctSchema , {
460+ uidMapper : { [ sourceUid ] : 'bltTargetEntry1' } ,
461+ mappedAssetUids : { } ,
462+ mappedAssetUrls : { }
463+ } ) ;
464+
465+ expect ( result . html_rte_field [ 0 ] ) . to . equal ( 'bltTargetEntry1' ) ;
466+ expect ( result . html_rte_field [ 1 ] ) . to . equal ( 'unrelated value' ) ;
467+ } ) ;
468+
469+ it ( 'should leave a UID embedded inside a larger string untouched in a multiple HTML RTE field' , ( ) => {
470+ const sourceUid = 'source.entry.1' ;
471+ const ctSchema = [
472+ { uid : 'html_rte_field' , data_type : 'text' , field_metadata : { rich_text_type : true } , multiple : true }
473+ ] ;
474+ const sourceStackEntry = {
475+ html_rte_field : [ `<span data-uid="${ sourceUid } "></span>` ]
476+ } ;
477+ const entry = { html_rte_field : [ 'placeholder' ] } ;
478+
479+ const result = restoreJsonRteEntryRefs ( entry , sourceStackEntry , ctSchema , {
480+ uidMapper : { [ sourceUid ] : 'bltTargetEntry1' } ,
481+ mappedAssetUids : { } ,
482+ mappedAssetUrls : { }
483+ } ) ;
484+
485+ // known limitation, not touched by this fix: the multiple branch matches with
486+ // Array.prototype.indexOf (exact element equality), so a UID embedded inside a larger
487+ // string element is never found and the field passes through unchanged
488+ expect ( result . html_rte_field [ 0 ] ) . to . equal ( sourceStackEntry . html_rte_field [ 0 ] ) ;
489+ } ) ;
490+
387491 it ( 'should restore entry references in JSON RTE' , ( ) => {
388492 const entry = JSON . parse ( JSON . stringify ( mockEntries . entryWithJsonRteReference ) ) ;
389493 const sourceStackEntry = mockEntries . sourceStackEntryWithJsonRte ;
0 commit comments