@@ -158,3 +158,94 @@ test('prepend + emoji', macro, '\u0600😀', 2);
158158
159159// Sequence consisting only of default ignorables should be zero
160160test ( 'only default ignorables (tags)' , macro , '\u{E0020}\u{E007F}' , 0 ) ;
161+
162+ // Emoji coverage for RGI and related cases
163+ // Case 1: Emoji with VS16 (should be double-width)
164+ test ( 'digit with VS16 (keycap base)' , macro , '0\uFE0F' , 1 ) ; // Digits/asterisk/pound are not RGI emoji
165+ test ( 'asterisk with VS16' , macro , '*\uFE0F' , 1 ) ;
166+ test ( 'pound with VS16' , macro , '#\uFE0F' , 1 ) ;
167+ test ( 'trademark with VS16' , macro , '™\uFE0F' , 2 ) ;
168+ test ( 'watch with VS16' , macro , '⌚\uFE0F' , 2 ) ;
169+ test ( 'phone with VS16' , macro , '☎\uFE0F' , 2 ) ;
170+ test ( 'keyboard with VS16' , macro , '⌨\uFE0F' , 2 ) ;
171+ test ( 'envelope with VS16' , macro , '✉\uFE0F' , 2 ) ;
172+
173+ // Case 2: Emoji_Presentation characters without VS15 (should be double-width)
174+ test ( 'face emoji (has Emoji_Presentation)' , macro , '😀' , 2 ) ;
175+ test ( 'heart emoji (has Emoji_Presentation)' , macro , '❤️' , 2 ) ;
176+ test ( 'fire emoji (has Emoji_Presentation)' , macro , '🔥' , 2 ) ;
177+ test ( 'rocket emoji (has Emoji_Presentation)' , macro , '🚀' , 2 ) ;
178+ test ( 'star emoji (has Emoji_Presentation)' , macro , '⭐' , 2 ) ;
179+
180+ // Case 3: Emoji_Presentation with VS15 (should be single-width)
181+ test ( 'heart with VS15 (text style)' , macro , '❤\uFE0E' , 1 ) ;
182+ test ( 'star with VS15 (text style)' , macro , '⭐\uFE0E' , 2 ) ; // Star with VS15 still renders as 2 in terminals
183+
184+ // Case 4: Multi-scalar meaningful sequences (should be double-width)
185+ test ( 'keycap sequence 0️⃣' , macro , '0️⃣' , 2 ) ;
186+ test ( 'keycap sequence 1️⃣' , macro , '1️⃣' , 2 ) ;
187+ test ( 'keycap sequence 9️⃣' , macro , '9️⃣' , 2 ) ;
188+ test ( 'keycap sequence *️⃣' , macro , '*️⃣' , 2 ) ;
189+ test ( 'keycap sequence #️⃣' , macro , '#️⃣' , 2 ) ;
190+ test ( 'flag emoji GB' , macro , '🇬🇧' , 2 ) ;
191+ test ( 'flag emoji JP' , macro , '🇯🇵' , 2 ) ;
192+ test ( 'skin tone modifier' , macro , '👋🏻' , 2 ) ;
193+ test ( 'skin tone modifier dark' , macro , '👋🏿' , 2 ) ;
194+ test ( 'couple with heart' , macro , '👨❤️👨' , 2 ) ;
195+ test ( 'woman technologist' , macro , '👩💻' , 2 ) ;
196+ test ( 'man health worker' , macro , '👨⚕️' , 2 ) ;
197+
198+ // Case 5: Non-emoji presentation without VS16 (should be single-width via EAW)
199+ test ( 'watch without VS' , macro , '⌚' , 2 ) ; // Watch is actually RGI emoji with Emoji_Presentation
200+ test ( 'phone without VS (not Emoji_Presentation)' , macro , '☎' , 1 ) ;
201+ test ( 'keyboard without VS (not Emoji_Presentation)' , macro , '⌨' , 1 ) ;
202+ test ( 'envelope without VS (not Emoji_Presentation)' , macro , '✉' , 1 ) ;
203+
204+ // Case 6: Edge cases for multi-scalar logic
205+ test ( 'single code point with VS15 (2 scalars but not meaningful)' , macro , 'A\uFE0E' , 1 ) ;
206+ test ( 'emoji with VS15 only (2 scalars, VS15 present)' , macro , '⌚\uFE0E' , 2 ) ; // Watch with VS15 still renders as 2
207+ test ( 'three code points with VS15 at end' , macro , 'A👋\uFE0E' , 3 ) ;
208+
209+ // Case 7: RGI emoji coverage
210+ test ( 'RGI emoji face' , macro , '😊' , 2 ) ;
211+ test ( 'RGI emoji hand' , macro , '✋' , 2 ) ;
212+ test ( 'RGI emoji animal' , macro , '🐶' , 2 ) ;
213+ test ( 'RGI emoji food' , macro , '🍕' , 2 ) ;
214+ test ( 'RGI emoji flag' , macro , '🏁' , 2 ) ;
215+
216+ // Case 8: Complex emoji sequences that test all branches
217+ test ( 'emoji with combining mark' , macro , '😀\u0301' , 2 ) ;
218+ test ( 'emoji with ZWJ and another emoji' , macro , '😀\u200D😀' , 2 ) ;
219+ test ( 'text character made emoji with VS16' , macro , '↔\uFE0F' , 2 ) ;
220+ test ( 'text character kept text with VS15' , macro , '↔\uFE0E' , 1 ) ;
221+
222+ // Case 9: Non-RGI sequences
223+ test ( 'non-RGI with VS16' , macro , '〰\uFE0F' , 2 ) ;
224+ test ( 'non-RGI multi-scalar' , macro , '☎\uFE0F\u20E3' , 1 ) ; // Not RGI emoji, counts as 1
225+
226+ // Case 10: VS15 on Emoji_Presentation remain width 2 in most terminals via EAW
227+ test ( 'hourglass with VS15' , macro , '⌛\uFE0E' , 2 ) ;
228+ test ( 'fast-forward with VS15' , macro , '⏩\uFE0E' , 2 ) ;
229+ test ( 'rewind with VS15' , macro , '⏪\uFE0E' , 2 ) ;
230+ test ( 'arrow double up with VS15' , macro , '⏫\uFE0E' , 2 ) ;
231+ test ( 'arrow double down with VS15' , macro , '⏬\uFE0E' , 2 ) ;
232+ test ( 'alarm clock with VS15' , macro , '⏰\uFE0E' , 2 ) ;
233+ test ( 'hourglass flowing sand with VS15' , macro , '⏳\uFE0E' , 2 ) ;
234+ test ( 'umbrella with rain with VS15' , macro , '☔\uFE0E' , 2 ) ;
235+ test ( 'hot beverage with VS15' , macro , '☕\uFE0E' , 2 ) ;
236+ // Removed redundant duplicate of '⭐\uFE0E' test
237+
238+ // Other emoji that may not have Emoji_Presentation but are affected
239+ test ( 'sun with VS15' , macro , '☀\uFE0E' , 1 ) ;
240+ test ( 'cloud with VS15' , macro , '☁\uFE0E' , 1 ) ;
241+ test ( 'umbrella with VS15' , macro , '☂\uFE0E' , 1 ) ;
242+ test ( 'snowman with VS15' , macro , '☃\uFE0E' , 1 ) ;
243+ test ( 'comet with VS15' , macro , '☄\uFE0E' , 1 ) ;
244+ test ( 'black nib with VS15' , macro , '✒\uFE0E' , 1 ) ;
245+ test ( 'heavy check mark with VS15' , macro , '✔\uFE0E' , 1 ) ;
246+
247+ // More edge cases for single-scalar text characters (not emoji)
248+ test ( 'digit zero as plain text (not emoji)' , macro , '0' , 1 ) ;
249+ test ( 'digit one as plain text' , macro , '1' , 1 ) ;
250+ test ( 'asterisk as plain text' , macro , '*' , 1 ) ;
251+ test ( 'hash as plain text' , macro , '#' , 1 ) ;
0 commit comments