yashaka / selene

User-oriented Web UI browser tests in Python
https://yashaka.github.io/selene/
MIT License
676 stars 145 forks source link

use ss() get elements ,get(query.text) not worked #502

Closed zhupengfarmer closed 5 months ago

zhupengfarmer commented 6 months ago

code: while ss(CallHomePage.callActionButtonList).matching(have.size_greater_than_orequal(5).not): pass ss(CallHomePage.callActionButtonList).get(query.text)

err:

selene.core.exceptions.TimeoutException: Message:

Timed out after 8s, while waiting for: browser.all(('css selector', 'div#header div.call-status.call-icon-box span')).text

yashaka commented 6 months ago

Heh, so far... This is as expected...

The idea of the entity.matching(condition) method – is to check condition immidiatly and return either True if condition is matched or False if condition is not matched for whatever reason.

The idea of the entity.wait_until(condition) method – is to wait for condition to be matched and return either True if condition is matched or False if condition is not matched for whatever reason after timeout was reached.

The idea of the entity.should(condition) method – is to wait for condition to be matched and either pass if condition is matched or raise error if condition is not matched for whatever reason and timeout was reached.

The idea of the entity.get(query) method - is to wait till query return any result without throwing error and then – either return this result or raise an error on timeout

If you want to do something similar to entity.get(query.something) but without waiting you can do entity.locate().something (or via shortcut: entity().something) - then such version will fail imidiately in case of error

If you want to do something similar to entity.get(query.something) that will not throw exception on timeout you have to implement a custom query, something like: entity.get(query.something_custom)... For example:

# my_framework/selene_extensions/query.py

from selene.query import *

def _get_maybe_currently_visible_text(element: Element) -> typing.Optional[str]:
    try:
        return element.locate().text
    except WebDriverException:
        return None

maybe_currently_visible_text = Query(
    'maybe currently visible text (or none)', _get_maybe_currently_visible_text
)

so you can use it like:

# texts/test_something.py

from my_framwork.selene_extensions import query

...

res = browser.element('#non-existing-element').get(query.maybe_currently_visible_text)  # will return None

...

res = browser.element('#non-existing-element').get(query.text)  # will fail on timeout

feel free to ask more questions if still something needs clarification...

zhupengfarmer commented 5 months ago

Thank you for the detailed explanation So far used selene s().get()/.by()/.should(be/hava) is worked but ss() similar methods are always not easy to use

code: while ss(CallHomePage.callActionButtonList).matching(have.size_greater_than_orequal(5).not): He means there are already 5 elements ss(CallHomePage.callActionButtonList).get(query.text) can not get text

yashaka commented 5 months ago

.not_ - works for any condition by negating it, or in other wrods - inverting it.

hence, ss(CallHomePage.callActionButtonList).matching(have.size_greater_than_or_equal(5).not_) should be same as not ss(CallHomePage.callActionButtonList).matching(have.size_greater_than_or_equal(5)) or ss(CallHomePage.callActionButtonList).matching(have.size_less_than(5)) – it it does not work like this, please, notify...

ss(CallHomePage.callActionButtonList).get(query.text) – this shoud not get text by design

If you look at the inferred type of query.text in your IDE:

image

– you will see, that this is a “query on Element”, more over, if you dig into implementation you'll see even more including the docstring:

image

So, it's explicitely say that you can call this query only on Element objects.

Then, you know that s(selector) return an Element object:

image

but ss(selector) return a Collection object

image

From where you concludes that you can call the query.text only on s(selector) not on ss(selector) ...

zhupengfarmer commented 5 months ago

What should I do if I want to obtain the text of ss () also For beginner Python developers like me, I need more documentation :) So far, I have been using selene , using the demo examples on the homepage

yashaka commented 5 months ago

So far, the Selene Documentation is pretty unstructured :( Sorry for that... We are working to improve it.

You can use these two guides and a cheatsheet of selene commands as a bit better starting point than the project README.md:

  1. https://autotest.how/selenides-quick-start-docs-md
  2. https://autotest.how/selenides-in-action-docs-md
  3. https://autotest.how/selene-cheatsheet-md

(there are buttons to select the python version and english as spoken language correspondingly in these articles)

What should I do if I want to obtain the text of ss ()

please, describe your case in more details, like – show an example of html (or maybe even ui rendered) and explain what you want to achieve from the "test logic" point of view, what is your test goal? (if you are implementing a test case) or what is your other goal? (if you are not doing testing but something else). Often selene users are trying to use Selene in some pretty not efficient way... It may happen, that actually you don't need to get text of ss... ss - finds a collection of many elements – You even would not say in native english something like "text of elements", ... technically you can collect a «concatenated» text of all items in the list, for example, but I can hardly imagine a case where you would need it in real test case scenario:) Most probably you would need textS – in plural :) of all corresponding elements of some collection of items. Currently there is no query.texts in Selene, maybe we will add it later (you can implement it by your own though)... But also, in context of testing, you usually need something from element - to assert it – then, you can use ss(selector).should(have.exact_texts('text of first element', ..., 'text of last element'))

yet, here are some snippets you can try to use in context of your question:''.join(element.get(query.text) for element in ss(selector)), if your collection found by selector is dynamically loaded during some time, then to have more stable version, you need to know in forward the amount of elements in collection and use something like ''.join(element.get(query.text) for element in ss(selector).should(have.size(expected_count)), or at least something like ''.join(element.get(query.text) for element in ss(selector).should(have.size_greater_than(0)) if you don't know expected count

zhupengfarmer commented 5 months ago

Thank you for the detailed explanation