├── .gitignore ├── .travis.yml ├── Nakal.png ├── README.md ├── android └── mask_images │ ├── htc10.png │ ├── nexus5.png │ ├── oneplus.png │ ├── onepluspng.xcf │ └── samsung5.png ├── difference_HomeScreen1.png ├── ios └── mask_images │ ├── iphone5.png │ └── iphone6.png ├── iosTest ├── nakal.yaml ├── pom.xml ├── src ├── main │ └── java │ │ └── com │ │ └── nakal │ │ ├── ScreenExecutor │ │ ├── Configuration.java │ │ ├── ExecutorService.java │ │ ├── NakalAttributeValidator.java │ │ └── NakalExecutor.java │ │ ├── capturescreen │ │ └── ScreenShooter.java │ │ ├── devices │ │ ├── AndroidDeviceScreen.java │ │ ├── DeviceInterface.java │ │ ├── ViewFactory.java │ │ └── iOSDeviceScreen.java │ │ ├── imageutil │ │ └── ImageUtil.java │ │ └── utils │ │ ├── CommandPrompt.java │ │ ├── InstallMobileDevice.java │ │ ├── ScreenPaths.java │ │ ├── Utils.java │ │ └── YamlReader.java └── test │ └── java │ └── com │ └── nakal │ └── screen │ ├── AndroidTest.java │ ├── NakalTest.java │ └── iOSTest.java └── testImages ├── ActivityScreen.png ├── ActivityScreen1.png ├── actual.png ├── googleActual.png ├── googleExpected.png └── resized_picture.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | /target/* 3 | ios/actual_images/* 4 | ios/baseline_images/* 5 | android/actual_images/* 6 | android/baseline_images/* 7 | android/native/* 8 | ?img? 9 | Desktop/* 10 | android/web/* 11 | testImages/* 12 | nakal_java.iml 13 | src/main/java/com/nakal/capturescreen/Sample.java 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | os: 3 | - linux 4 | sudo: enabled 5 | dist: trusty 6 | before_script: 7 | - rm -rf ImageMagick* 8 | - sudo apt-get install build-essential checkinstall && sudo apt-get build-dep imagemagick -y 9 | - wget http://www.imagemagick.org/download/ImageMagick.tar.gz 10 | - tar xzvf ImageMagick.tar.gz 11 | - cd "$(ls|grep ImageMagick-)" 12 | - pwd 13 | - ./configure && make 14 | - sudo checkinstall -y 15 | - sudo ldconfig /usr/local/lib 16 | - cd .. 17 | 18 | script: 19 | - mvn clean -Dtest=NakalTest test 20 | -------------------------------------------------------------------------------- /Nakal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/Nakal.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | nakal 4 |
5 |
6 |
7 |

8 | 9 | [![Build Status](https://travis-ci.org/saikrishna321/nakal_java.svg?branch=master)](https://travis-ci.org/saikrishna321/nakal_java) 10 | 11 | Automated visual testing of Android/iOS/Web applications. 12 | 13 | ![ScreenShot](https://raw.githubusercontent.com/saikrishna321/nakal_java/master/difference_HomeScreen1.png) 14 | 15 | Nakal is used to add visual validations in your existing test framework (using appium or selenium-webDriver etc). 16 | 17 | ## Installation 18 | You need to install [imagemagick](http://www.imagemagick.org/script/index.php) on your machine 19 | 20 | Add this to your pom.xml 21 | ``` 22 | 23 | 24 | jitpack.io 25 | https://jitpack.io 26 | 27 | 28 | 29 | 30 | com.github.saikrishna321 31 | nakal_java 32 | f4dcb0054e 33 | 34 | ``` 35 | 36 | ## Usage 37 | 38 | Following mandatory properties need to be set before running test: 39 | * nakal.platform=ios/android 40 | * nakal.maskimage=image which needs to be considered for masking 41 | * nakal.mode=build/compare 42 | 43 | For Example: 44 | 45 | ``` 46 | NakalExecutor nakalExecutor = new NakalExecutor(); 47 | //Compare mobile native app screen 48 | @Test 49 | public void compareImagesExecutor(){ 50 | Assert.assertTrue(nakalExecutor.nakalExecutorNativeCompare("HomeScreen")); 51 | } 52 | //Compare mobile-web/desktop-browser app screen 53 | @Test 54 | public void compareImagesExecutor(){ 55 | Assert.assertTrue(nakalExecutor.nakalExecutorWebCompare(driver, "GoogleScreen")); 56 | } 57 | ``` 58 | 59 | 60 | 1. Now, execute your test by passing env variable NAKAL_MODE=build to build the baseline images. All baseline images will be stored in baseline_images folder in current directory 61 | 62 | 2. Once baseline is built, next execution onwards, start using environment variable NAKAL_MODE=compare to compare against baseline. 63 | any difference will be put in the same directory with image file named "difference_current_screen_name.png" 64 | 65 | All mask images should and be stored at /ios/mask-images/fileName.png /android/mask-images/fileName.png.(mask images with transparent background has been created using gimp tool- eg:https://github.com/saikrishna321/nakal_java/tree/master/android/mask_images) 66 | MASKIMAGE value in the env should be the same fileName which is stored under the mask_images(For ex: ./mask_images/nexus5.png and MASKIMAGE="nexus5"). 67 | 68 | #Running the tests 69 | 70 | mvn clean -Dtest=AndroidTest test -Dnakal.platform=android -Dnakal.mode=build -Dnakal.maskimage=nexus5 (captures a baseline image) 71 | 72 | mvn clean -Dtest=AndroidTest test -Dnakal.platform=android -Dnakal.mode=compare -Dnakal.maskimage=nexus5 ( compares expected and actual image) 73 | 74 | ## Ignore certain regions of the image 75 | 76 | 1. Create nakal.yaml under the root directory. 77 | 2. You can specify the areas of a screen you want to mask/ignore while comparing in nakal.yaml as below 78 | 79 | ``` 80 | nexus5: 81 | HomeScreen: {mask_region_1: [69, 441, 357, 553],mask_region_2: [50, 1600, 371, 1652]} 82 | SearchScreen: {mask_region_1: [66,424,340,478],mask_region_2: [76,524,440,578]} 83 | ``` 84 | 3.If you want to set certain threshold while comparing. You can pass option as: 85 | 86 | ``` 87 | nakalExecutor.nakalExecutorNativeCompare("HomeScreen",3) 88 | ``` 89 | 90 | 91 |

Ruby Client

92 | https://github.com/rajdeepv/nakal 93 | 94 | 95 | ## Contributing 96 | 97 | 1. Fork it ( http://github.com//nakal_java/fork ) 98 | 2. Create your feature branch (`git checkout -b my-new-feature`) 99 | 3. Commit your changes (`git commit -am 'Add some feature'`) 100 | 4. Push to the branch (`git push origin my-new-feature`) 101 | 5. Create new Pull Request 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /android/mask_images/htc10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/android/mask_images/htc10.png -------------------------------------------------------------------------------- /android/mask_images/nexus5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/android/mask_images/nexus5.png -------------------------------------------------------------------------------- /android/mask_images/oneplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/android/mask_images/oneplus.png -------------------------------------------------------------------------------- /android/mask_images/onepluspng.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/android/mask_images/onepluspng.xcf -------------------------------------------------------------------------------- /android/mask_images/samsung5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/android/mask_images/samsung5.png -------------------------------------------------------------------------------- /difference_HomeScreen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/difference_HomeScreen1.png -------------------------------------------------------------------------------- /ios/mask_images/iphone5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/ios/mask_images/iphone5.png -------------------------------------------------------------------------------- /ios/mask_images/iphone6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/ios/mask_images/iphone6.png -------------------------------------------------------------------------------- /iosTest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/iosTest -------------------------------------------------------------------------------- /nakal.yaml: -------------------------------------------------------------------------------- 1 | common: &common 2 | [17, 71, 850, 174] 3 | 4 | oneplus: 5 | Login: {region_1: *common, region_2:[180, 900, 900, 1008]} 6 | 7 | nexus5: 8 | Login: {mask_region_1: [69, 441, 357, 553],mask_region_2: [50, 1600, 371, 1652]} 9 | SearchScreen: {mask_region_1: [66,424,340,478],mask_region_2: [76,524,440,578]} 10 | 11 | iphone6: 12 | Login: {mask_region_1: [69, 441, 357, 553],mask_region_2: [50, 1600, 371, 1652]} 13 | SearchScreen: {mask_region_1: [66,424,340,478],mask_region_2: [76,524,440,578]} 14 | 15 | fuzzPercentage: 5.00 16 | fullScreen: false 17 | 18 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | nakal_java 8 | nakal_java 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 1.8 17 | 1.8 18 | 19 | 20 | 21 | 22 | 23 | 24 | com.github.SrinivasanTarget 25 | DeviceManager 26 | 802c543a22 27 | 28 | 29 | com.github.cosysoft 30 | device-api 31 | 0.9.1 32 | 33 | 34 | junit 35 | junit 36 | 4.12 37 | 38 | 39 | 40 | org.im4java 41 | im4java 42 | 1.4.0 43 | 44 | 45 | 46 | org.yaml 47 | snakeyaml 48 | 1.17 49 | 50 | 51 | 52 | 53 | jitpack.io 54 | https://jitpack.io 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/ScreenExecutor/Configuration.java: -------------------------------------------------------------------------------- 1 | package com.nakal.ScreenExecutor; 2 | 3 | /** 4 | * Created by hiteshs on 2/20/18. 5 | */ 6 | public class Configuration { 7 | public static String nakalMode = System.getProperty("nakal.mode", "compare"); 8 | public static String platform = System.getProperty("nakal.platform", "android"); 9 | public static String maskImage = System.getProperty("nakal.maskimage"); 10 | public static String screenshotFolder= System.getProperty("nakal.screenshot.folder"); 11 | public static String baseDirectory= System.getProperty("user.dir"); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/ScreenExecutor/ExecutorService.java: -------------------------------------------------------------------------------- 1 | package com.nakal.ScreenExecutor; 2 | 3 | import com.nakal.capturescreen.ScreenShooter; 4 | import com.nakal.imageutil.ImageUtil; 5 | import com.nakal.utils.ScreenPaths; 6 | import org.im4java.core.IM4JavaException; 7 | import java.io.File; 8 | import java.io.FileNotFoundException; 9 | import java.io.IOException; 10 | import static com.nakal.ScreenExecutor.NakalAttributeValidator.isFullScreen; 11 | import static com.nakal.ScreenExecutor.NakalAttributeValidator.isIgnoreRegion; 12 | 13 | /** 14 | * Created by hiteshs on 2/25/18. 15 | */ 16 | public class ExecutorService { 17 | private ScreenPaths screenPaths; 18 | private File file; 19 | private ImageUtil imageUtil; 20 | public ExecutorService(){ 21 | imageUtil = new ImageUtil(); 22 | } 23 | 24 | public boolean compareMode(String baseLineImageName,int threshold) 25 | throws InterruptedException, IOException, IM4JavaException { 26 | screenPaths = new ScreenPaths(baseLineImageName); 27 | String actualImage= screenPaths.getActualImage(); 28 | String expectedImage=screenPaths.getExpectedImage(); 29 | String diffImage=screenPaths.getDiffImage(); 30 | screenCaptureAndMaskIfExist(baseLineImageName,actualImage); 31 | if (threshold >0 && imageUtil.compareImages(expectedImage, 32 | actualImage,diffImage )) { 33 | return true; 34 | }else{ 35 | if (imageUtil.compareImages(expectedImage, 36 | actualImage, diffImage,threshold)) 37 | return true; 38 | } 39 | mergerDiffHorizontal(expectedImage,actualImage,diffImage); 40 | return false; 41 | } 42 | public boolean buildMode(String baseLineImageName) 43 | throws InterruptedException, IOException, IM4JavaException { 44 | screenPaths = new ScreenPaths(baseLineImageName); 45 | String expectedImage= screenPaths.getExpectedImage(); 46 | screenCaptureAndMaskIfExist(baseLineImageName,expectedImage); 47 | return checkIfScreenshotsCaptured(expectedImage); 48 | } 49 | private boolean checkIfScreenshotsCaptured(String image) 50 | throws FileNotFoundException { 51 | File expectedImage= new File(image); 52 | return expectedImage.exists(); 53 | 54 | } 55 | private void screenCaptureAndMaskIfExist(String baseLineImageName, 56 | String image) 57 | throws IOException, InterruptedException, IM4JavaException { 58 | captureScreen(image); 59 | applyMaskIfPresent(baseLineImageName,image); 60 | 61 | } 62 | private void applyMaskIfPresent(String baseLineImageName,String image) 63 | throws IOException, InterruptedException, IM4JavaException { 64 | if(isIgnoreRegion(baseLineImageName)) 65 | ignoreRegionOnApp(image,baseLineImageName); 66 | if(!isFullScreen()) 67 | applyMaskImage(image); 68 | } 69 | private void ignoreRegionOnApp(String image, String baseLineImageName) throws InterruptedException, IOException, IM4JavaException { 70 | imageUtil.maskRegions(image,baseLineImageName); 71 | } 72 | 73 | private void captureScreen(String imagepath){ 74 | new ScreenShooter().screenCapture(imagepath); 75 | } 76 | private void applyMaskImage(String maskedRegionImage) throws InterruptedException, IOException, IM4JavaException { 77 | imageUtil.maskImage(maskedRegionImage, screenPaths.getMaskImage()); 78 | } 79 | private void mergerDiffHorizontal(String expectedImage,String actualImage,String diffImage) 80 | throws InterruptedException, IOException, IM4JavaException { 81 | imageUtil.mergeImagesHorizontally(expectedImage, 82 | actualImage, diffImage, 83 | screenPaths.getMergedDiffImage()); 84 | Thread.sleep(1000); 85 | file = new File(screenPaths.getDiffImage()); 86 | file.delete(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/ScreenExecutor/NakalAttributeValidator.java: -------------------------------------------------------------------------------- 1 | package com.nakal.ScreenExecutor; 2 | 3 | import com.nakal.utils.YamlReader; 4 | import java.io.FileNotFoundException; 5 | import java.util.LinkedHashMap; 6 | 7 | import static com.nakal.ScreenExecutor.Configuration.maskImage; 8 | import static com.nakal.ScreenExecutor.Configuration.nakalMode; 9 | import static com.nakal.ScreenExecutor.Configuration.platform; 10 | 11 | /** 12 | * Created by hiteshs on 2/25/18. 13 | */ 14 | public class NakalAttributeValidator { 15 | 16 | public static final String BUILDMODE = "build"; 17 | public static final String COMPAREMODE = "compare"; 18 | public static final String ANDROID="android"; 19 | public static final String CHROME="chrome"; 20 | public static final String IOS="ios"; 21 | public static final String SAFARI="safari"; 22 | 23 | 24 | public static boolean isBuildMode() { 25 | return BUILDMODE.equalsIgnoreCase(nakalMode); 26 | } 27 | public static boolean isCompareMode(){ 28 | return COMPAREMODE.equalsIgnoreCase(nakalMode); 29 | } 30 | 31 | public static boolean isAndroid(){ 32 | return ANDROID.equalsIgnoreCase(platform); 33 | } 34 | public static boolean isChrome(){ 35 | return CHROME.equalsIgnoreCase(platform); 36 | } 37 | 38 | public static boolean isiOS(){ 39 | return IOS.equalsIgnoreCase(platform); 40 | } 41 | public static boolean isSafari(){ 42 | return SAFARI.equalsIgnoreCase(platform); 43 | } 44 | public static boolean isMaskImagePresent() { 45 | return maskImage!=null && !maskImage.isEmpty(); 46 | } 47 | 48 | public static boolean isFullScreen() throws FileNotFoundException { 49 | final String fullScreenAttr="fullScreen"; 50 | boolean result= false; 51 | String fullScreen=null; 52 | if(isYamlPresent() && hasAttributeInYaml(fullScreenAttr)) { 53 | fullScreen = YamlReader.getInstance().getValue(fullScreenAttr).toString(); 54 | } 55 | return fullScreen!=null && fullScreen.equalsIgnoreCase("true"); 56 | } 57 | 58 | public static boolean isIgnoreRegion(String baseLineImageName) throws FileNotFoundException { 59 | Object mask=null; 60 | if(isMaskImagePresent() && isYamlPresent() && hasAttributeInYaml(maskImage)) { 61 | mask = YamlReader.getInstance().getValue(maskImage); 62 | } 63 | return mask!=null && ((LinkedHashMap)mask).get(baseLineImageName)!=null; 64 | } 65 | public static boolean isYamlPresent(){ 66 | return YamlReader.getInstance().checkIfYamlFileExists(); 67 | } 68 | public static boolean hasAttributeInYaml(String value){ 69 | return YamlReader.getInstance().getAllResultsMap().containsKey(value); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/ScreenExecutor/NakalExecutor.java: -------------------------------------------------------------------------------- 1 | package com.nakal.ScreenExecutor; 2 | 3 | import org.im4java.core.IM4JavaException; 4 | import java.io.IOException; 5 | import static com.nakal.ScreenExecutor.NakalAttributeValidator.isBuildMode; 6 | 7 | /** 8 | * Created by saikrisv on 22/02/16. 9 | */ 10 | public class NakalExecutor { 11 | 12 | private ExecutorService executorService; 13 | 14 | public NakalExecutor(){ 15 | executorService= new ExecutorService(); 16 | } 17 | 18 | /** 19 | * @param baseLineImageName 20 | * @return false if actual and expected images are not similar and generate a difference Image 21 | */ 22 | public boolean nakalExecutorNativeCompare(String baseLineImageName) 23 | throws InterruptedException, IOException, IM4JavaException { 24 | 25 | return isBuildMode() ? executorService.buildMode(baseLineImageName) 26 | : executorService.compareMode(baseLineImageName,0); 27 | } 28 | /** 29 | * @param baseLineImageName 30 | * @param threshold 31 | * @return false if actual and expected images are not similar and generate a difference Image 32 | */ 33 | public boolean nakalExecutorNativeCompare(String baseLineImageName, int threshold) 34 | throws InterruptedException, IOException, IM4JavaException { 35 | 36 | return isBuildMode() ? executorService.buildMode(baseLineImageName) 37 | : executorService.compareMode(baseLineImageName,threshold); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/capturescreen/ScreenShooter.java: -------------------------------------------------------------------------------- 1 | package com.nakal.capturescreen; 2 | 3 | import static com.nakal.ScreenExecutor.Configuration.platform; 4 | import com.nakal.devices.DeviceInterface; 5 | import com.nakal.devices.ViewFactory; 6 | 7 | /** 8 | * Created by saikrisv on 22/02/16. 9 | */ 10 | public class ScreenShooter { 11 | private ViewFactory viewFactory = new ViewFactory(); 12 | 13 | public void screenCapture(String imagePath) { 14 | DeviceInterface runnerInfo = viewFactory.getMobilePlatform(platform); 15 | runnerInfo.captureScreenShot(imagePath); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/devices/AndroidDeviceScreen.java: -------------------------------------------------------------------------------- 1 | package com.nakal.devices; 2 | 3 | import com.github.cosysoft.device.android.AndroidDevice; 4 | import com.github.cosysoft.device.android.impl.AndroidDeviceStore; 5 | import com.github.cosysoft.device.image.ImageUtils; 6 | import com.nakal.imageutil.ImageUtil; 7 | import com.nakal.utils.Utils; 8 | 9 | import java.awt.image.BufferedImage; 10 | import java.io.File; 11 | import java.util.TreeSet; 12 | 13 | /** 14 | * Created by saikrisv on 22/02/16. 15 | */ 16 | public class AndroidDeviceScreen implements DeviceInterface { 17 | 18 | TreeSet devices; 19 | AndroidDevice device; 20 | Utils util = new Utils(); 21 | ImageUtil imageUtil = new ImageUtil(); 22 | int deviceSize; 23 | 24 | public AndroidDeviceScreen() { 25 | getDeviceConnected(); 26 | if (checkIfDevicesAreConnected() == false) { 27 | System.out.println("No Devices Connected.... Quiting the execution"); 28 | System.exit(0); 29 | } 30 | } 31 | 32 | public AndroidDevice getDeviceConnected() { 33 | if (System.getenv("UDID") == null || System.getenv("UDID").isEmpty()) { 34 | devices = AndroidDeviceStore.getInstance() 35 | .getDevices(); 36 | deviceSize = devices.size(); 37 | device = devices.pollFirst(); 38 | return device; 39 | } else { 40 | device = AndroidDeviceStore.getInstance().getDeviceBySerial(System.getenv("UDID")); 41 | if(!device.getName().isEmpty()){ 42 | deviceSize = 1; 43 | } 44 | return device; 45 | } 46 | } 47 | 48 | public boolean checkIfDevicesAreConnected() { 49 | if (deviceSize > 0) { 50 | return true; 51 | } 52 | return false; 53 | } 54 | 55 | public String getDeviceName() { 56 | return device.getName().toString(); 57 | } 58 | 59 | 60 | public void captureScreenShot( String imagePath) { 61 | //util.createDirectory(arg); 62 | String directoryPath=null; 63 | File file = new File(imagePath); 64 | if (file.exists()) { 65 | System.out.println("BaseLine Image already Exists"); 66 | } else { 67 | if(!file.isDirectory()) 68 | directoryPath=util.getParentDirectoryFromFile(file); 69 | util.createDirectory(directoryPath); 70 | BufferedImage image = device.takeScreenshot(); 71 | ImageUtils.writeToFile(image, imagePath); 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/devices/DeviceInterface.java: -------------------------------------------------------------------------------- 1 | package com.nakal.devices; 2 | 3 | 4 | /** 5 | * Created by saikrisv on 22/02/16. 6 | */ 7 | public interface DeviceInterface { 8 | 9 | public void captureScreenShot(String imagePath); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/devices/ViewFactory.java: -------------------------------------------------------------------------------- 1 | package com.nakal.devices; 2 | import static com.nakal.ScreenExecutor.NakalAttributeValidator.isAndroid; 3 | import static com.nakal.ScreenExecutor.NakalAttributeValidator.isChrome; 4 | import static com.nakal.ScreenExecutor.NakalAttributeValidator.isiOS; 5 | import static com.nakal.ScreenExecutor.NakalAttributeValidator.isSafari; 6 | 7 | 8 | public class ViewFactory{ 9 | 10 | private AndroidDeviceScreen androidFlow; 11 | private iOSDeviceScreen iosFlow; 12 | 13 | 14 | public DeviceInterface getMobilePlatform(String platform) { 15 | if (platform == null) { 16 | return null; 17 | } 18 | if (isAndroid() || isChrome()) { 19 | if (androidFlow == null) { 20 | return androidFlow = new AndroidDeviceScreen(); 21 | } 22 | return androidFlow; 23 | } 24 | 25 | if (isiOS() || isSafari()) { 26 | if (iosFlow == null) { 27 | return iosFlow = new iOSDeviceScreen(); 28 | } 29 | return iosFlow; 30 | 31 | } 32 | return null; 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/devices/iOSDeviceScreen.java: -------------------------------------------------------------------------------- 1 | package com.nakal.devices; 2 | 3 | import com.nakal.ScreenExecutor.NakalAttributeValidator; 4 | import com.nakal.utils.CommandPrompt; 5 | import com.nakal.utils.Utils; 6 | import com.thoughtworks.device.Device; 7 | import com.thoughtworks.device.SimulatorManager; 8 | import com.thoughtworks.iOS.IOSManager; 9 | import org.apache.commons.io.FilenameUtils; 10 | 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * Created by saikrisv on 22/02/16. 19 | */ 20 | public class iOSDeviceScreen implements DeviceInterface { 21 | public ArrayList deviceUDIDiOS = new ArrayList(); 22 | CommandPrompt commandPrompt = new CommandPrompt(); 23 | SimulatorManager simulatorManager = new SimulatorManager(); 24 | Utils utils = new Utils(); 25 | 26 | public ArrayList getIOSUDID() throws IOException { 27 | List allAvailableDevices = new IOSManager().getDevices(); 28 | allAvailableDevices.forEach(iosudid -> { 29 | deviceUDIDiOS.add(iosudid.getUdid()); 30 | }); 31 | return deviceUDIDiOS; 32 | } 33 | 34 | public void captureScreenShot(String imagePath){ 35 | String directoryPath=null; 36 | File file = new File(imagePath); 37 | String fileName= file.getName(); 38 | if(NakalAttributeValidator.isBuildMode() && file.exists()){ 39 | System.out.println("BaseLine Image already Exists"); 40 | }else{ 41 | if(!file.isDirectory()) 42 | directoryPath=utils.getParentDirectoryFromFile(file); 43 | utils.createDirectory(directoryPath); 44 | try { 45 | if(getIOSUDID().size() != 0) { 46 | commandPrompt.runCommand("idevicescreenshot " + imagePath); 47 | } else if(simulatorManager.getAllBootedSimulators("iOS").size() != 0) { 48 | fileName= FilenameUtils.removeExtension(fileName); 49 | String UDID = simulatorManager.getAllBootedSimulators("iOS").get(0).getUdid(); 50 | simulatorManager.captureScreenshot(UDID, fileName, directoryPath, "png"); 51 | } else { 52 | System.exit(0); 53 | } 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } catch (InterruptedException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/imageutil/ImageUtil.java: -------------------------------------------------------------------------------- 1 | package com.nakal.imageutil; 2 | 3 | import static com.nakal.ScreenExecutor.NakalAttributeValidator.hasAttributeInYaml; 4 | import static com.nakal.ScreenExecutor.NakalAttributeValidator.isYamlPresent; 5 | 6 | import com.nakal.utils.YamlReader; 7 | import org.im4java.core.*; 8 | import org.im4java.process.ArrayListErrorConsumer; 9 | 10 | import javax.imageio.ImageIO; 11 | import java.awt.image.BufferedImage; 12 | import java.io.*; 13 | 14 | 15 | /** 16 | * Created by saikrisv on 22/02/16. 17 | */ 18 | public class ImageUtil { 19 | ArrayListErrorConsumer arrayListErrorConsumer = new ArrayListErrorConsumer(); 20 | 21 | /** 22 | * @param actualImage 23 | * @param expectedImage 24 | * @param diffImage 25 | * @return 26 | * @throws IOException 27 | * @throws InterruptedException 28 | * @throws IM4JavaException 29 | */ 30 | public boolean compareImages(String actualImage, String expectedImage, String diffImage) 31 | throws IOException, InterruptedException, IM4JavaException { 32 | 33 | IMOps cmpOp = new IMOperation(); 34 | cmpOp.metric("AE"); 35 | cmpOp.fuzz(getFuzzValue(), true); 36 | cmpOp.addImage(); 37 | cmpOp.addImage(); 38 | cmpOp.addImage(); 39 | CompareCmd compare = new CompareCmd(); 40 | compare.setErrorConsumer(arrayListErrorConsumer); 41 | try { 42 | compare.run(cmpOp, actualImage, expectedImage); 43 | System.out.println("**Image Is Same**"); 44 | return true; 45 | } catch (Throwable e) { 46 | try { 47 | compare.run(cmpOp, actualImage, expectedImage, diffImage); 48 | 49 | } catch (Throwable e1) { 50 | System.out.println( 51 | "Total Pixel Difference of the Images:::" + arrayListErrorConsumer.getOutput() 52 | .get(0)); 53 | } 54 | return false; 55 | } 56 | 57 | } 58 | 59 | 60 | /** 61 | * returns true if the images are similar 62 | * returns false if the images are not identical 63 | * 64 | * @param actualImage 65 | * @param expectedImage 66 | * @return 67 | * @throws IOException 68 | * @throws InterruptedException 69 | * @throws IM4JavaException 70 | */ 71 | public boolean compareImages(String actualImage, String expectedImage) 72 | throws IOException, InterruptedException, IM4JavaException { 73 | IMOps cmpOp = new IMOperation(); 74 | cmpOp.metric("AE"); 75 | cmpOp.fuzz(getFuzzValue(), true); 76 | cmpOp.addImage(); 77 | cmpOp.addImage(); 78 | cmpOp.addImage(); 79 | CompareCmd compare = new CompareCmd(); 80 | compare.setErrorConsumer(arrayListErrorConsumer); 81 | try { 82 | compare.run(cmpOp, actualImage, expectedImage); 83 | return true; 84 | } catch (Throwable e) { 85 | return false; 86 | } 87 | } 88 | 89 | /** 90 | * @param actualImage actualImagePath 91 | * @param value set the ignore % of image pixels 92 | * @throws IOException 93 | */ 94 | public boolean compareImages(String expectedImage, String actualImage, String diffImage, int value) 95 | throws IOException, IM4JavaException, InterruptedException { 96 | compareImages(actualImage, expectedImage, diffImage); 97 | long totalImagePixel = getCompleteImagePixel(actualImage); 98 | if (arrayListErrorConsumer.getOutput().get(0).contains("+") == true) { 99 | return false; 100 | } else { 101 | long totalPixelDifferne = Integer.parseInt(arrayListErrorConsumer.getOutput().get(0)); 102 | double c = ((double) totalPixelDifferne / totalImagePixel) * 100; 103 | long finalPercentageDifference = Math.round(c); 104 | System.out.println("Difference in the images is ::" + finalPercentageDifference + "%"); 105 | try { 106 | if (finalPercentageDifference <= value) { 107 | return true; 108 | } 109 | } catch (NumberFormatException e) { 110 | } 111 | return false; 112 | } 113 | } 114 | 115 | /** 116 | * @param actualImage 117 | * @param maskImage 118 | * @throws IOException 119 | * @throws InterruptedException 120 | * @throws IM4JavaException 121 | */ 122 | 123 | public void maskImage(String actualImage, String maskImage) 124 | throws IOException, InterruptedException, IM4JavaException { 125 | String maskedImage=actualImage; 126 | IMOperation op = new IMOperation(); 127 | op.addImage(actualImage); 128 | op.addImage(maskImage); 129 | op.alpha("on"); 130 | op.compose("DstOut"); 131 | op.composite(); 132 | op.addImage(maskedImage); 133 | ConvertCmd convert = new ConvertCmd(); 134 | convert.run(op); 135 | } 136 | 137 | /** 138 | * @throws InterruptedException 139 | * @throws IOException 140 | * @throws IM4JavaException 141 | */ 142 | public void mergeImagesHorizontally(String expected, String actual, String diffImage, 143 | String mergedImage) throws InterruptedException, IOException, IM4JavaException { 144 | ConvertCmd cmd1 = new ConvertCmd(); 145 | IMOperation op1 = new IMOperation(); 146 | op1.addImage(expected); // source file 147 | op1.addImage(actual); // destination file file 148 | op1.addImage(diffImage); 149 | //op1.resize(1024,576); 150 | op1.p_append(); 151 | op1.addImage(mergedImage); 152 | cmd1.run(op1); 153 | } 154 | 155 | 156 | public int getCompleteImagePixel(String actualImage) throws IOException { 157 | BufferedImage readImage = null; 158 | readImage = ImageIO.read(new File(actualImage)); 159 | int h = readImage.getHeight(); 160 | int w = readImage.getWidth(); 161 | return h * w; 162 | } 163 | 164 | public void maskRegions(String imageToMaskRegion, String screenName) 165 | throws InterruptedException, IOException, IM4JavaException { 166 | drawRectangleToIgnore(imageToMaskRegion, screenName); 167 | } 168 | 169 | private void drawRectangleToIgnore(String imageToMaskRegion, 170 | String screenName) throws IOException, InterruptedException, IM4JavaException { 171 | ConvertCmd reg = new ConvertCmd(); 172 | IMOperation rep_op = new IMOperation(); 173 | rep_op.addImage(imageToMaskRegion); 174 | rep_op.fill("Blue"); 175 | rep_op.draw(YamlReader.getInstance().fetchValueFromYaml(screenName)); 176 | rep_op.addImage(imageToMaskRegion); 177 | reg.run(rep_op); 178 | } 179 | public static Double getFuzzValue() throws FileNotFoundException { 180 | Double fuzz=5.0; 181 | if(isYamlPresent() && hasAttributeInYaml("fuzzPercentage")) { 182 | fuzz = new Double(YamlReader.getInstance().getValue("fuzzPercentage").toString()); 183 | } 184 | return fuzz; 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/utils/CommandPrompt.java: -------------------------------------------------------------------------------- 1 | package com.nakal.utils; 2 | 3 | /** 4 | * Command Prompt - this class contains method to run windows and mac commands 5 | */ 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | 11 | public class CommandPrompt { 12 | 13 | static Process p; 14 | ProcessBuilder builder; 15 | 16 | /** 17 | * This method run command on windows and mac 18 | * 19 | * @param command 20 | * to run 21 | */ 22 | public String runCommand(String command) throws InterruptedException, IOException { 23 | p = Runtime.getRuntime().exec(command); 24 | // get std output 25 | BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); 26 | String line = ""; 27 | String allLine = ""; 28 | int i = 1; 29 | while ((line = r.readLine()) != null) { 30 | /* if (line.isEmpty()) { 31 | break; 32 | }*/ 33 | allLine = allLine + "" + line + "\n"; 34 | if (line.contains("Console LogLevel: debug") && line.contains("Complete")) 35 | break; 36 | i++; 37 | } 38 | return allLine; 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/utils/InstallMobileDevice.java: -------------------------------------------------------------------------------- 1 | package com.nakal.utils; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Created by saikrisv on 22/02/16. 7 | */ 8 | public class InstallMobileDevice { 9 | 10 | CommandPrompt cmd = new CommandPrompt(); 11 | public void checkIfMobileDeviceApiIsInstalled() throws InterruptedException, IOException { 12 | boolean checkMobileDevice = cmd.runCommand("brew list").contains("ideviceinstaller"); 13 | if (checkMobileDevice) { 14 | System.out.println("iDeviceInstaller already exists"); 15 | } else { 16 | System.out.println("Brewing mobileDevice API...."); 17 | cmd.runCommand("brew install ideviceinstaller"); 18 | } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/utils/ScreenPaths.java: -------------------------------------------------------------------------------- 1 | package com.nakal.utils; 2 | 3 | 4 | import com.nakal.ScreenExecutor.Configuration; 5 | 6 | import java.io.File; 7 | 8 | import static com.nakal.ScreenExecutor.Configuration.baseDirectory; 9 | import static com.nakal.ScreenExecutor.Configuration.screenshotFolder; 10 | import static com.nakal.ScreenExecutor.Configuration.platform; 11 | 12 | /** 13 | * Created by saikrisv on 04/07/16. 14 | */ 15 | public class ScreenPaths { 16 | 17 | private String customPath; 18 | private String expectedImage; 19 | private String maskImage; 20 | private String actualImage; 21 | 22 | private String mergedDiffImage; 23 | private String diffImage; 24 | 25 | 26 | public ScreenPaths(String baseLineImageName) { 27 | if (screenshotFolder!=null) { 28 | customPath = screenshotFolder 29 | + File.separator + platform; 30 | } else { 31 | customPath = platform; 32 | } 33 | init(baseLineImageName); 34 | } 35 | 36 | 37 | private void init(String baseLineImageName){ 38 | 39 | setActualImage(baseLineImageName); 40 | setExpectedImage(baseLineImageName); 41 | setDiffImage(baseLineImageName); 42 | setMaskImage(); 43 | setMergedDiffImage(baseLineImageName); 44 | } 45 | 46 | private void setExpectedImage(String baseLineImageName) { 47 | this.expectedImage = 48 | getBasePathForBaseLine(baseLineImageName) + baseLineImageName 49 | + ".png"; 50 | } 51 | 52 | private void setMaskImage() { 53 | this.maskImage = 54 | baseDirectory + File.separator + platform + "/mask_images/" 55 | + Configuration.maskImage + ".png"; 56 | } 57 | 58 | private void setMergedDiffImage(String baseLineImageName) { 59 | this.mergedDiffImage = 60 | getBasePathForTargetActualImage(baseLineImageName)+ "difference_" 61 | + baseLineImageName + ".png"; 62 | 63 | } 64 | 65 | private void setDiffImage(String baseLineImageName) { 66 | this.diffImage = 67 | getBasePathForTargetActualImage(baseLineImageName)+ "diff_" 68 | + baseLineImageName + ".png"; 69 | 70 | } 71 | 72 | private void setActualImage(String baseLineImageName) { 73 | this.actualImage = 74 | getBasePathForTargetActualImage(baseLineImageName) 75 | + baseLineImageName + ".png"; 76 | } 77 | 78 | 79 | private String getBasePathForTargetActualImage(String baseLineImageName){ 80 | return baseDirectory + "/target/" + customPath 81 | + "/actual_images/" + baseLineImageName + File.separator; 82 | } 83 | 84 | private String getBasePathForBaseLine(String baseLineImageName){ 85 | return baseDirectory + File.separator + customPath 86 | + "/baseline_images/" + baseLineImageName + File.separator; 87 | } 88 | 89 | public String getCustomPath() { 90 | return customPath; 91 | } 92 | 93 | public String getExpectedImage() { 94 | return expectedImage; 95 | } 96 | 97 | public String getMaskImage() { 98 | return maskImage; 99 | } 100 | 101 | public String getActualImage() { 102 | return actualImage; 103 | } 104 | 105 | public String getMergedDiffImage() { 106 | return mergedDiffImage; 107 | } 108 | 109 | public String getDiffImage() { 110 | return diffImage; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.nakal.utils; 2 | 3 | import org.apache.commons.io.FileUtils; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by saikrisv on 22/02/16. 10 | */ 11 | public class Utils { 12 | 13 | public void createDirectory(String directoryPath) { 14 | 15 | deleteDirectory(directoryPath); 16 | File file = new File(directoryPath); 17 | if (!file.exists()) { 18 | if (file.mkdirs()) { 19 | System.out.println("BaseLine Image Directory is created!"); 20 | } else { 21 | System.out.println("Failed to create BaseLine image directory!"); 22 | } 23 | } 24 | } 25 | 26 | private void deleteDirectory(String path) { 27 | 28 | File file = new File(path); 29 | if (file.isDirectory()) { 30 | System.out.println("Deleting Directory :" + path); 31 | try { 32 | FileUtils.deleteDirectory(new File(path)); //deletes the whole folder 33 | } catch (IOException e) { 34 | // TODO Auto-generated catch block 35 | e.printStackTrace(); 36 | } 37 | } else { 38 | System.out.println("Deleting File :" + path); 39 | //it is a simple file. Proceed for deletion 40 | file.delete(); 41 | } 42 | 43 | } 44 | 45 | public String getParentDirectoryFromFile(File file) { 46 | return file.getParent(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/nakal/utils/YamlReader.java: -------------------------------------------------------------------------------- 1 | package com.nakal.utils; 2 | 3 | import org.yaml.snakeyaml.Yaml; 4 | 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.InputStream; 9 | import java.util.LinkedHashMap; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | import static com.nakal.ScreenExecutor.Configuration.baseDirectory; 14 | import static com.nakal.ScreenExecutor.Configuration.maskImage; 15 | 16 | /** 17 | * Created by shridhk on 12/20/17. 18 | */ 19 | public class YamlReader { 20 | static Map result; 21 | 22 | private static YamlReader yamlReader; 23 | 24 | private YamlReader() { 25 | final String fileName = getPath(); 26 | Yaml yaml = new Yaml(); 27 | InputStream yamlParams = null; 28 | try { 29 | yamlParams = new FileInputStream(new File(fileName)); 30 | } catch (FileNotFoundException e) { 31 | e.printStackTrace(); 32 | } 33 | result = (Map) yaml.load(yamlParams); 34 | } 35 | 36 | public static YamlReader getInstance() { 37 | if (yamlReader == null) { 38 | yamlReader = new YamlReader(); 39 | } 40 | return yamlReader; 41 | } 42 | 43 | private static String getPath() { 44 | return baseDirectory + "/nakal.yaml"; 45 | } 46 | 47 | public static Object getValue(String key) { 48 | return result.get(key); 49 | } 50 | 51 | public static Map getAllResultsMap() { 52 | return result; 53 | } 54 | 55 | public static boolean checkIfYamlFileExists() { 56 | final String fileName = getPath(); 57 | File file = new File(fileName); 58 | return file.exists(); 59 | } 60 | 61 | public String fetchValueFromYaml(String screenName) throws FileNotFoundException { 62 | Set mask_region = 63 | ((LinkedHashMap) ((LinkedHashMap) YamlReader.getInstance().getValue(maskImage)) 64 | .get(screenName)).entrySet(); 65 | String maskingRegions = ""; 66 | for (Object regions : mask_region) { 67 | maskingRegions = 68 | maskingRegions + " rectangle " + regions.toString().split("=")[1].toString() 69 | .replace("[", "").replace("]", "").trim(); 70 | } 71 | return maskingRegions; 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/com/nakal/screen/AndroidTest.java: -------------------------------------------------------------------------------- 1 | package com.nakal.screen; 2 | 3 | import com.nakal.ScreenExecutor.NakalExecutor; 4 | import com.nakal.imageutil.ImageUtil; 5 | import org.im4java.core.IM4JavaException; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by saikrisv on 22/02/16. 12 | */ 13 | public class AndroidTest { 14 | 15 | NakalExecutor nakalExecutor = new NakalExecutor(); 16 | 17 | @Test 18 | public void compareImagesExecutor() throws InterruptedException, IOException, IM4JavaException { 19 | Assert.assertTrue(nakalExecutor.nakalExecutorNativeCompare("Login",5)); 20 | } 21 | 22 | @Test 23 | public void compareImagesWithPixelDifference() 24 | throws InterruptedException, IOException, IM4JavaException { 25 | Assert.assertTrue(nakalExecutor.nakalExecutorNativeCompare("ActivityScreen",3)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/nakal/screen/NakalTest.java: -------------------------------------------------------------------------------- 1 | package com.nakal.screen; 2 | 3 | import com.nakal.imageutil.ImageUtil; 4 | import org.im4java.core.IM4JavaException; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by hiteshs on 3/2/18. 12 | */ 13 | public class NakalTest { 14 | 15 | ImageUtil imageUtil = new ImageUtil(); 16 | 17 | @Test 18 | public void verifyImagesAreSimilar() throws InterruptedException, IOException, IM4JavaException { 19 | boolean imagePixels = imageUtil.compareImages(System.getProperty("user.dir") + "/testImages/ActivityScreen.png", 20 | System.getProperty("user.dir") + "/testImages/ActivityScreen.png"); 21 | Assert.assertTrue(imagePixels); 22 | 23 | } 24 | 25 | @Test 26 | public void verifyImagesAreNotSimilarAndCreatDiffImage() throws InterruptedException, IOException, IM4JavaException { 27 | boolean imagePixels = imageUtil.compareImages(System.getProperty("user.dir") + "/testImages/ActivityScreen.png", 28 | System.getProperty("user.dir") + "/testImages/ActivityScreen1.png", 29 | System.getProperty("user.dir") + "/testImages/ActivityDifferentImage.png"); 30 | Assert.assertFalse(imagePixels); 31 | } 32 | 33 | @Test 34 | public void mergeImagesHorizontally() throws InterruptedException, IOException, IM4JavaException { 35 | imageUtil.mergeImagesHorizontally(System.getProperty("user.dir") + "/testImages/ActivityScreen.png", 36 | System.getProperty("user.dir") + "/testImages/ActivityScreen1.png", 37 | System.getProperty("user.dir") + "/testImages/ActivityDifferentImage.png", 38 | System.getProperty("user.dir") + "/testImages/MergedImage.png"); 39 | } 40 | 41 | 42 | @Test 43 | public void verifyThresholdDifference() throws InterruptedException, IOException, IM4JavaException { 44 | imageUtil.compareImages(System.getProperty("user.dir") + "/testImages/googleActual.png", 45 | System.getProperty("user.dir") + "/testImages/googleExpected.png", 46 | System.getProperty("user.dir") + "/testImages/percentageDiff.png",2); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/nakal/screen/iOSTest.java: -------------------------------------------------------------------------------- 1 | package com.nakal.screen; 2 | 3 | import com.nakal.ScreenExecutor.NakalExecutor; 4 | import org.im4java.core.IM4JavaException; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by saikrisv on 22/02/16. 12 | */ 13 | public class iOSTest { 14 | 15 | NakalExecutor nakalExecutor = new NakalExecutor(); 16 | 17 | @Test 18 | public void captureScreenShotFromDevice() 19 | throws InterruptedException, IOException, IM4JavaException { 20 | nakalExecutor.nakalExecutorNativeCompare("Login"); 21 | } 22 | 23 | @Test 24 | public void compareImagesWithPixelDifference() 25 | throws InterruptedException, IOException, IM4JavaException { 26 | Assert.assertTrue(nakalExecutor.nakalExecutorNativeCompare("ActivityScreen",3)); 27 | } 28 | } 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /testImages/ActivityScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/testImages/ActivityScreen.png -------------------------------------------------------------------------------- /testImages/ActivityScreen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/testImages/ActivityScreen1.png -------------------------------------------------------------------------------- /testImages/actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/testImages/actual.png -------------------------------------------------------------------------------- /testImages/googleActual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/testImages/googleActual.png -------------------------------------------------------------------------------- /testImages/googleExpected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/testImages/googleExpected.png -------------------------------------------------------------------------------- /testImages/resized_picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saikrishna321/nakal_java/d1685d640d50c8b2d4074a3f839685edce3b7f1b/testImages/resized_picture.jpg --------------------------------------------------------------------------------