Repository: myremylar/pygame_gui
Issue: Multiple <img> tag images sometimes cause text layout to hang 

**Describe the bug**
Noticed this as I was wrapping up the 0.6.0 release. Basically some layouts with multiple uses of the <img> tag will cause the layout process to hang in an infinite loop as it can't find a spot to place the image and dumps it back on the stack.

I'll try and find a minimum reproducer for this, though in general the layout builder should probably just give up on rectangles it is unable to place after 20 or so attempts to avoid hangs.

**To Reproduce**
Steps to reproduce the behaviour:
1. Create a UITextBox including multiple <img> tags.
2. Maybe it hangs in an infinite loop

**Expected behaviour**

No infinite loop, and ideally it manages to place all the images somewhere.



Changed files:
tests/test_core/test_text/test_text_effects.py, tests/test_core/test_text/test_text_box_layout.py, pygame_gui/core/text/text_box_layout.py, tests/test_elements/test_ui_text_box.py

diff --git a/pygame_gui/core/text/text_box_layout.py b/pygame_gui/core/text/text_box_layout.py
index ae535ab..2747c8e 100644
--- a/pygame_gui/core/text/text_box_layout.py
+++ b/pygame_gui/core/text/text_box_layout.py
@@ -36,6 +36,7 @@ class TextBoxLayout:
         self.input_data_rect_queue = input_data_queue.copy()
         self.layout_rect = layout_rect.copy()
         self.line_spacing = line_spacing
+        self.last_row_height = int(14 * self.line_spacing)
 
         self.view_rect = view_rect
 
@@ -120,14 +121,20 @@ class TextBoxLayout:
             else:
                 current_row = self._handle_regular_rect(current_row, text_layout_rect, input_queue)
         # make sure we add the last row to the layout
-        self._add_row_to_layout(current_row)
-
-    def _add_row_to_layout(self, current_row):
+        self._add_row_to_layout(current_row, last_row=True)
+
+    def _add_row_to_layout(self, current_row: TextBoxLayoutRow, last_row=False):
+        # handle an empty row being added to layout
+        # otherwise we add infinite rows with no height
+        # instead add a line break rect to an empty row.
+        if len(current_row.items) == 0 and not last_row:
+            current_row.add_item(LineBreakLayoutRect(dimensions=(2, self.last_row_height)))
         if current_row not in self.layout_rows:
             self.layout_rows.append(current_row)
         if current_row.bottom - self.layout_rect.y > self.layout_rect.height:
             self.layout_rect.height = current_row.bottom - self.layout_rect.y
         self._refresh_row_letter_counts()
+        self.last_row_height = current_row.height
 
     def _handle_regular_rect(self, current_row, text_layout_rect, input_queue):
 
@@ -172,6 +179,7 @@ class TextBoxLayout:
                     current_row,
                     self.layout_rect.width,
                     test_layout_rect,
+                    input_queue,
                     max_floater_line_height)
             else:
                 self.floating_rects.append(test_layout_rect)
@@ -197,6 +205,7 @@ class TextBoxLayout:
                     current_row,
                     self.layout_rect.width,
                     test_layout_rect,
+                    input_queue,
                     max_floater_line_height)
             else:
                 self._add_floating_rect(current_row, test_layout_rect, input_queue)
@@ -270,9 +279,9 @@ class TextBoxLayout:
             except ValueError:
                 warnings.warn('Unable to split word into chunks because text box is too narrow')
                 current_row.add_item(test_layout_rect)
+
                 if isinstance(test_layout_rect, HyperlinkTextChunk):
                     self.link_chunks.append(test_layout_rect)
-                    return current_row
 
         else:
             # can't split, have to move whole chunk down a line.
@@ -431,7 +440,8 @@ class TextBoxLayout:
         self._process_layout_queue(temp_layout_queue, row)
 
         if self.finalised_surface is not None:
-            if self.layout_rect.size != self.finalised_surface.get_size():
+            if ((self.layout_rect.width + self.edit_buffer,
+                 self.layout_rect.height) != self.finalised_surface.get_size()):
                 self.finalise_to_new()
             else:
                 for row in self.layout_rows[row_index:]:
@@ -640,17 +650,19 @@ class TextBoxLayout:
         found_chunk = None
         letter_index = 0
         letter_accumulator = 0
-        chunk_row, index_in_row = self._find_row_from_text_box_index(index)
-        row_index = chunk_row.row_index
         chunk_in_row_index = 0
