diffux_ci 0.3.1 → 0.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0434bae3bc243b30200763f7503dec75c2f884c1
4
- data.tar.gz: fab849f9e3bad3c2286f689beca20ab2bcee7957
3
+ metadata.gz: e5143ca3e39e34a1ea00043f6f20814a72971b16
4
+ data.tar.gz: 6fced8ad6eccd338a886769b2ca755768ab9c0f9
5
5
  SHA512:
6
- metadata.gz: 3cd2f4c3c22a01247ad075259954c2c8815a412b7f77fb20c201210877f826b515a6e9fdf62e660492dba84bf02ceb410c28526781485460680046306f9145ea
7
- data.tar.gz: 5c516aff788a20ad96cf7e9992647940b7d481f5edc70de3dd3cf32956e708cbe57d896492144bdb5883bfae6b0b20320eb004d74cb4c55e4472b20872a4c414
6
+ metadata.gz: 758fe05b11a9edcc343acc0cd9988615e2035432675e88de8b2837941f2c1704de1b9d6cd2a31102df05fd1b5276dc0a7b439b83a987a7e421e0b0177e7a8679
7
+ data.tar.gz: feb1f5d30adea55fc3fc0f467f1079eb629d09b15a6b0248ebfe60a91d2f2679a9e086a789c5822fbac5cf7611831358090be701aaa4ea7378e97859df8fe114
@@ -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 <<-EOS
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
@@ -50,30 +50,43 @@ begin
50
50
  fail "JavaScript errors found during initialization: \n#{errors.inspect}"
51
51
  end
52
52
 
53
- # We use the description of the example to store the snapshot. If a
54
- # description is duplicated with different code, it can cause seemingly random
55
- # and confusing differences. To avoid this issue, we want to keep track of the
56
- # descriptions that we've seen and fail if we come across the same description
57
- # twice.
58
- seen_descriptions = {}
59
-
60
- while current = driver.execute_script('return window.diffux.next()') do
61
- description = current['description']
62
-
63
- resolve_viewports(current).each do |viewport|
64
- # Make sure we don't have a duplicate description
65
- seen_descriptions[description] ||= {}
66
- if seen_descriptions[description][viewport['name']]
67
- fail <<-EOS
68
- Error while rendering "#{description}" @#{viewport['name']}:
69
- Duplicate description detected
70
- EOS
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
- seen_descriptions[description][viewport['name']] = true
84
+ print '├─ '
73
85
  end
86
+ description = example['description']
87
+ print " #{description} "
74
88
 
75
- # Resize window to the right size before rendering
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 `renderCurrent()` which calls it
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.renderCurrent(doneFunc);
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
- puts "First snapshot created (#{baseline_path})"
184
+ print '.'
185
+ puts " First snapshot created (#{baseline_path})"
168
186
  end
169
187
  end
170
188
  end
@@ -1,4 +1,4 @@
1
1
  # Defines the gem version.
2
2
  module DiffuxCI
3
- VERSION = '0.3.1'
3
+ VERSION = '0.3.2'
4
4
  end
@@ -1,52 +1,52 @@
1
1
  'use strict';
2
2
 
3
3
  window.diffux = {
4
- defined: [],
5
- currentIndex: 0,
6
- currentExample: undefined,
4
+ defined: {},
5
+ fdefined: [],
7
6
  currentRenderedElement: undefined,
8
7
  errors: [],
9
8
 
10
9
  define: function(description, func, options) {
11
- this.defined.push({
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.defined = []; // clear out all previously added examples
20
- this.define.apply(this, arguments); // add the example
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
- next: function() {
25
- if (this.currentRenderedElement) {
26
- if (window.React) {
27
- window.React.unmountComponentAtNode(document.body.lastChild);
28
- } else {
29
- this.currentRenderedElement.parentNode
30
- .removeChild(this.currentRenderedElement);
31
- }
32
- }
33
- this.currentExample = this.defined[this.currentIndex];
34
- if (!this.currentExample) {
35
- return;
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
- setCurrent: function(exampleDescription) {
42
- this.defined.forEach(function(example, index) {
43
- if (example.description === exampleDescription) {
44
- this.currentExample = example;
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: this.currentExample.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
- renderCurrent: function(doneFunc) {
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 = this.currentExample.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 (this.currentExample.options.snapshotEntireScreen) {
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: this.currentExample.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.setCurrent(example);
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.1
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
- - diffux_ci
133
+ - diffux
134
134
  extensions: []
135
135
  extra_rdoc_files: []
136
136
  files:
137
- - bin/diffux_ci
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