Skip to content

Commit 7202ae7

Browse files
authored
Mobile Emulation settings support for chromium-based browsers (#158)
* Mobile Emulation settings support for chromium-based browsers +semver:feature * Change ChromiumSettings constructor visibility to "protected" * Add clientHints into the README example Add ScreenshotListener for test failure analysis Collect failure artifacts in the pipeline * Fix coding issues in ScreenshotListener * Fix coding issues in ScreenshotListener, stabilize geolocation test
1 parent 1b8722e commit 7202ae7

9 files changed

Lines changed: 223 additions & 77 deletions

File tree

README.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,83 @@ browser.devTools().emulation().setGeolocationOverride(latitude, longitude, accur
7575
```
7676
See more DevTools use cases [here](./src/test/java/tests/usecases/devtools)
7777

78+
It is also possible to set mobile emulation capabilities (for chromium-based browsers) in resources/settings.json file, as well as to configure other arguments and options there:
79+
```json
80+
{
81+
"browserName": "chrome",
82+
"isRemote": false,
83+
"remoteConnectionUrl": "http://qa-auto-nexus:4444/wd/hub",
84+
"isElementHighlightEnabled": true,
85+
86+
"driverSettings": {
87+
"chrome": {
88+
"capabilities": {
89+
"selenoid:options":
90+
{
91+
"enableVNC": true
92+
},
93+
"mobileEmulation": {
94+
"userAgent": "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
95+
"deviceMetrics": {
96+
"width": 660,
97+
"height": 1040,
98+
"pixelRatio": 3.0
99+
},
100+
"clientHints": {
101+
"platform": "Android",
102+
"mobile": true
103+
}
104+
},
105+
"unhandledPromptBehavior": "ignore"
106+
},
107+
"options": {
108+
"download.prompt_for_download": "false",
109+
"download.default_directory": "./downloads"
110+
},
111+
"loggingPreferences": {
112+
"Performance": "All"
113+
},
114+
"excludedArguments": [ "enable-automation" ],
115+
"startArguments": [ "--disable-search-engine-choice-screen" ],
116+
"pageLoadStrategy": "normal"
117+
},
118+
"safari": {
119+
"options": {
120+
"safari.options.dataDir": "/Users/username/Downloads"
121+
}
122+
}
123+
},
124+
"timeouts": {
125+
"timeoutImplicit": 0,
126+
"timeoutCondition": 30,
127+
"timeoutScript": 10,
128+
"timeoutPageLoad": 60,
129+
"timeoutPollingInterval": 300,
130+
"timeoutCommand": 60
131+
},
132+
"retry": {
133+
"number": 2,
134+
"pollingInterval": 300
135+
},
136+
"logger": {
137+
"language": "en",
138+
"logPageSource": true
139+
},
140+
"elementCache": {
141+
"isEnabled": false
142+
},
143+
"visualization": {
144+
"imageExtension": "png",
145+
"maxFullFileNameLength": 255,
146+
"defaultThreshold": 0.012,
147+
"comparisonWidth": 16,
148+
"comparisonHeight": 16,
149+
"pathToDumps": "./src/test/resources/visualDumps/"
150+
}
151+
}
152+
```
153+
154+
78155
8. Quit browser at the end
79156
```java
80157
browser.quit();

azure-pipelines.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,21 @@ jobs:
6161
mavenAuthenticateFeed: false
6262
effectivePomSkip: false
6363
sonarQubeRunAnalysis: false
64+
65+
- task: CopyFiles@2
66+
displayName: 'Copy failure screenshots and test logs'
67+
inputs:
68+
SourceFolder: '$(Build.SourcesDirectory)/target'
69+
Contents: |
70+
surefire-reports/failure_screenshots/*.png
71+
log/*.log
72+
TargetFolder: '$(Build.ArtifactStagingDirectory)'
73+
condition: succeededOrFailed()
74+
75+
- task: PublishBuildArtifacts@1
76+
displayName: 'Publish copied artifacts'
77+
inputs:
78+
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
79+
ArtifactName: 'drop'
80+
publishLocation: 'Container'
81+
condition: succeededOrFailed()

src/main/java/aquality/selenium/configuration/driversettings/ChromeSettings.java

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
import org.openqa.selenium.chrome.ChromeOptions;
66
import org.openqa.selenium.remote.AbstractDriverOptions;
77

8-
import java.util.HashMap;
9-
import java.util.Map;
10-
11-
public class ChromeSettings extends DriverSettings {
8+
public class ChromeSettings extends ChromiumSettings {
129

1310
public ChromeSettings(ISettingsFile settingsFile){
1411
super(settingsFile);
@@ -17,39 +14,11 @@ public ChromeSettings(ISettingsFile settingsFile){
1714
@Override
1815
public AbstractDriverOptions<?> getDriverOptions() {
1916
ChromeOptions chromeOptions = new ChromeOptions();
20-
setChromePrefs(chromeOptions);
21-
setCapabilities(chromeOptions);
22-
setChromeArgs(chromeOptions);
23-
setExcludedArguments(chromeOptions);
24-
chromeOptions.setPageLoadStrategy(getPageLoadStrategy());
17+
setupDriverOptions(chromeOptions);
2518
setLoggingPreferences(chromeOptions, ChromeOptions.LOGGING_PREFS);
2619
return chromeOptions;
2720
}
2821

29-
private void setChromePrefs(ChromeOptions options){
30-
HashMap<String, Object> chromePrefs = new HashMap<>();
31-
Map<String, Object> configOptions = getBrowserOptions();
32-
configOptions.forEach((key, value) -> {
33-
if (key.equals(getDownloadDirCapabilityKey())) {
34-
chromePrefs.put(key, getDownloadDir());
35-
} else {
36-
chromePrefs.put(key, value);
37-
}
38-
});
39-
options.setExperimentalOption("prefs", chromePrefs);
40-
}
41-
42-
private void setChromeArgs(ChromeOptions options) {
43-
for (String arg : getBrowserStartArguments()) {
44-
options.addArguments(arg);
45-
}
46-
}
47-
48-
@Override
49-
public String getDownloadDirCapabilityKey() {
50-
return "download.default_directory";
51-
}
52-
5322
@Override
5423
public BrowserName getBrowserName() {
5524
return BrowserName.CHROME;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package aquality.selenium.configuration.driversettings;
2+
3+
import aquality.selenium.core.utilities.ISettingsFile;
4+
import org.openqa.selenium.MutableCapabilities;
5+
import org.openqa.selenium.chromium.ChromiumOptions;
6+
import org.openqa.selenium.logging.LoggingPreferences;
7+
8+
import java.util.HashMap;
9+
import java.util.Map;
10+
11+
public abstract class ChromiumSettings extends DriverSettings {
12+
13+
private static final String MOBILE_EMULATION_CAPABILITY_KEY = "mobileEmulation";
14+
private static final String DEVICE_METRICS_CAPABILITY_KEY = "deviceMetrics";
15+
private static final String CLIENT_HINTS_CAPABILITY_KEY = "clientHints";
16+
17+
protected ChromiumSettings(ISettingsFile settingsFile){
18+
super(settingsFile);
19+
}
20+
21+
protected <T extends ChromiumOptions<T>> void setupDriverOptions(T options) {
22+
setPrefs(options);
23+
setCapabilities(options);
24+
getBrowserStartArguments().forEach(options::addArguments);
25+
setExcludedArguments(options);
26+
options.setPageLoadStrategy(getPageLoadStrategy());
27+
}
28+
29+
void setLoggingPreferences(MutableCapabilities options, String capabilityKey) {
30+
if (!getLoggingPreferences().isEmpty()) {
31+
LoggingPreferences logs = new LoggingPreferences();
32+
getLoggingPreferences().forEach(logs::enable);
33+
options.setCapability(capabilityKey, logs);
34+
}
35+
}
36+
37+
@Override
38+
void setCapabilities(MutableCapabilities options) {
39+
getBrowserCapabilities().forEach((key, value) -> {
40+
if (key.equals(MOBILE_EMULATION_CAPABILITY_KEY)) {
41+
Map<String, Object> mobileOptions = getMapOrEmpty(getDriverSettingsPath(CapabilityType.CAPABILITIES.getKey(), MOBILE_EMULATION_CAPABILITY_KEY));
42+
if (mobileOptions.containsKey(DEVICE_METRICS_CAPABILITY_KEY)) {
43+
Map<String, Object> deviceMetrics = getMapOrEmpty(getDriverSettingsPath(CapabilityType.CAPABILITIES.getKey(), MOBILE_EMULATION_CAPABILITY_KEY, DEVICE_METRICS_CAPABILITY_KEY));
44+
mobileOptions.put(DEVICE_METRICS_CAPABILITY_KEY, deviceMetrics);
45+
}
46+
if (mobileOptions.containsKey(CLIENT_HINTS_CAPABILITY_KEY)) {
47+
Map<String, Object> clientHints = getMapOrEmpty(getDriverSettingsPath(CapabilityType.CAPABILITIES.getKey(), MOBILE_EMULATION_CAPABILITY_KEY, CLIENT_HINTS_CAPABILITY_KEY));
48+
mobileOptions.put(CLIENT_HINTS_CAPABILITY_KEY, clientHints);
49+
}
50+
((ChromiumOptions<?>)options).setExperimentalOption(key, mobileOptions);
51+
} else {
52+
options.setCapability(key, value);
53+
}
54+
});
55+
}
56+
57+
protected <T extends ChromiumOptions<T>> void setPrefs(T options){
58+
HashMap<String, Object> prefs = new HashMap<>();
59+
Map<String, Object> configOptions = getBrowserOptions();
60+
configOptions.forEach((key, value) -> {
61+
if (key.equals(getDownloadDirCapabilityKey())) {
62+
prefs.put(key, getDownloadDir());
63+
} else {
64+
prefs.put(key, value);
65+
}
66+
});
67+
options.setExperimentalOption("prefs", prefs);
68+
}
69+
70+
71+
protected <T extends ChromiumOptions<T>> void setExcludedArguments(T chromiumOptions) {
72+
chromiumOptions.setExperimentalOption("excludeSwitches", getExcludedArguments());
73+
}
74+
75+
@Override
76+
public String getDownloadDirCapabilityKey() {
77+
return "download.default_directory";
78+
}
79+
}

src/main/java/aquality/selenium/configuration/driversettings/DriverSettings.java

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import org.apache.commons.lang3.StringUtils;
88
import org.openqa.selenium.MutableCapabilities;
99
import org.openqa.selenium.PageLoadStrategy;
10-
import org.openqa.selenium.chromium.ChromiumOptions;
11-
import org.openqa.selenium.logging.LoggingPreferences;
1210

1311
import java.io.File;
1412
import java.io.IOException;
@@ -59,9 +57,13 @@ protected Map<String, Level> getLoggingPreferences() {
5957
return loggingPreferences;
6058
}
6159

60+
protected Map<String, Object> getMapOrEmpty(String path) {
61+
return getSettingsFile().isValuePresent(path) ? getSettingsFile().getMap(path) : Collections.emptyMap();
62+
}
63+
6264
private Map<String, Object> getMapOrEmpty(CapabilityType capabilityType) {
6365
String path = getDriverSettingsPath(capabilityType);
64-
Map<String, Object> map = getSettingsFile().isValuePresent(path) ? getSettingsFile().getMap(path) : Collections.emptyMap();
66+
Map<String, Object> map = getMapOrEmpty(path);
6567
logCollection("loc.browser.".concat(capabilityType.getKey()), map);
6668
return map;
6769
}
@@ -138,18 +140,6 @@ void setCapabilities(MutableCapabilities options) {
138140
getBrowserCapabilities().forEach(options::setCapability);
139141
}
140142

141-
<T extends ChromiumOptions<T>> void setExcludedArguments(T chromiumOptions) {
142-
chromiumOptions.setExperimentalOption("excludeSwitches", getExcludedArguments());
143-
}
144-
145-
void setLoggingPreferences(MutableCapabilities options, String capabilityKey) {
146-
if (!getLoggingPreferences().isEmpty()) {
147-
LoggingPreferences logs = new LoggingPreferences();
148-
getLoggingPreferences().forEach(logs::enable);
149-
options.setCapability(capabilityKey, logs);
150-
}
151-
}
152-
153143
@Override
154144
public String getDownloadDir() {
155145
Map<String, Object> browserOptions = getBrowserOptions();
@@ -162,7 +152,7 @@ public String getDownloadDir() {
162152
throw new IllegalArgumentException(String.format("failed to find %s profiles option for %s", key, getBrowserName()));
163153
}
164154

165-
private enum CapabilityType {
155+
protected enum CapabilityType {
166156
CAPABILITIES("capabilities"),
167157
OPTIONS("options"),
168158
START_ARGS("startArguments"),

src/main/java/aquality/selenium/configuration/driversettings/EdgeSettings.java

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
import org.openqa.selenium.edge.EdgeOptions;
66
import org.openqa.selenium.remote.AbstractDriverOptions;
77

8-
import java.util.HashMap;
9-
import java.util.Map;
10-
11-
public class EdgeSettings extends DriverSettings {
8+
public class EdgeSettings extends ChromiumSettings {
129

1310
public EdgeSettings(ISettingsFile settingsFile){
1411
super(settingsFile);
@@ -17,33 +14,11 @@ public EdgeSettings(ISettingsFile settingsFile){
1714
@Override
1815
public AbstractDriverOptions<?> getDriverOptions() {
1916
EdgeOptions edgeOptions = new EdgeOptions();
20-
setCapabilities(edgeOptions);
21-
setPrefs(edgeOptions);
22-
getBrowserStartArguments().forEach(edgeOptions::addArguments);
23-
setExcludedArguments(edgeOptions);
24-
edgeOptions.setPageLoadStrategy(getPageLoadStrategy());
17+
setupDriverOptions(edgeOptions);
2518
setLoggingPreferences(edgeOptions, EdgeOptions.LOGGING_PREFS);
2619
return edgeOptions;
2720
}
2821

29-
@Override
30-
public String getDownloadDirCapabilityKey() {
31-
return "download.default_directory";
32-
}
33-
34-
private void setPrefs(EdgeOptions options){
35-
HashMap<String, Object> prefs = new HashMap<>();
36-
Map<String, Object> configOptions = getBrowserOptions();
37-
configOptions.forEach((key, value) -> {
38-
if (key.equals(getDownloadDirCapabilityKey())) {
39-
prefs.put(key, getDownloadDir());
40-
} else {
41-
prefs.put(key, value);
42-
}
43-
});
44-
options.setExperimentalOption("prefs", prefs);
45-
}
46-
4722
@Override
4823
public BrowserName getBrowserName() {
4924
return BrowserName.EDGE;

src/test/java/forms/MyLocationForm.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ public MyLocationForm() {
1616
}
1717

1818
public double getLatitude() {
19-
if (!lblLatitude.state().isDisplayed() && btnConsent.state().isDisplayed()) {
19+
if (!lblLatitude.state().isDisplayed() && btnConsent.state().waitForDisplayed()) {
2020
clickConsent();
2121
}
22+
lblLatitude.state().waitForDisplayed();
2223
return Double.parseDouble(lblLatitude.getText());
2324
}
2425

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package testreport;
2+
3+
import aquality.selenium.browser.AqualityServices;
4+
import org.openqa.selenium.OutputType;
5+
import org.openqa.selenium.TakesScreenshot;
6+
import org.testng.ITestResult;
7+
import org.testng.Reporter;
8+
import org.testng.TestListenerAdapter;
9+
10+
import java.io.File;
11+
import java.io.IOException;
12+
import java.nio.file.Files;
13+
import java.time.LocalDateTime;
14+
import java.time.format.DateTimeFormatter;
15+
16+
public class ScreenshotListener extends TestListenerAdapter {
17+
@Override
18+
public void onTestFailure(ITestResult result) {
19+
String dateString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd_MM_yyyy_HH_mm_ss"));
20+
String methodName = result.getName();
21+
if (AqualityServices.isBrowserStarted()) {
22+
try {
23+
File scrFile = ((TakesScreenshot) AqualityServices.getBrowser().getDriver()).getScreenshotAs(OutputType.FILE);
24+
String reportDirectory = String.format("%s/target/surefire-reports", new File(System.getProperty("user.dir")).getAbsolutePath());
25+
File destFile = new File(String.format("%s/failure_screenshots/%s_%s.png", reportDirectory, methodName, dateString));
26+
Files.createDirectories(destFile.getParentFile().toPath());
27+
Files.copy(scrFile.toPath(), destFile.toPath());
28+
Reporter.log(String.format("<a href='%s'> <img src='%s' height='100' width='100'/> </a>", destFile.getAbsolutePath(), destFile.getAbsolutePath()));
29+
} catch (IOException e) {
30+
AqualityServices.getLogger().fatal("An IO exception occurred while tried to save a screenshot", e);
31+
}
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)