-        for chunk in chunk_row.items:
-            if isinstance(chunk, TextLineChunkFTFont) and found_chunk is None:
-                if index_in_row < letter_accumulator + chunk.letter_count:
-                    letter_index = index_in_row - letter_accumulator
-                    found_chunk = chunk
-                    break
-                letter_accumulator += chunk.letter_count
-                chunk_in_row_index += 1
+        row_index = 0
+        chunk_row, index_in_row = self._find_row_from_text_box_index(index)
+        if chunk_row is not None:
+            row_index = chunk_row.row_index
+            for chunk in chunk_row.items:
+                if isinstance(chunk, TextLineChunkFTFont) and found_chunk is None:
+                    if index_in_row < letter_accumulator + chunk.letter_count:
+                        letter_index = index_in_row - letter_accumulator
+                        found_chunk = chunk
+                        break
+                    letter_accumulator += chunk.letter_count
+                    chunk_in_row_index += 1
 
         if letter_index != 0:
             # split the chunk
@@ -693,19 +705,22 @@ class TextBoxLayout:
         :param parser: An optional HTML parser for text styling data
         """
         current_row, index_in_row = self._find_row_from_text_box_index(layout_index)
-        current_row.insert_text(text, index_in_row, parser)
+        if current_row is not None:
+            current_row.insert_text(text, index_in_row, parser)
 
-        temp_layout_queue = deque([])
-        for row in reversed(self.layout_rows[current_row.row_index:]):
-            row.rewind_row(temp_layout_queue)
+            temp_layout_queue = deque([])
+            for row in reversed(self.layout_rows[current_row.row_index:]):
+                row.rewind_row(temp_layout_queue)
 
-        self.layout_rows = self.layout_rows[:current_row.row_index]
+            self.layout_rows = self.layout_rows[:current_row.row_index]
 
-        self._process_layout_queue(temp_layout_queue, current_row)
+            self._process_layout_queue(temp_layout_queue, current_row)
 
-        if self.finalised_surface is not None:
-            for row in self.layout_rows[current_row.row_index:]:
-                row.finalise(self.finalised_surface)
+            if self.finalised_surface is not None:
+                for row in self.layout_rows[current_row.row_index:]:
+                    row.finalise(self.finalised_surface)
+        else:
+            raise RuntimeError("no rows in text box layout")
 
     def delete_selected_text(self):
         """
@@ -817,7 +832,7 @@ class TextBoxLayout:
                     row.finalise(self.finalised_surface)
 
     def _find_row_from_text_box_index(self, text_box_index: int):
-        if self.layout_rows != 0:
+        if len(self.layout_rows) != 0:
             row_index = bisect_left(self.row_lengths, text_box_index)
             if row_index >= len(self.layout_rows):
                 row_index = len(self.layout_rows) - 1
@@ -850,7 +865,9 @@ class TextBoxLayout:
         self._process_layout_queue(new_queue, last_row)
         if self.finalised_surface is not None:
             if self.finalised_surface is not None:
-                if self.layout_rect.size != self.finalised_surface.get_size():
+
+                if ((self.layout_rect.width + self.edit_buffer,
+                     self.layout_rect.height) != self.finalised_surface.get_size()):
                     self.finalise_to_new()
                 else:
                     for row in self.layout_rows[last_row.row_index:]:
diff --git a/tests/test_core/test_text/test_text_box_layout.py b/tests/test_core/test_text/test_text_box_layout.py
index 81e6b98..fa1bbc5 100644
--- a/tests/test_core/test_text/test_text_box_layout.py
+++ b/tests/test_core/test_text/test_text_box_layout.py
@@ -5,8 +5,10 @@ import pytest
 
 from pygame_gui.ui_manager import UIManager
 
-from pygame_gui.core.text.text_box_layout import TextBoxLayout
+from pygame_gui.core.text.text_box_layout import TextBoxLayout, TextFloatPosition
 from pygame_gui.core.text import SimpleTestLayoutRect, TextLineChunkFTFont, HyperlinkTextChunk
+from pygame_gui.core.text import ImageLayoutRect, HorizRuleLayoutRect
+from pygame_gui.core.text.text_layout_rect import Padding
 
 
 class TestTextBoxLayout:
@@ -25,15 +27,116 @@ class TestTextBoxLayout:
                             SimpleTestLayoutRect(dimensions=(50, 20)),
                             SimpleTestLayoutRect(dimensions=(30, 20)),
                             SimpleTestLayoutRect(dimensions=(90, 20)),
