Skip to content

Commit 692d85d

Browse files
committed
Page header components
1 parent ab0120b commit 692d85d

3 files changed

Lines changed: 293 additions & 0 deletions

File tree

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright (c) 2018-2026 LabKey Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of 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,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.labkey.test.components.ui;
17+
18+
import org.labkey.test.Locator;
19+
import org.labkey.test.components.Component;
20+
import org.labkey.test.components.WebDriverComponent;
21+
import org.openqa.selenium.WebDriver;
22+
import org.openqa.selenium.WebElement;
23+
24+
import static org.labkey.test.util.selenium.WebElementUtils.tryMapElement;
25+
26+
/**
27+
* Wraps <AppPageHeader/> component
28+
*/
29+
public class AppPageHeader extends WebDriverComponent<AppPageHeader.ElementCache>
30+
{
31+
private final WebElement _el;
32+
private final WebDriver _driver;
33+
34+
protected AppPageHeader(WebElement element, WebDriver driver)
35+
{
36+
_el = element;
37+
_driver = driver;
38+
}
39+
40+
@Override
41+
public WebElement getComponentElement()
42+
{
43+
return _el;
44+
}
45+
46+
@Override
47+
public WebDriver getDriver()
48+
{
49+
return _driver;
50+
}
51+
52+
/**
53+
* Gets the text of the page header. If there is no header returns an empty string.
54+
*
55+
* @return Text from the page title, empty string if element is not there.
56+
*/
57+
public String getTitle()
58+
{
59+
return tryMapElement(elementCache().title, WebElement::getText);
60+
}
61+
62+
/**
63+
* Get the text of the subtitle of the page. If there is no subtitle, return an empty string.
64+
*
65+
* @return Text from the page subtitle, empty string if element is not there.
66+
*/
67+
public String getSubtitle()
68+
{
69+
return tryMapElement(elementCache().subtitle, WebElement::getText);
70+
}
71+
72+
/**
73+
* Get the text of the description of the page. If there is no description, returns an empty string.
74+
*
75+
* @return Text from the page description, empty string if element is not there.
76+
*/
77+
public String getDescription()
78+
{
79+
return tryMapElement(elementCache().description, WebElement::getText);
80+
}
81+
82+
/**
83+
* @throws UnsupportedOperationException Label color is not supported by AppPageHeader.
84+
*/
85+
public String getLabelColor()
86+
{
87+
throw new UnsupportedOperationException("Label color is not supported by AppPageHeader.");
88+
}
89+
90+
/**
91+
* Get the source file of the page icon. If there is no icon returns an empty string.
92+
*
93+
* @return The 'src' attribute header icon, empty string if element is not there.
94+
*/
95+
public String getIconSource()
96+
{
97+
return tryMapElement(elementCache().icon, el -> el.getDomAttribute("src"));
98+
}
99+
100+
@Override
101+
protected ElementCache newElementCache()
102+
{
103+
return new ElementCache();
104+
}
105+
106+
protected class ElementCache extends Component<ElementCache>.ElementCache
107+
{
108+
public final WebElement icon = getIconLocator().findWhenNeeded(this);
109+
public final WebElement title = getTitleLocator().findWhenNeeded(this);
110+
public final WebElement subtitle = getSubtitleLocator().findWhenNeeded(this);
111+
public final WebElement description = getDescriptionLocator().findWhenNeeded(this);
112+
113+
protected Locator.XPathLocator getIconLocator()
114+
{
115+
return Locator.byClass("app-page-header__icon");
116+
}
117+
118+
protected Locator.XPathLocator getTitleLocator()
119+
{
120+
return Locator.byClass("app-page-header__title");
121+
}
122+
123+
protected Locator.XPathLocator getSubtitleLocator()
124+
{
125+
return Locator.byClass("app-page-header__subtitle");
126+
}
127+
128+
protected Locator.XPathLocator getDescriptionLocator()
129+
{
130+
return Locator.byClass("app-page-header__description");
131+
}
132+
}
133+
134+
public static class AppPageHeaderFinder extends WebDriverComponentFinder<AppPageHeader, AppPageHeaderFinder>
135+
{
136+
private final Locator.XPathLocator _baseLocator = Locator.byClass("app-page-header");
137+
138+
public AppPageHeaderFinder(WebDriver driver)
139+
{
140+
super(driver);
141+
}
142+
143+
@Override
144+
protected AppPageHeader construct(WebElement el, WebDriver driver)
145+
{
146+
return new AppPageHeader(el, driver);
147+
}
148+
149+
@Override
150+
protected Locator locator()
151+
{
152+
return _baseLocator;
153+
}
154+
}
155+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright (c) 2018-2026 LabKey Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of 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,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.labkey.test.components.ui;
17+
18+
import org.labkey.test.Locator;
19+
import org.openqa.selenium.WebDriver;
20+
import org.openqa.selenium.WebElement;
21+
22+
import static org.labkey.test.util.selenium.WebElementUtils.tryMapElement;
23+
24+
/**
25+
* Wraps <PageDetailHeader/> component
26+
*/
27+
public class PageDetailHeader extends AppPageHeader
28+
{
29+
protected PageDetailHeader(WebElement element, WebDriver driver)
30+
{
31+
super(element, driver);
32+
}
33+
34+
/**
35+
* Get the rgb style value for the label color in the header
36+
*
37+
* @return A string such as "rgb(104, 204, 202)" as used in the "color-icon__circle-small" "i" element in the detail header or the empty string if element is not there.
38+
*/
39+
@Override
40+
public String getLabelColor()
41+
{
42+
return tryMapElement(elementCache().colorIcon, el -> el.getCssValue("background-color"));
43+
}
44+
45+
@Override
46+
protected ElementCache elementCache()
47+
{
48+
return (ElementCache) super.elementCache();
49+
}
50+
51+
@Override
52+
protected ElementCache newElementCache()
53+
{
54+
return new ElementCache();
55+
}
56+
57+
protected class ElementCache extends AppPageHeader.ElementCache
58+
{
59+
@Override
60+
protected Locator.XPathLocator getTitleLocator()
61+
{
62+
return Locator.byClass("detail__header--name");
63+
}
64+
65+
@Override
66+
protected Locator.XPathLocator getDescriptionLocator()
67+
{
68+
return Locator.byClass("detail__header--desc");
69+
}
70+
71+
@Override
72+
protected Locator.XPathLocator getSubtitleLocator()
73+
{
74+
return Locator.byClass("detail-subtitle");
75+
}
76+
77+
@Override
78+
protected Locator.XPathLocator getIconLocator()
79+
{
80+
return Locator.byClass("detail__header-icon");
81+
}
82+
83+
final WebElement colorIcon = Locator.byClass("color-icon__circle-small").findWhenNeeded(subtitle);
84+
}
85+
86+
public static class PageDetailHeaderFinder extends WebDriverComponentFinder<PageDetailHeader, PageDetailHeaderFinder>
87+
{
88+
private final Locator.XPathLocator _baseLocator = Locator.byClass("page-header");
89+
90+
public PageDetailHeaderFinder(WebDriver driver)
91+
{
92+
super(driver);
93+
}
94+
95+
@Override
96+
protected PageDetailHeader construct(WebElement el, WebDriver driver)
97+
{
98+
return new PageDetailHeader(el, driver);
99+
}
100+
101+
@Override
102+
protected Locator locator()
103+
{
104+
return _baseLocator;
105+
}
106+
}
107+
}

src/org/labkey/test/util/selenium/WebElementUtils.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.List;
2828
import java.util.Objects;
2929
import java.util.Optional;
30+
import java.util.function.Function;
3031

3132
import static org.labkey.test.Locator.NBSP;
3233

@@ -137,4 +138,34 @@ public static boolean checkVisibility(WebElement element)
137138
return false;
138139
}
139140
}
141+
142+
/**
143+
* Convenience method to extract some attribute from a WebElement. Stale or missing elements will return a default.
144+
* @param element WebElement to inspect; may be stale or backed by a missing DOM node
145+
* @param mapper function applied to {@code element} to extract the desired value
146+
* @param defaultValue value returned when {@code element} is stale or missing
147+
* @return the mapped value, or {@code defaultValue} if the element is unavailable
148+
* @param <T> type of the extracted value
149+
*/
150+
public static <T> T tryMapElement(WebElement element, Function<WebElement, T> mapper, T defaultValue)
151+
{
152+
try
153+
{
154+
return mapper.apply(element);
155+
}
156+
catch (NoSuchElementException | StaleElementReferenceException _) { }
157+
158+
return defaultValue;
159+
}
160+
161+
/**
162+
* Convenience overload of {@link #tryMapElement(WebElement, Function, Object)} that defaults to an empty string.
163+
* @param element WebElement to inspect; may be stale or backed by a missing DOM node
164+
* @param mapper function applied to {@code element} to extract a String value
165+
* @return the mapped value, or {@code ""} if the element is unavailable
166+
*/
167+
public static String tryMapElement(WebElement element, Function<WebElement, String> mapper)
168+
{
169+
return tryMapElement(element, mapper, "");
170+
}
140171
}

0 commit comments

Comments
 (0)