├── .classpath
├── .gitignore
├── .project
├── .settings
└── org.eclipse.core.resources.prefs
├── AUTHORS
├── LICENSE
├── README.md
├── config.properties
├── data
└── GoogleSearchWithDataDriver.xls
├── imagecheck.properties
├── pom.xml
├── prop.properties
├── res
├── Readme
└── SeleniumCommand.exe
├── screenshot
└── 20130105-231629.png
├── src
├── com
│ └── netease
│ │ ├── dagger
│ │ ├── BrowserEmulator.java
│ │ ├── GlobalSettings.java
│ │ └── LogTools.java
│ │ ├── datadriver
│ │ └── ExcelDataProvider.java
│ │ ├── demo
│ │ ├── CheckPageStyle.java
│ │ ├── GoogleSearch.java
│ │ ├── GoogleSearchWithDataDriver.java
│ │ ├── TestNg.java
│ │ ├── TestNgClone.java
│ │ └── TestSimpleFlexApp.java
│ │ ├── flexauto
│ │ └── FlexAutomation.java
│ │ ├── imagecheck
│ │ ├── ImageContrast.java
│ │ ├── ImageProcess.java
│ │ └── Settings.java
│ │ └── test
│ │ ├── ClickOperations.java
│ │ ├── CommonFunction.java
│ │ ├── ExecuteJS.java
│ │ ├── IsElementPresentOperations.java
│ │ ├── IsTextPresentOperations.java
│ │ ├── MouseOverOperations.java
│ │ ├── OpenURL.java
│ │ ├── SelectWindow.java
│ │ └── TypeOperations.java
└── log4j.properties
└── test-xml
├── demo.xml
└── self-test.xml
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dagger
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding/=UTF-8
3 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | * ChenKan
2 | * LingFei
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | (The Apache License)
2 |
3 | Copyright (c) 2012-2013 NetEase, Inc. and other contributors
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Dagger - a light, robust Web UI autotest framework
2 |
3 | Dagger is a light, robust Web UI autotest framework based on [Selenium](http://seleniumhq.org/) and [TestNG](http://testng.org/doc/index.html).(中文介绍参见[这里](https://github.com/NetEase/Dagger/wiki/Dagger))
4 |
5 | > Dagger is an automation framework first, it provides APIs to control browsers;
6 | >
7 | > Dagger is a test framework then, it uses TestNG to organize testcases and TestNG's assertions are embedded in APIs above;
8 | >
9 | > Dagger is a design style at last: the framework and the testcases based on it both should be light and straightforward.
10 |
11 | * Wiki:
12 | * Issues:
13 | * Javadoc:
14 | * Quick Start:
15 | * Tags: Selenium, TestNG, autotest
16 |
17 | ## Features
18 |
19 | * Easy to learn while only less than 30 APIs altogether, see [this](http://netease.github.com/Dagger/classcom_1_1netease_1_1dagger_1_1_browser_emulator.html).
20 | * High speed with parallel mode which is indeed TestNG's feature, see [this](https://github.com/NetEase/Dagger/wiki/Parallel-Mode)
21 | * Re-run failed test cases using [Arrow](https://github.com/NetEase/arrow), see [this](https://github.com/NetEase/Dagger/wiki/Retry-Failed-Testcases-using-Arrow).
22 | * Use Chrome as default browser which is much quicker and more stable than all Firefox, IE and Safari.
23 | * Firefox, IE and Safari are also supported.
24 | * Capture screenshot automatically when testcase fails.
25 | * Support data-driven testing.
26 | * Contrast images and compare differences by crawling page elements and screenshots on the pixel level.
27 | * Support Flash automation testing.
28 |
29 |
30 | ## How to Use
31 |
32 | Dagger is quite suitable for a small group to kick off Web UI autotest. For this case, just checkout Dagger and configure maven with Eclipse and then write testcases with it.
33 |
34 | Already have an autotest framework? Please build Dagger into a .jar file before use, the steps are as follows:
35 | * Checkout Dagger's source code
36 | * Enter the root directory
37 | * Run `mvn clean package -DskipTests` in terminal
38 |
39 | Then fetch `dagger-1.3.jar` under `target` folder, or just [download](https://github.com/NetEase/Dagger/releases/download/v1.3/dagger-1.3.jar) the `dagger-1.3.jar`. See [this](https://github.com/NetEase/Dagger/wiki/FAQ) for more details.
40 |
41 | Currently, we use `selenium-server-standalone-2.39.0.jar` and `selenium-safari-driver-2.39.0.jar` , you can change the configuration in the _pom.xml_.
42 |
43 | By the way, you should download the [chromedriver_for_win_2.3.exe](http://chromedriver.storage.googleapis.com/2.3/chromedriver_win32.zip) and [iedriver_win32_2.39.0.exe](http://selenium.googlecode.com/files/IEDriverServer_Win32_2.39.0.zip) if necessary. We suggest to put the .exe files in the `res` folder.
44 |
45 | ## Contributors
46 |
47 | * NetEase, Inc.
48 | * chenDoInG
49 |
50 | ## How to Contribute
51 |
52 | You are welcome to contribute to Dagger as follow
53 |
54 | * add/edit wiki
55 | * report/fix issue
56 | * code review
57 | * commit new feature
58 | * add testcase/demo
59 |
60 | Meanwhile you'd better follow the rules below
61 |
62 | * It's *NOT* recommended to submit a pull request directly to Dagger's `master` branch. `develop` branch is more appropriate
63 | * Follow common Java coding conventions
64 | * Put all Java class files under *com.netease* package
65 | * Add the following [license](#license) in each Java class file
66 |
67 | ## License
68 |
69 | (The Apache License)
70 |
71 | Copyright (c) 2012-2014 NetEase, Inc. and other contributors
72 |
73 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
74 |
75 | http://www.apache.org/licenses/LICENSE-2.0
76 |
77 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
78 |
--------------------------------------------------------------------------------
/config.properties:
--------------------------------------------------------------------------------
1 | retryCount=1
--------------------------------------------------------------------------------
/data/GoogleSearchWithDataDriver.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Dagger/4f9408f0635bf46dab7cd94e46573657bdc6661e/data/GoogleSearchWithDataDriver.xls
--------------------------------------------------------------------------------
/imagecheck.properties:
--------------------------------------------------------------------------------
1 | # Set screen shot type
2 | # 1 => collect samples
3 | # 2 => image contrast
4 | ScreenShotType = 1
5 |
6 | # Image store path
7 | SampleImagePath = res/samples/
8 | ContrastImagePath = images/
9 |
10 | # Max color difference threshold
11 | MaxColorThreshold = 0
12 |
13 | # Browser core type
14 | # 1 => FireFox
15 | # 2 => Chrome
16 | # 3 => IE
17 | BrowserCoreType = 2
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.netease
5 | dagger
6 | 1.3
7 | jar
8 |
9 |
10 | UTF-8
11 | UTF-8
12 | 2
13 |
14 |
15 |
16 |
17 | arrow-mvn-repo
18 | https://raw.github.com/Netease/arrow/mvn-repo/
19 |
20 | true
21 | always
22 |
23 |
24 |
25 |
26 |
27 |
28 | com.netease.qa
29 | arrow
30 | 0.0.2
31 | test
32 |
33 |
34 | net.sourceforge.jexcelapi
35 | jxl
36 | 2.6.12
37 | provided
38 |
39 |
40 | log4j
41 | log4j
42 | 1.2.16
43 | provided
44 |
45 |
46 |
47 | org.jclouds
48 | jclouds-antcontrib
49 | 1.5.4
50 | provided
51 |
52 |
53 |
54 | org.seleniumhq.selenium
55 | selenium-server
56 | 2.39.0
57 | provided
58 |
59 |
60 |
61 | org.seleniumhq.selenium
62 | selenium-safari-driver
63 | 2.39.0
64 | provided
65 |
66 |
67 |
68 | org.testng
69 | testng
70 | 6.8.5
71 | provided
72 |
73 |
74 |
75 | src
76 |
77 |
78 | src
79 | true
80 |
81 | log4j.properties
82 |
83 |
84 |
85 |
86 |
87 | com.google.code.maven-replacer-plugin
88 | replacer
89 | 1.5.2
90 |
91 |
92 | process-test-resources
93 |
94 | replace
95 |
96 |
97 |
98 |
99 | prop.properties
100 | true
101 | BrowserCoreType.*
102 | BrowserCoreType=${BrowserCoreType}
103 |
104 |
105 |
106 | org.apache.maven.plugins
107 | maven-compiler-plugin
108 | 3.1
109 |
110 | eclipse
111 |
112 |
113 |
114 | org.codehaus.plexus
115 | plexus-compiler-eclipse
116 | 2.2
117 |
118 |
119 |
120 |
121 | org.apache.maven.plugins
122 | maven-surefire-plugin
123 | 2.16
124 |
125 |
126 |
127 | listener
128 | com.netease.qa.testng.PowerEmailableReporter,com.netease.qa.testng.RetryListener,com.netease.qa.testng.TestResultListener
129 |
130 |
131 | true
132 |
133 | ${basedir}/test-xml/self-test.xml
134 |
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/prop.properties:
--------------------------------------------------------------------------------
1 | # Browser core type
2 | # 1 => FireFox
3 | # 2 => Chrome
4 | # 3 => IE
5 | # 4 => Safari
6 | BrowserCoreType = 2
7 |
8 | # Drivers' path
9 | ChromeDriverPath = res/chromedriver_for_win.exe
10 | IEDriverPath = res/iedriver_32.exe
11 |
12 | # UI actions' interval in millisecond
13 | StepInterval = 500
14 |
15 | # UI actions' timeout in millisecond
16 | Timeout = 30000
--------------------------------------------------------------------------------
/res/Readme:
--------------------------------------------------------------------------------
1 | we suggest to put all drivers here if needed.
--------------------------------------------------------------------------------
/res/SeleniumCommand.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Dagger/4f9408f0635bf46dab7cd94e46573657bdc6661e/res/SeleniumCommand.exe
--------------------------------------------------------------------------------
/screenshot/20130105-231629.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetEase/Dagger/4f9408f0635bf46dab7cd94e46573657bdc6661e/screenshot/20130105-231629.png
--------------------------------------------------------------------------------
/src/com/netease/dagger/BrowserEmulator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
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 | */
17 | package com.netease.dagger;
18 |
19 | import java.awt.AWTException;
20 | import java.awt.Robot;
21 | import java.util.Arrays;
22 | import java.io.File;
23 | import java.io.IOException;
24 | import org.apache.log4j.Logger;
25 | import org.openqa.selenium.By;
26 | import org.openqa.selenium.JavascriptExecutor;
27 | import org.openqa.selenium.WebDriverBackedSelenium;
28 | import org.openqa.selenium.WebElement;
29 | import org.openqa.selenium.chrome.ChromeDriverService;
30 | import org.openqa.selenium.firefox.FirefoxDriver;
31 | import org.openqa.selenium.ie.InternetExplorerDriver;
32 | import org.openqa.selenium.interactions.Actions;
33 | import org.openqa.selenium.remote.DesiredCapabilities;
34 | import org.openqa.selenium.remote.RemoteWebDriver;
35 | import org.openqa.selenium.safari.SafariDriver;
36 | import org.openqa.selenium.support.ui.Select;
37 | import org.testng.Assert;
38 | import org.testng.Reporter;
39 |
40 | import com.thoughtworks.selenium.Wait;
41 |
42 | /**
43 | * BrowserEmulator is based on Selenium2 and adds some enhancements
44 | * @author ChenKan
45 | */
46 | public class BrowserEmulator {
47 |
48 | RemoteWebDriver browserCore;
49 | WebDriverBackedSelenium browser;
50 | ChromeDriverService chromeServer;
51 | JavascriptExecutor javaScriptExecutor;
52 |
53 | int stepInterval = Integer.parseInt(GlobalSettings.stepInterval);
54 | int timeout = Integer.parseInt(GlobalSettings.timeout);
55 |
56 | private static Logger logger = Logger.getLogger(BrowserEmulator.class.getName());
57 |
58 | public BrowserEmulator() {
59 | setupBrowserCoreType(GlobalSettings.browserCoreType);
60 | browser = new WebDriverBackedSelenium(browserCore, "http://www.163.com/");
61 | javaScriptExecutor = (JavascriptExecutor) browserCore;
62 | logger.info("Started BrowserEmulator");
63 | }
64 |
65 | private void setupBrowserCoreType(int type) {
66 | if (type == 1) {
67 | browserCore = new FirefoxDriver();
68 | logger.info("Using Firefox");
69 | return;
70 | }
71 | if (type == 2) {
72 | chromeServer = new ChromeDriverService.Builder().usingDriverExecutable(new File(GlobalSettings.chromeDriverPath)).usingAnyFreePort().build();
73 | try {
74 | chromeServer.start();
75 | } catch (IOException e) {
76 | e.printStackTrace();
77 | }
78 | DesiredCapabilities capabilities = DesiredCapabilities.chrome();
79 | capabilities.setCapability("chrome.switches", Arrays.asList("--start-maximized"));
80 | browserCore = new RemoteWebDriver(chromeServer.getUrl(), capabilities);
81 | logger.info("Using Chrome");
82 | return;
83 | }
84 | if (type == 3) {
85 | System.setProperty("webdriver.ie.driver", GlobalSettings.ieDriverPath);
86 | DesiredCapabilities capabilities = DesiredCapabilities.internetExplorer();
87 | capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
88 | browserCore = new InternetExplorerDriver(capabilities);
89 | logger.info("Using IE");
90 | return;
91 | }
92 | if (type == 4) {
93 | browserCore = new SafariDriver();
94 | logger.info("Using Safari");
95 | return;
96 | }
97 |
98 | Assert.fail("Incorrect browser type");
99 | }
100 |
101 | /**
102 | * Get the WebDriver instance embedded in BrowserEmulator
103 | * @return a WebDriver instance
104 | */
105 | public RemoteWebDriver getBrowserCore() {
106 | return browserCore;
107 | }
108 |
109 | /**
110 | * Get the WebDriverBackedSelenium instance embedded in BrowserEmulator
111 | * @return a WebDriverBackedSelenium instance
112 | */
113 | public WebDriverBackedSelenium getBrowser() {
114 | return browser;
115 | }
116 |
117 | /**
118 | * Get the JavascriptExecutor instance embedded in BrowserEmulator
119 | * @return a JavascriptExecutor instance
120 | */
121 | public JavascriptExecutor getJavaScriptExecutor() {
122 | return javaScriptExecutor;
123 | }
124 |
125 | /**
126 | * Open the URL
127 | * @param url
128 | * the target URL
129 | */
130 | public void open(String url) {
131 | pause(stepInterval);
132 | try {
133 | browser.open(url);
134 | } catch (Exception e) {
135 | e.printStackTrace();
136 | handleFailure("Failed to open url " + url);
137 | }
138 | logger.info("Opened url " + url);
139 | }
140 |
141 | /**
142 | * Quit the browser
143 | */
144 | public void quit() {
145 | pause(stepInterval);
146 | browserCore.quit();
147 | if (GlobalSettings.browserCoreType == 2) {
148 | chromeServer.stop();
149 | }
150 | logger.info("Quitted BrowserEmulator");
151 | }
152 |
153 | /**
154 | * Click the page element
155 | * @param xpath
156 | * the element's xpath
157 | */
158 | public void click(String xpath) {
159 | pause(stepInterval);
160 | expectElementExistOrNot(true, xpath, timeout);
161 | try {
162 | clickTheClickable(xpath, System.currentTimeMillis(), 2500);
163 | } catch (Exception e) {
164 | e.printStackTrace();
165 | handleFailure("Failed to click " + xpath);
166 | }
167 | logger.info("Clicked " + xpath);
168 | }
169 |
170 | /**
171 | * Click an element until it's clickable or timeout
172 | * @param xpath
173 | * @param startTime
174 | * @param timeout in millisecond
175 | * @throws Exception
176 | */
177 | private void clickTheClickable(String xpath, long startTime, int timeout) throws Exception {
178 | try {
179 | browserCore.findElementByXPath(xpath).click();
180 | } catch (Exception e) {
181 | if (System.currentTimeMillis() - startTime > timeout) {
182 | logger.info("Element " + xpath + " is unclickable");
183 | throw new Exception(e);
184 | } else {
185 | Thread.sleep(500);
186 | logger.info("Element " + xpath + " is unclickable, try again");
187 | clickTheClickable(xpath, startTime, timeout);
188 | }
189 | }
190 | }
191 |
192 | /**
193 | * Type text at the page element
194 | * Before typing, try to clear existed text
195 | * @param xpath
196 | * the element's xpath
197 | * @param text
198 | * the input text
199 | */
200 | public void type(String xpath, String text) {
201 | pause(stepInterval);
202 | expectElementExistOrNot(true, xpath, timeout);
203 |
204 | WebElement we = browserCore.findElement(By.xpath(xpath));
205 | try {
206 | we.clear();
207 | } catch (Exception e) {
208 | logger.warn("Failed to clear text at " + xpath);
209 | }
210 | try {
211 | we.sendKeys(text);
212 | } catch (Exception e) {
213 | e.printStackTrace();
214 | handleFailure("Failed to type " + text + " at " + xpath);
215 | }
216 |
217 | logger.info("Type " + text + " at " + xpath);
218 | }
219 |
220 | /**
221 | * Hover on the page element
222 | *
223 | * @param xpath
224 | * the element's xpath
225 | */
226 | public void mouseOver(String xpath) {
227 | pause(stepInterval);
228 | expectElementExistOrNot(true, xpath, timeout);
229 | // First make mouse out of browser
230 | Robot rb = null;
231 | try {
232 | rb = new Robot();
233 | } catch (AWTException e) {
234 | e.printStackTrace();
235 | }
236 | rb.mouseMove(0, 0);
237 |
238 | // Then hover
239 | WebElement we = browserCore.findElement(By.xpath(xpath));
240 |
241 | if (GlobalSettings.browserCoreType == 2) {
242 | try {
243 | Actions builder = new Actions(browserCore);
244 | builder.moveToElement(we).build().perform();
245 | } catch (Exception e) {
246 | e.printStackTrace();
247 | handleFailure("Failed to mouseover " + xpath);
248 | }
249 |
250 | logger.info("Mouseover " + xpath);
251 | return;
252 | }
253 |
254 | // Firefox and IE require multiple cycles, more than twice, to cause a
255 | // hovering effect
256 | if (GlobalSettings.browserCoreType == 1
257 | || GlobalSettings.browserCoreType == 3) {
258 | for (int i = 0; i < 5; i++) {
259 | Actions builder = new Actions(browserCore);
260 | builder.moveToElement(we).build().perform();
261 | }
262 | logger.info("Mouseover " + xpath);
263 | return;
264 | }
265 |
266 | // Selenium doesn't support the Safari browser
267 | if (GlobalSettings.browserCoreType == 4) {
268 | Assert.fail("Mouseover is not supported for Safari now");
269 | }
270 | Assert.fail("Incorrect browser type");
271 | }
272 |
273 | /**
274 | * Switch window/tab
275 | * @param windowTitle
276 | * the window/tab's title
277 | */
278 | public void selectWindow(String windowTitle) {
279 | pause(stepInterval);
280 | browser.selectWindow(windowTitle);
281 | logger.info("Switched to window " + windowTitle);
282 | }
283 |
284 | /**
285 | * Enter the iframe
286 | * @param xpath
287 | * the iframe's xpath
288 | */
289 | public void enterFrame(String xpath) {
290 | pause(stepInterval);
291 | browserCore.switchTo().frame(browserCore.findElementByXPath(xpath));
292 | logger.info("Entered iframe " + xpath);
293 | }
294 |
295 | /**
296 | * Leave the iframe
297 | */
298 | public void leaveFrame() {
299 | pause(stepInterval);
300 | browserCore.switchTo().defaultContent();
301 | logger.info("Left the iframe");
302 | }
303 |
304 | /**
305 | * Refresh the browser
306 | */
307 | public void refresh() {
308 | pause(stepInterval);
309 | browserCore.navigate().refresh();
310 | logger.info("Refreshed");
311 | }
312 |
313 | /**
314 | * Mimic system-level keyboard event
315 | * @param keyCode
316 | * such as KeyEvent.VK_TAB, KeyEvent.VK_F11
317 | */
318 | public void pressKeyboard(int keyCode) {
319 | pause(stepInterval);
320 | Robot rb = null;
321 | try {
322 | rb = new Robot();
323 | } catch (AWTException e) {
324 | e.printStackTrace();
325 | }
326 | rb.keyPress(keyCode); // press key
327 | rb.delay(100); // delay 100ms
328 | rb.keyRelease(keyCode); // release key
329 | logger.info("Pressed key with code " + keyCode);
330 | }
331 |
332 | /**
333 | * Mimic system-level keyboard event with String
334 | *
335 | * @param text
336 | *
337 | */
338 | public void inputKeyboard(String text) {
339 | String cmd = System.getProperty("user.dir") + "\\res\\SeleniumCommand.exe" + " sendKeys " + text;
340 |
341 | Process p = null;
342 | try {
343 | p = Runtime.getRuntime().exec(cmd);
344 | p.waitFor();
345 | } catch (Exception e) {
346 | e.printStackTrace();
347 | } finally {
348 | p.destroy();
349 | }
350 | logger.info("Pressed key with string " + text);
351 | }
352 |
353 | //TODO Mimic system-level mouse event
354 |
355 | /**
356 | * Expect some text exist or not on the page
357 | * Expect text exist, but not found after timeout => Assert fail
358 | * Expect text not exist, but found after timeout => Assert fail
359 | * @param expectExist
360 | * true or false
361 | * @param text
362 | * the expected text
363 | * @param timeout
364 | * timeout in millisecond
365 | */
366 | public void expectTextExistOrNot(boolean expectExist, final String text, int timeout) {
367 | if (expectExist) {
368 | try {
369 | new Wait() {
370 | public boolean until() {
371 | return isTextPresent(text, -1);
372 | }
373 | }.wait("Failed to find text " + text, timeout);
374 | } catch (Exception e) {
375 | e.printStackTrace();
376 | handleFailure("Failed to find text " + text);
377 | }
378 | logger.info("Found desired text " + text);
379 | } else {
380 | if (isTextPresent(text, timeout)) {
381 | handleFailure("Found undesired text " + text);
382 | } else {
383 | logger.info("Not found undesired text " + text);
384 | }
385 | }
386 | }
387 |
388 | /**
389 | * Expect an element exist or not on the page
390 | * Expect element exist, but not found after timeout => Assert fail
391 | * Expect element not exist, but found after timeout => Assert fail
392 | * Here exist means visible
393 | * @param expectExist
394 | * true or false
395 | * @param xpath
396 | * the expected element's xpath
397 | * @param timeout
398 | * timeout in millisecond
399 | */
400 | public void expectElementExistOrNot(boolean expectExist, final String xpath, int timeout) {
401 | if (expectExist) {
402 | try {
403 | new Wait() {
404 | public boolean until() {
405 | return isElementPresent(xpath, -1);
406 | }
407 | }.wait("Failed to find element " + xpath, timeout);
408 | } catch (Exception e) {
409 | e.printStackTrace();
410 | handleFailure("Failed to find element " + xpath);
411 | }
412 | logger.info("Found desired element " + xpath);
413 | } else {
414 | if (isElementPresent(xpath, timeout)) {
415 | handleFailure("Found undesired element " + xpath);
416 | } else {
417 | logger.info("Not found undesired element " + xpath);
418 | }
419 | }
420 | }
421 |
422 | /**
423 | * Is the text present on the page
424 | * @param text
425 | * the expected text
426 | * @param time
427 | * wait a moment (in millisecond) before search text on page;
428 | * minus time means search text at once
429 | * @return
430 | */
431 | public boolean isTextPresent(String text, int time) {
432 | pause(time);
433 | boolean isPresent = browser.isTextPresent(text);
434 | if (isPresent) {
435 | logger.info("Found text " + text);
436 | return true;
437 | } else {
438 | logger.info("Not found text " + text);
439 | return false;
440 | }
441 | }
442 |
443 | /**
444 | * Is the element present on the page
445 | * Here present means visible
446 | * @param xpath
447 | * the expected element's xpath
448 | * @param time
449 | * wait a moment (in millisecond) before search element on page;
450 | * minus time means search element at once
451 | * @return
452 | */
453 | public boolean isElementPresent(String xpath, int time) {
454 | pause(time);
455 | boolean isPresent = browser.isElementPresent(xpath) && browserCore.findElementByXPath(xpath).isDisplayed();
456 | if (isPresent) {
457 | logger.info("Found element " + xpath);
458 | return true;
459 | } else {
460 | logger.info("Not found element" + xpath);
461 | return false;
462 | }
463 | }
464 |
465 | /**
466 | * Pause
467 | * @param time in millisecond
468 | */
469 | public void pause(int time) {
470 | if (time <= 0) {
471 | return;
472 | }
473 | try {
474 | Thread.sleep(time);
475 | logger.info("Pause " + time + " ms");
476 | } catch (InterruptedException e) {
477 | e.printStackTrace();
478 | }
479 | }
480 |
481 | private void handleFailure(String notice) {
482 | String png = LogTools.screenShot(this);
483 | String log = notice + " >> capture screenshot at " + png;
484 | logger.error(log);
485 | if (GlobalSettings.baseStorageUrl.lastIndexOf("/") == GlobalSettings.baseStorageUrl.length()) {
486 | GlobalSettings.baseStorageUrl = GlobalSettings.baseStorageUrl.substring(0, GlobalSettings.baseStorageUrl.length() - 1);
487 | }
488 | Reporter.log(log + "
");
489 | Assert.fail(log);
490 | }
491 |
492 | /**
493 | * Return text from specified web element.
494 | * @param xpath
495 | * @return
496 | */
497 | public String getText(String xpath) {
498 | WebElement element = this.getBrowserCore().findElement(By.xpath(xpath));
499 | return element.getText();
500 | }
501 |
502 | /**
503 | * Select an option by visible text from <select> web element.
504 | * @param xpath
505 | * @param option
506 | */
507 | public void select(String xpath, String option) {
508 | WebElement element = this.browserCore.findElement(By.xpath(xpath));
509 | Select select = new Select(element);
510 | select.selectByVisibleText(option);
511 | }
512 | }
513 |
--------------------------------------------------------------------------------
/src/com/netease/dagger/GlobalSettings.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
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 | */
17 | package com.netease.dagger;
18 |
19 | import java.io.File;
20 | import java.io.FileInputStream;
21 | import java.util.Properties;
22 |
23 | /**
24 | * Global Settings
25 | * @author ChenKan
26 | */
27 | public class GlobalSettings {
28 |
29 | public static Properties prop = getProperties();
30 |
31 | public static int browserCoreType = Integer.parseInt(prop.getProperty("BrowserCoreType", "2"));
32 |
33 | public static String chromeDriverPath = prop.getProperty("ChromeDriverPath", "res/chromedriver_for_win.exe");
34 | // public static String chromeDriverPath = "/Users/chenDoInG/Downloads/chromedriver_for_mac_64";
35 | public static String ieDriverPath = prop.getProperty("IEDriverPath", "res/iedriver_32.exe");
36 |
37 | public static String stepInterval = prop.getProperty("StepInterval", "500");
38 |
39 | public static String timeout = prop.getProperty("Timeout", "30000");
40 |
41 | public static String baseStorageUrl = prop.getProperty("baseStorageUrl", System.getProperty("user.dir"));
42 |
43 | public static String getProperty(String property) {
44 | return prop.getProperty(property);
45 | }
46 |
47 | public static Properties getProperties() {
48 | Properties prop = new Properties();
49 | try {
50 | FileInputStream file = new FileInputStream("prop.properties");
51 | prop.load(file);
52 | file.close();
53 | } catch (Exception e) {
54 | e.printStackTrace();
55 | }
56 | return prop;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/com/netease/dagger/LogTools.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012-2013 NetEase, Inc. and other contributors
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 | */
17 | package com.netease.dagger;
18 |
19 | import java.io.File;
20 | import java.text.SimpleDateFormat;
21 | import java.util.Date;
22 | import org.apache.commons.io.FileUtils;
23 | import org.openqa.selenium.Dimension;
24 | import org.openqa.selenium.OutputType;
25 | import org.openqa.selenium.Point;
26 | import org.openqa.selenium.TakesScreenshot;
27 | import org.openqa.selenium.WebDriver;
28 | import org.openqa.selenium.remote.Augmenter;
29 |
30 | /**
31 | * Log Tools
32 | * @author ChenKan
33 | */
34 | public class LogTools {
35 |
36 | public static void log(String logText) {
37 | System.out.println("[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis())) + "] " + logText);
38 | }
39 |
40 | public static String screenShot(BrowserEmulator be) {
41 | String dir = "screenshot"; // TODO
42 | String time = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date());
43 | String screenShotPath = dir + File.separator + time + ".png";
44 |
45 | WebDriver augmentedDriver = null;
46 | if (GlobalSettings.browserCoreType == 1 || GlobalSettings.browserCoreType == 3) {
47 | augmentedDriver = be.getBrowserCore();
48 | augmentedDriver.manage().window().setPosition(new Point(0, 0));
49 | augmentedDriver.manage().window().setSize(new Dimension(9999, 9999));
50 | } else if (GlobalSettings.browserCoreType == 2) {
51 | augmentedDriver = new Augmenter().augment(be.getBrowserCore());
52 | } else {
53 | return "Incorrect browser type";
54 | }
55 |
56 | try {
57 | File sourceFile = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.FILE);
58 | FileUtils.copyFile(sourceFile, new File(screenShotPath));
59 | } catch (Exception e) {
60 | e.printStackTrace();
61 | return "Failed to screenshot";
62 | }
63 |
64 | // Convert '\' into '/' for web image browsing.
65 | return screenShotPath.replace("\\", "/");
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/com/netease/datadriver/ExcelDataProvider.java:
--------------------------------------------------------------------------------
1 | package com.netease.datadriver;
2 |
3 | import java.io.FileInputStream;
4 | import java.io.InputStream;
5 | import java.util.HashMap;
6 | import java.util.Iterator;
7 | import java.util.Map;
8 |
9 | import org.testng.Assert;
10 |
11 | import jxl.*;
12 |
13 | /**
14 | * Excel放在Data文件夹下
15 | * Excel命名方式:测试类名.xls
16 | * Excel的sheet命名方式:测试方法名
17 | * Excel第一行为Map键值
18 | * 代码参考郑鸿志的Blog
19 | * {@link www.zhenghongzhi.cn/post/42.html}
20 | * @ClassName: ExcelDataProvider
21 | * @Description: TODO(读取Excel数据)
22 | * @author chenDoInG
23 | */
24 | public class ExcelDataProvider implements Iterator