Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JSON serialization for ShadowRoot #13680

Merged
merged 4 commits into from
Mar 25, 2024

Conversation

sbabcoc
Copy link
Contributor

@sbabcoc sbabcoc commented Mar 11, 2024

User description

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

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

Currently, attempts to specify ShadowRoot objects as arguments for JavaScript fragments via the executeScript() function fail with this exception:

java.lang.IllegalArgumentException: Argument is of an illegal type: org.openqa.selenium.remote.ShadowRoot
	at org.openqa.selenium.remote.internal.WebElementToJsonConverter.apply(WebElementToJsonConverter.java:81)

Examination of the apply() function confirms that no support is provided for ShadowRoot.

Description

This PR adds support for specifying ShadowRoot objects as script arguments in executeScript() calls.

Motivation and Context

It's currently impossible to provide a ShadowRoot search context as an argument to a JavaScript fragment.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • I have read the contributing document.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

Type

bug_fix, enhancement


Description

  • Added support for JSON serialization of ShadowRoot objects in Selenium, allowing them to be used as arguments in executeScript() calls.
  • Implemented a new WebElementToJsonConverter class to handle JSON serialization of RemoteWebElement and ShadowRoot.
  • Deprecated the old WebElementToJsonConverter class and updated references across the project to use the new class.
  • Updated tests to reflect changes in WebElementToJsonConverter usage.

Changes walkthrough

Relevant files
Configuration changes
RemoteWebDriver.java
Remove Deprecated Import in RemoteWebDriver                           

