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