likadan 0.0.2 → 0.0.3

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: ba7c704150fa0176ec40681e44e1b605cd5dff34
4
- data.tar.gz: 9ecd0dc4806257c91574b12e1d336b3c82ab60ce
3
+ metadata.gz: 2bdf5b94276da8220ca5344b009e26e11e900afc
4
+ data.tar.gz: 55145af24ef76484e796629f18a980f5c332ff38
5
5
  SHA512:
6
- metadata.gz: 6862ffbbcf213a66b4c6ccc24142880ce6646f91885451eb647035558a3e1a1212e7b9f2cb2a2701fe02b1eb1b5383838d19fe104edabe71a73e3aa6276a6986
7
- data.tar.gz: 2a77b00434e19e1e82094899338c3ca2ae372ae47eeab3cc28fd9d7efb7939734f299dd3d8cea9845907085eff712d8de3a3553b074e3ddaa0d9782d1c65caf3
6
+ metadata.gz: 8109fd809d7d81f6c99779a972446e27402a7632b8cedc4a0288eecff586e7bbb3ee7e187694b7478de00a91c94c1672fdfdc6b956516370ef003144eb0bde2b
7
+ data.tar.gz: 038492a9885d98a506d0279a35a20a22e6db256200e598fc7a3c9813ff707c11c637d5010c6bda27f738b34d6de313ffe0004e3a6c6ff8295adaf5fd063d3dff
@@ -1,12 +1,30 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- Thread.new do # trivial example work thread
4
- begin
5
- require 'likadan_runner'
6
- ensure
7
- exit
3
+ require 'likadan_utils'
4
+
5
+ action = ARGV[0] || 'run'
6
+ case action
7
+ when 'run'
8
+ Thread.new do # trivial example work thread
9
+ begin
10
+ require 'likadan_runner'
11
+ ensure
12
+ puts 'Press Enter to exit...'
13
+ gets
14
+ exit
15
+ end
8
16
  end
9
- end
17
+ require 'likadan_server'
10
18
 
11
- require 'likadan_server'
19
+ when 'review'
20
+ system 'open', LikadanUtils.construct_url('/review')
21
+ require 'likadan_server'
12
22
 
23
+ when 'approve', 'reject'
24
+ require 'likadan_action'
25
+ abort 'Missing example name' unless example_name = ARGV[1]
26
+ abort 'Missing viewport width' unless width = ARGV[2]
27
+ LikadanAction.new(example_name, width).send(action)
28
+ else
29
+ abort "Unknown action \"#{action}\""
30
+ end
@@ -0,0 +1,29 @@
1
+ require 'likadan_utils'
2
+ require 'FileUtils'
3
+
4
+ class LikadanAction
5
+ def initialize(example_name, width)
6
+ @example_name = example_name
7
+ @width = width
8
+ end
9
+
10
+ def approve
11
+ diff_path = LikadanUtils.path_to(@example_name, @width, 'diff.png')
12
+ baseline_path = LikadanUtils.path_to(@example_name, @width, 'baseline.png')
13
+ candidate_path = LikadanUtils.path_to(@example_name, @width, 'candidate.png')
14
+
15
+ FileUtils.rm(diff_path, force: true)
16
+
17
+ if File.exist? candidate_path
18
+ FileUtils.mv(candidate_path, baseline_path)
19
+ end
20
+ end
21
+
22
+ def reject
23
+ diff_path = LikadanUtils.path_to(@example_name, @width, 'diff.png')
24
+ candidate_path = LikadanUtils.path_to(@example_name, @width, 'candidate.png')
25
+
26
+ FileUtils.rm(diff_path, force: true)
27
+ FileUtils.rm(candidate_path, force: true)
28
+ end
29
+ end
@@ -6,53 +6,57 @@ require 'diffux_core/snapshot_comparison_image/before'
6
6
  require 'diffux_core/snapshot_comparison_image/overlayed'
7
7
  require 'diffux_core/snapshot_comparison_image/after'
8
8
  require 'chunky_png'
9
- require 'yaml'
9
+ require 'likadan_utils'
10
10
 
11
11
  driver = Selenium::WebDriver.for :firefox
12
12
  begin
13
- config = YAML.load_file('.likadan.yaml')
14
- snapshots_folder = config['snapshots_folder'] || './snapshots'
15
-
16
- driver.navigate.to 'http://localhost:4567/'
13
+ driver.navigate.to LikadanUtils.construct_url('/')
17
14
 
