├── .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 { 25 | 26 | private Workbook book = null; 27 | private Sheet sheet = null; 28 | private int rowNum = 0; 29 | private int currentRowNo = 0; 30 | private int columnNum = 0; 31 | private String[] columnnName; 32 | 33 | public ExcelDataProvider(String classname, String methodname) { 34 | 35 | try { 36 | 37 | int dotNum = classname.indexOf("."); 38 | 39 | if (dotNum > 0) { 40 | classname = classname.substring(classname.lastIndexOf(".") + 1, 41 | classname.length()); 42 | } 43 | 44 | String path = "data/" + classname + ".xls"; 45 | InputStream inputStream = new FileInputStream(path); 46 | 47 | book = Workbook.getWorkbook(inputStream); 48 | // sheet = book.getSheet(methodname); 49 | sheet = book.getSheet(0); 50 | rowNum = sheet.getRows(); 51 | Cell[] cell = sheet.getRow(0); 52 | columnNum = cell.length; 53 | columnnName = new String[cell.length]; 54 | 55 | for (int i = 0; i < cell.length; i++) { 56 | columnnName[i] = cell[i].getContents().toString(); 57 | } 58 | this.currentRowNo++; 59 | 60 | } catch (Exception e) { 61 | e.printStackTrace(); 62 | Assert.fail("unable to read Excel data"); 63 | } 64 | } 65 | 66 | public boolean hasNext() { 67 | 68 | if (this.rowNum == 0 || this.currentRowNo >= this.rowNum) { 69 | 70 | try { 71 | book.close(); 72 | } catch (Exception e) { 73 | e.printStackTrace(); 74 | } 75 | return false; 76 | } else { 77 | // sheet下一行内容为空判定结束 78 | if ((sheet.getRow(currentRowNo))[0].getContents().equals("")) 79 | return false; 80 | return true; 81 | } 82 | } 83 | 84 | public Object[] next() { 85 | 86 | Cell[] c = sheet.getRow(this.currentRowNo); 87 | Map data = new HashMap(); 88 | // List list = new ArrayList(); 89 | 90 | for (int i = 0; i < this.columnNum; i++) { 91 | 92 | String temp = ""; 93 | 94 | try { 95 | temp = c[i].getContents().toString(); 96 | } catch (ArrayIndexOutOfBoundsException ex) { 97 | temp = ""; 98 | } 99 | 100 | // if(temp != null&& !temp.equals("")) 101 | // list.add(temp); 102 | data.put(this.columnnName[i], temp); 103 | } 104 | Object object[] = new Object[1]; 105 | object[0] = data; 106 | this.currentRowNo++; 107 | return object; 108 | } 109 | 110 | public void remove() { 111 | throw new UnsupportedOperationException("remove unsupported."); 112 | } 113 | } -------------------------------------------------------------------------------- /src/com/netease/demo/CheckPageStyle.java: -------------------------------------------------------------------------------- 1 | package com.netease.demo; 2 | 3 | import com.netease.imagecheck.ImageProcess; 4 | import com.netease.dagger.BrowserEmulator; 5 | 6 | /** 7 | * This demo shows how to use image contrast feature to check page style 8 | * Please see details at https://github.com/leonar/Dagger/wiki/Demo-for-Image-Contrast 9 | * @author LingFei 10 | */ 11 | public class CheckPageStyle { 12 | 13 | public static void main(String[] args) throws Exception { 14 | // Change githubUrl to https://github.com/login for contrast 15 | // Remember to change ScreenShotType from 1 to 2 in imagecheck.properties 16 | String githubUrl = "https://github.com/"; 17 | String topbarXpath = "//div[@class='container clearfix']"; 18 | String checkPoint = "topbar"; 19 | String folderName = "github"; 20 | BrowserEmulator be = new BrowserEmulator(); 21 | be.open(githubUrl); 22 | ImageProcess.process(be.getBrowserCore(), folderName, checkPoint, topbarXpath); 23 | be.quit(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/com/netease/demo/GoogleSearch.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.demo; 18 | 19 | import com.netease.dagger.BrowserEmulator; 20 | 21 | /** 22 | * The demo to show Dagger's basic usage 23 | * @author ChenKan 24 | */ 25 | public class GoogleSearch { 26 | 27 | public static void main(String[] args) { 28 | 29 | String googleUrl = "http://www.google.com"; 30 | String searchBox = "//input[@name='q']"; 31 | String searchBtn = "//input[@name='btnK']"; 32 | BrowserEmulator be = new BrowserEmulator(); 33 | 34 | be.open(googleUrl); 35 | be.type(searchBox, "github"); 36 | be.click(searchBtn); 37 | be.expectTextExistOrNot(true, "https://github.com/", 5000); 38 | be.quit(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/com/netease/demo/GoogleSearchWithDataDriver.java: -------------------------------------------------------------------------------- 1 | package com.netease.demo; 2 | import java.io.*; 3 | import java.lang.reflect.Method; 4 | import java.util.Iterator; 5 | import java.util.Map; 6 | 7 | import org.testng.annotations.AfterClass; 8 | import org.testng.annotations.BeforeClass; 9 | import org.testng.annotations.DataProvider; 10 | import org.testng.annotations.Test; 11 | 12 | import com.netease.dagger.BrowserEmulator; 13 | import com.netease.datadriver.ExcelDataProvider; 14 | /** 15 | * 16 | * @author chenDoInG 17 | * 18 | */ 19 | public class GoogleSearchWithDataDriver { 20 | 21 | BrowserEmulator browserEmulator; 22 | 23 | @BeforeClass 24 | public void setUp() throws Exception { 25 | browserEmulator = new BrowserEmulator(); 26 | } 27 | @Test(dataProvider = "dp" ) 28 | public void search(Map data) { 29 | browserEmulator.open(data.get("url").trim()); 30 | browserEmulator.type(data.get("input1").trim(), data.get("name1").trim()); 31 | browserEmulator.click(data.get("button").trim()); 32 | browserEmulator.isTextPresent(data.get("expect").trim(), 3000); 33 | } 34 | 35 | @AfterClass(alwaysRun = true) 36 | public void tearDown() { 37 | browserEmulator.quit(); 38 | } 39 | @DataProvider(name = "dp") 40 | public Iterator dataFortestMethod(Method method) throws IOException { 41 | return new ExcelDataProvider(this.getClass().getName(),method.getName()); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/com/netease/demo/TestNg.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.demo; 18 | 19 | import org.testng.annotations.AfterClass; 20 | import org.testng.annotations.BeforeClass; 21 | import org.testng.annotations.DataProvider; 22 | import org.testng.annotations.Test; 23 | import com.netease.dagger.BrowserEmulator; 24 | 25 | /** 26 | * The demo to show TestNg's basic usage 27 | * @author ChenKan 28 | */ 29 | public class TestNg { 30 | 31 | String googleUrl = "http://www.google.com"; 32 | String searchBox = "//input[@name='q']"; 33 | String searchBtn = "//input[@name='btnK']"; 34 | BrowserEmulator be; 35 | 36 | @BeforeClass 37 | public void doBeforeClass() throws Exception { 38 | be = new BrowserEmulator(); 39 | } 40 | 41 | @Test(dataProvider = "data") 42 | public void doTest(String keyword, String result) { 43 | be.open(googleUrl); 44 | be.type(searchBox, keyword); 45 | be.click(searchBtn); 46 | be.expectTextExistOrNot(true, result, 5000); 47 | } 48 | 49 | @AfterClass(alwaysRun = true) 50 | public void doAfterClass() { 51 | be.quit(); 52 | } 53 | 54 | @DataProvider(name = "data") 55 | public Object[][] data() { 56 | return new Object[][] { 57 | { "java", "www.java.com" }, 58 | { "github", "https://github.com/" }, 59 | }; 60 | } 61 | } -------------------------------------------------------------------------------- /src/com/netease/demo/TestNgClone.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.demo; 18 | 19 | import org.testng.annotations.AfterClass; 20 | import org.testng.annotations.BeforeClass; 21 | import org.testng.annotations.DataProvider; 22 | import org.testng.annotations.Test; 23 | import com.netease.dagger.BrowserEmulator; 24 | 25 | /** 26 | * A clone of TestNg.java to show "Parallel Mode"
27 | * Please see https://github.com/NetEase/Dagger/wiki/Parallel-Mode 28 | * @author ChenKan 29 | */ 30 | public class TestNgClone { 31 | 32 | String googleUrl = "http://www.google.com"; 33 | String searchBox = "//input[@name='q']"; 34 | String searchBtn = "//input[@name='btnK']"; 35 | BrowserEmulator be; 36 | 37 | @BeforeClass 38 | public void doBeforeClass() throws Exception { 39 | be = new BrowserEmulator(); 40 | } 41 | 42 | @Test(dataProvider = "data") 43 | public void doTest(String keyword, String result) { 44 | be.open(googleUrl); 45 | be.type(searchBox, keyword); 46 | be.click(searchBtn); 47 | be.expectTextExistOrNot(true, result, 5000); 48 | } 49 | 50 | @AfterClass(alwaysRun = true) 51 | public void doAfterClass() { 52 | be.quit(); 53 | } 54 | 55 | @DataProvider(name = "data") 56 | public Object[][] data() { 57 | return new Object[][] { 58 | { "java", "www.java.com" }, 59 | { "github", "https://github.com/" }, 60 | }; 61 | } 62 | } -------------------------------------------------------------------------------- /src/com/netease/demo/TestSimpleFlexApp.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.demo; 18 | 19 | import org.testng.annotations.AfterClass; 20 | import org.testng.annotations.BeforeClass; 21 | import org.testng.annotations.Test; 22 | 23 | import com.netease.dagger.BrowserEmulator; 24 | import com.netease.dagger.LogTools; 25 | import com.netease.flexauto.FlexAutomation; 26 | 27 | /** 28 | * This demo shows how to use flex automation feature to test simple flex app 29 | * Detail wiki to be continued 30 | * 31 | * @author ddook007 32 | */ 33 | public class TestSimpleFlexApp { 34 | 35 | String flexUrl = "http://ddook007.github.io/demo/demo.html"; 36 | String flexId = "demo"; 37 | BrowserEmulator be; 38 | FlexAutomation fa; 39 | 40 | @BeforeClass 41 | public void doBeforeClass() throws Exception { 42 | be = new BrowserEmulator(); 43 | } 44 | 45 | @Test 46 | public void doTest() { 47 | be.open(flexUrl); 48 | fa = new FlexAutomation(be, flexId); 49 | // input 'flex automation is simple' 50 | fa.input(new String[] { "id", "input" }, "flex automation is simple"); 51 | // click the button, show the input in above label 52 | fa.click(new String[] { "id", "inputBtn" }); 53 | // what we input should be shown in the label 54 | fa.verifyProperty(new String[] { "id", "lb" }, "text", "flex automation is simple"); 55 | 56 | // get the button's value of label property 57 | LogTools.log("The label of the button is - " + fa.getProperty(new String[] { "id", "inputBtn" }, "label")); 58 | } 59 | 60 | @AfterClass(alwaysRun = true) 61 | public void doAfterClass() { 62 | be.quit(); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/com/netease/flexauto/FlexAutomation.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.flexauto; 18 | 19 | import org.openqa.selenium.JavascriptExecutor; 20 | import org.testng.Assert; 21 | 22 | import com.netease.dagger.BrowserEmulator; 23 | import com.netease.dagger.GlobalSettings; 24 | import com.netease.dagger.LogTools; 25 | 26 | /** 27 | * FlexAutomation enables operation and verification automation for embedded 28 | * flex application. The flex application needs to be re-compiled with some 29 | * libraries added. 30 | * 31 | * @author ddook007 32 | */ 33 | public class FlexAutomation { 34 | 35 | BrowserEmulator be; 36 | JavascriptExecutor js; 37 | String flashObjId; 38 | int stepInterval = Integer.parseInt(GlobalSettings.stepInterval); 39 | int pollingTimes = stepInterval == 0 ? 60 : Integer.parseInt(GlobalSettings.timeout) / stepInterval; 40 | 41 | /** 42 | * @param wrapper 43 | * who actually perform flash auto test 44 | * @param flashObjIdInHTML 45 | * id of target flash object in HTML 46 | */ 47 | public FlexAutomation(BrowserEmulator wrapper, String flashObjIdInHTML) { 48 | be = wrapper; 49 | js = (JavascriptExecutor) be.getBrowserCore(); 50 | flashObjId = flashObjIdInHTML; 51 | } 52 | 53 | /** 54 | * flex element locator using 1 or 2 prop:value pairs
55 | * 56 | * @param args 57 | * prop:value pairs String array 58 | * @return 59 | */ 60 | private String locatorGenerator(String[] args) { 61 | 62 | int argnum = args.length; 63 | switch (argnum) { 64 | // if so, 'automationName' is used as prop 65 | case 1: { 66 | String value = args[0]; 67 | return " value=\"" + value + "\" "; 68 | } 69 | 70 | // 1 pair of prop:value 71 | case 2: { 72 | String prop = args[0]; 73 | String value = args[1]; 74 | return " prop=\"" + prop + "\" value=\"" + value + "\" "; 75 | } 76 | 77 | // 2 pairs of prop:value, first pair from target flex element and second 78 | // from its ancestor 79 | case 4: { 80 | 81 | String prop = args[0]; 82 | String value = args[1]; 83 | String containerProp = args[2]; 84 | String containerValue = args[3]; 85 | return " prop=\"" + prop + "\" value=\"" + value + "\" " + "containerProp=\"" + containerProp + "\" containerValue=\"" + containerValue + "\" "; 86 | } 87 | 88 | default: { 89 | Assert.fail("flex element locator generating failed with provided String array:" + args); 90 | return null; 91 | } 92 | 93 | } 94 | } 95 | 96 | /** 97 | * polling the js command communicating with the target flex application 98 | * 99 | * @param automationJS 100 | * the js command 101 | */ 102 | private void pollingCommand(String automationJS) { 103 | if (GlobalSettings.browserCoreType != 3) { 104 | // when tests run in parallel, 105 | // "This SWF is no longer connected to the FlexMonkey console" alert 106 | // will be seen. 107 | // this annoying alert has no title, using this to handle 108 | String checkcmd = "return document.getElementById('" + flashObjId + "').getForSelenium('','')"; 109 | String clickcmd = "return document.getElementById('" + flashObjId + "').verifyFromSelenium('','')"; 110 | String title = ""; 111 | for (int t = 0;; t++) { 112 | try { 113 | Thread.sleep(stepInterval); 114 | } catch (InterruptedException e) { 115 | e.printStackTrace(); 116 | } 117 | if (t >= pollingTimes) 118 | Assert.fail("flex automation failed to perform the following operation in time: " + automationJS); 119 | try { 120 | title = (String) js.executeScript(checkcmd); 121 | if (title.equals("")) 122 | js.executeScript(clickcmd); 123 | boolean r = (Boolean) js.executeScript(automationJS); 124 | if (r) 125 | break; 126 | } catch (Exception e) { 127 | } 128 | 129 | } 130 | } else { 131 | // where there's IE there's if-else 132 | // TODO 133 | 134 | } 135 | } 136 | 137 | /** 138 | * verify if value of specified flex element's prop is expected
139 | * 140 | * @param locatorArgs 141 | * @param propertyString 142 | * @param expectedValue 143 | */ 144 | public void verifyProperty(String[] locatorArgs, String propertyString, String expectedValue) { 145 | 146 | String locator = locatorGenerator(locatorArgs); 147 | String flexmonkeyCommand = ""; 148 | String automationJS = "return document.getElementById('" + flashObjId + "')." + "verifyFromSelenium('" + flexmonkeyCommand + "','')"; 149 | pollingCommand(automationJS); 150 | // avoiding unnecessary logging 151 | if ("initialized".equals(propertyString) && "true".equals(expectedValue)) 152 | return; 153 | LogTools.log("flex element: " + locator + " is as expected in prop:value - " + propertyString + ":\"" + expectedValue + "\""); 154 | 155 | } 156 | 157 | /** 158 | * get the value of specified property of the flex element
159 | * 160 | * @param locatorArgs 161 | * @param propertyString 162 | * @return 163 | */ 164 | public String getProperty(String[] locatorArgs, String propertyString) { 165 | String getR = ""; 166 | String locator = locatorGenerator(locatorArgs); 167 | String flexmonkeyCommand = ""; 168 | String automationJS = "return document.getElementById('" + flashObjId + "')." + "getForSelenium('" + flexmonkeyCommand + "','')"; 169 | // verifyProperty(locatorArgs, "initialized", "true"); 170 | 171 | try { 172 | getR = (String) js.executeScript(automationJS); 173 | } catch (Exception e) { 174 | e.printStackTrace(); 175 | Assert.fail("flex automation failed to perform the following operation: " + automationJS); 176 | } 177 | 178 | return getR; 179 | } 180 | 181 | /** 182 | * click operation 183 | * 184 | * @param locatorArgs 185 | * the prop-value pair String array used to locate the flex 186 | * element 187 | */ 188 | public void click(String[] locatorArgs) { 189 | String locator = locatorGenerator(locatorArgs); 190 | String flexmonkeyCommand = ""; 191 | String automationJS = "return document.getElementById('" + flashObjId + "')." + "verifyFromSelenium('" + flexmonkeyCommand + "','')"; 192 | 193 | verifyProperty(locatorArgs, "initialized", "true"); 194 | LogTools.log("clicking flex element: " + locator); 195 | pollingCommand(automationJS); 196 | LogTools.log("click flex element: " + locator + " finished"); 197 | 198 | } 199 | 200 | /** 201 | * input in a text area 202 | * 203 | * @param locatorArgs 204 | * prop-value pair String array used to locate the flex element 205 | * @param inputValue 206 | * whatever you want 207 | */ 208 | public void input(String[] locatorArgs, String inputValue) { 209 | String locator = locatorGenerator(locatorArgs); 210 | String flexmonkeyCommand = ""; 211 | String automationJS = "return document.getElementById('" + flashObjId + "')." + "verifyFromSelenium('" + flexmonkeyCommand + "','')"; 212 | verifyProperty(locatorArgs, "initialized", "true"); 213 | LogTools.log("inputing in flex element: " + locator + " with \"" + inputValue + "\""); 214 | pollingCommand(automationJS); 215 | LogTools.log("input in flex element: " + locator + " finished"); 216 | 217 | } 218 | 219 | /** 220 | * select operation for TabBar etc. 221 | * 222 | * @param tabarLocatorArgs 223 | * prop-value pair String array used to locate the tab bar 224 | * @param tabLocatorArgs 225 | * prop-value pair String array used to locate the very tab 226 | */ 227 | public void select(String[] tabarLocatorArgs, String[] tabLocatorArgs) { 228 | String tabarLocator = locatorGenerator(tabarLocatorArgs); 229 | String tabLocator = locatorGenerator(tabLocatorArgs); 230 | String flexmonkeyCommand = ""; 231 | String automationJS = "return document.getElementById('" + flashObjId + "')." + "verifyFromSelenium('" + flexmonkeyCommand + "','')"; 232 | verifyProperty(tabarLocatorArgs, "initialized", "true"); 233 | verifyProperty(tabLocatorArgs, "initialized", "true"); 234 | LogTools.log("selecting in: " + tabarLocator + "for tab: " + tabLocator + ""); 235 | pollingCommand(automationJS); 236 | LogTools.log("select finished"); 237 | 238 | } 239 | 240 | } 241 | -------------------------------------------------------------------------------- /src/com/netease/imagecheck/ImageContrast.java: -------------------------------------------------------------------------------- 1 | package com.netease.imagecheck; 2 | 3 | import java.awt.image.BufferedImage; 4 | import java.io.File; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | import javax.imageio.ImageIO; 10 | import org.openqa.selenium.Dimension; 11 | import org.openqa.selenium.Point; 12 | import org.openqa.selenium.WebElement; 13 | import com.sun.image.codec.jpeg.JPEGCodec; 14 | import com.sun.image.codec.jpeg.JPEGImageEncoder; 15 | 16 | /** 17 | * ImageContrast changes image from RGB to LAB and make comparison 18 | * @author LingFei 19 | */ 20 | @SuppressWarnings("restriction") 21 | public class ImageContrast { 22 | 23 | private static Point wePosition; 24 | private static Dimension weDim; 25 | 26 | /** 27 | * Change RGB to XYZ 28 | * 29 | * @param rgb 30 | * @return xyz 31 | */ 32 | public static ColorXYZ rgb2xyz(int rgb) { 33 | ColorXYZ xyz = new ColorXYZ(); 34 | int r = (rgb & 0xff0000) >> 16; 35 | int g = (rgb & 0xff00) >> 8; 36 | int b = (rgb & 0xff); 37 | if ((r == 0) && (g == 0) && (b == 0)) { 38 | xyz.x = 0; 39 | xyz.y = 0; 40 | xyz.z = 0; 41 | } else { 42 | xyz.x = (0.490 * r + 0.310 * g + 0.200 * b) / (0.667 * r + 1.132 * g + 1.200 * b); 43 | xyz.y = (0.117 * r + 0.812 * g + 0.010 * b) / (0.667 * r + 1.132 * g + 1.200 * b); 44 | xyz.z = (0.000 * r + 0.010 * g + 0.990 * b) / (0.667 * r + 1.132 * g + 1.200 * b); 45 | } 46 | return xyz; 47 | } 48 | 49 | /** 50 | * Change XYZ to LAB 51 | * 52 | * @param xyz 53 | * @return lab 54 | */ 55 | public static ColorLAB xyz2lab(ColorXYZ xyz) { 56 | ColorLAB lab = new ColorLAB(); 57 | double x = xyz.x / 95.047; 58 | double y = xyz.y / 100.000; 59 | double z = xyz.z / 108.883; 60 | x = (x > 0.008856) ? Math.pow(x, 1.0 / 3.0) : (7.787 * x + 16.0 / 116); 61 | y = (y > 0.008856) ? Math.pow(y, 1.0 / 3.0) : (7.787 * y + 16.0 / 116); 62 | z = (z > 0.008856) ? Math.pow(z, 1.0 / 3.0) : (7.787 * z + 16.0 / 116); 63 | lab.l = 116 * Math.pow(y, 1.0 / 3.0) - 16; 64 | lab.a = 500 * (Math.pow(x, 1.0 / 3.0) - Math.pow(y, 1.0 / 3.0)); 65 | lab.b = 200 * (Math.pow(y, 1.0 / 3.0) - Math.pow(z, 1.0 / 3.0)); 66 | return lab; 67 | } 68 | 69 | /** 70 | * Calculate the color difference 71 | * 72 | * @param lab1 73 | * @param lab2 74 | * @return totalColorDifference 75 | */ 76 | public static double getDelta(ColorLAB lab1, ColorLAB lab2) { 77 | double deltaL = lab1.l - lab2.l; // lightness difference 78 | double deltaA = lab1.a - lab2.a; // chromaticity difference 79 | double deltaB = lab1.b - lab2.b; // chromaticity difference 80 | return Math.pow((Math.pow(deltaL, 2) + Math.pow(deltaA, 2) + Math.pow(deltaB, 2)), 0.5); // total color difference 81 | } 82 | 83 | /** 84 | * Contrast images 85 | * 86 | * @param sampleImagePath 87 | * @param actualImagePath 88 | * @param differenceImagePath 89 | */ 90 | public static boolean contrastImages(String sampleImagePath, String actualImagePath, String differenceImagePath, WebElement we) throws Exception { 91 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss"); 92 | String time = sdf.format(new Date()); 93 | BufferedImage sample = null, actual = null, difference = null; 94 | wePosition = we.getLocation(); 95 | weDim = we.getSize(); 96 | int left = wePosition.getX(); 97 | int top = wePosition.getY(); 98 | Integer width = weDim.getWidth(); 99 | Integer height = weDim.getHeight(); 100 | try { 101 | sample = ImageIO.read(new File(sampleImagePath + ".png")); 102 | actual = ImageIO.read(new File(actualImagePath)); 103 | } catch (IOException e) { 104 | e.printStackTrace(); 105 | } 106 | difference = new BufferedImage(sample.getWidth(), sample.getHeight(), BufferedImage.TYPE_INT_RGB); 107 | boolean isMatched = true; 108 | for (int y = top; y < top + height; ++y) { 109 | for (int x = left; x < left + width; ++x) { 110 | int expRGB = sample.getRGB(x, y); 111 | int actRGB = actual.getRGB(x, y); 112 | int newRGB = actRGB; 113 | if (expRGB != actRGB) { 114 | double deltaE = getDelta(xyz2lab(rgb2xyz(expRGB)), xyz2lab(rgb2xyz(actRGB))); 115 | if (deltaE > Settings.maxColorThreshold) { 116 | newRGB = 0xff0000;// set red in difference place 117 | } 118 | isMatched = false; 119 | } 120 | difference.setRGB(x, y, newRGB); 121 | } 122 | } 123 | FileOutputStream out = null; 124 | if (!isMatched) { 125 | try { 126 | out = new FileOutputStream(Settings.contrastImagePath + differenceImagePath + time + ".png"); 127 | JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); 128 | encoder.encode(difference); 129 | } catch (IOException e) { 130 | e.printStackTrace(); 131 | } finally { 132 | out.close(); 133 | System.err.println("UIstyle wrong!" + Settings.contrastImagePath + differenceImagePath + time + ".png"); 134 | } 135 | } 136 | return isMatched; 137 | } 138 | 139 | /** 140 | * RGB color space 141 | * @author LingFei 142 | */ 143 | public static class ColorLAB { 144 | public double l; 145 | public double a; 146 | public double b; 147 | } 148 | 149 | /** 150 | * XYZ color space 151 | * @author LingFei 152 | */ 153 | public static class ColorXYZ { 154 | public double x; 155 | public double y; 156 | public double z; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/com/netease/imagecheck/ImageProcess.java: -------------------------------------------------------------------------------- 1 | package com.netease.imagecheck; 2 | 3 | import java.awt.AWTException; 4 | import java.awt.Robot; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import org.apache.commons.io.FileUtils; 8 | import org.openqa.selenium.By; 9 | import org.openqa.selenium.Dimension; 10 | import org.openqa.selenium.OutputType; 11 | import org.openqa.selenium.Point; 12 | import org.openqa.selenium.TakesScreenshot; 13 | import org.openqa.selenium.WebDriver; 14 | import org.openqa.selenium.WebElement; 15 | import org.openqa.selenium.remote.Augmenter; 16 | import org.openqa.selenium.support.ui.ExpectedConditions; 17 | import org.openqa.selenium.support.ui.WebDriverWait; 18 | 19 | /** 20 | * ImageProcess contains APIs for screen shot and image process 21 | * @author LingFei 22 | */ 23 | public class ImageProcess { 24 | 25 | /** 26 | * ScreenShot 27 | * 28 | * @param augmentedDriver 29 | * @param folderName 30 | * @param imageName 31 | * @param type 32 | * @return path 33 | */ 34 | public static String screenShot(WebDriver augmentedDriver, String folderName, String imageName) { 35 | Robot rb = null; 36 | try { 37 | rb = new Robot(); 38 | } catch (AWTException e) { 39 | e.printStackTrace(); 40 | } 41 | rb.mouseMove(0, 0);// move mouse to top left corner, for eliminating some effects of hover in pages 42 | 43 | String dirName = null; 44 | if (Settings.screenShotType == 1) { 45 | dirName = Settings.sampleImagePath + folderName; 46 | } else if (Settings.screenShotType == 2) { 47 | dirName = Settings.contrastImagePath + folderName; 48 | } else { 49 | System.err.println("Wrong type!Please check type.properties!"); 50 | } 51 | if (Settings.browserCoreType == 1 || Settings.browserCoreType == 3) { 52 | augmentedDriver.manage().window().setPosition(new Point(0, 0)); 53 | augmentedDriver.manage().window().setSize(new Dimension(9999, 9999)); 54 | } else if (Settings.browserCoreType == 2) { 55 | augmentedDriver = new Augmenter().augment(augmentedDriver); 56 | } else { 57 | System.err.println("Wrong type!Please check imagecheck.properties!"); 58 | } 59 | try { 60 | File sourceFile = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.FILE); 61 | FileUtils.copyFile(sourceFile, new File(dirName + File.separator + imageName + ".png")); 62 | } catch (IOException e) { 63 | e.printStackTrace(); 64 | } 65 | return dirName + File.separator + imageName + ".png"; 66 | } 67 | 68 | /** 69 | * Clear images 70 | * 71 | * @param dir 72 | */ 73 | public static void deleteFiles(String dir) { 74 | try { 75 | File file = new File(dir); 76 | if (!file.exists()) { 77 | return; 78 | } 79 | if (!file.isDirectory()) { 80 | return; 81 | } 82 | String[] tempList = file.list(); 83 | File temp = null; 84 | for (int i = 0; i < tempList.length; i++) { 85 | if (dir.endsWith(File.separator)) { 86 | temp = new File(dir + tempList[i]); 87 | } else { 88 | temp = new File(dir + File.separator + tempList[i]); 89 | } 90 | if (temp.isFile()) { 91 | temp.delete(); 92 | } 93 | } 94 | } catch (Exception e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | 99 | /** 100 | * Image process 101 | * 102 | * @param driver 103 | * @param folderName 104 | * @param checkPoint 105 | * @param xpath 106 | */ 107 | public static void process(WebDriver driver, String folderName, String checkPoint, String xpath) throws Exception { 108 | WebDriverWait wait = new WebDriverWait(driver, 10); 109 | WebElement we = wait.until(ExpectedConditions.elementToBeClickable(By.xpath(xpath))); 110 | we = findElement(driver, By.xpath(xpath)); 111 | String sampleImage = checkPoint + "_sample"; 112 | String differenceImage = checkPoint + "_difference"; 113 | String actualImage = null; 114 | if (Settings.screenShotType == 1) { 115 | actualImage = ImageProcess.screenShot(driver, folderName, sampleImage); 116 | } else if (Settings.screenShotType == 2) { 117 | actualImage = ImageProcess.screenShot(driver, folderName, checkPoint); 118 | ImageContrast.contrastImages(Settings.sampleImagePath + folderName + File.separator + sampleImage, actualImage, folderName + File.separator + differenceImage, we); 119 | } else { 120 | System.err.println("Wrong type!Please check type.properties!"); 121 | } 122 | } 123 | 124 | private static WebElement findElement(WebDriver driver, By by) { 125 | WebElement webElement = null; 126 | try { 127 | webElement = driver.findElement(by); 128 | } catch (Exception e) { 129 | System.err.println(by.toString() + " can not be found!"); 130 | } 131 | return webElement; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/com/netease/imagecheck/Settings.java: -------------------------------------------------------------------------------- 1 | package com.netease.imagecheck; 2 | 3 | import java.io.FileInputStream; 4 | import java.util.Properties; 5 | 6 | /** 7 | * Parameter settings 8 | * @author LingFei 9 | */ 10 | public class Settings { 11 | 12 | public static Properties prop = getProperties(); 13 | 14 | public static int screenShotType = Integer.parseInt(prop.getProperty("ScreenShotType", "1")); 15 | 16 | public static String sampleImagePath = prop.getProperty("SampleImagePath", "res/samples/"); 17 | 18 | public static String contrastImagePath = prop.getProperty("ContrastImagePath", "images/"); 19 | 20 | public static double maxColorThreshold = Double.parseDouble(prop.getProperty("MaxColorThreshold", "0")); 21 | 22 | public static int browserCoreType = Integer.parseInt(prop.getProperty("BrowserCoreType", "2")); 23 | 24 | public static String getProperty(String property) { 25 | return prop.getProperty(property); 26 | } 27 | 28 | public static Properties getProperties() { 29 | Properties prop = new Properties(); 30 | try { 31 | FileInputStream file = new FileInputStream("imagecheck.properties"); 32 | prop.load(file); 33 | file.close(); 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | return prop; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/com/netease/test/ClickOperations.java: -------------------------------------------------------------------------------- 1 | package com.netease.test; 2 | 3 | import org.testng.annotations.AfterClass; 4 | import org.testng.annotations.BeforeClass; 5 | import org.testng.annotations.Test; 6 | 7 | import com.netease.dagger.BrowserEmulator; 8 | 9 | /** 10 | * click的一系列测试 11 | * @author WeiYating 12 | */ 13 | public class ClickOperations { 14 | 15 | BrowserEmulator be; 16 | String input = "hello"; 17 | 18 | @BeforeClass 19 | public void doBeforeClass() { 20 | be = new BrowserEmulator(); 21 | CommonFunction.openCaptain(be); 22 | } 23 | 24 | @Test 25 | public void clickOperations() { 26 | CommonFunction.clickOperations(be); 27 | } 28 | 29 | @Test(dependsOnMethods = "clickOperations") 30 | public void clickButton() { 31 | CommonFunction.clickButton(be); 32 | } 33 | 34 | @Test(dependsOnMethods = "clickButton") 35 | public void clickLink() { 36 | CommonFunction.clickLink(be); 37 | } 38 | 39 | @Test(dependsOnMethods = "clickLink") 40 | public void submitForm() { 41 | CommonFunction.submitForm(be, input); 42 | } 43 | 44 | @AfterClass(alwaysRun = true) 45 | public void doAfterClass() { 46 | be.quit(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/com/netease/test/CommonFunction.java: -------------------------------------------------------------------------------- 1 | package com.netease.test; 2 | 3 | import org.openqa.selenium.JavascriptExecutor; 4 | import org.testng.Assert; 5 | 6 | import com.netease.dagger.BrowserEmulator; 7 | 8 | /** 9 | * SanityTest 公共操作 10 | * 11 | * @author WeiYating 12 | */ 13 | public class CommonFunction { 14 | 15 | public static String ip = "10.240.156.4"; 16 | public static String port = "3001"; 17 | 18 | /** 19 | * 打开页面Captain 20 | * 21 | * @param be 22 | * @param ip 23 | * @param port 24 | */ 25 | public static void openCaptain(BrowserEmulator be) { 26 | be.open("http://" + ip + ":" + port + "/welcome"); 27 | be.expectElementExistOrNot(true, 28 | "//h1[text()='Dagger测试系统 - SanityTest']", 5000); 29 | } 30 | 31 | /** 32 | * 打开网页 33 | * 34 | * @param be 35 | */ 36 | public static void openURL(BrowserEmulator be) { 37 | be.open("http://" + ip + ":" + port + "/open"); 38 | be.expectElementExistOrNot(true, 39 | "//h1[contains(text(),'BrowserEmulator.open')]", 5000); 40 | } 41 | 42 | // TODO 43 | // /** 44 | // * 网页超时 45 | // * @param be 46 | // */ 47 | // public static void overTime(BrowserEmulator be) { 48 | // be.click("//a[contains(text(),'BrowserEmulator.open')]"); 49 | // be.expectElementExistOrNot(true, 50 | // "//div[@id='errorTitle']/h1[text()='无法连接']", 8000); 51 | // } 52 | 53 | /** 54 | * 点击click链接,进入click操作页面 55 | * 56 | * @param be 57 | */ 58 | public static void clickOperations(BrowserEmulator be) { 59 | be.click("//a[contains(text(),'BrowserEmulator.click')]"); 60 | be.expectElementExistOrNot(true, 61 | "//h1[contains(text(),' BrowserEmulator.click')]", 5000); 62 | } 63 | 64 | /** 65 | * 点击Button按钮 66 | * 67 | * @param be 68 | */ 69 | public static void clickButton(BrowserEmulator be) { 70 | be.click("//input[@value='点击button,触发Ajax']"); 71 | be.expectElementExistOrNot(true, "//h1[(text()='点击了button,触发了Ajax')]", 72 | 10000); 73 | } 74 | 75 | /** 76 | * 点击链接 77 | * 78 | * @param be 79 | */ 80 | public static void clickLink(BrowserEmulator be) { 81 | be.click("//a[(text()='点击节点,页面跳转')]"); 82 | be.expectElementExistOrNot(true, 83 | "//h1[text()='Dagger测试系统 - SanityTest']", 5000); 84 | be.click("//a[contains(text(),'BrowserEmulator.click')]"); 85 | } 86 | 87 | /** 88 | * 输入内容,点击Button提交表单 89 | * 90 | * @param be 91 | * @param input 92 | */ 93 | public static void submitForm(BrowserEmulator be, String input) { 94 | be.type("//input[@value='在此输入内容']", input); 95 | be.click("//input[@value='点击,提交表单']"); 96 | be.expectElementExistOrNot(true, "//h1[text()='" + input + "']", 5000); 97 | } 98 | 99 | // TODO 100 | // /** 101 | // * 点击不可点击部分 102 | // * @param be 103 | // */ 104 | // public static void clickNothing(BrowserEmulator be){ 105 | // be.click(""); 106 | // be.expectElementExistOrNot(true, 107 | // "//h1[text()='本页面用于测试 - BrowserEmulator.click(String locator)']", 5000); 108 | // } 109 | 110 | /** 111 | * 点击type链接,进入type操作页面 112 | * 113 | * @param be 114 | */ 115 | public static void typeOperations(BrowserEmulator be) { 116 | be.click("//a[contains(text(),'BrowserEmulator.type')]"); 117 | be.expectElementExistOrNot(true, 118 | "//h1[contains(text(),'BrowserEmulator.type')]", 5000); 119 | } 120 | 121 | /** 122 | * 在input中输入文本 123 | * 124 | * @param be 125 | * @param input 126 | */ 127 | public static void typeInInput(BrowserEmulator be, String input) { 128 | be.type("//input[@value='在输入文本']", input); 129 | be.click("//input[@value='提交节点文本']"); 130 | be.expectElementExistOrNot(true, "//h1[text()='" + input + "']", 10000); 131 | be.open("http://" + ip + ":" + port + "/type"); 132 | } 133 | 134 | /** 135 | * 在iframe中输入文本 136 | * 137 | * @param be 138 | * @param input 139 | */ 140 | public static void typeInIframe(BrowserEmulator be, String input) { 141 | be.type("//body/iframe", input); 142 | be.click("//input[@value='提交