java/src/org/openqa/selenium/remote/RemoteWebDriver.java

  • Removed import for WebElementToJsonConverter from
    org.openqa.selenium.remote.internal.
  • +0/-1     
    W3CHttpCommandCodec.java
    Update Import to New WebElementToJsonConverter                     

    java/src/org/openqa/selenium/remote/codec/w3c/W3CHttpCommandCodec.java

  • Updated import to use the new WebElementToJsonConverter from
    org.openqa.selenium.remote.
  • +1/-1     
    Enhancement
    WebElementToJsonConverter.java
    Add JSON Serialization Support for ShadowRoot                       

    java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java

  • Added a new WebElementToJsonConverter class supporting JSON
    serialization for RemoteWebElement and ShadowRoot.
  • Implemented conversion logic for various types including primitives,
    collections, maps, and specifically ShadowRoot.
  • +93/-0   
    WebElementToJsonConverter.java
    Deprecate Old WebElementToJsonConverter                                   

    java/src/org/openqa/selenium/remote/internal/WebElementToJsonConverter.java

  • Deprecated the old WebElementToJsonConverter class.
  • Redirected usage to the new WebElementToJsonConverter location.
  • +3/-69   
    Tests
    WebElementToJsonConverterTest.java
    Update Tests for New WebElementToJsonConverter                     

    java/test/org/openqa/selenium/remote/internal/WebElementToJsonConverterTest.java

    • Updated test to use the new WebElementToJsonConverter.
    +2/-1     
    UsingPageFactoryTest.java
    Update PageFactory Test Imports                                                   

    java/test/org/openqa/selenium/support/pagefactory/UsingPageFactoryTest.java

    • Updated import to use the new WebElementToJsonConverter.
    +1/-1     

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

    Copy link
    Contributor

    PR Description updated to latest commit (7bcf22a)

    Copy link
    Contributor

    PR Review

    ⏱️ Estimated effort to review [1-5]

    3, because the PR involves changes across multiple files including core functionality, tests, and internal structure adjustments. The complexity of the changes, especially around serialization and deserialization, requires a thorough understanding of the existing codebase and the WebDriver protocol.

    🧪 Relevant tests

    No

    🔍 Possible issues

    Possible Bug: The PR adds support for ShadowRoot serialization but does not include unit tests specifically for this new functionality. It's crucial to ensure that this addition does not introduce regressions or unexpected behavior.

    Compatibility Concern: The removal of WebElementToJsonConverter from org.openqa.selenium.remote.internal and its addition to org.openqa.selenium.remote might lead to compatibility issues with code depending on the old location. While the PR attempts to address this by extending the new class in the old location, thorough testing is needed to ensure this change does not break existing integrations.

    🔒 Security concerns

    No


    ✨ Review tool usage guide:

    Overview:
    The review tool scans the PR code changes, and generates a PR review. The tool can be triggered automatically every time a new PR is opened, or can be invoked manually by commenting on any PR.
    When commenting, to edit configurations related to the review tool (pr_reviewer section), use the following template:

    /review --pr_reviewer.some_config1=... --pr_reviewer.some_config2=...
    

    With a configuration file, use the following template:

    [pr_reviewer]
    some_config1=...
    some_config2=...
    
    Utilizing extra instructions

    The review tool can be configured with extra instructions, which can be used to guide the model to a feedback tailored to the needs of your project.

    Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify the relevant sub-tool, and the relevant aspects of the PR that you want to emphasize.

    Examples for extra instructions:

    [pr_reviewer] # /review #
    extra_instructions="""
    In the 'possible issues' section, emphasize the following:
    - Does the code logic cover relevant edge cases?
    - Is the code logic clear and easy to understand?
    - Is the code logic efficient?
    ...
    """
    

    Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.

    How to enable\disable automation
    • When you first install PR-Agent app, the default mode for the review tool is:
    pr_commands = ["/review", ...]
    

    meaning the review tool will run automatically on every PR, with the default configuration.
    Edit this field to enable/disable the tool, or to change the used configurations

    Auto-labels

    The review tool can auto-generate two specific types of labels for a PR:

    • a possible security issue label, that detects possible security issues (enable_review_labels_security flag)
    • a Review effort [1-5]: x label, where x is the estimated effort to review the PR (enable_review_labels_effort flag)
    Extra sub-tools

    The review tool provides a collection of possible feedbacks about a PR.
    It is recommended to review the possible options, and choose the ones relevant for your use case.
    Some of the feature that are disabled by default are quite useful, and should be considered for enabling. For example:
    require_score_review, require_soc2_ticket, and more.

    Auto-approve PRs

    By invoking:

    /review auto_approve
    

    The tool will automatically approve the PR, and add a comment with the approval.

    To ensure safety, the auto-approval feature is disabled by default. To enable auto-approval, you need to actively set in a pre-defined configuration file the following:

    [pr_reviewer]
    enable_auto_approval = true
    

    (this specific flag cannot be set with a command line argument, only in the configuration file, committed to the repository)

    You can also enable auto-approval only if the PR meets certain requirements, such as that the estimated_review_effort is equal or below a certain threshold, by adjusting the flag:

    [pr_reviewer]
    maximal_review_effort = 5
    
    More PR-Agent commands

    To invoke the PR-Agent, add a comment using one of the following commands:

    • /review: Request a review of your Pull Request.
    • /describe: Update the PR title and description based on the contents of the PR.
    • /improve [--extended]: Suggest code improvements. Extended mode provides a higher quality feedback.
    • /ask <QUESTION>: Ask a question about the PR.
    • /update_changelog: Update the changelog based on the PR's contents.
    • /add_docs 💎: Generate docstring for new components introduced in the PR.
    • /generate_labels 💎: Generate labels for the PR based on the PR's contents.
    • /analyze 💎: Automatically analyzes the PR, and presents changes walkthrough for each component.

    See the tools guide for more details.
    To list the possible configuration parameters, add a /config comment.

    See the review usage page for a comprehensive guide on using this tool.

    Copy link
    Contributor

    codiumai-pr-agent-pro bot commented Mar 11, 2024

    PR Code Suggestions

    CategorySuggestions                                                                                                                                                       
    Enhancement
    Improve exception handling for non-String Map keys.

    Consider implementing a more specific exception handling strategy for the
    IllegalArgumentException thrown when a non-String key is encountered in a Map. This could
    include logging the error or attempting to convert the key to a String if possible, to
    make the code more robust and user-friendly.

    java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java [74-75]

    -throw new IllegalArgumentException(
    -    "All keys in Map script arguments must be strings: " + key.getClass().getName());
    +// Example of a more detailed exception handling
    +try {
    +    String keyStr = key.toString();
    +    converted.put(keyStr, apply(entry.getValue()));
    +} catch (Exception e) {
    +    // Log the error or handle it accordingly
    +    throw new IllegalArgumentException(
    +        "Failed to convert Map key to String: " + key.getClass().getName(), e);
    +}
     
    Use Java 8 Streams in arrayToList for conciseness.

    For the arrayToList method, consider using Java 8 Streams for a more concise and readable
    implementation. This approach can improve the readability and potentially the performance
    of the code.

    java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java [86-91]

    -List<Object> list = new ArrayList<>();
    -for (int i = 0; i < Array.getLength(array); i++) {
    -  list.add(Array.get(array, i));
    -}
    -return list;
    +return IntStream.range(0, Array.getLength(array))
    +                .mapToObj(i -> Array.get(array, i))
    +                .collect(Collectors.toList());
     
    Add unit tests for ShadowRoot serialization.

    Consider adding unit tests specifically for the new ShadowRoot serialization logic to
    ensure its correct behavior and to catch any potential issues early. This would enhance
    the reliability of the serialization process.

    java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java [55-56]

    -if (arg instanceof ShadowRoot) {
    -  return Map.of(Dialect.W3C.getShadowRootElementKey(), ((ShadowRoot) arg).getId());
    +// Suggested unit test structure
    +@Test
    +public void shadowRootSerializationTest() {
    +    ShadowRoot shadowRoot = mock(ShadowRoot.class);
    +    when(shadowRoot.getId()).thenReturn("shadow-root-id");
    +    Object result = new WebElementToJsonConverter().apply(shadowRoot);
    +    assertEquals(Map.of("shadow-6066-11e4-a52e-4f735466cecf", "shadow-root-id"), result);
     }
     
    Maintainability
    Refactor apply method for better readability.

    To enhance code readability and maintainability, consider refactoring the apply method by
    extracting the logic for handling different types of arguments (e.g., RemoteWebElement,
    ShadowRoot, Collection, Map) into separate private methods.

    java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java [43-83]

     if (arg instanceof RemoteWebElement) {
    -  return Map.of(Dialect.W3C.getEncodedElementKey(), ((RemoteWebElement) arg).getId());
    +  return handleRemoteWebElement(arg);
     }
     ...
     if (arg instanceof Map<?, ?>) {
    +  return handleMap(arg);
    +}
    +...
    +// Example private methods
    +private Object handleRemoteWebElement(Object arg) {
    +  return Map.of(Dialect.W3C.getEncodedElementKey(), ((RemoteWebElement) arg).getId());
    +}
    +private Object handleMap(Object arg) {
       Map<?, ?> args = (Map<?, ?>) arg;
       ...
    +  return converted;
     }
     
    Best practice
    Use generics for type safety in apply method.

    To ensure type safety and avoid potential ClassCastException, use generics for the Map and
    Collection handling in the apply method. This will make the code more robust and
    maintainable.

    java/src/org/openqa/selenium/remote/WebElementToJsonConverter.java [63-79]

    -if (arg instanceof Collection<?>) {
    +if (arg instanceof Collection) {
       Collection<?> args = (Collection<?>) arg;
    -  return args.stream().map(this).collect(toList());
    +  return args.stream().map(this::apply).collect(Collectors.toList());
     }
     ...
    -if (arg instanceof Map<?, ?>) {
    +if (arg instanceof Map) {
       Map<?, ?> args = (Map<?, ?>) arg;
    -  ...
    +  Map<String, Object> converted = new HashMap<>();
    +  args.forEach((key, value) -> {
    +    if (!(key instanceof String)) {
    +      throw new IllegalArgumentException("Map key must be a String: " + key);
    +    }
    +    converted.put((String) key, apply(value));
    +  });
    +  return converted;
     }
     

    ✨ Improve tool usage guide:

    Overview:
    The improve tool scans the PR code changes, and automatically generates suggestions for improving the PR code. The tool can be triggered automatically every time a new PR is opened, or can be invoked manually by commenting on a PR.
    When commenting, to edit configurations related to the improve tool (pr_code_suggestions section), use the following template:

    /improve --pr_code_suggestions.some_config1=... --pr_code_suggestions.some_config2=...
    

    With a configuration file, use the following template:

    [pr_code_suggestions]
    some_config1=...
    some_config2=...
    
    Enabling\disabling automation

    When you first install the app, the default mode for the improve tool is:

    pr_commands = ["/improve --pr_code_suggestions.summarize=true", ...]
    

    meaning the improve tool will run automatically on every PR, with summarization enabled. Delete this line to disable the tool from running automatically.

    Utilizing extra instructions

    Extra instructions are very important for the improve tool, since they enable to guide the model to suggestions that are more relevant to the specific needs of the project.

    Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify relevant aspects that you want the model to focus on.

    Examples for extra instructions:

    [pr_code_suggestions] # /improve #
    extra_instructions="""
    Emphasize the following aspects:
    - Does the code logic cover relevant edge cases?
    - Is the code logic clear and easy to understand?
    - Is the code logic efficient?
    ...
    """
    

    Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.

    A note on code suggestions quality
    • While the current AI for code is getting better and better (GPT-4), it's not flawless. Not all the suggestions will be perfect, and a user should not accept all of them automatically.
    • Suggestions are not meant to be simplistic. Instead, they aim to give deep feedback and raise questions, ideas and thoughts to the user, who can then use his judgment, experience, and understanding of the code base.
    • Recommended to use the 'extra_instructions' field to guide the model to suggestions that are more relevant to the specific needs of the project, or use the custom suggestions 💎 tool
    • With large PRs, best quality will be obtained by using 'improve --extended' mode.
    More PR-Agent commands

    To invoke the PR-Agent, add a comment using one of the following commands:

    • /review: Request a review of your Pull Request.
    • /describe: Update the PR title and description based on the contents of the PR.
    • /improve [--extended]: Suggest code improvements. Extended mode provides a higher quality feedback.
    • /ask <QUESTION>: Ask a question about the PR.
    • /update_changelog: Update the changelog based on the PR's contents.
    • /add_docs 💎: Generate docstring for new components introduced in the PR.
    • /generate_labels 💎: Generate labels for the PR based on the PR's contents.
    • /analyze 💎: Automatically analyzes the PR, and presents changes walkthrough for each component.

    See the tools guide for more details.
    To list the possible configuration parameters, add a /config comment.

    See the improve usage page for a more comprehensive guide on using this tool.

    @sbabcoc sbabcoc force-pushed the pr/fix-javascript-shadowroot-issue branch 7 times, most recently from 57ef477 to 4d4740a Compare March 11, 2024 20:43
    @joerg1985
    Copy link
    Member

    @sbabcoc i had a short look, why has the WebElementToJsonConverter been moved to another package?

    @sbabcoc
    Copy link
    Contributor Author

    sbabcoc commented Mar 12, 2024

    @joerg1985 I had to move WebElementToJsonConverter because it needs access to ShadowRoot, which is package private. It's a bit odd that WebElementToJsonConverter was in the "internal" package in the first place, because the companion JsonToWebElementConverter is not - probably because it also requires access to ShadowRoot.

    @sbabcoc sbabcoc force-pushed the pr/fix-javascript-shadowroot-issue branch 2 times, most recently from 31de941 to 64c21d0 Compare March 12, 2024 21:00
    @sbabcoc sbabcoc force-pushed the pr/fix-javascript-shadowroot-issue branch 2 times, most recently from 81932fd to 7862009 Compare March 12, 2024 21:56
    @codecov-commenter
    Copy link

    codecov-commenter commented Mar 13, 2024

    Codecov Report

    All modified and coverable lines are covered by tests ✅

    Project coverage is 58.48%. Comparing base (f437fdd) to head (37a4f03).

    ❗ Current head 37a4f03 differs from pull request most recent head 61df1d4. Consider uploading reports for the commit 61df1d4 to get more accurate results

    ❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

    Additional details and impacted files
    @@           Coverage Diff           @@
    ##            trunk   #13680   +/-   ##
    =======================================
      Coverage   58.48%   58.48%           
    =======================================
      Files          86       86           
      Lines        5270     5270           
      Branches      220      220           
    =======================================
      Hits         3082     3082           
      Misses       1968     1968           
      Partials      220      220           

    ☔ View full report in Codecov by Sentry.
    📢 Have feedback on the report? Share it here.

    @sbabcoc sbabcoc force-pushed the pr/fix-javascript-shadowroot-issue branch 2 times, most recently from e748f40 to 421154c Compare March 13, 2024 18:54
    @sbabcoc sbabcoc force-pushed the pr/fix-javascript-shadowroot-issue branch 5 times, most recently from 5ef6dc6 to 5e03d14 Compare March 15, 2024 23:51
    @sbabcoc sbabcoc force-pushed the pr/fix-javascript-shadowroot-issue branch 2 times, most recently from c0b3c38 to 61df1d4 Compare March 23, 2024 22:24
    @diemol diemol force-pushed the pr/fix-javascript-shadowroot-issue branch from 61df1d4 to 6866131 Compare March 25, 2024 17:21
    Copy link
    Member

    @diemol diemol left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Thank you, @sbabcoc!

    @diemol diemol merged commit 4668df3 into SeleniumHQ:trunk Mar 25, 2024
    36 of 39 checks passed
    @diemol
    Copy link
    Member

    diemol commented Mar 25, 2024

    @sbabcoc was there an issue attached to this PR?

    @sbabcoc
    Copy link
    Contributor Author

    sbabcoc commented Mar 25, 2024

    @diemol I haven't written one up. The issue is clearly an inconsistency in the handling of ShadowDom contexts, though, and the failure occurs in 100% of cases.
    Would you like me to write up an issue?

    @sbabcoc sbabcoc deleted the pr/fix-javascript-shadowroot-issue branch March 25, 2024 18:38
    @diemol
    Copy link
    Member

    diemol commented Mar 25, 2024

    Not needed, I just thought I missed it.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    5 participants