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 +4 -4
- data/bin/likadan +25 -7
- data/lib/likadan_action.rb +29 -0
- data/lib/likadan_runner.rb +39 -35
- data/lib/likadan_server.rb +37 -15
- data/lib/likadan_utils.rb +29 -0
- data/lib/public/likadan-runner.js +55 -10
- data/lib/views/index.erb +12 -1
- data/lib/views/review.erb +44 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2bdf5b94276da8220ca5344b009e26e11e900afc
|
4
|
+
data.tar.gz: 55145af24ef76484e796629f18a980f5c332ff38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8109fd809d7d81f6c99779a972446e27402a7632b8cedc4a0288eecff586e7bbb3ee7e187694b7478de00a91c94c1672fdfdc6b956516370ef003144eb0bde2b
|
7
|
+
data.tar.gz: 038492a9885d98a506d0279a35a20a22e6db256200e598fc7a3c9813ff707c11c637d5010c6bda27f738b34d6de313ffe0004e3a6c6ff8295adaf5fd063d3dff
|
data/bin/likadan
CHANGED
@@ -1,12 +1,30 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
17
|
+
require 'likadan_server'
|
10
18
|
|
11
|
-
|
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
|
data/lib/likadan_runner.rb
CHANGED
@@ -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 '
|
9
|
+
require 'likadan_utils'
|
10
10
|
|
11
11
|
driver = Selenium::WebDriver.for :firefox
|
12
12
|
begin
|
13
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
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
|
-
|
data/lib/likadan_server.rb
CHANGED
@@ -1,30 +1,42 @@
|
|
1
1
|
require 'sinatra/base'
|
2
2
|
require 'yaml'
|
3
|
-
|
4
|
-
|
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,
|
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 =
|
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
|
-
|
13
|
-
if (!
|
18
|
+
this.currentExample = this.defined[this.currentIndex];
|
19
|
+
if (!this.currentExample) {
|
14
20
|
return;
|
15
21
|
}
|
16
|
-
|
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
|
-
|
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:
|
24
|
-
width:
|
25
|
-
height:
|
68
|
+
name: this.currentExample.name,
|
69
|
+
width: width,
|
70
|
+
height: height
|
26
71
|
};
|
27
72
|
}
|
28
73
|
};
|
data/lib/views/index.erb
CHANGED
@@ -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.
|
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.
|
148
|
+
rubygems_version: 2.4.5
|
146
149
|
signing_key:
|
147
150
|
specification_version: 4
|
148
151
|
summary: Likadan
|