18
15
  while current = driver.execute_script('return window.likadan.next()') do
19
- now = Time.now.to_i
20
- normalized_name = current['name'].gsub(/[^a-zA-Z0-9\-_]/, '_')
21
- file = "#{snapshots_folder}/#{normalized_name}/snapshot_#{now}.png"
22
- dirname = File.dirname(file)
23
- unless File.directory?(dirname)
24
- FileUtils.mkdir_p(dirname)
25
- end
26
-
27
- previous_png = Dir.glob(File.join(dirname, 'snapshot_*.png')).max do |a,b|
28
- File.ctime(a) <=> File.ctime(b)
29
- end
16
+ current['viewportWidths'].each do |width|
17
+ # Resize window to the right size before rendering
18
+ driver.manage.window.resize_to(width, width)
30
19
 
31
- driver.save_screenshot(file)
32
- to_crop = ChunkyPNG::Image.from_file(file)
33
- to_crop.crop!(0, 0, current['width'], current['height'])
34
- to_crop.save(file)
20
+ # Render the example
21
+ rendered = driver.execute_script('return window.likadan.renderCurrent()')
22
+ output_file = LikadanUtils.path_to(current['name'], width, 'candidate.png')
35
23
 
36
- print "Checking \"#{current['name']}\"... "
37
-
38
- if previous_png
39
- comparison = Diffux::SnapshotComparer.new(
40
- ChunkyPNG::Image.from_file(previous_png),
41
- ChunkyPNG::Image.from_file(file),
42
- ).compare!
24
+ # Create the folder structure if it doesn't already exist
25
+ unless File.directory?(dirname = File.dirname(output_file))
26
+ FileUtils.mkdir_p(dirname)
27
+ end
43
28
 
44
- if img = comparison[:diff_image]
45
- diff_output = File.join(dirname, "diff_#{now}.png")
46
- img.save(diff_output)
47
- puts "#{comparison[:diff_in_percent]}% (#{diff_output})"
29
+ # Save and crop the screenshot
30
+ driver.save_screenshot(output_file)
31
+ cropped = ChunkyPNG::Image.from_file(output_file)
32
+ cropped.crop!(0, 0, rendered['width'], rendered['height'])
33
+ cropped.save(output_file)
34
+
35
+ print "Checking \"#{current['name']}\" at #{width}px... "
36
+
37
+ # Run the diff if needed
38
+ baseline_file = LikadanUtils.path_to(current['name'], width, 'baseline.png')
39
+
40
+ if File.exist? baseline_file
41
+ comparison = Diffux::SnapshotComparer.new(
42
+ ChunkyPNG::Image.from_file(baseline_file),
43
+ cropped
44
+ ).compare!
45
+
46
+ if img = comparison[:diff_image]
47
+ diff_output = LikadanUtils.path_to(current['name'], width, 'diff.png')
48
+ img.save(diff_output)
49
+ puts "#{comparison[:diff_in_percent]}% (#{diff_output})"
50
+ else
51
+ File.delete(output_file)
52
+ puts 'No diff.'
53
+ end
48
54
  else
49
- puts 'No diff.'
55
+ File.rename(output_file, baseline_file)
56
+ puts "First snapshot created (#{baseline_file})"
50
57
  end
51
- else
52
- puts "First snapshot created (#{file})"
53
58
  end
54
59
  end
55
60
  ensure
56
61
  driver.quit
57
62
  end
58
-
@@ -1,30 +1,42 @@
1
1
  require 'sinatra/base'
2
2
  require 'yaml'
3
-
4
- def get_configuration
5
- config = {
6
- 'source_files' => [],
7
- 'stylesheets' => [],
8
- 'port' => 4567
9
- }
10
- config_file = '.likadan.yaml'
11
- if File.exist? config_file
12
- config = config.merge(YAML.load_file(config_file))
13
- end
14
- config
15
- end
3
+ require 'likadan_utils'
4
+ require 'likadan_action'
16
5
 
17
6
  class LikadanServer < Sinatra::Base
18
7
  configure do
19
8
  enable :static
