1 package com.atlassian.selenium.visualcomparison.v2;
2
3 import com.atlassian.annotations.ExperimentalApi;
4 import com.atlassian.selenium.visualcomparison.ScreenElement;
5 import com.atlassian.selenium.visualcomparison.VisualComparableClient;
6 import com.atlassian.selenium.visualcomparison.VisualComparer;
7 import com.atlassian.selenium.visualcomparison.utils.BoundingBox;
8 import com.atlassian.selenium.visualcomparison.utils.ScreenResolution;
9 import com.atlassian.selenium.visualcomparison.v2.settings.PagePart;
10 import com.atlassian.selenium.visualcomparison.v2.settings.Replacement;
11 import com.atlassian.selenium.visualcomparison.v2.settings.Resolution;
12 import com.google.common.base.Function;
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.ImmutableMap;
15
16 import javax.annotation.Nonnull;
17 import javax.annotation.Nullable;
18 import javax.annotation.concurrent.Immutable;
19 import java.io.File;
20 import java.util.List;
21 import java.util.Map;
22
23 import static com.atlassian.selenium.visualcomparison.v2.ComparisonSettings.emptySettings;
24 import static com.google.common.base.Preconditions.checkNotNull;
25 import static com.google.common.base.Preconditions.checkState;
26 import static com.google.common.collect.Iterables.toArray;
27 import static com.google.common.collect.Iterables.transform;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 @ExperimentalApi
47 @Immutable
48 public final class DefaultComparer implements Comparer
49 {
50 private final BrowserEngine engine;
51 private final ComparisonSettings settings;
52
53 public DefaultComparer(@Nonnull BrowserEngine engine, ComparisonSettings settings) {
54 this.engine = checkNotNull(engine, "engine");
55 this.settings = checkNotNull(settings, "settings");
56 }
57
58 @Override
59 public void compare(@Nonnull String id)
60 {
61 compare(id, emptySettings());
62 }
63
64 @Override
65 public void compare(@Nonnull String id, @Nonnull ComparisonSettings extraSettings)
66 {
67 checkNotNull(id, "id");
68 checkNotNull(extraSettings, "settings");
69 final ComparisonSettings effectiveSettings = settings.merge(extraSettings);
70 validateSettings(effectiveSettings);
71 VisualComparer comparer = getComparer(effectiveSettings);
72 try
73 {
74 if (!comparer.uiMatches(id, effectiveSettings.getBaselineDirectory().getAbsolutePath()))
75 {
76 String message = "Screenshots did not match the baseline in "
77 + effectiveSettings.getBaselineDirectory().getAbsolutePath() + ".";
78 if (effectiveSettings.isReportingEnabled())
79 {
80 message += " Check reports in " + effectiveSettings.getReportingDirectory().getAbsolutePath()
81 + " for more details.";
82 }
83 throw new VisualComparisonFailedException(id, message);
84 }
85 }
86 catch (Exception e)
87 {
88 if (e instanceof VisualComparisonFailedException)
89 {
90 throw (VisualComparisonFailedException) e;
91 }
92 else
93 {
94 throw new VisualComparisonFailedException(id, "Error when performing comparison", e);
95 }
96 }
97 }
98
99 @SuppressWarnings("ConstantConditions")
100 private void validateSettings(ComparisonSettings effectiveSettings)
101 {
102 checkState(effectiveSettings.getBaselineDirectory() != null, "Baseline directory must be provided");
103 if (!effectiveSettings.getBaselineDirectory().isDirectory())
104 {
105 checkState(effectiveSettings.getBaselineDirectory().mkdirs(), "Unable to create baseline directory "
106 + effectiveSettings.getBaselineDirectory().getAbsolutePath());
107 }
108 if (effectiveSettings.isReportingEnabled() && !effectiveSettings.getReportingDirectory().isDirectory())
109 {
110 checkState(effectiveSettings.getReportingDirectory().mkdirs(), "Unable to create reporting directory "
111 + effectiveSettings.getReportingDirectory().getAbsolutePath());
112 }
113 }
114
115 private VisualComparer getComparer(ComparisonSettings effectiveSettings)
116 {
117 VisualComparer comparer = new VisualComparer(new BrowserEngineComparableClient());
118 comparer.setScreenResolutions(getResolutions(effectiveSettings));
119 if (effectiveSettings.isReportingEnabled())
120 {
121 comparer.enableReportGeneration(effectiveSettings.getReportingDirectory().getAbsolutePath());
122 }
123 comparer.setIgnoreSingleLineDiffs(effectiveSettings.isIgnoreSingleLineDifferences());
124 comparer.setRefreshAfterResize(effectiveSettings.isRefreshAfterResize());
125 comparer.setIgnoreAreas(getIgnoreAreas(effectiveSettings));
126 comparer.setUIStringReplacements(getReplacements(effectiveSettings));
127 comparer.setWaitforJQueryTimeout(5000);
128 return comparer;
129 }
130
131 private ScreenResolution[] getResolutions(ComparisonSettings effectiveSettings)
132 {
133 return toArray(transform(effectiveSettings.getResolutions(), new Function<Resolution, ScreenResolution>()
134 {
135 @Nullable
136 @Override
137 public ScreenResolution apply(Resolution input)
138 {
139 return new ScreenResolution(input.getWidth(), input.getHeight());
140 }
141 }), ScreenResolution.class);
142 }
143
144 private List<BoundingBox> getIgnoreAreas(ComparisonSettings effectiveSettings)
145 {
146 return ImmutableList.copyOf(transform(effectiveSettings.getIgnoredParts(), new Function<PagePart, BoundingBox>()
147 {
148 @Nullable
149 @Override
150 public BoundingBox apply(PagePart input)
151 {
152 return new BoundingBox(input.getLeft(), input.getTop(), input.getRight(), input.getBottom());
153 }
154 }));
155 }
156
157 private Map<String, String> getReplacements(ComparisonSettings effectiveSettings)
158 {
159 ImmutableMap.Builder<String,String> builder = ImmutableMap.builder();
160 for (Replacement replacement : effectiveSettings.getReplacements())
161 {
162 builder.put(replacement.getElementId(), replacement.getHtml());
163 }
164 return builder.build();
165 }
166
167 private final class BrowserEngineComparableClient implements VisualComparableClient
168 {
169
170 @Override
171 public void captureEntirePageScreenshot(String filePath)
172 {
173 engine.captureScreenshotTo(new File(filePath));
174 }
175
176 @Override
177 public ScreenElement getElementAtPoint(int x, int y)
178 {
179 return engine.getElementAt(x, y);
180 }
181
182 @Override
183 public void evaluate(String command)
184 {
185 execute(command);
186 }
187
188 @Override
189 public Object execute(String command, Object... arguments)
190 {
191 return engine.executeScript(Object.class, command, arguments);
192 }
193
194 @Override
195 public boolean resizeScreen(ScreenResolution resolution, boolean refreshAfterResize)
196 {
197 engine.resizeTo(new Resolution((int) resolution.getWidth(), (int) resolution.getHeight()));
198 if (refreshAfterResize)
199 {
200 refreshAndWait();
201 }
202 return true;
203 }
204
205 @Override
206 public void refreshAndWait()
207 {
208 engine.reloadPage();
209 }
210
211 @Override
212 public boolean waitForJQuery(long waitTimeMillis)
213 {
214 Long jQueryActive = null;
215 try
216 {
217 while (jQueryActive == null || jQueryActive != 0)
218 {
219 jQueryActive = engine.executeScript(Long.class, "return window.jQuery.active");
220 Thread.sleep(100);
221 }
222 }
223 catch (InterruptedException e)
224 {
225 return false;
226 }
227 return true;
228 }
229 }
230 }