├── .gitignore
├── README.md
├── binaries
└── .gitignore
├── pom.xml
└── src
├── main
├── java
│ └── cucumber
│ │ └── jvm
│ │ └── parallel
│ │ ├── JSONReportMerger.java
│ │ ├── JSONWriter.java
│ │ ├── JSReportMerger.java
│ │ └── JUnitReportMerger.java
└── resources
│ ├── RepositoryMapForMavenWebDriverBinaryDownloaderPlugin.xml
│ └── RepositoryMapForMavenWebDriverBinaryDownloaderPlugin.xsd
└── test
├── java
└── cucumber
│ └── examples
│ └── java
│ └── testNG
│ ├── DriverManager.java
│ ├── LocalDriverFactory.java
│ ├── LocalWebDriverListener.java
│ ├── RemoteDriverFactory.java
│ ├── RemoteWebDriverListener.java
│ ├── page_objects
│ └── GoogleCom.java
│ ├── runners
│ ├── RunCukesTestInChrome.java
│ ├── RunCukesTestInFirefox.java
│ └── RunSingleFeature.java
│ └── stepDefinitions
│ ├── BeforeAfterHooks.java
│ ├── Feature1And2Stepdefs.java
│ └── bugs
│ └── Bug12345.java
└── resources
├── TestNGRunTestsLocally.xml
├── TestNGRunTestsRemotely.xml
├── features
├── WIP
│ └── WIP.feature
├── bugs
│ └── bug_no_12345.feature
├── feature_no1
│ └── first.feature
└── feature_no2
│ └── second.feature
└── log4j.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # files
2 | *.tmp
3 | *.log
4 | *.log
5 | .DS_Store
6 | *.out
7 | *.swp
8 | *.zip
9 | 0
10 |
11 | #folders
12 | target
13 | test-output
14 |
15 | #intellij idea
16 | .idea
17 | *.iml
18 | *.ipr
19 | *.iws
20 |
21 | # eclipse
22 | .project
23 | .classpath
24 | .settings
25 |
26 | #gedit
27 | *.*~
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This example project is based on few other projects:
2 | * [Cucumber-JVM-Parallel](https://github.com/tristanmccarthy/Cucumber-JVM-Parallel)
3 | * [java-parallel](https://github.com/cucumber/cucumber-jvm/tree/java-parallel-example/examples/java-parallel)
4 | * [java-webbit-websockets-selenium](https://github.com/cucumber/cucumber-jvm/tree/java-parallel-example/examples/java-webbit-websockets-selenium)
5 |
6 | It allows you to run Cucumber features (tests/scenarios) in multiple browsers simultaneously using Selenium (WebDriver) and TestNG.
7 |
8 |
9 | ## Running features in IDE
10 | Tested in IntelliJ Idea 13.1.1
11 | To run all stories from IDE only in Firefox, simply right click on one of the files:
12 | * cucumber.examples.java.testNG.runners.RunCukesTestInChrome
13 | * cucumber.examples.java.testNG.runners.RunCukesTestInFirefox
14 |
15 | And chose "Run ..."
16 | (Yes, choosing RunCukesTestInChrome will also run tests in FF!)
17 |
18 |
19 | To run all stories simultaneously in both browsers (Chrome and Firefox) right click on one of the files:
20 | * src/test/resources/TestNGRunTestsLocally.xml
21 | * src/test/resources/TestNGRunTestsRemotely.xml
22 |
23 | And chose "Run ..."
24 |
25 | To run just one selected feature, change the feature name in the class below:
26 |
27 | cucumber.examples.java.testNG.runners.RunSingleFeature
28 |
29 | And as in previous example, right click on this class and chose "Run ..."
30 |
31 |
32 | ## Running features from CLI
33 | Run tests using local browsers:
34 |
35 | mvn clean install
36 |
37 | Run tests using browsers running on remote nodes:
38 |
39 | mvn clean install -P runTestsRemotely
40 |
41 |
42 | ## Viewing the results
43 | All Cucumber reports [html, json, xml, js] are in: target/cucumber-report
44 |
45 |
46 | ## How to download WebDriver binaries automatically
47 | This project is using Mark Collin's "selenium-standalone-server-plugin" which is a Maven plugin that can download
48 | WebDriver binaries automatically.
49 | Once you configure the plugin to your liking, then:
50 |
51 | mvn clean install -P downloadDriverBinaries
52 |
53 | The pom.xml is currently configured to download only a Chrome driver binary for 64bit Linux OSes.
54 | If you can't download desired driver binary, then check if its URL and checksum specified in:
55 |
56 | src/main/resources/RepositoryMapForMavenWebDriverBinaryDownloaderPlugin.xml
57 |
58 | are correct. If not, then modify this file accordingly.
59 |
60 |
61 | ## Jenkins configuration
62 | I'll add a tutorial later
63 |
64 | ### tools that need to be installed on the Jenkins Host machine
65 | maven 2/3
66 |
67 | ### List of useful plugins
68 | AnsiColor
69 | Cucumber json test reporting.
70 | cucumber-perf
71 | cucumber-reports
72 | GIT client plugin
73 | GIT plugin
74 | Hudson Locks and Latches plugin
75 | Maven Integration plugin
76 | SSH Credentials Plugin
77 | TestNG Results Plugin
78 | Xvfb plugin
--------------------------------------------------------------------------------
/binaries/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore all other files
2 | *
3 |
4 | # do not ignore itself :)
5 | !.gitignore
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | info.cukes
4 | cucumber-testng-parallel-selenium
5 | jar
6 | Example Java TestNG Cucumber parallel test executor
7 | 1.0
8 |
9 |
10 | UTF-8
11 | 2.9
12 | 1.7
13 | 2
14 |
15 | 1.0.0
16 | 1.1.7-SNAPSHOT
17 | 1.1.7-SNAPSHOT
18 | 1.0.3
19 | 1.2.17
20 | 1.3.2
21 | 3.1
22 | 2.16
23 | 1.2.1
24 | 1.3
25 | 2.40.0
26 | 6.8.7
27 |
28 |
29 |
30 |
35 |
36 | runTestsLocally
37 |
38 | true
39 |
40 |
41 | src/test/resources/TestNGRunTestsLocally.xml
42 |
43 |
44 |
45 |
46 | org.apache.maven.plugins
47 | maven-compiler-plugin
48 | ${org.apache.maven.plugins.maven-compiler-plugin.version}
49 |
50 | UTF-8
51 | ${source.and.target.JVM.version}
52 | ${source.and.target.JVM.version}
53 |
54 |
55 |
56 | org.apache.maven.plugins
57 | maven-surefire-plugin
58 | ${org.apache.maven.plugins.maven-surefire-plugin.version}
59 |
60 |
61 | ${testNG.suiteXmlFile}
62 |
63 | false
64 | -Duser.language=en
65 | -Xmx1024m
66 | -XX:MaxPermSize=256m
67 | -Dfile.encoding=UTF-8
68 | false
69 |
73 | true
74 |
75 |
76 |
77 | integration-test
78 |
79 | test
80 |
81 |
82 |
83 |
84 |
85 | org.codehaus.mojo
86 | exec-maven-plugin
87 | ${org.codehaus.mojo.exec-maven-plugin.version}
88 |
89 |
90 | merge-cucumber-js-reports
91 | post-integration-test
92 |
93 | java
94 |
95 |
96 | test
97 | cucumber.jvm.parallel.JSReportMerger
98 |
99 | target/cucumber-report/
100 |
101 |
102 |
103 |
104 | merge-cucumber-json-reports
105 | post-integration-test
106 |
107 | java
108 |
109 |
110 | test
111 | cucumber.jvm.parallel.JSONReportMerger
112 |
113 | target/cucumber-report/
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | runTestsRemotely
124 |
132 |
133 | src/test/resources/TestNGRunTestsRemotely.xml
134 |
135 |
136 |
137 |
138 | org.apache.maven.plugins
139 | maven-compiler-plugin
140 | ${org.apache.maven.plugins.maven-compiler-plugin.version}
141 |
142 | UTF-8
143 | ${source.and.target.JVM.version}
144 | ${source.and.target.JVM.version}
145 |
146 |
147 |
148 | org.apache.maven.plugins
149 | maven-surefire-plugin
150 | ${org.apache.maven.plugins.maven-surefire-plugin.version}
151 |
152 |
153 | ${testNG.suiteXmlFile}
154 |
155 | false
156 | -Duser.language=en
157 | -Xmx1024m
158 | -XX:MaxPermSize=256m
159 | -Dfile.encoding=UTF-8
160 | false
161 |
165 | true
166 |
167 |
168 |
169 | integration-test
170 |
171 | test
172 |
173 |
174 |
175 |
176 |
177 | org.codehaus.mojo
178 | exec-maven-plugin
179 | ${org.codehaus.mojo.exec-maven-plugin.version}
180 |
195 |
196 | merge-cucumber-json-reports
197 | post-integration-test
198 |
199 | java
200 |
201 |
202 | test
203 | cucumber.jvm.parallel.JSONReportMerger
204 |
205 | target/cucumber-report/
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
219 | downloadDriverBinaries
220 |
221 |
222 |
223 | org.apache.maven.plugins
224 | maven-compiler-plugin
225 | ${org.apache.maven.plugins.maven-compiler-plugin.version}
226 |
227 | ${source.and.target.JVM.version}
228 | ${source.and.target.JVM.version}
229 |
230 |
231 |
232 | com.lazerycode.selenium
233 | driver-binary-downloader-maven-plugin
234 | ${com.lazerycode.selenium.version}
235 |
236 |
237 | binaries
238 |
239 | binaries/zips
240 | src/main/resources/RepositoryMapForMavenWebDriverBinaryDownloaderPlugin.xml
241 |
242 | false
243 | true
244 | false
245 |
246 |
247 | false
248 |
249 | true
250 |
251 | true
252 | true
253 |
254 |
255 | ${googlchromedriverbinary.version}
256 |
257 |
258 |
259 | 2
260 |
261 | 20000
262 |
263 | 10000
264 |
265 | false
266 |
267 |
268 |
269 |
270 |
271 |
272 | selenium
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 | sonatype-snapshots
285 | https://oss.sonatype.org/content/repositories/snapshots
286 |
287 | true
288 |
289 |
290 |
291 |
292 |
293 |
294 | info.cukes
295 | cucumber-jvm-deps
296 | ${info.cukes.cucumber-jvm-deps.version}
297 |
298 |
299 | com.thoughtworks.xstream
300 | xstream
301 |
302 |
303 | com.googlecode.java-diff-utils
304 | diffutils
305 |
306 |
307 |
308 |
309 | info.cukes
310 | cucumber-java
311 | ${info.cukes.cucumber-java.version}
312 | test
313 |
314 |
315 | org.testng
316 | testng
317 | ${org.testng.testng.version}
318 |
319 |
320 | info.cukes
321 | cucumber-testng
322 | ${info.cukes.cucumber-testng.version}
323 |
324 |
325 | junit
326 | junit
327 |
328 |
329 |
330 |
331 | info.cukes
332 | cucumber-junit
333 | ${info.cukes.cucumber-java.version}
334 | test
335 |
336 |
337 | org.seleniumhq.selenium
338 | selenium-server
339 | ${org.seleniumhq.selenium.selenium-server.version}
340 | test
341 |
342 |
343 | log4j
344 | log4j
345 | ${log4j.log4j.version}
346 |
347 |
348 | org.apache.commons
349 | commons-io
350 | ${org.apache.commons.commons-io.version}
351 |
352 |
353 |
360 | org.hamcrest
361 | hamcrest-library
362 | ${org.hamcrest.hamcrest-library.version}
363 |
364 |
365 |
372 | com.lazerycode.selenium
373 | driver-binary-downloader-maven-plugin
374 | ${com.lazerycode.selenium.version}
375 |
376 |
377 |
380 | com.googlecode.json-simple
381 | json-simple
382 | 1.1
383 |
384 |
385 |
--------------------------------------------------------------------------------
/src/main/java/cucumber/jvm/parallel/JSONReportMerger.java:
--------------------------------------------------------------------------------
1 | package cucumber.jvm.parallel;
2 |
3 | import org.apache.log4j.Logger;
4 | import org.json.simple.JSONArray;
5 | import org.json.simple.parser.JSONParser;
6 | import org.json.simple.parser.ParseException;
7 | import org.json.simple.JSONObject;
8 |
9 | import org.apache.commons.io.FileUtils;
10 |
11 |
12 | import java.io.*;
13 | import java.nio.file.Files;
14 | import java.nio.file.LinkOption;
15 | import java.nio.file.Path;
16 | import java.nio.file.Paths;
17 | import java.util.Collection;
18 | import java.util.UUID;
19 |
20 | /**
21 | * A Cucumber JSON report merger, based on Tristan McCarthy's Cucumber JS Report Merger
22 | * @author Janusz Kowalczyk
23 | * @see - http://www.opencredo.com/2013/07/02/running-cucumber-jvm-tests-in-parallel/
24 | * @see - https://github.com/tristanmccarthy/Cucumber-JVM-Parallel
25 | */
26 | public class JSONReportMerger {
27 | private static String reportFileName = "cucumber.json";
28 | private static String reportImageExtension = "png";
29 | static Logger log;
30 |
31 | static {
32 | log = Logger.getLogger(JSONReportMerger.class);
33 | }
34 |
35 | public static void main(String[] args) throws Throwable {
36 | File reportDirectory = new File(args[0]);
37 | if (reportDirectory.exists()) {
38 | JSONReportMerger munger = new JSONReportMerger();
39 | munger.mergeReports(reportDirectory);
40 | }
41 | }
42 |
43 |
44 | /**
45 | * Merge all reports together into master report in given reportDirectory
46 | * @param reportDirectory
47 | * @throws Exception
48 | */
49 | public void mergeReports(File reportDirectory) throws Throwable {
50 |
51 | // check if other reporter already copied json report to target directory
52 | // if so, then delete it, so that we can merge all the sub reports properly
53 | Path targetReportPath = Paths.get(reportDirectory.toString() + File.separator + reportFileName);
54 | if ( Files.exists(targetReportPath, LinkOption.NOFOLLOW_LINKS) ) {
55 | FileUtils.forceDelete(targetReportPath.toFile());
56 | }
57 |
58 | File mergedReport = null;
59 | Collection existingReports = FileUtils.listFiles(reportDirectory, new String[]{"json"}, true);
60 |
61 | for (File report : existingReports) {
62 | //only address report files
63 | if (report.getName().equals(reportFileName)) {
64 | //rename all the image files (to give unique names) in report directory and update report
65 | renameEmbededImages(report);
66 | // prepend parent report directory name to all feature IDs and Names
67 | // this helps analyse report later on
68 | renameFeatureIDsAndNames(report);
69 | /*
70 | //if we are on the first pass, copy the directory of the file to use as basis for merge
71 | if (mergedReport == null) {
72 | // copy just the cucumber.json
73 | FileUtils.copyFileToDirectory(report, reportDirectory);
74 | // access this first copied report
75 | mergedReport = new File(reportDirectory, reportFileName);
76 | } else {
77 | //otherwise merge this report into existing master report
78 | mergeFiles(mergedReport, report);
79 | }*/
80 | }
81 | }
82 | }
83 |
84 | /**
85 | * merge source file into target
86 | *
87 | * @param target
88 | * @param source
89 | */
90 | public void mergeFiles(File target, File source) throws Throwable {
91 |
92 | String targetReport = FileUtils.readFileToString(target);
93 | String sourceReport = FileUtils.readFileToString(source);
94 |
95 | JSONParser jp = new JSONParser();
96 |
97 | try {
98 | JSONArray parsedTargetJSON = (JSONArray)jp.parse(targetReport);
99 | JSONArray parsedSourceJSON = (JSONArray)jp.parse(sourceReport);
100 | // Merge two JSON reports
101 | parsedTargetJSON.addAll(parsedSourceJSON);
102 | // this is a new writer that adds JSON indentation.
103 | Writer writer = new JSONWriter();
104 | // convert our parsedJSON to a pretty form
105 | parsedTargetJSON.writeJSONString(writer);
106 | // and save the pretty version to disk
107 | FileUtils.writeStringToFile(target, writer.toString());
108 | } catch (ParseException pe) {
109 | pe.printStackTrace();
110 | } catch (Exception e) {
111 | e.printStackTrace();
112 | }
113 | }
114 |
115 | /**
116 | * Prepend parent directory name to all feature names for easier report analysis
117 | *
118 | * @param reportFile
119 | */
120 | public void renameFeatureIDsAndNames(File reportFile) throws Throwable {
121 | String reportDirName = reportFile.getParentFile().getName();
122 | String fileAsString = FileUtils.readFileToString(reportFile);
123 | JSONParser jp = new JSONParser();
124 |
125 | try {
126 | JSONArray parsedJSON = (JSONArray)jp.parse(fileAsString);
127 |
128 | for (Object o : parsedJSON) {
129 | JSONObject jo = (JSONObject) o;
130 | String curFeatureID = jo.get("id").toString();
131 | String curFeatureName = jo.get("name").toString();
132 |
133 | String newFeatureID = String.format("%s - %s", reportDirName, curFeatureID);
134 | String newFeatureName = String.format("%s - %s", reportDirName, curFeatureName);
135 |
136 | log.info("Changing feature ID and Name from: " + curFeatureID + " / " + curFeatureName + " to: " + newFeatureID + " / " + newFeatureName);
137 |
138 | jo.put("id", newFeatureID);
139 | jo.put("name", newFeatureName);
140 | }
141 | // this is a new writer that adds JSON indentation.
142 | Writer writer = new JSONWriter();
143 | // convert our parsedJSON to a pretty form
144 | parsedJSON.writeJSONString(writer);
145 | // and save the pretty version to disk
146 | FileUtils.writeStringToFile(reportFile, writer.toString());
147 | } catch (ParseException pe) {
148 | pe.printStackTrace();
149 | } catch (Exception e) {
150 | e.printStackTrace();
151 | }
152 | }
153 |
154 | /**
155 | * Give unique names to embedded images to ensure they aren't lost during merge
156 | * Update report file to reflect new image names
157 | *
158 | * @param reportFile
159 | */
160 | public void renameEmbededImages(File reportFile) throws Throwable {
161 | File reportDirectory = reportFile.getParentFile();
162 | Collection embeddedImages = FileUtils.listFiles(reportDirectory, new String[]{reportImageExtension}, true);
163 |
164 | String fileAsString = FileUtils.readFileToString(reportFile);
165 |
166 | for (File image : embeddedImages) {
167 | String curImageName = image.getName();
168 | String uniqueImageName = reportDirectory.getName() + "-" + UUID.randomUUID().toString() + "." + reportImageExtension;
169 |
170 | image.renameTo(new File(reportDirectory, uniqueImageName));
171 | fileAsString = fileAsString.replace(curImageName, uniqueImageName);
172 | }
173 |
174 | FileUtils.writeStringToFile(reportFile, fileAsString);
175 | }
176 | }
--------------------------------------------------------------------------------
/src/main/java/cucumber/jvm/parallel/JSONWriter.java:
--------------------------------------------------------------------------------
1 | package cucumber.jvm.parallel;
2 |
3 | import java.io.StringWriter;
4 |
5 | /**
6 | *
7 | * http://code.google.com/p/json-simple/issues/detail?id=22
8 | * http://code.google.com/p/json-simple/issues/attachmentText?id=22&aid=220009000&name=JSonWriter.java&token=JFPBdQPSUs1cIM6Bl0KdKxP5BUs%3A1397646495589
9 | * @author Elad Tabak
10 | * @since 28-Nov-2011
11 | * @version 0.1
12 | *
13 | */
14 | public class JSONWriter extends StringWriter {
15 |
16 | private int indent = 0;
17 |
18 | @Override
19 | public void write(int c) {
20 | if (((char)c) == '[' || ((char)c) == '{') {
21 | super.write(c);
22 | super.write('\n');
23 | indent++;
24 | writeIndentation();
25 | } else if (((char)c) == ',') {
26 | super.write(c);
27 | super.write('\n');
28 | writeIndentation();
29 | } else if (((char)c) == ']' || ((char)c) == '}') {
30 | super.write('\n');
31 | indent--;
32 | writeIndentation();
33 | super.write(c);
34 | } else {
35 | super.write(c);
36 | }
37 |
38 | }
39 |
40 | private void writeIndentation() {
41 | for (int i = 0; i < indent; i++) {
42 | super.write(" ");
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/cucumber/jvm/parallel/JSReportMerger.java:
--------------------------------------------------------------------------------
1 | package cucumber.jvm.parallel;
2 |
3 | import org.apache.commons.io.FileUtils;
4 | import java.io.*;
5 | import java.util.Collection;
6 | import java.util.UUID;
7 |
8 | /**
9 | *
10 | * @author Tristan McCarthy
11 | * @see - http://www.opencredo.com/2013/07/02/running-cucumber-jvm-tests-in-parallel/
12 | * @see - https://github.com/tristanmccarthy/Cucumber-JVM-Parallel
13 | */
14 | public class JSReportMerger {
15 | private static String reportFileName = "report.js";
16 | private static String reportImageExtension = "png";
17 |
18 | public static void main(String[] args) throws Throwable {
19 | File reportDirectory = new File(args[0]);
20 | if (reportDirectory.exists()) {
21 | JSReportMerger munger = new JSReportMerger();
22 | munger.mergeReports(reportDirectory);
23 | }
24 | }
25 |
26 | /**
27 | * Merge all reports together into master report in given reportDirectory
28 | * @param reportDirectory
29 | * @throws Exception
30 | */
31 | public void mergeReports(File reportDirectory) throws Throwable {
32 | Collection existingReports = FileUtils.listFiles(reportDirectory, new String[]{"js"}, true);
33 |
34 | File mergedReport = null;
35 |
36 | for (File report : existingReports) {
37 | //only address report files
38 | if (report.getName().equals(reportFileName)) {
39 | //rename all the image files (to give unique names) in report directory and update report
40 | renameEmbededImages(report);
41 |
42 | //if we are on the first pass, copy the directory of the file to use as basis for merge
43 | if (mergedReport == null) {
44 | FileUtils.copyDirectory(report.getParentFile(), reportDirectory);
45 | mergedReport = new File(reportDirectory, reportFileName);
46 | //otherwise merge this report into existing master report
47 | } else {
48 | mergeFiles(mergedReport, report);
49 | }
50 | }
51 | }
52 | }
53 |
54 | /**
55 | * merge source file into target
56 | *
57 | * @param target
58 | * @param source
59 | */
60 | public void mergeFiles(File target, File source) throws Throwable {
61 | //copy embeded images
62 | Collection embeddedImages = FileUtils.listFiles(source.getParentFile(), new String[]{reportImageExtension}, true);
63 | for (File image : embeddedImages) {
64 | FileUtils.copyFileToDirectory(image, target.getParentFile());
65 | }
66 |
67 | //merge report files
68 | String targetReport = FileUtils.readFileToString(target);
69 | String sourceReport = FileUtils.readFileToString(source);
70 |
71 | FileUtils.writeStringToFile(target, targetReport + sourceReport);
72 | }
73 |
74 | /**
75 | * Give unique names to embedded images to ensure they aren't lost during merge
76 | * Update report file to reflect new image names
77 | *
78 | * @param reportFile
79 | */
80 | public void renameEmbededImages(File reportFile) throws Throwable {
81 | File reportDirectory = reportFile.getParentFile();
82 | Collection embeddedImages = FileUtils.listFiles(reportDirectory, new String[]{reportImageExtension}, true);
83 |
84 | String fileAsString = FileUtils.readFileToString(reportFile);
85 |
86 | for (File image : embeddedImages) {
87 | String curImageName = image.getName();
88 | String uniqueImageName = UUID.randomUUID().toString() + "." + reportImageExtension;
89 |
90 | image.renameTo(new File(reportDirectory, uniqueImageName));
91 | fileAsString = fileAsString.replace(curImageName, uniqueImageName);
92 | }
93 |
94 | FileUtils.writeStringToFile(reportFile, fileAsString);
95 | }
96 | }
--------------------------------------------------------------------------------
/src/main/java/cucumber/jvm/parallel/JUnitReportMerger.java:
--------------------------------------------------------------------------------
1 | package cucumber.jvm.parallel;
2 |
3 | import org.apache.commons.io.FileUtils;
4 |
5 | import java.io.File;
6 | import java.util.Collection;
7 | import java.util.UUID;
8 |
9 | /**
10 | *
11 | * @author Tristan McCarthy
12 | * @see - http://www.opencredo.com/2013/07/02/running-cucumber-jvm-tests-in-parallel/
13 | * @see - https://github.com/tristanmccarthy/Cucumber-JVM-Parallel
14 | */
15 | public class JUnitReportMerger {
16 | private static String reportFileName = "report.js";
17 | private static String reportImageExtension = "png";
18 |
19 | public static void main(String[] args) throws Throwable {
20 | File reportDirectory = new File(args[0]);
21 | if (reportDirectory.exists()) {
22 | JUnitReportMerger munger = new JUnitReportMerger();
23 | munger.mergeReports(reportDirectory);
24 | }
25 | }
26 |
27 | /**
28 | * Merge all reports together into master report in given reportDirectory
29 | * @param reportDirectory
30 | * @throws Exception
31 | */
32 | public void mergeReports(File reportDirectory) throws Throwable {
33 | Collection existingReports = FileUtils.listFiles(reportDirectory, new String[]{"js"}, true);
34 |
35 | File mergedReport = null;
36 |
37 | for (File report : existingReports) {
38 | //only address report files
39 | if (report.getName().equals(reportFileName)) {
40 | //rename all the image files (to give unique names) in report directory and update report
41 | renameEmbededImages(report);
42 |
43 | //if we are on the first pass, copy the directory of the file to use as basis for merge
44 | if (mergedReport == null) {
45 | FileUtils.copyDirectory(report.getParentFile(), reportDirectory);
46 | mergedReport = new File(reportDirectory, reportFileName);
47 | //otherwise merge this report into existing master report
48 | } else {
49 | mergeFiles(mergedReport, report);
50 | }
51 | }
52 | }
53 | }
54 |
55 | /**
56 | * merge source file into target
57 | *
58 | * @param target
59 | * @param source
60 | */
61 | public void mergeFiles(File target, File source) throws Throwable {
62 | //copy embeded images
63 | Collection embeddedImages = FileUtils.listFiles(source.getParentFile(), new String[]{reportImageExtension}, true);
64 | for (File image : embeddedImages) {
65 | FileUtils.copyFileToDirectory(image, target.getParentFile());
66 | }
67 |
68 | //merge report files
69 | String targetReport = FileUtils.readFileToString(target);
70 | String sourceReport = FileUtils.readFileToString(source);
71 |
72 | FileUtils.writeStringToFile(target, targetReport + sourceReport);
73 | }
74 |
75 | /**
76 | * Give unique names to embedded images to ensure they aren't lost during merge
77 | * Update report file to reflect new image names
78 | *
79 | * @param reportFile
80 | */
81 | public void renameEmbededImages(File reportFile) throws Throwable {
82 | File reportDirectory = reportFile.getParentFile();
83 | Collection embeddedImages = FileUtils.listFiles(reportDirectory, new String[]{reportImageExtension}, true);
84 |
85 | String fileAsString = FileUtils.readFileToString(reportFile);
86 |
87 | for (File image : embeddedImages) {
88 | String curImageName = image.getName();
89 | String uniqueImageName = UUID.randomUUID().toString() + "." + reportImageExtension;
90 |
91 | image.renameTo(new File(reportDirectory, uniqueImageName));
92 | fileAsString = fileAsString.replace(curImageName, uniqueImageName);
93 | }
94 |
95 | FileUtils.writeStringToFile(reportFile, fileAsString);
96 | }
97 | }
--------------------------------------------------------------------------------
/src/main/resources/RepositoryMapForMavenWebDriverBinaryDownloaderPlugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | https://selenium.googlecode.com/files/IEDriverServer_Win32_2.37.0.zip
8 | d23aa898f50946f6b1ae5fa933116cff1b83f150
9 | sha1
10 |
11 |
12 | https://selenium.googlecode.com/files/IEDriverServer_x64_2.37.0.zip
13 | 38acef909ef660953aa189558cf5d7bff2f6d801
14 | sha1
15 |
16 |
17 |
18 |
19 | http://selenium-release.storage.googleapis.com/2.39/IEDriverServer_Win32_2.39.0.zip
20 | bd4bc2b77a04999148e7fab974336e99
21 | md5
22 |
23 |
24 | http://selenium-release.storage.googleapis.com/2.39/IEDriverServer_x64_2.39.0.zip
25 | 7d19f3d7ffb9cb40fc26cc38885b9160
26 | md5
27 |
28 |
29 |
30 |
31 | http://selenium-release.storage.googleapis.com/2.40/IEDriverServer_Win32_2.40.0.zip
32 | 90c495b4bcd5e86c0a2e5c5c4ac16369
33 | md5
34 |
35 |
36 | http://selenium-release.storage.googleapis.com/2.40/IEDriverServer_x64_2.40.0.zip
37 | 7769f840dc566657efe0a1268ec24904
38 | md5
39 |
40 |
41 |
42 |
43 |
44 |
45 | http://chromedriver.storage.googleapis.com/2.6/chromedriver_win32.zip
46 | 48f8d2462e44b8d06f88ff56857620c0
47 | md5
48 |
49 |
50 |
51 |
52 | http://chromedriver.storage.googleapis.com/2.7/chromedriver_win32.zip
53 | f9b86861545951bd24da0235f9793f67
54 | md5
55 |
56 |
57 |
58 |
59 | http://chromedriver.storage.googleapis.com/2.8/chromedriver_win32.zip
60 | 209236a3fc361f06c0bf8554010fc231
61 | md5
62 |
63 |
64 |
65 |
66 | http://chromedriver.storage.googleapis.com/2.9/chromedriver_win32.zip
67 | ef6a4819563ef993c3aac8f229c0ca91
68 | md5
69 |
70 |
71 |
72 |
73 |
74 |
75 | https://phantomjs.googlecode.com/files/phantomjs-1.9.2-windows.zip
76 | 5fcfb32d9df9e603a3980139026bc33d516dae01
77 | sha1
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | http://chromedriver.storage.googleapis.com/2.0/chromedriver_linux32.zip
87 | c0d96102715c4916b872f91f5bf9b12c
88 | md5
89 |
90 |
91 | http://chromedriver.storage.googleapis.com/2.0/chromedriver_linux64.zip
92 | 858ebaf47e13dce7600191ed59974c09
93 | md5
94 |
95 |
96 |
97 |
98 | http://chromedriver.storage.googleapis.com/2.1/chromedriver_linux32.zip
99 | 1d7e908253f7240d1596332082cc5742
100 | md5
101 |
102 |
103 | http://chromedriver.storage.googleapis.com/2.1/chromedriver_linux64.zip
104 | de406b5a1aac2bfb2f419ac01d7231e2
105 | md5
106 |
107 |
108 |
109 |
110 | http://chromedriver.storage.googleapis.com/2.2/chromedriver_linux32.zip
111 | 801b9f6c28a32575d8eae2abb1cdecd5
112 | md5
113 |
114 |
115 | http://chromedriver.storage.googleapis.com/2.2/chromedriver_linux64.zip
116 | d5b73ee424717e45601553e91e204ad6
117 | md5
118 |
119 |
120 |
121 |
122 | http://chromedriver.storage.googleapis.com/2.3/chromedriver_linux32.zip
123 | f3af4d92060e6d61c2d2ed86ad584246
124 | md5
125 |
126 |
127 | http://chromedriver.storage.googleapis.com/2.3/chromedriver_linux64.zip
128 | 1a816cc185a15af4d450805629790b0a
129 | md5
130 |
131 |
132 |
133 |
134 | http://chromedriver.storage.googleapis.com/2.6/chromedriver_linux32.zip
135 | 3f8ef2f01a7fb5805bed2d644569acba
136 | md5
137 |
138 |
139 | http://chromedriver.storage.googleapis.com/2.6/chromedriver_linux64.zip
140 | 5e70555cbbf75e3480dd1629a35bc7e3
141 | md5
142 |
143 |
144 |
145 |
146 | http://chromedriver.storage.googleapis.com/2.5/chromedriver_linux32.zip
147 | aa61a13c88349be12750d339c7798dc7
148 | md5
149 |
150 |
151 | http://chromedriver.storage.googleapis.com/2.5/chromedriver_linux64.zip
152 | 2a45ca3b32bd1685bb796eeb564e9fc8
153 | md5
154 |
155 |
156 |
157 |
158 | http://chromedriver.storage.googleapis.com/2.6/chromedriver_linux32.zip
159 | 007bbff965cb4be6fe7734236b5afd52
160 | md5
161 |
162 |
163 | http://chromedriver.storage.googleapis.com/2.6/chromedriver_linux64.zip
164 | 0dbc5606c8481f8ae9775d1a27c3a8a5
165 | md5
166 |
167 |
168 |
169 |
170 | http://chromedriver.storage.googleapis.com/2.7/chromedriver_linux32.zip
171 | f054ab7718126c55daa73dd8b13d10bf
172 | md5
173 |
174 |
175 | http://chromedriver.storage.googleapis.com/2.7/chromedriver_linux64.zip
176 | 07e80e731ee84bc16834c36142d03b8e
177 | md5
178 |
179 |
180 |
181 |
182 | http://chromedriver.storage.googleapis.com/2.8/chromedriver_linux32.zip
183 | 8db373d7cd45a4a188519041ccf271db
184 | md5
185 |
186 |
187 | http://chromedriver.storage.googleapis.com/2.8/chromedriver_linux64.zip
188 | 72a3eacd74b6e3d67d4871a747ecbeb2
189 | md5
190 |
191 |
192 |
193 |
194 | http://chromedriver.storage.googleapis.com/2.9/chromedriver_linux32.zip
195 | 780c12bf34d4f4029b7586b927b1fa69
196 | md5
197 |
198 |
199 | http://chromedriver.storage.googleapis.com/2.9/chromedriver_linux64.zip
200 | e2e44f064ecb69ec5b6f1b80f3d13c93
201 | md5
202 |
203 |
204 |
205 |
206 |
207 |
208 | https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-x86_64.tar.bz2
209 | c78c4037d98fa893e66fc516214499c58228d2f9
210 | sha1
211 |
212 |
213 | https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-i686.tar.bz2
214 | 9ead5dd275f79eaced61ce63dbeca58be4d7f090
215 | sha1
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | http://chromedriver.storage.googleapis.com/2.6/chromedriver_mac32.zip
225 | 4643652d403961dd9a9a1980eb1a06bf8b6e9bad
226 | sha1
227 |
228 |
229 |
230 |
231 |
232 |
233 | https://phantomjs.googlecode.com/files/phantomjs-1.9.2-macosx.zip
234 | 36357dc95c0676fb4972420ad25455f49a8f3331
235 | sha1
236 |
237 |
238 |
239 |
240 |
--------------------------------------------------------------------------------
/src/main/resources/RepositoryMapForMavenWebDriverBinaryDownloaderPlugin.xsd:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/DriverManager.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG;
2 |
3 | import org.apache.log4j.Logger;
4 | import org.openqa.selenium.*;
5 | import org.openqa.selenium.remote.RemoteWebDriver;
6 |
7 | import java.util.concurrent.TimeUnit;
8 |
9 | /**
10 | * A generic WebDriver manager, it works with local and remote instances of WebDriver.
11 | *
12 | * @author: Confusions Personified
13 | * @src: http://rationaleemotions.wordpress.com/2013/07/31/parallel-webdriver-executions-using-testng/
14 | */
15 | public class DriverManager {
16 |
17 | /*
18 | This simple line does all the mutlithread magic.
19 | For more details please refer to the src link above :)
20 | */
21 | private static ThreadLocal driver = new ThreadLocal();
22 | static Logger log;
23 |
24 | static {
25 | log = Logger.getLogger(DriverManager.class);
26 | }
27 |
28 | public static WebDriver getDriver() {
29 | if (driver.get() == null) {
30 | // this is need when running tests from IDE
31 | log.info("Thread has no WedDriver, creating new one");
32 | setWebDriver(LocalDriverFactory.createInstance(null));
33 | }
34 | log.debug("Getting instance of remote driver" + driver.get().getClass());
35 | return driver.get();
36 | }
37 |
38 | public static void setWebDriver(WebDriver driver) {
39 | driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
40 | DriverManager.driver.set(driver);
41 | }
42 |
43 | /**
44 | * Returns a string containing current browser name, its version and OS name.
45 | * This method is used in the the *WebDriverListeners to change the test name.
46 | * */
47 | public static String getBrowserInfo(){
48 | log.debug("Getting browser info");
49 | // we have to cast WebDriver object to RemoteWebDriver here, because the first one does not have a method
50 | // that would tell you which browser it is driving. (sick!)
51 | Capabilities cap = ((RemoteWebDriver) DriverManager.getDriver()).getCapabilities();
52 | String b = cap.getBrowserName();
53 | String os = cap.getPlatform().toString();
54 | String v = cap.getVersion();
55 | return String.format("%s v:%s %s", b, v, os);
56 | }
57 | }
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/LocalDriverFactory.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG;
2 |
3 | import org.openqa.selenium.WebDriver;
4 | import org.openqa.selenium.chrome.ChromeDriver;
5 | import org.openqa.selenium.firefox.FirefoxDriver;
6 | import org.openqa.selenium.htmlunit.HtmlUnitDriver;
7 | import org.openqa.selenium.ie.InternetExplorerDriver;
8 |
9 | /**
10 | * Based on the LocalDriverFactory found at: onrationaleemotions.wordpress.com
11 | * @author: Confusions Personified
12 | * @src: http://rationaleemotions.wordpress.com/2013/07/31/parallel-webdriver-executions-using-testng/
13 | */
14 | public class LocalDriverFactory {
15 | public static WebDriver createInstance(String browserName) {
16 | WebDriver driver;
17 | browserName = (browserName != null) ? browserName : "firefox";
18 |
19 | switch (Browser.valueOf(browserName.toUpperCase())) {
20 | case FIREFOX:
21 | driver = new FirefoxDriver();
22 | break;
23 | case IE:
24 | driver = new InternetExplorerDriver();
25 | break;
26 | case CHROME:
27 | System.setProperty("webdriver.chrome.driver", "binaries/linux/googlechrome/64bit/2.9/chromedriver");
28 | driver = new ChromeDriver();
29 | break;
30 | case HTMLUNIT:
31 | driver = new HtmlUnitDriver();
32 | break;
33 | case HTMLUNITJS:
34 | driver = new HtmlUnitDriver(true);
35 | break;
36 | default:
37 | driver = new FirefoxDriver();
38 | break;
39 | }
40 | // maximize browser's window on start
41 | driver.manage().window().maximize();
42 | return driver;
43 | }
44 |
45 | private static enum Browser {
46 | FIREFOX,
47 | CHROME,
48 | IE,
49 | HTMLUNIT,
50 | HTMLUNITJS;
51 | }
52 | }
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/LocalWebDriverListener.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG;
2 |
3 | import org.apache.log4j.Logger;
4 | import org.openqa.selenium.WebDriver;
5 | import org.testng.IInvokedMethod;
6 | import org.testng.IInvokedMethodListener;
7 | import org.testng.ITestResult;
8 | import org.testng.internal.BaseTestMethod;
9 |
10 | import java.lang.reflect.Field;
11 |
12 |
13 | /**
14 | * Based on the LocalDriverFactory found at: onrationaleemotions.wordpress.com
15 | * @author: Confusions Personified
16 | * @src: http://rationaleemotions.wordpress.com/2013/07/31/parallel-webdriver-executions-using-testng/
17 | */
18 | public class LocalWebDriverListener implements IInvokedMethodListener {
19 |
20 | static Logger log = Logger.getLogger(LocalWebDriverListener.class);
21 |
22 | @Override
23 | public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
24 | log.debug("BEGINNING: org.stng.jbehave.LocalWebDriverListener.beforeInvocation");
25 | if (method.isTestMethod()) {
26 | // get browser name specified in the TestNG XML test suite file
27 | String browserName = method.getTestMethod().getXmlTest().getLocalParameters().get("browserName");
28 | // get and set new instance of local WebDriver
29 | log.info("getting driver for: " + browserName);
30 | WebDriver driver = LocalDriverFactory.createInstance(browserName);
31 | DriverManager.setWebDriver(driver);
32 | log.info("Done! Created "+ browserName + " driver!" );
33 | } else {
34 | log.warn("Provided method is NOT a TestNG testMethod!!!");
35 | }
36 | log.debug("END: org.stng.jbehave.LocalWebDriverListener.beforeInvocation");
37 | }
38 |
39 | @Override
40 | public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
41 | log.debug("BEGINNING: org.stng.jbehave.LocalWebDriverListener.afterInvocation");
42 | if (method.isTestMethod()) {
43 | try {
44 | String browser = DriverManager.getBrowserInfo();
45 | // change the name of the test method that will appear in the report to one that will contain
46 | // also browser name, version and OS.
47 | // very handy when analysing results.
48 | BaseTestMethod bm = (BaseTestMethod)testResult.getMethod();
49 | Field f = bm.getClass().getSuperclass().getDeclaredField("m_methodName");
50 | f.setAccessible(true);
51 | String newTestName = testResult.getTestContext().getCurrentXmlTest().getName() + " - " + bm.getMethodName() + " - " + browser;
52 | log.info("Renaming test method name from: '" + bm.getMethodName() + "' to: '" + newTestName + "'");
53 | f.set(bm, newTestName);
54 | } catch (Exception ex) {
55 | System.out.println("afterInvocation exception:\n" + ex.getMessage());
56 | ex.printStackTrace();
57 | } finally {
58 | // close the browser
59 | WebDriver driver = DriverManager.getDriver();
60 | if (driver != null) {
61 | driver.quit();
62 | }
63 | }
64 | }
65 | log.debug("END: org.stng.jbehave.LocalWebDriverListener.afterInvocation");
66 | }
67 | }
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/RemoteDriverFactory.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG;
2 |
3 | import org.apache.log4j.Logger;
4 | import org.openqa.selenium.remote.DesiredCapabilities;
5 | import org.openqa.selenium.remote.RemoteWebDriver;
6 |
7 | import java.net.MalformedURLException;
8 | import java.net.URL;
9 |
10 | /**
11 | * A factory of remote WebDrivers. Based on an example taken from:
12 | * @src: http://rationaleemotions.wordpress.com/2013/07/31/parallel-webdriver-executions-using-testng/
13 | */
14 | public class RemoteDriverFactory {
15 |
16 | static Logger log = Logger.getLogger(RemoteDriverFactory.class);
17 |
18 | static RemoteWebDriver createInstance(String browserName) {
19 | URL hubUrl = null;
20 | try {
21 | hubUrl = new URL("http://localhost:4444/wd/hub");
22 | } catch (MalformedURLException e) {
23 | e.printStackTrace();
24 | }
25 | return RemoteDriverFactory.createInstance(hubUrl,browserName);
26 | }
27 |
28 | static RemoteWebDriver createInstance(URL hubUrl, String browserName) {
29 | RemoteWebDriver driver = null;
30 | if (browserName.equalsIgnoreCase("firefox")) {
31 | DesiredCapabilities capability = DesiredCapabilities.firefox();
32 | driver = new RemoteWebDriver(hubUrl, capability);
33 | return driver;
34 | }
35 | if (browserName.equalsIgnoreCase("chrome")) {
36 | DesiredCapabilities capability = DesiredCapabilities.chrome();
37 | driver = new RemoteWebDriver(hubUrl, capability);
38 | return driver;
39 | }
40 | if (browserName.equalsIgnoreCase("ie")) {
41 | DesiredCapabilities capability = DesiredCapabilities.internetExplorer();
42 | driver = new RemoteWebDriver(hubUrl, capability);
43 | return driver;
44 | }
45 | if (browserName.equalsIgnoreCase("safari")) {
46 | DesiredCapabilities capability = DesiredCapabilities.safari();
47 | driver = new RemoteWebDriver(hubUrl, capability);
48 | return driver;
49 | }
50 | if (browserName.equalsIgnoreCase("opera")) {
51 | DesiredCapabilities capability = DesiredCapabilities.opera();
52 | driver = new RemoteWebDriver(hubUrl, capability);
53 | return driver;
54 | }
55 | if (browserName.equalsIgnoreCase("phantomjs")) {
56 | DesiredCapabilities capability = DesiredCapabilities.phantomjs();
57 | driver = new RemoteWebDriver(hubUrl, capability);
58 | return driver;
59 | }
60 | if (browserName.equalsIgnoreCase("android")) {
61 | DesiredCapabilities capability = DesiredCapabilities.android();
62 | driver = new RemoteWebDriver(hubUrl, capability);
63 | return driver;
64 | }
65 | if (browserName.equalsIgnoreCase("htmlUnit")) {
66 | DesiredCapabilities capability = DesiredCapabilities.htmlUnit();
67 | driver = new RemoteWebDriver(hubUrl, capability);
68 | return driver;
69 | }
70 | if (browserName.equalsIgnoreCase("htmlUnitWithJs")) {
71 | DesiredCapabilities capability = DesiredCapabilities.htmlUnitWithJs();
72 | driver = new RemoteWebDriver(hubUrl, capability);
73 | return driver;
74 | }
75 | if (browserName.equalsIgnoreCase("ipad")) {
76 | DesiredCapabilities capability = DesiredCapabilities.ipad();
77 | driver = new RemoteWebDriver(hubUrl, capability);
78 | return driver;
79 | }
80 | if (browserName.equalsIgnoreCase("iphone")) {
81 | DesiredCapabilities capability = DesiredCapabilities.iphone();
82 | driver = new RemoteWebDriver(hubUrl, capability);
83 | return driver;
84 | }
85 | log.info("RemoteDriverFactory created an instance of RemoteWebDriver for: " + browserName);
86 | return driver;
87 | }
88 | }
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/RemoteWebDriverListener.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG;
2 |
3 | import org.apache.log4j.Logger;
4 | import org.openqa.selenium.WebDriver;
5 | import org.testng.IInvokedMethod;
6 | import org.testng.IInvokedMethodListener;
7 | import org.testng.ITestResult;
8 | import org.testng.internal.BaseTestMethod;
9 |
10 | import java.lang.reflect.Field;
11 | import java.net.MalformedURLException;
12 | import java.net.URL;
13 |
14 |
15 | /**
16 | * A factory of remote WebDrivers.
17 | * Based on the LocalDriverFactory found at: onrationaleemotions.wordpress.com
18 | * @author: Confusions Personified
19 | * @src: http://rationaleemotions.wordpress.com/2013/07/31/parallel-webdriver-executions-using-testng/
20 | */
21 | public class RemoteWebDriverListener implements IInvokedMethodListener {
22 |
23 | static Logger log = Logger.getLogger(RemoteWebDriverListener.class);
24 |
25 | @Override
26 | public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
27 | log.debug("BEGINNING: org.stng.jbehave.RemoteWebDriverListener.beforeInvocation");
28 | if (method.isTestMethod()) {
29 | // get browser name specified in the TestNG XML test suite file
30 | String browserName = method.getTestMethod().getXmlTest().getLocalParameters().get("browserName");
31 | URL hubURL = null;
32 | try {
33 | // get hub URL specified in the TestNG XML test suite file
34 | // if not provided then use default http://localhost:4444/wd/hub
35 | hubURL = new URL(
36 | (method.getTestMethod().getXmlTest().getSuite().getParameter("hubURL") != "")
37 | ? method.getTestMethod().getXmlTest().getSuite().getParameter("hubURL")
38 | : "http://localhost:4444/wd/hub");
39 | } catch (MalformedURLException e) {
40 | System.out.println("ex:\n" + e.getMessage() + "");
41 | e.printStackTrace();
42 | }
43 | log.info("HUB URL: " + hubURL);
44 | // get and set new instance of remote WebDriver
45 | WebDriver driver = RemoteDriverFactory.createInstance(hubURL, browserName);
46 | DriverManager.setWebDriver(driver);
47 | log.info("Done! Created "+ browserName + " driver!" );
48 | } else {
49 | log.warn("Provided method is NOT a TestNG testMethod!!!");
50 | }
51 | log.debug("END: org.stng.jbehave.RemoteWebDriverListener.beforeInvocation");
52 | }
53 |
54 | @Override
55 | public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
56 | log.debug("BEGINNING: org.stng.jbehave.RemoteWebDriverListener.afterInvocation");
57 | if (method.isTestMethod()) {
58 | try {
59 | String browser = DriverManager.getBrowserInfo();
60 | // change the name of the test method that will appear in the report to one that will contain
61 | // also browser name, version and OS.
62 | // very handy when analysing results.
63 | BaseTestMethod bm = (BaseTestMethod)testResult.getMethod();
64 | Field f = bm.getClass().getSuperclass().getDeclaredField("m_methodName");
65 | f.setAccessible(true);
66 | String newTestName = testResult.getTestContext().getCurrentXmlTest().getName() + " - " + bm.getMethodName() + " - " + browser;
67 | log.info("Renaming test method name from: '" + bm.getMethodName() + "' to: '" + newTestName + "'");
68 | f.set(bm, newTestName);
69 | } catch (Exception ex) {
70 | System.out.println("afterInvocation exception:\n" + ex.getMessage());
71 | ex.printStackTrace();
72 | } finally {
73 | // close the browser
74 | WebDriver driver = DriverManager.getDriver();
75 | if (driver != null) {
76 | driver.quit();
77 | }
78 | }
79 | }
80 | log.debug("END: org.stng.jbehave.RemoteWebDriverListener.afterInvocation");
81 | }
82 | }
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/page_objects/GoogleCom.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG.page_objects;
2 |
3 | import org.openqa.selenium.By;
4 | import org.openqa.selenium.WebDriver;
5 | import org.openqa.selenium.WebElement;
6 | import org.openqa.selenium.support.ui.ExpectedConditions;
7 | import org.openqa.selenium.support.ui.WebDriverWait;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * Created by jay on 10/04/14.
13 | */
14 | public class GoogleCom {
15 |
16 | private By search_box = By.id("gbqfq");
17 | private By search_button = By.id("gbqfb");
18 | private By search_results = By.cssSelector("li.g");
19 | private By search_results_list = By.cssSelector("div.srg");
20 | private WebDriver driver;
21 | private WebDriverWait wait;
22 | private List theSearchResults;
23 |
24 | public GoogleCom(WebDriver driver) {
25 | this.driver = driver;
26 | this.wait = new WebDriverWait(this.driver, 10);
27 | }
28 |
29 | public void go(){
30 | this.driver.get("https://www.google.co.uk/");
31 | this.wait.until(ExpectedConditions.visibilityOfElementLocated(search_box));
32 | }
33 |
34 |
35 | public void searchFor(String keywords){
36 | this.driver.findElement(search_box).clear();
37 | this.driver.findElement(search_box).sendKeys(keywords);
38 | this.driver.findElement(search_button).click();
39 | this.wait.until(ExpectedConditions.visibilityOfElementLocated(search_results_list));
40 | }
41 |
42 | public List getTheSearchResults() {
43 | return this.driver.findElements(search_results);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/runners/RunCukesTestInChrome.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG.runners;
2 |
3 | import cucumber.api.CucumberOptions;
4 | import cucumber.api.testng.AbstractTestNGCucumberTests;
5 |
6 | /**
7 | * Please notice that cucumber.examples.java.testNG.stepDefinitions.BeforeAfterHooks class
8 | * is in the same package as the steps definitions.
9 | * It has two methods that are executed before or after scenario.
10 | * I'm using it to delete cookies and take a screenshot if scenario fails.
11 | */
12 | @CucumberOptions(
13 | features = "target/test-classes/features",
14 | glue = {"cucumber.examples.java.testNG.stepDefinitions"},
15 | format = {"pretty",
16 | "html:target/cucumber-report/chrome",
17 | "json:target/cucumber-report/chrome/cucumber.json",
18 | "junit:target/cucumber-report/chrome/cucumber.xml"})
19 | public class RunCukesTestInChrome extends AbstractTestNGCucumberTests {
20 | }
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/runners/RunCukesTestInFirefox.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG.runners;
2 |
3 | import cucumber.api.CucumberOptions;
4 | import cucumber.api.testng.AbstractTestNGCucumberTests;
5 |
6 | /**
7 | * Please notice that cucumber.examples.java.testNG.stepDefinitions.BeforeAfterHooks class
8 | * is in the same package as the steps definitions.
9 | * It has two methods that are executed before or after scenario.
10 | * I'm using it to delete cookies and take a screenshot if scenario fails.
11 | */
12 | @CucumberOptions(
13 | features = "target/test-classes/features",
14 | glue = {"cucumber.examples.java.testNG.stepDefinitions"},
15 | format = {"pretty",
16 | "html:target/cucumber-report/firefox",
17 | "json:target/cucumber-report/firefox/cucumber.json",
18 | "junit:target/cucumber-report/firefox/cucumber.xml"})
19 | public class RunCukesTestInFirefox extends AbstractTestNGCucumberTests {
20 | }
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/runners/RunSingleFeature.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG.runners;
2 |
3 | import cucumber.api.CucumberOptions;
4 | import cucumber.api.junit.Cucumber;
5 | import cucumber.api.testng.AbstractTestNGCucumberTests;
6 | import org.junit.runner.RunWith;
7 |
8 | @RunWith(Cucumber.class)
9 | @CucumberOptions(
10 | features = "target/test-classes/features/bugs/bug_no_12345.feature",
11 | glue = {"cucumber.examples.java.testNG.stepDefinitions"},
12 | format = {"pretty",
13 | "html:target/cucumber-report/singleFeature",
14 | "json:target/cucumber-report/singleFeature/cucumber.json",
15 | "junit:target/cucumber-report/singleFeature/cucumber.xml"})
16 | public class RunSingleFeature extends AbstractTestNGCucumberTests {
17 | }
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/stepDefinitions/BeforeAfterHooks.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG.stepDefinitions;
2 |
3 | import cucumber.api.Scenario;
4 | import cucumber.api.java.After;
5 | import cucumber.api.java.Before;
6 | import cucumber.examples.java.testNG.DriverManager;
7 | import org.apache.log4j.Logger;
8 | import org.openqa.selenium.OutputType;
9 | import org.openqa.selenium.TakesScreenshot;
10 | import org.openqa.selenium.WebDriverException;
11 |
12 | /**
13 | * Based on SharedDriver.java taken from cucumber-jvm/examples/java-webbit-websockets-selenium
14 | *
15 | * @src: https://github.com/cucumber/cucumber-jvm/blob/master/examples/java-webbit-websockets-selenium/src/test/java/cucumber/examples/java/websockets/SharedDriver.java
16 | * @author: https://github.com/aslakhellesoy
17 | */
18 | public class BeforeAfterHooks {
19 |
20 | static Logger log;
21 |
22 | static {
23 | log = Logger.getLogger(BeforeAfterHooks.class);
24 | }
25 |
26 | /**
27 | * Delete all cookies at the start of each scenario to avoid
28 | * shared state between tests
29 | */
30 | @Before
31 | public void deleteAllCookies() {
32 | log.info("Deleting all cookies...");
33 | DriverManager.getDriver().manage().deleteAllCookies();
34 | }
35 |
36 | /**
37 | * Embed a screenshot in test report if test is marked as failed
38 | */
39 | @After
40 | public static void embedScreenshot(Scenario scenario) {
41 | if ( scenario.isFailed() ) {
42 | log.error("Scenario failed! Browser: " + DriverManager.getBrowserInfo() + " Taking screenshot...");
43 | scenario.write("Current Page URL is: " + DriverManager.getDriver().getCurrentUrl());
44 | scenario.write("Scenario Failed in: " + DriverManager.getBrowserInfo());
45 | try {
46 | byte[] screenshot = ((TakesScreenshot) DriverManager.getDriver()).getScreenshotAs(OutputType.BYTES);
47 | scenario.embed(screenshot, "image/png");
48 | } catch (WebDriverException somePlatformsDontSupportScreenshots) {
49 | log.error(somePlatformsDontSupportScreenshots.getMessage());
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/stepDefinitions/Feature1And2Stepdefs.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG.stepDefinitions;
2 |
3 | import cucumber.api.java.en.Given;
4 | import cucumber.api.java.en.Then;
5 | import cucumber.api.java.en.When;
6 | import cucumber.examples.java.testNG.DriverManager;
7 | import org.apache.log4j.Logger;
8 | import org.openqa.selenium.By;
9 | import org.openqa.selenium.WebDriver;
10 | import org.openqa.selenium.WebElement;
11 | import org.openqa.selenium.support.ui.ExpectedConditions;
12 | import org.openqa.selenium.support.ui.WebDriverWait;
13 |
14 | import static org.hamcrest.Matchers.is;
15 | import static org.junit.Assert.assertThat;
16 |
17 | /**
18 | * Contains step definitions for two feature files: first.feature & second.feature
19 | * @author jk
20 | */
21 | public class Feature1And2Stepdefs {
22 |
23 | static Logger log = Logger.getLogger(Feature1And2Stepdefs.class);
24 | WebDriver driver = DriverManager.getDriver();
25 | WebElement webElement;
26 | WebDriverWait wait = new WebDriverWait(driver, 10);
27 |
28 | @Given("^I am on (.+)$")
29 | public void givenIAmOn(String URL) {
30 | log.info("Given I'm on "+URL+"
");
31 | driver.get(URL);
32 | }
33 |
34 | @When("^I search for element (.+)$")
35 | public void whenISearchForElement(String element_id) {
36 | log.info("When I search for element " + element_id);
37 | webElement = driver.findElement(By.id(element_id));
38 | wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(element_id)));
39 | }
40 |
41 | @Then("^I should see this element$")
42 | public void thenIShouldSeeThisElement() {
43 | log.info("Then I should see this element");
44 | assertThat("Element is not visible!!!", webElement.isDisplayed(), is(true));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/java/cucumber/examples/java/testNG/stepDefinitions/bugs/Bug12345.java:
--------------------------------------------------------------------------------
1 | package cucumber.examples.java.testNG.stepDefinitions.bugs;
2 |
3 | import cucumber.api.java.en.Given;
4 | import cucumber.api.java.en.Then;
5 | import cucumber.api.java.en.When;
6 | import cucumber.examples.java.testNG.DriverManager;
7 | import cucumber.examples.java.testNG.page_objects.GoogleCom;
8 | import org.apache.log4j.Logger;
9 | import org.openqa.selenium.*;
10 | import java.util.List;
11 |
12 | import static org.hamcrest.Matchers.is;
13 | import static org.junit.Assert.assertThat;
14 |
15 | /**
16 | * Simple steps mapping class that always fails.
17 | * I've added it to show how failed story will look like in the final Cucumber HTML report.
18 | * @author jk
19 | */
20 | public class Bug12345 {
21 |
22 | static Logger log;
23 | WebDriver driver = DriverManager.getDriver();
24 | private GoogleCom googleCom;
25 | private List searchResults;
26 |
27 | static {
28 | log = Logger.getLogger(Bug12345.class);
29 | }
30 |
31 | @Given("^I search on google.com for (.+)")
32 | public void givenISearchFor(String keywords) {
33 | log.info("Given I search google.com for " + keywords);
34 | googleCom = new GoogleCom(this.driver);
35 | googleCom.go();
36 | googleCom.searchFor(keywords);
37 | }
38 |
39 | @When("^I get the search results$")
40 | public void whenIGetTheSearchResults() {
41 | log.info("When I get the search results");
42 | this.searchResults = googleCom.getTheSearchResults();
43 | }
44 |
45 | @Then("^I should find the answer \"(.+)\" among the search result$")
46 | public void thenIShouldSeeThisElement(String answer) {
47 | log.info("Then I should find the answer '"+answer+"' among the search result");
48 | boolean didIFindTheAnswerToMyQuery = false;
49 | for ( WebElement we : this.searchResults){
50 | // change the state of didIFindTheAnswerToMyQuery only if the answer was found
51 | didIFindTheAnswerToMyQuery = (we.getText().toLowerCase().contains(answer.toLowerCase())) ? true : didIFindTheAnswerToMyQuery;
52 | }
53 | assertThat("Couldn't find the answer for my question!!!", didIFindTheAnswerToMyQuery, is(true));
54 | }
55 | }
--------------------------------------------------------------------------------
/src/test/resources/TestNGRunTestsLocally.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/test/resources/TestNGRunTestsRemotely.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/test/resources/features/WIP/WIP.feature:
--------------------------------------------------------------------------------
1 | @WIP
2 | Feature: this is not implemented
3 |
4 | Scenario: Download Internet on your pendrive
5 | Given I have a nice and shiny pendrive
6 | When I plug it in to my laptop
7 | Then I should download whole Internet on to it
--------------------------------------------------------------------------------
/src/test/resources/features/bugs/bug_no_12345.feature:
--------------------------------------------------------------------------------
1 | @bug_check
2 | @regression
3 | Feature: this feature does the regression check for bug no 12345
4 |
5 | Scenario Outline: Check if bug no 12345 haven't reoccurred
6 | Given I search on google.com for
7 | When I get the search results
8 | Then I should find the answer "" among the search result
9 |
10 | Examples:
11 | | keywords | answer |
12 | | can google search for itself? | no, it'd blow up the whole Internet |
13 | | where does Elvis live? | next door |
--------------------------------------------------------------------------------
/src/test/resources/features/feature_no1/first.feature:
--------------------------------------------------------------------------------
1 | @functional_test
2 | @other_meta_tag
3 | Feature: this is the first feature name
4 | this is an example additional feature description
5 |
6 |
7 | Scenario: Go to cukes.info
8 | Given I am on http://cukes.info/
9 | When I search for element header
10 | Then I should see this element
11 |
12 | Scenario Outline: Open various websites
13 | Given I am on
14 | When I search for element
15 | Then I should see this element
16 |
17 | Examples:
18 | | URL | element_id |
19 | | https://www.google.co.uk/ | hplogo |
20 | | https://www.facebook.com/ | blueBarHolder |
--------------------------------------------------------------------------------
/src/test/resources/features/feature_no2/second.feature:
--------------------------------------------------------------------------------
1 | @functional_test
2 | @other_meta_tag
3 | Feature: this is the second feature name
4 | you can write anything you want here
5 |
6 |
7 | Scenario: Go to amazon.co.uk
8 | Given I am on http://www.amazon.co.uk/
9 | When I search for element nav-logo
10 | Then I should see this element
11 |
12 | Scenario Outline: Open various websites
13 | Given I am on
14 | When I search for element
15 | Then I should see this element
16 |
17 | Examples:
18 | | URL | element_id |
19 | | https://github.com/cucumber/cucumber/wiki/Gherkin | wiki-wrapper |
20 | | http://testng.org/doc/index.html | topmenu |
--------------------------------------------------------------------------------
/src/test/resources/log4j.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------