-                            SimpleTestLayoutRect(dimensions=(175, 20))])
+                            SimpleTestLayoutRect(dimensions=(175, 20)),
+                            HorizRuleLayoutRect(height=1, colour_or_gradient=pygame.Color(255, 255, 255)),
+                            SimpleTestLayoutRect(dimensions=(110, 20)),
+                            SimpleTestLayoutRect(dimensions=(100, 20), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(20, 20), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(20, 30), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(22, 20), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(32, 20), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(32, 20), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(100, 20), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(20, 20), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(20, 30), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(33, 20), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(32, 20), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(22, 20), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20))
+                            ])
 
         layout = TextBoxLayout(input_data_queue=input_data,
-                               layout_rect=pygame.Rect(0, 0, 200, 300),
+                               layout_rect=pygame.Rect(0, 0, 200, 110),
+                               view_rect=pygame.Rect(0, 0, 200, 150),
+                               line_spacing=1.0)
+
+        assert len(layout.layout_rows) > 0
+
+        input_data = deque([SimpleTestLayoutRect(dimensions=(25, 20), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(25, 20), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(25, 30), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(21, 20), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(22, 20), float_pos=TextFloatPosition.LEFT),
+                            SimpleTestLayoutRect(dimensions=(23, 20), float_pos=TextFloatPosition.LEFT)])
+
+        layout = TextBoxLayout(input_data_queue=input_data,
+                               layout_rect=pygame.Rect(0, 0, 100, 110),
+                               view_rect=pygame.Rect(0, 0, 200, 150),
+                               line_spacing=1.0)
+
+        assert len(layout.layout_rows) > 0
+
+        input_data = deque([SimpleTestLayoutRect(dimensions=(25, 20), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(25, 20), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(25, 30), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(21, 20), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(22, 20), float_pos=TextFloatPosition.RIGHT),
+                            SimpleTestLayoutRect(dimensions=(23, 20), float_pos=TextFloatPosition.RIGHT)])
+
+        layout = TextBoxLayout(input_data_queue=input_data,
+                               layout_rect=pygame.Rect(0, 0, 100, 110),
+                               view_rect=pygame.Rect(0, 0, 200, 150),
+                               line_spacing=1.0)
+
+        assert len(layout.layout_rows) > 0
+
+        input_data = deque([ImageLayoutRect(image_path='tests/data/images/test_emoji.png',
+                                            float_position=TextFloatPosition.RIGHT,
+                                            padding=Padding(0, 0, 0, 0)),
+                            ImageLayoutRect(image_path='tests/data/images/test_emoji.png',
+                                            float_position=TextFloatPosition.RIGHT,
+                                            padding=Padding(0, 0, 0, 0)),
+                            ImageLayoutRect(image_path='tests/data/images/test_emoji.png',
+                                            float_position=TextFloatPosition.RIGHT,
+                                            padding=Padding(0, 0, 0, 0)),
+                            ImageLayoutRect(image_path='tests/data/images/test_emoji.png',
+                                            float_position=TextFloatPosition.RIGHT,
+                                            padding=Padding(0, 0, 0, 0)),
+                            ImageLayoutRect(image_path='tests/data/images/test_emoji.png',
+                                            float_position=TextFloatPosition.RIGHT,
+                                            padding=Padding(0, 0, 0, 0))])
+
+        layout = TextBoxLayout(input_data_queue=input_data,
+                               layout_rect=pygame.Rect(0, 0, 100, 90),
                                view_rect=pygame.Rect(0, 0, 200, 150),
                                line_spacing=1.0)
 
         assert len(layout.layout_rows) > 0
 
