Skip to content

Commit

Permalink
fix _gather_elements in User class to only return visible elements (#…
Browse files Browse the repository at this point in the history
…4057)

When using `User` simulation methods such as `should_see`,
`should_not_see`, or `find`, the `visible` state of the element is
ignored. This can result in unexpected behavior. For example, if an
element is invisible, but `should_see` or `find` is used, the visibility
state is disregarded.

Consider a scenario where an element is created and set to be invisible,
but becomes visible at a later point. In the current version, testing
with `should_see` would always pass because the 'visible' state is
ignored. Ideally, `should_see` should raise an error if the element is
not visible.

This behavior is similar for `find` and `should_not_see`, where the
visibility state is not taken into account during the test.

This is fixed by only returning visible elements in `_gather_elements`.
Additionally for better debugging the `__str__` method of `Element`
includes the `visible` state of the element.

---------

Co-authored-by: Moritz Erlacher <[email protected]>
  • Loading branch information
rlcmtzc and Moritz Erlacher authored Dec 4, 2024
1 parent 5d4a09a commit e168cf2
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 10 deletions.
2 changes: 2 additions & 0 deletions nicegui/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ def shorten(content: Any, length: int = 20) -> str:
for key, value in self._props.items()
if not key.startswith('_') and key not in IGNORED_PROPS and value
]
if not self.visible:
additions.append(f'visible={self.visible}')
if additions:
result += f' [{", ".join(additions)}]'

Expand Down
23 changes: 13 additions & 10 deletions nicegui/testing/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,20 +191,23 @@ def current_layout(self) -> Element:
"""Return the root layout element of the current page."""
return self._client.layout

def _gather_elements(self,
target: Union[str, Type[T], None] = None,
kind: Optional[Type[T]] = None,
marker: Union[str, List[str], None] = None,
content: Union[str, List[str], None] = None,
) -> Set[T]:
def _gather_elements(
self,
target: Union[str, Type[T], None] = None,
kind: Optional[Type[T]] = None,
marker: Union[str, List[str], None] = None,
content: Union[str, List[str], None] = None,
) -> Set[T]:
if target is None:
if kind is None:
return set(ElementFilter(marker=marker, content=content)) # type: ignore
return set(ElementFilter(kind=kind, marker=marker, content=content))
elements = set(ElementFilter(marker=marker, content=content))
else:
elements = set(ElementFilter(kind=kind, marker=marker, content=content))
elif isinstance(target, str):
return set(ElementFilter(marker=target)).union(ElementFilter(content=target)) # type: ignore
elements = set(ElementFilter(marker=target)).union(ElementFilter(content=target))
else:
return set(ElementFilter(kind=target))
elements = set(ElementFilter(kind=target))
return {e for e in elements if e.visible} # type: ignore

def _build_error_message(self,
target: Union[str, Type[T], None] = None,
Expand Down
49 changes: 49 additions & 0 deletions tests/test_user_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,52 @@ async def test_trigger_autocomplete(user: User) -> None:
await user.should_not_see('apple')
user.find('fruit').type('a').trigger('keydown.tab')
await user.should_see('apple')

async def test_should_see_on_invisible_element(user: User) -> None:
lable = ui.label('Hello')
lable.visible = False

await user.open('/')
with pytest.raises(AssertionError):
await user.should_see('Hello')
lable.visible = True
await user.should_see('Hello')


async def test_should_not_see_on_invisible_element(user: User) -> None:
lable = ui.label('Hello')
lable.visible = True

await user.open('/')
with pytest.raises(AssertionError):
await user.should_not_see('Hello')
lable.visible = False
await user.should_not_see('Hello')


async def test_find_on_invisible_element(user: User) -> None:
button = ui.button('click me', on_click=lambda: ui.label('clicked'))
button.visible = False

await user.open('/')
with pytest.raises(AssertionError):
user.find('click me').click()
button.visible = True
user.find('click me').click()
await user.should_see('clicked')

async def test_page_to_sting_output_with_hidden_elements(user: User) -> None:
ui.label("Hello")
hidden_label = ui.label("Hidden Label")
hidden_label.visible = False

await user.open('/')
output = str(user.current_layout)
assert output == '''
q-layout
q-page-container
q-page
div
Label [text=Hello]
Label [text=Hidden Label, visible=False]
'''.strip()

0 comments on commit e168cf2

Please sign in to comment.