Skip to content

Commit a3940ea

Browse files
test: add surrogate pair handling tests for font substitution and string conversion
1 parent ee5c96b commit a3940ea

2 files changed

Lines changed: 128 additions & 0 deletions

File tree

packages/textkit/tests/engines/fontSubstitution.test.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,112 @@ describe('FontSubstitution', () => {
116116
expect(string.runs[1].attributes.font).toEqual([SimplifiedChineseFont]);
117117
});
118118
});
119+
120+
describe('Surrogate Pairs', () => {
121+
const EmojiFont = {
122+
name: 'EmojiFont',
123+
unitsPerEm: 1000,
124+
hasGlyphForCodePoint: (codePoint) =>
125+
codePoint === 0x1f600 || // 😀 Grinning Face
126+
codePoint === 0x1f60d, // 😍 Heart Eyes
127+
};
128+
129+
const RegularFont = {
130+
name: 'RegularFont',
131+
unitsPerEm: 1000,
132+
hasGlyphForCodePoint: (codePoint) => codePoint < 0xffff,
133+
};
134+
135+
test('should handle surrogate pairs in text', () => {
136+
const run = {
137+
start: 0,
138+
end: 3, // A + surrogate pair (2 JS chars)
139+
attributes: {
140+
font: [RegularFont, EmojiFont],
141+
fontSize: 12,
142+
},
143+
} as any;
144+
145+
// A + 😀 (Grinning Face emoji, surrogate pair)
146+
const string = instance({ string: 'A😀', runs: [run] });
147+
148+
expect(string).toHaveProperty('string', 'A😀');
149+
expect(string.runs).toHaveLength(2);
150+
151+
// First run is the letter "A" with RegularFont
152+
expect(string.runs[0]).toHaveProperty('start', 0);
153+
expect(string.runs[0]).toHaveProperty('end', 1);
154+
expect(string.runs[0].attributes.font).toEqual([RegularFont]);
155+
156+
// Second run is the emoji "😀" with EmojiFont
157+
expect(string.runs[1]).toHaveProperty('start', 1);
158+
expect(string.runs[1]).toHaveProperty('end', 3); // Surrogate pair takes 2 positions
159+
expect(string.runs[1].attributes.font).toEqual([EmojiFont]);
160+
});
161+
162+
test('should handle multiple surrogate pairs in text', () => {
163+
const run = {
164+
start: 0,
165+
end: 5, // A + surrogate pair (2) + B (1) + surrogate pair (2) = 5
166+
attributes: {
167+
font: [RegularFont, EmojiFont],
168+
fontSize: 12,
169+
},
170+
} as any;
171+
172+
// A + 😀 (Grinning Face) + 😍 (Heart Eyes)
173+
const string = instance({ string: 'A😀😍', runs: [run] });
174+
175+
expect(string).toHaveProperty('string', 'A😀😍');
176+
expect(string.runs).toHaveLength(2);
177+
178+
// First run is the letter "A" with RegularFont
179+
expect(string.runs[0]).toHaveProperty('start', 0);
180+
expect(string.runs[0]).toHaveProperty('end', 1);
181+
expect(string.runs[0].attributes.font).toEqual([RegularFont]);
182+
183+
// Second run is both emojis with EmojiFont
184+
expect(string.runs[1]).toHaveProperty('start', 1);
185+
expect(string.runs[1]).toHaveProperty('end', 5); // Two surrogate pairs
186+
expect(string.runs[1].attributes.font).toEqual([EmojiFont]);
187+
});
188+
189+
test('should handle surrogate pairs interspersed with regular text', () => {
190+
const run = {
191+
start: 0,
192+
end: 7, // A + surrogate pair (2) + B + surrogate pair (2) + C = 7
193+
attributes: {
194+
font: [RegularFont, EmojiFont],
195+
fontSize: 12,
196+
},
197+
} as any;
198+
199+
// A + 😀 (Grinning Face) + B + 😍 (Heart Eyes) + C
200+
const string = instance({ string: 'A😀B😍C', runs: [run] });
201+
202+
expect(string).toHaveProperty('string', 'A😀B😍C');
203+
expect(string.runs).toHaveLength(5);
204+
205+
// Alternating fonts for regular chars and emojis
206+
expect(string.runs[0]).toHaveProperty('start', 0);
207+
expect(string.runs[0]).toHaveProperty('end', 1);
208+
expect(string.runs[0].attributes.font).toEqual([RegularFont]);
209+
210+
expect(string.runs[1]).toHaveProperty('start', 1);
211+
expect(string.runs[1]).toHaveProperty('end', 3);
212+
expect(string.runs[1].attributes.font).toEqual([EmojiFont]);
213+
214+
expect(string.runs[2]).toHaveProperty('start', 3);
215+
expect(string.runs[2]).toHaveProperty('end', 4);
216+
expect(string.runs[2].attributes.font).toEqual([RegularFont]);
217+
218+
expect(string.runs[3]).toHaveProperty('start', 4);
219+
expect(string.runs[3]).toHaveProperty('end', 6);
220+
expect(string.runs[3].attributes.font).toEqual([EmojiFont]);
221+
222+
expect(string.runs[4]).toHaveProperty('start', 6);
223+
expect(string.runs[4]).toHaveProperty('end', 7);
224+
expect(string.runs[4].attributes.font).toEqual([RegularFont]);
225+
});
226+
});
119227
});

packages/textkit/tests/utils/stringFromCodePoints.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,24 @@ describe('utils stringFromCodePoints operator', () => {
2929
test('should return get correct string for even mode code points', () => {
3030
expect(stringFromCodePoints([76, 64257, 77, 64259, 78])).toBe('LfiMffiN');
3131
});
32+
33+
test('should handle surrogate pair code points correctly', () => {
34+
expect(stringFromCodePoints([0x1f600])).toBe(String.fromCodePoint(0x1f600));
35+
});
36+
37+
test('should handle multiple surrogate pair code points', () => {
38+
expect(stringFromCodePoints([0x1f600, 0x1f60d])).toBe(
39+
String.fromCodePoint(0x1f600) + String.fromCodePoint(0x1f60d),
40+
);
41+
});
42+
43+
test('should handle mixed regular and surrogate pair code points', () => {
44+
expect(stringFromCodePoints([65, 0x1f600, 66, 0x1f60d, 67])).toBe(
45+
'A' +
46+
String.fromCodePoint(0x1f600) +
47+
'B' +
48+
String.fromCodePoint(0x1f60d) +
49+
'C',
50+
);
51+
});
3252
});

0 commit comments

Comments
 (0)