compaa 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Rakefile +10 -2
- data/bin/compaa +6 -0
- data/compaa.gemspec +3 -0
- data/lib/assets/blender.js +59 -0
- data/lib/assets/compaa.js +138 -140
- data/lib/assets/context_blender.js +51 -0
- data/lib/assets/index.haml +4 -4
- data/lib/compaa/rack_app.rb +6 -3
- data/lib/compaa/version.rb +1 -1
- data/mock_app.rb +24 -15
- data/spec/generated_image_spec.rb +1 -1
- data/spec/integration/integration_spec.rb +1 -1
- data/spec/javascripts/BlenderSpec.js +26 -0
- data/spec/javascripts/support/expected.png +0 -0
- data/spec/javascripts/support/generated.png +0 -0
- data/spec/javascripts/support/imagediff.js +382 -0
- data/spec/javascripts/support/jasmine.yml +4 -4
- data/spec/javascripts/support/one.jpg +0 -0
- data/spec/javascripts/support/reference.png +0 -0
- data/spec/javascripts/support/three.png +0 -0
- data/spec/javascripts/support/two.jpg +0 -0
- metadata +48 -8
- data/lib/assets/jquery-2.0.0.js +0 -8755
- data/lib/assets/underscore.js +0 -1227
- data/spec/javascripts/CompaaSpec.js +0 -81
- data/spec/javascripts/support/jasmine-jquery.js +0 -658
data/lib/assets/index.haml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
!!!
|
2
2
|
%head
|
3
|
-
%script{src: '/assets/
|
4
|
-
%script{src: '/assets/
|
3
|
+
%script{src: '/assets/context_blender.js'}
|
4
|
+
%script{src: '/assets/blender.js'}
|
5
5
|
%script{src: '/assets/compaa.js'}
|
6
6
|
%link{rel: 'stylesheet', href: '/assets/bootstrap.css'}
|
7
7
|
:javascript
|
8
8
|
var compaa;
|
9
|
-
|
9
|
+
document.addEventListener('DOMContentLoaded', function() {
|
10
10
|
compaa = Object.create(Compaa);
|
11
11
|
compaa.init();
|
12
|
-
});
|
12
|
+
}, false);
|
13
13
|
|
14
14
|
%body
|
15
15
|
%canvas#difference
|
data/lib/compaa/rack_app.rb
CHANGED
@@ -37,9 +37,12 @@ module Compaa
|
|
37
37
|
|
38
38
|
def get
|
39
39
|
case @request.path
|
40
|
-
when '/'
|
41
|
-
when '/artifacts.json'
|
42
|
-
|
40
|
+
when '/' then index
|
41
|
+
when '/artifacts.json' then artifacts_json
|
42
|
+
when '/compaa.js' then compaa_js
|
43
|
+
when '/blender.js' then blender_js
|
44
|
+
when '/context_blender.js' then context_blender_js
|
45
|
+
else four_oh_four
|
43
46
|
end
|
44
47
|
end
|
45
48
|
|
data/lib/compaa/version.rb
CHANGED
data/mock_app.rb
CHANGED
@@ -1,22 +1,31 @@
|
|
1
|
-
require 'sinatra'
|
1
|
+
require 'sinatra/base'
|
2
2
|
require 'json'
|
3
3
|
require 'rack/cors'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
class MockCompaa < Sinatra::Base
|
6
|
+
set :port, 4567
|
7
|
+
disable :logging
|
8
|
+
|
9
|
+
use Rack::Cors do
|
10
|
+
allow do
|
11
|
+
origins '*'
|
12
|
+
resource '/*', :headers => :any, :methods => [:get, :post]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/artifacts.json' do
|
17
|
+
content_type 'application/json'
|
18
|
+
{
|
19
|
+
artifacts: %w(
|
20
|
+
artifacts/reference_screenshots/one.png
|
21
|
+
artifacts/reference_screenshots/two.png
|
22
|
+
artifacts/reference_screenshots/three.png
|
23
|
+
artifacts/reference_screenshots/four.png
|
24
|
+
)
|
25
|
+
}.to_json
|
9
26
|
end
|
10
27
|
end
|
11
28
|
|
12
|
-
|
13
|
-
|
14
|
-
{
|
15
|
-
artifacts: %w(
|
16
|
-
artifacts/reference_screenshots/one.png
|
17
|
-
artifacts/reference_screenshots/two.png
|
18
|
-
artifacts/reference_screenshots/three.png
|
19
|
-
artifacts/reference_screenshots/four.png
|
20
|
-
)
|
21
|
-
}.to_json
|
29
|
+
Thread.new do
|
30
|
+
MockCompaa.run!
|
22
31
|
end
|
@@ -71,7 +71,7 @@ describe Compaa::GeneratedImage do
|
|
71
71
|
difference_path =
|
72
72
|
File.join %w[artifacts differences_in_screenshots_this_run dir file.png_difference.gif]
|
73
73
|
|
74
|
-
mock_file_manager.expect :rm, true, [difference_path]
|
74
|
+
mock_file_manager.expect :rm, true, [difference_path, { force: true }]
|
75
75
|
|
76
76
|
subject.delete_difference_image
|
77
77
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
describe("blender", function() {
|
2
|
+
describe("makeItBlend()", function() {
|
3
|
+
it("produces a difference image", function() {
|
4
|
+
var imageOne = new Image(),
|
5
|
+
imageTwo = new Image(),
|
6
|
+
expectedImage = new Image(),
|
7
|
+
actual;
|
8
|
+
|
9
|
+
this.addMatchers(imagediff.jasmine);
|
10
|
+
|
11
|
+
expectedImage.src = '/spec/javascripts/support/expected.png';
|
12
|
+
imageOne.src = '/spec/javascripts/support/generated.png';
|
13
|
+
imageTwo.src = '/spec/javascripts/support/reference.png';
|
14
|
+
|
15
|
+
waitsFor(function() {
|
16
|
+
return imageOne.complete && imageTwo.complete && expectedImage.complete;
|
17
|
+
});
|
18
|
+
|
19
|
+
runs(function() {
|
20
|
+
window.blender.makeItBlend(imageOne, imageTwo, function(blended) {
|
21
|
+
expect(blended).toImageDiffEqual(expectedImage);
|
22
|
+
});
|
23
|
+
});
|
24
|
+
});
|
25
|
+
});
|
26
|
+
});
|
Binary file
|
Binary file
|
@@ -0,0 +1,382 @@
|
|
1
|
+
// js-imagediff 1.0.3
|
2
|
+
// (c) 2011-2012 Carl Sutherland, Humble Software
|
3
|
+
// Distributed under the MIT License
|
4
|
+
// For original source and documentation visit:
|
5
|
+
// http://www.github.com/HumbleSoftware/js-imagediff
|
6
|
+
|
7
|
+
(function (name, definition) {
|
8
|
+
var root = this;
|
9
|
+
if (typeof module !== 'undefined') {
|
10
|
+
var Canvas = require('canvas');
|
11
|
+
module.exports = definition(root, name, Canvas);
|
12
|
+
} else if (typeof define === 'function' && typeof define.amd === 'object') {
|
13
|
+
define(definition);
|
14
|
+
} else {
|
15
|
+
root[name] = definition(root, name);
|
16
|
+
}
|
17
|
+
})('imagediff', function (root, name, Canvas) {
|
18
|
+
|
19
|
+
var
|
20
|
+
TYPE_ARRAY = /\[object Array\]/i,
|
21
|
+
TYPE_CANVAS = /\[object (Canvas|HTMLCanvasElement)\]/i,
|
22
|
+
TYPE_CONTEXT = /\[object CanvasRenderingContext2D\]/i,
|
23
|
+
TYPE_IMAGE = /\[object (Image|HTMLImageElement)\]/i,
|
24
|
+
TYPE_IMAGE_DATA = /\[object ImageData\]/i,
|
25
|
+
|
26
|
+
UNDEFINED = 'undefined',
|
27
|
+
|
28
|
+
canvas = getCanvas(),
|
29
|
+
context = canvas.getContext('2d'),
|
30
|
+
previous = root[name],
|
31
|
+
imagediff, jasmine;
|
32
|
+
|
33
|
+
// Creation
|
34
|
+
function getCanvas (width, height) {
|
35
|
+
var
|
36
|
+
canvas = Canvas ?
|
37
|
+
new Canvas() :
|
38
|
+
document.createElement('canvas');
|
39
|
+
if (width) canvas.width = width;
|
40
|
+
if (height) canvas.height = height;
|
41
|
+
return canvas;
|
42
|
+
}
|
43
|
+
function getImageData (width, height) {
|
44
|
+
canvas.width = width;
|
45
|
+
canvas.height = height;
|
46
|
+
context.clearRect(0, 0, width, height);
|
47
|
+
return context.createImageData(width, height);
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
// Type Checking
|
52
|
+
function isImage (object) {
|
53
|
+
return isType(object, TYPE_IMAGE);
|
54
|
+
}
|
55
|
+
function isCanvas (object) {
|
56
|
+
return isType(object, TYPE_CANVAS);
|
57
|
+
}
|
58
|
+
function isContext (object) {
|
59
|
+
return isType(object, TYPE_CONTEXT);
|
60
|
+
}
|
61
|
+
function isImageData (object) {
|
62
|
+
return !!(object &&
|
63
|
+
isType(object, TYPE_IMAGE_DATA) &&
|
64
|
+
typeof(object.width) !== UNDEFINED &&
|
65
|
+
typeof(object.height) !== UNDEFINED &&
|
66
|
+
typeof(object.data) !== UNDEFINED);
|
67
|
+
}
|
68
|
+
function isImageType (object) {
|
69
|
+
return (
|
70
|
+
isImage(object) ||
|
71
|
+
isCanvas(object) ||
|
72
|
+
isContext(object) ||
|
73
|
+
isImageData(object)
|
74
|
+
);
|
75
|
+
}
|
76
|
+
function isType (object, type) {
|
77
|
+
return typeof (object) === 'object' && !!Object.prototype.toString.apply(object).match(type);
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
// Type Conversion
|
82
|
+
function copyImageData (imageData) {
|
83
|
+
var
|
84
|
+
height = imageData.height,
|
85
|
+
width = imageData.width,
|
86
|
+
data = imageData.data,
|
87
|
+
newImageData, newData, i;
|
88
|
+
|
89
|
+
canvas.width = width;
|
90
|
+
canvas.height = height;
|
91
|
+
newImageData = context.getImageData(0, 0, width, height);
|
92
|
+
newData = newImageData.data;
|
93
|
+
|
94
|
+
for (i = imageData.data.length; i--;) {
|
95
|
+
newData[i] = data[i];
|
96
|
+
}
|
97
|
+
|
98
|
+
return newImageData;
|
99
|
+
}
|
100
|
+
function toImageData (object) {
|
101
|
+
if (isImage(object)) { return toImageDataFromImage(object); }
|
102
|
+
if (isCanvas(object)) { return toImageDataFromCanvas(object); }
|
103
|
+
if (isContext(object)) { return toImageDataFromContext(object); }
|
104
|
+
if (isImageData(object)) { return object; }
|
105
|
+
}
|
106
|
+
function toImageDataFromImage (image) {
|
107
|
+
var
|
108
|
+
height = image.height,
|
109
|
+
width = image.width;
|
110
|
+
canvas.width = width;
|
111
|
+
canvas.height = height;
|
112
|
+
context.clearRect(0, 0, width, height);
|
113
|
+
context.drawImage(image, 0, 0);
|
114
|
+
return context.getImageData(0, 0, width, height);
|
115
|
+
}
|
116
|
+
function toImageDataFromCanvas (canvas) {
|
117
|
+
var
|
118
|
+
height = canvas.height,
|
119
|
+
width = canvas.width,
|
120
|
+
context = canvas.getContext('2d');
|
121
|
+
return context.getImageData(0, 0, width, height);
|
122
|
+
}
|
123
|
+
function toImageDataFromContext (context) {
|
124
|
+
var
|
125
|
+
canvas = context.canvas,
|
126
|
+
height = canvas.height,
|
127
|
+
width = canvas.width;
|
128
|
+
return context.getImageData(0, 0, width, height);
|
129
|
+
}
|
130
|
+
function toCanvas (object) {
|
131
|
+
var
|
132
|
+
data = toImageData(object),
|
133
|
+
canvas = getCanvas(data.width, data.height),
|
134
|
+
context = canvas.getContext('2d');
|
135
|
+
|
136
|
+
context.putImageData(data, 0, 0);
|
137
|
+
return canvas;
|
138
|
+
}
|
139
|
+
|
140
|
+
|
141
|
+
// ImageData Equality Operators
|
142
|
+
function equalWidth (a, b) {
|
143
|
+
return a.width === b.width;
|
144
|
+
}
|
145
|
+
function equalHeight (a, b) {
|
146
|
+
return a.height === b.height;
|
147
|
+
}
|
148
|
+
function equalDimensions (a, b) {
|
149
|
+
return equalHeight(a, b) && equalWidth(a, b);
|
150
|
+
}
|
151
|
+
function equal (a, b, tolerance) {
|
152
|
+
|
153
|
+
var
|
154
|
+
aData = a.data,
|
155
|
+
bData = b.data,
|
156
|
+
length = aData.length,
|
157
|
+
i;
|
158
|
+
|
159
|
+
tolerance = tolerance || 0;
|
160
|
+
|
161
|
+
if (!equalDimensions(a, b)) return false;
|
162
|
+
for (i = length; i--;) if (aData[i] !== bData[i] && Math.abs(aData[i] - bData[i]) > tolerance) return false;
|
163
|
+
|
164
|
+
return true;
|
165
|
+
}
|
166
|
+
|
167
|
+
|
168
|
+
// Diff
|
169
|
+
function diff (a, b) {
|
170
|
+
return (equalDimensions(a, b) ? diffEqual : diffUnequal)(a, b);
|
171
|
+
}
|
172
|
+
function diffEqual (a, b) {
|
173
|
+
|
174
|
+
var
|
175
|
+
height = a.height,
|
176
|
+
width = a.width,
|
177
|
+
c = getImageData(width, height), // c = a - b
|
178
|
+
aData = a.data,
|
179
|
+
bData = b.data,
|
180
|
+
cData = c.data,
|
181
|
+
length = cData.length,
|
182
|
+
row, column,
|
183
|
+
i, j, k, v;
|
184
|
+
|
185
|
+
for (i = 0; i < length; i += 4) {
|
186
|
+
cData[i] = Math.abs(aData[i] - bData[i]);
|
187
|
+
cData[i+1] = Math.abs(aData[i+1] - bData[i+1]);
|
188
|
+
cData[i+2] = Math.abs(aData[i+2] - bData[i+2]);
|
189
|
+
cData[i+3] = Math.abs(255 - aData[i+3] - bData[i+3]);
|
190
|
+
}
|
191
|
+
|
192
|
+
return c;
|
193
|
+
}
|
194
|
+
function diffUnequal (a, b) {
|
195
|
+
|
196
|
+
var
|
197
|
+
height = Math.max(a.height, b.height),
|
198
|
+
width = Math.max(a.width, b.width),
|
199
|
+
c = getImageData(width, height), // c = a - b
|
200
|
+
aData = a.data,
|
201
|
+
bData = b.data,
|
202
|
+
cData = c.data,
|
203
|
+
rowOffset,
|
204
|
+
columnOffset,
|
205
|
+
row, column,
|
206
|
+
i, j, k, v;
|
207
|
+
|
208
|
+
|
209
|
+
for (i = cData.length - 1; i > 0; i = i - 4) {
|
210
|
+
cData[i] = 255;
|
211
|
+
}
|
212
|
+
|
213
|
+
// Add First Image
|
214
|
+
offsets(a);
|
215
|
+
for (row = a.height; row--;){
|
216
|
+
for (column = a.width; column--;) {
|
217
|
+
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
|
218
|
+
j = 4 * (row * a.width + column);
|
219
|
+
cData[i+0] = aData[j+0]; // r
|
220
|
+
cData[i+1] = aData[j+1]; // g
|
221
|
+
cData[i+2] = aData[j+2]; // b
|
222
|
+
// cData[i+3] = aData[j+3]; // a
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
// Subtract Second Image
|
227
|
+
offsets(b);
|
228
|
+
for (row = b.height; row--;){
|
229
|
+
for (column = b.width; column--;) {
|
230
|
+
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
|
231
|
+
j = 4 * (row * b.width + column);
|
232
|
+
cData[i+0] = Math.abs(cData[i+0] - bData[j+0]); // r
|
233
|
+
cData[i+1] = Math.abs(cData[i+1] - bData[j+1]); // g
|
234
|
+
cData[i+2] = Math.abs(cData[i+2] - bData[j+2]); // b
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
// Helpers
|
239
|
+
function offsets (imageData) {
|
240
|
+
rowOffset = Math.floor((height - imageData.height) / 2);
|
241
|
+
columnOffset = Math.floor((width - imageData.width) / 2);
|
242
|
+
}
|
243
|
+
|
244
|
+
return c;
|
245
|
+
}
|
246
|
+
|
247
|
+
|
248
|
+
// Validation
|
249
|
+
function checkType () {
|
250
|
+
var i;
|
251
|
+
for (i = 0; i < arguments.length; i++) {
|
252
|
+
if (!isImageType(arguments[i])) {
|
253
|
+
throw {
|
254
|
+
name : 'ImageTypeError',
|
255
|
+
message : 'Submitted object was not an image.'
|
256
|
+
};
|
257
|
+
}
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
|
262
|
+
// Jasmine Matchers
|
263
|
+
function get (element, content) {
|
264
|
+
element = document.createElement(element);
|
265
|
+
if (element && content) {
|
266
|
+
element.innerHTML = content;
|
267
|
+
}
|
268
|
+
return element;
|
269
|
+
}
|
270
|
+
|
271
|
+
jasmine = {
|
272
|
+
|
273
|
+
toBeImageData : function () {
|
274
|
+
return imagediff.isImageData(this.actual);
|
275
|
+
},
|
276
|
+
|
277
|
+
toImageDiffEqual : function (expected, tolerance) {
|
278
|
+
|
279
|
+
if (typeof (document) !== UNDEFINED) {
|
280
|
+
this.message = function () {
|
281
|
+
var
|
282
|
+
div = get('div'),
|
283
|
+
a = get('div', '<div>Actual:</div>'),
|
284
|
+
b = get('div', '<div>Expected:</div>'),
|
285
|
+
c = get('div', '<div>Diff:</div>'),
|
286
|
+
diff = imagediff.diff(this.actual, expected),
|
287
|
+
canvas = getCanvas(),
|
288
|
+
context;
|
289
|
+
|
290
|
+
canvas.height = diff.height;
|
291
|
+
canvas.width = diff.width;
|
292
|
+
|
293
|
+
div.style.overflow = 'hidden';
|
294
|
+
a.style.float = 'left';
|
295
|
+
b.style.float = 'left';
|
296
|
+
c.style.float = 'left';
|
297
|
+
|
298
|
+
context = canvas.getContext('2d');
|
299
|
+
context.putImageData(diff, 0, 0);
|
300
|
+
|
301
|
+
a.appendChild(toCanvas(this.actual));
|
302
|
+
b.appendChild(toCanvas(expected));
|
303
|
+
c.appendChild(canvas);
|
304
|
+
|
305
|
+
div.appendChild(a);
|
306
|
+
div.appendChild(b);
|
307
|
+
div.appendChild(c);
|
308
|
+
|
309
|
+
return [
|
310
|
+
div,
|
311
|
+
"Expected not to be equal."
|
312
|
+
];
|
313
|
+
};
|
314
|
+
}
|
315
|
+
|
316
|
+
return imagediff.equal(this.actual, expected, tolerance);
|
317
|
+
}
|
318
|
+
};
|
319
|
+
|
320
|
+
|
321
|
+
// Image Output
|
322
|
+
function imageDataToPNG (imageData, outputFile, callback) {
|
323
|
+
|
324
|
+
var
|
325
|
+
canvas = toCanvas(imageData),
|
326
|
+
base64Data,
|
327
|
+
decodedImage;
|
328
|
+
|
329
|
+
callback = callback || Function;
|
330
|
+
|
331
|
+
base64Data = canvas.toDataURL().replace(/^data:image\/\w+;base64,/,"");
|
332
|
+
decodedImage = new Buffer(base64Data, 'base64');
|
333
|
+
require('fs').writeFile(outputFile, decodedImage, callback);
|
334
|
+
}
|
335
|
+
|
336
|
+
|
337
|
+
// Definition
|
338
|
+
imagediff = {
|
339
|
+
|
340
|
+
createCanvas : getCanvas,
|
341
|
+
createImageData : getImageData,
|
342
|
+
|
343
|
+
isImage : isImage,
|
344
|
+
isCanvas : isCanvas,
|
345
|
+
isContext : isContext,
|
346
|
+
isImageData : isImageData,
|
347
|
+
isImageType : isImageType,
|
348
|
+
|
349
|
+
toImageData : function (object) {
|
350
|
+
checkType(object);
|
351
|
+
if (isImageData(object)) { return copyImageData(object); }
|
352
|
+
return toImageData(object);
|
353
|
+
},
|
354
|
+
|
355
|
+
equal : function (a, b, tolerance) {
|
356
|
+
checkType(a, b);
|
357
|
+
a = toImageData(a);
|
358
|
+
b = toImageData(b);
|
359
|
+
return equal(a, b, tolerance);
|
360
|
+
},
|
361
|
+
diff : function (a, b) {
|
362
|
+
checkType(a, b);
|
363
|
+
a = toImageData(a);
|
364
|
+
b = toImageData(b);
|
365
|
+
return diff(a, b);
|
366
|
+
},
|
367
|
+
|
368
|
+
jasmine : jasmine,
|
369
|
+
|
370
|
+
// Compatibility
|
371
|
+
noConflict : function () {
|
372
|
+
root[name] = previous;
|
373
|
+
return imagediff;
|
374
|
+
}
|
375
|
+
};
|
376
|
+
|
377
|
+
if (typeof module !== 'undefined') {
|
378
|
+
imagediff.imageDataToPNG = imageDataToPNG;
|
379
|
+
}
|
380
|
+
|
381
|
+
return imagediff;
|
382
|
+
});
|