likadan 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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