20
- set :port, get_configuration['port']
9
+ set :port, LikadanUtils.config['port']
10
+ end
11
+
12
+ def current_snapshots
13
+ prepare_file = lambda do |file|
14
+ width_dir = File.expand_path('..', file)
15
+ name_dir = File.expand_path('..', width_dir)
16
+ {
17
+ name: File.basename(name_dir),
18
+ width: File.basename(width_dir).sub('@', '').to_i,
19
+ file: file,
20
+ }
21
+ end
22
+ diff_files = Dir.glob("#{LikadanUtils.config['snapshots_folder']}/**/diff.png")
23
+ baselines = Dir.glob("#{LikadanUtils.config['snapshots_folder']}/**/baseline.png")
24
+ {
25
+ diffs: diff_files.map(&prepare_file),
26
+ baselines: baselines.map(&prepare_file)
27
+ }
21
28
  end
22
29
 
23
30
  get '/' do
24
- @config = get_configuration
31
+ @config = LikadanUtils.config
25
32
  erb :index
26
33
  end
27
34
 
35
+ get '/review' do
36
+ @snapshots = current_snapshots
37
+ erb :review
38
+ end
39
+
28
40
  get '/resource' do
29
41
  file = params[:file]
30
42
  if file.start_with? 'http'
@@ -34,5 +46,15 @@ class LikadanServer < Sinatra::Base
34
46
  end
35
47
  end
36
48
 
49
+ post '/reject' do
50
+ LikadanAction.new(params[:name], params[:width]).reject
51
+ redirect back
52
+ end
53
+
54
+ post '/approve' do
55
+ LikadanAction.new(params[:name], params[:width]).approve
56
+ redirect back
57
+ end
58
+
37
59
  run!
38
60
  end
