View Javadoc

1   package com.atlassian.selenium.visualcomparison;
2   
3   import com.atlassian.annotations.Internal;
4   import com.atlassian.selenium.visualcomparison.utils.BoundingBox;
5   import com.atlassian.selenium.visualcomparison.utils.PageDifference;
6   import com.atlassian.selenium.visualcomparison.utils.PageElementInfo;
7   import com.atlassian.selenium.visualcomparison.utils.ScreenResolution;
8   import com.atlassian.selenium.visualcomparison.utils.Screenshot;
9   import com.atlassian.selenium.visualcomparison.utils.ScreenshotDiff;
10  import junit.framework.Assert;
11  
12  import javax.imageio.ImageIO;
13  import java.awt.*;
14  import java.io.File;
15  import java.io.FileFilter;
16  import java.io.IOException;
17  import java.util.ArrayList;
18  import java.util.Collections;
19  import java.util.List;
20  import java.util.Map;
21  
22  /**
23   * Implements the visual comparison functionality.
24   *
25   * <p/>
26   * NOTE: this class is considered internal as of 2.3 and may be subject to API changes and removal as of 3.0. Please
27   * use {@link com.atlassian.selenium.visualcomparison.v2.Comparer} and the associated family of classes making up the
28   * Visual Comparison V2 API instead of this class directly.
29   */
30  @Internal
31  public class VisualComparer
32  {
33      private ScreenResolution[] resolutions = new ScreenResolution[]
34              {
35                      new ScreenResolution(1280, 1024)
36              };
37      private VisualComparableClient client;
38      private boolean refreshAfterResize = false;
39      private boolean reportingEnabled = false;
40      private String reportOutputPath;
41      private String imageSubDirName = "report_images";
42      private String tempPath = System.getProperty("java.io.tmpdir");
43      private Map<String, String> uiStringReplacements = null;
44      private long waitforJQueryTimeout = 0;
45      private List<BoundingBox> ignoreAreas = null;
46      private boolean ignoreSingleLineDiffs = false;
47  
48      public long getWaitforJQueryTimeout() {
49          return waitforJQueryTimeout;
50      }
51  
52      public void setWaitforJQueryTimeout(long waitforJQueryTimeout) {
53          this.waitforJQueryTimeout = waitforJQueryTimeout;
54      }
55  
56      public VisualComparer(VisualComparableClient client)
57      {
58          this.client = client;
59      }
60  
61      public void setScreenResolutions(ScreenResolution[] resolutions)
62      {
63          this.resolutions = resolutions;
64      }
65  
66      public ScreenResolution[] getScreenResolutions()
67      {
68          return this.resolutions;
69      }
70  
71      public void setRefreshAfterResize(boolean refreshAfterResize)
72      {
73          this.refreshAfterResize = refreshAfterResize;
74      }
75  
76      public boolean getRefreshAfterResize()
77      {
78          return this.refreshAfterResize;
79      }
80  
81      public void setUIStringReplacements(Map<String,String> uiStringReplacements)
82      {
83          this.uiStringReplacements = uiStringReplacements;
84      }
85  
86      public Map<String,String> getUIStringReplacements()
87      {
88          return this.uiStringReplacements;
89      }
90  
91      public void enableReportGeneration(String reportOutputPath)
92      {
93          this.reportingEnabled = true;
94          this.reportOutputPath = reportOutputPath;
95          
96          File file = new File(reportOutputPath + "/" + imageSubDirName);
97          file.mkdirs();
98      }
99  
100     public void disableReportGeneration()
101     {
102         this.reportingEnabled = false;
103     }
104 
105     public void setTempPath(String tempPath)
106     {
107         File file = new File(tempPath);
108         file.mkdirs();
109         this.tempPath = tempPath;
110     }
111 
112     public String getTempPath()
113     {
114         return this.tempPath;
115     }
116 
117 
118     public List<BoundingBox> getIgnoreAreas()
119     {
120         return ignoreAreas;
121     }
122 
123     public boolean getIgnoreSingleLineDiffs()
124     {
125         return ignoreSingleLineDiffs;
126     }
127 
128     public void setIgnoreSingleLineDiffs(boolean ignoreSingleLineDiffs)
129     {
130         this.ignoreSingleLineDiffs = ignoreSingleLineDiffs;
131     }
132 
133     public void setIgnoreAreas(List<BoundingBox> ignoreAreas)
134     {
135         this.ignoreAreas = ignoreAreas;
136     }
137 
138     public void assertUIMatches(String id, String baselineImagePath)
139     {
140         try
141         {
142             Assert.assertTrue("Screenshots were not equal", uiMatches(id, baselineImagePath));
143         }
144         catch (Exception e)
145         {
146             throw new RuntimeException(e);
147         }
148     }
149 
150     public boolean uiMatches(final String id, final String baselineImagePath) throws Exception
151     {
152         ArrayList<Screenshot> currentScreenshots = takeScreenshots(id);
153         ArrayList<Screenshot> baselineScreenshots = loadBaselineScreenshots(id, baselineImagePath);
154         return compareScreenshots(baselineScreenshots, currentScreenshots);
155     }
156 
157     public ArrayList<Screenshot> takeScreenshots(final String id) throws IOException
158     {
159         // Capture a series of screenshots in all the valid screen resolutions.
160         ArrayList<Screenshot> screenshots = new ArrayList<Screenshot>();
161         for (ScreenResolution res : resolutions)
162         {
163             client.resizeScreen(res, refreshAfterResize);
164             if (waitforJQueryTimeout > 0)
165             {
166                 if (!client.waitForJQuery (waitforJQueryTimeout))
167                 {
168                     Assert.fail("Timed out while waiting for jQuery to complete");
169                 }
170             }
171             if (uiStringReplacements != null)
172             {
173                 // Remove strings from the UI that we are expecting will change
174                 // (such as the build number in the JIRA footer)
175                 for (String key : uiStringReplacements.keySet())
176                 {
177                     replaceUIHtml(key, uiStringReplacements.get(key));
178                 }
179             }
180             screenshots.add(new Screenshot(client, id, tempPath, res));
181         }
182         Collections.sort(screenshots);
183         return screenshots;
184     }
185 
186     public ArrayList<Screenshot> loadBaselineScreenshots(final String id, final String baselineImagePath) throws IOException
187     {
188         File screenshotDir = new File(baselineImagePath);
189         File[] screenshotFiles = screenshotDir.listFiles(
190                 new FileFilter()
191                 {
192                     public boolean accept(File file) { return file.getName().startsWith(id);}
193                 });
194 
195         ArrayList<Screenshot> screenshots = new ArrayList<Screenshot>();
196         for (File screenshotFile : screenshotFiles)
197         {
198             screenshots.add(new Screenshot(screenshotFile));
199         }
200         Collections.sort(screenshots);
201         return screenshots;
202     }
203 
204     protected void replaceUIHtml(String id, String newContent)
205     {
206         final String script = "var content, el = window.document.getElementById('" + id + "');" +
207                 "if (el) { content = el.innerHTML; el.innerHTML = \"" + newContent + "\"; } content;";
208         final Object result = client.execute(script);
209         final String value = String.valueOf(result);
210     }
211 
212     public boolean compareScreenshots(ArrayList<Screenshot> oldScreenshots, ArrayList<Screenshot> newScreenshots)
213             throws Exception
214     {
215         if (oldScreenshots.size() != newScreenshots.size())
216         {
217             if (oldScreenshots.size() == 0)
218             {
219                 if (reportingEnabled)
220                 {
221                     String imageOutputDir = ScreenshotDiff.getImageOutputDir(reportOutputPath, imageSubDirName);
222                     for (Screenshot newScreenshot : newScreenshots)
223                     {
224                         // Copy the new image to the output directory.
225                         ImageIO.write(newScreenshot.getImage(), "png", new File(imageOutputDir + newScreenshot.getFileName()));
226                     }
227                 }
228                 throw new IllegalArgumentException("There were new screenshots, but no baseline images. Is this a new test?"
229                     + " If reporting is enabled, the new screenshots will be output in the report.");
230             }
231             else
232             {
233                 throw new IllegalArgumentException("Incorrect number of images."
234                         + " There were " + oldScreenshots.size() + " baseline images,"
235                         + " but only " + newScreenshots.size() + " new images.");
236             }
237         }
238 
239         boolean matches = true;
240         for (int i = 0; i < oldScreenshots.size(); i++)
241         {
242             ScreenshotDiff diff = getScreenshotDiff(oldScreenshots.get(i), newScreenshots.get(i));
243 
244             for (PageDifference difference : diff.getDifferences())
245             {
246                 BoundingBox box = difference.getBoundingBox();
247                 int x = new Double(Math.floor(box.getLeft() + box.getWidth() / 2)).intValue();
248                 int y = new Double(Math.floor(box.getTop() + box.getHeight() / 2)).intValue();
249                 ScreenElement thing = client.getElementAtPoint(x, y);
250                 PageElementInfo info = new PageElementInfo();
251                 info.htmlContent = thing.getHtml();
252                 info.position = new Point(x,y);
253                 difference.addPageElement(info);
254             }
255 
256             if (reportingEnabled)
257             {
258                 diff.writeDiffReport(reportOutputPath, imageSubDirName);
259             }
260             matches = !diff.hasDifferences() && matches;
261         }
262 
263         return matches;
264     }
265 
266     public ScreenshotDiff getScreenshotDiff(Screenshot oldScreenshot, Screenshot newScreenshot) throws Exception
267     {
268         return oldScreenshot.getDiff(newScreenshot, ignoreAreas, ignoreSingleLineDiffs);
269     }
270 
271 
272 }