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

fix: generate suggested locators upon selecting element instead of upon retrieving source #1389

Merged
merged 36 commits into from
Mar 14, 2024

Conversation

eglitise
Copy link
Collaborator

This is a sizeable refactor of the logic behind generating suggested locators.
Until now, locator generation was invoked right after retrieving the app source, and it was run for every single node in the XML source, which is entirely unnecessary. For smaller XMLs, this delay was barely noticeable, but bigger XMLs could show a delay of several seconds or even more. iOS was also affected more than Android, due to the generation of class chains and predicate strings.
With this change, locator generation is invoked only upon selecting an element in an already-retrieved source, and it is run only for that element. In my experience it is also fast enough to not even be noticeable.
As a result of this change, the delay after refreshing the app source is reduced. I didn't do extensive testing, but for one sample app, the delay on Android was reduced by around 100ms, and on iOS by an entire second.
Fixes #1359.

Notable changes:

  • Invoke locator generation upon selecting an element instead of after retrieving the app source
  • Add a dedicated Redux event for locator generation
  • Remove unused selectedElementVariableName and electedElementVariableType from Redux storage
  • Move retrieval of simple locators to util.js (which already contained methods for retrieving complex locators)
    • Test spec files were merged accordingly
  • Move findElementByPath to util.js (it did not belong in the reducers file)
  • Rename ambiguous instances of source to clarify whether it is XML, JSON or Document
  • Use a single DOMParser instance instead of creating new ones for every conversion
  • Add findDOMNodeByPath method for finding a Node by path, similarly to findElementByPath for a JSON element
  • Update unit tests and add new ones for findDOMNodeByPath, findJSONElementByPath, getOptimalClassChain, getOptimalPredicateString
  • Enable predicate string for XCUIElementTypeApplication (which is a valid type). Not really sure why class chain did not work, so that was left disabled.

@github-actions github-actions bot added the fix This resolves a user-facing problem label Mar 13, 2024
@mykola-mokhnach
Copy link
Contributor

I love the optimization idea itself

I would say the whole current implementation of location heuristics might/should be refactored and nicely isolated to separate logical units (classes and methods). It is just a pain to read long methods containing hundreds of lines of code, especially without proper understanding of the background logic.

Would it make sense for you @eglitise if the logic for each locator type would be isolated into classes with shorter methods and cleaner hierarchy?

@eglitise
Copy link
Collaborator Author

I would say the whole current implementation of location heuristics might/should be refactored and nicely isolated to separate logical units (classes and methods).

I'm not sure about separating each locator type into its own module/class - really it's only xpath that has multiple methods. But separating all locator generation-specific methods from e.g. the source translation methods sounds very reasonable.

Copy link
Member

@KazuCocoa KazuCocoa left a comment

Choose a reason for hiding this comment

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

lgtm, overall. Thank you for this change, I tried to move the timing into select element, but did not have much time to understand and track the implementation... I tested with large source like I attached in the original issue.

I hope this will improve inspector users' experience after the page source loading

app/renderer/util.js Outdated Show resolved Hide resolved
test/unit/util-specs.js Outdated Show resolved Hide resolved
* @param {string} selectedElement element node in JSON format
* @param {string} sourceXML
* @param {boolean} isNative whether native context is active
* @returns {Object} mapping of strategies to selectors
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe we could have a better typedef for the returned result

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I couldn't find a definitive answer for how to describe an array of tuples, but hopefully Array<[string, string]> is valid syntax.

/**
* Get suggested selectors for simple locator strategies (which match a specific attribute)
*
* @param {Object} attributes element attributes
Copy link
Contributor

Choose a reason for hiding this comment

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

this typedef could be improved

* @param {Object} attributes element attributes
* @param {Document} sourceDoc
* @param {boolean} isNative whether native context is active
* @returns {Object} mapping of strategies to selectors
Copy link
Contributor

Choose a reason for hiding this comment

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

same as above

*
* @param {string} path a dot-separated string of indices
* @param {Document} sourceDoc
* @returns {Object} mapping of strategies to selectors
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

@eglitise eglitise merged commit 6093734 into appium:main Mar 14, 2024
5 of 6 checks passed
@eglitise eglitise deleted the optimize-locator-gen branch March 14, 2024 13:31
@jlipps
Copy link
Member

jlipps commented Mar 14, 2024

Didn't make it here in time to do a full review, but this is an extremely useful improvement! Thank you

laib3 pushed a commit to laib3/appium-inspector that referenced this pull request Nov 16, 2024
…on retrieving source (appium#1389)

* add slight caching for retrieved locator map

* remove unused third param for getOptimalXPath calls

* add comment to point to next actions

* WIP moving xpath calc to getLocators

* chore: remove unnecessary filter call

* chore: move findElementByPath to utils

* chore: remove unused state vars

* chore: revert to object for getLocators response

* chore: fix getLocators parameter

* chore: move getLocators to utils file

* start work on the complex locator detection

* chore: rename various source variables for clarity

* chore: move DOM conversion from areAttrAndValueUnique

* chore: add function to retrieve DOM node using path

* chore: update docstrings

* chore: save DOMParser in a const

* fix: calculate optimal xpath only on demand

* chore: do not pass local consts as parameters

* fix: enable predicate string for XCUIElementTypeApplication

* chore: adjust comments for getOptimalClassChain

* fix: calculate optimal class chain/predicate only on demand

* chore: define NATIVE_APP only once

* chore: update more var names

* chore: remove dedicated isIOS variable

* fix: skip accessibility id strategy in webviews

* chore: simplify childNodesOf

* chore: lint & format

* test: update unit tests

* test: add tests for accessibility id and class name

* test: add tests for path lookup methods

* test: add tests for class chain/predicate + format

* chore: split utils file

* chore: address review comments

* chore: update typedefs

* chore: update typedef again
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fix This resolves a user-facing problem
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bug: improve translateRecursively performance
4 participants