-
Notifications
You must be signed in to change notification settings - Fork 229
/
FormValidation.java
183 lines (161 loc) · 7.59 KB
/
FormValidation.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
* The MIT License
*
* Copyright (c) Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jenkinsci.test.acceptance.po;
import org.hamcrest.Description;
import org.hamcrest.StringDescription;
import org.jenkinsci.test.acceptance.Matcher;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import javax.annotation.Nonnull;
import java.util.List;
import static org.hamcrest.Matchers.*;
import static org.jenkinsci.test.acceptance.po.CapybaraPortingLayer.by;
/**
* Result of form field validation.
*
* @see Control#getFormValidation()
* @see FormValidation#silent()
* @see FormValidation#reports(Kind, String)
*
* @author ogondza.
*/
public class FormValidation {
public enum Kind {
OK, WARNING, ERROR;
public static Kind get(String cls) {
if (cls == null || cls.isEmpty()) { // class can be empty as it is handled differently than other attributes by selenium
cls = "ok"; // No class means ok without message
}
return valueOf(cls.toUpperCase());
}
}
private final @Nonnull Kind kind;
private final @Nonnull String message;
public static FormValidation await(Control control) {
return await(control, false);
}
public static FormValidation await(Control control, boolean silent) {
WebElement element = control.resolve();
WebElement validationArea;
// Special handling for validation buttons and their markup
if (element.getTagName().equals("button")) {
WebElement spinner = element.findElement(by.xpath("./../../../following-sibling::div[1]"));
// Wait as long as there is some spinner shown on the page
control.waitFor().until(() -> !spinner.isDisplayed());
// Expand details (are there any) before we get the area element as doing so afterwards would stale the element reference
for (WebElement elem : element.findElements(By.linkText("(show details)"))) {
elem.click();
}
validationArea = element.findElement(by.xpath("./../../../following-sibling::div[2]"));
} else {
// Fire validation if it was not already
element.sendKeys(Keys.TAB);
// Wait for validation area to stop being <div></div>
try {
validationArea = control.waitFor().until(() -> {
// path to validation area is the parents sibling with class `validation-error-area`
String xpath = silent ? "../../div[contains(@class,'validation-error-area')]" :
"../../div[contains(@class,'validation-error-area--visible')]";
return element.findElement(by.xpath(xpath));
});
} catch (NoSuchElementException e) {
// TODO old form validation, remove once not in current LTS line
validationArea = control.waitFor().until(() -> {
// path to validation area is the parents sibling with class `validation-error-area`
WebElement va = element.findElement(by.xpath("../../div[contains(@class,'validation-error-area')]"));
try {
va.findElement(by.xpath("./div[2]"));
return va;
} catch (NoSuchElementException noDiv) {
// https://issues.jenkins-ci.org/browse/JENKINS-59605?focusedCommentId=377474&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-377474
// There are known false-negatives in ATH so let's presume this is done and successful until the core is fixed.
return va;
}
});
}
}
return new FormValidation(validationArea);
}
public FormValidation(WebElement element) {
List<WebElement> divs = element.findElements(by.xpath("div"));
// TODO the divs.get(1) part is old form validation, remove once not in current LTS line
WebElement outcome = divs.size() == 1 ? divs.get(0) : divs.get(1).findElement(by.xpath("div"));
this.kind = extractKind(outcome);
this.message = outcome.getText();
}
private @Nonnull Kind extractKind(WebElement element) {
String kindClass = element.getAttribute("class");
try {
return Kind.get(kindClass);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(
"Unknown kind class provided '" + kindClass + "' in " + element.getAttribute("outerHTML"),
ex
);
}
}
public @Nonnull Kind getKind() {
return kind;
}
public @Nonnull String getMessage() {
return message;
}
@Override public String toString() {
return kind + "(" + message + ")";
}
/**
* When either there is no validation or empty OK was returned (there is no way to tell that apart).
*/
public static Matcher<FormValidation> silent() {
return new Matcher<FormValidation>("No form validation result should be presented") {
@Override public boolean matchesSafely(FormValidation item) {
return item.getKind() == Kind.OK && item.getMessage().isEmpty();
}
@Override public void describeMismatchSafely(FormValidation item, Description mismatchDescription) {
mismatchDescription.appendText("It is " + item.toString());
}
};
}
public static Matcher<FormValidation> reports(final Kind kind, final String message) {
return reports(kind, equalTo(message));
}
public static Matcher<FormValidation> reports(final Kind kind, final org.hamcrest.Matcher<String> message) {
StringDescription sd = new StringDescription();
message.describeTo(sd);
return new Matcher<FormValidation>("Validation reporting " + kind + " with message: " + sd.toString()) {
@Override public boolean matchesSafely(FormValidation item) {
return item.getKind() == kind && message.matches(item.getMessage());
}
@Override public void describeMismatchSafely(FormValidation item, Description mismatchDescription) {
if (item.getKind() != kind) {
mismatchDescription.appendText("It is " + item.toString());
} else {
message.describeMismatch(item.getMessage(), mismatchDescription);
}
}
};
}
}