Skip to content

Commit 5a44673

Browse files
committed
Add indexing support for all query types
1 parent 04db5db commit 5a44673

File tree

8 files changed

+206
-25
lines changed

8 files changed

+206
-25
lines changed

src/main/java/javafxlibrary/keywords/AdditionalKeywords/Find.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package javafxlibrary.keywords.AdditionalKeywords;
22

33
import javafx.scene.Parent;
4+
import javafxlibrary.exceptions.JavaFXLibraryFatalException;
45
import javafxlibrary.exceptions.JavaFXLibraryNonFatalException;
56
import javafxlibrary.utils.finder.Finder;
67
import javafxlibrary.utils.RobotLog;
@@ -46,11 +47,12 @@ public Object find(String query, boolean failIfNotFound, Parent root) {
4647
try {
4748
return mapObject(new Finder().find(query, root));
4849

49-
} catch (JavaFXLibraryNonFatalException e){
50+
} catch (JavaFXLibraryNonFatalException e) {
5051
if (failIfNotFound)
5152
throw new JavaFXLibraryNonFatalException("Unable to find anything with query: \"" + query + "\"");
5253
return "";
53-
54+
} catch (JavaFXLibraryFatalException e) {
55+
throw e;
5456
} catch (Exception e) {
5557
throw new JavaFXLibraryNonFatalException("Find operation failed for query: \"" + query + "\"", e);
5658
}
@@ -69,6 +71,8 @@ public Object find(String query, boolean failIfNotFound) {
6971
throw new JavaFXLibraryNonFatalException("Unable to find anything with query: \"" + query + "\"");
7072
return "";
7173

74+
} catch (JavaFXLibraryFatalException e) {
75+
throw e;
7276
} catch (Exception e) {
7377
throw new JavaFXLibraryNonFatalException("Find operation failed for query: \"" + query + "\"", e);
7478
}
@@ -95,6 +99,8 @@ public List<Object> findAll(String query, boolean failIfNotFound, Parent root) {
9599
if (failIfNotFound)
96100
throw new JavaFXLibraryNonFatalException("Unable to find anything with query: \"" + query + "\"");
97101
return new ArrayList<>();
102+
} catch (JavaFXLibraryFatalException e) {
103+
throw e;
98104
} catch (Exception e) {
99105
throw new JavaFXLibraryNonFatalException("Find operation failed for query: \"" + query + "\"", e);
100106
}

src/main/java/javafxlibrary/utils/finder/FindOperation.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@
2323
import javafx.scene.Parent;
2424
import javafxlibrary.exceptions.JavaFXLibraryNonFatalException;
2525
import javafxlibrary.matchers.InstanceOfMatcher;
26+
import javafxlibrary.utils.RobotLog;
2627
import javafxlibrary.utils.TestFxAdapter;
2728
import org.testfx.api.FxRobotInterface;
2829
import org.testfx.matcher.control.LabeledMatchers;
2930
import org.testfx.service.query.NodeQuery;
3031

32+
import java.util.*;
33+
3134
public class FindOperation {
3235

3336
private Parent root;
@@ -43,9 +46,30 @@ public FindOperation(Parent root, Query query, boolean findAll) {
4346
}
4447

4548
protected Object executeLookup() {
46-
FindPrefix prefix = query.getPrefix();
47-
String lookupQuery = query.getQuery();
49+
// If find is called with a query that contains an index, use findAll methods instead
50+
if (!findAll && query.containsIndex()) {
51+
return executeOverriddenLookup();
52+
} else if (query.containsIndex()) {
53+
Set<Node> lookupResults = new LinkedHashSet<>((Set<Node>)executeLookup(query.getPrefix(), query.getQuery()));
54+
lookupResults.remove(root);
55+
Node nodeAtIndex = getLookupResultByIndex(lookupResults, query.getIndex());
56+
57+
if (nodeAtIndex != null)
58+
return new LinkedHashSet<>(Collections.singletonList(nodeAtIndex));
59+
return Collections.emptySet();
60+
}
4861

62+
return executeLookup(query.getPrefix(), query.getQuery());
63+
}
64+
65+
protected Object executeOverriddenLookup() {
66+
this.findAll = true;
67+
Set<Node> result = new LinkedHashSet<>((Set<Node>)executeLookup(query.getPrefix(), query.getQuery()));
68+
result.remove(root);
69+
return getLookupResultByIndex(result, query.getIndex());
70+
}
71+
72+
private Object executeLookup(FindPrefix prefix, String lookupQuery) {
4973
switch (prefix) {
5074
case ID:
5175
case CSS:
@@ -91,4 +115,12 @@ private NodeQuery classLookup(Parent root, String query) {
91115
"Node lookup: class was not found");
92116
}
93117
}
118+
119+
private Node getLookupResultByIndex(Set<Node> lookupResults, int index) {
120+
try {
121+
return new ArrayList<>(lookupResults).get(index);
122+
} catch (IndexOutOfBoundsException e) {
123+
return null;
124+
}
125+
}
94126
}

src/main/java/javafxlibrary/utils/finder/Finder.java

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,10 @@ public Node find(String query, Parent root) {
7373
}
7474

7575
private Node find(Parent root, int queryIndex) {
76-
String query = queries[queryIndex];
76+
Query query = new Query(queries[queryIndex]);
7777

7878
if (queryIndex < queries.length - 1) {
79-
// lookupResults might be unmodifiable, copy contents to a new Set
80-
Set<Node> lookupResults = executeFindAll(root, query);
81-
Set<Node> nodes = new LinkedHashSet<>();
82-
nodes.addAll(lookupResults);
79+
Set<Node> nodes = new LinkedHashSet<>(executeFindAll(root, query));
8380
nodes.remove(root);
8481

8582
for (Node node : nodes) {
@@ -129,12 +126,8 @@ public Set<Node> findAll(String query, Parent root) {
129126
}
130127

131128
private Set<Node> findAll(Parent root, int queryIndex) {
132-
String query = queries[queryIndex];
133-
Set<Node> lookupResults = executeFindAll(root, query);
134-
Set<Node> nodes = new LinkedHashSet<>();
135-
nodes.addAll(lookupResults);
136-
nodes.remove(root);
137-
129+
Query query = new Query(queries[queryIndex]);
130+
Set<Node> nodes = new LinkedHashSet<>(executeFindAll(root, query));
138131
if (queryIndex < queries.length - 1) {
139132
for (Node node : nodes)
140133
if (node instanceof Parent)
@@ -146,16 +139,15 @@ private Set<Node> findAll(Parent root, int queryIndex) {
146139
return results;
147140
}
148141

149-
private Node executeFind(Parent root, String query) {
150-
RobotLog.debug("Executing find with root: " + root + " and query: " + query);
151-
FindOperation findOperation = new FindOperation(root, new Query(query), false);
142+
private Node executeFind(Parent root, Query query) {
143+
RobotLog.debug("Executing find with root: " + root + " and query: " + query.getQuery());
144+
FindOperation findOperation = new FindOperation(root, query, false);
152145
return (Node) findOperation.executeLookup();
153146
}
154147

155-
// TODO: Add support for using indexes in queries (css=VBox[3]), xPath already implements this
156-
private Set<Node> executeFindAll(Parent root, String query) {
157-
RobotLog.debug("Executing find all with root: " + root + " and query: " + query);
158-
FindOperation findOperation = new FindOperation(root, new Query(query), true);
159-
return (Set<Node>) findOperation.executeLookup();
148+
private Set<Node> executeFindAll(Parent root, Query query) {
149+
RobotLog.debug("Executing find all with root: " + root + " and query: " + query.getQuery());
150+
FindOperation findOperation = new FindOperation(root, query, true);
151+
return new LinkedHashSet<>((Set<Node>)findOperation.executeLookup());
160152
}
161153
}

src/main/java/javafxlibrary/utils/finder/Query.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,25 @@
1717

1818
package javafxlibrary.utils.finder;
1919

20+
import javafxlibrary.exceptions.JavaFXLibraryFatalException;
21+
2022
public class Query {
2123

2224
private FindPrefix prefix;
2325
private String query;
26+
private int index = -1;
2427

2528
public Query(String query) {
2629
this.prefix = QueryParser.getPrefix(query);
30+
31+
if (this.prefix != FindPrefix.XPATH && QueryParser.containsIndex(query)) {
32+
this.index = QueryParser.getQueryIndex(query);
33+
if (this.index < 0) {
34+
throw new JavaFXLibraryFatalException("Invalid query \"" + query + "\": Minimum index value is 1!");
35+
}
36+
query = QueryParser.removeQueryIndex(query);
37+
}
38+
2739
this.query = QueryParser.removePrefix(query, this.prefix);
2840
}
2941

@@ -34,4 +46,12 @@ public FindPrefix getPrefix() {
3446
public String getQuery() {
3547
return this.query;
3648
}
49+
50+
public int getIndex() {
51+
return this.index;
52+
}
53+
54+
public boolean containsIndex() {
55+
return this.index != -1;
56+
}
3757
}

src/main/java/javafxlibrary/utils/finder/QueryParser.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import java.util.ArrayList;
2121
import java.util.Arrays;
2222
import java.util.List;
23+
import java.util.regex.Matcher;
24+
import java.util.regex.Pattern;
2325

2426
public class QueryParser {
2527

@@ -121,4 +123,18 @@ protected static String removePrefix(String query, FindPrefix prefix) {
121123
}
122124
throw new IllegalArgumentException("FindPrefix value " + prefix + " of query " + query + " is not supported");
123125
}
126+
127+
protected static boolean containsIndex(String query) {
128+
Pattern pattern = Pattern.compile(".*\\[\\d*]$");
129+
Matcher matcher = pattern.matcher(query);
130+
return matcher.matches();
131+
}
132+
133+
protected static int getQueryIndex(String query) {
134+
return Integer.parseInt(query.substring(query.lastIndexOf('[') + 1, query.length() - 1)) - 1;
135+
}
136+
137+
protected static String removeQueryIndex(String query) {
138+
return query.substring(0, query.lastIndexOf('['));
139+
}
124140
}

src/test/java/javafxlibrary/utils/finder/QueryParserTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,31 @@ public void getPrefix_InvalidValue() {
6262
thrown.expectMessage("Query \"notaprefix=someValue\" does not contain any supported prefix");
6363
QueryParser.getPrefix("notaprefix=someValue");
6464
}
65+
66+
@Test
67+
public void containsIndex() {
68+
Assert.assertTrue(QueryParser.containsIndex("Rectangle[0]"));
69+
Assert.assertTrue(QueryParser.containsIndex("Rectangle[8]"));
70+
Assert.assertTrue(QueryParser.containsIndex("Rectangle[22]"));
71+
Assert.assertTrue(QueryParser.containsIndex("Rectangle[1995]"));
72+
Assert.assertFalse(QueryParser.containsIndex("Node1"));
73+
Assert.assertFalse(QueryParser.containsIndex("xpath=Text[@text='text[2]'"));
74+
}
75+
76+
@Test
77+
public void getQueryIndex() {
78+
Assert.assertTrue(0 == QueryParser.getQueryIndex("Rectangle[1]"));
79+
Assert.assertTrue(8 == QueryParser.getQueryIndex("Rectangle[9]"));
80+
Assert.assertTrue(22 == QueryParser.getQueryIndex("Rectangle[23]"));
81+
Assert.assertTrue(1995 == QueryParser.getQueryIndex("Rectangle[1996]"));
82+
}
83+
84+
@Test
85+
public void removeQueryIndex() {
86+
Assert.assertEquals("Rectangle", QueryParser.removeQueryIndex("Rectangle[0]"));
87+
Assert.assertEquals("Rectangle", QueryParser.removeQueryIndex("Rectangle[22]"));
88+
Assert.assertEquals("Rectangle", QueryParser.removeQueryIndex("Rectangle[1995]"));
89+
Assert.assertEquals("VBox", QueryParser.removeQueryIndex("VBox[2]"));
90+
Assert.assertEquals("SomeClass", QueryParser.removeQueryIndex("SomeClass[12]"));
91+
}
6592
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package javafxlibrary.utils.finder;
2+
3+
import javafxlibrary.exceptions.JavaFXLibraryFatalException;
4+
import org.junit.Assert;
5+
import org.junit.Rule;
6+
import org.junit.Test;
7+
import org.junit.rules.ExpectedException;
8+
9+
public class QueryTest {
10+
11+
@Rule
12+
public ExpectedException thrown = ExpectedException.none();
13+
14+
@Test
15+
public void validQuery_withIndex() {
16+
Query query = new Query("css=VBox[1]");
17+
Assert.assertEquals("VBox", query.getQuery());
18+
Assert.assertEquals(FindPrefix.CSS, query.getPrefix());
19+
Assert.assertTrue(query.containsIndex());
20+
Assert.assertEquals(0, query.getIndex());
21+
}
22+
23+
@Test
24+
public void validQuery_noIndex() {
25+
Query query = new Query("class=javafx.scene.layout.VBox");
26+
Assert.assertEquals("javafx.scene.layout.VBox", query.getQuery());
27+
Assert.assertEquals(FindPrefix.CLASS, query.getPrefix());
28+
Assert.assertFalse(query.containsIndex());
29+
Assert.assertEquals(-1, query.getIndex());
30+
}
31+
32+
@Test
33+
public void invalidQueryIndex() {
34+
thrown.expect(JavaFXLibraryFatalException.class);
35+
thrown.expectMessage("Invalid query \"css=VBox[0]\": Minimum index value is 1!");
36+
new Query("css=VBox[0]");
37+
}
38+
}

src/test/robotframework/acceptance/FindTest.robot

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,52 @@ Find All From Multiple Windows Using Chained Selector
220220
Find All From Multiple Windows Containing Multiple Matches Using Chained Selector
221221
[Tags] smoke
222222
Set Test App ${FINDER_APP}
223-
${nodes} Find All css=VBox css=HBox css=.button
224-
Length Should Be ${nodes} 24
223+
${nodes} Find All css=VBox css=HBox css=.button
224+
Length Should Be ${nodes} 24
225+
226+
Find With Index
227+
[Tags] smoke
228+
Set Test App ${FINDER_APP}
229+
${root} Get Node Parent id=firstButton
230+
${target} Find xpath=/HBox/Button[4] true ${root}
231+
${button} Find css=Button[4]
232+
Should Be Equal ${button} ${target}
233+
234+
Find With Index - Chained Selector
235+
[Tags] smoke
236+
Set Test App ${FINDER_APP}
237+
${button} Find id=firstButton
238+
Click On ${button}
239+
${node} Find css=VBox pseudo=hover[2]
240+
Should Be Equal ${node} ${button}
241+
242+
Find With Index - Chained Selector Contains Index
243+
[Tags] smoke
244+
Set Test App ${FINDER_APP}
245+
${node} Find css=VBox[2] css=HBox Button
246+
${root} Get Node Parent text="Scene type 1"
247+
${target} Find xpath=/VBox/VBox[2]/HBox/Button true ${root}
248+
Should Be Equal ${node} ${target}
249+
250+
Find All With Index
251+
[Tags] smoke
252+
Set Test App ${FINDER_APP}
253+
${nodes} Find All css=Button[2]
254+
Length Should Be ${nodes} 4
255+
All Nodes Should Have Text ${nodes} 2
256+
257+
Find All With Index - Chained Selector Contains Index
258+
[Tags] smoke
259+
Set Test App ${FINDER_APP}
260+
${nodes} Find All css=VBox[2] css=HBox Button[1]
261+
Length Should Be ${nodes} 3
262+
All Nodes Should Have Text ${nodes} 5
263+
264+
Find With Index Below Minimum Value
265+
[Tags] smoke negative
266+
Set Test App ${FINDER_APP}
267+
${msg} Run Keyword And Expect Error * Find css=VBox[0]
268+
Should Be Equal ${msg} Invalid query "css=VBox[0]": Minimum index value is 1!
225269

226270
*** Keywords ***
227271
Set Test App
@@ -237,3 +281,9 @@ Change Current Application
237281

238282
Teardown all tests
239283
Close Javafx Application
284+
285+
All Nodes Should Have Text
286+
[Arguments] ${nodes} ${text}
287+
:FOR ${node} IN @{nodes}
288+
\ ${value} Get Node Text ${node}
289+
\ Should Be Equal ${value} ${text}

0 commit comments

Comments
 (0)