SeleniumHQ / seleniumhq.github.io

Official Selenium website and documentation
https://selenium.dev
Apache License 2.0
1.13k stars 1.32k forks source link

Update python locators docs #1903

Open pmartinez1 opened 2 months ago

pmartinez1 commented 2 months ago

User description

Thanks for contributing to the Selenium site and documentation! A PR well described will help maintainers to review and merge it quickly

Before submitting your PR, please check our contributing guidelines. Avoid large PRs, and help reviewers by making them as simple and short as possible.

Description

This PR moves the code to the test_locators.py file in the python/tests directory.

Motivation and Context

Helps the code be more organized, instead of having stand alone code that has not been tested.

Types of changes

Checklist


PR Type

Tests, Documentation


Description


Changes walkthrough 📝

Relevant files
Tests
test_locators.py
Add test cases for various Selenium locators in Python     

examples/python/tests/elements/test_locators.py
  • Added multiple test functions for different locator strategies.
  • Included tests for class name, CSS selector, ID, name, link text,
    partial link text, tag name, and XPath.
  • Added a test for relative locators with a skip marker.
  • +84/-0   
    Documentation
    locators.en.md
    Update Python locator examples to reference test file       

    website_and_docs/content/documentation/webdriver/elements/locators.en.md
  • Updated Python code examples to reference new test file.
  • Replaced inline code with links to the test file.
  • +30/-40 
    locators.ja.md
    Update Python locator examples to reference test file       

    website_and_docs/content/documentation/webdriver/elements/locators.ja.md
  • Updated Python code examples to reference new test file.
  • Replaced inline code with links to the test file.
  • +30/-40 
    locators.pt-br.md
    Update Python locator examples to reference test file       

    website_and_docs/content/documentation/webdriver/elements/locators.pt-br.md
  • Updated Python code examples to reference new test file.
  • Replaced inline code with links to the test file.
  • +30/-40 
    locators.zh-cn.md
    Update Python locator examples to reference test file       

    website_and_docs/content/documentation/webdriver/elements/locators.zh-cn.md
  • Updated Python code examples to reference new test file.
  • Replaced inline code with links to the test file.
  • +30/-40 

    💡 PR-Agent usage: Comment /help on the PR to get a list of all available PR-Agent tools and their descriptions

    netlify[bot] commented 2 months ago

    Deploy request for selenium-dev pending review.

    Visit the deploys page to approve it

    Name Link
    Latest commit 64fae4b34addeea5bc995cc6dec449b02d1ca8d6
    codiumai-pr-agent-pro[bot] commented 2 months ago

    PR Reviewer Guide 🔍

    ⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
    🧪 PR contains tests
    🔒 No security concerns identified
    ⚡ Key issues to review

    Resource Management
    The WebDriver instances are not properly closed using a context manager or try-finally block, which may lead to resource leaks. Test Isolation
    The tests are not properly isolated, as they share the same WebDriver instance across multiple test functions.
    codiumai-pr-agent-pro[bot] commented 2 months ago

    PR Code Suggestions ✨

    CategorySuggestion                                                                                                                                    Score
    Best practice
    Use a context manager for WebDriver to ensure proper resource cleanup ___ **Consider using a context manager (with statement) for the WebDriver to ensure proper
    cleanup of resources, even if an exception occurs.** [examples/python/tests/elements/test_locators.py [7-11]](https://github.com/SeleniumHQ/seleniumhq.github.io/pull/1903/files#diff-37a84073184f55bf24b44b3a435c07704ca453e3d74fac87370df04efec76cb8R7-R11) ```diff -driver = webdriver.Chrome() -driver.get("https://www.selenium.dev/") +with webdriver.Chrome() as driver: + driver.get("https://www.selenium.dev/") + driver.find_element(By.CLASS_NAME, "td-home") -driver.find_element(By.CLASS_NAME, "td-home") - -driver.quit() - ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 9 Why: Using a context manager for WebDriver is a best practice that ensures resources are properly cleaned up even if an exception occurs, improving code reliability and maintainability.
    9
    Improve code block formatting for consistency and readability ___ **Use consistent formatting for the Python code blocks across all examples. Add a
    blank line after the opening triple backticks and before the closing triple
    backticks to improve readability.** [website_and_docs/content/documentation/webdriver/elements/locators.en.md [84-85]](https://github.com/SeleniumHQ/seleniumhq.github.io/pull/1903/files#diff-bad5c08d78f84e8bd7528d1a2a3181973375147d3f8bc5b9678cfce7dbe76aabR84-R85) ```diff {{< tab header="Python" text=true >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}} ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 7 Why: The suggestion improves readability by adding a blank line in the code block, which is a minor but beneficial enhancement for consistency across examples.
    7
    Enhancement
    Add an explicit wait before finding elements to improve test reliability ___ **Consider adding an explicit wait before finding elements to ensure the page has
    loaded completely, improving test reliability.** [examples/python/tests/elements/test_locators.py [7-11]](https://github.com/SeleniumHQ/seleniumhq.github.io/pull/1903/files#diff-37a84073184f55bf24b44b3a435c07704ca453e3d74fac87370df04efec76cb8R7-R11) ```diff +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + driver = webdriver.Chrome() driver.get("https://www.selenium.dev/") -driver.find_element(By.CLASS_NAME, "td-home") +element = WebDriverWait(driver, 10).until( + EC.presence_of_element_located((By.CLASS_NAME, "td-home")) +) driver.quit() ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 9 Why: Implementing an explicit wait ensures that elements are only searched for once they are present, which significantly improves the reliability of the tests by reducing flakiness due to timing issues.
    9
    Error handling
    Add error handling for potential exceptions during element location ___ **Add error handling to catch and handle potential exceptions that may occur during
    element location, such as NoSuchElementException.** [examples/python/tests/elements/test_locators.py [7-11]](https://github.com/SeleniumHQ/seleniumhq.github.io/pull/1903/files#diff-37a84073184f55bf24b44b3a435c07704ca453e3d74fac87370df04efec76cb8R7-R11) ```diff +from selenium.common.exceptions import NoSuchElementException + driver = webdriver.Chrome() driver.get("https://www.selenium.dev/") -driver.find_element(By.CLASS_NAME, "td-home") +try: + driver.find_element(By.CLASS_NAME, "td-home") +except NoSuchElementException: + print("Element not found") +finally: + driver.quit() -driver.quit() - ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 8 Why: Adding error handling for exceptions like `NoSuchElementException` enhances the robustness of the test by allowing graceful handling of errors, which is crucial for reliable test execution.
    8
    Consistency
    ✅ Synchronize Python code examples across different language versions of the documentation ___
    Suggestion Impact:The Python code examples in the Japanese documentation were updated to match the English version by adjusting line numbers. code diff: ```diff {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L9" >}} {{< /tab >}} {{< tab header="CSharp" >}} var driver = new ChromeDriver(); @@ -111,7 +111,7 @@ driver.findElement(By.cssSelector("#fname")); {{< /tab >}} {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L18" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L17" >}} {{< /tab >}} {{< tab header="CSharp" >}} var driver = new ChromeDriver(); @@ -141,7 +141,7 @@ driver.findElement(By.id("lname")); {{< /tab >}} {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L26" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L25" >}} {{< /tab >}} {{< tab header="CSharp" >}} var driver = new ChromeDriver(); @@ -172,7 +172,7 @@ driver.findElement(By.name("newsletter")); {{< /tab >}} {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L34" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L33" >}} {{< /tab >}} {{< tab header="CSharp" >}} var driver = new ChromeDriver(); @@ -201,7 +201,7 @@ driver.findElement(By.linkText("Selenium Official Page")); {{< /tab >}} {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L42" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L41" >}} {{< /tab >}} {{< tab header="CSharp" >}} var driver = new ChromeDriver(); @@ -231,7 +231,7 @@ driver.findElement(By.partialLinkText("Official Page")); {{< /tab >}} {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L50" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L49" >}} {{< /tab >}} {{< tab header="CSharp" >}} var driver = new ChromeDriver(); @@ -259,7 +259,7 @@ driver.findElement(By.tagName("a")); {{< /tab >}} {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L58" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L57" >}} {{< /tab >}} {{< tab header="CSharp" >}} var driver = new ChromeDriver(); @@ -293,7 +293,7 @@ driver.findElement(By.xpath("//input[@value='f']")); {{< /tab >}} {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L66" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L65" >}} {{< /tab >}} {{< tab header="CSharp" >}} var driver = new ChromeDriver(); @@ -318,6 +318,8 @@ all you need to do is utilize `webdriver.common.by.By`, however in others it's as simple as setting a parameter in the FindElement function +### By + {{< tabpane langEqualsHeader=true >}} {{< badge-examples >}} {{< tab header="Java" >}} @@ -326,7 +328,7 @@ driver.findElement(By.className("information")); {{< /tab >}} {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L9" >}} {{< /tab >}} {{< tab header="CSharp" >}} var driver = new ChromeDriver(); @@ -346,7 +348,59 @@ {{< /tab >}} {{< /tabpane >}} - +### ByChained + +The `ByChained` class enables you to _chain_ two By locators together. For example, instead of having to +locate a parent element, and then a child element of that parent, you can instead combine those two `FindElement` +functions into one. + +{{< tabpane langEqualsHeader=true >}} + {{< tab header="Java" text=true >}} + {{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/elements/LocatorsTest.java#L37-L38" >}} + {{< /tab >}} + {{< tab header="Python" text=true >}} + {{< badge-code >}} + {{< /tab >}} + {{< tab header="CSharp" text=true >}} + {{< badge-code >}} + {{< /tab >}} + {{< tab header="Ruby" text=true >}} + {{< badge-code >}} + {{< /tab >}} + {{< tab header="JavaScript" text=true >}} + {{< badge-code >}} + {{< /tab >}} + {{< tab header="Kotlin" text=true >}} + {{< badge-code >}} + {{< /tab >}} +{{< /tabpane >}} + +### ByAll + +The `ByAll` class enables you to utilize two By locators at once, finding elements that mach _either_ of your By locators. +For example, instead of having to utilize two `FindElement()` functions to find the username and password input fields seperately, +you can instead find them together in one clean `FindElements()` + +{{< tabpane langEqualsHeader=true >}} + {{< tab header="Java" text=true >}} + {{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/elements/LocatorsTest.java#L22-L23">}} + {{< /tab >}} + {{< tab header="Python" text=true >}} + {{< badge-code >}} + {{< /tab >}} + {{< tab header="CSharp" text=true >}} + {{< badge-code >}} + {{< /tab >}} + {{< tab header="Ruby" text=true >}} + {{< badge-code >}} + {{< /tab >}} + {{< tab header="JavaScript" text=true >}} + {{< badge-code >}} + {{< /tab >}} + {{< tab header="Kotlin" text=true >}} + {{< badge-code >}} + {{< /tab >}} +{{< /tabpane >}} ## 相対ロケーター @@ -382,7 +436,7 @@ By emailLocator = RelativeLocator.with(By.tagName("input")).above(By.id("password")); {{< /tab >}} {{< tab header="Python" text=true >}} -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L75" >}} +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L73" >}} {{< /tab >}} {{< tab header="CSharp" >}} var emailLocator = RelativeBy.WithLocator(By.TagName("input")).Above(By.Id("password")); @@ -408,7 +462,7 @@ By passwordLocator = RelativeLocator.with(By.tagName("input")).below(By.id("email")); {{< /tab >}} {{< tab header="Python" text=true >}} -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L77" >}} +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L75" >}} {{< /tab >}} {{< tab header="CSharp" >}} var passwordLocator = RelativeBy.WithLocator(By.TagName("input")).Below(By.Id("email")); @@ -434,7 +488,7 @@ By cancelLocator = RelativeLocator.with(By.tagName("button")).toLeftOf(By.id("submit")); {{< /tab >}} {{< tab header="Python" text=true >}} -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L79" >}} +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L77" >}} {{< /tab >}} {{< tab header="CSharp" >}} var cancelLocator = RelativeBy.WithLocator(By.tagName("button")).LeftOf(By.Id("submit")); @@ -460,7 +514,7 @@ By submitLocator = RelativeLocator.with(By.tagName("button")).toRightOf(By.id("cancel")); {{< /tab >}} {{< tab header="Python" text=true >}} -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L81" >}} +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L79" >}} {{< /tab >}} {{< tab header="CSharp" >}} var submitLocator = RelativeBy.WithLocator(By.tagName("button")).RightOf(By.Id("cancel")); @@ -488,7 +542,7 @@ By emailLocator = RelativeLocator.with(By.tagName("input")).near(By.id("lbl-email")); {{< /tab >}} {{< tab header="Python" text=true >}} -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L83" >}} +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L81" >}} {{< /tab >}} {{< tab header="CSharp" >}} var emailLocator = RelativeBy.WithLocator(By.tagName("input")).Near(By.Id("lbl-email")); @@ -513,7 +567,7 @@ By submitLocator = RelativeLocator.with(By.tagName("button")).below(By.id("email")).toRightOf(By.id("cancel")); {{< /tab >}} {{< tab header="Python" text=true >}} -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L85" >}} +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L83" >}} ```
    ___ **Ensure that the Python code examples are consistent across all language versions of
    the documentation. Update the Japanese version to match the changes made in the
    English version.** [website_and_docs/content/documentation/webdriver/elements/locators.ja.md [81-82]](https://github.com/SeleniumHQ/seleniumhq.github.io/pull/1903/files#diff-06ad92a757672227e965b2d837608b89da6bb02315528815d9e6fa2eac9114afR81-R82) ```diff {{< tab header="Python" text=true >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}} ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 7 Why: Ensuring consistency across language versions is important for maintaining uniformity in documentation, although it is a minor change.
    7
    ✅ Align Python code examples in the Portuguese-Brazilian documentation with the English version ___
    Suggestion Impact:The commit updated the line numbers in the Portuguese-Brazilian documentation to align with the English version, ensuring consistency across language versions. code diff: ```diff {{< tab header="Python" text=true >}} - {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L9" >}} ```
    ___ **Update the Portuguese-Brazilian version of the documentation to match the changes
    made in the English version, ensuring consistency across all language versions.** [website_and_docs/content/documentation/webdriver/elements/locators.pt-br.md [84-85]](https://github.com/SeleniumHQ/seleniumhq.github.io/pull/1903/files#diff-d1f5d96e2cf4217d2473ebd28f060129cedf8bde199fc884493ffd01d205d994R84-R85) ```diff {{< tab header="Python" text=true >}} + {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}} ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 7 Why: The suggestion promotes consistency across different language versions, which is beneficial for users accessing the documentation in various languages, though it is a minor change.
    7
    Maintainability
    Parameterize test functions to reduce code duplication and improve maintainability ___ **Consider parameterizing the test functions to reduce code duplication and make it
    easier to add new test cases for different locator types.** [examples/python/tests/elements/test_locators.py [6-19]](https://github.com/SeleniumHQ/seleniumhq.github.io/pull/1903/files#diff-37a84073184f55bf24b44b3a435c07704ca453e3d74fac87370df04efec76cb8R6-R19) ```diff -def test_find_by_classname(): - driver = webdriver.Chrome() - driver.get("https://www.selenium.dev/") +@pytest.mark.parametrize("locator, value", [ + (By.CLASS_NAME, "td-home"), + (By.CSS_SELECTOR, "#announcement-banner"), + # Add more locator types and values here +]) +def test_find_element(locator, value): + with webdriver.Chrome() as driver: + driver.get("https://www.selenium.dev/") + driver.find_element(locator, value) - driver.find_element(By.CLASS_NAME, "td-home") - - driver.quit() - -def test_find_by_css_selector(): - driver = webdriver.Chrome() - driver.get("https://www.selenium.dev/") - - driver.find_element(By.CSS_SELECTOR, "#announcement-banner") - - driver.quit() - ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 7 Why: Parameterizing test functions reduces code duplication and enhances maintainability, making it easier to add new test cases and manage existing ones, although it is not as critical as resource management or error handling.
    7
    pmartinez1 commented 1 month ago

    Could I get a review please?

    pmartinez1 commented 1 month ago

    Thank you for the contribution @pmartinez1 ! A few changes must be made before it can be properly reviewed:

    pytest import is unnecessary, pytest will not run a function if it is not prefixed by test unless explicitly called in a test function. Line 70 can be removed as well

    Thanks for pointing that out. I've updated the PR.

    shbenzer commented 1 month ago

    Sorry about that, honestly forgot the relative locator section was included in this pr. I only did a cursory look through this commit's changes and commented when I saw you added an import for pytest.

    Could you expand the relative locator section to have an individual test for each relative locator (above, below, rightof, leftof, and near)?

    Once that's done I'll test it locally

    pmartinez1 commented 1 month ago

    Sorry about that, honestly forgot the relative locator section was included in this pr. I only did a cursory look through this commit's changes and commented when I saw you added an import for pytest.

    Could you expand the relative locator section to have an individual test for each relative locator (above, below, rightof, leftof, and near)?

    Once that's done I'll test it locally

    No problem. And yes, will split them up.

    pmartinez1 commented 1 month ago

    Sorry about that, honestly forgot the relative locator section was included in this pr. I only did a cursory look through this commit's changes and commented when I saw you added an import for pytest. Could you expand the relative locator section to have an individual test for each relative locator (above, below, rightof, leftof, and near)? Once that's done I'll test it locally

    No problem. And yes, will split them up.

    I ended updating them a bit more. Originally, I had created the test not to run, but follow along with the static image that is present, but I think your approach is better. I borrowed a little from your PR (for the sake of expediency) and now the tests are ready to be run.

    shbenzer commented 1 month ago

    Use what's available, I don't mind. I'll pull the branch for test

    shbenzer commented 1 month ago

    Tested changes locally and looks good. The goal is to move all the code examples to executable files in the repo, so eventually this whole section on relative locators will eventually be refactored to no longer refer to that image.

    What do you think about this PR? @harsha509

    pmartinez1 commented 4 weeks ago

    Hi @harsha509,

    Would you be able to give this a review? @shbenzer has already given it one pass.

    Thank you.

    pmartinez1 commented 3 weeks ago

    Wow. Again tests are failing on my PR :(

    For some reason, the "test_find_by_link_text" test is failing on Windows only, but seems to be deterministic. Does anyone have a Windows machine they can test on? Any ideas? I can't think of a reason why it fails there or at all. The test is simply going to the Selenium and looking for "Documentation". It passes in every other environment and a similar test, "test_find_by_partial_link_text" passes with no issue everywhere.

    I could add waits to see if it is a timing/loading issue?

    I haven't gotten around to finding an option for testing Windows yet, but it is on my list.

    Screenshot 2024-10-04 at 11 40 42 AM