@@ -0,0 +1,29 @@
1
+ require 'yaml'
2
+
3
+ class LikadanUtils
4
+ def self.config
5
+ @@config ||= {
6
+ 'snapshots_folder' => './snapshots',
7
+ 'source_files' => [],
8
+ 'stylesheets' => [],
9
+ 'port' => 4567
10
+ }.merge(YAML.load_file('.likadan.yaml'))
11
+ end
12
+
13
+ def self.normalize_name(name)
14
+ name.gsub(/[^a-zA-Z0-9\-_]/, '_')
15
+ end
16
+
17
+ def self.path_to(name, width, file_name)
18
+ File.join(
19
+ config['snapshots_folder'],
20
+ normalize_name(name),
21
+ "@#{width}",
22
+ file_name
23
+ )
24
+ end
25
+
26
+ def self.construct_url(absolute_path)
27
+ return "http://localhost:#{config['port']}#{absolute_path}"
28
+ end
29
+ end
@@ -1,28 +1,73 @@
1
1
  window.likadan = {
2
2
  defined: [],
3
+ currentIndex: 0,
4
+ currentExample: undefined,
3
5
 
4
- define: function(name, func) {
6
+ define: function(name, func, viewportWidths, options) {
5
7
  this.defined.push({
6
8
  name: name,
7
- func: func
9
+ func: func,
10
+ viewportWidths: viewportWidths || [1024],
11
+ options: options || {
12
+ snapshotEntireScreen: false
13
+ }
8
14
  });
9
15
  },
10
16
 
11
17
  next: function() {
12
- var next = this.defined.shift();
13
- if (!next) {
18
+ this.currentExample = this.defined[this.currentIndex];
19
+ if (!this.currentExample) {
14
20
  return;
15
21
  }
16
- var out = document.getElementById('likadan-output');
22
+ this.currentIndex++;
23
+ return this.currentExample;
24
+ },
25
+
26
+ getOutputElement: function() {
27
+ return document.getElementById('likadan-output');
28
+ },
29
+
30
+ clearVisibleElements: function() {
31
+ var out = this.getOutputElement();
32
+ var allElements = Array.prototype.slice.call(document.querySelectorAll('body > *'));
33
+ allElements.forEach(function(element) {
34
+ if (element === out) {
35
+ return;
36
+ }
37
+ var style = window.getComputedStyle(element);
38
+ if (style.display !== 'none') {
39
+ element.parentNode.removeChild(element);
40
+ }
41
+ });
42
+ },
43
+
44
+ renderCurrent: function() {
45
+ var out = this.getOutputElement();
17
46
  out.innerHTML = '';
18
- next.func(out);
19
47
 
20
- var elem = out.firstChild;
48
+ // We need to clear out anything currently visible. There's no guarantee
49
+ // that examples only render inside the .likadan-output container.
50
+ this.clearVisibleElements();
51
+
52
+ var elem = this.currentExample.func();
53
+ if (elem.getDOMNode) {
54
+ // Soft-dependency to React here. If the thing returned has a
55
+ // `getDOMNode` method, call it to get the real DOM node.
56
+ elem = elem.getDOMNode();
57
+ }
58
+ out.appendChild(elem);
21
59
 
60
+ var width = elem.offsetWidth;
61
+ var height = elem.offsetHeight;
62
+
63
+ if (this.currentExample.options.snapshotEntireScreen) {
64
+ width = window.innerWidth;
65
+ height = window.innerHeight;
66
+ }
22
67
  return {
23
- name: next.name,
24
- width: elem.offsetWidth,
25
- height: elem.offsetHeight
68
+ name: this.currentExample.name,
69
+ width: width,
70
+ height: height
26
71
  };
27
72
  }
28
73
  };
@@ -1,12 +1,23 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
+ <style type="text/css">
5
+ * {
6
+ -webkit-transition: none !important;
7
+ -moz-transition: none !important;
8
+ transition: none !important;
9
+ -webkit-animation-duration: 0 !important;
10
+ -moz-animation-duration: 0s !important;
11
+ animation-duration: 0s !important;
12
+ }
13
+ </style>
14
+
4
15
  <% @config['stylesheets'].each do |stylesheet| %>
5
16
  <link rel="stylesheet" type="text/css"
6
17
  href="/resource?file=<%= ERB::Util.url_encode(stylesheet) %>">
7
18
  <% end %>
8
19
  </head>
9
- <body style="margin: 0;">
20
+ <body style="margin: 0; background-color: #fff;">
10
21
  <div id="likadan-output"/>
11
22
  <script src="/likadan-runner.js"></script>
12
23
  <% @config['source_files'].each do |source_file| %>
@@ -0,0 +1,44 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <style type="text/css">
5
+ body {
6
+ background-color: #f0f0f0;
7
+ font-family: helvetica, arial;
8
+ }
9
+ form {
10
+ display: inline-block;
11
+ }
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <h1>Likadan Review Tool</h1>
16
+ <h2>DIFFS</h2>
17
+ <% @snapshots[:diffs].each do |diff| %>
18
+ <h3>
19
+ <%= diff[:name] %> @ <%= diff[:width] %>
20
+ </h3>
21
+ <p><img src="/resource?file=<%= ERB::Util.url_encode(diff[:file]) %>"></p>
22
+ <form style="display: inline-block"
23
+ action="/approve?name=<%= diff[:name] %>&width=<%= diff[:width] %>"
24
+ method="POST">
25
+ <button type="submit">Approve</button>
26
+ </form>
27
+ <form style="display: inline-block"
28
+ action="/reject?name=<%= diff[:name] %>&width=<%= diff[:width] %>"
29
+ method="POST">
30
+ <button type="submit">Reject</button>
31
+ </form>
32
+ <% end %>
33
+
34
+ <hr>
35
+
36
+ <h2>BASELINES</h2>
37
+ <% @snapshots[:baselines].each do |baseline| %>
38
+ <h3>
39
+ <%= baseline[:name] %> @ <%= baseline[:width] %>
40
+ </h3>
41
+ <p><img src="/resource?file=<%= ERB::Util.url_encode(baseline[:file]) %>"></p>
42
+ <% end %>
43
+ </body>
44
+ </html>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: likadan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henric Trotzig
@@ -118,10 +118,13 @@ extensions: []
118
118
  extra_rdoc_files: []
119
119
  files:
120
120
  - bin/likadan
121
+ - lib/likadan_action.rb
121
122
  - lib/likadan_runner.rb
122
123
  - lib/likadan_server.rb
124
+ - lib/likadan_utils.rb
123
125
  - lib/public/likadan-runner.js
124
126
  - lib/views/index.erb
127
+ - lib/views/review.erb
125
128
  homepage: http://rubygems.org/gems/likadan
126
129
  licenses:
127
130
  - MIT
@@ -142,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
145
  version: '0'
143
146
  requirements: []
144
147
  rubyforge_project:
145
- rubygems_version: 2.2.2
148
+ rubygems_version: 2.4.5
146
149
  signing_key:
147
150
  specification_version: 4
148
151
  summary: Likadan