diffux_ci 0.2.0 → 0.3.0
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 +6 -0
- data/lib/diffux_ci_runner.rb +38 -18
- data/lib/diffux_ci_utils.rb +10 -3
- data/lib/diffux_ci_version.rb +1 -1
- data/lib/public/diffux_ci-runner.js +33 -18
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 792c6b08f42ae6f8715b7283bbd5e48de674281d
|
4
|
+
data.tar.gz: fe881d359ff8ac648c74802f0092ff862184ac1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 777f67034c3e7cd0bb60f246892cebebfb982602acddbd957f004f98b73cb8e06a273ae4c77002f55eca5b4128eade1b76f200a4872489f50d4dff1eee0bb105
|
7
|
+
data.tar.gz: ec413e930adecf64943f5f95d7f9d6bd519e63fa37c063b07297e2933249ae9ba9c328cbc0b0c40fe5f60b584eaaee91d4c2e7211475904ff6eb31b98f196692
|
data/bin/diffux_ci
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'diffux_ci_utils'
|
4
4
|
require 'diffux_ci_action'
|
5
5
|
require 'diffux_ci_uploader'
|
6
|
+
require 'diffux_ci_version'
|
6
7
|
require 'fileutils'
|
7
8
|
|
8
9
|
action = ARGV[0] || 'run'
|
@@ -39,6 +40,9 @@ when 'upload_diffs'
|
|
39
40
|
# `upload_diffs` returns a URL to a static html file
|
40
41
|
puts DiffuxCIUploader.new.upload_diffs
|
41
42
|
|
43
|
+
when '--version'
|
44
|
+
puts "diffux_ci version #{DiffuxCI::VERSION}"
|
45
|
+
|
42
46
|
when '--help'
|
43
47
|
puts <<-EOS
|
44
48
|
Commands:
|
@@ -49,6 +53,8 @@ Commands:
|
|
49
53
|
approve
|
50
54
|
reject
|
51
55
|
upload_diffs
|
56
|
+
--help
|
57
|
+
--version
|
52
58
|
EOS
|
53
59
|
else
|
54
60
|
abort "Unknown action \"#{action}\""
|
data/lib/diffux_ci_runner.rb
CHANGED
@@ -20,21 +20,28 @@ def resolve_viewports(example)
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
23
|
+
def init_driver
|
24
|
+
tries = 0
|
25
|
+
begin
|
26
|
+
driver = Selenium::WebDriver.for DiffuxCIUtils.config['driver'].to_sym
|
27
|
+
rescue Selenium::WebDriver::Error::WebDriverError => e
|
28
|
+
# "unable to obtain stable firefox connection in 60 seconds"
|
29
|
+
#
|
30
|
+
# This seems to happen sporadically for some versions of Firefox, so we want
|
31
|
+
# to retry a couple of times it in case it will work the second time around.
|
32
|
+
tries += 1
|
33
|
+
retry if tries <= 3
|
34
|
+
raise e
|
35
|
+
end
|
36
|
+
|
37
|
+
driver.manage.timeouts.script_timeout = 3 # move to config?
|
38
|
+
|
39
|
+
driver
|
34
40
|
end
|
35
41
|
|
42
|
+
driver = init_driver
|
43
|
+
|
36
44
|
begin
|
37
|
-
driver.manage.timeouts.script_timeout = 3 # move to config?
|
38
45
|
driver.navigate.to DiffuxCIUtils.construct_url('/')
|
39
46
|
|
40
47
|
# Check for errors during startup
|
@@ -98,10 +105,23 @@ begin
|
|
98
105
|
|
99
106
|
# Crop the screenshot to the size of the rendered element
|
100
107
|
screenshot = ChunkyPNG::Image.from_blob(driver.screenshot_as(:png))
|
108
|
+
|
109
|
+
# In our JavScript we are rounding up, which can sometimes give us a
|
110
|
+
# dimensions that are larger than the screenshot dimensions. We need to
|
111
|
+
# guard against that here.
|
112
|
+
crop_width = [
|
113
|
+
[rendered['width'], 1].max,
|
114
|
+
screenshot.width - rendered['left']
|
115
|
+
].min
|
116
|
+
crop_height = [
|
117
|
+
[rendered['height'], 1].max,
|
118
|
+
screenshot.height - rendered['top']
|
119
|
+
].min
|
120
|
+
|
101
121
|
screenshot.crop!(rendered['left'],
|
102
122
|
rendered['top'],
|
103
|
-
|
104
|
-
|
123
|
+
crop_width,
|
124
|
+
crop_height)
|
105
125
|
|
106
126
|
print "Checking \"#{description}\" at [#{viewport['name']}]... "
|
107
127
|
|
@@ -123,13 +143,13 @@ begin
|
|
123
143
|
# image to disk. This will allow it to be reviewed by someone.
|
124
144
|
diff_path = DiffuxCIUtils.path_to(
|
125
145
|
description, viewport['name'], 'diff.png')
|
126
|
-
comparison[:diff_image].save(diff_path)
|
146
|
+
comparison[:diff_image].save(diff_path, :fast_rgb)
|
127
147
|
|
128
148
|
candidate_path = DiffuxCIUtils.path_to(
|
129
149
|
description, viewport['name'], 'candidate.png')
|
130
|
-
screenshot.save(candidate_path)
|
150
|
+
screenshot.save(candidate_path, :fast_rgb)
|
131
151
|
|
132
|
-
puts "#{comparison[:diff_in_percent].round(1)}% (#{
|
152
|
+
puts "#{comparison[:diff_in_percent].round(1)}% (#{candidate_path})"
|
133
153
|
else
|
134
154
|
# No visual difference was found, so we don't need to do any more
|
135
155
|
# work.
|
@@ -143,7 +163,7 @@ begin
|
|
143
163
|
unless File.directory?(dirname = File.dirname(baseline_path))
|
144
164
|
FileUtils.mkdir_p(dirname)
|
145
165
|
end
|
146
|
-
screenshot.save(baseline_path)
|
166
|
+
screenshot.save(baseline_path, :fast_rgb)
|
147
167
|
puts "First snapshot created (#{baseline_path})"
|
148
168
|
end
|
149
169
|
end
|
data/lib/diffux_ci_utils.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'erb'
|
3
|
+
require 'uri'
|
3
4
|
|
4
5
|
class DiffuxCIUtils
|
5
6
|
def self.config
|
6
|
-
config_file_name = ENV['DIFFUX_CI_CONFIG_FILE'] || '.diffux_ci.yaml'
|
7
7
|
@@config ||= {
|
8
8
|
'snapshots_folder' => './snapshots',
|
9
9
|
'source_files' => [],
|
@@ -24,7 +24,12 @@ class DiffuxCIUtils
|
|
24
24
|
'height' => 444
|
25
25
|
}
|
26
26
|
}
|
27
|
-
}.merge(
|
27
|
+
}.merge(config_from_file)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.config_from_file
|
31
|
+
config_file_name = ENV['DIFFUX_CI_CONFIG_FILE'] || '.diffux_ci.yaml'
|
32
|
+
YAML.load(ERB.new(File.read(config_file_name)).result)
|
28
33
|
end
|
29
34
|
|
30
35
|
def self.normalize_description(description)
|
@@ -41,10 +46,12 @@ class DiffuxCIUtils
|
|
41
46
|
end
|
42
47
|
|
43
48
|
def self.construct_url(absolute_path, params = {})
|
49
|
+
query = URI.encode_www_form(params) unless params.empty?
|
50
|
+
|
44
51
|
URI::HTTP.build(host: 'localhost',
|
45
52
|
port: config['port'],
|
46
53
|
path: absolute_path,
|
47
|
-
query:
|
54
|
+
query: query).to_s
|
48
55
|
end
|
49
56
|
|
50
57
|
def self.current_snapshots
|
data/lib/diffux_ci_version.rb
CHANGED
@@ -49,11 +49,18 @@ window.diffux = {
|
|
49
49
|
}
|
50
50
|
},
|
51
51
|
|
52
|
+
isElementVisible: function(element) {
|
53
|
+
// element.offsetParent is a cheap way to determine visibility for most
|
54
|
+
// elements, but it doesn't work for elements with fixed positioning so we
|
55
|
+
// will need to fall back to the more expensive getComputedStyle.
|
56
|
+
return element.offsetParent ||
|
57
|
+
window.getComputedStyle(element).display !== 'none';
|
58
|
+
},
|
59
|
+
|
52
60
|
clearVisibleElements: function() {
|
53
61
|
var allElements = document.querySelectorAll('body > *');
|
54
62
|
for (var element of allElements) {
|
55
|
-
|
56
|
-
if (style.display !== 'none') {
|
63
|
+
if (this.isElementVisible(element)) {
|
57
64
|
element.parentNode.removeChild(element);
|
58
65
|
}
|
59
66
|
}
|
@@ -75,7 +82,7 @@ window.diffux = {
|
|
75
82
|
*/
|
76
83
|
tryAsync: function(func) {
|
77
84
|
return new Promise(function(resolve, reject) {
|
78
|
-
//
|
85
|
+
// Safety valve: if the function does not finish after 3s, then something
|
79
86
|
// went haywire and we need to move on.
|
80
87
|
var timeout = setTimeout(function() {
|
81
88
|
reject(new Error('Async callback was not invoked within timeout.'));
|
@@ -131,8 +138,8 @@ window.diffux = {
|
|
131
138
|
processElem: function(elem) {
|
132
139
|
try {
|
133
140
|
// TODO: elem.getDOMNode is deprecated in React, so we need to convert
|
134
|
-
// this to
|
135
|
-
// into the examples.
|
141
|
+
// this to ReactDOM.findDOMNode(elem) at some point, or push this
|
142
|
+
// requirement into the examples.
|
136
143
|
if (elem.getDOMNode) {
|
137
144
|
// Soft-dependency to React here. If the thing returned has a
|
138
145
|
// `getDOMNode` method, call it to get the real DOM node.
|
@@ -141,23 +148,31 @@ window.diffux = {
|
|
141
148
|
|
142
149
|
this.currentRenderedElement = elem;
|
143
150
|
|
144
|
-
var
|
145
|
-
var height = elem.offsetHeight;
|
146
|
-
var top = elem.offsetTop;
|
147
|
-
var left = elem.offsetLeft;
|
148
|
-
|
151
|
+
var rect;
|
149
152
|
if (this.currentExample.options.snapshotEntireScreen) {
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
153
|
+
rect = {
|
154
|
+
width: window.innerWidth,
|
155
|
+
height: window.innerHeight,
|
156
|
+
top: 0,
|
157
|
+
left: 0,
|
158
|
+
};
|
159
|
+
} else {
|
160
|
+
// We use elem.getBoundingClientRect() instead of offsetTop and its ilk
|
161
|
+
// because elem.getBoundingClientRect() is more accurate and it also
|
162
|
+
// takes CSS transformations and other things of that nature into
|
163
|
+
// account whereas offsetTop and company do not.
|
164
|
+
//
|
165
|
+
// Note that this method returns floats, so we need to round those off
|
166
|
+
// to integers before returning.
|
167
|
+
rect = elem.getBoundingClientRect();
|
154
168
|
}
|
169
|
+
|
155
170
|
return {
|
156
171
|
description: this.currentExample.description,
|
157
|
-
width: width,
|
158
|
-
height: height,
|
159
|
-
top: top,
|
160
|
-
left: left
|
172
|
+
width: Math.ceil(rect.width),
|
173
|
+
height: Math.ceil(rect.height),
|
174
|
+
top: Math.floor(rect.top),
|
175
|
+
left: Math.floor(rect.left),
|
161
176
|
};
|
162
177
|
} catch (error) {
|
163
178
|
return this.handleError(error);
|