diffux_ci 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/{diffux_ci → diffux} +15 -13
- data/lib/diffux_ci_runner.rb +47 -29
- data/lib/diffux_ci_version.rb +1 -1
- data/lib/public/diffux_ci-runner.js +63 -55
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5143ca3e39e34a1ea00043f6f20814a72971b16
|
4
|
+
data.tar.gz: 6fced8ad6eccd338a886769b2ca755768ab9c0f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 758fe05b11a9edcc343acc0cd9988615e2035432675e88de8b2837941f2c1704de1b9d6cd2a31102df05fd1b5276dc0a7b439b83a987a7e421e0b0177e7a8679
|
7
|
+
data.tar.gz: feb1f5d30adea55fc3fc0f467f1079eb629d09b15a6b0248ebfe60a91d2f2679a9e086a789c5822fbac5cf7611831358090be701aaa4ea7378e97859df8fe114
|
data/bin/{diffux_ci → diffux}
RENAMED
@@ -6,6 +6,19 @@ require 'diffux_ci_uploader'
|
|
6
6
|
require 'diffux_ci_version'
|
7
7
|
require 'fileutils'
|
8
8
|
|
9
|
+
help_text = <<-EOS
|
10
|
+
Commands:
|
11
|
+
run (default)
|
12
|
+
debug
|
13
|
+
review
|
14
|
+
clean
|
15
|
+
approve
|
16
|
+
reject
|
17
|
+
upload_diffs
|
18
|
+
--help
|
19
|
+
--version
|
20
|
+
EOS
|
21
|
+
|
9
22
|
action = ARGV[0] || 'run'
|
10
23
|
case action
|
11
24
|
when 'run'
|
@@ -44,18 +57,7 @@ when '--version'
|
|
44
57
|
puts "diffux_ci version #{DiffuxCI::VERSION}"
|
45
58
|
|
46
59
|
when '--help'
|
47
|
-
puts
|
48
|
-
Commands:
|
49
|
-
run (default)
|
50
|
-
debug
|
51
|
-
review
|
52
|
-
clean
|
53
|
-
approve
|
54
|
-
reject
|
55
|
-
upload_diffs
|
56
|
-
--help
|
57
|
-
--version
|
58
|
-
EOS
|
60
|
+
puts help_text
|
59
61
|
else
|
60
|
-
abort "Unknown action \"#{action}\""
|
62
|
+
abort "Unknown action \"#{action}\"\n\n#{help_text}"
|
61
63
|
end
|
data/lib/diffux_ci_runner.rb
CHANGED
@@ -50,30 +50,43 @@ begin
|
|
50
50
|
fail "JavaScript errors found during initialization: \n#{errors.inspect}"
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
#
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
53
|
+
all_examples = driver.execute_script('return window.diffux.getAllExamples()')
|
54
|
+
|
55
|
+
# To avoid the overhead of resizing the window all the time, we are going to
|
56
|
+
# render all examples for each given viewport size all in one go.
|
57
|
+
examples_by_viewport = {}
|
58
|
+
|
59
|
+
all_examples.each do |example|
|
60
|
+
viewports = resolve_viewports(example)
|
61
|
+
|
62
|
+
viewports.each do |viewport|
|
63
|
+
examples_by_viewport[viewport['name']] ||= {}
|
64
|
+
examples_by_viewport[viewport['name']][:viewport] ||= viewport
|
65
|
+
examples_by_viewport[viewport['name']][:examples] ||= []
|
66
|
+
|
67
|
+
examples_by_viewport[viewport['name']][:examples] << example
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
examples_by_viewport.each do |_, example_by_viewport|
|
72
|
+
viewport = example_by_viewport[:viewport]
|
73
|
+
examples = example_by_viewport[:examples]
|
74
|
+
|
75
|
+
puts "#{viewport['name']} (#{viewport['width']}x#{viewport['height']})"
|
76
|
+
|
77
|
+
# Resize window to the right size before rendering
|
78
|
+
driver.manage.window.resize_to(viewport['width'], viewport['height'])
|
79
|
+
|
80
|
+
examples.each do |example|
|
81
|
+
if example == examples.last
|
82
|
+
print '└─ '
|
71
83
|
else
|
72
|
-
|
84
|
+
print '├─ '
|
73
85
|
end
|
86
|
+
description = example['description']
|
87
|
+
print " #{description} "
|
74
88
|
|
75
|
-
|
76
|
-
driver.manage.window.resize_to(viewport['width'], viewport['height'])
|
89
|
+
print '.'
|
77
90
|
|
78
91
|
# Render the example
|
79
92
|
|
@@ -84,15 +97,16 @@ begin
|
|
84
97
|
# through to Rubyland), or until WebDriver's `script_timeout` is reached,
|
85
98
|
# before continuing. Since we don't define the signature of this function,
|
86
99
|
# we can't name the argument so we access it using JavaScript's magic
|
87
|
-
# arguments object and pass it down to `
|
100
|
+
# arguments object and pass it down to `renderExample()` which calls it
|
88
101
|
# when it is done--either synchronously if our example doesn't take an
|
89
102
|
# argument, or asynchronously via the Promise and `done` callback if it
|
90
103
|
# does.
|
91
104
|
script = <<-EOS
|
92
105
|
var doneFunc = arguments[arguments.length - 1];
|
93
|
-
window.diffux.
|
106
|
+
window.diffux.renderExample(arguments[0], doneFunc);
|
94
107
|
EOS
|
95
|
-
rendered = driver.execute_async_script(script)
|
108
|
+
rendered = driver.execute_async_script(script, description)
|
109
|
+
print '.'
|
96
110
|
|
97
111
|
if rendered['error']
|
98
112
|
fail <<-EOS
|
@@ -105,6 +119,7 @@ begin
|
|
105
119
|
|
106
120
|
# Crop the screenshot to the size of the rendered element
|
107
121
|
screenshot = ChunkyPNG::Image.from_blob(driver.screenshot_as(:png))
|
122
|
+
print '.'
|
108
123
|
|
109
124
|
# In our JavScript we are rounding up, which can sometimes give us a
|
110
125
|
# dimensions that are larger than the screenshot dimensions. We need to
|
@@ -122,8 +137,7 @@ begin
|
|
122
137
|
rendered['top'],
|
123
138
|
crop_width,
|
124
139
|
crop_height)
|
125
|
-
|
126
|
-
print "Checking \"#{description}\" at [#{viewport['name']}]... "
|
140
|
+
print '.'
|
127
141
|
|
128
142
|
# Run the diff if needed
|
129
143
|
baseline_path = DiffuxCIUtils.path_to(
|
@@ -136,6 +150,7 @@ begin
|
|
136
150
|
ChunkyPNG::Image.from_file(baseline_path),
|
137
151
|
screenshot
|
138
152
|
).compare!
|
153
|
+
print '.'
|
139
154
|
|
140
155
|
if comparison[:diff_image]
|
141
156
|
# There was a visual difference between the new snapshot and the
|
@@ -144,16 +159,18 @@ begin
|
|
144
159
|
diff_path = DiffuxCIUtils.path_to(
|
145
160
|
description, viewport['name'], 'diff.png')
|
146
161
|
comparison[:diff_image].save(diff_path, :fast_rgba)
|
162
|
+
print '.'
|
147
163
|
|
148
164
|
candidate_path = DiffuxCIUtils.path_to(
|
149
165
|
description, viewport['name'], 'candidate.png')
|
150
166
|
screenshot.save(candidate_path, :fast_rgba)
|
167
|
+
print '.'
|
151
168
|
|
152
|
-
puts "#{comparison[:diff_in_percent].round(1)}% (#{candidate_path})"
|
169
|
+
puts " #{comparison[:diff_in_percent].round(1)}% (#{candidate_path})"
|
153
170
|
else
|
154
171
|
# No visual difference was found, so we don't need to do any more
|
155
172
|
# work.
|
156
|
-
puts 'No diff.'
|
173
|
+
puts ' No diff.'
|
157
174
|
end
|
158
175
|
else
|
159
176
|
# There was no baseline image yet, so we want to start by saving a new
|
@@ -164,7 +181,8 @@ begin
|
|
164
181
|
FileUtils.mkdir_p(dirname)
|
165
182
|
end
|
166
183
|
screenshot.save(baseline_path, :fast_rgba)
|
167
|
-
|
184
|
+
print '.'
|
185
|
+
puts " First snapshot created (#{baseline_path})"
|
168
186
|
end
|
169
187
|
end
|
170
188
|
end
|
data/lib/diffux_ci_version.rb
CHANGED
@@ -1,52 +1,52 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
3
|
window.diffux = {
|
4
|
-
defined:
|
5
|
-
|
6
|
-
currentExample: undefined,
|
4
|
+
defined: {},
|
5
|
+
fdefined: [],
|
7
6
|
currentRenderedElement: undefined,
|
8
7
|
errors: [],
|
9
8
|
|
10
9
|
define: function(description, func, options) {
|
11
|
-
|
10
|
+
// Make sure we don't have a duplicate description
|
11
|
+
if (this.defined[description]) {
|
12
|
+
throw 'Error while defining "' + description +
|
13
|
+
'": Duplicate description detected'
|
14
|
+
}
|
15
|
+
this.defined[description] = {
|
12
16
|
description: description,
|
13
17
|
func: func,
|
14
18
|
options: options || {}
|
15
|
-
}
|
19
|
+
};
|
16
20
|
},
|
17
21
|
|
18
|
-
fdefine: function() {
|
19
|
-
this.
|
20
|
-
this.
|
21
|
-
this.define = function() {}; // make `define` a no-op from now on
|
22
|
+
fdefine: function(description, func, options) {
|
23
|
+
this.define(description, func, options); // add the example
|
24
|
+
this.fdefined.push(description);
|
22
25
|
},
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
/**
|
28
|
+
* @return {Array.<Object>}
|
29
|
+
*/
|
30
|
+
getAllExamples: function() {
|
31
|
+
var descriptions = Object.keys(this.defined);
|
32
|
+
|
33
|
+
if (this.fdefined.length) {
|
34
|
+
// Some examples have been focused, so we want to filter out anything that
|
35
|
+
// has not been focused.
|
36
|
+
descriptions = descriptions.filter(function(description) {
|
37
|
+
return this.fdefined.indexOf(description) !== -1;
|
38
|
+
}.bind(this));
|
36
39
|
}
|
37
|
-
this.currentIndex++;
|
38
|
-
return this.currentExample;
|
39
|
-
},
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
return descriptions.map(function(description) {
|
42
|
+
var example = this.defined[description];
|
43
|
+
// We return a subset of the properties of an example (only those relevant
|
44
|
+
// for diffux_runner.rb).
|
45
|
+
return {
|
46
|
+
description: example.description,
|
47
|
+
options: example.options,
|
48
|
+
};
|
46
49
|
}.bind(this));
|
47
|
-
if (!this.currentExample) {
|
48
|
-
throw 'No example found with description "' + exampleDescription + '"';
|
49
|
-
}
|
50
50
|
},
|
51
51
|
|
52
52
|
isElementVisible: function(element) {
|
@@ -66,10 +66,10 @@ window.diffux = {
|
|
66
66
|
}
|
67
67
|
},
|
68
68
|
|
69
|
-
handleError: function(error) {
|
69
|
+
handleError: function(currentExample, error) {
|
70
70
|
console.error(error);
|
71
71
|
return {
|
72
|
-
description:
|
72
|
+
description: currentExample.description,
|
73
73
|
error: error.message
|
74
74
|
};
|
75
75
|
},
|
@@ -107,49 +107,58 @@ window.diffux = {
|
|
107
107
|
},
|
108
108
|
|
109
109
|
/**
|
110
|
+
* Clean up the DOM for a rendered element that has already been processed.
|
111
|
+
*
|
112
|
+
* @param {Object} renderedElement
|
113
|
+
*/
|
114
|
+
cleanOutElement: function(renderedElement) {
|
115
|
+
renderedElement.parentNode.removeChild(renderedElement);
|
116
|
+
},
|
117
|
+
|
118
|
+
/**
|
119
|
+
* @param {String} exampleDescription
|
110
120
|
* @param {Function} doneFunc injected by driver.execute_async_script in
|
111
121
|
* diffux_ci_runner.rb
|
112
122
|
*/
|
113
|
-
|
123
|
+
renderExample: function(exampleDescription, doneFunc) {
|
124
|
+
var currentExample = this.defined[exampleDescription];
|
125
|
+
if (!currentExample) {
|
126
|
+
throw 'No example found with description "' + exampleDescription + '"';
|
127
|
+
}
|
128
|
+
|
114
129
|
try {
|
130
|
+
if (this.currentRenderedElement) {
|
131
|
+
this.cleanOutElement(this.currentRenderedElement);
|
132
|
+
}
|
115
133
|
this.clearVisibleElements();
|
116
134
|
|
117
|
-
var func =
|
135
|
+
var func = currentExample.func;
|
118
136
|
if (func.length) {
|
119
137
|
// The function takes an argument, which is a callback that is called
|
120
138
|
// once it is done executing. This can be used to write functions that
|
121
139
|
// have asynchronous code in them.
|
122
140
|
this.tryAsync(func).then(function(elem) {
|
123
|
-
doneFunc(this.processElem(elem));
|
141
|
+
doneFunc(this.processElem(currentExample, elem));
|
124
142
|
}.bind(this)).catch(function(error) {
|
125
|
-
doneFunc(this.handleError(error));
|
143
|
+
doneFunc(this.handleError(currentExample, error));
|
126
144
|
}.bind(this));
|
127
145
|
} else {
|
128
146
|
// The function does not take an argument, so we can run it
|
129
147
|
// synchronously.
|
130
148
|
var elem = func();
|
131
|
-
doneFunc(this.processElem(elem));
|
149
|
+
doneFunc(this.processElem(currentExample, elem));
|
132
150
|
}
|
133
151
|
} catch (error) {
|
134
|
-
doneFunc(this.handleError(error));
|
152
|
+
doneFunc(this.handleError(currentExample, error));
|
135
153
|
}
|
136
154
|
},
|
137
155
|
|
138
|
-
processElem: function(elem) {
|
156
|
+
processElem: function(currentExample, elem) {
|
139
157
|
try {
|
140
|
-
// TODO: elem.getDOMNode is deprecated in React, so we need to convert
|
141
|
-
// this to ReactDOM.findDOMNode(elem) at some point, or push this
|
142
|
-
// requirement into the examples.
|
143
|
-
if (elem.getDOMNode) {
|
144
|
-
// Soft-dependency to React here. If the thing returned has a
|
145
|
-
// `getDOMNode` method, call it to get the real DOM node.
|
146
|
-
elem = elem.getDOMNode();
|
147
|
-
}
|
148
|
-
|
149
158
|
this.currentRenderedElement = elem;
|
150
159
|
|
151
160
|
var rect;
|
152
|
-
if (
|
161
|
+
if (currentExample.options.snapshotEntireScreen) {
|
153
162
|
rect = {
|
154
163
|
width: window.innerWidth,
|
155
164
|
height: window.innerHeight,
|
@@ -168,14 +177,14 @@ window.diffux = {
|
|
168
177
|
}
|
169
178
|
|
170
179
|
return {
|
171
|
-
description:
|
180
|
+
description: currentExample.description,
|
172
181
|
width: Math.ceil(rect.width),
|
173
182
|
height: Math.ceil(rect.height),
|
174
183
|
top: Math.floor(rect.top),
|
175
184
|
left: Math.floor(rect.left),
|
176
185
|
};
|
177
186
|
} catch (error) {
|
178
|
-
return this.handleError(error);
|
187
|
+
return this.handleError(currentExample, error);
|
179
188
|
}
|
180
189
|
}
|
181
190
|
};
|
@@ -186,8 +195,7 @@ window.addEventListener('load', function() {
|
|
186
195
|
return;
|
187
196
|
}
|
188
197
|
var example = decodeURIComponent(matches[1]);
|
189
|
-
window.diffux.
|
190
|
-
window.diffux.renderCurrent(function() {});
|
198
|
+
window.diffux.renderExample(example, function() {});
|
191
199
|
});
|
192
200
|
|
193
201
|
// We need to redefine a few global functions that halt execution. Without this,
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: diffux_ci
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henric Trotzig
|
@@ -130,11 +130,11 @@ email:
|
|
130
130
|
- henric.trotzig@gmail.com
|
131
131
|
- joe.lencioni@gmail.com
|
132
132
|
executables:
|
133
|
-
-
|
133
|
+
- diffux
|
134
134
|
extensions: []
|
135
135
|
extra_rdoc_files: []
|
136
136
|
files:
|
137
|
-
- bin/
|
137
|
+
- bin/diffux
|
138
138
|
- lib/diffux_ci-diffs.html.erb
|
139
139
|
- lib/diffux_ci_action.rb
|
140
140
|
- lib/diffux_ci_runner.rb
|
@@ -167,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
167
|
version: '0'
|
168
168
|
requirements: []
|
169
169
|
rubyforge_project:
|
170
|
-
rubygems_version: 2.4.5
|
170
|
+
rubygems_version: 2.4.5.1
|
171
171
|
signing_key:
|
172
172
|
specification_version: 4
|
173
173
|
summary: Diffux-CI
|