1- using NUnit . Framework ;
2- using SmartFormat . Core . Parsing ;
3- using SmartFormat . Core . Settings ;
4- using SmartFormat . Tests . TestUtils ;
5- using System ;
1+ using System ;
2+ using System . Collections . Generic ;
63using System . IO ;
74using System . Linq ;
85using System . Text . RegularExpressions ;
6+ using NUnit . Framework ;
7+ using SmartFormat . Core . Parsing ;
8+ using SmartFormat . Core . Settings ;
9+ using SmartFormat . Tests . TestUtils ;
910
1011namespace SmartFormat . Tests . Core ;
1112
@@ -66,9 +67,9 @@ public void Parser_Throws_Exceptions(string format)
6667 Assert . Throws < ParsingErrors > ( ( ) => formatter . Test ( format , args , "Error" ) ) ;
6768 }
6869
69- [ TestCase ( "{V(LU)}" ) ] // braces are illegal
70- [ TestCase ( "{V LU }" ) ] // blanks are illegal
71- [ TestCase ( "{VĀLUĒ }" ) ] // 0x100 and 0x112 are illegal chars
70+ [ TestCase ( "{V(LU)}" ) ] // braces are not allowed
71+ [ TestCase ( "{V LU\\ }" ) ] // escape char is not allowed
72+ [ TestCase ( "{V?LU, }" ) ] // ? and , are allowed chars
7273 public void Parser_Throws_On_Illegal_Selector_Chars ( string format )
7374 {
7475 var parser = GetRegularParser ( ) ;
@@ -81,9 +82,9 @@ public void Parser_Throws_On_Illegal_Selector_Chars(string format)
8182 {
8283 Assert . Multiple ( ( ) =>
8384 {
84- // Throws, because selector contains 2 illegal characters
85+ // Throws, because selector contains disallowed characters
8586 Assert . That ( e , Is . InstanceOf < ParsingErrors > ( ) ) ;
86- Assert . That ( ( ( ParsingErrors ) e ) . Issues , Has . Count . EqualTo ( 2 ) ) ;
87+ Assert . That ( ( ( ParsingErrors ) e ) . Issues , Has . Count . GreaterThanOrEqualTo ( 1 ) ) ;
8788 } ) ;
8889 }
8990 }
@@ -202,13 +203,14 @@ public void Parser_Error_Action_MaintainTokens(string invalidTemplate, bool last
202203 public void Parser_Error_Action_OutputErrorInResult ( )
203204 {
204205 // | Literal | Erroneous |
206+ // ▼ Selector must not contain {
205207 var invalidTemplate = "Hello, I'm {Name from {City}" ;
206208
207209 var parser = GetRegularParser ( new SmartSettings { Parser = new ParserSettings { ErrorAction = ParseErrorAction . OutputErrorInResult } } ) ;
208210 using var parsed = parser . ParseFormat ( invalidTemplate ) ;
209211
210212 Assert . That ( parsed . Items , Has . Count . EqualTo ( 1 ) ) ;
211- Assert . That ( parsed . Items [ 0 ] . RawText , Does . StartWith ( "The format string has 3 issues " ) ) ;
213+ Assert . That ( parsed . Items [ 0 ] . RawText , Does . StartWith ( "The format string has 1 issue " ) ) ;
212214 }
213215
214216 [ Test ]
@@ -415,8 +417,8 @@ public void Parser_NotifyParsingError()
415417 var res = formatter . Format ( "{NoName {Other} {Same" , default ( object ) ! ) ;
416418 Assert . Multiple ( ( ) =>
417419 {
418- Assert . That ( parsingError ! . Issues , Has . Count . EqualTo ( 3 ) ) ;
419- Assert . That ( parsingError . Issues [ 2 ] . Issue , Is . EqualTo ( new Parser . ParsingErrorText ( ) [ SmartFormat . Core . Parsing . Parser . ParsingError . MissingClosingBrace ] ) ) ;
420+ Assert . That ( parsingError ! . Issues , Has . Count . EqualTo ( 2 ) ) ;
421+ Assert . That ( parsingError . Issues [ 1 ] . Issue , Is . EqualTo ( new Parser . ParsingErrorText ( ) [ SmartFormat . Core . Parsing . Parser . ParsingError . MissingClosingBrace ] ) ) ;
420422 } ) ;
421423 }
422424
@@ -568,6 +570,29 @@ public void Selectors_With_Custom_Operator_Character(string formatString, char c
568570 } ) ;
569571 }
570572
573+ [ TestCase ( "German |öäüßÖÄÜ!" ) ]
574+ [ TestCase ( "Russian абвгдеёжзийклмн" ) ]
575+ [ TestCase ( "French >éèêëçàùâîô" ) ]
576+ [ TestCase ( "Spanish <áéíóúñü¡¿" ) ]
577+ [ TestCase ( "Portuguese !ãõáâêéíóúç" ) ]
578+ [ TestCase ( "Chinese 汉字测试" ) ]
579+ [ TestCase ( "Arabic مرحبا بالعالم" ) ]
580+ [ TestCase ( "Turkish çğöşüİı" ) ]
581+ [ TestCase ( "Hindi नमस्ते दुनिया" ) ]
582+ public void Selector_WorksWithAllUnicodeChars ( string selector )
583+ {
584+ // See https://github.com/axuno/SmartFormat/issues/454
585+
586+ const string expected = "The Value" ;
587+ // The default formatter with default settings should be able to handle any
588+ // Unicode characters in selectors except the "magic" disallowed ones
589+ var formatter = Smart . CreateDefaultSmartFormat ( ) ;
590+ // Use the Unicode string as a selector of the placeholder
591+ var template = $ "{{{selector}}}";
592+ var result = formatter . Format ( template , new Dictionary < string , string > { { selector , expected } } ) ;
593+ Assert . That ( result , Is . EqualTo ( expected ) ) ;
594+ }
595+
571596 [ TestCase ( "{A?.B}" ) ]
572597 [ TestCase ( "{Selector0?.Selector1}" ) ]
573598 [ TestCase ( "{A?[1].B}" ) ]
@@ -681,30 +706,25 @@ public void ParseInputAsHtml(string input)
681706 Assert . That ( literalText ! . RawText , Is . EqualTo ( input ) ) ;
682707 }
683708
684- [ TestCase ( "<script>{Placeholder}</script>" , false ) ] // should parse a placeholder
685- [ TestCase ( "<style>{Placeholder}</style>" , false ) ] // should parse a placeholder
686- [ TestCase ( "Something <style>h1 { color : #000; }</style>! nice" , true ) ] // illegal selector chars
687- [ TestCase ( "Something <script>{const a = '</script>';}</script>! nice" , true ) ] // illegal selector chars
688- public void ParseHtmlInput_Without_ParserSetting_IsHtml ( string input , bool shouldThrow )
709+ [ TestCase ( "<script>{Placeholder}</script>" , "{Placeholder}" ) ]
710+ [ TestCase ( "<style>{Placeholder}</style>" , "{Placeholder}" ) ]
711+ [ TestCase ( "Something <style>h1 { color : #000; }</style>! nice" , "{ color : #000; }" ) ]
712+ [ TestCase ( "Something <script>{const a = '</script>';}</script>! nice" , "{const a = '</script>';}" ) ]
713+ public void ParseHtmlInput_Without_ParserSetting_IsHtml ( string input , string selector )
689714 {
690715 var parser = GetRegularParser ( new SmartSettings
691716 {
692717 StringFormatCompatibility = false ,
693718 Parser = new ParserSettings { ErrorAction = ParseErrorAction . ThrowError , ParseInputAsHtml = false }
694719 } ) ;
695720
696- switch ( shouldThrow )
721+ var result = parser . ParseFormat ( input ) ;
722+ Assert . Multiple ( ( ) =>
697723 {
698- case true :
699- Assert . That ( ( ) => _ = parser . ParseFormat ( input ) , Throws . TypeOf < ParsingErrors > ( ) ) ;
700- break ;
701- case false :
702- {
703- var result = parser . ParseFormat ( input ) ;
704- Assert . That ( result . Items , Has . Count . EqualTo ( 3 ) ) ;
705- break ;
706- }
707- }
724+ Assert . That ( result . Items , Has . Count . EqualTo ( 3 ) ) ;
725+ Assert . That ( ( ( Placeholder ) result . Items [ 1 ] ) . RawText , Is . EqualTo ( selector ) ) ;
726+ } ) ;
727+
708728 }
709729
710730 /// <summary>
@@ -807,29 +827,31 @@ function interpolationSearch(sortedArray, seekIndex) {
807827 [ TestCase ( true , false ) ]
808828 public void StyleTags_Can_Be_Parsed_Without_Failure ( bool inputIsHtml , bool shouldFail )
809829 {
810- var styles = @"
811- <style type='text/css'>
812- .media {
813- display: grid;
814- grid-template-columns: 1fr 3fr;
815- }
830+ var styles = """
816831
817- .media .content {
818- font-size: .8rem;
819- }
832+ <style type='text/css'>
833+ .media {
834+ display: grid;
835+ grid-template-columns: 1fr 3fr;
836+ }
820837
821- .comment img {
822- border: 1px solid grey;
823- anything: 'xyz'
824- }
838+ .media .content {
839+ font-size: .8rem;
840+ }
825841
826- .list-item {
827- border-bottom: 1px solid grey;
828- }
829- /* Comment: { which mixes up the parser without ParserSettings.ParseInputAsHtml = true */
830- </style>
831- <p>############### {TheVariable} ###############</p>
832- " ;
842+ .comment img {
843+ border: 1px solid grey;
844+ anything: 'xyz'
845+ }
846+
847+ .list-item {
848+ border-bottom: 1px solid grey;
849+ }
850+ /* Comment: { which mixes up the parser without ParserSettings.ParseInputAsHtml = true */
851+ </style>
852+ <p>############### {TheVariable} ###############</p>
853+
854+ """ ;
833855 var parsingFailures = 0 ;
834856 var parser = GetRegularParser ( new SmartSettings
835857 {
0 commit comments