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.4KB

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