+    def test_too_wide_image(self, _init_pygame, default_ui_manager: UIManager):
+        input_data = deque([SimpleTestLayoutRect(dimensions=(50, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            SimpleTestLayoutRect(dimensions=(90, 20)),
+                            SimpleTestLayoutRect(dimensions=(175, 20)),
+                            SimpleTestLayoutRect(dimensions=(50, 20)),
+                            SimpleTestLayoutRect(dimensions=(30, 20)),
+                            ImageLayoutRect(image_path='tests/data/images/space_1.jpg',
+                                            float_position=TextFloatPosition.RIGHT,
+                                            padding=Padding(0, 0, 0, 0))])
+
+        with pytest.warns(UserWarning, match="too wide for text layout"):
+            TextBoxLayout(input_data_queue=input_data,
+                          layout_rect=pygame.Rect(0, 0, 200, 300),
+                          view_rect=pygame.Rect(0, 0, 200, 150),
+                          line_spacing=1.0)
+
     def test_reprocess_layout_queue(self, _init_pygame, default_ui_manager: UIManager):
         input_data = deque([SimpleTestLayoutRect(dimensions=(50, 20)),
                             SimpleTestLayoutRect(dimensions=(30, 20)),
@@ -210,8 +313,8 @@ class TestTextBoxLayout:
                             ])
 
         layout = TextBoxLayout(input_data_queue=input_data,
-                               layout_rect=pygame.Rect(0, 0, 500, 300),
-                               view_rect=pygame.Rect(0, 0, 500, 150),
+                               layout_rect=pygame.Rect(0, 0, 300, 150),
+                               view_rect=pygame.Rect(0, 0, 300, 150),
                                line_spacing=1.0)
 
         insert_data = deque([TextLineChunkFTFont(text='n insertion',
@@ -231,6 +334,50 @@ class TestTextBoxLayout:
 
         assert chunk.text == 'this is an insertion'
 
+        layout_surface = layout.finalise_to_new()
+
+        insert_data = deque([TextLineChunkFTFont(text=' with more text',
+                                                 font=the_font,
+                                                 underlined=False,
+                                                 colour=pygame.Color('#FFFFFF'),
+                                                 using_default_text_colour=False,
+                                                 bg_colour=pygame.Color('#FF0000'))])
+
+        layout.insert_layout_rects(layout_rects=insert_data,
+                                   row_index=0,
+                                   item_index=0,
+                                   chunk_index=31)
+
+        row = layout.layout_rows[1]
+        chunk = row.items[0]
+
+        assert chunk.text == 'with more text'
+
+        insert_data = deque([TextLineChunkFTFont(text=' and then even more text, text forever. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text. '
+                                                      'loads of text. loads of text. loads of text. loads of text. '
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text. '
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.',
+                                                 font=the_font,
+                                                 underlined=False,
+                                                 colour=pygame.Color('#FFFFFF'),
+                                                 using_default_text_colour=False,
+                                                 bg_colour=pygame.Color('#FF0000'))])
+
+        layout.insert_layout_rects(layout_rects=insert_data,
+                                   row_index=0,
+                                   item_index=0,
+                                   chunk_index=46)
+
     def test_horiz_centre_all_rows(self, _init_pygame, default_ui_manager: UIManager):
         the_font = pygame.freetype.Font(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
@@ -468,6 +615,9 @@ class TestTextBoxLayout:
         assert layout.cursor_text_row.cursor_index == 2
         assert layout.cursor_text_row.cursor_draw_width == 17
 
+        layout.cursor_text_row.edit_cursor_active = True
+        layout.set_cursor_position(10)
+
     def test_set_cursor_from_click_pos(self, _init_pygame, default_ui_manager: UIManager):
         the_font = pygame.freetype.Font(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
@@ -500,6 +650,9 @@ class TestTextBoxLayout:
         assert layout.cursor_text_row.cursor_index == 2
         assert layout.cursor_text_row.cursor_draw_width == 17
 
+        layout.cursor_text_row.edit_cursor_active = True
+        layout.set_cursor_from_click_pos((17, 24))
+
     def test_toggle_cursor(self, _init_pygame, default_ui_manager: UIManager):
         the_font = pygame.freetype.Font(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
@@ -556,6 +709,12 @@ class TestTextBoxLayout:
                                                 colour=pygame.Color('#FFFFFF'),
                                                 using_default_text_colour=False,
                                                 bg_colour=pygame.Color('#FF0000')),
+                            TextLineChunkFTFont(text=' end chunk',
+                                                font=the_font,
+                                                underlined=False,
+                                                colour=pygame.Color('#FF0000'),
+                                                using_default_text_colour=False,
+                                                bg_colour=pygame.Color('#FF0000'))
                             ])
 
         layout = TextBoxLayout(input_data_queue=input_data,
@@ -574,6 +733,8 @@ class TestTextBoxLayout:
 
         assert selected_text == ' this is a t'
 
+        layout.set_text_selection(5, 20)
+
     def test_set_default_text_colour(self, _init_pygame, default_ui_manager: UIManager):
         the_font = pygame.freetype.Font(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
@@ -639,6 +800,16 @@ class TestTextBoxLayout:
 
         assert chunk.text == 'this is another insertion'
 
+        layout = TextBoxLayout(input_data_queue=input_data,
+                               layout_rect=pygame.Rect(0, 0, 500, 300),
+                               view_rect=pygame.Rect(0, 0, 500, 150),
+                               line_spacing=1.0)
+
+        layout.layout_rows = []
+
+        with pytest.raises(RuntimeError, match="no rows in text box layout"):
+            layout.insert_text('this should fail', 0)
+
     def test_delete_selected_text(self, _init_pygame, default_ui_manager: UIManager):
         the_font = pygame.freetype.Font(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
@@ -695,6 +866,40 @@ class TestTextBoxLayout:
 
         assert remaining_text == 'helloest'
 
+        layout.set_text_selection(0, 0)
+
+        with pytest.raises(IndexError, match='No selected rows.'):
+            layout.delete_selected_text()
+
+        insert_data = deque([TextLineChunkFTFont(text=' and then even more text, text forever. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text. '
+                                                      'loads of text. loads of text. loads of text. loads of text. '
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. <a href=none>loads of text</a>. '
+                                                      'loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text. '
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.'
+                                                      'loads of text. loads of text. loads of text. loads of text.',
+                                                 font=the_font,
+                                                 underlined=False,
+                                                 colour=pygame.Color('#FFFFFF'),
+                                                 using_default_text_colour=False,
+                                                 bg_colour=pygame.Color('#FF0000'))])
+
+        layout.insert_layout_rects(layout_rects=insert_data,
+                                   row_index=0,
+                                   item_index=0,
+                                   chunk_index=8)
+
+        layout.set_text_selection(62, 112)
+        layout.delete_selected_text()
+
     def test_delete_at_cursor(self, _init_pygame, default_ui_manager: UIManager):
         the_font = pygame.freetype.Font(None, 20)
         input_data = deque([TextLineChunkFTFont(text='hello ',
diff --git a/tests/test_core/test_text/test_text_effects.py b/tests/test_core/test_text/test_text_effects.py
index d9f5d5a..90ff581 100644
--- a/tests/test_core/test_text/test_text_effects.py
+++ b/tests/test_core/test_text/test_text_effects.py
@@ -96,6 +96,18 @@ class TestTypingAppearEffect:
 
         assert text_box.active_text_chunk_effects == []
 
+        text_box = UITextBox('<a href=none>Hello</a> World <a href=none>this is</a> a test <a href=none>of a longer</a> '
+                             'bit <a href=none>of typing</a> text',
+                             pygame.Rect((10, 10), (90, 150)),
+                             default_ui_manager)
+
+        text_box.set_active_effect(TEXT_EFFECT_TYPING_APPEAR)
+
+        for _ in range(0, 30):
+            text_box.update(time_delta=0.06)
+
+        assert label.active_text_effect is None
+
     def test_has_text_changed(self, _init_pygame, default_ui_manager: UIManager):
         text_box = UITextBox('hello <font color=#FF0000>this is a</font> test',
                              pygame.Rect((10, 10), (200, 100)),
diff --git a/tests/test_elements/test_ui_text_box.py b/tests/test_elements/test_ui_text_box.py
index 1c09c8c..7a7e5b3 100644
--- a/tests/test_elements/test_ui_text_box.py
+++ b/tests/test_elements/test_ui_text_box.py
@@ -118,7 +118,7 @@ class TestUITextBox:
                                            'LLALAALALA ALALA ALAL ALA'
                                            'LAALA ALALA ALALA AAaal aa'
                                            'ALALAa laalal alalal alala'
-                                           'alalalala alalalalalal alal'
+                                           '<a>alalalala</a> alalalalalal alal'
                                            'alalalala alala alalala ala'
                                            'alalalalal lalal alalalal al',
                                  relative_rect=pygame.Rect(100, 100, 5, 50),
@@ -887,10 +887,10 @@ class TestUITextBox:
                                        'alalalala alalalalalal alal'
                                        'alalalala <a href=none>alala<a/> '
                                        'alalala ala'
-                                       'alalalalal lalal alalalal al'
+                                       '<a href=none>alalalalal lalal alalalal al'
                                        'al alalalal lfed alal alal alal al'
                                        'ala lalalal lasda lal a lalalal slapl'
-                                       'alalala lal la blop lal alal aferlal al',
+                                       'alalala lal la blop lal alal aferlal al</a>',
                              relative_rect=pygame.Rect(0, 0, 150, 100),
                              manager=default_ui_manager)