|
1 | 1 | # A part of NonVisual Desktop Access (NVDA) |
2 | 2 | # This file is covered by the GNU General Public License. |
3 | 3 | # See the file COPYING for more details. |
4 | | -# Copyright (C) 2023 NV Access Limited, Leonard de Ruijter |
| 4 | +# Copyright (C) 2023-2024 NV Access Limited, Leonard de Ruijter |
5 | 5 |
|
6 | | -"""Unit tests for the move system caret when routing review cursor braille setting.""" |
| 6 | +"""Unit tests for braille cursor routing.""" |
7 | 7 |
|
8 | 8 | import config |
9 | 9 | import braille |
10 | 10 | import textInfos |
11 | 11 | import api |
12 | 12 | import controlTypes |
13 | | -from ..textProvider import CursorManager |
| 13 | +from ..textProvider import CursorManager, BasicTextProvider |
14 | 14 | import unittest |
15 | 15 | import time |
16 | 16 | from config.featureFlagEnums import ReviewRoutingMovesSystemCaretFlag |
@@ -147,3 +147,136 @@ def test_moveCaret_always_instantActivate(self): |
147 | 147 | self.assertGreaterEqual(self.cm.lastActivateTime, curTime) |
148 | 148 | caret = self.cm.makeTextInfo(textInfos.POSITION_CARET) |
149 | 149 | self.assertEquals(caret, review) |
| 150 | + |
| 151 | + |
| 152 | +class TestTextInfoRegionRouting(unittest.TestCase): |
| 153 | + """A test for TextInfoRegion.getTextInfoForBraillePos, which is used in braille cursor routing. |
| 154 | + This test ensures that braille routes to the expected character when dealing with emoji |
| 155 | + or other composites. |
| 156 | + These glyphs are threated as one character by uniscribe, however they span multiple characters |
| 157 | + on a braille display. |
| 158 | + Note that due to the nature of this test, it relies on uniscribe to be available. |
| 159 | + """ |
| 160 | + |
| 161 | + def test_routeToEmoji(self): |
| 162 | + testText = "⚠️test" |
| 163 | + obj = BasicTextProvider(text=testText) |
| 164 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 165 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 166 | + self.assertEqual(ti.text, testText[:2]) |
| 167 | + ti.collapse(end=True) |
| 168 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 169 | + self.assertEqual(ti.text, testText[2]) |
| 170 | + region = braille.TextInfoRegion(obj) |
| 171 | + region.update() |
| 172 | + index = 3 # Position of e |
| 173 | + pos = region.rawToBraillePos[index] |
| 174 | + region.routeTo(pos) |
| 175 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 176 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 177 | + self.assertEqual(ti.text, testText[index]) |
| 178 | + |
| 179 | + def test_routeToComposite(self): |
| 180 | + testText = "רבְּר" |
| 181 | + obj = BasicTextProvider(text=testText) |
| 182 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 183 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 184 | + self.assertEqual(ti.text, testText[0]) |
| 185 | + ti.collapse(end=True) |
| 186 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 187 | + self.assertEqual(ti.text, testText[1:4]) |
| 188 | + ti.collapse(end=True) |
| 189 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 190 | + self.assertEqual(ti.text, testText[4]) |
| 191 | + region = braille.TextInfoRegion(obj) |
| 192 | + region.update() |
| 193 | + index = 1 # Position of ב |
| 194 | + pos = region.rawToBraillePos[index] |
| 195 | + region.routeTo(pos) |
| 196 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 197 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 198 | + self.assertEqual(ti.text, testText[1:4]) |
| 199 | + index = 3 # Position of ּ |
| 200 | + pos = region.rawToBraillePos[index] |
| 201 | + region.routeTo(pos) |
| 202 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 203 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 204 | + self.assertEqual(ti.text, testText[1:4]) |
| 205 | + |
| 206 | + def test_routeToMultipleEmoji(self): |
| 207 | + testText = "👩🏽🚀👨🏻🚒" |
| 208 | + obj = BasicTextProvider(text=testText) |
| 209 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 210 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 211 | + self.assertEqual(ti.text, testText[:2]) |
| 212 | + ti.collapse(end=True) |
| 213 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 214 | + self.assertEqual(ti.text, testText[2:4]) |
| 215 | + region = braille.TextInfoRegion(obj) |
| 216 | + region.update() |
| 217 | + index = 4 # Position of the second emoji |
| 218 | + pos = region.rawToBraillePos[index] |
| 219 | + region.routeTo(pos) |
| 220 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 221 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 222 | + self.assertEqual(ti.text, testText[4:6]) |
| 223 | + |
| 224 | + def test_routeToZeroWidthJoiner(self): |
| 225 | + testText = "👨👩👧👦" |
| 226 | + obj = BasicTextProvider(text=testText) |
| 227 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 228 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 229 | + self.assertEqual(ti.text, testText[:1]) |
| 230 | + ti.collapse(end=True) |
| 231 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 232 | + self.assertEqual(ti.text, testText[1:2]) |
| 233 | + region = braille.TextInfoRegion(obj) |
| 234 | + region.update() |
| 235 | + index = 2 # Position of the second family member |
| 236 | + pos = region.rawToBraillePos[index] |
| 237 | + region.routeTo(pos) |
| 238 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 239 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 240 | + self.assertEqual(ti.text, testText[2:3]) |
| 241 | + |
| 242 | + def test_routeToVariationSelector(self): |
| 243 | + testText = "✌️" |
| 244 | + obj = BasicTextProvider(text=testText) |
| 245 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 246 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 247 | + self.assertEqual(ti.text, testText[:1]) |
| 248 | + ti.collapse(end=True) |
| 249 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 250 | + self.assertEqual(ti.text, testText[1:2]) |
| 251 | + region = braille.TextInfoRegion(obj) |
| 252 | + region.update() |
| 253 | + index = 1 # Position of the variation selector |
| 254 | + pos = region.rawToBraillePos[index] |
| 255 | + region.routeTo(pos) |
| 256 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 257 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 258 | + self.assertEqual(ti.text, testText[1:2]) |
| 259 | + |
| 260 | + def test_routeToMixedContent(self): |
| 261 | + testText = "Hello 👋, how are you? רָבּ" |
| 262 | + obj = BasicTextProvider(text=testText) |
| 263 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 264 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 265 | + self.assertEqual(ti.text, testText[:1]) |
| 266 | + ti.collapse(end=True) |
| 267 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 268 | + self.assertEqual(ti.text, testText[1:2]) |
| 269 | + region = braille.TextInfoRegion(obj) |
| 270 | + region.update() |
| 271 | + index = 6 # Position of the emoji |
| 272 | + pos = region.rawToBraillePos[index] |
| 273 | + region.routeTo(pos) |
| 274 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 275 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 276 | + self.assertEqual(ti.text, testText[6:7]) |
| 277 | + index = 18 # Position of the Hebrew composite character |
| 278 | + pos = region.rawToBraillePos[index] |
| 279 | + region.routeTo(pos) |
| 280 | + ti = obj.makeTextInfo(textInfos.POSITION_CARET) |
| 281 | + ti.expand(textInfos.UNIT_CHARACTER) |
| 282 | + self.assertEqual(ti.text, testText[18:21]) |
0 commit comments