You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SelectorPath.java 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.debug.internal;
  17. import java.util.HashMap;
  18. import java.util.List;
  19. import java.util.Locale;
  20. import java.util.Map;
  21. import com.google.gwt.dom.client.Element;
  22. import com.vaadin.client.ServerConnector;
  23. import com.vaadin.client.componentlocator.ComponentLocator;
  24. import com.vaadin.client.componentlocator.SelectorPredicate;
  25. /**
  26. * A single segment of a selector path pointing to an Element.
  27. * <p>
  28. * This class should be considered internal to the framework and may change at
  29. * any time.
  30. * <p>
  31. *
  32. * @since 7.1.x
  33. */
  34. public class SelectorPath {
  35. private final String path;
  36. private final Element element;
  37. private final ComponentLocator locator;
  38. private static Map<String, Integer> counter = new HashMap<>();
  39. private static Map<String, String> legacyNames = new HashMap<>();
  40. static {
  41. legacyNames.put("FilterSelect", "ComboBox");
  42. legacyNames.put("ScrollTable", "Table");
  43. }
  44. protected SelectorPath(ServerConnector c, Element e) {
  45. element = e;
  46. locator = new ComponentLocator(c.getConnection());
  47. path = locator.getPathForElement(e);
  48. }
  49. public String getPath() {
  50. return path;
  51. }
  52. public Element getElement() {
  53. return element;
  54. }
  55. public ComponentLocator getLocator() {
  56. return locator;
  57. }
  58. /**
  59. * Generate ElementQuery code for Java. Fallback to By.vaadin(path) if
  60. * dealing with LegacyLocator
  61. *
  62. * @return String containing Java code for finding the element described by
  63. * path
  64. */
  65. public String getElementQuery() {
  66. if (path.isEmpty() || locator.isValidForLegacyLocator(path)) {
  67. return getLegacyLocatorQuery();
  68. }
  69. String[] fragments;
  70. String tmpPath = path;
  71. List<SelectorPredicate> postFilters = SelectorPredicate
  72. .extractPostFilterPredicates(path);
  73. if (!postFilters.isEmpty()) {
  74. tmpPath = tmpPath.substring(1, tmpPath.lastIndexOf(')'));
  75. if (tmpPath.contains("#")) {
  76. // FIXME: SubParts should be handled.
  77. tmpPath = tmpPath.split("#")[0];
  78. }
  79. }
  80. // Generate an ElementQuery
  81. fragments = tmpPath.split("/");
  82. String elementQueryString = "";
  83. int index = 0;
  84. for (SelectorPredicate p : postFilters) {
  85. if (p.getIndex() > 0) {
  86. index = p.getIndex();
  87. }
  88. }
  89. for (int i = 1; i < fragments.length; ++i) {
  90. if (fragments[i].isEmpty()) {
  91. // Recursive searches cause empty fragments
  92. continue;
  93. }
  94. // if i == 1 or previous fragment was empty, search is recursive
  95. boolean recursive = (i > 1 ? fragments[i - 1].isEmpty() : false);
  96. // if elementQueryString is not empty, join the next query with .
  97. String queryFragment = (!elementQueryString.isEmpty() ? "." : "");
  98. // if search is not recursive, add another $ in front of fragment
  99. queryFragment += (!recursive ? "$" : "")
  100. + generateFragment(fragments[i]);
  101. elementQueryString += queryFragment;
  102. }
  103. if (!hasId(fragments[fragments.length - 1])) {
  104. if (index == 0) {
  105. elementQueryString += ".first()";
  106. } else {
  107. elementQueryString += ".get(" + index + ")";
  108. }
  109. }
  110. // Return full Java variable assignment and eQuery
  111. return generateJavaVariable(fragments[fragments.length - 1])
  112. + elementQueryString + ";";
  113. }
  114. /**
  115. * Finds out if the given query fragment has a defined id
  116. *
  117. * @param fragment
  118. * Query fragment
  119. * @return true if has id
  120. */
  121. private boolean hasId(String fragment) {
  122. for (SelectorPredicate p : SelectorPredicate
  123. .extractPredicates(fragment)) {
  124. if (p.getName().equals("id")) {
  125. return true;
  126. }
  127. }
  128. return false;
  129. }
  130. /**
  131. * Generates a recursive ElementQuery for given path fragment
  132. *
  133. * @param fragment
  134. * Query fragment
  135. * @return ElementQuery java code as a String
  136. */
  137. private String generateFragment(String fragment) {
  138. // Get Element.class -name
  139. String elementClass = getComponentName(fragment) + "Element.class";
  140. String queryFragment = "$(" + elementClass + ")";
  141. for (SelectorPredicate p : SelectorPredicate
  142. .extractPredicates(fragment)) {
  143. // Add in predicates like .caption and .id
  144. queryFragment += "." + p.getName() + "(\"" + p.getValue() + "\")";
  145. }
  146. return queryFragment;
  147. }
  148. /**
  149. * Returns the name of the component described by given query fragment.
  150. *
  151. * @param fragment
  152. * Query fragment
  153. * @return Class part of fragment
  154. */
  155. protected String getComponentName(String fragment) {
  156. return fragment.split("\\[")[0];
  157. }
  158. /**
  159. * Generates a legacy locator for SelectorPath.
  160. *
  161. * @return String containing Java code for element search and assignment
  162. */
  163. private String getLegacyLocatorQuery() {
  164. String name;
  165. if (!path.isEmpty()) {
  166. String[] frags = path.split("/");
  167. name = getComponentName(frags[frags.length - 1]).substring(1);
  168. } else {
  169. name = "root";
  170. }
  171. if (legacyNames.containsKey(name)) {
  172. name = legacyNames.get(name);
  173. }
  174. name = getNameWithCount(name);
  175. // Use direct path and elementX naming style.
  176. return "WebElement " + name.substring(0, 1).toLowerCase(Locale.ROOT)
  177. + name.substring(1) + " = getDriver().findElement(By.vaadin(\""
  178. + path + "\"));";
  179. }
  180. /**
  181. * Get variable name with counter for given component name.
  182. *
  183. * @param name
  184. * Component name
  185. * @return name followed by count
  186. */
  187. protected String getNameWithCount(String name) {
  188. if (!counter.containsKey(name)) {
  189. counter.put(name, 0);
  190. }
  191. counter.put(name, counter.get(name) + 1);
  192. name += counter.get(name);
  193. return name;
  194. }
  195. /**
  196. * Generate Java variable assignment from given selector fragment
  197. *
  198. * @param pathFragment
  199. * Selector fragment
  200. * @return piece of java code
  201. */
  202. private String generateJavaVariable(String pathFragment) {
  203. // Get element type and predicates from fragment
  204. List<SelectorPredicate> predicates = SelectorPredicate
  205. .extractPredicates(pathFragment);
  206. String elementType = pathFragment.split("\\[")[0];
  207. String name = getNameFromPredicates(predicates, elementType);
  208. if (name.equals(elementType)) {
  209. name = getNameWithCount(name);
  210. }
  211. // Replace unusable characters
  212. name = name.replaceAll("\\W", "");
  213. // Lowercase the first character of name
  214. return elementType + "Element "
  215. + name.substring(0, 1).toLowerCase(Locale.ROOT)
  216. + name.substring(1) + " = ";
  217. }
  218. /**
  219. * Get variable name based on predicates. Fallback to elementType
  220. *
  221. * @param predicates
  222. * Predicates related to element
  223. * @param elementType
  224. * Element type
  225. * @return name for Variable
  226. */
  227. private String getNameFromPredicates(List<SelectorPredicate> predicates,
  228. String elementType) {
  229. String name = elementType;
  230. for (SelectorPredicate p : predicates) {
  231. if ("caption".equals(p.getName())) {
  232. // Caption + elementType is a suitable name
  233. name = p.getValue() + elementType;
  234. } else if ("id".equals(p.getName())) {
  235. // Just id. This is unique, use it.
  236. return p.getValue();
  237. }
  238. }
  239. return name;
  240. }
  241. }